Current section
Files
Jump to
Current section
Files
src/rockbox@player.erl
-module(rockbox@player).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/rockbox/player.gleam").
-export([default_config/0, new/0, with_config/1, set_queue/2, insert/3, queue/1, enqueue/2, play/1, pause/1, toggle/1, stop/1, next/1, previous/1, skip_to/2, seek_ms/2, set_volume/2, volume/1, sample_rate/1, set_crossfade/7, set_replaygain/4, status/1, resume/1, save_resume/1, clear_resume/1, load_resume/1, import_m3u/3, load_m3u/2, export_m3u/2, m3u_read/1, m3u_write/2, is_url/1]).
-export_type([player/0, insert_position/0, config/0, resume_state/0, m3u_entry/0, status/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Queue-based player with native ReplayGain and Rockbox crossfade.\n"
"\n"
" A `Player` owns a live audio output device and a background engine\n"
" thread, so it only works where an output device exists. The handle is a\n"
" NIF resource, freed by the BEAM garbage collector (which stops playback).\n"
"\n"
" ReplayGain `mode` here uses the player values: 0 off, 1 track, 2 album.\n"
" Crossfade `mode`: 0 off, 1 auto-skip, 2 manual-skip, 3 shuffle,\n"
" 4 shuffle-or-manual, 5 always. Mix mode: 0 crossfade, 1 mix.\n"
).
-type player() :: any().
-type insert_position() :: prepend |
insert |
insert_next |
insert_last |
insert_shuffled |
insert_last_shuffled |
replace |
{index, integer()}.
-type config() :: {config,
integer(),
float(),
float(),
integer(),
float(),
boolean(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
gleam@option:option(binary()),
integer()}.
-type resume_state() :: {resume_state, list(binary()), integer(), integer()}.
-type m3u_entry() :: {m3u_entry,
binary(),
gleam@option:option(integer()),
gleam@option:option(binary())}.
-type status() :: {status,
binary(),
gleam@option:option(integer()),
integer(),
integer(),
integer()}.
-file("src/rockbox/player.gleam", 75).
?DOC(
" Rockbox-default configuration (device sample rate, no crossfade,\n"
" ReplayGain off, full volume, resume disabled).\n"
).
-spec default_config() -> config().
default_config() ->
{config, 0, 4.0, 1.0, 0, +0.0, true, 0, 0, 2000, 0, 2000, 0, none, 0}.
-file("src/rockbox/player.gleam", 96).
?DOC(
" Encode an `InsertPosition` as the ABI's integer tag plus the index arg\n"
" (only meaningful for `Index`).\n"
).
-spec insert_position_args(insert_position()) -> {integer(), integer()}.
insert_position_args(Position) ->
case Position of
prepend ->
{0, 0};
insert ->
{1, 0};
insert_next ->
{2, 0};
insert_last ->
{3, 0};
insert_shuffled ->
{4, 0};
insert_last_shuffled ->
{5, 0};
replace ->
{6, 0};
{index, I} ->
{7, I}
end.
-file("src/rockbox/player.gleam", 122).
?DOC(" Create a player on the default device with default settings.\n").
-spec new() -> player().
new() ->
rockbox_ffi_nif:player_new().
-file("src/rockbox/player.gleam", 125).
?DOC(" Create a player with explicit configuration (including resume).\n").
-spec with_config(config()) -> player().
with_config(C) ->
Resume_file = case erlang:element(14, C) of
{some, Path} ->
gleam_stdlib:identity(Path);
none ->
gleam@dynamic:nil()
end,
rockbox_ffi_nif:player_new_with_config_ex(
erlang:element(2, C),
erlang:element(3, C),
erlang:element(4, C),
erlang:element(5, C),
erlang:element(6, C),
erlang:element(7, C),
erlang:element(8, C),
erlang:element(9, C),
erlang:element(10, C),
erlang:element(11, C),
erlang:element(12, C),
erlang:element(13, C),
Resume_file,
erlang:element(15, C)
).
-file("src/rockbox/player.gleam", 151).
?DOC(" Replace the queue with a list of file paths (or http(s):// URLs).\n").
-spec set_queue(player(), list(binary())) -> nil.
set_queue(Player, Paths) ->
rockbox_ffi_nif:player_set_queue_json(
Player,
erlang:iolist_to_binary(json:encode(Paths))
).
-file("src/rockbox/player.gleam", 156).
?DOC(" Insert a list of paths / URLs into the queue at `position`.\n").
-spec insert(player(), list(binary()), insert_position()) -> nil.
insert(Player, Paths, Position) ->
{Pos, Index} = insert_position_args(Position),
rockbox_ffi_nif:player_insert_json(
Player,
erlang:iolist_to_binary(json:encode(Paths)),
Pos,
Index
).
-file("src/rockbox/player.gleam", 166).
?DOC(" The current queue as a list of paths / URLs.\n").
-spec queue(player()) -> list(binary()).
queue(Player) ->
case gleam@dynamic@decode:run(
json:decode(rockbox_ffi_nif:player_queue_json(Player)),
gleam@dynamic@decode:list(
{decoder, fun gleam@dynamic@decode:decode_string/1}
)
) of
{ok, Paths} ->
Paths;
{error, _} ->
[]
end.
-file("src/rockbox/player.gleam", 174).
-spec enqueue(player(), binary()) -> nil.
enqueue(Player, Path) ->
rockbox_ffi_nif:player_enqueue(Player, Path).
-file("src/rockbox/player.gleam", 177).
-spec play(player()) -> nil.
play(Player) ->
rockbox_ffi_nif:player_play(Player).
-file("src/rockbox/player.gleam", 180).
-spec pause(player()) -> nil.
pause(Player) ->
rockbox_ffi_nif:player_pause(Player).
-file("src/rockbox/player.gleam", 183).
-spec toggle(player()) -> nil.
toggle(Player) ->
rockbox_ffi_nif:player_toggle(Player).
-file("src/rockbox/player.gleam", 186).
-spec stop(player()) -> nil.
stop(Player) ->
rockbox_ffi_nif:player_stop(Player).
-file("src/rockbox/player.gleam", 189).
-spec next(player()) -> nil.
next(Player) ->
rockbox_ffi_nif:player_next(Player).
-file("src/rockbox/player.gleam", 192).
-spec previous(player()) -> nil.
previous(Player) ->
rockbox_ffi_nif:player_previous(Player).
-file("src/rockbox/player.gleam", 195).
-spec skip_to(player(), integer()) -> nil.
skip_to(Player, Index) ->
rockbox_ffi_nif:player_skip_to(Player, Index).
-file("src/rockbox/player.gleam", 198).
-spec seek_ms(player(), integer()) -> nil.
seek_ms(Player, Ms) ->
rockbox_ffi_nif:player_seek_ms(Player, Ms).
-file("src/rockbox/player.gleam", 201).
-spec set_volume(player(), float()) -> nil.
set_volume(Player, Volume) ->
rockbox_ffi_nif:player_set_volume(Player, Volume).
-file("src/rockbox/player.gleam", 204).
-spec volume(player()) -> float().
volume(Player) ->
rockbox_ffi_nif:player_volume(Player).
-file("src/rockbox/player.gleam", 207).
-spec sample_rate(player()) -> integer().
sample_rate(Player) ->
rockbox_ffi_nif:player_sample_rate(Player).
-file("src/rockbox/player.gleam", 210).
-spec set_crossfade(
player(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer()
) -> nil.
set_crossfade(
Player,
Mode,
Fade_out_delay_ms,
Fade_out_duration_ms,
Fade_in_delay_ms,
Fade_in_duration_ms,
Mix_mode
) ->
rockbox_ffi_nif:player_set_crossfade(
Player,
Mode,
Fade_out_delay_ms,
Fade_out_duration_ms,
Fade_in_delay_ms,
Fade_in_duration_ms,
Mix_mode
).
-file("src/rockbox/player.gleam", 221).
-spec set_replaygain(player(), integer(), float(), boolean()) -> nil.
set_replaygain(Player, Mode, Preamp_db, Prevent_clipping) ->
rockbox_ffi_nif:player_set_replaygain(
Player,
Mode,
Preamp_db,
Prevent_clipping
).
-file("src/rockbox/player.gleam", 359).
-spec status_decoder() -> gleam@dynamic@decode:decoder(status()).
status_decoder() ->
gleam@dynamic@decode:field(
<<"state"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(State) ->
gleam@dynamic@decode:field(
<<"index"/utf8>>,
gleam@dynamic@decode:one_of(
gleam@dynamic@decode:map(
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Field@0) -> {some, Field@0} end
),
[gleam@dynamic@decode:success(none)]
),
fun(Index) ->
gleam@dynamic@decode:field(
<<"position_ms"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Position_ms) ->
gleam@dynamic@decode:field(
<<"duration_ms"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Duration_ms) ->
gleam@dynamic@decode:field(
<<"queue_len"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_int/1},
fun(Queue_len) ->
gleam@dynamic@decode:success(
{status,
State,
Index,
Position_ms,
Duration_ms,
Queue_len}
)
end
)
end
)
end
)
end
)
end
).
-file("src/rockbox/player.gleam", 229).
?DOC(" A snapshot of the player's status.\n").
-spec status(player()) -> status().
status(Player) ->
Status@1 = case gleam@dynamic@decode:run(
json:decode(rockbox_ffi_nif:player_status_json(Player)),
status_decoder()
) of
{ok, Status} -> Status;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"rockbox/player"/utf8>>,
function => <<"status"/utf8>>,
line => 230,
value => _assert_fail,
start => 6832,
'end' => 6926,
pattern_start => 6843,
pattern_end => 6853})
end,
Status@1.
-file("src/rockbox/player.gleam", 335).
-spec resume_decoder() -> gleam@dynamic@decode:decoder(resume_state()).
resume_decoder() ->
gleam@dynamic@decode:field(
<<"tracks"/utf8>>,
gleam@dynamic@decode:list(
{decoder, fun gleam@dynamic@decode:decode_string/1}
),
fun(Tracks) ->
gleam@dynamic@decode:field(
<<"index"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Index) ->
gleam@dynamic@decode:field(
<<"elapsed_ms"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Elapsed_ms) ->
gleam@dynamic@decode:success(
{resume_state, Tracks, Index, Elapsed_ms}
)
end
)
end
)
end
).
-file("src/rockbox/player.gleam", 313).
-spec decode_resume(gleam@dynamic:dynamic_()) -> gleam@option:option(resume_state()).
decode_resume(Value) ->
case gleam@dynamic@decode:run(
Value,
{decoder, fun gleam@dynamic@decode:decode_string/1}
) of
{ok, Json} ->
case gleam@dynamic@decode:run(json:decode(Json), resume_decoder()) of
{ok, State} ->
{some, State};
{error, _} ->
none
end;
{error, _} ->
none
end.
-file("src/rockbox/player.gleam", 239).
?DOC(
" Restore the queue and exact position from the configured resume file (does\n"
" NOT auto-play). `None` if there is nothing to resume.\n"
).
-spec resume(player()) -> gleam@option:option(resume_state()).
resume(Player) ->
decode_resume(rockbox_ffi_nif:player_resume(Player)).
-file("src/rockbox/player.gleam", 245).
?DOC(" Persist the current queue + position to the resume file right now.\n").
-spec save_resume(player()) -> nil.
save_resume(Player) ->
rockbox_ffi_nif:player_save_resume(Player).
-file("src/rockbox/player.gleam", 249).
?DOC(" Delete the resume file (forget the saved state).\n").
-spec clear_resume(player()) -> nil.
clear_resume(Player) ->
rockbox_ffi_nif:player_clear_resume(Player).
-file("src/rockbox/player.gleam", 252).
?DOC(" Peek at a resume file without a player. `None` if it is missing/unreadable.\n").
-spec load_resume(binary()) -> gleam@option:option(resume_state()).
load_resume(Path) ->
case gleam@dynamic@decode:run(
rockbox_ffi_nif:load_resume_json(Path),
{decoder, fun gleam@dynamic@decode:decode_string/1}
) of
{ok, Json} ->
decode_resume(gleam_stdlib:identity(Json));
{error, _} ->
none
end.
-file("src/rockbox/player.gleam", 324).
-spec decode_string_list(gleam@dynamic:dynamic_()) -> list(binary()).
decode_string_list(Value) ->
case gleam@dynamic@decode:run(
Value,
{decoder, fun gleam@dynamic@decode:decode_string/1}
) of
{ok, Json} ->
case gleam@dynamic@decode:run(
json:decode(Json),
gleam@dynamic@decode:list(
{decoder, fun gleam@dynamic@decode:decode_string/1}
)
) of
{ok, Paths} ->
Paths;
{error, _} ->
[]
end;
{error, _} ->
[]
end.
-file("src/rockbox/player.gleam", 263).
?DOC(
" Import a playlist file into the queue at `position`; returns the imported\n"
" paths.\n"
).
-spec import_m3u(player(), binary(), insert_position()) -> list(binary()).
import_m3u(Player, Path, Position) ->
{Pos, Index} = insert_position_args(Position),
decode_string_list(
rockbox_ffi_nif:player_import_m3u(Player, Path, Pos, Index)
).
-file("src/rockbox/player.gleam", 273).
?DOC(" Replace the queue with a playlist file; returns the loaded paths.\n").
-spec load_m3u(player(), binary()) -> list(binary()).
load_m3u(Player, Path) ->
decode_string_list(rockbox_ffi_nif:player_load_m3u(Player, Path)).
-file("src/rockbox/player.gleam", 278).
?DOC(" Export the current queue to an `.m3u8` (atomic). `Ok(Nil)` / `Error(Nil)`.\n").
-spec export_m3u(player(), binary()) -> {ok, nil} | {error, nil}.
export_m3u(Player, Path) ->
case rockbox_ffi_nif:player_export_m3u(Player, Path) of
0 ->
{ok, nil};
_ ->
{error, nil}
end.
-file("src/rockbox/player.gleam", 353).
-spec optional_string() -> gleam@dynamic@decode:decoder(gleam@option:option(binary())).
optional_string() ->
gleam@dynamic@decode:one_of(
gleam@dynamic@decode:map(
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Field@0) -> {some, Field@0} end
),
[gleam@dynamic@decode:success(none)]
).
-file("src/rockbox/player.gleam", 349).
-spec optional_int() -> gleam@dynamic@decode:decoder(gleam@option:option(integer())).
optional_int() ->
gleam@dynamic@decode:one_of(
gleam@dynamic@decode:map(
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Field@0) -> {some, Field@0} end
),
[gleam@dynamic@decode:success(none)]
).
-file("src/rockbox/player.gleam", 342).
-spec m3u_entry_decoder() -> gleam@dynamic@decode:decoder(m3u_entry()).
m3u_entry_decoder() ->
gleam@dynamic@decode:field(
<<"path"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Path) ->
gleam@dynamic@decode:field(
<<"duration_ms"/utf8>>,
optional_int(),
fun(Duration_ms) ->
gleam@dynamic@decode:field(
<<"title"/utf8>>,
optional_string(),
fun(Title) ->
gleam@dynamic@decode:success(
{m3u_entry, Path, Duration_ms, Title}
)
end
)
end
)
end
).
-file("src/rockbox/player.gleam", 286).
?DOC(" Parse a playlist file into its entries. `Error(Nil)` if it cannot be read.\n").
-spec m3u_read(binary()) -> {ok, list(m3u_entry())} | {error, nil}.
m3u_read(Path) ->
case gleam@dynamic@decode:run(
rockbox_ffi_nif:m3u_read_json(Path),
{decoder, fun gleam@dynamic@decode:decode_string/1}
) of
{ok, Json} ->
case gleam@dynamic@decode:run(
json:decode(Json),
gleam@dynamic@decode:list(m3u_entry_decoder())
) of
{ok, Entries} ->
{ok, Entries};
{error, _} ->
{error, nil}
end;
{error, _} ->
{error, nil}
end.
-file("src/rockbox/player.gleam", 298).
?DOC(" Write a list of paths as an `.m3u8`. `Ok(Nil)` / `Error(Nil)`.\n").
-spec m3u_write(binary(), list(binary())) -> {ok, nil} | {error, nil}.
m3u_write(Path, Paths) ->
case rockbox_ffi_nif:m3u_write_json(
Path,
erlang:iolist_to_binary(json:encode(Paths))
) of
0 ->
{ok, nil};
_ ->
{error, nil}
end.
-file("src/rockbox/player.gleam", 307).
?DOC(" Whether a string looks like an http(s):// URL.\n").
-spec is_url(binary()) -> boolean().
is_url(S) ->
rockbox_ffi_nif:is_url(S).