def cache_song(song_filename): """Play the next song from the play list (or --file argument).""" # Initialize FFT stats matrix = [0 for _ in range(GPIOLEN) ] # get length of gpio and assign it to a variable # Set up audio if song_filename.endswith('.wav'): musicfile = wave.open(song_filename, 'r') else: musicfile = decoder.open(song_filename) sample_rate = musicfile.getframerate() num_channels = musicfile.getnchannels() song_filename = os.path.abspath(song_filename) # create empty array for the cache_matrix cache_matrix = np.empty(shape=[0, GPIOLEN]) cache_filename = \ os.path.dirname(song_filename) + "/." + os.path.basename(song_filename) + ".sync" # The values 12 and 1.5 are good estimates for first time playing back # (i.e. before we have the actual mean and standard deviations # calculated for each channel). mean = [12.0 for _ in range(GPIOLEN)] std = [1.5 for _ in range(GPIOLEN)] # Process audio song_filename row = 0 data = musicfile.readframes( CHUNK_SIZE) # move chunk_size to configuration_manager frequency_limits = calculate_channel_frequency( _MIN_FREQUENCY, _MAX_FREQUENCY, _CUSTOM_CHANNEL_MAPPING, _CUSTOM_CHANNEL_FREQUENCIES) while data != '': # No cache - Compute FFT in this chunk, and cache results matrix = fft.calculate_levels(data, CHUNK_SIZE, sample_rate, frequency_limits, GPIOLEN) # Add the matrix to the end of the cache cache_matrix = np.vstack([cache_matrix, matrix]) # Read next chunk of data from music song_filename data = musicfile.readframes(CHUNK_SIZE) row = row + 1 # Compute the standard deviation and mean values for the cache for i in range(0, GPIOLEN): std[i] = np.std([item for item in cache_matrix[:, i] if item > 0]) mean[i] = np.mean([item for item in cache_matrix[:, i] if item > 0]) # Add mean and std to the top of the cache cache_matrix = np.vstack([mean, cache_matrix]) cache_matrix = np.vstack([std, cache_matrix]) # Save the cache using numpy savetxt np.savetxt(cache_filename, cache_matrix)
def generate_limits(self, wave_file): sample_rate = wave_file.getframerate() data = wave_file.readframes(self.chunk_size) limits = [] while data != '': limits.append(fft.calculate_levels( data, self.chunk_size, sample_rate, self.freq_limits, self.gpio_len, self.n_channnels)) data = wave_file.readframes(self.chunk_size) return limits
def cache_song(song_filename): """Play the next song from the play list (or --file argument).""" # Initialize FFT stats matrix = [0 for _ in range(GPIOLEN)] # get length of gpio and assign it to a variable # Set up audio if song_filename.endswith('.wav'): musicfile = wave.open(song_filename, 'r') else: musicfile = decoder.open(song_filename) sample_rate = musicfile.getframerate() num_channels = musicfile.getnchannels() song_filename = os.path.abspath(song_filename) # create empty array for the cache_matrix cache_matrix = np.empty(shape=[0, GPIOLEN]) cache_filename = \ os.path.dirname(song_filename) + "/." + os.path.basename(song_filename) + ".sync" # The values 12 and 1.5 are good estimates for first time playing back # (i.e. before we have the actual mean and standard deviations # calculated for each channel). mean = [12.0 for _ in range(GPIOLEN)] std = [1.5 for _ in range(GPIOLEN)] # Process audio song_filename row = 0 data = musicfile.readframes(CHUNK_SIZE) # move chunk_size to configuration_manager frequency_limits = calculate_channel_frequency(_MIN_FREQUENCY, _MAX_FREQUENCY, _CUSTOM_CHANNEL_MAPPING, _CUSTOM_CHANNEL_FREQUENCIES) while data != '': # No cache - Compute FFT in this chunk, and cache results matrix = fft.calculate_levels(data, CHUNK_SIZE, sample_rate, frequency_limits, GPIOLEN) # Add the matrix to the end of the cache cache_matrix = np.vstack([cache_matrix, matrix]) # Read next chunk of data from music song_filename data = musicfile.readframes(CHUNK_SIZE) row = row + 1 # Compute the standard deviation and mean values for the cache for i in range(0, GPIOLEN): std[i] = np.std([item for item in cache_matrix[:, i] if item > 0]) mean[i] = np.mean([item for item in cache_matrix[:, i] if item > 0]) # Add mean and std to the top of the cache cache_matrix = np.vstack([mean, cache_matrix]) cache_matrix = np.vstack([std, cache_matrix]) # Save the cache using numpy savetxt np.savetxt(cache_filename, cache_matrix)
def generate_limits(self, wave_file): sample_rate = wave_file.getframerate() data = wave_file.readframes(self.chunk_size) limits = [] while data != '': limits.append( fft.calculate_levels(data, self.chunk_size, sample_rate, self.freq_limits, self.gpio_len, self.n_channnels)) data = wave_file.readframes(self.chunk_size) return limits
def play_song(song_filename): # Initialize Lights # hc.initialize() # Ensure play_now is reset before beginning playback # Set up audio music_file = audioread.audio_open(song_filename) sample_rate = music_file.samplerate num_channels = music_file.channels output = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL) output.setchannels(num_channels) output.setrate(sample_rate) output.setformat(aa.PCM_FORMAT_S16_LE) output.setperiodsize(CHUNK_SIZE) print("Playing: " + song_filename + " (" + str(music_file.duration) + " sec)") # Output a bit about what we're about to play to the logs song_filename = os.path.abspath(song_filename) # The values 12 and 1.5 are good estimates for first time playing back # (i.e. before we have the actual mean and standard deviations # calculated for each channel). mean = [12.0 for _ in range(NUM_CHANNELS)] std = [1.5 for _ in range(NUM_CHANNELS)] # Process audio song_filename row = 0 frequency_limits = calculate_channel_frequency(_MIN_FREQUENCY, _MAX_FREQUENCY) frames_played = 0 update_rate = sample_rate / UPDATE_HZ last_update_spec = 0 for data in music_file: output.write(data) # Control lights with cached timing values if they exist # No cache - Compute FFT in this chunk, and cache results matrix = fft.calculate_levels(data, CHUNK_SIZE, sample_rate, frequency_limits, NUM_CHANNELS) frames_played += len(data) / 4 if last_update_spec + update_rate < frames_played: update_lights(matrix, mean, std) last_update_spec = frames_played # Read next chunk of data from music song_filename row += 1
frequency_limits, stream = set_stream(HIGHER_FREQ, LOWER_FREQ, SAMPLE_RATE, CHUNK_SIZE) elif chosen_type == "waterfall": coldict = popcol(LED_NUMBER, "grad_increase") frequency_limits, stream = set_stream(HIGHER_FREQ, LOWER_FREQ, SAMPLE_RATE, CHUNK_SIZE) elif chosen_type == "frequency": coldict = popcol(LED_NUMBER, "gradient") rolling_col_dict = copy.deepcopy(coldict) frequency_limits, stream = set_stream(HIGHER_FREQ, LOWER_FREQ, SAMPLE_RATE, CHUNK_SIZE) """ Continously reads data from stream and passes it to update_lights,also detects if a song has ended and changes the update lights function and coldict """ while True: data = stream.read() matrix = fft.calculate_levels(data[1], CHUNK_SIZE, SAMPLE_RATE, frequency_limits, 2) maxmatrix = max(matrix) if maxmatrix < 9: lowcount += 1 else: lowcount = 0 silence = False if lowcount > 10.0 and silence == False: silence = True chosen_type = random.choice(light_types) if chosen_type == "volume": coldict = popcol(LED_NUMBER, "binned") frequency_limits, stream = set_stream(HIGHER_FREQ, LOWER_FREQ, SAMPLE_RATE, CHUNK_SIZE) elif chosen_type == "waterfall": coldict = popcol(LED_NUMBER, "grad_increase")
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()
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()
def play_song(song_filename): # Initialize Lights # hc.initialize() # Ensure play_now is reset before beginning playback # Set up audio if song_filename.endswith(".wav"): music_file = wave.open(song_filename, "r") else: music_file = decoder.open(song_filename) sample_rate = music_file.getframerate() num_channels = music_file.getnchannels() print ("Playing: " + song_filename + " (" + str(music_file.getnframes() / sample_rate) + " sec)") # Output a bit about what we're about to play to the logs song_filename = os.path.abspath(song_filename) # create empty array for the cache_matrix cache_matrix = np.empty(shape=[0, NUM_CHANNELS]) cache_found = False cache_filename = os.path.dirname(song_filename) + "/." + os.path.basename(song_filename) + ".sync" print cache_filename # The values 12 and 1.5 are good estimates for first time playing back # (i.e. before we have the actual mean and standard deviations # calculated for each channel). mean = [12.0 for _ in range(NUM_CHANNELS)] std = [1.5 for _ in range(NUM_CHANNELS)] # Read in cached fft try: # load cache from file using numpy loadtxt cache_matrix = np.loadtxt(cache_filename) cache_found = True # get std from matrix / located at index 0 std = np.array(cache_matrix[0]) # get mean from matrix / located at index 1 mean = np.array(cache_matrix[1]) # delete mean and std from the array cache_matrix = np.delete(cache_matrix, 0, axis=0) cache_matrix = np.delete(cache_matrix, 0, axis=0) print ("std: " + str(std) + ", mean: " + str(mean)) except IOError: print ("Cached sync data song_filename not found: '" + cache_filename + "'. One will be generated.") # Process audio song_filename row = 0 data = music_file.readframes(CHUNK_SIZE) frequency_limits = calculate_channel_frequency(_MIN_FREQUENCY, _MAX_FREQUENCY) # timer frames_played = 0 yield num_channels, sample_rate while data != "": # Control lights with cached timing values if they exist matrix = None if cache_found and args.readcache: if row < len(cache_matrix): matrix = cache_matrix[row] else: print ("Ran out of cached FFT values, will update the cache.") cache_found = False if matrix is None: # No cache - Compute FFT in this chunk, and cache results matrix = fft.calculate_levels(data, CHUNK_SIZE, sample_rate, frequency_limits, NUM_CHANNELS) # Add the matrix to the end of the cache cache_matrix = np.vstack([cache_matrix, matrix]) frames_played += len(data) / 4 yield data, frames_played, total_frames, sample_rate, update_lights(matrix, mean, std) # Read next chunk of data from music song_filename data = music_file.readframes(CHUNK_SIZE) row += 1 if not cache_found: # Compute the standard deviation and mean values for the cache for i in range(0, NUM_CHANNELS): std[i] = np.std([item for item in cache_matrix[:, i] if item > 0]) mean[i] = np.mean([item for item in cache_matrix[:, i] if item > 0]) # Add mean and std to the top of the cache cache_matrix = np.vstack([mean, cache_matrix]) cache_matrix = np.vstack([std, cache_matrix]) # Save the cache using numpy savetxt np.savetxt(cache_filename, cache_matrix)
np.set_printoptions(suppress=True) # Set up audio rate = 44100 chunk = 1024 inp = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NORMAL, 'sysdefault:CARD=1') inp.setchannels(2) inp.setrate(rate) inp.setformat(aa.PCM_FORMAT_S16_LE) inp.setperiodsize(chunk) # Process audio file count = 0 while True: count += 1 l, data = inp.read() matrix = np.floor(calculate_levels(matrix, weighting, data, chunk, rate)) if len(sys.argv) > 1: if count % 5 == 0: height = int(matrix[int(sys.argv[1])]*7/4095) r = 1 y = 1 g = 1 if height > 0: g = 0 if height > 2: y = 0 if height > 4: r = 0 wiringpi.digitalWrite(16, g) wiringpi.digitalWrite(20, y) wiringpi.digitalWrite(21, r) else: print matrix
def play_song(song_filename): # Initialize Lights # hc.initialize() # Ensure play_now is reset before beginning playback # Set up audio if song_filename.endswith('.wav'): music_file = wave.open(song_filename, 'r') else: music_file = decoder.open(song_filename) sample_rate = music_file.getframerate() num_channels = music_file.getnchannels() output = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL) output.setchannels(num_channels) output.setrate(sample_rate) output.setformat(aa.PCM_FORMAT_S16_LE) output.setperiodsize(CHUNK_SIZE) print("Playing: " + song_filename + " (" + str(music_file.getnframes() / sample_rate) + " sec)") # Output a bit about what we're about to play to the logs song_filename = os.path.abspath(song_filename) # create empty array for the cache_matrix cache_matrix = np.empty(shape=[0, NUM_CHANNELS]) cache_found = False cache_filename = os.path.dirname(song_filename) + \ "/." + os.path.basename(song_filename) + ".sync" print cache_filename # The values 12 and 1.5 are good estimates for first time playing back # (i.e. before we have the actual mean and standard deviations # calculated for each channel). mean = [12.0 for _ in range(NUM_CHANNELS)] std = [1.5 for _ in range(NUM_CHANNELS)] # Read in cached fft try: # load cache from file using numpy loadtxt cache_matrix = np.loadtxt(cache_filename) cache_found = True # get std from matrix / located at index 0 std = np.array(cache_matrix[0]) # get mean from matrix / located at index 1 mean = np.array(cache_matrix[1]) # delete mean and std from the array cache_matrix = np.delete(cache_matrix, 0, axis=0) cache_matrix = np.delete(cache_matrix, 0, axis=0) print("std: " + str(std) + ", mean: " + str(mean)) except IOError: print("Cached sync data song_filename not found: '" + cache_filename + "'. One will be generated.") # Process audio song_filename row = 0 data = music_file.readframes(CHUNK_SIZE) frequency_limits = calculate_channel_frequency(_MIN_FREQUENCY, _MAX_FREQUENCY) # timer total_seconds = music_file.getnframes() / sample_rate m, s = divmod(total_seconds, 60) total_time = "%d:%02d" % (m,s) frames_played = 0 update_rate = sample_rate / 10 last_update_display = 0 last_update_spec = 0 while data != '': output.write(data) # Control lights with cached timing values if they exist matrix = None if cache_found: if row < len(cache_matrix): matrix = cache_matrix[row] else: print("Ran out of cached FFT values, will update the cache.") cache_found = False if matrix is None: # No cache - Compute FFT in this chunk, and cache results matrix = fft.calculate_levels(data, CHUNK_SIZE, sample_rate, frequency_limits, NUM_CHANNELS) # Add the matrix to the end of the cache cache_matrix = np.vstack([cache_matrix, matrix]) frames_played += len(data) / 4 if last_update_display + sample_rate < frames_played: seconds = frames_played / sample_rate m, s = divmod(seconds, 60) lcd.set_cursor(0,0) lcd.message('Now playing\n{}/{}'.format("%d:%02d" % (m, s), total_time)) last_update_display = frames_played if last_update_spec + update_rate < frames_played: update_lights(matrix, mean, std) last_update_spec = frames_played # Read next chunk of data from music song_filename data = music_file.readframes(CHUNK_SIZE) row += 1 # Load new application state in case we've been interrupted # cm.load_state() # play_now = int(cm.get_state('play_now', "0")) if not cache_found: # Compute the standard deviation and mean values for the cache for i in range(0, NUM_CHANNELS): std[i] = np.std([item for item in cache_matrix[:, i] if item > 0]) mean[i] = np.mean([item for item in cache_matrix[:, i] if item > 0]) # Add mean and std to the top of the cache cache_matrix = np.vstack([mean, cache_matrix]) cache_matrix = np.vstack([std, cache_matrix]) # Save the cache using numpy savetxt np.savetxt(cache_filename, cache_matrix) print("Cached sync data written to '." + cache_filename + "' [" + str(len(cache_matrix)) + " rows]")
def play_song(): """Play the next song from the play list (or --file argument).""" song_to_play = int(cm.get_state('song_to_play', 0)) play_now = int(cm.get_state('play_now', 0)) # Arguments parser = argparse.ArgumentParser() filegroup = parser.add_mutually_exclusive_group() filegroup.add_argument('--playlist', default=_PLAYLIST_PATH, help='Playlist to choose song from.') filegroup.add_argument('--file', help='path to the song to play (required if no' 'playlist is designated)') parser.add_argument( '--readcache', type=int, default=1, help='read light timing from cache if available. Default: true') args = parser.parse_args() # Make sure one of --playlist or --file was specified if args.file == None and args.playlist == None: print "One of --playlist or --file must be specified" sys.exit() # Handle the pre/post show if not play_now: result = PrePostShow('preshow').execute() # now unused. if play_now = True # song to play is always the first song in the playlist if result == PrePostShow.play_now_interrupt: play_now = int(cm.get_state('play_now', 0)) # Initialize Lights hc.initialize() # Determine the next file to play song_filename = args.file if args.playlist != None and args.file == None: most_votes = [None, None, []] current_song = None with open(args.playlist, 'rb') as playlist_fp: fcntl.lockf(playlist_fp, fcntl.LOCK_SH) playlist = csv.reader(playlist_fp, delimiter='\t') songs = [] for song in playlist: if len(song) < 2 or len(song) > 4: logging.error( 'Invalid playlist. Each line should be in the form: ' '<song name><tab><path to song>') sys.exit() elif len(song) == 2: song.append(set()) else: song[2] = set(song[2].split(',')) if len(song) == 3 and len(song[2]) >= len(most_votes[2]): most_votes = song songs.append(song) fcntl.lockf(playlist_fp, fcntl.LOCK_UN) if most_votes[0] != None: logging.info("Most Votes: " + str(most_votes)) current_song = most_votes # Update playlist with latest votes with open(args.playlist, 'wb') as playlist_fp: fcntl.lockf(playlist_fp, fcntl.LOCK_EX) writer = csv.writer(playlist_fp, delimiter='\t') for song in songs: if current_song == song and len(song) == 3: song.append("playing!") if len(song[2]) > 0: song[2] = ",".join(song[2]) else: del song[2] writer.writerows(songs) fcntl.lockf(playlist_fp, fcntl.LOCK_UN) else: # Get a "play now" requested song if play_now > 0 and play_now <= len(songs): current_song = songs[play_now - 1] # Get random song elif _RANDOMIZE_PLAYLIST: # Use python's random.randrange() to get a random song current_song = songs[random.randrange(0, len(songs))] # Play next song in the lineup else: song_to_play = song_to_play if ( song_to_play <= len(songs) - 1) else 0 current_song = songs[song_to_play] next_song = (song_to_play + 1) if ( (song_to_play + 1) <= len(songs) - 1) else 0 cm.update_state('song_to_play', next_song) # Get filename to play and store the current song playing in state cfg song_filename = current_song[1] cm.update_state('current_song', songs.index(current_song)) song_filename = song_filename.replace("$SYNCHRONIZED_LIGHTS_HOME", cm.HOME_DIR) # Ensure play_now is reset before beginning playback if play_now: cm.update_state('play_now', 0) play_now = 0 # Initialize FFT stats matrix = [0 for _ in range(hc.GPIOLEN)] # Set up audio if song_filename.endswith('.wav'): musicfile = wave.open(song_filename, 'r') else: musicfile = decoder.open(song_filename) sample_rate = musicfile.getframerate() num_channels = musicfile.getnchannels() if _usefm == 'true': logging.info("Sending output as fm transmission") with open(os.devnull, "w") as dev_null: # play_stereo is always True as coded, Should it be changed to # an option in the config file? fm_process = subprocess.Popen(["sudo", cm.HOME_DIR + "/bin/pifm", "-", str(frequency), "44100", "stereo" if play_stereo else "mono"],\ stdin=music_pipe_r,\ stdout=dev_null) else: output = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL) output.setchannels(num_channels) output.setrate(sample_rate) output.setformat(aa.PCM_FORMAT_S16_LE) output.setperiodsize(CHUNK_SIZE) logging.info("Playing: " + song_filename + " (" + str(musicfile.getnframes() / sample_rate) + " sec)") # Output a bit about what we're about to play to the logs song_filename = os.path.abspath(song_filename) # create empty array for the cache_matrix cache_matrix = np.empty(shape=[0, hc.GPIOLEN]) cache_found = False cache_filename = \ os.path.dirname(song_filename) + "/." + os.path.basename(song_filename) + ".sync" # The values 12 and 1.5 are good estimates for first time playing back # (i.e. before we have the actual mean and standard deviations # calculated for each channel). mean = [12.0 for _ in range(hc.GPIOLEN)] std = [1.5 for _ in range(hc.GPIOLEN)] if args.readcache: # Read in cached fft try: # load cache from file using numpy loadtxt cache_matrix = np.loadtxt(cache_filename) cache_found = True # get std from matrix / located at index 0 std = np.array(cache_matrix[0]) # get mean from matrix / located at index 1 mean = np.array(cache_matrix[1]) # delete mean and std from the array cache_matrix = np.delete(cache_matrix, (0), axis=0) cache_matrix = np.delete(cache_matrix, (0), axis=0) logging.debug("std: " + str(std) + ", mean: " + str(mean)) except IOError: logging.warn("Cached sync data song_filename not found: '" + cache_filename + ". One will be generated.") # Process audio song_filename row = 0 data = musicfile.readframes(CHUNK_SIZE) frequency_limits = calculate_channel_frequency( _MIN_FREQUENCY, _MAX_FREQUENCY, _CUSTOM_CHANNEL_MAPPING, _CUSTOM_CHANNEL_FREQUENCIES) while data != '' and not play_now: if _usefm == 'true': os.write(music_pipe_w, data) else: output.write(data) # Control lights with cached timing values if they exist matrix = None if cache_found and args.readcache: if row < len(cache_matrix): matrix = cache_matrix[row] else: logging.warning( "Ran out of cached FFT values, will update the cache.") cache_found = False if matrix == None: # No cache - Compute FFT in this chunk, and cache results matrix = fft.calculate_levels(data, CHUNK_SIZE, sample_rate, frequency_limits, hc.GPIOLEN) # Add the matrix to the end of the cache cache_matrix = np.vstack([cache_matrix, matrix]) update_lights(matrix, mean, std) # Read next chunk of data from music song_filename data = musicfile.readframes(CHUNK_SIZE) row = row + 1 # Load new application state in case we've been interrupted cm.load_state() play_now = int(cm.get_state('play_now', 0)) if not cache_found: # Compute the standard deviation and mean values for the cache for i in range(0, hc.GPIOLEN): std[i] = np.std([item for item in cache_matrix[:, i] if item > 0]) mean[i] = np.mean( [item for item in cache_matrix[:, i] if item > 0]) # Add mean and std to the top of the cache cache_matrix = np.vstack([mean, cache_matrix]) cache_matrix = np.vstack([std, cache_matrix]) # Save the cache using numpy savetxt np.savetxt(cache_filename, cache_matrix) logging.info("Cached sync data written to '." + cache_filename + "' [" + str(len(cache_matrix)) + " rows]") # Cleanup the pifm process if _usefm == 'true': fm_process.kill() # check for postshow done = PrePostShow('postshow').execute() # We're done, turn it all off and clean up things ;) hc.clean_up()
def 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()