random_playlist.lua make index more sparse

The name of the game here is to load the database file without taking over the
audio buffer.

5mb database file will now successfully load

added an option to save playlist directly to disk and bypass the rb builtin function completely
however if you choose play the playlist will be loaded back from the disk into the inram dynamic playlist

Change-Id: I43e76f63379721f36ed082c0ad47a6f2539fb15f
This commit is contained in:
William Wilgus 2021-12-08 21:47:29 -05:00
parent 8c88d5c5e2
commit 5433ea5405

View file

@ -20,6 +20,19 @@
* *
****************************************************************************/ ****************************************************************************/
]] ]]
--[[ random_playlist
This script opens the users database file containg track path + filenames
first it reads the database file making an index of tracks
[for large playlists it only saves an index every [10|100|1000] tracks.
tracks will be incrementally loaded along with the results of the entries
traversed but the garbage collector will erase them when needed]
next tracks are choosen at random and added either to an in-ram playlist
using plugin functions OR
to a on disk playlist using a table as a write buffer
the user can also choose to play the playlist in either case
]]
require ("actions") require ("actions")
require("dbgettags") require("dbgettags")
get_tags = nil -- unneeded get_tags = nil -- unneeded
@ -29,6 +42,7 @@ local playlistpath = "/Playlists"
local max_tracks = 500; -- size of playlist to create local max_tracks = 500; -- size of playlist to create
local min_repeat = 500; -- this many songs before a repeat local min_repeat = 500; -- this many songs before a repeat
local play_on_success = true; local play_on_success = true;
local playlist_name = "random_playback.m3u8"
--program vars --program vars
local playlist_handle local playlist_handle
local t_playlistbuf -- table for playlist write buffer local t_playlistbuf -- table for playlist write buffer
@ -63,12 +77,15 @@ local function text_extent(msg, font)
return rb.font_getstringsize(msg, font) return rb.font_getstringsize(msg, font)
end end
local function _setup_random_playlist(tag_entries, play, min_repeat, trackcount) local function _setup_random_playlist(tag_entries, play, savepl, min_repeat, trackcount)
-- Setup string tables -- Setup string tables
local tPLAYTEXT = {"Play? [ %s ] (up/dn)", "true = play tracks on success"} local tPLAYTEXT = {"Play? [ %s ] (up/dn)", "true = play tracks on success"}
local tSAVETEXT = {"Save to disk? [ %s ] (up/dn)",
"true = tracks saved to",
playlist_name};
local tREPEATTEXT = {"Repeat hist? [ %d ] (up/dn)","higher = less repeated songs"} local tREPEATTEXT = {"Repeat hist? [ %d ] (up/dn)","higher = less repeated songs"}
local tPLSIZETEXT = {"Find [ %d ] tracks? (up/dn)", local tPLSIZETEXT = {"Find [ %d ] tracks? (up/dn)",
"Warning overwrites dynamic playlist", "Warning may overwrite dynamic playlist",
"Press back to cancel"}; "Press back to cancel"};
-- how many lines can we fit on the screen? -- how many lines can we fit on the screen?
local res, w, h = text_extent("I") local res, w, h = text_extent("I")
@ -125,6 +142,22 @@ local function _setup_random_playlist(tag_entries, play, min_repeat, trackcount)
end end
end end
-- Save the playlist to disk true/false?
function setup_get_save()
action = ask_user_action(tdesc,
string.format(tSAVETEXT[1], tostring(savepl)),
tSAVETEXT[2], tSAVETEXT[3]);
if action == ADD_BUTTON then
savepl = true
elseif action == SUB_BUTTON then
savepl = false
elseif action == OK_BUTTON then
ask = setup_get_play;
setup_get_save = nil
action = 0
end
end
-- Repeat song buffer list of previously added tracks 0-?? -- Repeat song buffer list of previously added tracks 0-??
function setup_get_repeat() function setup_get_repeat()
if min_repeat >= trackcount then min_repeat = trackcount - 1 end if min_repeat >= trackcount then min_repeat = trackcount - 1 end
@ -139,7 +172,7 @@ local function _setup_random_playlist(tag_entries, play, min_repeat, trackcount)
min_repeat = min_repeat - increment min_repeat = min_repeat - increment
if min_repeat < 0 then min_repeat = 0 end if min_repeat < 0 then min_repeat = 0 end
elseif action == OK_BUTTON then elseif action == OK_BUTTON then
ask = setup_get_play; ask = setup_get_save;
setup_get_repeat = nil setup_get_repeat = nil
action = 0 action = 0
end end
@ -173,9 +206,11 @@ local function _setup_random_playlist(tag_entries, play, min_repeat, trackcount)
if action == CANCEL_BUTTON then rb.lcd_scroll_stop(); return nil end if action == CANCEL_BUTTON then rb.lcd_scroll_stop(); return nil end
until (action == OK_BUTTON) until (action == OK_BUTTON)
return play, min_repeat, trackcount; return play, savepl, min_repeat, trackcount;
end end
--[[ manually create a playlist
playlist is created initially by creating a new file (or erasing old)
and adding the BOM]]
--deletes existing file and creates a new playlist --deletes existing file and creates a new playlist
local function playlist_create(filename) local function playlist_create(filename)
local filehandle = io.open(filename, "w+") --overwrite local filehandle = io.open(filename, "w+") --overwrite
@ -187,44 +222,39 @@ local function playlist_create(filename)
filehandle:write("\239\187\191") -- Write BOM --"\xEF\xBB\xBF" filehandle:write("\239\187\191") -- Write BOM --"\xEF\xBB\xBF"
playlist_handle = filehandle playlist_handle = filehandle
return true return true
--os.remove( playlistpath .. "/" .. playlist)
--rb.playlist("remove_all_tracks")
--rb.playlist("create", playlistpath .. "/", playlist)
end end
-- writes track path to a buffer must be flushed -- writes track path to a buffer must be later flushed to playlist file
local function playlist_write(trackpath) local function playlist_insert(trackpath)
t_playlistbuf[#t_playlistbuf + 1] = trackpath local bufp = #t_playlistbuf + 1
t_playlistbuf[#t_playlistbuf + 1] = "\n" t_playlistbuf[bufp] = trackpath
--[[if rb.playlist("insert_track", str) < 0 then bufp = bufp + 1
rb.splash(rb.HZ, sPLAYLISTERROR) t_playlistbuf[bufp] = "\n"
break; -- ERROR, PLAYLIST FULL? return bufp
end]]
end end
-- flushes playlist buffer to file -- flushes playlist buffer to file
local function playlist_flush() local function playlist_flush()
playlist_handle:write(table.concat(t_playlistbuf)) playlist_handle:write(table.concat(t_playlistbuf))
t_playlistbuf = {} t_playlistbuf = {}
--[[if rb.playlist("insert_track", str) < 0 then
rb.splash(rb.HZ, sPLAYLISTERROR)
break; -- ERROR, PLAYLIST FULL?
end]]
end end
-- closes playlist file descriptor -- closes playlist file descriptor
local function playlist_finalize() local function playlist_finalize()
playlist_handle:close() playlist_handle:close()
return true
end end
--[[ Given the filenameDB file [database] --[[ Given the filenameDB file [database]
creates a random dynamic playlist with a default savename of [playlist] creates a random dynamic playlist with a default savename of [playlist]
containing [trackcount] tracks, played on completion if [play] is true]] containing [trackcount] tracks, played on completion if [play] is true]]
local function create_random_playlist(database, playlist, trackcount, play) local function create_random_playlist(database, playlist, trackcount, play, savepl)
if not database or not playlist or not trackcount then return end if not database or not playlist or not trackcount then return end
if not play then play = false end if not play then play = false end
if not savepl then savepl = false end
local playlist_handle local playlist_handle
local playlistisfinalized = false
local file = io.open('/' .. database or "", "r") --read local file = io.open('/' .. database or "", "r") --read
if not file then rb.splash(100, string.format(sERROROPENFMT, database)) return end if not file then rb.splash(100, string.format(sERROROPENFMT, database)) return end
@ -258,11 +288,25 @@ local function create_random_playlist(database, playlist, trackcount, play)
end end
local tag_entries = bytesLE_n(tagcache_entries) local tag_entries = bytesLE_n(tagcache_entries)
if tag_entries > 50000 then play = false end
play, min_repeat, trackcount = _setup_random_playlist( play, savepl, min_repeat, trackcount = _setup_random_playlist(
tag_entries, play, min_repeat, trackcount); tag_entries, play, savepl, min_repeat, trackcount);
_setup_random_playlist = nil _setup_random_playlist = nil
if savepl == false then
-- Use the rockbox playlist functions to add tracks to in-ram playlist
playlist_create = function(filename)
return (rb.playlist("create", playlistpath .. "/", playlist) >= 0)
end
playlist_insert = function(str)
return rb.playlist("insert_track", str)
end
playlist_flush = function() end
playlist_finalize = function()
return (rb.playlist("amount") >= trackcount)
end
end
if not playlist_create(playlistpath .. "/" .. playlist) then return end
collectgarbage("collect") collectgarbage("collect")
-- how many lines can we fit on the screen? -- how many lines can we fit on the screen?
@ -344,7 +388,7 @@ local function create_random_playlist(database, playlist, trackcount, play)
end end
get_index() --init get_index fn get_index() --init get_index fn
-- Playlist insert loop -- Playlist insert loop
while true do while true do
str = nil str = nil
tries = 0 tries = 0
@ -365,7 +409,11 @@ local function create_random_playlist(database, playlist, trackcount, play)
tracks = tracks + 1 tracks = tracks + 1
show_progress() show_progress()
push_lru(idxp) -- add to repeat list push_lru(idxp) -- add to repeat list
playlist_write(str) if playlist_insert(str) < 0 then
rb.sleep(rb.HZ) --rb playlist fn display own message wait for that
rb.splash(rb.HZ, sPLAYLISTERROR)
break; -- ERROR, PLAYLIST FULL?
end
end end
if tracks >= trackcount then if tracks >= trackcount then
@ -384,7 +432,7 @@ local function create_random_playlist(database, playlist, trackcount, play)
function build_anchor_index() function build_anchor_index()
-- index every n files -- index every n files
ANCHOR_INTV = 1 -- for small db we can put all the entries in ram ANCHOR_INTV = 1 -- for small db we can put all the entries in ram
local ent = tag_entries / 1000 -- more than 10,000 will be incrementally loaded local ent = tag_entries / 100 -- more than 1000 will be incrementally loaded
while ent >= 10 do -- need to reduce the size of the anchor index? while ent >= 10 do -- need to reduce the size of the anchor index?
ent = ent / 10 ent = ent / 10
ANCHOR_INTV = ANCHOR_INTV * 10 ANCHOR_INTV = ANCHOR_INTV * 10
@ -442,8 +490,6 @@ local function create_random_playlist(database, playlist, trackcount, play)
track_index = anchor_index track_index = anchor_index
anchor_index = nil anchor_index = nil
end end
if not playlist_create(playlistpath .. "/" .. playlist) then return end
--[[ --profiling --[[ --profiling
local starttime = rb.current_tick(); local starttime = rb.current_tick();
get_tracks_random() get_tracks_random()
@ -453,16 +499,19 @@ local function create_random_playlist(database, playlist, trackcount, play)
if (false) then if (false) then
--]] --]]
get_tracks_random() get_tracks_random()
playlist_finalize(playlist_handle) playlistisfinalized = playlist_finalize(playlist_handle)
end end
file:close() file:close()
collectgarbage("collect") collectgarbage("collect")
if trackcount and play == true then if trackcount and play == true and playlistisfinalized == true then
rb.audio("stop") rb.audio("stop")
rb.yield() rb.yield()
rb.playlist("create", playlistpath .. "/", "dynamic_playlist.m3u8") if savepl == true then
rb.playlist("insert_playlist", playlistpath .. "/" .. playlist) rb.playlist("create", playlistpath .. "/", playlist)
rb.playlist("insert_playlist", playlistpath .. "/" .. playlist)
rb.sleep(rb.HZ)
end
rb.playlist("start", 0, 0, 0) rb.playlist("start", 0, 0, 0)
end end
@ -482,12 +531,12 @@ local function main()
rb.lcd_update() rb.lcd_update()
collectgarbage("collect") collectgarbage("collect")
create_random_playlist(rb.ROCKBOX_DIR .. "/database_4.tcd", create_random_playlist(rb.ROCKBOX_DIR .. "/database_4.tcd",
"random_playback.m3u8", max_tracks, play_on_success); playlist_name, max_tracks, play_on_success);
rb.splash(rb.HZ * 2, sGOODBYE)
-- Restore user backlight settings -- Restore user backlight settings
rb.backlight_use_settings() rb.backlight_use_settings()
if rb.cpu_boost then rb.cpu_boost(false) end if rb.cpu_boost then rb.cpu_boost(false) end
rb.sleep(rb.HZ)
rb.splash(rb.HZ * 2, sGOODBYE)
--[[ --[[
local used, allocd, free = rb.mem_stats() local used, allocd, free = rb.mem_stats()
local lu = collectgarbage("count") local lu = collectgarbage("count")