Commits

clee  committed 3a50d03 Draft

work on stream player--not working yet

  • Participants
  • Parent commits 5a4d54a

Comments (0)

Files changed (6)

 syntax: glob
 *~
-*#
+*\#

File stream_play/rfc2217_server.py

+#!/usr/bin/env python
+
+# (C) 2009 Chris Liechti <cliechti@gmx.net>
+# redirect data from a TCP/IP connection to a serial port and vice versa
+# using RFC 2217
+
+
+import sys
+import os
+import threading
+import time
+import socket
+import serial
+import serial.rfc2217
+import logging
+
+class Redirector:
+    def __init__(self, serial_instance, socket, debug=None):
+        self.serial = serial_instance
+        self.socket = socket
+        self._write_lock = threading.Lock()
+        self.rfc2217 = serial.rfc2217.PortManager(
+            self.serial,
+            self,
+            logger = (debug and logging.getLogger('rfc2217.server'))
+            )
+        self.log = logging.getLogger('redirector')
+
+    def statusline_poller(self):
+        self.log.debug('status line poll thread started')
+        while self.alive:
+            time.sleep(1)
+            self.rfc2217.check_modem_lines()
+        self.log.debug('status line poll thread terminated')
+
+    def shortcut(self):
+        """connect the serial port to the TCP port by copying everything
+           from one side to the other"""
+        self.alive = True
+        self.thread_read = threading.Thread(target=self.reader)
+        self.thread_read.setDaemon(True)
+        self.thread_read.setName('serial->socket')
+        self.thread_read.start()
+        self.thread_poll = threading.Thread(target=self.statusline_poller)
+        self.thread_poll.setDaemon(True)
+        self.thread_poll.setName('status line poll')
+        self.thread_poll.start()
+        self.writer()
+
+    def reader(self):
+        """loop forever and copy serial->socket"""
+        self.log.debug('reader thread started')
+        while self.alive:
+            try:
+                data = self.serial.read(1)              # read one, blocking
+                n = self.serial.inWaiting()             # look if there is more
+                if n:
+                    data = data + self.serial.read(n)   # and get as much as possible
+                if data:
+                    # escape outgoing data when needed (Telnet IAC (0xff) character)
+                    data = serial.to_bytes(self.rfc2217.escape(data))
+                    self._write_lock.acquire()
+                    try:
+                        self.socket.sendall(data)       # send it over TCP
+                    finally:
+                        self._write_lock.release()
+            except socket.error, msg:
+                self.log.error('%s' % (msg,))
+                # probably got disconnected
+                break
+        self.alive = False
+        self.log.debug('reader thread terminated')
+
+    def write(self, data):
+        """thread safe socket write with no data escaping. used to send telnet stuff"""
+        self._write_lock.acquire()
+        try:
+            self.socket.sendall(data)
+        finally:
+            self._write_lock.release()
+
+    def writer(self):
+        """loop forever and copy socket->serial"""
+        while self.alive:
+            try:
+                data = self.socket.recv(1024)
+                if not data:
+                    break
+                self.serial.write(serial.to_bytes(self.rfc2217.filter(data)))
+            except socket.error, msg:
+                self.log.error('%s' % (msg,))
+                # probably got disconnected
+                break
+        self.stop()
+
+    def stop(self):
+        """Stop copying"""
+        self.log.debug('stopping')
+        if self.alive:
+            self.alive = False
+            self.thread_read.join()
+            self.thread_poll.join()
+
+
+if __name__ == '__main__':
+    import optparse
+
+    parser = optparse.OptionParser(
+        usage = "%prog [options] port",
+        description = "RFC 2217 Serial to Network (TCP/IP) redirector.",
+        epilog = """\
+NOTE: no security measures are implemented. Anyone can remotely connect
+to this service over the network.
+
+Only one connection at once is supported. When the connection is terminated
+it waits for the next connect.
+""")
+
+    parser.add_option("-p", "--localport",
+        dest = "local_port",
+        action = "store",
+        type = 'int',
+        help = "local TCP port",
+        default = 2217
+    )
+
+    parser.add_option("-v", "--verbose",
+        dest = "verbosity",
+        action = "count",
+        help = "print more diagnostic messages (option can be given multiple times)",
+        default = 0
+    )
+
+    (options, args) = parser.parse_args()
+
+    if len(args) != 2:
+        parser.error('<serial port name> and <wavefile name> are required as arguments')
+
+    if options.verbosity > 3:
+        options.verbosity = 3
+    level = (
+        logging.WARNING,
+        logging.INFO,
+        logging.DEBUG,
+        logging.NOTSET,
+        )[options.verbosity]
+    logging.basicConfig(level=logging.INFO)
+    logging.getLogger('root').setLevel(logging.INFO)
+    logging.getLogger('rfc2217').setLevel(level)
+
+    # connect to serial port
+    ser = serial.Serial()
+    ser.port     = args[0]
+    ser.timeout  = 3     # required so that the reader thread can exit
+
+    logging.info("RFC 2217 TCP/IP to Serial redirector - type Ctrl-C / BREAK to quit")
+
+    try:
+        ser.open()
+    except serial.SerialException, e:
+        logging.error("Could not open serial port %s: %s" % (ser.portstr, e))
+        sys.exit(1)
+
+    logging.info("Serving serial port: %s" % (ser.portstr,))
+    settings = ser.getSettingsDict()
+    # reset control line as no _remote_ "terminal" has been connected yet
+    ser.setDTR(False)
+    ser.setRTS(False)
+
+    srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    srv.bind( ('', options.local_port) )
+    srv.listen(1)
+    logging.info("TCP/IP port: %s" % (options.local_port,))
+    while True:
+        try:
+            connection, addr = srv.accept()
+            logging.info('Connected by %s:%s' % (addr[0], addr[1]))
+            connection.setsockopt( socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
+            ser.setRTS(True)
+            ser.setDTR(True)
+            # enter network <-> serial loop
+            r = Redirector(
+                ser,
+                connection,
+                options.verbosity > 0
+            )
+            try:
+                r.shortcut()
+            finally:
+                logging.info('Disconnected')
+                r.stop()
+                connection.close()
+                ser.setDTR(False)
+                ser.setRTS(False)
+            # Restore port settings (may have been changed by RFC 2217 capable
+            # client)
+            ser.applySettingsDict(settings)
+        except KeyboardInterrupt:
+            break
+        except socket.error, msg:
+            logging.error('%s' % (msg,))
+
+    logging.info('--- exit ---')

File stream_play/stream_play.pde

+/* 
+  stream_play.pde (MAPLE + AudioCodec Board) test sketch
+
+  Additional it shows how to use Serial3 USART to communicate with the board during
+  processing.
+
+  Hardware connections
+  --------------------
+  From the the maple documentaiton the USART Pin Map
+  Tx     Rx      Ck      cts     rts
+  Serial3	D29	D30	D31	D32	D33
+  (this also applies to the Maple RET6 edition)
+  These pins are also listed as being ***5V tolerant***
+
+
+  I chose Serial3 because these pins are on the 2x8 header "EXT" block which aren't
+  connected to the audio codec shield.
+
+
+  So I connected 3 wires to my FTDI basic board:
+  D29 (Tx) to the Rx on the FTDI basic board
+  D30 (Rx) to the Tx on the FTDI basic board
+  GND to GND on on the FTDI basic board
+*/
+
+// setup codec parameters
+// must be done before #includes
+// see readme file in libraries folder for explanations
+#define SAMPLE_RATE 44 // 44.1kHz sample rate
+#define ADCS 2 // use both ADCs
+
+// try faster 9600, 19200, 14400, 38400, 57600, 56000,
+// 115200, 128000, 230400,460800, 153600, 256000,921600
+// 2000000, 2500000
+// the FTDI 232R datasheet says it can go to 3 Mbaud
+// the stm32F103 data sheet mentioned a minimum of 2.25 Mbits/s or one at 4 Mbits/s
+
+// #define BAUDRATE 9600    // works, safe
+// #define BAUDRATE 921600  // works
+// #define BAUDRATE 1500000 // works
+// #define BAUDRATE  1000000   // works
+#define BAUDRATE  2000000   // works
+      // #define BAUDRATE  3000000   // doesn't work (as expected)
+// #define BAUDRATE 115200
+   
+// include necessary libraries
+// note the Maple library
+#include <AudioCodec_Maple.h>
+
+
+
+#define PBUFSIZE  1024
+#define NPBUFS 2
+
+
+#define BUFFER_NO_DATA 0
+// #define BUFFER_OLD_DATA 1
+#define BUFFER_FRESH_DATA 2
+
+int16 _buf0[PBUFSIZE];
+int16 _buf1[PBUFSIZE];
+
+class Buffer {
+public:
+  int16* buf;
+  uint8 status;
+  int16 rpos; // read position
+  Buffer();
+  int16 readnext(void);
+  bool atend(void) { return (rpos == PBUFSIZE ? 1 : 0); };
+  void serial_request_data(void); // through this in to get it to compile
+};
+
+Buffer::Buffer(void)
+{
+  status = BUFFER_NO_DATA;
+  rpos = 0;
+}
+
+inline int16 Buffer::readnext(void)
+{
+  return buf[rpos++];
+}
+
+
+void Buffer::serial_request_data(void)
+{
+  // request packet of data from server
+
+  uint16 lsb,msb;
+  
+  Serial3.write('U');
+
+  for (int ii =0; ii < PBUFSIZE; ii++) {
+    while(!Serial3.available())
+      ;
+    lsb=Serial3.read();
+    while(!Serial3.available())
+      ;
+    msb=Serial3.read();
+    buf[ii] = (msb << 8) + lsb;
+  }
+  rpos = 0;
+  status = BUFFER_FRESH_DATA;
+}
+
+
+/***************** global variables *******************/
+
+int gPlaySound = 0;
+int gCount =0;
+
+// create data variables for audio transfer
+// even though there is no input needed, the codec requires stereo data
+// note the uint16 typedef, there is no "unsigned int16" type
+int16 left_in = 0; // in from codec (LINE_IN)
+int16 right_in = 0;
+int16 left_out = 0; // out to codec (HP_OUT)
+int16 right_out = 0;
+
+
+// create variables for ADC results
+// it only has positive values -> unsigned
+uint16 mod0_value = 0;
+uint16 mod1_value = 0;
+uint16 gCurBufNum=0;
+
+struct Buffer gBuffers[NPBUFS];
+
+
+void setup()
+{
+  SerialUSB.end(); // usb conflicts with the codec in this mode
+  // a different operating mode which allows both
+  // is in the works
+  Serial3.begin(BAUDRATE);
+  gCurBufNum = 0;
+  gBuffers[0].status = 0;
+  gBuffers[0].rpos = 0;
+  gBuffers[0].buf = _buf0;
+ 
+  gBuffers[1].status = 0;
+  gBuffers[1].rpos = 0;
+  gBuffers[0].buf = _buf1;
+  // setup codec and microcontroller registers
+  AudioCodec_init(); // call this last if you are setting up other things
+}
+
+
+void serial_term(void)
+{
+  uint8 notcurbuf_num = gCurBufNum^0x1; // probably more efficient to do this with xor
+
+  if (Serial3.available()) {
+    uint8 input = Serial3.read();
+    switch(input) {
+
+    case 'h':
+      Serial3.println("Hello!");
+      break;
+
+    case 's': // stop
+      gPlaySound = 0;
+      break;
+
+    case 'p': 
+      {
+	gPlaySound = 1;
+      }
+      break;
+
+    case 'w': // wip
+      gBuffers[0].status = BUFFER_NO_DATA;
+      gBuffers[1].status = BUFFER_NO_DATA;
+      break;
+
+    case 't':
+      gBuffers[0].serial_request_data();
+      break;
+
+    case '1':
+      gBuffers[1].serial_request_data();
+      break;
+      
+    case 'd': //debug
+      Serial3.print("debug::");
+      Serial3.println(gCount);
+      Serial3.print("gCurBufNum::");
+      Serial3.println(gCurBufNum);
+      Serial3.print("gPlaySound::");
+      Serial3.println(gPlaySound);
+      Serial3.print("gBuffers[0]::status,rpos");
+      Serial3.print(gBuffers[0].status);
+      Serial3.println(gBuffers[0].rpos);
+      Serial3.print("gBuffers[1]::status,rpos");
+      Serial3.print(gBuffers[1].status);
+      Serial3.println(gBuffers[1].rpos);
+      Serial3.print("mod0_value::");
+      Serial3.println(mod0_value);
+      break;
+	
+      
+    default: // -------------------------------
+      // do nothing but really something is wrong!
+      Serial3.write('e'); // signal error, unrecognized byte read
+    }
+  }
+
+  if (!gPlaySound) {
+    return;
+  }
+  
+  // request data
+  if (!(gBuffers[gCurBufNum].status == BUFFER_FRESH_DATA)) {
+    gBuffers[gCurBufNum].serial_request_data();
+  }
+
+  if (!(gBuffers[notcurbuf_num].status == BUFFER_FRESH_DATA)) {
+    gBuffers[notcurbuf_num].serial_request_data();
+  }
+  
+}
+
+
+void loop() {
+  
+  while (1) { // reduces clock jitter (? maybe because no function call?)
+    // delay(1);
+    serial_term();
+    gCount++;
+  }
+}
+
+// timer1 interrupt routine - all data processed here
+void AudioCodec_interrupt() {
+  Buffer* curbuf = &(gBuffers[gCurBufNum]);
+  int16 * buf = curbuf->buf;
+  // &'s are necessary on data_in variables
+
+  AudioCodec_data(&left_in, &right_in, left_out, right_out);
+
+  if (gPlaySound && (curbuf->status == BUFFER_FRESH_DATA)) {
+      // create some temporary variables
+      // these tend to work faster than using the main data variables
+      // as they arent fetched and stored all the time
+    int16 temp1;
+  
+    // create a variable frequency and amplitude sinewave
+    // fetch a sample from the lookup table
+    // temp1 = curbuf->readnext();  // advances rpos
+    temp1 = *(buf+curbuf->rpos);
+    curbuf->rpos++;
+    // step through table at rate determined by mod1
+    // use upper byte of mod1 value to set the rate
+    // and have an offset of 1 so there is always an increment.
+      // if weve gone over the table boundary -> loop back
+      // around to the other side.
+    if (curbuf->rpos >= PBUFSIZE ) {
+      curbuf->status = BUFFER_NO_DATA;
+      curbuf->rpos=0;
+      // flip the current buffer number and start reading from the other buffer
+      gCurBufNum = (gCurBufNum ? 0 : 1);
+    }
+  
+    // set amplitude with mod0
+    // multiply our sinewave by the mod0 value
+    // since it returns a 32bit value it needs to be scaled with ">> 16"
+    // temp1 = (temp1 * mod0_value) >> 16;
+    left_out = temp1; // put sinusoid out on left channel
+    right_out = -temp1; // put inverted version out on right chanel
+
+    // get ADC values
+    // & is required before adc variables
+  }
+  AudioCodec_ADC(&mod0_value, &mod1_value);
+
+
+  // you dont need to reti() with Maple
+}
+

File stream_play/testclient.py

+# FTDI basic client
+# ser.getSupportedBaudrates()
+# Out[116]: 
+# [('50', 50),
+#  ('75', 75),
+#  ('110', 110),
+#  ('134', 134),
+#  ('150', 150),
+#  ('200', 200),
+#  ('300', 300),
+#  ('600', 600),
+#  ('1200', 1200),
+#  ('1800', 1800),
+#  ('2400', 2400),
+#  ('4800', 4800),
+#  ('9600', 9600),
+#  ('19200', 19200),
+#  ('38400', 38400),
+#  ('57600', 57600),
+#  ('115200', 115200),
+#  ('230400', 230400),
+#  ('460800', 460800),
+#  ('500000', 500000),
+#  ('576000', 576000),
+#  ('921600', 921600),
+#  ('1000000', 1000000),
+#  ('1152000', 1152000),
+#  ('1500000', 1500000),
+#  ('2000000', 2000000),
+#  ('2500000', 2500000),
+#  ('3000000', 3000000),
+#  ('3500000', 3500000),
+#  ('4000000', 4000000)]
+
+
+import serial, time
+import numpy as np
+from glob import glob
+# ls /dev/ttyUSB*  # to see what ports are available
+# ser = serial.Serial(r'/dev/ttyUSB2', 9600)   # safe, works
+# ser = serial.Serial(r'/dev/ttyUSB2', 921600) # works
+
+BAUDRATE =  115200 # works
+
+def discover_ftdi_port():
+    
+    # for linux, discover likely port to use; others require different methods
+    port = r'/dev/ttyUSB2'
+    possibleports = glob(r'/dev/ttyUSB*')
+    try:
+        port = possibleports[0]
+    except IndexError:
+        print "no ftdi USB to serial port found in /dev/ttyUSB*"
+        raise
+
+PORT = discover_tdi_port()
+print "Opening %s" % PORT
+ser = serial.Serial(PORT, BAUDRATE) # 8N1
+# this doesn't work as libmaple sets up usart
+# serial.Serial(port, BAUDRATE, bytesize=8, parity=serial.PARITY_EVEN, stopbits=1, xonxoff=0,rtscts=0,timeout=5.0) 
+print "Number of char in receive buffer: ", ser.inWaiting()
+# can also flushInput() and flushOutput()
+ser.flushInput()
+ser.flushOutput()
+
+def hello():
+    ser.write('w')
+    print ser.readline()
+
+
+
+def make_double_sine(f1=1,f2=2, srate=44000,num=1024, Vmin=0, Vmax=4096):
+    dt = 1.0/srate
+    nf1 = f1/srate
+    t = np.arange(num) # actually n
+    y1 = np.sin(2*np.pi * f1*t/1024.0)
+    y2 = np.sin(2*np.pi * f2*t/1024.0)
+    z = y1+y2
+    z = (z/z.max()) * Vmax
+    arr =np.zeros(num, dtype='<i2')
+    arr[:] = z[:]
+    # return arr,y1,y2, t,dt
+    return arr
+
+def test1():
+    raw_input("press key to start")
+    ser.write('w')
+    resp = ser.readline()
+    print resp
+    raw_input("start download of 256 bytes")
+    ser.write('U')
+    return read_chars(256)
+
+def read_chars(n):
+    buf = []    
+    for ii in xrange(n):
+        c = ser.read()
+        buf.append(c)
+    return buf
+
+
+
+def test_download_256():
+    ser.flush()
+    ser.write('U')
+    resp = read_chars(256)
+    # now check for correct response
+    return resp
+
+def test_download_1024_uint16():
+    ser.write('d')
+    resp = ser.read(size=2048) # two bytes per uint16
+    return resp # returns bytes
+#def convert_char2uint8(lstarr):
+#    np.
+
+def assemble_uints(bytearr):
+    # probably there is a better way to do this ? see structure or array package
+    iarr = map(ord,bytearr)
+    return [ iarr[2*ii] | (iarr[2*ii+1] << 8) for ii in xrange(len(iarr)/2) ]
+
+def uints2bytearray(uintarr):
+    # guess I need to return a string
+    n = len(uintarr)
+    out = []
+    for ii in xrange(n):
+        lsb = np.uint8(uintarr[ii])
+        msb = np.uint8(uintarr[ii]>>8)
+        out.append(chr(lsb))
+        out.append(chr(msb))
+    return "".join(out)
+        
+def test_upload_1024_int16(data):
+    ser.write('u')
+    for cc in data:
+        ser.write(cc)
+    ser.readline() # get done line back
+
+lim = np.array((1,),dtype='<i2')
+lim[0]=256
+limbytes = lim.tostring()
+arr1 = make_double_sine(1,0,srate=44100, num=1024, Vmax=2048)
+rawarr1 = limbytes + arr1.tostring()
+
+def up():
+    ser.write('r')
+    # ser.write(limbytes)
+    ser.write(rawarr1[:lim[0]])
+    
+
+def roundtrip():
+    ser.write('r')
+    # ser.write(limbytes)
+    ser.write(rawarr1[:lim[0]])
+    time.sleep(0.5)
+    ser.write('R') # request
+    ser.write(limbytes[0:lim[0]*2])
+    rawresp = ser.read(2*lim[0])
+    
+    arr2 = np.fromstring(rawresp, dtype='<i2')
+    return rawresp, arr2
+    
+# notes on string to array conversion and wav files
+
+# example of 
+# import wave, struct
+
+# waveFile = wave.open('sine.wav', 'r')
+
+# nchan = waveFile.getnchannels()
+# length = waveFile.getnframes()
+# for i in range(0,length):
+#     waveData = waveFile.readframes(1)
+#     data = struct.unpack("<h", waveData)
+#     print int(data[0])
+
+
+# see also scipy.io.wavfile {read('file'); write("filename", rate, data) }
+
+
+# to convert a larger string (bytestring) to an array
+# b4arr = array.array('h', b4)
+
+# np.fromstring(b2, dtype='<i2')
+
+
+def play(arr16):
+    ser.write('p')

File stream_play/waveheader.py

+# WavHeader.py
+#   Extract basic header information from a WAV file
+ 
+def PrintWavHeader(strWAVFile):
+    """ Extracts data in the first 44 bytes in a WAV file and writes it
+            out in a human-readable format
+    """
+    import os
+    import struct
+    import logging
+    logging.basicConfig(level=logging.DEBUG)
+    def DumpHeaderOutput(structHeaderFields):
+        for key in structHeaderFields.keys():
+            print "%s: " % (key), structHeaderFields[key]
+        # end for
+    # Open file
+    try:
+        fileIn = open(strWAVFile, 'rb')
+    except IOError, err:
+        logging.debug("Could not open input file %s" % (strWAVFile))
+        return
+    # end try
+    # Read in all data
+    bufHeader = fileIn.read(38)
+    # Verify that the correct identifiers are present
+    if (bufHeader[0:4] != "RIFF") or \
+       (bufHeader[12:16] != "fmt "): 
+         logging.debug("Input file not a standard WAV file")
+         return
+    # endif
+    stHeaderFields = {'ChunkSize' : 0, 'Format' : '',
+        'Subchunk1Size' : 0, 'AudioFormat' : 0,
+        'NumChannels' : 0, 'SampleRate' : 0,
+        'ByteRate' : 0, 'BlockAlign' : 0,
+        'BitsPerSample' : 0, 'Filename': ''}
+    # Parse fields
+    stHeaderFields['ChunkSize'] = struct.unpack('<L', bufHeader[4:8])[0]
+    stHeaderFields['Format'] = bufHeader[8:12]
+    stHeaderFields['Subchunk1Size'] = struct.unpack('<L', bufHeader[16:20])[0]
+    stHeaderFields['AudioFormat'] = struct.unpack('<H', bufHeader[20:22])[0]
+    stHeaderFields['NumChannels'] = struct.unpack('<H', bufHeader[22:24])[0]
+    stHeaderFields['SampleRate'] = struct.unpack('<L', bufHeader[24:28])[0]
+    stHeaderFields['ByteRate'] = struct.unpack('<L', bufHeader[28:32])[0]
+    stHeaderFields['BlockAlign'] = struct.unpack('<H', bufHeader[32:34])[0]
+    stHeaderFields['BitsPerSample'] = struct.unpack('<H', bufHeader[34:36])[0]
+    # Locate & read data chunk
+    chunksList = []
+    dataChunkLocation = 0
+    fileIn.seek(0, 2) # Seek to end of file
+    inputFileSize = fileIn.tell()
+    nextChunkLocation = 12 # skip the RIFF header
+    while 1:
+        # Read subchunk header
+        fileIn.seek(nextChunkLocation)
+        bufHeader = fileIn.read(8)
+        if bufHeader[0:4] == "data":
+            dataChunkLocation = nextChunkLocation
+        # endif
+        nextChunkLocation += (8 + struct.unpack('<L', bufHeader[4:8])[0])
+        chunksList.append(bufHeader[0:4])
+        if nextChunkLocation >= inputFileSize:
+            break
+        # endif
+    # end while
+    # Dump subchunk list
+    print "Subchunks Found: "
+    for chunkName in chunksList:
+        print "%s, " % (chunkName),
+    # end for
+    print "\n"
+    # Dump data chunk information
+    if dataChunkLocation != 0:
+        fileIn.seek(dataChunkLocation)
+        bufHeader = fileIn.read(8)
+        print "Data Chunk located at offset [%s] of data length [%s] bytes" % \
+            (dataChunkLocation, struct.unpack('<L', bufHeader[4:8])[0])
+    # endif
+    # Print output
+    stHeaderFields['Filename'] = os.path.basename(strWAVFile)
+    DumpHeaderOutput(stHeaderFields)
+    # Close file
+    fileIn.close()
+ 
+if __name__ == "__main__":
+    import sys
+    if len(sys.argv) != 2:
+        print "Invalid argument. Exactly one wave file location required as argument"
+    else:
+        PrintWavHeader(sys.argv[1])

File stream_play/wavserver.py

+import sys, os
+import time
+import logging
+import threading
+import serial
+import scipy.io.wavfile
+
+BAUDRATE = 2000000
+
+class Wavserver(object):
+    def __init__(self, wavarr, srate, serialinstance):
+        self.wavarr = wavarr
+        self.srate = srate
+        self.ser = serialinstance
+        self.pos = 0
+        self.blcksize=1024
+
+    def hello(self):
+        self.ser.flushOutput()
+        self.ser.flushInput()
+        self.ser.write('h')
+        resp = self.ser.readline(),
+        if not resp:
+            print "error--timeout, no response"
+        else:
+            print resp[0],
+
+    def stop(self):
+        self.ser.flushOutput()
+        self.ser.write('s')
+
+    def play(self):
+        ser = self.ser
+        ser.write('p')
+        while 1:
+            # waiting = ser.inWaiting()
+            ## if waiting:
+            ch = ser.read(1)
+
+            if ch == 'U':
+                datastr=self.wavarr[self.pos:self.pos+self.blcksize].tostring()
+                print "len(datastr): %d" % len(datastr)
+                ser.write(datastr)
+                self.pos += self.blcksize
+            else:
+                print ch,
+
+    def test_serial_upload(self):
+        ser = self.ser
+        ser.write('t')
+        ch = ser.read(1)
+
+        if ch == 'U':
+            datastr=self.wavarr[self.pos:self.pos+self.blcksize].tostring()
+            print "len(datastr): %d" % len(datastr)
+            ser.write(datastr)
+            self.pos += self.blcksize
+        else:
+            print ch,
+
+
+
+
+        
+    def debug(self):
+        self.ser.write('d');
+        for xx in self.ser.readlines():
+            print xx
+
+if __name__ == '__main__':
+    import optparse
+
+    parser = optparse.OptionParser(
+        usage = "%prog [options] port wavefilename",
+        description = "serve a wavfile to the maple+audio_codec board.",
+        epilog = """\
+
+
+Only one connection at once is supported. When the connection is terminated
+it waits for the next connect.
+""")
+
+
+    parser.add_option("-v", "--verbose",
+        dest = "verbosity",
+        action = "count",
+        help = "print more diagnostic messages (option can be given multiple times)",
+        default = 0
+    )
+
+    (options, args) = parser.parse_args()
+    if len(args) != 2:
+        parser.error('<serial port name> and <wavefile name> are required as arguments')
+
+    port = args[0]
+    wavefilename = args[1]
+        
+
+    logging.basicConfig(level=logging.INFO)
+    logging.getLogger('root').setLevel(logging.INFO)
+    logging.getLogger('wavserver').setLevel(logging.INFO)
+
+    try:
+        srate, wavarr = scipy.io.wavfile.read(wavefilename)
+    except:
+        logging.error("Could not open wavefile %s - %s " % wavefilename)
+        sys.exit(1)
+    logging.info("Opened wavfile: %s" % wavefilename)
+    logging.info("srate %s, wavarr.dtype: %s, shape=%s" % (srate, wavarr.dtype, wavarr.shape))
+    
+    ser = serial.Serial()
+    ser.port = port
+    ser.timeout=3
+    ser.baudrate = BAUDRATE
+    try:
+        ser.open()
+    except serial.SerialException, e:
+        logging.error("Could not open serial port %s: %s" % (ser.portstr, e))
+        sys.exit(1)
+
+    logging.info("Opened serial port: %s" % (ser.portstr,))
+
+
+    wserver = Wavserver(wavarr, srate, ser)
+    ws=wserver
+
+
+
+
+
+
+
+
+
+
+
+
+
+