The python program referred to in 5(c) above is as follows:
Code:
--------------------
#!/usr/bin/python
import BaseHTTPServer
import SocketServer
import subprocess # For running flac, sox
or whatever
import sys # For writing to stderr
import os # For environment
variables
import errno
import socket
HOST = os.environ.get('HOST') # The server host. Can
also be set to ""
PORT = int(os.environ.get('VBPORT')) # An http port
BITRATE = int(os.environ.get('BITRATE')) # Bitrate 44100, 48000,
96000 or 192000
DEPTH = int(os.environ.get('DEPTH')) # A valid depth - 16,
24 or 32
COMPRESSION = os.environ.get('COMPRESSION') # Set to 0 to 8
(least compression to most) for flac
FORMAT = os.environ.get('FORMAT') # Format must be FLAC
or WAV
FLACPROG = os.environ.get('FLACPROG') # FLAC program - flac
or sox
WAVPROG = os.environ.get('WAVPROG') # WAV program - sox
FLACMIMETYPE = 'audio/flac'
WAVMIMETYPE = 'audio/x-wav'
BUFFER = 65536
class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
##################################################################################################################
# These next two functions, finish and handle_one_request, are cribbed
directly from the /usr/lib/python libraries,
# overriding the originals to suppress the broken pipe trace.
# If I'm finishing, an I/O error is of no interest.
def finish(self,*args,**kw):
if not self.wfile.closed:
try:
self.wfile.flush()
self.wfile.close()
self.rfile.close()
except socket.error:
pass
return
# At the end of the request a broken pipe error is not abnormal
def handle_one_request(self):
"""Handle a single HTTP request.
You normally don't need to override this method; see the class
__doc__ string for information on how to handle specific HTTP
commands such as GET and POST.
"""
try:
self.raw_requestline = self.rfile.readline(65537)
if len(self.raw_requestline) > 65536:
self.requestline = ''
self.request_version = ''
self.command = ''
self.send_error(414)
return
if not self.raw_requestline:
self.close_connection = 1
return
if not self.parse_request():
# An error code has been sent, just exit
return
mname = 'do_' + self.command
if not hasattr(self, mname):
self.send_error(501, "Unsupported method (%r)" % self.command)
return
method = getattr(self, mname)
method()
self.wfile.flush() #actually send the response if not already done.
except socket.timeout, e:
#a read or a write timed out. Discard this connection
self.log_error("Request timed out: %r", e)
self.close_connection = 1
return
except socket.error, e: # For a broken pipe error
(32), ignore it.
#socket error
if e[0] != errno.EPIPE:
self.log_error("I/O Error: %r", e)
self.close_connection = 1
return
##################################################################################################################
# Function to create and send the headers.
def do_HEAD(s):
s.send_response(200) # OK
if FORMAT == "FLAC": # A Flac or WAV audio format
MIMETYPE = FLACMIMETYPE
else:
MIMETYPE = WAVMIMETYPE
s.send_header('Content-Type', MIMETYPE) # Wav/flac
# We don't, and can't know the length, so sending a Content-Length header
won't work.
# I've used 'chunked' instead, which seems to work (as does no content lenth
and not chunked)
s.send_header('Transfer-Encoding', 'chunked') # Chunked
s.end_headers()
return
# Function to get data from stdin (squeezelite) and send it out to the
renderer.
def do_GET(s):
s.do_HEAD() # Send the headers
# Use flac or sox to transform raw data to flac or wav, using the environment
variables for bitrate and depth (bits) and compression.
# All input is raw from stdin, and out to stdout (default for -c). Warnings
are errors in flac. No info/error messages from either.
# I've left (commented out) an option to use sox for flac instead of the flac
program should that be of interest.
if FORMAT == "FLAC":
if FLACPROG == WAVPROG:
engine = str(WAVPROG) + ' -V0 -t raw -r ' + str(BITRATE) + ' -b ' +
str(DEPTH) + ' -L -e signed -c 2 - -t flac -C ' + str(COMPRESSION) + ' - '
else:
engine = str(FLACPROG) + ' - -w --totally-silent --force-raw-format
--channels 2 --bps ' + str(DEPTH) + ' --sample-rate ' + str(BITRATE) + '
--endian little --sign signed -F -' + str(COMPRESSION)
else:
engine = str(WAVPROG) + ' -V0 -t raw -r ' + str(BITRATE) + ' -b ' +
str(DEPTH) + ' -L -e signed -c 2 - -t wav - '
pa = subprocess.Popen (engine, shell = True, bufsize = BUFFER, stdout =
subprocess.PIPE)
# Loop until no data or an error, reading and writing. The writes are in
'chunks'.
# The format is length (%X), crlf (\r\n), Data, crlf.
# I used to do shorter reads and hence writes (1024), but have settled on
16384 as a reasonable amount to
# keep the load from the loop down - at CD quality it's around 0.1 secs (I
think!).
try:
while True:
data = pa.stdout.read(16384)
if len(data) == 0: break
s.wfile.write('%X\r\n%s\r\n'%(len(data), data))
# Never get here since I never seem to get EOF. Never mind. Write a pukka
end chunk.
# The trailer is said to be optional, so don't send one.
s.wfile.write('0\r\n\r\n')
sys.stderr.write(str('No more data') + '\n')
# Any error is bad news for real time data
except socket.error, e:
if e[0] == errno.EPIPE: # error 32
sys.stderr.write (str('The Renderer disconnected') + '\n')
else:
sys.stderr.write (str('Write error - ') + str(e) + '\n')
pass
# Any other error
except:
sys.stderr.write(str('Exception whilst reading and writing audio stream') +
'\n')
pass
# Done. Report and kill off the subprocess.
finally:
pa.kill()
return
# Execution starts here....
# Allow the port to be re-used. If there's a failure but the client keeps
the port open, restarting the server can fail
# unless this is set.
SocketServer.TCPServer.allow_reuse_address = True
# Define the server, using the above host and port, executing the Handler
class
httpd = SocketServer.TCPServer((HOST, PORT), Handler)
sys.stderr.write(str('Listening on port ') + str(PORT) + '\n')
# Log which method is being use.
if FORMAT == "FLAC":
sys.stderr.write(str('Sending flac format') + '\n')
else:
sys.stderr.write(str('Sending wav format') + '\n')
# And now handle requests one by one. When one request finishes, usually with
an error 32 (Broken pipe), the request finishes.
# I could use serve_forever instead.
while True:
try:
httpd.handle_request()
# Close if run from terminal and closed, or a sigkill/term is received.
except (KeyboardInterrupt, SystemExit):
break
except:
sys.stderr.write(str('error on request') + '\n')
pass
# Something odd is going on. I never see the following, but it does stop on
a SIGKILL.
sys.stderr.write(str('Exit') + '\n')
httpd.server_close()
--------------------
LMS 7.9 on VortexBox Midi running Xubuntu 14.04, FLACs 16->24 bit,
44.1->192kbps. Wired Touch + EDO, coax to Musical Fidelity M1 CLiC.
Wireless Xubuntu 14.04 laptop controls LMS via Chromium. Meridian
Explorer USB DAC to listen via Squeezelite on Vortexbox & other PCs as
required. Spare Touch in loft.
------------------------------------------------------------------------
PasTim's Profile: http://forums.slimdevices.com/member.php?userid=41642
View this thread: http://forums.slimdevices.com/showthread.php?t=101721
_______________________________________________
plugins mailing list
[email protected]
http://lists.slimdevices.com/mailman/listinfo/plugins