Advertisement
DOGGYWOOF

Untitled

Nov 3rd, 2024
4
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.58 KB | None | 0 0
  1. -- SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
  2. --
  3. -- SPDX-License-Identifier: MPL-2.0
  4.  
  5. local function get_speakers(name)
  6. if name then
  7. local speaker = peripheral.wrap(name)
  8. if speaker == nil then
  9. error(("Speaker %q does not exist"):format(name), 0)
  10. return
  11. elseif not peripheral.hasType(name, "speaker") then
  12. error(("%q is not a speaker"):format(name), 0)
  13. end
  14.  
  15. return { speaker }
  16. else
  17. local speakers = { peripheral.find("speaker") }
  18. if #speakers == 0 then
  19. error("No speakers attached", 0)
  20. end
  21. return speakers
  22. end
  23. end
  24.  
  25. local function pcm_decoder(chunk)
  26. local buffer = {}
  27. for i = 1, #chunk do
  28. buffer[i] = chunk:byte(i) - 128
  29. end
  30. return buffer
  31. end
  32.  
  33. local function report_invalid_format(format)
  34. printError(("speaker cannot play %s files."):format(format))
  35. local pp = require "cc.pretty"
  36. pp.print("Run '" .. pp.text("help speaker", colours.lightGrey) .. "' for information on supported formats.")
  37. end
  38.  
  39.  
  40. local cmd = ...
  41. if cmd == "stop" then
  42. local _, name = ...
  43. for _, speaker in pairs(get_speakers(name)) do speaker.stop() end
  44. elseif cmd == "play" then
  45. local _, file, name = ...
  46. if not file then
  47. error("Usage: speaker play <file or url> [speaker]", 0)
  48. end
  49.  
  50. local speaker = get_speakers(name)[1]
  51.  
  52. local handle, err
  53. if http and file:match("^https?://") then
  54. print("Downloading...")
  55. handle, err = http.get(file)
  56. else
  57. handle, err = fs.open(shell.resolve(file), "r")
  58. end
  59.  
  60. if not handle then
  61. printError("Could not play audio:")
  62. error(err, 0)
  63. end
  64.  
  65. local start = handle.read(4)
  66. local pcm = false
  67. local size = 16 * 1024 - 4
  68. if start == "RIFF" then
  69. handle.read(4)
  70. if handle.read(8) ~= "WAVEfmt " then
  71. handle.close()
  72. error("Could not play audio: Unsupported WAV file", 0)
  73. end
  74.  
  75. local fmtsize = ("<I4"):unpack(handle.read(4))
  76. local fmt = handle.read(fmtsize)
  77. local format, channels, rate, _, _, bits = ("<I2I2I4I4I2I2"):unpack(fmt)
  78. if not ((format == 1 and bits == 8) or (format == 0xFFFE and bits == 1)) then
  79. handle.close()
  80. error("Could not play audio: Unsupported WAV file", 0)
  81. end
  82. if channels ~= 1 or rate ~= 48000 then
  83. print("Warning: Only 48 kHz mono WAV files are supported. This file may not play correctly.")
  84. end
  85. if format == 0xFFFE then
  86. local guid = fmt:sub(25)
  87. if guid ~= "\x3A\xC1\xFA\x38\x81\x1D\x43\x61\xA4\x0D\xCE\x53\xCA\x60\x7C\xD1" then -- DFPWM format GUID
  88. handle.close()
  89. error("Could not play audio: Unsupported WAV file", 0)
  90. end
  91. size = size + 4
  92. else
  93. pcm = true
  94. size = 16 * 1024 * 8
  95. end
  96.  
  97. repeat
  98. local chunk = handle.read(4)
  99. if chunk == nil then
  100. handle.close()
  101. error("Could not play audio: Invalid WAV file", 0)
  102. elseif chunk ~= "data" then -- Ignore extra chunks
  103. local size = ("<I4"):unpack(handle.read(4))
  104. handle.read(size)
  105. end
  106. until chunk == "data"
  107.  
  108. handle.read(4)
  109. start = nil
  110. -- Detect several other common audio files.
  111. elseif start == "OggS" then return report_invalid_format("Ogg")
  112. elseif start == "fLaC" then return report_invalid_format("FLAC")
  113. elseif start:sub(1, 3) == "ID3" then return report_invalid_format("MP3")
  114. elseif start == "<!DO" --[[<!DOCTYPE]] then return report_invalid_format("HTML")
  115. end
  116.  
  117. print("Playing " .. file)
  118.  
  119. local decoder = pcm and pcm_decoder or require "cc.audio.dfpwm".make_decoder()
  120. while true do
  121. local chunk = handle.read(size)
  122. if not chunk then break end
  123. if start then
  124. chunk, start = start .. chunk, nil
  125. size = size + 4
  126. end
  127.  
  128. local buffer = decoder(chunk)
  129. while not speaker.playAudio(buffer) do
  130. os.pullEvent("speaker_audio_empty")
  131. end
  132. end
  133.  
  134. handle.close()
  135. elseif cmd == "sound" then
  136. local _, sound, volume, pitch, name = ...
  137.  
  138. if not sound then
  139. error("Usage: speaker sound <sound> [volume] [pitch] [speaker]", 0)
  140. return
  141. end
  142.  
  143. if volume then
  144. volume = tonumber(volume)
  145. if not volume then
  146. error("Volume must be a number", 0)
  147. end
  148. if volume < 0 or volume > 3 then
  149. error("Volume must be between 0 and 3", 0)
  150. end
  151. end
  152.  
  153. if pitch then
  154. pitch = tonumber(pitch)
  155. if not pitch then
  156. error("Pitch must be a number", 0)
  157. end
  158. if pitch < 0 or pitch > 2 then
  159. error("Pitch must be between 0 and 2", 0)
  160. end
  161. end
  162.  
  163. local speaker = get_speakers(name)[1]
  164.  
  165. if speaker.playSound(sound, volume, pitch) then
  166. print(("Played sound %q on speaker %q with volume %s and pitch %s."):format(
  167. sound, peripheral.getName(speaker), volume or 1, pitch or 1
  168. ))
  169. else
  170. error(("Could not play sound %q"):format(sound), 0)
  171. end
  172. else
  173. local programName = arg[0] or fs.getName(shell.getRunningProgram())
  174. print("Usage:")
  175. print(programName .. " play <file or url> [speaker]")
  176. print(programName .. " sound <sound> [volume] [pitch] [speaker]")
  177. print(programName .. " stop [speaker]")
  178. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement