示例#1
0
    def __init__(self):
        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 for _ in xrange(hc.GPIOLEN)], dtype='float64')
        std = np.array([1.5 for _ in xrange(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)
示例#2
0
    def __init__(self):
        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 for _ in xrange(hc.GPIOLEN)], dtype='float64')
        std = np.array([1.5 for _ in xrange(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)
示例#3
0
    def __init__(self, show="preshow", hardware=None):
        """

        :param show: which show should be preformed
        :type show: str

        :param hardware: an instance of hardware_controller.py
        :type hardware: object
        """
        if hardware:
            self.hc = hardware
        else:
            self.hc = __import__('hardware_controller')
            self.hc.initialize()

        self.config = cm.lightshow()[show]
        self.show = show
        self.audio = None
示例#4
0
    def __init__(self, show="preshow", hardware=None):
        """

        :param show: which show should be preformed
        :type show: str

        :param hardware: an instance of hardware_controller.py
        :type hardware: object
        """
        if hardware:
            self.hc = hardware
        else:
            self.hc = __import__('hardware_controller')
            self.hc.initialize()

        self.config = cm.lightshow()[show]
        self.show = show
        self.audio = None
示例#5
0
def main():
    """Process sms messages

    Download and process all sms messages from a Google Voice account.
    Runs in a loop that is executed every 15 seconds
    """
    parser = argparse.ArgumentParser()
    parser.add_argument('--playlist',
                        default=cm.lightshow()['playlist_path'].replace("$SYNCHRONIZED_LIGHTS_HOME",
                                                                        cm.HOME_DIR),
                        help='filename with the song playlist, one song per line in the format: '
                             '<song name><tab><path to song>')
    parser.add_argument('--setup', default=False,
                        help='use this option to setup the default configuration file for Google Voice')

    parser.add_argument('--log', default='INFO',
                        help='Set the logging level. levels:INFO, DEBUG, WARNING, ERROR, CRITICAL')
    args = parser.parse_args()

    logging.basicConfig(filename=cm.LOG_DIR + '/music_and_lights.check.dbg',
                        format='[%(asctime)s] %(levelname)s {%(pathname)s:%(lineno)d}'
                               ' - %(message)s',
                        level=logging.INFO)
    # logging levels
    levels = {'DEBUG': logging.DEBUG,
              'INFO': logging.INFO,
              'WARNING': logging.WARNING,
              'ERROR': logging.ERROR,
              'CRITICAL': logging.CRITICAL}

    level = levels.get(parser.parse_args().log.upper())
    logging.getLogger().setLevel(level)


    # Load playlist from file, notifying users of any of their requests that have now played
    logging.info('loading playlist ' + args.playlist)
    while True:
        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:
                logging.debug(song)

                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())
                elif len(song) >= 3:
                    # Votes for the song are stored in the 3rd column
                    song[2] = set(song[2].split(','))

                    if len(song) == 4:
                        # Notification of a song being played is stored in the 4th column
                        song_played(song)
                        del song[3]
                        song[2] = set()

                songs.append(song)

            fcntl.lockf(playlist_fp, fcntl.LOCK_UN)

        logging.info('loaded %d songs from playlist', len(songs))
        cm.set_songs(songs)

        # Parse and act on any new sms messages
        messages = VOICE.sms().messages
        for msg in extract_sms(VOICE.sms.html):
            logging.debug(str(msg))
            response = commands.execute(msg['text'], msg['from'])
            if response:
                logging.info('Request: "' + msg['text'] + '" from ' + msg['from'])

                try:
                    if isinstance(response, basestring):
                        VOICE.send_sms(msg['from'], response)
                    else:
                        # Multiple parts, send them with a delay in hopes to avoid
                        # them being received out of order by the recipient.
                        for part in response:
                            VOICE.send_sms(msg['from'], str(part))
                            time.sleep(2)
                except:
                    logging.warn('Error sending sms response (command still executed)', exc_info=1)

                logging.info('Response: "' + str(response) + '"')
            else:
                logging.info('Unknown request: "' + msg['text'] + '" from ' + msg['from'])
                VOICE.send_sms(msg['from'], _CONFIG['unknown_command_response'])

        # 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 len(song[2]) > 0:
                    song[2] = ",".join(song[2])
                else:
                    del song[2]
            writer.writerows(songs)
            fcntl.lockf(playlist_fp, fcntl.LOCK_UN)

        # Delete all messages now that we've processed them
        for msg in messages:
            msg.delete(1)

        if args.setup:
            break
        time.sleep(15)
示例#6
0
 def __init__(self, show="preshow"):
     hc.initialize()
     self.config = cm.lightshow()[show]
     self.show = show
     self.audio = None
示例#7
0
def main():
    """Process sms messages

    Download and process all sms messages from a Google Voice account.
    Runs in a loop that is executed every 15 seconds
    """
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--playlist',
        default=cm.lightshow()['playlist_path'].replace(
            "$SYNCHRONIZED_LIGHTS_HOME", cm.HOME_DIR),
        help='filename with the song playlist, one song per line in the format: '
        '<song name><tab><path to song>')
    parser.add_argument(
        '--setup',
        default=False,
        help=
        'use this option to setup the default configuration file for Google Voice'
    )

    parser.add_argument(
        '--log',
        default='INFO',
        help=
        'Set the logging level. levels:INFO, DEBUG, WARNING, ERROR, CRITICAL')
    args = parser.parse_args()

    logging.basicConfig(
        filename=cm.LOG_DIR + '/music_and_lights.check.dbg',
        format='[%(asctime)s] %(levelname)s {%(pathname)s:%(lineno)d}'
        ' - %(message)s',
        level=logging.INFO)
    # logging levels
    levels = {
        'DEBUG': logging.DEBUG,
        'INFO': logging.INFO,
        'WARNING': logging.WARNING,
        'ERROR': logging.ERROR,
        'CRITICAL': logging.CRITICAL
    }

    level = levels.get(parser.parse_args().log.upper())
    logging.getLogger().setLevel(level)

    # Load playlist from file, notifying users of any of their requests that have now played
    logging.info('loading playlist ' + args.playlist)
    while True:
        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:
                logging.debug(song)

                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())
                elif len(song) >= 3:
                    # Votes for the song are stored in the 3rd column
                    song[2] = set(song[2].split(','))

                    if len(song) == 4:
                        # Notification of a song being played is stored in the 4th column
                        song_played(song)
                        del song[3]
                        song[2] = set()

                songs.append(song)

            fcntl.lockf(playlist_fp, fcntl.LOCK_UN)

        logging.info('loaded %d songs from playlist', len(songs))
        cm.set_songs(songs)

        # Parse and act on any new sms messages
        messages = VOICE.sms().messages
        for msg in extract_sms(VOICE.sms.html):
            logging.debug(str(msg))
            response = commands.execute(msg['text'], msg['from'])
            if response:
                logging.info('Request: "' + msg['text'] + '" from ' +
                             msg['from'])

                try:
                    if isinstance(response, basestring):
                        VOICE.send_sms(msg['from'], response)
                    else:
                        # Multiple parts, send them with a delay in hopes to avoid
                        # them being received out of order by the recipient.
                        for part in response:
                            VOICE.send_sms(msg['from'], str(part))
                            time.sleep(2)
                except:
                    logging.warn(
                        'Error sending sms response (command still executed)',
                        exc_info=1)

                logging.info('Response: "' + str(response) + '"')
            else:
                logging.info('Unknown request: "' + msg['text'] + '" from ' +
                             msg['from'])
                VOICE.send_sms(msg['from'],
                               _CONFIG['unknown_command_response'])

        # 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 len(song[2]) > 0:
                    song[2] = ",".join(song[2])
                else:
                    del song[2]
            writer.writerows(songs)
            fcntl.lockf(playlist_fp, fcntl.LOCK_UN)

        # Delete all messages now that we've processed them
        for msg in messages:
            msg.delete(1)

        if args.setup:
            break
        time.sleep(15)
示例#8
0
 def __init__(self, show="preshow"):
     hc.initialize()
     self.config = cm.lightshow()[show]
     self.show = show
     self.audio = None
        if height < .55:
            height = .05
        elif height > 1.0:
            height = 1.0
        led.fillRGB(r=102,g=0,b=204,start=44,end = 63)

    bright = height * 180
    if bright < 20:
        bright = 20
    if bright > 200:
        bright = 200
    led.setMasterBrightness(int(round(bright)))

# Configurations - TODO(todd): Move more of this into configuration manager
_CONFIG = cm.CONFIG
_MODE = cm.lightshow()['mode']
_MIN_FREQUENCY = _CONFIG.getfloat('audio_processing', 'min_frequency')
_MAX_FREQUENCY = _CONFIG.getfloat('audio_processing', 'max_frequency')
_RANDOMIZE_PLAYLIST = _CONFIG.getboolean('lightshow', 'randomize_playlist')
try:
    _CUSTOM_CHANNEL_MAPPING = [int(channel) for channel in
                               _CONFIG.get('audio_processing', 'custom_channel_mapping').split(',')]
except:
    _CUSTOM_CHANNEL_MAPPING = 0
try:
    _CUSTOM_CHANNEL_FREQUENCIES = [int(channel) for channel in
                                   _CONFIG.get('audio_processing',
                                               'custom_channel_frequencies').split(',')]
except:
    _CUSTOM_CHANNEL_FREQUENCIES = 0
try:
is_a_raspberryPI = platform.platform_detect() == 1


if is_a_raspberryPI:
    import wiringpi2 as wiringpi
else:
    # if this is not a RPi you can't run wiringpi so lets load
    # something in its place
    import wiring_pi_stub as wiringpi
    logging.debug("Not running on a raspberryPI")


# Get Configurations - TODO(todd): Move more of this into configuration manager
_CONFIG = cm.CONFIG
_LIGHTSHOW_CONFIG = cm.lightshow()
_HARDWARE_CONFIG = cm.hardware()
_GPIO_PINS = [int(gpio_pin) for gpio_pin in
              _CONFIG.get('hardware', 'gpio_pins').split(',')]
_PWM_MAX = int(_CONFIG.get('hardware', 'pwm_range'))
_ACTIVE_LOW_MODE = _CONFIG.getboolean('hardware', 'active_low_mode')
_ALWAYS_ON_CHANNELS = [int(channel) for channel in
                       _LIGHTSHOW_CONFIG['always_on_channels'].split(',')]
_ALWAYS_OFF_CHANNELS = [int(channel) for channel in
                        _LIGHTSHOW_CONFIG['always_off_channels'].split(',')]
_INVERTED_CHANNELS = [int(channel) for channel in
                      _LIGHTSHOW_CONFIG['invert_channels'].split(',')]
_EXPORT_PINS = _CONFIG.getboolean('hardware', 'export_pins')
_GPIO_UTILITY_PATH = _CONFIG.get('hardware', 'gpio_utility_path')

I2C_DEVICES = ["mcp23017", "mcp23016", "mcp23008", "pcf8574"]
示例#11
0
import configuration_manager as cm
import platform

is_a_raspberryPI = platform.platform_detect() == 1

if is_a_raspberryPI:
    import wiringpi2 as wiringpi
else:
    # if this is not a RPi you can't run wiringpi so lets load
    # something in its place
    import wiring_pi_stub as wiringpi
    logging.debug("Not running on a raspberryPI")

# Get Configurations - TODO(todd): Move more of this into configuration manager
_CONFIG = cm.CONFIG
_LIGHTSHOW_CONFIG = cm.lightshow()
_HARDWARE_CONFIG = cm.hardware()
_GPIO_PINS = [
    int(gpio_pin)
    for gpio_pin in _CONFIG.get('hardware', 'gpio_pins').split(',')
]
_PWM_MAX = int(_CONFIG.get('hardware', 'pwm_range'))
_ACTIVE_LOW_MODE = _CONFIG.getboolean('hardware', 'active_low_mode')
_ALWAYS_ON_CHANNELS = [
    int(channel)
    for channel in _LIGHTSHOW_CONFIG['always_on_channels'].split(',')
]
_ALWAYS_OFF_CHANNELS = [
    int(channel)
    for channel in _LIGHTSHOW_CONFIG['always_off_channels'].split(',')
]
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()
import subprocess
import sys
import wave

import alsaaudio as aa
import fft
import configuration_manager as cm
import decoder
import hardware_controller as hc
import numpy as np

from prepostshow import PrePostShow

# Configurations - TODO(todd): Move more of this into configuration manager
_CONFIG = cm.CONFIG
_MODE = cm.lightshow()['mode']
_MIN_FREQUENCY = _CONFIG.getfloat('audio_processing', 'min_frequency')
_MAX_FREQUENCY = _CONFIG.getfloat('audio_processing', 'max_frequency')
_RANDOMIZE_PLAYLIST = _CONFIG.getboolean('lightshow', 'randomize_playlist')
try:
    _CUSTOM_CHANNEL_MAPPING = \
        [int(channel) for channel in _CONFIG.get('audio_processing',\
            'custom_channel_mapping').split(',')]
except:
    _CUSTOM_CHANNEL_MAPPING = 0
try:
    _CUSTOM_CHANNEL_FREQUENCIES = [
        int(channel) for channel in _CONFIG.get(
            'audio_processing', 'custom_channel_frequencies').split(',')
    ]
except:
示例#14
0
def get_audio_input_handler(song_filename, chunk_size):
    cfg = cm.CONFIG
    if cm.lightshow()['mode'] == 'audio-in':
        return LineInput()
    else:
        return StreamInput(song_filename, chunk_size)
示例#15
0
def get_audio_input_handler(song_filename, chunk_size):
    cfg = cm.CONFIG
    if cm.lightshow()['mode'] == 'audio-in':
        return LineInput()
    else:
        return StreamInput(song_filename, chunk_size)
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()
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()
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()
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()
示例#20
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()