https://sudoroom.org/mediawiki/index.php?title=SudoPod&feed=atom&action=history
SudoPod - Revision history
2024-03-29T07:14:36Z
Revision history for this page on the wiki
MediaWiki 1.37.2
https://sudoroom.org/mediawiki/index.php?title=SudoPod&diff=4463&oldid=prev
Thex: Created page with "File:SudoPod-snap.jpgFile:SudoPod-web.jpg A basic streaming audio player with integrated API. <pre> package { import flash.events.Event; import flash.events.MouseE..."
2013-05-24T18:50:37Z
<p>Created page with "<a href="/wiki/File:SudoPod-snap.jpg" title="File:SudoPod-snap.jpg">File:SudoPod-snap.jpg</a><a href="/wiki/File:SudoPod-web.jpg" title="File:SudoPod-web.jpg">File:SudoPod-web.jpg</a> A basic streaming audio player with integrated API. <pre> package { import flash.events.Event; import flash.events.MouseE..."</p>
<p><b>New page</b></p><div>[[File:SudoPod-snap.jpg]][[File:SudoPod-web.jpg]]<br />
<br />
A basic streaming audio player with integrated API.<br />
<br />
<pre><br />
package<br />
{<br />
import flash.events.Event;<br />
import flash.events.MouseEvent;<br />
import flash.media.SoundTransform;<br />
import flash.display.Sprite;<br />
import flash.media.SoundMixer;<br />
import flash.display.MovieClip;<br />
import flash.net.URLRequest;<br />
import flash.media.Sound;<br />
import flash.events.ProgressEvent;<br />
import flash.media.SoundChannel;<br />
import flash.utils.ByteArray;<br />
import flash.display.Graphics;<br />
import flash.events.IOErrorEvent;<br />
import flash.display.NativeWindow;<br />
import flash.events.ServerSocketConnectEvent;<br />
import flash.net.Socket;<br />
import flash.events.OutputProgressEvent;<br />
import flash.net.ServerSocket;<br />
<br />
public class sudoPod extends MovieClip<br />
{<br />
public var volumeDial:Sprite;<br />
<br />
private var _volume:Number = 0.5;<br />
<br />
private var window:NativeWindow;<br />
<br />
private var audioPath:String = "http://sudoradio.com:8000/";<br />
private var sound:Sound;<br />
private var soundChannel:SoundChannel;<br />
<br />
private var playbackActive:Boolean;<br />
<br />
private var localhostSocket:ServerSocket;<br />
// TODO: Update to custom class using vectors<br />
private var localhostClients:Object;<br />
<br />
private var lastX:Number;<br />
<br />
public function sudoPod() <br />
{<br />
initUI();<br />
initSound();<br />
initLocalhost();<br />
}<br />
<br />
public function get volume():Number<br />
{<br />
return _volume;<br />
}<br />
<br />
public function set volume(level:Number):void<br />
{<br />
_volume = Math.max(0, Math.min(1, level));<br />
<br />
// Add SO for remembering last volume on restart<br />
// Add volume tween/fader<br />
<br />
var t:SoundTransform = SoundMixer.soundTransform;<br />
t.volume = _volume;<br />
SoundMixer.soundTransform = t;<br />
<br />
volumeDial.rotation = _volume * 120.0; <br />
}<br />
<br />
private function update():void<br />
{<br />
drawSpectrum();<br />
}<br />
<br />
private function initUI():void<br />
{<br />
volumeDial.rotation = _volume * 120.0;<br />
<br />
volumeDial.addEventListener(MouseEvent.MOUSE_DOWN, dialHandler);<br />
<br />
window = stage.nativeWindow;<br />
window.stage.addEventListener(MouseEvent.MOUSE_DOWN, downHandler);<br />
<br />
this.addEventListener(MouseEvent.MOUSE_WHEEL, wheelHandler);<br />
this.addEventListener(Event.ENTER_FRAME, frameHandler);<br />
}<br />
<br />
private function initSound():void<br />
{<br />
sound = new Sound();<br />
sound.addEventListener(Event.COMPLETE, completeHandler);<br />
sound.addEventListener(Event.ID3, id3Handler);<br />
sound.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);<br />
sound.addEventListener(ProgressEvent.PROGRESS, progressHandler);<br />
<br />
volume = _volume;<br />
<br />
playbackActive = true;<br />
loadSound(audioPath);<br />
}<br />
<br />
private function initLocalhost():void<br />
{<br />
localhostClients = new Object();<br />
<br />
localhostSocket = new ServerSocket();<br />
localhostSocket.addEventListener(Event.CONNECT, localhostConnect);<br />
localhostSocket.addEventListener(Event.CLOSE, localhostClose);<br />
localhostSocket.bind(21337, "0.0.0.0");<br />
localhostSocket.listen();<br />
}<br />
<br />
private function startPlayback():void<br />
{<br />
if (!playbackActive)<br />
{<br />
playbackActive = true;<br />
initSound();<br />
}<br />
}<br />
<br />
private function stopPlayback():void<br />
{<br />
if (playbackActive)<br />
{<br />
playbackActive = false;<br />
soundChannel.stop();<br />
<br />
sound.removeEventListener(Event.COMPLETE, completeHandler);<br />
sound.removeEventListener(Event.ID3, id3Handler);<br />
sound.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);<br />
sound.removeEventListener(ProgressEvent.PROGRESS, progressHandler);<br />
sound.close();<br />
}<br />
}<br />
<br />
private function loadSound(path:String):void<br />
{<br />
audioPath = path;<br />
sound.load(new URLRequest(audioPath));<br />
<br />
soundChannel = sound.play();<br />
}<br />
<br />
private function drawSpectrum():void<br />
{<br />
var bytes:ByteArray = new ByteArray();<br />
<br />
var g:Graphics = spectrumGraph.graphics;<br />
g.clear();<br />
<br />
SoundMixer.computeSpectrum(bytes, true, 4);<br />
renderBytes(g, [0x0066CC, 0x00CC66], bytes);<br />
<br />
SoundMixer.computeSpectrum(bytes, false, 0);<br />
renderBytes(g, [0x6600CC, 0xCC0066], bytes);<br />
}<br />
<br />
private function renderBytes(g:Graphics, c:Array, bytes:ByteArray):void<br />
{<br />
const PLOT_WIDTH:int = 200;<br />
const CHANNEL_LENGTH:int = 256;<br />
<br />
const L_OFF = -0;<br />
const R_OFF = 0;<br />
<br />
g.lineStyle(0, c[0], 0.8);<br />
g.beginFill(c[0], 0.5);<br />
g.moveTo(L_OFF, CHANNEL_LENGTH * 2);<br />
<br />
var n:Number = 0;<br />
<br />
for (var i:int = 0; i < CHANNEL_LENGTH; i++) {<br />
n = (bytes.readFloat() * PLOT_WIDTH);<br />
g.lineTo(-n + L_OFF, (CHANNEL_LENGTH * 2) - (i * 2));<br />
}<br />
<br />
g.lineTo(L_OFF, 0);<br />
g.endFill();<br />
<br />
<br />
g.lineStyle(0, c[1], 0.8);<br />
g.beginFill(c[1], 0.5);<br />
g.moveTo(R_OFF, CHANNEL_LENGTH * 2);<br />
<br />
for (i = CHANNEL_LENGTH; i > 0; i--) {<br />
n = (bytes.readFloat() * PLOT_WIDTH);<br />
g.lineTo(n + R_OFF, i * 2);<br />
}<br />
<br />
g.lineTo(R_OFF, 0);<br />
g.endFill();<br />
}<br />
<br />
private function frameHandler(e:Event):void<br />
{<br />
update();<br />
}<br />
<br />
private function downHandler(e:MouseEvent):void<br />
{<br />
window.startMove();<br />
}<br />
<br />
private function dialHandler(e:MouseEvent):void<br />
{<br />
e.stopPropagation();<br />
<br />
lastX = mouseX;<br />
<br />
volumeDial.addEventListener(MouseEvent.MOUSE_MOVE, dialMoveHandler);<br />
this.addEventListener(MouseEvent.MOUSE_UP, upHandler);<br />
}<br />
<br />
private function upHandler(e:MouseEvent):void<br />
{<br />
volumeDial.removeEventListener(MouseEvent.MOUSE_MOVE, dialMoveHandler);<br />
this.removeEventListener(MouseEvent.MOUSE_UP, upHandler);<br />
}<br />
<br />
private function dialMoveHandler(e:MouseEvent):void<br />
{<br />
volume += (mouseX - lastX) / 100.0;<br />
lastX = mouseX;<br />
}<br />
<br />
private function wheelHandler(e:MouseEvent):void<br />
{<br />
volume += e.delta / 100.0;<br />
}<br />
<br />
private function completeHandler(e:Event):void<br />
{<br />
trace("Complete:", e);<br />
}<br />
<br />
private function id3Handler(e:Event):void<br />
{<br />
trace("ID3:", e);<br />
}<br />
<br />
private function ioErrorHandler(e:Event):void<br />
{<br />
trace("IO Error:", e);<br />
}<br />
<br />
private function progressHandler(e:ProgressEvent):void<br />
{<br />
// trace("Progress:", e);<br />
/*<br />
bytesLoadedText.text = e.bytesLoaded.toString();<br />
bytesTotalText.text = e.bytesTotal.toString();<br />
<br />
var p:Number = e.bytesLoaded / e.bytesTotal;<br />
percentLoadedText.text = Math.round(p * 100.0).toString() + "%";<br />
*/<br />
}<br />
<br />
<br />
/////////////////////////////////////////////////////////////<br />
/////////////////// localhost server ////////////////////////<br />
/////////////////////////////////////////////////////////////<br />
<br />
private function localhostConnect(e:ServerSocketConnectEvent):void<br />
{<br />
var socket:Socket = e.socket as Socket;<br />
localhostClients[socket.remoteAddress + socket.remotePort] = socket;<br />
<br />
socket.addEventListener(ProgressEvent.SOCKET_DATA, localhostSocketData);<br />
socket.addEventListener(Event.CLOSE, localhostClientClose);<br />
socket.addEventListener(IOErrorEvent.IO_ERROR, localhostIOError);<br />
socket.addEventListener(OutputProgressEvent.OUTPUT_PROGRESS, localhostProgress);<br />
<br />
trace("Connect: " + socket.remoteAddress + ":" + socket.remotePort);<br />
}<br />
<br />
private function closeLocalhostSocket(socket:Socket):void<br />
{<br />
trace("Close: " + socket.remoteAddress + ":" + socket.remotePort);<br />
<br />
socket.removeEventListener(ProgressEvent.SOCKET_DATA, localhostSocketData);<br />
socket.removeEventListener(Event.CLOSE, localhostClientClose);<br />
socket.removeEventListener(IOErrorEvent.IO_ERROR, localhostIOError);<br />
socket.removeEventListener(OutputProgressEvent.OUTPUT_PROGRESS, localhostProgress);<br />
<br />
localhostClients[socket.remoteAddress + socket.remotePort] = null;<br />
<br />
if (socket.connected)<br />
socket.close();<br />
<br />
socket = null;<br />
}<br />
<br />
private function localhostProgress(e:OutputProgressEvent):void<br />
{<br />
if (e.bytesPending == 0)<br />
{<br />
var socket:Socket = e.target as Socket;<br />
closeLocalhostSocket(socket);<br />
}<br />
}<br />
<br />
private function genHTML():String<br />
{<br />
var html:String = "";<br />
html += "<html>\n"<br />
html += " <head>\n";<br />
html += " <title>sudoPod API</title>\n";<br />
html += " </head>\n";<br />
html += " <body>\n";<br />
html += " <h1>sudoPod</h1>\n";<br />
html += " <p>\n";<br />
html += " <button onclick='window.location = \"/volumeUp\"'>Volume +</button><br/><br/>\n";<br />
html += " Volume: " + Math.round(volume * 100.0).toString() + "%<br/><br/>\n";<br />
html += " <button onclick='window.location = \"/volumeDown\"'>Volume -</button>\n";<br />
html += " </p><br/<br/>\n";<br />
html += " <p>\n";<br />
html += " <button onclick='window.location = \"/start\"'>start</button>\n";<br />
html += " <button onclick='window.location = \"/stop\"'>stop</button>\n";<br />
html += " </p>\n";<br />
html += " <p>status</p>\n";<br />
html += " <p>Playing: " + (playbackActive ? "true" : "false") + "</p>\n";<br />
html += " <p>Path: " + audioPath + "</p>\n";<br />
html += " <p>Position: " + Math.round(soundChannel.position / 1000.0) + " seconds</p>\n";<br />
html += " <p>Volume: " + Math.round(volume * 100.0).toString() + "%</p>\n";<br />
html += " <p>\n";<br />
html += " <button onclick='window.location = \"/\"'>Refresh</button><br/>\n";<br />
html += " </p>\n";<br />
html += " </body>\n";<br />
html += "</html>";<br />
return html;<br />
}<br />
<br />
private function localhostSocketData(e:ProgressEvent):void<br />
{<br />
var socket:Socket = e.target as Socket;<br />
var msg:String = socket.readUTFBytes(socket.bytesAvailable);<br />
<br />
var html:String = "<!DOCTYPE html>\n";<br />
<br />
trace(socket.remoteAddress + ":" + socket.remotePort + "<" + msg.substr(0, msg.indexOf("\n")));<br />
<br />
// check for http GET /<br />
if (msg.substr(0, 11) == "GET /hello ")<br />
{<br />
html += "<html>\n<head>\n<title>Hello HTML</title>\n</head>\n<body>\n<p>sudoPod API!</p>\n</body>\n</html>";<br />
socket.writeUTFBytes(html);<br />
socket.flush();<br />
}<br />
else if (msg.substr(0, 6) == "GET / ")<br />
{<br />
html += genHTML();<br />
<br />
socket.writeUTFBytes(html);<br />
socket.flush();<br />
}<br />
else if (msg.substr(0, 11) == "GET /start ")<br />
{<br />
startPlayback();<br />
<br />
html += genHTML();<br />
<br />
socket.writeUTFBytes(html);<br />
socket.flush();<br />
}<br />
else if (msg.substr(0, 10) == "GET /stop ")<br />
{<br />
stopPlayback();<br />
<br />
html += genHTML();<br />
<br />
socket.writeUTFBytes(html);<br />
socket.flush();<br />
}<br />
else if (msg.substr(0, 14) == "GET /volumeUp ")<br />
{<br />
volume += 0.1;<br />
<br />
html += genHTML();<br />
<br />
socket.writeUTFBytes(html);<br />
socket.flush();<br />
}<br />
else if (msg.substr(0, 16) == "GET /volumeDown ")<br />
{<br />
volume -= 0.1;<br />
<br />
html += genHTML();<br />
<br />
socket.writeUTFBytes(html);<br />
socket.flush();<br />
}<br />
else if (msg.substr(0, 17) == "GET /favicon.ico ")<br />
{<br />
// Noisebridge icon (w/counterfit header)<br />
var bytes:Array = [0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, <br />
0x20, 0x33, 0x30, 0x32, 0x20, 0x46, 0x6f, 0x75, <br />
0x6e, 0x64, 0x0d, 0x0a, 0x44, 0x61, 0x74, 0x65, <br />
0x3a, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x30, <br />
0x39, 0x20, 0x41, 0x70, 0x72, 0x20, 0x32, 0x30, <br />
0x31, 0x33, 0x20, 0x30, 0x36, 0x3a, 0x30, 0x30, <br />
0x3a, 0x33, 0x38, 0x20, 0x47, 0x4d, 0x54, 0x0d, <br />
0x0a, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3a, <br />
0x20, 0x41, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2f, <br />
0x32, 0x2e, 0x32, 0x2e, 0x32, 0x32, 0x20, 0x28, <br />
0x55, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x29, 0x0d, <br />
0x0a, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, <br />
0x6e, 0x3a, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, <br />
0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6e, <br />
0x6f, 0x69, 0x73, 0x65, 0x62, 0x72, 0x69, 0x64, <br />
0x67, 0x65, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x66, <br />
0x61, 0x76, 0x69, 0x63, 0x6f, 0x6e, 0x2e, 0x69, <br />
0x63, 0x6f, 0x0d, 0x0a, 0x56, 0x61, 0x72, 0x79, <br />
0x3a, 0x20, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, <br />
0x2d, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, <br />
0x67, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x65, <br />
0x6e, 0x74, 0x2d, 0x45, 0x6e, 0x63, 0x6f, 0x64, <br />
0x69, 0x6e, 0x67, 0x3a, 0x20, 0x67, 0x7a, 0x69, <br />
0x70, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x65, <br />
0x6e, 0x74, 0x2d, 0x4c, 0x65, 0x6e, 0x67, 0x74, <br />
0x68, 0x3a, 0x20, 0x31, 0x39, 0x37, 0x0d, 0x0a, <br />
0x4b, 0x65, 0x65, 0x70, 0x2d, 0x41, 0x6c, 0x69, <br />
0x76, 0x65, 0x3a, 0x20, 0x74, 0x69, 0x6d, 0x65, <br />
0x6f, 0x75, 0x74, 0x3d, 0x35, 0x2c, 0x20, 0x6d, <br />
0x61, 0x78, 0x3d, 0x31, 0x30, 0x30, 0x0d, 0x0a, <br />
0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, <br />
0x6f, 0x6e, 0x3a, 0x20, 0x4b, 0x65, 0x65, 0x70, <br />
0x2d, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x0d, 0x0a, <br />
0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, <br />
0x54, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x74, 0x65, <br />
0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3b, <br />
0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, <br />
0x3d, 0x69, 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, <br />
0x39, 0x2d, 0x31, 0x0d, 0x0a, 0x0d, 0x0a, 0x1f, <br />
0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, <br />
0x03, 0x2d, 0x8e, 0x3b, 0x0f, 0xc2, 0x30, 0x0c, <br />
0x84, 0xf7, 0xfe, 0x0a, 0xd3, 0x9d, 0x98, 0xc7, <br />
0x86, 0x4c, 0x06, 0x68, 0x11, 0x48, 0x3c, 0x3a, <br />
0x84, 0x81, 0x31, 0x10, 0x43, 0x2a, 0xd1, 0xa4, <br />
0x6a, 0xd3, 0x56, 0xfc, 0x7b, 0xda, 0xc2, 0x72, <br />
0xb2, 0xef, 0x6c, 0x7d, 0x47, 0x93, 0xe4, 0xb2, <br />
0x55, 0xb7, 0x2c, 0x85, 0xbd, 0x3a, 0x1d, 0x21, <br />
0xbb, 0x6e, 0x8e, 0x87, 0x2d, 0xc4, 0x53, 0xc4, <br />
0x43, 0xaa, 0x76, 0x88, 0x89, 0x4a, 0x7e, 0xc9, <br />
0x42, 0xcc, 0x10, 0xd3, 0x73, 0x2c, 0x23, 0xb2, <br />
0xa1, 0x78, 0x4b, 0xb2, 0xac, 0x4d, 0xbf, 0x84, <br />
0x3c, 0xbc, 0x59, 0x2e, 0x67, 0x0b, 0xd8, 0xf9, <br />
0xc6, 0x19, 0xc2, 0x9f, 0x11, 0x11, 0x8e, 0x07, <br />
0x74, 0xf7, 0xe6, 0x33, 0xfc, 0xcc, 0xe5, 0x3f, <br />
0xef, 0xa7, 0x88, 0x4a, 0xa9, 0x2c, 0x83, 0xf1, <br />
0x8f, 0xa6, 0x60, 0x17, 0xc0, 0xea, 0x1a, 0x0a, <br />
0xdf, 0xb2, 0x01, 0xd2, 0x60, 0x2b, 0x7e, 0xae, <br />
0x63, 0x1b, 0x42, 0x59, 0xaf, 0x10, 0xbb, 0xae, <br />
0x13, 0xce, 0xe7, 0x35, 0xdf, 0xab, 0xdc, 0xbc, <br />
0x58, 0x38, 0x0e, 0xf8, 0xd4, 0x6d, 0xfe, 0xf0, <br />
0x4e, 0xf4, 0x12, 0x4b, 0xcb, 0x15, 0x13, 0x6a, <br />
0x29, 0x08, 0xcb, 0x01, 0x3b, 0x02, 0x7b, 0xcc, <br />
0x50, 0x32, 0xfa, 0x02, 0x4c, 0x97, 0xdc, 0x16, <br />
0xdf, 0x00, 0x00, 0x00];<br />
for (var i:int = 0; i < bytes.length; i++)<br />
{<br />
socket.writeByte(bytes[i]);<br />
}<br />
socket.flush();<br />
}<br />
else<br />
{<br />
closeLocalhostSocket(socket);<br />
}<br />
}<br />
<br />
private function localhostClientClose(e:Event):void<br />
{<br />
var socket:Socket = e.target as Socket;<br />
closeLocalhostSocket(socket);<br />
}<br />
<br />
private function localhostIOError(e:IOErrorEvent):void<br />
{<br />
// TODO: Add error handling and recover<br />
trace("ERROR: " + e.text);<br />
}<br />
<br />
private function localhostClose(e:Event):void<br />
{<br />
// TODO: Add error handling and recover<br />
trace("localhost Server socket closed.");<br />
}<br />
} <br />
}<br />
</pre></div>
Thex