Ejemplo n.º 1
0
    def play(self, id, track, finished):

        # If we're already playing a track, interrupt it and wait
        # for everything to be cleared
        if self.playing:
            self._restart = True
            return

        # id of caller
        self.curId = id

        # Set up audio
        if track.lower().endswith('.wav'):
            self.musicFile = wave.open(track, 'r')
        else:
            self.musicFile = decoder.open(track)

        self.sample_rate = self.musicFile.getframerate()
        self.num_channels = self.musicFile.getnchannels()

        self.output = audio.PCM(audio.PCM_PLAYBACK, audio.PCM_NORMAL)
        self.output.setchannels(self.num_channels)
        self.output.setrate(self.sample_rate)
        self.output.setformat(audio.PCM_FORMAT_S16_LE)
        self.output.setperiodsize(CHUNK_SIZE)

        self.frequency_limits = self._calculate_channel_frequency(50, 15000)

        # Start playing...
        self.playing = True
        self.thread = threading.Thread(target=self._play, args=[finished])
        self.thread.start()
Ejemplo n.º 2
0
    def getSong(self, path):
        header = False

        if any([ax for ax in [".mp4", ".m4a", ".m4b"] if ax in path]):
            header = True

        return decoder.open(path, header)
Ejemplo n.º 3
0
    def setup_audio(self):
        """Setup audio file

        and setup the output.  device.output is a lambda that will send data to
        fm process or to the specified ALSA sound card
        """
        # Set up audio
        force_header = False

        if any([ax for ax in [".mp4", ".m4a", ".m4b"] if ax in self.song_filename]):
            force_header = True

        self.music_file = decoder.open(self.song_filename, force_header)

        self.sample_rate = self.music_file.getframerate()
        self.num_channels = self.music_file.getnchannels()

        self.fft_calc = fft.FFT(self.chunk_size,
                                self.sample_rate,
                                cm.hardware.gpio_len,
                                cm.audio_processing.min_frequency,
                                cm.audio_processing.max_frequency,
                                cm.audio_processing.custom_channel_mapping,
                                cm.audio_processing.custom_channel_frequencies)

        # setup output device
        self.set_audio_device()

        chunks_per_sec = ((16 * self.num_channels * self.sample_rate) / 8) / self.chunk_size
        self.light_delay = int(cm.lightshow.light_delay * chunks_per_sec)

        # Output a bit about what we're about to play to the logs
        num_frames = str(self.music_file.getnframes() / self.sample_rate)
        log.info("Playing: " + self.song_filename + " (" + num_frames + " sec)")
Ejemplo n.º 4
0
def cache_song(song_filename):
    """Play the next song from the play list (or --file argument)."""
    # Initialize FFT stats
    matrix = [0 for _ in range(GPIOLEN)
              ]  # get length of gpio and assign it to a variable

    # Set up audio
    if song_filename.endswith('.wav'):
        musicfile = wave.open(song_filename, 'r')
    else:
        musicfile = decoder.open(song_filename)

    sample_rate = musicfile.getframerate()
    num_channels = musicfile.getnchannels()

    song_filename = os.path.abspath(song_filename)

    # create empty array for the cache_matrix
    cache_matrix = np.empty(shape=[0, GPIOLEN])
    cache_filename = \
        os.path.dirname(song_filename) + "/." + os.path.basename(song_filename) + ".sync"

    # The values 12 and 1.5 are good estimates for first time playing back
    # (i.e. before we have the actual mean and standard deviations
    # calculated for each channel).
    mean = [12.0 for _ in range(GPIOLEN)]
    std = [1.5 for _ in range(GPIOLEN)]

    # Process audio song_filename
    row = 0
    data = musicfile.readframes(
        CHUNK_SIZE)  # move chunk_size to configuration_manager
    frequency_limits = calculate_channel_frequency(
        _MIN_FREQUENCY, _MAX_FREQUENCY, _CUSTOM_CHANNEL_MAPPING,
        _CUSTOM_CHANNEL_FREQUENCIES)

    while data != '':
        # No cache - Compute FFT in this chunk, and cache results
        matrix = fft.calculate_levels(data, CHUNK_SIZE, sample_rate,
                                      frequency_limits, GPIOLEN)

        # Add the matrix to the end of the cache
        cache_matrix = np.vstack([cache_matrix, matrix])

        # Read next chunk of data from music song_filename
        data = musicfile.readframes(CHUNK_SIZE)
        row = row + 1

    # Compute the standard deviation and mean values for the cache
    for i in range(0, GPIOLEN):
        std[i] = np.std([item for item in cache_matrix[:, i] if item > 0])
        mean[i] = np.mean([item for item in cache_matrix[:, i] if item > 0])

    # Add mean and std to the top of the cache
    cache_matrix = np.vstack([mean, cache_matrix])
    cache_matrix = np.vstack([std, cache_matrix])

    # Save the cache using numpy savetxt
    np.savetxt(cache_filename, cache_matrix)
Ejemplo n.º 5
0
def cache_song(song_filename):
    """Play the next song from the play list (or --file argument)."""
    # Initialize FFT stats
    matrix = [0 for _ in range(GPIOLEN)] # get length of gpio and assign it to a variable

    # Set up audio
    if song_filename.endswith('.wav'):
        musicfile = wave.open(song_filename, 'r')
    else:
        musicfile = decoder.open(song_filename)

    sample_rate = musicfile.getframerate()
    num_channels = musicfile.getnchannels()

    song_filename = os.path.abspath(song_filename)

    # create empty array for the cache_matrix
    cache_matrix = np.empty(shape=[0, GPIOLEN])
    cache_filename = \
        os.path.dirname(song_filename) + "/." + os.path.basename(song_filename) + ".sync"

    # The values 12 and 1.5 are good estimates for first time playing back 
    # (i.e. before we have the actual mean and standard deviations 
    # calculated for each channel).
    mean = [12.0 for _ in range(GPIOLEN)]
    std = [1.5 for _ in range(GPIOLEN)]

    # Process audio song_filename
    row = 0
    data = musicfile.readframes(CHUNK_SIZE) # move chunk_size to configuration_manager
    frequency_limits = calculate_channel_frequency(_MIN_FREQUENCY,
                                                   _MAX_FREQUENCY,
                                                   _CUSTOM_CHANNEL_MAPPING,
                                                   _CUSTOM_CHANNEL_FREQUENCIES)

    while data != '':
        # No cache - Compute FFT in this chunk, and cache results
        matrix = fft.calculate_levels(data, CHUNK_SIZE, sample_rate, frequency_limits, GPIOLEN)

        # Add the matrix to the end of the cache 
        cache_matrix = np.vstack([cache_matrix, matrix])

        # Read next chunk of data from music song_filename
        data = musicfile.readframes(CHUNK_SIZE)
        row = row + 1

    # Compute the standard deviation and mean values for the cache
    for i in range(0, GPIOLEN):
        std[i] = np.std([item for item in cache_matrix[:, i] if item > 0])
        mean[i] = np.mean([item for item in cache_matrix[:, i] if item > 0])

    # Add mean and std to the top of the cache
    cache_matrix = np.vstack([mean, cache_matrix])
    cache_matrix = np.vstack([std, cache_matrix])

    # Save the cache using numpy savetxt
    np.savetxt(cache_filename, cache_matrix)
Ejemplo n.º 6
0
    def open(self, path, mono=False):
        """Open the audio resource."""
        self.pos = 0

        # Set up audio
        if path.endswith('.wav'):
            self.audio_file = audio_file = wave.open(path, 'r')
        else:
            # needs my updates in decoder.py
            self.audio_file = audio_file = \
                decoder.open(path, force_mono=mono)

        self.num_channels = 1 if mono else audio_file.getnchannels()
Ejemplo n.º 7
0
def load (msg=None):
    """Load audio file"""
    global cf, stream, cpdata, blockshow, fileinfo
    blockshow = 1
    clear()
    print intro
    if msg: print >> sys.stderr, msg
    cfn = ""
    while 1:
        cfn = raw_input("Audio file path: ")
        if not cfn: break
        cfn = os.path.normpath(cfn)
        if os.path.exists(cfn): break
        print >> sys.stderr, "File '"+cfn+"' does not exist!"
    if not cfn:
        clear()
        print intro, fileinfo
        if cpdata:
            x = cpdata.status()
            if x==1: print "Playing"
            if x==-1: print "Paused"
            if x==0: print "Stopped"
        blockshow = 0
        return
    if os.path.splitext(cfn)[1] not in decoder.wildcard:
        return load("File format not supported!")
    wasplaying = 0
    if cpdata:
        if cpdata.status()==1: wasplaying = 1
        cpdata.stop(); cpdata.wait()
    if stream: stream.close()
    if cf: cf.close()
    fh = 0 # ForceHeader
    if os.name=="posix" and cfn[-4:] in (".aac", ".m4a", ".m4b", ".mp4"): fh = 1
    # 'faad' decoder on Linux needs forced headers to work correctly
    cf = decoder.open(cfn, fh)
    stream = p.open(format = p.get_format_from_width(cf.getsampwidth()),
        channels = cf.getnchannels(), 
        rate = cf.getframerate(),
        output = True)
    clear(); print intro
    print cfn, "loaded!"
    fileinfo = decoder.info(cfn).info.pprint()
    # Using function from module fileinfo.py through decoder.py
    print fileinfo
    if wasplaying: print "Playing"
    else: print "Stopped"
    blockshow = 0
    if wasplaying: play()
Ejemplo n.º 8
0
def play(file):
    global _stream, _song

    if _stream is not None:
        stop()

    _song = decoder.open(file)
    _stream = _p.open(
        format=_p.get_format_from_width(_song.getsampwidth()),
        channels=_song.getnchannels(),
        rate=_song.getframerate(),
        output=True,
        stream_callback=_callback,
    )

    _stream.start_stream()
Ejemplo n.º 9
0
def loadfile(file):
	global wf, stream
	try:
		stream.close() #close le stream
		wf.close() #close le wave-like object
	except:
		pass
	wf = decoder.open(file, "r") #create a read-onry wave-like obj
	
	if wf.getnchannels() >2:
		print "multichannel files not supported"
		exit(-1)

	# open stream
	stream = p.open(format = p.get_format_from_width(wf.getsampwidth()),
		channels = 2,
		rate = wf.getframerate(),
		output = True)
Ejemplo n.º 10
0
def setup_audio(song_filename):
    """Setup audio file

    and setup setup the output device.output is a lambda that will send data to
    fm process or to the specified ALSA sound card

    :param song_filename: path / filename to music file
    :type song_filename: str
    :return: output, fm_process, fft_calc, music_file
    :rtype tuple: lambda, subprocess, fft.FFT, decoder
    """
    # Set up audio
    force_header = False

    if any([ax for ax in [".mp4", ".m4a", ".m4b"] if ax in song_filename]):
        force_header = True

    music_file = decoder.open(song_filename, force_header)

    sample_rate = music_file.getframerate()
    num_channels = music_file.getnchannels()

    fft_calc = fft.FFT(CHUNK_SIZE,
                       sample_rate,
                       hc.GPIOLEN,
                       cm.audio_processing.min_frequency,
                       cm.audio_processing.max_frequency,
                       cm.audio_processing.custom_channel_mapping,
                       cm.audio_processing.custom_channel_frequencies)

    # setup output device
    output = set_audio_device(sample_rate, num_channels)

    chunks_per_sec = ((16 * num_channels * sample_rate) / 8) / CHUNK_SIZE
    light_delay = int(cm.audio_processing.light_delay * chunks_per_sec)

    # Output a bit about what we're about to play to the logs
    nframes = str(music_file.getnframes() / sample_rate)
    log.info("Playing: " + song_filename + " (" + nframes + " sec)")

    return output, fft_calc, music_file, light_delay
Ejemplo n.º 11
0
def setup_audio(song_filename):
    """Setup audio file

    and setup setup the output device.output is a lambda that will send data to
    fm process or to the specified ALSA sound card

    :param song_filename: path / filename to music file
    :type song_filename: str
    :return: output, fm_process, fft_calc, music_file
    :rtype tuple: lambda, subprocess, fft.FFT, decoder
    """
    # Set up audio
    force_header = False

    if any([ax for ax in [".mp4", ".m4a", ".m4b"] if ax in song_filename]):
        force_header = True

    music_file = decoder.open(song_filename, force_header)

    sample_rate = music_file.getframerate()
    num_channels = music_file.getnchannels()

    fft_calc = fft.FFT(CHUNK_SIZE, sample_rate, hc.GPIOLEN,
                       cm.audio_processing.min_frequency,
                       cm.audio_processing.max_frequency,
                       cm.audio_processing.custom_channel_mapping,
                       cm.audio_processing.custom_channel_frequencies)

    # setup output device
    output = set_audio_device(sample_rate, num_channels)

    chunks_per_sec = ((16 * num_channels * sample_rate) / 8) / CHUNK_SIZE
    light_delay = int(cm.audio_processing.light_delay * chunks_per_sec)

    # Output a bit about what we're about to play to the logs
    nframes = str(music_file.getnframes() / sample_rate)
    log.info("Playing: " + song_filename + " (" + nframes + " sec)")

    return output, fft_calc, music_file, light_delay
Ejemplo n.º 12
0
def send_sound():
    # setup the connection
    #hostname = raw_input("Enter server address: ")
    hostname = HOSTNAME
    f_in = decoder.open(FILENAME)
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # get a socket
    sock.bind((hostname, IN_PORT))
    #sock.connect((hostname,PORT)) # connect to remote host
    #sock.send('S') # indicate that we wish to send data only
    #sock.send('\n') # send newline to indicate lack of name
    #while not '\n' == sock.recv(1):
    #  a = 1

    #rate = f_in.getframerate() # store the sample rate
    #sock.send(chr(rate >> 8)) # send 1st byte of sample rate
    #sock.send(chr(rate & 0xFF)) # send 2nd byte of sample rate
    #sock.send(chr(f_in.getnchannels())) # send the number of channels
    #sock.send(chr(f_in.getsampwidth())) # send the samle width

    # print some basic stats
    print f_in.getframerate(), f_in.getnchannels(), f_in.getsampwidth()

    # use this for timings
    rate = f_in.getframerate()
    delay = 1.0 / rate * CHUNK

    # start sending data
    bytes = f_in.readframes(CHUNK)
    average = 1
    while bytes != '':
        timer = time.time()
        sock.sendto(bytes, (hostname, OUT_PORT))
        sock.recvfrom(1)
        bytes = f_in.readframes(CHUNK)
        #time.sleep(delay) #sync with reciever
        #average = .875*average + (time.time() - timer)/delay*.125
        #print average  # scaling factor
    sock.close()
    f_in.close()
Ejemplo n.º 13
0
def reload ():
    """Reloads the file"""
    global cf
    try:
        if not cf: return
    except: return
    # Get the filename
    try:
        # Filename in fakewave object
        cfn = cf.obj.filename
        # cf.obj is a object returned by subprocess.Popen()
        # with added attribute (by 'decoder.py') "filename"
    except:
        # Filename in Wave_read or Aifc_read object
        cfn = cf._file.file.name
        # cf._file is a Chunk object within Wave/Aifc_read object,
        # cf._file.file is a normal file object where 'name' represents a filename
    try: cf.close()
    except: reload()
    fh = 0 # ForceHeader
    if os.name=="posix" and cfn[-4:] in (".aac", ".m4a", ".m4b", ".mp4"): fh = 1
    cf = decoder.open(cfn, fh)
Ejemplo n.º 14
0
def send_sound():
  # setup the connection
  #hostname = raw_input("Enter server address: ")
  hostname = HOSTNAME
  f_in = decoder.open(FILENAME)
  sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # get a socket
  sock.bind((hostname,IN_PORT))
  #sock.connect((hostname,PORT)) # connect to remote host
  #sock.send('S') # indicate that we wish to send data only
  #sock.send('\n') # send newline to indicate lack of name
  #while not '\n' == sock.recv(1):
  #  a = 1

  #rate = f_in.getframerate() # store the sample rate
  #sock.send(chr(rate >> 8)) # send 1st byte of sample rate
  #sock.send(chr(rate & 0xFF)) # send 2nd byte of sample rate
  #sock.send(chr(f_in.getnchannels())) # send the number of channels
  #sock.send(chr(f_in.getsampwidth())) # send the samle width

  # print some basic stats
  print f_in.getframerate(), f_in.getnchannels(), f_in.getsampwidth()

  # use this for timings
  rate = f_in.getframerate()
  delay = 1.0/rate * CHUNK

  # start sending data
  bytes = f_in.readframes(CHUNK)
  average = 1
  while bytes != '':
    timer = time.time()
    sock.sendto(bytes,(hostname,OUT_PORT))
    sock.recvfrom(1)
    bytes = f_in.readframes(CHUNK)
    #time.sleep(delay) #sync with reciever
    #average = .875*average + (time.time() - timer)/delay*.125
    #print average  # scaling factor
  sock.close()
  f_in.close()
Ejemplo n.º 15
0
def songThread():
	global data
	global queue
	global song
	global skip
	global curSong
	while True:
		stream = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL)
		while len(queue) == 0:
			time.sleep(1)
		curSong = queue.pop(0)
		song = decoder.open(curSong)
		stream.setperiodsize(chunk)
		data = song.readframes(chunk)
		while data != '':
			while pause:
				time.sleep(.1)
			if skip:
				skip = False
				break
			stream.write(data)
			data = song.readframes(chunk)
	print "escaped songThread loop"
Ejemplo n.º 16
0
def read_musicfile_in_chunks(path, chunk_size=CHUNK_SIZE, play_audio=True):
    """ Read the music file at the given path, in chunks of the given size. """

    musicfile = decoder.open(path)
    sample_rate = musicfile.getframerate()
    num_channels = musicfile.getnchannels()

    if play_audio:
        output = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL)
        output.setchannels(num_channels)
        output.setrate(sample_rate)
        output.setformat(aa.PCM_FORMAT_S16_LE)
        output.setperiodsize(chunk_size)

    # fixme: we could do the writing to audio in a thread ... ?

    while True:
        chunk = musicfile.readframes(chunk_size)
        if len(chunk) == 0:
            break
        if play_audio:
            output.write(chunk)

        yield chunk, sample_rate
Ejemplo n.º 17
0
def send_sound():
  # setup the connection
  hostname = raw_input("Enter server address: ")
  f_in = decoder.open(FILENAME)
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # get a socket
  sock.connect((hostname,PORT)) # connect to remote host
  sock.send('S') # indicate that we wish to send data only
  sock.send('\n') # send newline to indicate lack of name
  while not '\n' == sock.recv(1):
    a = 1

  rate = f_in.getframerate() # store the sample rate
  sock.send(chr(rate >> 8)) # send 1st byte of sample rate
  sock.send(chr(rate & 0xFF)) # send 2nd byte of sample rate
  sock.send(chr(f_in.getnchannels())) # send the number of channels
  sock.send(chr(f_in.getsampwidth())) # send the samle width

  # start sending data
  bytes = f_in.readframes(CHUNK)
  while bytes != '':
    sock.send(bytes)
    bytes = f_in.readframes(CHUNK)
  sock.close()
  f_in.close()
Ejemplo n.º 18
0
def send_sound():
    # setup the connection
    hostname = raw_input("Enter server address: ")
    f_in = decoder.open(FILENAME)
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # get a socket
    sock.connect((hostname, PORT))  # connect to remote host
    sock.send('S')  # indicate that we wish to send data only
    sock.send('\n')  # send newline to indicate lack of name
    while not '\n' == sock.recv(1):
        a = 1

    rate = f_in.getframerate()  # store the sample rate
    sock.send(chr(rate >> 8))  # send 1st byte of sample rate
    sock.send(chr(rate & 0xFF))  # send 2nd byte of sample rate
    sock.send(chr(f_in.getnchannels()))  # send the number of channels
    sock.send(chr(f_in.getsampwidth()))  # send the samle width

    # start sending data
    bytes = f_in.readframes(CHUNK)
    while bytes != '':
        sock.send(bytes)
        bytes = f_in.readframes(CHUNK)
    sock.close()
    f_in.close()
Ejemplo n.º 19
0
#! /usr/bin/env python
"""Example:
Decoding a compressed audio file to PCM WAVE and saving the result."""

import decoder, wave, os

cfn = raw_input("Compressed audio file path: ")
cfn = os.path.normpath(cfn)
while not os.path.exists(cfn):
    print "File '"+cfn+"' does not exist!"
    cfn = raw_input("Compressed audio file path: ")
    cfn = os.path.normpath(cfn)

wfn = raw_input("Output wave file path: ")

fh = 0 # ForceHeader
if os.name=="posix" and cfn[-4:] in (".aac", ".m4a", ".m4b", ".mp4"): fh = 1

cf = decoder.open(cfn, fh)
wf = wave.open(wfn, "w")

print "Decoding; Please wait!"
decoder.copywaveobj(cf, wf)
print "Done!"
cf.close(); wf.close()
raw_input("Press enter to continue . . . ")
def play_song():
    '''Play the next song from the play list (or --file argument).'''
    song_to_play = int(cm.get_state('song_to_play', 0))
    play_now = int(cm.get_state('play_now', 0))

    # Arguments
    parser = argparse.ArgumentParser()
    filegroup = parser.add_mutually_exclusive_group()
    filegroup.add_argument('--playlist', default=_PLAYLIST_PATH,
                           help='Playlist to choose song from.')
    filegroup.add_argument('--file', help='path to the song to play (required if no'
                           'playlist is designated)')
    parser.add_argument('--readcache', type=int, default=1,
                        help='read light timing from cache if available. Default: true')
    args = parser.parse_args()

    # Make sure one of --playlist or --file was specified
    if args.file == None and args.playlist == None:
        print "One of --playlist or --file must be specified"
        sys.exit()

    # Initialize Lights
    hc.initialize()

    # Handle the pre-show
    if not play_now:
        result = Preshow().execute()
        if result == Preshow.PlayNowInterrupt:
            play_now = True

    # Determine the next file to play
    song_filename = args.file
    if args.playlist != None and args.file == None:
        most_votes = [None, None, []]
        current_song = None
        with open(args.playlist, 'rb') as playlist_fp:
            fcntl.lockf(playlist_fp, fcntl.LOCK_SH)
            playlist = csv.reader(playlist_fp, delimiter='\t')
            songs = []
            for song in playlist:
                if len(song) < 2 or len(song) > 4:
                    logging.error('Invalid playlist.  Each line should be in the form: '
                                 '<song name><tab><path to song>')
                    sys.exit()
                elif len(song) == 2:
                    song.append(set())
                else:
                    song[2] = set(song[2].split(','))
                    if len(song) == 3 and len(song[2]) >= len(most_votes[2]):
                        most_votes = song
                songs.append(song)
            fcntl.lockf(playlist_fp, fcntl.LOCK_UN)

        if most_votes[0] != None:
            logging.info("Most Votes: " + str(most_votes))
            current_song = most_votes

            # Update playlist with latest votes
            with open(args.playlist, 'wb') as playlist_fp:
                fcntl.lockf(playlist_fp, fcntl.LOCK_EX)
                writer = csv.writer(playlist_fp, delimiter='\t')
                for song in songs:
                    if current_song == song and len(song) == 3:
                        song.append("playing!")
                    if len(song[2]) > 0:
                        song[2] = ",".join(song[2])
                    else:
                        del song[2]
                writer.writerows(songs)
                fcntl.lockf(playlist_fp, fcntl.LOCK_UN)

        else:
            # Get a "play now" requested song
            if play_now > 0 and play_now <= len(songs):
                current_song = songs[play_now - 1]
            # Get random song
            elif _RANDOMIZE_PLAYLIST:
                current_song = songs[random.randint(0, len(songs) - 1)]
            # Play next song in the lineup
            else:
                song_to_play = song_to_play if (song_to_play <= len(songs) - 1) else 0
                current_song = songs[song_to_play]
                next_song = (song_to_play + 1) if ((song_to_play + 1) <= len(songs) - 1) else 0
                cm.update_state('song_to_play', next_song)

        # Get filename to play and store the current song playing in state cfg
        song_filename = current_song[1]
        cm.update_state('current_song', songs.index(current_song))

    song_filename = song_filename.replace("$SYNCHRONIZED_LIGHTS_HOME", cm.HOME_DIR)

    # Ensure play_now is reset before beginning playback
    if play_now:
        cm.update_state('play_now', 0)
        play_now = 0

    # Initialize FFT stats
    matrix = [0 for _ in range(hc.GPIOLEN)]

    # Set up audio
    if song_filename.endswith('.wav'):
        musicfile = wave.open(song_filename, 'r')
    else:
        musicfile = decoder.open(song_filename)

    sample_rate = musicfile.getframerate()
    num_channels = musicfile.getnchannels()

    if _usefm=='true':
        logging.info("Sending output as fm transmission")
        with open(os.devnull, "w") as dev_null:
            fm_process = subprocess.Popen(["sudo",cm.HOME_DIR + "/bin/pifm","-",str(frequency),"44100", "stereo" if play_stereo else "mono"], stdin=music_pipe_r, stdout=dev_null)
    else:
        output = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL)
        output.setchannels(num_channels)
        output.setrate(sample_rate)
        output.setformat(aa.PCM_FORMAT_S16_LE)
        output.setperiodsize(CHUNK_SIZE)
    
    logging.info("Playing: " + song_filename + " (" + str(musicfile.getnframes() / sample_rate)
                 + " sec)")
    # Output a bit about what we're about to play to the logs
    song_filename = os.path.abspath(song_filename)
    

    cache = []
    cache_found = False
    cache_filename = os.path.dirname(song_filename) + "/." + os.path.basename(song_filename) \
        + ".sync.gz"
    # The values 12 and 1.5 are good estimates for first time playing back (i.e. before we have
    # the actual mean and standard deviations calculated for each channel).
    mean = [12.0 for _ in range(hc.GPIOLEN)]
    std = [1.5 for _ in range(hc.GPIOLEN)]
    if args.readcache:
        # Read in cached fft
        try:
            with gzip.open(cache_filename, 'rb') as playlist_fp:
                cachefile = csv.reader(playlist_fp, delimiter=',')
                for row in cachefile:
                    cache.append([0.0 if np.isinf(float(item)) else float(item) for item in row])
                cache_found = True
                # TODO(todd): Optimize this and / or cache it to avoid delay here
                cache_matrix = np.array(cache)
                for i in range(0, hc.GPIOLEN):
                    std[i] = np.std([item for item in cache_matrix[:, i] if item > 0])
                    mean[i] = np.mean([item for item in cache_matrix[:, i] if item > 0])
                logging.debug("std: " + str(std) + ", mean: " + str(mean))
        except IOError:
            logging.warn("Cached sync data song_filename not found: '" + cache_filename
                         + ".  One will be generated.")

    # Process audio song_filename
    row = 0
    data = musicfile.readframes(CHUNK_SIZE)
    frequency_limits = calculate_channel_frequency(_MIN_FREQUENCY,
                                                   _MAX_FREQUENCY,
                                                   _CUSTOM_CHANNEL_MAPPING,
                                                   _CUSTOM_CHANNEL_FREQUENCIES)

    while data != '' and not play_now:
        if _usefm=='true':
            os.write(music_pipe_w, data)
        else:
            output.write(data)

        # Control lights with cached timing values if they exist
        matrix = None
        if cache_found and args.readcache:
            if row < len(cache):
                matrix = cache[row]
            else:
                logging.warning("Ran out of cached FFT values, will update the cache.")
                cache_found = False

        if matrix == None:
            # No cache - Compute FFT in this chunk, and cache results
            matrix = fft.calculate_levels(data, CHUNK_SIZE, sample_rate, frequency_limits)
            cache.append(matrix)
            
        update_lights(matrix, mean, std)

        # Read next chunk of data from music song_filename
        data = musicfile.readframes(CHUNK_SIZE)
        row = row + 1

        # Load new application state in case we've been interrupted
        cm.load_state()
        play_now = int(cm.get_state('play_now', 0))

    if not cache_found:
        with gzip.open(cache_filename, 'wb') as playlist_fp:
            writer = csv.writer(playlist_fp, delimiter=',')
            writer.writerows(cache)
            logging.info("Cached sync data written to '." + cache_filename
                         + "' [" + str(len(cache)) + " rows]")

    # Cleanup the pifm process
    if _usefm=='true':
        fm_process.kill()

    # We're done, turn it all off and clean up things ;)
    hc.clean_up()
Ejemplo n.º 21
0
    r = requests.get(url, stream=True)
    d = r.headers["Content-Disposition"]
    filename = re.findall("filename=(.+)", d)[0]
    if os.path.isfile(filename):
        r.connection.close()
        return filename
    with open(filename, "wb") as f:
        for chunk in r.iter_content(chunk_size=4096):
            if chunk:  # filter out keep-alive new chunks
                f.write(chunk)
    return filename


song_filename = download_file("http://intense.io:9001/get-track")
cache_filename = download_file("http://intense.io:9001/get-fft")
music_file = decoder.open(song_filename)
num_channels = music_file.getnchannels()
sample_rate = music_file.getframerate()
init(num_channels, sample_rate)

# load cache from file using numpy loadtxt
cache_matrix = np.loadtxt(cache_filename)

# get std from matrix / located at index 0
std = np.array(cache_matrix[0])

# get mean from matrix / located at index 1
mean = np.array(cache_matrix[1])

# delete mean and std from the array
cache_matrix = np.delete(cache_matrix, 0, axis=0)
Ejemplo n.º 22
0
def play_song(song_filename):

    # Initialize Lights
    # hc.initialize()

    # Ensure play_now is reset before beginning playback

    # Set up audio
    if song_filename.endswith(".wav"):
        music_file = wave.open(song_filename, "r")
    else:
        music_file = decoder.open(song_filename)

    sample_rate = music_file.getframerate()
    num_channels = music_file.getnchannels()

    print ("Playing: " + song_filename + " (" + str(music_file.getnframes() / sample_rate) + " sec)")

    # Output a bit about what we're about to play to the logs
    song_filename = os.path.abspath(song_filename)

    # create empty array for the cache_matrix
    cache_matrix = np.empty(shape=[0, NUM_CHANNELS])
    cache_found = False
    cache_filename = os.path.dirname(song_filename) + "/." + os.path.basename(song_filename) + ".sync"
    print cache_filename

    # The values 12 and 1.5 are good estimates for first time playing back
    # (i.e. before we have the actual mean and standard deviations
    # calculated for each channel).
    mean = [12.0 for _ in range(NUM_CHANNELS)]
    std = [1.5 for _ in range(NUM_CHANNELS)]

    # Read in cached fft
    try:
        # load cache from file using numpy loadtxt
        cache_matrix = np.loadtxt(cache_filename)
        cache_found = True

        # get std from matrix / located at index 0
        std = np.array(cache_matrix[0])

        # get mean from matrix / located at index 1
        mean = np.array(cache_matrix[1])

        # delete mean and std from the array
        cache_matrix = np.delete(cache_matrix, 0, axis=0)
        cache_matrix = np.delete(cache_matrix, 0, axis=0)

        print ("std: " + str(std) + ", mean: " + str(mean))
    except IOError:
        print ("Cached sync data song_filename not found: '" + cache_filename + "'.  One will be generated.")

    # Process audio song_filename
    row = 0
    data = music_file.readframes(CHUNK_SIZE)
    frequency_limits = calculate_channel_frequency(_MIN_FREQUENCY, _MAX_FREQUENCY)

    # timer
    frames_played = 0

    yield num_channels, sample_rate

    while data != "":

        # Control lights with cached timing values if they exist
        matrix = None
        if cache_found and args.readcache:
            if row < len(cache_matrix):
                matrix = cache_matrix[row]
            else:
                print ("Ran out of cached FFT values, will update the cache.")
                cache_found = False

        if matrix is None:
            # No cache - Compute FFT in this chunk, and cache results
            matrix = fft.calculate_levels(data, CHUNK_SIZE, sample_rate, frequency_limits, NUM_CHANNELS)

            # Add the matrix to the end of the cache
            cache_matrix = np.vstack([cache_matrix, matrix])

        frames_played += len(data) / 4

        yield data, frames_played, total_frames, sample_rate, update_lights(matrix, mean, std)

        # Read next chunk of data from music song_filename
        data = music_file.readframes(CHUNK_SIZE)
        row += 1

    if not cache_found:
        # Compute the standard deviation and mean values for the cache
        for i in range(0, NUM_CHANNELS):
            std[i] = np.std([item for item in cache_matrix[:, i] if item > 0])
            mean[i] = np.mean([item for item in cache_matrix[:, i] if item > 0])

        # Add mean and std to the top of the cache
        cache_matrix = np.vstack([mean, cache_matrix])
        cache_matrix = np.vstack([std, cache_matrix])

        # Save the cache using numpy savetxt
        np.savetxt(cache_filename, cache_matrix)
Ejemplo n.º 23
0
def main():
    '''main'''
    song_to_play = int(cm.get_state('song_to_play', 0))
    play_now = int(cm.get_state('play_now', 0))

    # Arguments
    parser = argparse.ArgumentParser()
    filegroup = parser.add_mutually_exclusive_group()
    filegroup.add_argument('--playlist', default=_PLAYLIST_PATH,
                           help='Playlist to choose song from.')
    filegroup.add_argument('--file', help='path to the song to play (required if no'
                           'playlist is designated)')
    parser.add_argument('--readcache', type=int, default=1,
                        help='read light timing from cache if available. Default: true')
    args = parser.parse_args()

    # Log everything to our log file
    # TODO(todd): Add logging configuration options.
    logging.basicConfig(filename=cm.LOG_DIR + '/music_and_lights.play.dbg',
                        format='[%(asctime)s] %(levelname)s {%(pathname)s:%(lineno)d}'
                        ' - %(message)s',
                        level=logging.DEBUG)

    # Make sure one of --playlist or --file was specified
    if args.file == None and args.playlist == None:
        print "One of --playlist or --file must be specified"
        sys.exit()

    # Initialize Lights
    hc.initialize()

    # Only execute preshow if no specific song has been requested to be played right now
    if not play_now:
        execute_preshow(cm.lightshow()['preshow'])

    # Determine the next file to play
    song_filename = args.file
    if args.playlist != None and args.file == None:
        most_votes = [None, None, []]
        current_song = None
        with open(args.playlist, 'rb') as playlist_fp:
            fcntl.lockf(playlist_fp, fcntl.LOCK_SH)
            playlist = csv.reader(playlist_fp, delimiter='\t')
            songs = []
            for song in playlist:
                if len(song) < 2 or len(song) > 4:
                    logging.error('Invalid playlist.  Each line should be in the form: '
                                 '<song name><tab><path to song>')
                    sys.exit()
                elif len(song) == 2:
                    song.append(set())
                else:
                    song[2] = set(song[2].split(','))
                    if len(song) == 3 and len(song[2]) >= len(most_votes[2]):
                        most_votes = song
                songs.append(song)
            fcntl.lockf(playlist_fp, fcntl.LOCK_UN)

        if most_votes[0] != None:
            logging.info("Most Votes: " + str(most_votes))
            current_song = most_votes

            # Update playlist with latest votes
            with open(args.playlist, 'wb') as playlist_fp:
                fcntl.lockf(playlist_fp, fcntl.LOCK_EX)
                writer = csv.writer(playlist_fp, delimiter='\t')
                for song in songs:
                    if current_song == song and len(song) == 3:
                        song.append("playing!")
                    if len(song[2]) > 0:
                        song[2] = ",".join(song[2])
                    else:
                        del song[2]
                writer.writerows(songs)
                fcntl.lockf(playlist_fp, fcntl.LOCK_UN)

        else:
            # Get random song
            if _RANDOMIZE_PLAYLIST:
                current_song = songs[random.randint(0, len(songs) - 1)]
            # Get a "play now" requested song
            elif play_now > 0 and play_now <= len(songs):
                current_song = songs[play_now - 1]
            # Play next song in the lineup
            else:
                song_to_play = song_to_play if (song_to_play <= len(songs) - 1) else 0
                current_song = songs[song_to_play]
                next_song = (song_to_play + 1) if ((song_to_play + 1) <= len(songs) - 1) else 0
                cm.update_state('song_to_play', next_song)

        # Get filename to play and store the current song playing in state cfg
        song_filename = current_song[1]
        cm.update_state('current_song', songs.index(current_song))

    song_filename = song_filename.replace("$SYNCHRONIZED_LIGHTS_HOME", cm.HOME_DIR)

    # Ensure play_now is reset before beginning playback
    if play_now:
        cm.update_state('play_now', 0)
        play_now = 0

    # Initialize FFT stats
    matrix = [0 for _ in range(hc.GPIOLEN)]
    offct = [0 for _ in range(hc.GPIOLEN)]

    # Build the limit list
    if len(_LIMIT_LIST) == 1:
        limit = [_LIMIT_LIST[0] for _ in range(hc.GPIOLEN)]
    else:
        limit = _LIMIT_LIST

    # Set up audio
    if song_filename.endswith('.wav'):
        musicfile = wave.open(song_filename, 'r')
    else:
        musicfile = decoder.open(song_filename)

    sample_rate = musicfile.getframerate()
    num_channels = musicfile.getnchannels()
    output = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL)
    output.setchannels(num_channels)
    output.setrate(sample_rate)
    output.setformat(aa.PCM_FORMAT_S16_LE)
    output.setperiodsize(CHUNK_SIZE)

    # Output a bit about what we're about to play
    song_filename = os.path.abspath(song_filename)
    logging.info("Playing: " + song_filename + " (" + str(musicfile.getnframes() / sample_rate)
                 + " sec)")

    cache = []
    cache_found = False
    cache_filename = os.path.dirname(song_filename) + "/." + os.path.basename(song_filename) \
        + ".sync.gz"
    # The values 12 and 1.5 are good estimates for first time playing back (i.e. before we have
    # the actual mean and standard deviations calculated for each channel).
    mean = [12.0 for _ in range(hc.GPIOLEN)]
    std = [1.5 for _ in range(hc.GPIOLEN)]
    if args.readcache:
        # Read in cached fft
        try:
            with gzip.open(cache_filename, 'rb') as playlist_fp:
                cachefile = csv.reader(playlist_fp, delimiter=',')
                for row in cachefile:
                    cache.append([0.0 if np.isinf(float(item)) else float(item) for item in row])
                cache_found = True
                # TODO(todd): Optimize this and / or cache it to avoid delay here
                cache_matrix = np.array(cache)
                for i in range(0, hc.GPIOLEN):
                    std[i] = np.std([item for item in cache_matrix[:, i] if item > 0])
                    mean[i] = np.mean([item for item in cache_matrix[:, i] if item > 0])
                logging.debug("std: " + str(std) + ", mean: " + str(mean))
        except IOError:
            logging.warn("Cached sync data song_filename not found: '" + cache_filename
                         + ".  One will be generated.")

    # Process audio song_filename
    row = 0
    data = musicfile.readframes(CHUNK_SIZE)
    frequency_limits = calculate_channel_frequency(_MIN_FREQUENCY,
                                                   _MAX_FREQUENCY,
                                                   _CUSTOM_CHANNEL_MAPPING,
                                                   _CUSTOM_CHANNEL_FREQUENCIES)

    while data != '' and not play_now:
        output.write(data)

        # Control lights with cached timing values if they exist
        matrix = None
        if cache_found and args.readcache:
            if row < len(cache):
                matrix = cache[row]
            else:
                logging.warning("Ran out of cached FFT values, will update the cache.")
                cache_found = False

        if matrix == None:
            # No cache - Compute FFT in this chunk, and cache results
            matrix = calculate_levels(data, sample_rate, frequency_limits)
            cache.append(matrix)


        # blank out the display
        led.fill(Color(0,0,0),0,151)
        for i in range(0, hc.GPIOLEN):
            if hc.is_pin_pwm(i):
                # Output pwm, where off is at 0.5 std below the mean
                # and full on is at 0.75 std above the mean.

                display_column(i,matrix[i])

                #brightness = matrix[i] - mean[i] + 0.5 * std[i]
                #brightness = brightness / (1.25 * std[i])
                #if brightness > 1.0:
                    #brightness = 1.0
                #if brightness < 0:
                    #brightness = 0
                #hc.turn_on_light(i, True, int(brightness * 60))
            else:
                if limit[i] < matrix[i] * _LIMIT_THRESHOLD:
                    limit[i] = limit[i] * _LIMIT_THRESHOLD_INCREASE
                    logging.debug("++++ channel: {0}; limit: {1:.3f}".format(i, limit[i]))
                # Amplitude has reached threshold
                if matrix[i] > limit[i]:
                    hc.turn_on_light(i, True)
                    offct[i] = 0
                else:  # Amplitude did not reach threshold
                    offct[i] = offct[i] + 1
                    if offct[i] > _MAX_OFF_CYCLES:
                        offct[i] = 0
                        limit[i] = limit[i] * _LIMIT_THRESHOLD_DECREASE  # old value 0.8
                    logging.debug("---- channel: {0}; limit: {1:.3f}".format(i, limit[i]))
                    hc.turn_off_light(i, True)

        # send out data to RGB LED Strip
        led.update()
        # Read next chunk of data from music song_filename
        data = musicfile.readframes(CHUNK_SIZE)
        row = row + 1

        # Load new application state in case we've been interrupted
        cm.load_state()
        play_now = int(cm.get_state('play_now', 0))

    if not cache_found:
        with gzip.open(cache_filename, 'wb') as playlist_fp:
            writer = csv.writer(playlist_fp, delimiter=',')
            writer.writerows(cache)
            logging.info("Cached sync data written to '." + cache_filename
                         + "' [" + str(len(cache)) + " rows]")

    # We're done, turn it all off ;)
    hc.clean_up()
Ejemplo n.º 24
0
import decoder
import time
import sys
import struct
import pyaudio
import numpy
import math

filename = "song.mp3"
if len(sys.argv) > 1:
    filename = sys.argv[1]
mp3 = decoder.open(filename)
framerate = mp3.getframerate()
samplewidth = mp3.getsampwidth()
channels = mp3.getnchannels()
chunksize = 2048

# Loudness contour data, might want to adjust output accordingly
#f = [20, 25, 31.5, 40, 50, 63, 80, 100, 125, 160, 200, 250, 315, 400, 500, 630, 800,
#     1000, 1250, 1600, 2000, 2500, 3150, 4000, 5000, 6300, 8000, 10000, 12500];
#
#Tf = [ 78.5, 68.7, 59.5, 51.1, 44.0, 37.5, 31.5, 26.5, 22.1, 17.9, 14.4,
#       11.4, 8.6, 6.2, 4.4, 3.0, 2.2, 2.4, 3.5, 1.7, -1.3, -4.2,
#       -6.0, -5.4, -1.5, 6.0, 12.6, 13.9, 12.3];
#
#contourfactors = [pow(10, (x/10)) for x in Tf]

p = pyaudio.PyAudio()
stream = p.open(format=p.get_format_from_width(samplewidth),
    channels=channels,
    rate=framerate,
Ejemplo n.º 25
0
def play_song(song_filename):

    # Initialize Lights
    # hc.initialize()

    # Ensure play_now is reset before beginning playback

    # Set up audio
    if song_filename.endswith('.wav'):
        music_file = wave.open(song_filename, 'r')
    else:
        music_file = decoder.open(song_filename)

    sample_rate = music_file.getframerate()
    num_channels = music_file.getnchannels()
    output = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL)
    output.setchannels(num_channels)
    output.setrate(sample_rate)
    output.setformat(aa.PCM_FORMAT_S16_LE)
    output.setperiodsize(CHUNK_SIZE)

    print("Playing: " + song_filename + " (" + str(music_file.getnframes() / sample_rate)
                 + " sec)")

    # Output a bit about what we're about to play to the logs
    song_filename = os.path.abspath(song_filename)

    # create empty array for the cache_matrix
    cache_matrix = np.empty(shape=[0, NUM_CHANNELS])
    cache_found = False
    cache_filename = os.path.dirname(song_filename) + \
        "/." + os.path.basename(song_filename) + ".sync"
    print cache_filename

    # The values 12 and 1.5 are good estimates for first time playing back
    # (i.e. before we have the actual mean and standard deviations
    # calculated for each channel).
    mean = [12.0 for _ in range(NUM_CHANNELS)]
    std = [1.5 for _ in range(NUM_CHANNELS)]

    # Read in cached fft
    try:
        # load cache from file using numpy loadtxt
        cache_matrix = np.loadtxt(cache_filename)
        cache_found = True

        # get std from matrix / located at index 0
        std = np.array(cache_matrix[0])

        # get mean from matrix / located at index 1
        mean = np.array(cache_matrix[1])

        # delete mean and std from the array
        cache_matrix = np.delete(cache_matrix, 0, axis=0)
        cache_matrix = np.delete(cache_matrix, 0, axis=0)

        print("std: " + str(std) + ", mean: " + str(mean))
    except IOError:
        print("Cached sync data song_filename not found: '"
                     + cache_filename
                     + "'.  One will be generated.")

    # Process audio song_filename
    row = 0
    data = music_file.readframes(CHUNK_SIZE)
    frequency_limits = calculate_channel_frequency(_MIN_FREQUENCY, _MAX_FREQUENCY)

    # timer
    total_seconds = music_file.getnframes() / sample_rate
    m, s = divmod(total_seconds, 60)
    total_time = "%d:%02d" % (m,s)
    frames_played = 0
    update_rate = sample_rate / 10
    last_update_display = 0
    last_update_spec = 0

    while data != '':
        output.write(data)

        # Control lights with cached timing values if they exist
        matrix = None
        if cache_found:
            if row < len(cache_matrix):
                matrix = cache_matrix[row]
            else:
                print("Ran out of cached FFT values, will update the cache.")
                cache_found = False

        if matrix is None:
            # No cache - Compute FFT in this chunk, and cache results
            matrix = fft.calculate_levels(data, CHUNK_SIZE, sample_rate, frequency_limits,
                                          NUM_CHANNELS)

            # Add the matrix to the end of the cache
            cache_matrix = np.vstack([cache_matrix, matrix])

        frames_played += len(data) / 4
        if last_update_display + sample_rate < frames_played:
	    seconds = frames_played / sample_rate
            m, s = divmod(seconds, 60)
            lcd.set_cursor(0,0)
	    lcd.message('Now playing\n{}/{}'.format("%d:%02d" % (m, s), total_time))
            last_update_display = frames_played

        if last_update_spec + update_rate < frames_played:
            update_lights(matrix, mean, std)
            last_update_spec = frames_played

        # Read next chunk of data from music song_filename
        data = music_file.readframes(CHUNK_SIZE)
        row += 1

        # Load new application state in case we've been interrupted
        # cm.load_state()
        # play_now = int(cm.get_state('play_now', "0"))

    if not cache_found:
        # Compute the standard deviation and mean values for the cache
        for i in range(0, NUM_CHANNELS):
            std[i] = np.std([item for item in cache_matrix[:, i] if item > 0])
            mean[i] = np.mean([item for item in cache_matrix[:, i] if item > 0])

        # Add mean and std to the top of the cache
        cache_matrix = np.vstack([mean, cache_matrix])
        cache_matrix = np.vstack([std, cache_matrix])

        # Save the cache using numpy savetxt
        np.savetxt(cache_filename, cache_matrix)

        print("Cached sync data written to '." + cache_filename
                     + "' [" + str(len(cache_matrix)) + " rows]")
Ejemplo n.º 26
0
def play_song():
    """Play the next song from the play list (or --file argument)."""
    song_to_play = int(cm.get_state('song_to_play', 0))
    play_now = int(cm.get_state('play_now', 0))

    # Arguments
    parser = argparse.ArgumentParser()
    filegroup = parser.add_mutually_exclusive_group()
    filegroup.add_argument('--playlist',
                           default=_PLAYLIST_PATH,
                           help='Playlist to choose song from.')
    filegroup.add_argument('--file',
                           help='path to the song to play (required if no'
                           'playlist is designated)')
    parser.add_argument(
        '--readcache',
        type=int,
        default=1,
        help='read light timing from cache if available. Default: true')
    args = parser.parse_args()

    # Make sure one of --playlist or --file was specified
    if args.file == None and args.playlist == None:
        print "One of --playlist or --file must be specified"
        sys.exit()

    # Handle the pre/post show
    if not play_now:
        result = PrePostShow('preshow').execute()
        # now unused.  if play_now = True
        # song to play is always the first song in the playlist

        if result == PrePostShow.play_now_interrupt:
            play_now = int(cm.get_state('play_now', 0))

    # Initialize Lights
    hc.initialize()

    # Determine the next file to play
    song_filename = args.file
    if args.playlist != None and args.file == None:
        most_votes = [None, None, []]
        current_song = None
        with open(args.playlist, 'rb') as playlist_fp:
            fcntl.lockf(playlist_fp, fcntl.LOCK_SH)
            playlist = csv.reader(playlist_fp, delimiter='\t')
            songs = []
            for song in playlist:
                if len(song) < 2 or len(song) > 4:
                    logging.error(
                        'Invalid playlist.  Each line should be in the form: '
                        '<song name><tab><path to song>')
                    sys.exit()
                elif len(song) == 2:
                    song.append(set())
                else:
                    song[2] = set(song[2].split(','))
                    if len(song) == 3 and len(song[2]) >= len(most_votes[2]):
                        most_votes = song
                songs.append(song)
            fcntl.lockf(playlist_fp, fcntl.LOCK_UN)

        if most_votes[0] != None:
            logging.info("Most Votes: " + str(most_votes))
            current_song = most_votes

            # Update playlist with latest votes
            with open(args.playlist, 'wb') as playlist_fp:
                fcntl.lockf(playlist_fp, fcntl.LOCK_EX)
                writer = csv.writer(playlist_fp, delimiter='\t')
                for song in songs:
                    if current_song == song and len(song) == 3:
                        song.append("playing!")
                    if len(song[2]) > 0:
                        song[2] = ",".join(song[2])
                    else:
                        del song[2]
                writer.writerows(songs)
                fcntl.lockf(playlist_fp, fcntl.LOCK_UN)

        else:
            # Get a "play now" requested song
            if play_now > 0 and play_now <= len(songs):
                current_song = songs[play_now - 1]
            # Get random song
            elif _RANDOMIZE_PLAYLIST:
                # Use python's random.randrange() to get a random song
                current_song = songs[random.randrange(0, len(songs))]

            # Play next song in the lineup
            else:
                song_to_play = song_to_play if (
                    song_to_play <= len(songs) - 1) else 0
                current_song = songs[song_to_play]
                next_song = (song_to_play + 1) if (
                    (song_to_play + 1) <= len(songs) - 1) else 0
                cm.update_state('song_to_play', next_song)

        # Get filename to play and store the current song playing in state cfg
        song_filename = current_song[1]
        cm.update_state('current_song', songs.index(current_song))

    song_filename = song_filename.replace("$SYNCHRONIZED_LIGHTS_HOME",
                                          cm.HOME_DIR)

    # Ensure play_now is reset before beginning playback
    if play_now:
        cm.update_state('play_now', 0)
        play_now = 0

    # Initialize FFT stats
    matrix = [0 for _ in range(hc.GPIOLEN)]

    # Set up audio
    if song_filename.endswith('.wav'):
        musicfile = wave.open(song_filename, 'r')
    else:
        musicfile = decoder.open(song_filename)

    sample_rate = musicfile.getframerate()
    num_channels = musicfile.getnchannels()

    if _usefm == 'true':
        logging.info("Sending output as fm transmission")

        with open(os.devnull, "w") as dev_null:
            # play_stereo is always True as coded, Should it be changed to
            # an option in the config file?
            fm_process = subprocess.Popen(["sudo",
                                           cm.HOME_DIR + "/bin/pifm",
                                           "-",
                                           str(frequency),
                                           "44100",
                                           "stereo" if play_stereo else "mono"],\
                                               stdin=music_pipe_r,\
                                                   stdout=dev_null)
    else:
        output = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL)
        output.setchannels(num_channels)
        output.setrate(sample_rate)
        output.setformat(aa.PCM_FORMAT_S16_LE)
        output.setperiodsize(CHUNK_SIZE)

    logging.info("Playing: " + song_filename + " (" +
                 str(musicfile.getnframes() / sample_rate) + " sec)")
    # Output a bit about what we're about to play to the logs
    song_filename = os.path.abspath(song_filename)

    # create empty array for the cache_matrix
    cache_matrix = np.empty(shape=[0, hc.GPIOLEN])
    cache_found = False
    cache_filename = \
        os.path.dirname(song_filename) + "/." + os.path.basename(song_filename) + ".sync"

    # The values 12 and 1.5 are good estimates for first time playing back
    # (i.e. before we have the actual mean and standard deviations
    # calculated for each channel).
    mean = [12.0 for _ in range(hc.GPIOLEN)]
    std = [1.5 for _ in range(hc.GPIOLEN)]

    if args.readcache:
        # Read in cached fft
        try:
            # load cache from file using numpy loadtxt
            cache_matrix = np.loadtxt(cache_filename)
            cache_found = True

            # get std from matrix / located at index 0
            std = np.array(cache_matrix[0])

            # get mean from matrix / located at index 1
            mean = np.array(cache_matrix[1])

            # delete mean and std from the array
            cache_matrix = np.delete(cache_matrix, (0), axis=0)
            cache_matrix = np.delete(cache_matrix, (0), axis=0)

            logging.debug("std: " + str(std) + ", mean: " + str(mean))
        except IOError:
            logging.warn("Cached sync data song_filename not found: '" +
                         cache_filename + ".  One will be generated.")

    # Process audio song_filename
    row = 0
    data = musicfile.readframes(CHUNK_SIZE)
    frequency_limits = calculate_channel_frequency(
        _MIN_FREQUENCY, _MAX_FREQUENCY, _CUSTOM_CHANNEL_MAPPING,
        _CUSTOM_CHANNEL_FREQUENCIES)

    while data != '' and not play_now:
        if _usefm == 'true':
            os.write(music_pipe_w, data)
        else:
            output.write(data)

        # Control lights with cached timing values if they exist
        matrix = None
        if cache_found and args.readcache:
            if row < len(cache_matrix):
                matrix = cache_matrix[row]
            else:
                logging.warning(
                    "Ran out of cached FFT values, will update the cache.")
                cache_found = False

        if matrix == None:
            # No cache - Compute FFT in this chunk, and cache results
            matrix = fft.calculate_levels(data, CHUNK_SIZE, sample_rate,
                                          frequency_limits, hc.GPIOLEN)

            # Add the matrix to the end of the cache
            cache_matrix = np.vstack([cache_matrix, matrix])

        update_lights(matrix, mean, std)

        # Read next chunk of data from music song_filename
        data = musicfile.readframes(CHUNK_SIZE)
        row = row + 1

        # Load new application state in case we've been interrupted
        cm.load_state()
        play_now = int(cm.get_state('play_now', 0))

    if not cache_found:
        # Compute the standard deviation and mean values for the cache
        for i in range(0, hc.GPIOLEN):
            std[i] = np.std([item for item in cache_matrix[:, i] if item > 0])
            mean[i] = np.mean(
                [item for item in cache_matrix[:, i] if item > 0])

        # Add mean and std to the top of the cache
        cache_matrix = np.vstack([mean, cache_matrix])
        cache_matrix = np.vstack([std, cache_matrix])

        # Save the cache using numpy savetxt
        np.savetxt(cache_filename, cache_matrix)

        logging.info("Cached sync data written to '." + cache_filename +
                     "' [" + str(len(cache_matrix)) + " rows]")

    # Cleanup the pifm process
    if _usefm == 'true':
        fm_process.kill()

    # check for postshow
    done = PrePostShow('postshow').execute()

    # We're done, turn it all off and clean up things ;)
    hc.clean_up()
def main():
    '''main'''
    song_to_play = int(cm.get_state('song_to_play', 0))
    play_now = int(cm.get_state('play_now', 0))

    # Arguments
    parser = argparse.ArgumentParser()
    filegroup = parser.add_mutually_exclusive_group()
    filegroup.add_argument('--playlist',
                           default=_PLAYLIST_PATH,
                           help='Playlist to choose song from.')
    filegroup.add_argument('--file',
                           help='path to the song to play (required if no'
                           'playlist is designated)')
    parser.add_argument(
        '--readcache',
        type=int,
        default=1,
        help='read light timing from cache if available. Default: true')
    args = parser.parse_args()

    # Log everything to our log file
    # TODO(todd): Add logging configuration options.
    logging.basicConfig(
        filename=cm.LOG_DIR + '/music_and_lights.play.dbg',
        format='[%(asctime)s] %(levelname)s {%(pathname)s:%(lineno)d}'
        ' - %(message)s',
        level=logging.DEBUG)

    # Make sure one of --playlist or --file was specified
    if args.file == None and args.playlist == None:
        print "One of --playlist or --file must be specified"
        sys.exit()

    # Initialize Lights
    hc.initialize()

    # Only execute preshow if no specific song has been requested to be played right now
    if not play_now:
        execute_preshow(cm.lightshow()['preshow'])

    # Determine the next file to play
    song_filename = args.file
    if args.playlist != None and args.file == None:
        most_votes = [None, None, []]
        current_song = None
        with open(args.playlist, 'rb') as playlist_fp:
            fcntl.lockf(playlist_fp, fcntl.LOCK_SH)
            playlist = csv.reader(playlist_fp, delimiter='\t')
            songs = []
            for song in playlist:
                if len(song) < 2 or len(song) > 4:
                    logging.error(
                        'Invalid playlist.  Each line should be in the form: '
                        '<song name><tab><path to song>')
                    sys.exit()
                elif len(song) == 2:
                    song.append(set())
                else:
                    song[2] = set(song[2].split(','))
                    if len(song) == 3 and len(song[2]) >= len(most_votes[2]):
                        most_votes = song
                songs.append(song)
            fcntl.lockf(playlist_fp, fcntl.LOCK_UN)

        if most_votes[0] != None:
            logging.info("Most Votes: " + str(most_votes))
            current_song = most_votes

            # Update playlist with latest votes
            with open(args.playlist, 'wb') as playlist_fp:
                fcntl.lockf(playlist_fp, fcntl.LOCK_EX)
                writer = csv.writer(playlist_fp, delimiter='\t')
                for song in songs:
                    if current_song == song and len(song) == 3:
                        song.append("playing!")
                    if len(song[2]) > 0:
                        song[2] = ",".join(song[2])
                    else:
                        del song[2]
                writer.writerows(songs)
                fcntl.lockf(playlist_fp, fcntl.LOCK_UN)

        else:
            # Get random song
            if _RANDOMIZE_PLAYLIST:
                current_song = songs[random.randint(0, len(songs) - 1)]
            # Get a "play now" requested song
            elif play_now > 0 and play_now <= len(songs):
                current_song = songs[play_now - 1]
            # Play next song in the lineup
            else:
                song_to_play = song_to_play if (
                    song_to_play <= len(songs) - 1) else 0
                current_song = songs[song_to_play]
                next_song = (song_to_play + 1) if (
                    (song_to_play + 1) <= len(songs) - 1) else 0
                cm.update_state('song_to_play', next_song)

        # Get filename to play and store the current song playing in state cfg
        song_filename = current_song[1]
        cm.update_state('current_song', songs.index(current_song))

    song_filename = song_filename.replace("$SYNCHRONIZED_LIGHTS_HOME",
                                          cm.HOME_DIR)

    # Ensure play_now is reset before beginning playback
    if play_now:
        cm.update_state('play_now', 0)
        play_now = 0

    # Initialize FFT stats
    matrix = [0 for _ in range(hc.GPIOLEN)]
    offct = [0 for _ in range(hc.GPIOLEN)]

    # Build the limit list
    if len(_LIMIT_LIST) == 1:
        limit = [_LIMIT_LIST[0] for _ in range(hc.GPIOLEN)]
    else:
        limit = _LIMIT_LIST

    # Set up audio
    if song_filename.endswith('.wav'):
        musicfile = wave.open(song_filename, 'r')
    else:
        musicfile = decoder.open(song_filename)

    sample_rate = musicfile.getframerate()
    num_channels = musicfile.getnchannels()
    output = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL)
    output.setchannels(num_channels)
    output.setrate(sample_rate)
    output.setformat(aa.PCM_FORMAT_S16_LE)
    output.setperiodsize(CHUNK_SIZE)

    # Output a bit about what we're about to play
    song_filename = os.path.abspath(song_filename)
    logging.info("Playing: " + song_filename + " (" +
                 str(musicfile.getnframes() / sample_rate) + " sec)")

    cache = []
    cache_found = False
    cache_filename = os.path.dirname(song_filename) + "/." + os.path.basename(song_filename) \
        + ".sync.gz"
    # The values 12 and 1.5 are good estimates for first time playing back (i.e. before we have
    # the actual mean and standard deviations calculated for each channel).
    mean = [12.0 for _ in range(hc.GPIOLEN)]
    std = [1.5 for _ in range(hc.GPIOLEN)]
    if args.readcache:
        # Read in cached fft
        try:
            with gzip.open(cache_filename, 'rb') as playlist_fp:
                cachefile = csv.reader(playlist_fp, delimiter=',')
                for row in cachefile:
                    cache.append([
                        0.0 if np.isinf(float(item)) else float(item)
                        for item in row
                    ])
                cache_found = True
                # TODO(todd): Optimize this and / or cache it to avoid delay here
                cache_matrix = np.array(cache)
                for i in range(0, hc.GPIOLEN):
                    std[i] = np.std(
                        [item for item in cache_matrix[:, i] if item > 0])
                    mean[i] = np.mean(
                        [item for item in cache_matrix[:, i] if item > 0])
                logging.debug("std: " + str(std) + ", mean: " + str(mean))
        except IOError:
            logging.warn("Cached sync data song_filename not found: '" +
                         cache_filename + ".  One will be generated.")

    # Process audio song_filename
    row = 0
    data = musicfile.readframes(CHUNK_SIZE)
    frequency_limits = calculate_channel_frequency(
        _MIN_FREQUENCY, _MAX_FREQUENCY, _CUSTOM_CHANNEL_MAPPING,
        _CUSTOM_CHANNEL_FREQUENCIES)

    while data != '' and not play_now:
        output.write(data)

        # Control lights with cached timing values if they exist
        matrix = None
        if cache_found and args.readcache:
            if row < len(cache):
                matrix = cache[row]
            else:
                logging.warning(
                    "Ran out of cached FFT values, will update the cache.")
                cache_found = False

        if matrix == None:
            # No cache - Compute FFT in this chunk, and cache results
            matrix = calculate_levels(data, sample_rate, frequency_limits)
            cache.append(matrix)

        # blank out the display
        led.fill(Color(0, 0, 0), 0, 151)
        for i in range(0, hc.GPIOLEN):
            if hc.is_pin_pwm(i):
                # Output pwm, where off is at 0.5 std below the mean
                # and full on is at 0.75 std above the mean.

                display_column(i, matrix[i])

                #brightness = matrix[i] - mean[i] + 0.5 * std[i]
                #brightness = brightness / (1.25 * std[i])
                #if brightness > 1.0:
                #brightness = 1.0
                #if brightness < 0:
                #brightness = 0
                #hc.turn_on_light(i, True, int(brightness * 60))
            else:
                if limit[i] < matrix[i] * _LIMIT_THRESHOLD:
                    limit[i] = limit[i] * _LIMIT_THRESHOLD_INCREASE
                    logging.debug("++++ channel: {0}; limit: {1:.3f}".format(
                        i, limit[i]))
                # Amplitude has reached threshold
                if matrix[i] > limit[i]:
                    hc.turn_on_light(i, True)
                    offct[i] = 0
                else:  # Amplitude did not reach threshold
                    offct[i] = offct[i] + 1
                    if offct[i] > _MAX_OFF_CYCLES:
                        offct[i] = 0
                        limit[i] = limit[
                            i] * _LIMIT_THRESHOLD_DECREASE  # old value 0.8
                    logging.debug("---- channel: {0}; limit: {1:.3f}".format(
                        i, limit[i]))
                    hc.turn_off_light(i, True)

        # send out data to RGB LED Strip
        led.update()
        # Read next chunk of data from music song_filename
        data = musicfile.readframes(CHUNK_SIZE)
        row = row + 1

        # Load new application state in case we've been interrupted
        cm.load_state()
        play_now = int(cm.get_state('play_now', 0))

    if not cache_found:
        with gzip.open(cache_filename, 'wb') as playlist_fp:
            writer = csv.writer(playlist_fp, delimiter=',')
            writer.writerows(cache)
            logging.info("Cached sync data written to '." + cache_filename +
                         "' [" + str(len(cache)) + " rows]")

    # We're done, turn it all off ;)
    hc.clean_up()