Пример #1
0
def main():
    """
    Empyt script template for you to use
    """
    # initialize our hardware for use
    hc.initialize()

    # USE ONE OF THE BELOW LOOPS AS A STARTING POINT
    # JUST UNCOMMENT ONE AND DELETE THE OTHERS

    #for count in range(10):

    #count = 0
    #while count < 10:
        #count += 1

    # change number_of_seconds_to_run_for to the number of seconds you what
    # this to run for 60 is 1 minute 3600 is 1 hour

    #end_time = time.time() + number_of_seconds_to_run_for
    #while time.time() < end_time:

    ####<PUT ALL YOU CODE IN THIS BLOCK>

    # clean up and end
    hc.clean_up()
Пример #2
0
def main():
    """Turn all the lights on for 2 minutes"""

    # initialize your hardware for use
    hc.initialize()

    # turn on all the lights
    hc.turn_on_lights()

    # run for 2 minutes
    end = time.time() + 120

    # working loop will run as long as time.time() is less then "end"
    while time.time() < end:
        # try except block to catch keyboardinterrupt by user to stop
        try:
            # do nothing, just wait
            pass
        # if the user pressed <CTRL> + C to exit early break out of the loop
        except KeyboardInterrupt:
            print "\nstopped"
            break

    # This ends and cleans up everything
    hc.clean_up()
Пример #3
0
def main():
    """Turn all the lights on for 2 minutes"""

    # initialize your hardware for use
    hc.initialize()

    # turn on all the lights
    hc.turn_on_lights()

    # run for 2 minutes
    end = time.time() + 120

    # working loop will run as long as time.time() is less then "end"
    while time.time() < end:
        # try except block to catch keyboardinterrupt by user to stop
        try:
            # do nothing, just wait
            pass
        # if the user pressed <CTRL> + C to exit early break out of the loop
        except KeyboardInterrupt:
            print "\nstopped"
            break

    # This ends and cleans up everything
    hc.clean_up()
Пример #4
0
def main():
    """
    Play a message

    Play a recorded message for the people and go through the lights
    one channel at a time in order, then back down to the first
    """

    # initialize your hardware for use
    hc.initialize()

    # start with all the lights off
    hc.turn_off_lights()

    # Before we start the lights we should start playing the audio
    # we have installed mpg123 to make this easy
    # if you do not have mpg123 installed then use this command to install it
    # sudo apt-get install mpg123
    # now all you have to do is use the below command to play an mp3 file
    message_file = "/home/pi/lightshowpi/py/examples/message.mp3"
    message = subprocess.Popen(["mpg123", "-q", message_file])

    # subprocess.Popen will open mpg123 player and play an audio file for you
    # and give you a few options that will come in real handy
    # you can stop mpg123 before the audio has finished using the instance
    # variable we just created by calling message.kill()
    # or at any point in the script you can make everything wait for the audio
    # to finish playing with message.wait() that could be usefull if you
    # ran a short seuqence like in the default preshow and your audio as longer
    # then your sequence and you wanted the audio to finish before continuing
    # and if you use message.poll() or message.returncode you could find out
    # if it has finished, then you might start something else or end everything
    # and shutdown your pi.

    # working loop
    while True:
        # try except block to catch keyboardinterrupt by user to stop
        try:
            hc.turn_on_lights()

        except KeyboardInterrupt:
            print "\nstopped"
            break

        # if audio playback has finished break out of the loop
        if message.poll() != None:
            break

    # This ends and cleans up everything
    hc.clean_up()
Пример #5
0
def main():
    """
    Play a message

    Play a recorded message for the people and go through the lights
    one channel at a time in order, then back down to the first
    """

    # initialize your hardware for use
    hc.initialize()

    # start with all the lights off
    hc.turn_off_lights()

    # Before we start the lights we should start playing the audio
    # we have installed mpg123 to make this easy
    # if you do not have mpg123 installed then use this command to install it
    # sudo apt-get install mpg123
    # now all you have to do is use the below command to play an mp3 file
    message_file = "/home/pi/lightshowpi/py/examples/message.mp3"
    message = subprocess.Popen(["mpg123", "-q", message_file])

    # subprocess.Popen will open mpg123 player and play an audio file for you
    # and give you a few options that will come in real handy
    # you can stop mpg123 before the audio has finished using the instance
    # variable we just created by calling message.kill()
    # or at any point in the script you can make everything wait for the audio
    # to finish playing with message.wait() that could be usefull if you
    # ran a short seuqence like in the default preshow and your audio as longer
    # then your sequence and you wanted the audio to finish before continuing
    # and if you use message.poll() or message.returncode you could find out
    # if it has finished, then you might start something else or end everything
    # and shutdown your pi.

    # working loop
    while True:
        # try except block to catch keyboardinterrupt by user to stop
        try:
            hc.turn_on_lights()

        except KeyboardInterrupt:
            print "\nstopped"
            break

        # if audio playback has finished break out of the loop
        if message.poll() != None:
            break

    # This ends and cleans up everything
    hc.clean_up()
def network_client():
    """Network client support

    If in client mode, ignore everything else and just
    read data from the network and blink the lights
    """
    log.info("Network client mode starting")
    print "Network client mode starting..."
    print "press CTRL<C> to end"

    hc.initialize()

    print

    try:
        channels = network.channels
        channel_keys = channels.keys()

        while True:
            data = network.receive()

            if isinstance(data[0], int):
                pin = data[0]
                if pin in channel_keys:
                    hc.set_light(channels[pin], True, float(data[1]))
                continue

            elif isinstance(data[0], np.ndarray):
                blevels = data[0]

            else:
                continue

            for pin in channel_keys:
                hc.set_light(channels[pin], True, blevels[pin])

    except KeyboardInterrupt:
        log.info("CTRL<C> pressed, stopping")
        print "stopping"

        network.close_connection()
        hc.clean_up()
Пример #7
0
def network_client():
    """Network client support

    If in client mode, ignore everything else and just
    read data from the network and blink the lights
    """
    log.info("Network client mode starting")
    print "Network client mode starting..."
    print "press CTRL<C> to end"

    hc.initialize()

    print

    try:
        channels = network.channels
        channel_keys = channels.keys()

        while True:
            data = network.receive()

            if isinstance(data[0], int):
                pin = data[0]
                if pin in channel_keys:
                    hc.set_light(channels[pin], True, float(data[1]))
                continue

            elif isinstance(data[0], np.ndarray):
                blevels = data[0]

            else:
                continue

            for pin in channel_keys:
                hc.set_light(channels[pin], True, blevels[pin])

    except KeyboardInterrupt:
        log.info("CTRL<C> pressed, stopping")
        print "stopping"

        network.close_connection()
        hc.clean_up()
Пример #8
0
def main():
    """
    Test pattern2

    Unlights one channel at a time in order
    """
    # this is a list of all the channels you have access to
    lights = hc._GPIO_PINS

    # initialize your hardware for use
    hc.initialize()

    # start with all the lights off
    hc.turn_off_lights()

    # pause for 1 second
    time.sleep(2)

    # working loop
    for _ in range(50):
        # try except block to catch keyboardinterrupt by user to stop
        try:
            # here we just loop over the gpio pins and do something with them
            for light in lights:
                # turn on all the lights
                hc.turn_on_lights()

                # then turn off one
                hc.turn_off_light(light)

                # wait a little bit before the for loop
                # starts again and turns off the next light
                time.sleep(.4)

        # if the user pressed <CTRL> + C to exit early break out of the loop
        except KeyboardInterrupt:
            print "\nstopped"
            break

    # This ends and cleans up everything
    hc.clean_up()
Пример #9
0
def initialize_interface():
    global initialized
    if not initialized:
        initialize()
        initialized = True
Пример #10
0
def play_song(num_songs):
    """Play the next song from the play list (or --file argument)."""
    play_now = int(cm.get_state('play_now', "0"))

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

    current_playlist = playlist.Playlist(args.playlist, num_songs)

    # Initialize Lights
    hc.initialize()

    # Fork and warm the cache. Technically race prone but meh
    pool = multiprocessing.pool.Pool(processes=1)

    for (song_title, song_filename, chunk_size) in current_playlist.get_song():

        cache_proc = pool.apply_async(cache_song, [song_filename, chunk_size])

        # Handle the pre/post show
        if not play_now:
            result = prepostshow.PrePostShow('preshow', hc).execute()

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

        # TODO(mdietz): What the hell is this play_now variable really for?
        # Ensure play_now is reset before beginning playback
        if play_now:
            cm.update_state('play_now', "0")
            play_now = 0

        # Wait for the cache
        cache_proc.wait()
        mean, std, cache_matrix = cache_proc.get()

        # NOTE(mdietz): Adapt this to a standard radio, not an SDR. The SDR
        #               has a clear extra amount of delay
        light_show_delay = _CONFIG.getfloat("lightshow", "light_delay")
        logging.info("Delaying light show by %f seconds" % light_show_delay)

        audio_in_stream = audio_input.get_audio_input_handler(
            song_filename, chunk_size)
        audio_out_stream = audio_output.get_audio_output_handler(
            audio_in_stream.num_channels, audio_in_stream.sample_rate,
            song_title, chunk_size)

        try:
            # Process audio
            row = 0
            start_time = time.time()
            while True:
                data = audio_in_stream.next_chunk()
                if not data or play_now:
                    break

                audio_out_stream.write(data)
                # TODO(mdietz): This actually pretty much works, but it would
                #               be nice to figure out what the actual delay
                #               time is, and also make it a config value
                # TODO(mdietz): I may be able to time the popen to first stdout
                #               from the fm proc for a dynamic delay
                if time.time() - start_time < light_show_delay:
                    continue

                matrix = cache_matrix[row]
                update_lights(matrix, mean, std)

                # Read next chunk of data from music

                # Load new application state in case we've been interrupted
                # TODO(mdietz): not the way to do this. Read from a db,
                #               accept a signal or some other OOB proc
                cm.load_state()
                play_now = int(cm.get_state('play_now', "0"))
                row += 1

            # Cleanup the fm process if there is one
        except Exception:
            logging.exception("Error in playback")
        finally:
            audio_out_stream.cleanup()

        # check for postshow
        prepostshow.PrePostShow('postshow', hc).execute()
Пример #11
0
def main():
    """
    PWM example

    Start at each end and walk to the other using pwm
    """
    # this is a list of all the channels you have access to
    lights = hc._GPIO_PINS

    # the gpio pins in reversed order
    lights2 = lights[::-1]

    # get _PWM_MAX from the hc module
    # this is the max value for the pwm channels
    pwm_max = hc._PWM_MAX
    # initialize your hardware for use
    hc.initialize()

    # start with all the lights off
    hc.turn_off_lights()

    # pause for 1 second
    time.sleep(1)

    # working loop, we will do this sequence 10 times then end
    for _ in range(10):
        # here we just loop over the gpio pins and turn then on and off
        # with the pwm feature of lightshowpi
        for light in range(int(len(lights) / 2)):
            if hc.is_pin_pwm(lights[light]) and hc.is_pin_pwm(lights2[light]):
                for brightness in range(0, pwm_max):
                    # fade in
                    hc.turn_on_light(lights[light], 0,
                                     float(brightness) / pwm_max)

                    hc.turn_on_light(lights2[light], 0,
                                     float(brightness) / pwm_max)

                    time.sleep(.05 / pwm_max)

                for brightness in range(pwm_max - 1, -1, -1):
                    # fade out
                    hc.turn_on_light(lights[light], 0,
                                     float(brightness) / pwm_max)

                    hc.turn_on_light(lights2[light], 0,
                                     float(brightness) / pwm_max)

                    time.sleep(.05 / pwm_max)

        for light in range(int(len(lights) / 2) - 1, -1, -1):
            if hc.is_pin_pwm(lights[light]) and hc.is_pin_pwm(lights2[light]):
                for brightness in range(0, pwm_max):
                    # fade in
                    hc.turn_on_light(lights[light], 0,
                                     float(brightness) / pwm_max)

                    hc.turn_on_light(lights2[light], 0,
                                     float(brightness) / pwm_max)

                    time.sleep(.05 / pwm_max)

                for brightness in range(pwm_max - 1, -1, -1):
                    # fade out
                    hc.turn_on_light(lights[light], 0,
                                     float(brightness) / pwm_max)

                    hc.turn_on_light(lights2[light], 0,
                                     float(brightness) / pwm_max)

                    time.sleep(.05 / pwm_max)

    # This ends and cleans up everything
    hc.clean_up()
Пример #12
0
def audio_in():
    """Control the lightshow from audio coming in from a USB audio card"""
    sample_rate = cm.lightshow()['audio_in_sample_rate']
    input_channels = cm.lightshow()['audio_in_channels']

    # Open the input stream from default input device
    audio_in_card = cm.lightshow()['audio_in_card']
    stream = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NORMAL, audio_in_card)
    stream.setchannels(input_channels)
    stream.setformat(aa.PCM_FORMAT_S16_LE)  # Expose in config if needed
    stream.setrate(sample_rate)
    stream.setperiodsize(CHUNK_SIZE)

    logging.debug("Running in audio-in mode - will run until Ctrl+C "
                  "is pressed")
    print "Running in audio-in mode, use Ctrl+C to stop"

    # Start with these as our initial guesses - will calculate a rolling
    # mean / std as we get input data.
    mean = np.array([12.0] * hc.GPIOLEN, dtype='float64')
    std = np.array([1.5] * hc.GPIOLEN, dtype='float64')
    count = 2

    running_stats = running_stats.Stats(hc.GPIOLEN)

    # preload running_stats to avoid errors, and give us a show that looks
    # good right from the start
    running_stats.preload(mean, std, count)

    try:
        hc.initialize()
        fft_calc = fft.FFT(CHUNK_SIZE,
                           sample_rate,
                           hc.GPIOLEN,
                           _MIN_FREQUENCY,
                           _MAX_FREQUENCY,
                           _CUSTOM_CHANNEL_MAPPING,
                           _CUSTOM_CHANNEL_FREQUENCIES,
                           input_channels)

        # Listen on the audio input device until CTRL-C is pressed
        while True:
            length, data = stream.read()
            if length > 0:
                # if the maximum of the absolute value of all samples in
                # data is below a threshold we will disreguard it
                audio_max = audioop.max(data, 2)
                if audio_max < 250:
                    # we will fill the matrix with zeros and turn the
                    # lights off
                    matrix = np.zeros(hc.GPIOLEN, dtype="float64")
                    logging.debug("below threshold: '" + str(
                        audio_max) + "', turning the lights off")
                else:
                    matrix = fft_calc.calculate_levels(data)
                    running_stats.push(matrix)
                    mean = running_stats.mean()
                    std = running_stats.std()

                update_lights(matrix, mean, std)

    except KeyboardInterrupt:
        pass

    finally:
        print "\nStopping"
        hc.clean_up()
Пример #13
0
def play_song():
    """Play the next song from the play list (or --file argument)."""

    # get the next song to play
    song_filename, config_filename, cache_filename = get_song()

    # load custom configuration from file
    load_custom_config(config_filename)

    # Initialize Lights
    network.set_playing()
    hc.initialize()

    # Handle the pre/post show
    play_now = int(cm.get_state('play_now', "0"))

    network.unset_playing()

    if not play_now:
        result = PrePostShow('preshow', hc).execute()

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

    network.set_playing()

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

    # setup audio file and output device
    output, fft_calc, music_file, light_delay = setup_audio(song_filename)

    # setup our cache_matrix, std, mean
    cache_found, cache_matrix, std, mean = setup_cache(cache_filename,
                                                       fft_calc)

    matrix_buffer = deque([], 1000)

    # Process audio song_filename
    row = 0
    data = music_file.readframes(CHUNK_SIZE)

    while data != '' and not play_now:
        # output data to sound device
        output(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:
                log.warning(
                    "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_calc.calculate_levels(data)

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

        matrix_buffer.appendleft(matrix)

        if len(matrix_buffer) > light_delay:
            matrix = matrix_buffer[light_delay]
            update_lights(matrix, mean, std)

        # 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 and not play_now:
        save_cache(cache_matrix, cache_filename, fft_calc)

    # Cleanup the pifm process
    if cm.audio_processing.fm:
        fm_process.kill()

    # check for postshow
    network.unset_playing()

    if not play_now:
        PrePostShow('postshow', hc).execute()

    # We're done, turn it all off and clean up things ;)
    hc.clean_up()
Пример #14
0
def main():
    """
    Random flashing lights
    """
    # this is a list of all the channels you have access to
    # I'm also tracking the time here so that I know when I turned a light off
    # So I'm putting everything in a dict
    gpio_pins = hc._GPIO_PINS
    lights = dict.fromkeys(range(0, len(gpio_pins)), [True, time.time()])

    # get a number that is about 40% the length of your gpio's
    # this will be use to make sure that no more then 40% of
    # the light will be off at any one time
    max_off = int(round(len(lights) * .4))

    # initialize your hardware for use
    hc.initialize()
    print "Press <CTRL>-C to stop"

    # start with all the lights on
    hc.turn_on_lights()

    # lets run for 2 minutes
    end = time.time() + 120

    # working loop will run as long as time.time() is less then "end"
    while time.time() < end:
        # try except block to catch keyboardinterrupt by user to stop
        try:
            # here we just loop over the gpio pins
            for light in lights:
                # this is where we check to see if we have any light
                # that are turned off
                # if they are off we will check the time to see if we
                # want to turn them back on yet, if we do then turn it on
                if not lights[light][0]:
                    if lights[light][1] < time.time():
                        lights[light][0] = True
                        hc.turn_on_light(light)

            # count the number of lights that are off
            off = [k for (k, v) in lights.iteritems() if v.count(1) == False]

            # if less then out max count of light that we chose
            # we can turn one off
            if len(off) < max_off:
                # pick a light at random to turn off
                choice = random.randrange(0, len(gpio_pins))
                # if it's on then lets turn it off
                if lights[choice][0]:
                    # pick a duration for that light to be off
                    # default times are between 1/2 and secong and 1.8 seconds
                    duration = random.uniform(0.5, 1.8)

                    # store this informatin in our dict
                    lights[choice] = [False, time.time() + duration]
                    # and turn that light off then continue with the main loop
                    # and do it all over again
                    hc.turn_off_light(choice)

        # if the user pressed <CTRL> + C to exit early break out of the loop
        except KeyboardInterrupt:
            print "\nstopped"
            break

    # This ends and cleans up everything
    hc.clean_up()
def audio_in():
    '''Control the lightshow from audio coming in from a USB audio card'''
    sample_rate = cm.lightshow()['audio_in_sample_rate']
    input_channels = cm.lightshow()['audio_in_channels']

    # Open the input stream from default input device
    stream = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NORMAL, cm.lightshow()['audio_in_card'])
    stream.setchannels(input_channels)
    stream.setformat(aa.PCM_FORMAT_S16_LE) # Expose in config if needed
    stream.setrate(sample_rate)
    stream.setperiodsize(CHUNK_SIZE)
         
    logging.debug("Running in audio-in mode - will run until Ctrl+C is pressed")
    print "Running in audio-in mode, use Ctrl+C to stop"
    try:
        hc.initialize()
        frequency_limits = calculate_channel_frequency(_MIN_FREQUENCY,
                                                       _MAX_FREQUENCY,
                                                       _CUSTOM_CHANNEL_MAPPING,
                                                       _CUSTOM_CHANNEL_FREQUENCIES)

        # Start with these as our initial guesses - will calculate a rolling mean / std 
        # as we get input data.
        mean = [12.0 for _ in range(hc.GPIOLEN)]
        std = [0.5 for _ in range(hc.GPIOLEN)]
        recent_samples = np.empty((250, hc.GPIOLEN))
        num_samples = 0
    
        # Listen on the audio input device until CTRL-C is pressed
        while True:            
            l, data = stream.read()
            
            if l:
                try:
                    matrix = fft.calculate_levels(data, CHUNK_SIZE, sample_rate, frequency_limits, input_channels)
                    if not np.isfinite(np.sum(matrix)):
                        # Bad data --- skip it
                        continue
                except ValueError as e:
                    # TODO(todd): This is most likely occuring due to extra time in calculating
                    # mean/std every 250 samples which causes more to be read than expected the
                    # next time around.  Would be good to update mean/std in separate thread to
                    # avoid this --- but for now, skip it when we run into this error is good 
                    # enough ;)
                    logging.debug("skipping update: " + str(e))
                    continue

                update_lights(matrix, mean, std)

                # Keep track of the last N samples to compute a running std / mean
                #
                # TODO(todd): Look into using this algorithm to compute this on a per sample basis:
                # http://www.johndcook.com/blog/standard_deviation/                
                if num_samples >= 250:
                    no_connection_ct = 0
                    for i in range(0, hc.GPIOLEN):
                        mean[i] = np.mean([item for item in recent_samples[:, i] if item > 0])
                        std[i] = np.std([item for item in recent_samples[:, i] if item > 0])
                        
                        # Count how many channels are below 10, if more than 1/2, assume noise (no connection)
                        if mean[i] < 10.0:
                            no_connection_ct += 1
                            
                    # If more than 1/2 of the channels appear to be not connected, turn all off
                    if no_connection_ct > hc.GPIOLEN / 2:
                        logging.debug("no input detected, turning all lights off")
                        mean = [20 for _ in range(hc.GPIOLEN)]
                    else:
                        logging.debug("std: " + str(std) + ", mean: " + str(mean))
                    num_samples = 0
                else:
                    for i in range(0, hc.GPIOLEN):
                        recent_samples[num_samples][i] = matrix[i]
                    num_samples += 1
 
    except KeyboardInterrupt:
        pass
    finally:
        print "\nStopping"
        hc.clean_up()
Пример #16
0
def play_song():
    """Play the next song from the play list (or --file argument)."""

    # get the next song to play
    song_filename, config_filename, cache_filename = get_song()

    # load custom configuration from file
    load_custom_config(config_filename)

    # Initialize Lights
    network.set_playing()
    hc.initialize()

    # Handle the pre/post show
    play_now = int(cm.get_state('play_now', "0"))

    network.unset_playing()

    if not play_now:
        result = PrePostShow('preshow', hc).execute()

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

    network.set_playing()

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

    # setup audio file and output device
    output, fft_calc, music_file, light_delay = setup_audio(song_filename)

    # setup our cache_matrix, std, mean
    cache_found, cache_matrix, std, mean = setup_cache(cache_filename, fft_calc)

    matrix_buffer = deque([], 1000)

    # Process audio song_filename
    row = 0
    data = music_file.readframes(CHUNK_SIZE)

    while data != '' and not play_now:
        # output data to sound device
        output(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:
                log.warning("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_calc.calculate_levels(data)

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

        matrix_buffer.appendleft(matrix)

        if len(matrix_buffer) > light_delay:
            matrix = matrix_buffer[light_delay]
            update_lights(matrix, mean, std)

        # 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:
        save_cache(cache_matrix, cache_filename, fft_calc)

    # Cleanup the pifm process
    if cm.audio_processing.fm:
        fm_process.kill()

    # check for postshow
    network.unset_playing()

    if not play_now:
        PrePostShow('postshow', hc).execute()

    # We're done, turn it all off and clean up things ;)
    hc.clean_up()
Пример #17
0
def audio_in():
    """Control the lightshow from audio coming in from a real time audio"""
    global streaming
    stream_reader = None
    streaming = None

    sample_rate = cm.lightshow.input_sample_rate
    num_channels = cm.lightshow.input_channels

    if cm.lightshow.mode == 'audio-in':
        # Open the input stream from default input device
        streaming = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NORMAL, cm.lightshow.audio_in_card)
        streaming.setchannels(num_channels)
        streaming.setformat(aa.PCM_FORMAT_S16_LE)  # Expose in config if needed
        streaming.setrate(sample_rate)
        streaming.setperiodsize(CHUNK_SIZE)

        stream_reader = lambda: streaming.read()[-1]

    elif cm.lightshow.mode == 'stream-in':

        if cm.lightshow.use_fifo:
            streaming = subprocess.Popen(cm.lightshow.stream_command_string,
                                         stdin=subprocess.PIPE,
                                         stdout=subprocess.PIPE,
                                         preexec_fn=os.setsid)
            io = os.open(cm.lightshow.fifo, os.O_RDONLY | os.O_NONBLOCK)
            stream_reader = lambda: os.read(io, CHUNK_SIZE)
        else:
            # Open the input stream from command string
            streaming = subprocess.Popen(cm.lightshow.stream_command_string,
                                         stdin=subprocess.PIPE,
                                         stdout=subprocess.PIPE,
                                         stderr=subprocess.PIPE)
            stream_reader = lambda: streaming.stdout.read(CHUNK_SIZE)

    log.debug("Running in %s mode - will run until Ctrl+C is pressed" % cm.lightshow.mode)
    print "Running in %s mode, use Ctrl+C to stop" % cm.lightshow.mode

    # setup light_delay.
    chunks_per_sec = ((16 * num_channels * sample_rate) / 8) / CHUNK_SIZE
    light_delay = int(cm.audio_processing.light_delay * chunks_per_sec)
    matrix_buffer = deque([], 1000)

    output = set_audio_device(sample_rate, num_channels)

    # Start with these as our initial guesses - will calculate a rolling mean / std 
    # as we get input data.
    mean = np.array([12.0 for _ in range(hc.GPIOLEN)], dtype='float32')
    std = np.array([1.5 for _ in range(hc.GPIOLEN)], dtype='float32')
    count = 2

    running_stats = RunningStats.Stats(hc.GPIOLEN)

    # preload running_stats to avoid errors, and give us a show that looks
    # good right from the start
    running_stats.preload(mean, std, count)

    hc.initialize()
    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,
                       1)

    if server:
        network.network.set_playing()

    # Listen on the audio input device until CTRL-C is pressed
    while True:
        try:
            data = stream_reader()

        except OSError as err:
            if err.errno == errno.EAGAIN or err.errno == errno.EWOULDBLOCK:
                continue
        try:
            output(data)
        except aa.ALSAAudioError:
            continue

        if len(data):
            # if the maximum of the absolute value of all samples in
            # data is below a threshold we will disregard it
            audio_max = audioop.max(data, 2)
            if audio_max < 250:
                # we will fill the matrix with zeros and turn the lights off
                matrix = np.zeros(hc.GPIOLEN, dtype="float32")
                log.debug("below threshold: '" + str(audio_max) + "', turning the lights off")
            else:
                matrix = fft_calc.calculate_levels(data)
                running_stats.push(matrix)
                mean = running_stats.mean()
                std = running_stats.std()

            matrix_buffer.appendleft(matrix)

            if len(matrix_buffer) > light_delay:
                matrix = matrix_buffer[light_delay]
                update_lights(matrix, mean, std)
Пример #18
0
 def __init__(self, show="preshow"):
     hc.initialize()
     self.config = cm.lightshow()[show]
     self.show = show
     self.audio = None
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()
Пример #20
0
def play_song(num_songs):
    """Play the next song from the play list (or --file argument)."""
    play_now = int(cm.get_state('play_now', "0"))

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

    current_playlist = playlist.Playlist(args.playlist, num_songs)

    # Initialize Lights
    hc.initialize()

    # Fork and warm the cache. Technically race prone but meh
    pool = multiprocessing.pool.Pool(processes=1)

    for (song_title,
         song_filename,
         chunk_size) in current_playlist.get_song():

        cache_proc = pool.apply_async(cache_song, [song_filename, chunk_size])

        # Handle the pre/post show
        if not play_now:
            result = prepostshow.PrePostShow('preshow', hc).execute()

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

        # TODO(mdietz): What the hell is this play_now variable really for?
        # Ensure play_now is reset before beginning playback
        if play_now:
            cm.update_state('play_now', "0")
            play_now = 0

        # Wait for the cache
        cache_proc.wait()
        mean, std, cache_matrix = cache_proc.get()

        # NOTE(mdietz): Adapt this to a standard radio, not an SDR. The SDR
        #               has a clear extra amount of delay
        light_show_delay = _CONFIG.getfloat("lightshow", "light_delay")
        logging.info("Delaying light show by %f seconds" % light_show_delay)

        audio_in_stream = audio_input.get_audio_input_handler(song_filename,
                                                              chunk_size)
        audio_out_stream = audio_output.get_audio_output_handler(
            audio_in_stream.num_channels, audio_in_stream.sample_rate,
            song_title, chunk_size)

        try:
            # Process audio
            row = 0
            start_time = time.time()
            while True:
                data = audio_in_stream.next_chunk()
                if not data or play_now:
                    break

                audio_out_stream.write(data)
                # TODO(mdietz): This actually pretty much works, but it would
                #               be nice to figure out what the actual delay
                #               time is, and also make it a config value
                # TODO(mdietz): I may be able to time the popen to first stdout
                #               from the fm proc for a dynamic delay
                if time.time() - start_time < light_show_delay:
                    continue

                matrix = cache_matrix[row]
                update_lights(matrix, mean, std)

                # Read next chunk of data from music

                # Load new application state in case we've been interrupted
                # TODO(mdietz): not the way to do this. Read from a db,
                #               accept a signal or some other OOB proc
                cm.load_state()
                play_now = int(cm.get_state('play_now', "0"))
                row += 1

            # Cleanup the fm process if there is one
        except Exception:
            logging.exception("Error in playback")
        finally:
            audio_out_stream.cleanup()

        # check for postshow
        prepostshow.PrePostShow('postshow', hc).execute()
Пример #21
0
def main():
    """
    Random flashing lights
    """
    # this is a list of all the channels you have access to
    # I'm also tracking the time here so that I know when I turned a light off
    # So I'm putting everything in a dict
    gpio_pins = hc._GPIO_PINS
    lights = dict.fromkeys(range(0, len(gpio_pins)), [True, time.time()])

    # get a number that is about 40% the length of your gpio's
    # this will be use to make sure that no more then 40% of
    # the light will be off at any one time
    max_off = int(round(len(lights) * .4))

    # initialize your hardware for use
    hc.initialize()
    print "Press <CTRL>-C to stop"

    # start with all the lights on
    hc.turn_on_lights()

    # lets run for 2 minutes
    end = time.time() + 120

    # working loop will run as long as time.time() is less then "end"
    while time.time() < end:
        # try except block to catch keyboardinterrupt by user to stop
        try:
            # here we just loop over the gpio pins
            for light in lights:
                # this is where we check to see if we have any light
                # that are turned off
                # if they are off we will check the time to see if we
                # want to turn them back on yet, if we do then turn it on
                if not lights[light][0]:
                    if lights[light][1] < time.time():
                        lights[light][0] = True
                        hc.turn_on_light(light)

            # count the number of lights that are off
            off = [k for (k, v) in lights.iteritems() if v.count(1) == False]

            # if less then out max count of light that we chose
            # we can turn one off
            if len(off) < max_off:
                # pick a light at random to turn off
                choice = random.randrange(0, len(gpio_pins))
                # if it's on then lets turn it off
                if lights[choice][0]:
                    # pick a duration for that light to be off
                    # default times are between 1/2 and secong and 1.8 seconds
                    duration = random.uniform(0.5, 1.8)

                    # store this informatin in our dict
                    lights[choice] = [False, time.time() + duration]
                    # and turn that light off then continue with the main loop
                    # and do it all over again
                    hc.turn_off_light(choice)

        # if the user pressed <CTRL> + C to exit early break out of the loop
        except KeyboardInterrupt:
            print "\nstopped"
            break

    # This ends and cleans up everything
    hc.clean_up()
Пример #22
0
def audio_in():
    """Control the lightshow from audio coming in from a USB audio card"""
    sample_rate = cm.lightshow()['audio_in_sample_rate']
    input_channels = cm.lightshow()['audio_in_channels']

    # Open the input stream from default input device
    audio_in_card = cm.lightshow()['audio_in_card']
    stream = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NORMAL, audio_in_card)
    stream.setchannels(input_channels)
    stream.setformat(aa.PCM_FORMAT_S16_LE)  # Expose in config if needed
    stream.setrate(sample_rate)
    stream.setperiodsize(CHUNK_SIZE)

    logging.debug("Running in audio-in mode - will run until Ctrl+C "
                  "is pressed")
    print "Running in audio-in mode, use Ctrl+C to stop"

    # Start with these as our initial guesses - will calculate a rolling
    # mean / std as we get input data.
    mean = np.array([12.0] * hc.GPIOLEN, dtype='float64')
    std = np.array([1.5] * hc.GPIOLEN, dtype='float64')
    count = 2

    running_stats = running_stats.Stats(hc.GPIOLEN)

    # preload running_stats to avoid errors, and give us a show that looks
    # good right from the start
    running_stats.preload(mean, std, count)

    try:
        hc.initialize()
        fft_calc = fft.FFT(CHUNK_SIZE, sample_rate, hc.GPIOLEN, _MIN_FREQUENCY,
                           _MAX_FREQUENCY, _CUSTOM_CHANNEL_MAPPING,
                           _CUSTOM_CHANNEL_FREQUENCIES, input_channels)

        # Listen on the audio input device until CTRL-C is pressed
        while True:
            length, data = stream.read()
            if length > 0:
                # if the maximum of the absolute value of all samples in
                # data is below a threshold we will disreguard it
                audio_max = audioop.max(data, 2)
                if audio_max < 250:
                    # we will fill the matrix with zeros and turn the
                    # lights off
                    matrix = np.zeros(hc.GPIOLEN, dtype="float64")
                    logging.debug("below threshold: '" + str(audio_max) +
                                  "', turning the lights off")
                else:
                    matrix = fft_calc.calculate_levels(data)
                    running_stats.push(matrix)
                    mean = running_stats.mean()
                    std = running_stats.std()

                update_lights(matrix, mean, std)

    except KeyboardInterrupt:
        pass

    finally:
        print "\nStopping"
        hc.clean_up()
Пример #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()
Пример #24
0
 def __init__(self, show="preshow"):
     hc.initialize()
     self.config = cm.lightshow()[show]
     self.show = show
     self.audio = None
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()
Пример #26
0
def audio_in():
    """Control the lightshow from audio coming in from a USB audio card"""
    sample_rate = cm.lightshow()['audio_in_sample_rate']
    input_channels = cm.lightshow()['audio_in_channels']

    # Open the input stream from default input device
    stream = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NORMAL,
                    cm.lightshow()['audio_in_card'])
    stream.setchannels(input_channels)
    stream.setformat(aa.PCM_FORMAT_S16_LE)  # Expose in config if needed
    stream.setrate(sample_rate)
    stream.setperiodsize(CHUNK_SIZE)

    logging.debug(
        "Running in audio-in mode - will run until Ctrl+C is pressed")
    print "Running in audio-in mode, use Ctrl+C to stop"
    try:
        hc.initialize()
        frequency_limits = calculate_channel_frequency(
            _MIN_FREQUENCY, _MAX_FREQUENCY, _CUSTOM_CHANNEL_MAPPING,
            _CUSTOM_CHANNEL_FREQUENCIES)

        # Start with these as our initial guesses - will calculate a rolling mean / std
        # as we get input data.
        mean = [12.0 for _ in range(hc.GPIOLEN)]
        std = [0.5 for _ in range(hc.GPIOLEN)]
        recent_samples = np.empty((250, hc.GPIOLEN))
        num_samples = 0

        # Listen on the audio input device until CTRL-C is pressed
        while True:
            l, data = stream.read()

            if l:
                try:
                    matrix = fft.calculate_levels(data, CHUNK_SIZE,
                                                  sample_rate,
                                                  frequency_limits, hc.GPIOLEN,
                                                  input_channels)
                    if not np.isfinite(np.sum(matrix)):
                        # Bad data --- skip it
                        continue
                except ValueError as e:
                    # TODO(todd): This is most likely occuring due to extra time in calculating
                    # mean/std every 250 samples which causes more to be read than expected the
                    # next time around.  Would be good to update mean/std in separate thread to
                    # avoid this --- but for now, skip it when we run into this error is good
                    # enough ;)
                    logging.debug("skipping update: " + str(e))
                    continue

                update_lights(matrix, mean, std)

                # Keep track of the last N samples to compute a running std / mean
                #
                # TODO(todd): Look into using this algorithm to compute this on a per sample basis:
                # http://www.johndcook.com/blog/standard_deviation/
                if num_samples >= 250:
                    no_connection_ct = 0
                    for i in range(0, hc.GPIOLEN):
                        mean[i] = np.mean([
                            item for item in recent_samples[:, i] if item > 0
                        ])
                        std[i] = np.std([
                            item for item in recent_samples[:, i] if item > 0
                        ])

                        # Count how many channels are below 10,
                        # if more than 1/2, assume noise (no connection)
                        if mean[i] < 10.0:
                            no_connection_ct += 1

                    # If more than 1/2 of the channels appear to be not connected, turn all off
                    if no_connection_ct > hc.GPIOLEN / 2:
                        logging.debug(
                            "no input detected, turning all lights off")
                        mean = [20 for _ in range(hc.GPIOLEN)]
                    else:
                        logging.debug("std: " + str(std) + ", mean: " +
                                      str(mean))
                    num_samples = 0
                else:
                    for i in range(0, hc.GPIOLEN):
                        recent_samples[num_samples][i] = matrix[i]
                    num_samples += 1

    except KeyboardInterrupt:
        pass
    finally:
        print "\nStopping"
        hc.clean_up()
Пример #27
0
def audio_in():
    """Control the lightshow from audio coming in from a real time audio"""
    global streaming
    stream_reader = None
    streaming = None
    songcount = 0

    sample_rate = cm.lightshow.input_sample_rate
    num_channels = cm.lightshow.input_channels

    if cm.lightshow.mode == 'audio-in':
        # Open the input stream from default input device
        streaming = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NORMAL,
                           cm.lightshow.audio_in_card)
        streaming.setchannels(num_channels)
        streaming.setformat(aa.PCM_FORMAT_S16_LE)  # Expose in config if needed
        streaming.setrate(sample_rate)
        streaming.setperiodsize(CHUNK_SIZE)

        stream_reader = lambda: streaming.read()[-1]

    elif cm.lightshow.mode == 'stream-in':

        outq = Queue()

        if cm.lightshow.use_fifo:
            streaming = subprocess.Popen(cm.lightshow.stream_command_string,
                                         stdin=subprocess.PIPE,
                                         stdout=subprocess.PIPE,
                                         preexec_fn=os.setsid)
            io = os.open(cm.lightshow.fifo, os.O_RDONLY | os.O_NONBLOCK)
            stream_reader = lambda: os.read(io, CHUNK_SIZE)
            outthr = Thread(target=enqueue_output,
                            args=(streaming.stdout, outq))
        else:
            # Open the input stream from command string
            streaming = subprocess.Popen(cm.lightshow.stream_command_string,
                                         stdin=subprocess.PIPE,
                                         stdout=subprocess.PIPE,
                                         stderr=subprocess.PIPE)
            stream_reader = lambda: streaming.stdout.read(CHUNK_SIZE)
            outthr = Thread(target=enqueue_output,
                            args=(streaming.stderr, outq))

        outthr.daemon = True
        outthr.start()

    log.debug("Running in %s mode - will run until Ctrl+C is pressed" %
              cm.lightshow.mode)
    print "Running in %s mode, use Ctrl+C to stop" % cm.lightshow.mode

    # setup light_delay.
    chunks_per_sec = ((16 * num_channels * sample_rate) / 8) / CHUNK_SIZE
    light_delay = int(cm.audio_processing.light_delay * chunks_per_sec)
    matrix_buffer = deque([], 1000)

    output = set_audio_device(sample_rate, num_channels)

    # Start with these as our initial guesses - will calculate a rolling mean / std
    # as we get input data.
    mean = np.array([12.0 for _ in range(hc.GPIOLEN)], dtype='float32')
    std = np.array([1.5 for _ in range(hc.GPIOLEN)], dtype='float32')
    count = 2

    running_stats = RunningStats.Stats(hc.GPIOLEN)

    # preload running_stats to avoid errors, and give us a show that looks
    # good right from the start
    running_stats.preload(mean, std, count)

    hc.initialize()
    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, 1)

    if server:
        network.set_playing()

    # Listen on the audio input device until CTRL-C is pressed
    while True:

        try:
            streamout = outq.get_nowait().strip('\n\r')
        except Empty:
            pass
        else:
            print streamout
            if cm.lightshow.stream_song_delim in streamout:
                songcount += 1
                if cm.lightshow.songname_command:
                    streamout = streamout.replace('\033[2K', '')
                    streamout = streamout.replace(
                        cm.lightshow.stream_song_delim, '')
                    streamout = streamout.replace('"', '')
                    os.system(cm.lightshow.songname_command +
                              ' "Now Playing ' + streamout + '"')

            if cm.lightshow.stream_song_exit_count > 0 and songcount > cm.lightshow.stream_song_exit_count:
                break

        try:
            data = stream_reader()

        except OSError as err:
            if err.errno == errno.EAGAIN or err.errno == errno.EWOULDBLOCK:
                continue
        try:
            output(data)
        except aa.ALSAAudioError:
            continue

        if len(data):
            # if the maximum of the absolute value of all samples in
            # data is below a threshold we will disregard it
            audio_max = audioop.max(data, 2)
            if audio_max < 250:
                # we will fill the matrix with zeros and turn the lights off
                matrix = np.zeros(hc.GPIOLEN, dtype="float32")
                log.debug("below threshold: '" + str(audio_max) +
                          "', turning the lights off")
            else:
                matrix = fft_calc.calculate_levels(data)
                running_stats.push(matrix)
                mean = running_stats.mean()
                std = running_stats.std()

            matrix_buffer.appendleft(matrix)

            if len(matrix_buffer) > light_delay:
                matrix = matrix_buffer[light_delay]
                update_lights(matrix, mean, std)
Пример #28
0
def main():
    """
    ladder

    Lights one channel at a time in order
    Then backs down to the first
    Then repeat everything 20 times
    """
    # this is a list of all the channels you have access to
    lights = hc._GPIO_PINS

    # initialize your hardware for use
    hc.initialize()

    # start with all the lights off
    hc.turn_off_lights()

    # pause for 1 second
    time.sleep(1)

    # working loop
    for _ in range(20):
        # try except block to catch keyboardinterrupt by user to stop
        try:
            # here we just loop over the gpio pins and do something with them
            # except the last one
            for light in range(len(lights) - 1):
                # turn off all the lights
                hc.turn_off_lights()

                # then turn on one
                hc.turn_on_light(lights[light])

                # wait a little bit
                time.sleep(.04)

            # to make the transition back smoother we handle the last pin here
            hc.turn_off_lights()
            hc.turn_on_light(lights[light + 1])

            # this loop walks it back the other way
            for light in range(len(lights) - 1, 0, -1):
                # turn off all the lights
                hc.turn_off_lights()

                # then turn on one
                hc.turn_on_light(lights[light])

                # wait a little bit
                time.sleep(.04)

            # again to make it smoother handle the first pin like the last pin
            hc.turn_off_lights()
            hc.turn_on_light(lights[light - 1])

        # if the user pressed <CTRL> + C to exit early break out of the loop
        except KeyboardInterrupt:
            print "\nstopped"
            break

    # This ends and cleans up everything
    hc.clean_up()
Пример #29
0
def main():
    """
    ladder

    Lights one channel at a time in order
    Then backs down to the first
    Then repeat everything 20 times
    """
    # this is a list of all the channels you have access to
    lights = hc._GPIO_PINS

    # initialize your hardware for use
    hc.initialize()

    # start with all the lights off
    hc.turn_off_lights()

    # pause for 1 second
    time.sleep(1)

    # working loop
    for _ in range(20):
        # try except block to catch keyboardinterrupt by user to stop
        try:
            # here we just loop over the gpio pins and do something with them
            # except the last one
            for light in range(len(lights)-1):
                # turn off all the lights
                hc.turn_off_lights()

                # then turn on one
                hc.turn_on_light(lights[light])

                # wait a little bit
                time.sleep(.04)

            # to make the transition back smoother we handle the last pin here
            hc.turn_off_lights()
            hc.turn_on_light(lights[light + 1])

            # this loop walks it back the other way
            for light in range(len(lights)-1, 0, -1):
                # turn off all the lights
                hc.turn_off_lights()

                # then turn on one
                hc.turn_on_light(lights[light])

                # wait a little bit
                time.sleep(.04)

            # again to make it smoother handle the first pin like the last pin
            hc.turn_off_lights()
            hc.turn_on_light(lights[light - 1])

        # if the user pressed <CTRL> + C to exit early break out of the loop
        except KeyboardInterrupt:
            print "\nstopped"
            break

    # This ends and cleans up everything
    hc.clean_up()
Пример #30
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()