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

Reply via email to