initialize
This commit is contained in:
commit
5b23ea99fd
43 changed files with 2336 additions and 0 deletions
95
MusicPlayer/Client/client.lua
Normal file
95
MusicPlayer/Client/client.lua
Normal file
|
@ -0,0 +1,95 @@
|
|||
local graphics = require("graphics")
|
||||
|
||||
local modem = peripheral.find('modem')
|
||||
if not modem then error('No modem found') end
|
||||
modem.open(7101)
|
||||
|
||||
local dfpwm = require("cc.audio.dfpwm")
|
||||
local speaker = peripheral.find("speaker")
|
||||
if not speaker then error('No speaker found') end
|
||||
local decoder = dfpwm.make_decoder()
|
||||
|
||||
local status_vars = {
|
||||
is_playing = true,
|
||||
is_paused = false,
|
||||
current_second = 0,
|
||||
track_index = 1,
|
||||
start_index = 1,
|
||||
}
|
||||
|
||||
local cfg_vars = {
|
||||
forward_skip = 5,
|
||||
backward_skip = 5,
|
||||
port = 7101,
|
||||
chunk_size = 0.5
|
||||
}
|
||||
|
||||
local function get_catalogue()
|
||||
modem.transmit(7101, 7101, {type = 'query'})
|
||||
local event, sides, channel, replyChannel, message, distance = os.pullEvent('modem_message')
|
||||
return message.audio_catalogue
|
||||
end
|
||||
local audio_catalogue = get_catalogue()
|
||||
|
||||
local function music_loop()
|
||||
local play_music = require("playback")
|
||||
while true do
|
||||
status_vars.current_second = play_music(status_vars, cfg_vars, audio_catalogue, modem)
|
||||
sleep()
|
||||
end
|
||||
end
|
||||
|
||||
local function draw_ui_loop()
|
||||
while true do
|
||||
if not status_vars.is_playing then
|
||||
graphics.draw_track_list(1, 1, audio_catalogue, status_vars.start_index, select(2, term.getSize()) - 1, term)
|
||||
else
|
||||
graphics.draw_track_list(1, 1, audio_catalogue, status_vars.start_index, select(2, term.getSize()) - 4, term)
|
||||
end
|
||||
sleep(1)
|
||||
end
|
||||
end
|
||||
|
||||
local function control_loop()
|
||||
while true do
|
||||
local event, button, x, y = os.pullEvent("mouse_click")
|
||||
|
||||
local btn_actions = {
|
||||
pause = {
|
||||
x = x + math.floor((select(1, term.getSize()) - 11) / 2) + 5,
|
||||
y = select(2, term.getSize()) - 1,
|
||||
func = function ()
|
||||
status_vars.is_paused = not status_vars.is_paused
|
||||
end
|
||||
},
|
||||
forward = {
|
||||
x = math.floor((select(1, term.getSize()) - 11) / 2) + 9,
|
||||
y = select(2, term.getSize()) - 1,
|
||||
func = function ()
|
||||
status_vars.current_second = status_vars.current_second + cfg_vars.forward_skip
|
||||
end
|
||||
},
|
||||
backward = {
|
||||
x = math.floor((select(1, term.getSize()) - 11) / 2) + 1,
|
||||
y = select(2, term.getSize()) - 1,
|
||||
func = function ()
|
||||
status_vars.current_second = status_vars.current_second - cfg_vars.backward_skip
|
||||
end
|
||||
}
|
||||
}
|
||||
if x == 3 and y == 1 then return end
|
||||
|
||||
term.setCursorPos(btn_actions.pause.x, btn_actions.pause.y)
|
||||
term.write('%')
|
||||
|
||||
for i, v in pairs(btn_actions) do
|
||||
if x == v.x and y == v.y then
|
||||
v.func()
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
term.clear() -- Clear terminal
|
||||
parallel.waitForAny(music_loop, draw_ui_loop, control_loop) -- Start loops
|
167
MusicPlayer/Client/graphics.lua
Normal file
167
MusicPlayer/Client/graphics.lua
Normal file
|
@ -0,0 +1,167 @@
|
|||
local api = {}
|
||||
|
||||
api.symbols = {
|
||||
audio_controls = {
|
||||
play = "\16",
|
||||
pause = "\19",
|
||||
forward = "\16",
|
||||
backward = "\17",
|
||||
full_back = "\171"
|
||||
},
|
||||
app_controls = {
|
||||
up = "\30",
|
||||
down = "\31",
|
||||
download = "\25",
|
||||
play = "\16"
|
||||
},
|
||||
misc = {
|
||||
music = "\15",
|
||||
}
|
||||
}
|
||||
|
||||
-- Draw a spinner loop at a specified position. Useful for waitForAny.
|
||||
function api.spinner_loop(x, y, spin_monitor)
|
||||
local spin_chars = {
|
||||
"\139",
|
||||
"\138",
|
||||
"\142",
|
||||
"\140",
|
||||
"\141",
|
||||
"\133",
|
||||
"\135",
|
||||
"\131"
|
||||
}
|
||||
local index = 1
|
||||
while true do
|
||||
spin_monitor.setCursorPos(x, y)
|
||||
spin_monitor.write(spin_chars[index])
|
||||
index = index + 1
|
||||
if index > #spin_chars then index = index - #spin_chars end
|
||||
sleep(0.1)
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
Function to create simple progress bars of any length.
|
||||
size - length of the bar in pixels, taken, max - ratio of the bar that should be full.
|
||||
|
||||
There is an ability to set custom symbols for full and empty pixels,
|
||||
however it is not necessary and by default the program uses '#' and '.' for these
|
||||
]]
|
||||
function api.make_progressbar(size, taken, max, char_full, char_empty)
|
||||
--[[
|
||||
Maths behind the function are quite simple:
|
||||
taken/max = x/size
|
||||
taken * size = x * max
|
||||
x = taken * size / max
|
||||
]]
|
||||
local full_symbol, empty_symbol = char_full or '#', char_empty or '.'
|
||||
size = size - 2 -- make space for "[" and "]"
|
||||
local progress = math.floor(size * taken / max)
|
||||
local hash = (full_symbol):rep(progress)
|
||||
local dots = (empty_symbol):rep(size - progress)
|
||||
return ("[" .. hash .. dots .. "]")
|
||||
end
|
||||
|
||||
local function seconds_to_string(seconds)
|
||||
-- int to mm:ss
|
||||
local minutes = math.floor(seconds / 60)
|
||||
local seconds = seconds - minutes * 60
|
||||
return string.format("%02d:%02d", minutes, seconds)
|
||||
end
|
||||
|
||||
function api.draw_audio_bar(x, y, progress_seconds, track_info, is_paused, monitor)
|
||||
--[[
|
||||
Example:
|
||||
| Among us soundtrack lol |
|
||||
| [<] [####........[<]............] [>] |
|
||||
| 12:43/14:31 |
|
||||
]]
|
||||
local x_max, y_max = monitor.getSize()
|
||||
|
||||
-- Title
|
||||
monitor.setCursorPos(x, y)
|
||||
local title = track_info.title
|
||||
if #title > x_max - 2 then
|
||||
title = title:sub(1, x_max - 4) .. ".."
|
||||
end
|
||||
monitor.write((' '):rep(math.floor((x_max - #title) / 2)) .. title .. (' '):rep(math.ceil((x_max - #title) / 2)))
|
||||
|
||||
-- Progress bar
|
||||
monitor.setCursorPos(x, y + 1)
|
||||
local progress_bar = api.make_progressbar(x_max - 16, progress_seconds, track_info.length)
|
||||
--monitor.blit(' ' .. seconds_to_string(progress_seconds) .. ' ' .. progress_bar .. ' ' .. seconds_to_string(track_info.length), 'f00000f0' .. ('3'):rep(x_max - 16) .. '0f00000f', ('f'):rep(x_max))
|
||||
monitor.write(' ' .. seconds_to_string(progress_seconds) .. ' ' .. progress_bar .. ' ' .. seconds_to_string(track_info.length))
|
||||
|
||||
-- Controls
|
||||
local controls = {
|
||||
api.symbols.audio_controls.backward,
|
||||
is_paused and api.symbols.audio_controls.play or api.symbols.audio_controls.pause,
|
||||
api.symbols.audio_controls.forward
|
||||
}
|
||||
for i, control in ipairs(controls) do
|
||||
monitor.setCursorPos(x + math.floor((x_max - 11) / 2) + (i - 1) * 4, y + 1)
|
||||
monitor.write('[' .. control .. ']')
|
||||
end
|
||||
end
|
||||
|
||||
-- Copy of draw_audio_bar, but for when no track is played - just a blank bar.
|
||||
function api.draw_track_list(x, y, audio_catalogue, start_index, height, monitor)
|
||||
height = height or select(2, monitor.getSize())
|
||||
local x_max, y_max = monitor.getSize()
|
||||
|
||||
--[[
|
||||
top bar:
|
||||
"[X] 21:00:01 Tue 12"
|
||||
|
||||
-button to close the app
|
||||
-time and date
|
||||
|
||||
bottom bar:
|
||||
"[up_btn] [down_btn] Showing [current_max_index - list_size]-[current_max_index] of [#audio_catalogue] | [REFRESH]"
|
||||
-buttons to go up and down the list
|
||||
-current index and total number of tracks
|
||||
-button to refresh the list
|
||||
|
||||
list items:
|
||||
"[index] [title] [length] [play_btn] [download_btn]"
|
||||
-index - number of the track in the list
|
||||
-title - title of the track
|
||||
-length - length of the track
|
||||
-play_btn - button to play the track
|
||||
-download_btn - button to download the track (not implemented yet)
|
||||
]]
|
||||
|
||||
-- top bar
|
||||
monitor.setCursorPos(x, y)
|
||||
local current_time = os.date("%H:%M:%S %b %d")
|
||||
local exit_btn = "[X]"
|
||||
|
||||
monitor.blit(' ' .. exit_btn .. ' ' ..current_time, 'f0e0f00000000f000f00', 'ffffffffffffffffffff')
|
||||
|
||||
-- bottom bar
|
||||
monitor.setCursorPos(x, y + height - 1)
|
||||
local bottom_btns = '[' .. api.symbols.app_controls.up .. '] [' .. api.symbols.app_controls.down .. ']'
|
||||
local showing = 'Showing ' .. tostring(start_index) .. '-' .. tostring(start_index + height - 2) .. ' of ' .. tostring(#audio_catalogue)
|
||||
local refresh_btn = '[REFRESH]'
|
||||
monitor.blit(' ' .. bottom_btns .. ' ' .. showing .. ' | ' .. refresh_btn, 'f0e0f0b0f' .. ('0'):rep(#showing) .. "f0f033333330", ('f'):rep(#bottom_btns + #showing + #refresh_btn + 5))
|
||||
-- monitor.write(' ' .. bottom_btns .. ' ' .. showing .. ' | ' .. refresh_btn)
|
||||
|
||||
-- list items
|
||||
-- i should be current_max_index - height - 2 or 1 if current_max_index - height - 2 < 1
|
||||
for i = start_index, math.min(start_index + height - 2, #audio_catalogue) do
|
||||
if (y + 1 + i >= y + height - 1) or i > #audio_catalogue then break end
|
||||
|
||||
local track = audio_catalogue[i]
|
||||
local index = tostring(i)
|
||||
local title = track.title
|
||||
local length = seconds_to_string(track.length)
|
||||
local play_btn = api.symbols.audio_controls.play
|
||||
local download_btn = api.symbols.app_controls.download
|
||||
local list_item = ' ' .. index .. ' ' .. title .. ' ' .. length .. ' ' .. play_btn .. ' ' .. download_btn
|
||||
monitor.setCursorPos(x, y + 1 + i)
|
||||
monitor.write(list_item)
|
||||
end
|
||||
end
|
||||
|
||||
return api
|
27
MusicPlayer/Client/playback.lua
Normal file
27
MusicPlayer/Client/playback.lua
Normal file
|
@ -0,0 +1,27 @@
|
|||
local dfpwm = require("cc.audio.dfpwm")
|
||||
local speaker = peripheral.find("speaker")
|
||||
local decoder = dfpwm.make_decoder()
|
||||
|
||||
local graphics = require("graphics")
|
||||
|
||||
|
||||
local function play_music(status_vars, cfg_vars, audio_catalogue, modem)
|
||||
if (not status_vars.is_playing) or status_vars.is_paused then return 0 end
|
||||
|
||||
modem.transmit(7101, 7101, {type = 'track_chunk',
|
||||
index = status_vars.track_index,
|
||||
starting_second = status_vars.current_second,
|
||||
length = cfg_vars.chunk_size})
|
||||
local event, sides, channel, replyChannel, message, distance = os.pullEvent('modem_message')
|
||||
|
||||
local buffer = decoder(message.track)
|
||||
|
||||
while not speaker.playAudio(buffer) do
|
||||
os.pullEvent("speaker_audio_empty")
|
||||
graphics.draw_audio_bar(1, select(2, term.getSize()) - 2, status_vars.current_second + cfg_vars.chunk_size, audio_catalogue[status_vars.track_index], status_vars.is_paused, term)
|
||||
end
|
||||
|
||||
return status_vars.current_second + cfg_vars.chunk_size
|
||||
end
|
||||
|
||||
return play_music
|
59
MusicPlayer/Client/touch_processing.lua
Normal file
59
MusicPlayer/Client/touch_processing.lua
Normal file
|
@ -0,0 +1,59 @@
|
|||
local touch_processer = {}
|
||||
|
||||
touch_processer.init = function ()
|
||||
local self = {}
|
||||
|
||||
|
||||
|
||||
function self.process_minimised_controls()
|
||||
local x, y = e[3], e[4]
|
||||
local btn_actions = {
|
||||
pause = {
|
||||
x = pause_btn_coords.x,
|
||||
y = pause_btn_coords.y,
|
||||
func = function()
|
||||
status_vars.is_paused = not status_vars.is_paused
|
||||
end
|
||||
},
|
||||
forward = {
|
||||
x = forward_btn_coords.x,
|
||||
y = forward_btn_coords.y,
|
||||
func = function()
|
||||
status_vars.current_second = math.min(status_vars.current_second + cfg_vars.forward_skip, status_vars.max_seconds)
|
||||
end
|
||||
},
|
||||
backward = {
|
||||
x = backward_btn_coords.x,
|
||||
y = backward_btn_coords.y,
|
||||
func = function()
|
||||
status_vars.current_second = math.max(status_vars.current_second - cfg_vars.backward_skip, 0)
|
||||
end
|
||||
},
|
||||
exit = {
|
||||
x = exit_btn_coords.x,
|
||||
y = exit_btn_coords.y,
|
||||
func = function()
|
||||
status_vars.is_playing = false
|
||||
return
|
||||
end
|
||||
},
|
||||
minimise = {
|
||||
x = minimise_btn_coords.x,
|
||||
y = minimise_btn_coords.y,
|
||||
func = function()
|
||||
status_vars.is_minimised = not status_vars.is_minimised
|
||||
end
|
||||
},
|
||||
}
|
||||
for k, v in pairs(btn_actions) do
|
||||
if x == v.x and y == v.y then
|
||||
v.func()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
local function key_to_func()
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue