-
Notifications
You must be signed in to change notification settings - Fork 3
/
austream_tm.lua
134 lines (130 loc) · 5.73 KB
/
austream_tm.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
local aukit = require "aukit"
local loop = require "taskmaster"()
local speakers = {peripheral.find("speaker")}
if #speakers == 0 then error("No speaker attached") end
if #speakers == 2 and peripheral.getName(speakers[1]) == "right" and peripheral.getName(speakers[2]) == "left" then speakers = {speakers[2], speakers[1]} end
local path = ...
if not path then error("Usage: austream <file/URL> [arguments for PCM/DFPWM]") end
local params = select(2, ...)
local v = {}
if params then
local fn, err = load("return {" .. params:gsub("[^%w,=\"%.]+", "") .. "}", "=unserialize", "t", setmetatable({}, {__index = function(_, idx) return idx end}))
if not fn then error(err) end
v = fn()
end
local mono = v.mono or #speakers == 1
aukit.defaultInterpolation = v.interpolation or aukit.defaultInterpolation
local data
if path:match("^https?://") then
local handle, err = http.get(path, nil, true)
if not handle then error("Could not connect to " .. path .. ": " .. err) end
local code = handle.getResponseCode()
if code ~= 200 then handle.close() error("Could not connect to " .. path .. ": HTTP " .. code) end
if v.streamData then
local closed = false
function data()
if closed then return nil end
local d = handle.read(48000)
if not d then handle.close() closed = true return nil end
return d
end
else
data = handle.readAll()
handle.close()
end
elseif path:match("^wss?://") then
local handle, err = http.websocket(path)
if not handle then error("Could not connect to " .. path .. ": " .. err) end
local closed = false
function data()
local _ = handle -- keep the handle alive
if closed then return nil end
while true do
local ev, url, msg, binary = os.pullEvent()
if ev == "websocket_message" and url == path then
if not binary then print("Warning: Text message detected! This audio may be corrupt.") end
return msg
elseif ev == "websocket_closed" and url == path then
closed = true
return nil
end
end
end
elseif path:match("^rednet://%d+") or path:match("^rednet%+%l+://%d+") then
peripheral.find("modem", rednet.open)
local proto, id, name = path:match("^rednet%+?(%l*)://(%d+)(/?.*)$")
id = tonumber(id)
if proto == "" then proto = nil end
if path ~= "" and id >= 0 then rednet.send(id, name, proto) end
function data()
local i, msg
repeat i, msg = rednet.receive(proto) until id == -1 or i == id
return msg
end
else
path = shell.resolve(...)
local file, err = fs.open(path, "rb")
if not file then error("Could not open " .. path .. ": " .. err) end
if v.streamData then
local closed = false
function data()
if closed then return nil end
local d = file.read(48000)
if not d then file.close() closed = true return nil end
return d
end
else
data = file.readAll()
file.close()
end
end
local iter, length
if type(v.type) == "string" then v.type = v.type:lower() end
if v.type == "dfpwm" or path:lower():match("%.dfpwm$") then iter, length = aukit.stream.dfpwm(data, v.sampleRate, v.channels, mono)
elseif v.type == "wav" or path:lower():match("%.wav$") then iter, length = aukit.stream.wav(data, mono, v.ignoreHeader)
elseif v.type == "aiff" or path:lower():match("%.aiff?$") then iter, length = aukit.stream.aiff(data, mono, v.ignoreHeader)
elseif v.type == "au" or path:lower():match("%.au$") then iter, length = aukit.stream.au(data, mono, v.ignoreHeader)
elseif v.type == "flac" or path:lower():match("%.flac$") then iter, length = aukit.stream.flac(data, mono)
elseif v.type == "pcm" or path:lower():match("%.pcm$") or path:lower():match("%.raw$") or path:match("^rednet%+?%l*://") then iter, length = aukit.stream.pcm(data, v.bitDepth, v.dataType, v.channels, v.sampleRate, v.bigEndian, mono)
else error("Unknown file type. Make sure to add the right file extension to the path/URL.") end
if length == nil then length = 0 end
print("Streaming...")
local w = term.getSize()
local y = select(2, term.getCursorPos())
local fg, bg = colors.toBlit(term.getTextColor()), colors.toBlit(term.getBackgroundColor())
term.write(("00:00 %s %02d:%02d"):format(("\127"):rep(w - 12), math.floor(length / 60), length % 60))
local function progress(pos)
pos = math.min(pos, 5999)
local p = pos / length
term.setCursorPos(1, y)
if p > 1 then
term.blit(("%02d:%02d %s --:--"):format(math.floor(pos / 60), pos % 60, (" "):rep(w - 12)), fg:rep(w), bg:rep(6) .. fg:rep(w - 12) .. bg:rep(6))
else
term.blit(("%02d:%02d %s%s %02d:%02d"):format(math.floor(pos / 60), pos % 60, (" "):rep(math.floor((w - 12) * p)), ("\127"):rep((w - 12) - math.floor((w - 12) * p)), math.floor(length / 60), length % 60),
fg:rep(w), bg:rep(6) .. fg:rep(math.floor((w - 12) * p)) .. bg:rep((w - 12) - math.floor((w - 12) * p) + 6))
end
end
local player = aukit.player(loop, iter, table.unpack(speakers))
loop:addTask(function()
while true do
local event, param = os.pullEvent("key")
if param == keys.space then
if player.isPaused then player:play()
else player:pause() end
elseif param == keys.left then
player:seek(math.max(player:livePosition() - 5, 0))
elseif param == keys.right then
player:seek(player:livePosition() + 5)
elseif param == keys.q then
player:stop()
return
end
progress(player:livePosition())
end
end)
loop:addTimer(0.25, function()
if player.playerTask == nil then return 0 end
progress(player:livePosition())
end)
loop:run(2)
sleep(0)