def play(self, id, track, finished): # If we're already playing a track, interrupt it and wait # for everything to be cleared if self.playing: self._restart = True return # id of caller self.curId = id # Set up audio if track.lower().endswith('.wav'): self.musicFile = wave.open(track, 'r') else: self.musicFile = decoder.open(track) self.sample_rate = self.musicFile.getframerate() self.num_channels = self.musicFile.getnchannels() self.output = audio.PCM(audio.PCM_PLAYBACK, audio.PCM_NORMAL) self.output.setchannels(self.num_channels) self.output.setrate(self.sample_rate) self.output.setformat(audio.PCM_FORMAT_S16_LE) self.output.setperiodsize(CHUNK_SIZE) self.frequency_limits = self._calculate_channel_frequency(50, 15000) # Start playing... self.playing = True self.thread = threading.Thread(target=self._play, args=[finished]) self.thread.start()
def getSong(self, path): header = False if any([ax for ax in [".mp4", ".m4a", ".m4b"] if ax in path]): header = True return decoder.open(path, header)
def setup_audio(self): """Setup audio file and setup the output. device.output is a lambda that will send data to fm process or to the specified ALSA sound card """ # Set up audio force_header = False if any([ax for ax in [".mp4", ".m4a", ".m4b"] if ax in self.song_filename]): force_header = True self.music_file = decoder.open(self.song_filename, force_header) self.sample_rate = self.music_file.getframerate() self.num_channels = self.music_file.getnchannels() self.fft_calc = fft.FFT(self.chunk_size, self.sample_rate, cm.hardware.gpio_len, cm.audio_processing.min_frequency, cm.audio_processing.max_frequency, cm.audio_processing.custom_channel_mapping, cm.audio_processing.custom_channel_frequencies) # setup output device self.set_audio_device() chunks_per_sec = ((16 * self.num_channels * self.sample_rate) / 8) / self.chunk_size self.light_delay = int(cm.lightshow.light_delay * chunks_per_sec) # Output a bit about what we're about to play to the logs num_frames = str(self.music_file.getnframes() / self.sample_rate) log.info("Playing: " + self.song_filename + " (" + num_frames + " sec)")
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 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 open(self, path, mono=False): """Open the audio resource.""" self.pos = 0 # Set up audio if path.endswith('.wav'): self.audio_file = audio_file = wave.open(path, 'r') else: # needs my updates in decoder.py self.audio_file = audio_file = \ decoder.open(path, force_mono=mono) self.num_channels = 1 if mono else audio_file.getnchannels()
def load (msg=None): """Load audio file""" global cf, stream, cpdata, blockshow, fileinfo blockshow = 1 clear() print intro if msg: print >> sys.stderr, msg cfn = "" while 1: cfn = raw_input("Audio file path: ") if not cfn: break cfn = os.path.normpath(cfn) if os.path.exists(cfn): break print >> sys.stderr, "File '"+cfn+"' does not exist!" if not cfn: clear() print intro, fileinfo if cpdata: x = cpdata.status() if x==1: print "Playing" if x==-1: print "Paused" if x==0: print "Stopped" blockshow = 0 return if os.path.splitext(cfn)[1] not in decoder.wildcard: return load("File format not supported!") wasplaying = 0 if cpdata: if cpdata.status()==1: wasplaying = 1 cpdata.stop(); cpdata.wait() if stream: stream.close() if cf: cf.close() fh = 0 # ForceHeader if os.name=="posix" and cfn[-4:] in (".aac", ".m4a", ".m4b", ".mp4"): fh = 1 # 'faad' decoder on Linux needs forced headers to work correctly cf = decoder.open(cfn, fh) stream = p.open(format = p.get_format_from_width(cf.getsampwidth()), channels = cf.getnchannels(), rate = cf.getframerate(), output = True) clear(); print intro print cfn, "loaded!" fileinfo = decoder.info(cfn).info.pprint() # Using function from module fileinfo.py through decoder.py print fileinfo if wasplaying: print "Playing" else: print "Stopped" blockshow = 0 if wasplaying: play()
def play(file): global _stream, _song if _stream is not None: stop() _song = decoder.open(file) _stream = _p.open( format=_p.get_format_from_width(_song.getsampwidth()), channels=_song.getnchannels(), rate=_song.getframerate(), output=True, stream_callback=_callback, ) _stream.start_stream()
def loadfile(file): global wf, stream try: stream.close() #close le stream wf.close() #close le wave-like object except: pass wf = decoder.open(file, "r") #create a read-onry wave-like obj if wf.getnchannels() >2: print "multichannel files not supported" exit(-1) # open stream stream = p.open(format = p.get_format_from_width(wf.getsampwidth()), channels = 2, rate = wf.getframerate(), output = True)
def setup_audio(song_filename): """Setup audio file and setup setup the output device.output is a lambda that will send data to fm process or to the specified ALSA sound card :param song_filename: path / filename to music file :type song_filename: str :return: output, fm_process, fft_calc, music_file :rtype tuple: lambda, subprocess, fft.FFT, decoder """ # Set up audio force_header = False if any([ax for ax in [".mp4", ".m4a", ".m4b"] if ax in song_filename]): force_header = True music_file = decoder.open(song_filename, force_header) sample_rate = music_file.getframerate() num_channels = music_file.getnchannels() fft_calc = fft.FFT(CHUNK_SIZE, sample_rate, hc.GPIOLEN, cm.audio_processing.min_frequency, cm.audio_processing.max_frequency, cm.audio_processing.custom_channel_mapping, cm.audio_processing.custom_channel_frequencies) # setup output device output = set_audio_device(sample_rate, num_channels) chunks_per_sec = ((16 * num_channels * sample_rate) / 8) / CHUNK_SIZE light_delay = int(cm.audio_processing.light_delay * chunks_per_sec) # Output a bit about what we're about to play to the logs nframes = str(music_file.getnframes() / sample_rate) log.info("Playing: " + song_filename + " (" + nframes + " sec)") return output, fft_calc, music_file, light_delay
def send_sound(): # setup the connection #hostname = raw_input("Enter server address: ") hostname = HOSTNAME f_in = decoder.open(FILENAME) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # get a socket sock.bind((hostname, IN_PORT)) #sock.connect((hostname,PORT)) # connect to remote host #sock.send('S') # indicate that we wish to send data only #sock.send('\n') # send newline to indicate lack of name #while not '\n' == sock.recv(1): # a = 1 #rate = f_in.getframerate() # store the sample rate #sock.send(chr(rate >> 8)) # send 1st byte of sample rate #sock.send(chr(rate & 0xFF)) # send 2nd byte of sample rate #sock.send(chr(f_in.getnchannels())) # send the number of channels #sock.send(chr(f_in.getsampwidth())) # send the samle width # print some basic stats print f_in.getframerate(), f_in.getnchannels(), f_in.getsampwidth() # use this for timings rate = f_in.getframerate() delay = 1.0 / rate * CHUNK # start sending data bytes = f_in.readframes(CHUNK) average = 1 while bytes != '': timer = time.time() sock.sendto(bytes, (hostname, OUT_PORT)) sock.recvfrom(1) bytes = f_in.readframes(CHUNK) #time.sleep(delay) #sync with reciever #average = .875*average + (time.time() - timer)/delay*.125 #print average # scaling factor sock.close() f_in.close()
def reload (): """Reloads the file""" global cf try: if not cf: return except: return # Get the filename try: # Filename in fakewave object cfn = cf.obj.filename # cf.obj is a object returned by subprocess.Popen() # with added attribute (by 'decoder.py') "filename" except: # Filename in Wave_read or Aifc_read object cfn = cf._file.file.name # cf._file is a Chunk object within Wave/Aifc_read object, # cf._file.file is a normal file object where 'name' represents a filename try: cf.close() except: reload() fh = 0 # ForceHeader if os.name=="posix" and cfn[-4:] in (".aac", ".m4a", ".m4b", ".mp4"): fh = 1 cf = decoder.open(cfn, fh)
def send_sound(): # setup the connection #hostname = raw_input("Enter server address: ") hostname = HOSTNAME f_in = decoder.open(FILENAME) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # get a socket sock.bind((hostname,IN_PORT)) #sock.connect((hostname,PORT)) # connect to remote host #sock.send('S') # indicate that we wish to send data only #sock.send('\n') # send newline to indicate lack of name #while not '\n' == sock.recv(1): # a = 1 #rate = f_in.getframerate() # store the sample rate #sock.send(chr(rate >> 8)) # send 1st byte of sample rate #sock.send(chr(rate & 0xFF)) # send 2nd byte of sample rate #sock.send(chr(f_in.getnchannels())) # send the number of channels #sock.send(chr(f_in.getsampwidth())) # send the samle width # print some basic stats print f_in.getframerate(), f_in.getnchannels(), f_in.getsampwidth() # use this for timings rate = f_in.getframerate() delay = 1.0/rate * CHUNK # start sending data bytes = f_in.readframes(CHUNK) average = 1 while bytes != '': timer = time.time() sock.sendto(bytes,(hostname,OUT_PORT)) sock.recvfrom(1) bytes = f_in.readframes(CHUNK) #time.sleep(delay) #sync with reciever #average = .875*average + (time.time() - timer)/delay*.125 #print average # scaling factor sock.close() f_in.close()
def songThread(): global data global queue global song global skip global curSong while True: stream = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL) while len(queue) == 0: time.sleep(1) curSong = queue.pop(0) song = decoder.open(curSong) stream.setperiodsize(chunk) data = song.readframes(chunk) while data != '': while pause: time.sleep(.1) if skip: skip = False break stream.write(data) data = song.readframes(chunk) print "escaped songThread loop"
def read_musicfile_in_chunks(path, chunk_size=CHUNK_SIZE, play_audio=True): """ Read the music file at the given path, in chunks of the given size. """ musicfile = decoder.open(path) sample_rate = musicfile.getframerate() num_channels = musicfile.getnchannels() if play_audio: output = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL) output.setchannels(num_channels) output.setrate(sample_rate) output.setformat(aa.PCM_FORMAT_S16_LE) output.setperiodsize(chunk_size) # fixme: we could do the writing to audio in a thread ... ? while True: chunk = musicfile.readframes(chunk_size) if len(chunk) == 0: break if play_audio: output.write(chunk) yield chunk, sample_rate
def send_sound(): # setup the connection hostname = raw_input("Enter server address: ") f_in = decoder.open(FILENAME) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # get a socket sock.connect((hostname,PORT)) # connect to remote host sock.send('S') # indicate that we wish to send data only sock.send('\n') # send newline to indicate lack of name while not '\n' == sock.recv(1): a = 1 rate = f_in.getframerate() # store the sample rate sock.send(chr(rate >> 8)) # send 1st byte of sample rate sock.send(chr(rate & 0xFF)) # send 2nd byte of sample rate sock.send(chr(f_in.getnchannels())) # send the number of channels sock.send(chr(f_in.getsampwidth())) # send the samle width # start sending data bytes = f_in.readframes(CHUNK) while bytes != '': sock.send(bytes) bytes = f_in.readframes(CHUNK) sock.close() f_in.close()
def send_sound(): # setup the connection hostname = raw_input("Enter server address: ") f_in = decoder.open(FILENAME) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # get a socket sock.connect((hostname, PORT)) # connect to remote host sock.send('S') # indicate that we wish to send data only sock.send('\n') # send newline to indicate lack of name while not '\n' == sock.recv(1): a = 1 rate = f_in.getframerate() # store the sample rate sock.send(chr(rate >> 8)) # send 1st byte of sample rate sock.send(chr(rate & 0xFF)) # send 2nd byte of sample rate sock.send(chr(f_in.getnchannels())) # send the number of channels sock.send(chr(f_in.getsampwidth())) # send the samle width # start sending data bytes = f_in.readframes(CHUNK) while bytes != '': sock.send(bytes) bytes = f_in.readframes(CHUNK) sock.close() f_in.close()
#! /usr/bin/env python """Example: Decoding a compressed audio file to PCM WAVE and saving the result.""" import decoder, wave, os cfn = raw_input("Compressed audio file path: ") cfn = os.path.normpath(cfn) while not os.path.exists(cfn): print "File '"+cfn+"' does not exist!" cfn = raw_input("Compressed audio file path: ") cfn = os.path.normpath(cfn) wfn = raw_input("Output wave file path: ") fh = 0 # ForceHeader if os.name=="posix" and cfn[-4:] in (".aac", ".m4a", ".m4b", ".mp4"): fh = 1 cf = decoder.open(cfn, fh) wf = wave.open(wfn, "w") print "Decoding; Please wait!" decoder.copywaveobj(cf, wf) print "Done!" cf.close(); wf.close() raw_input("Press enter to continue . . . ")
def play_song(): '''Play the next song from the play list (or --file argument).''' song_to_play = int(cm.get_state('song_to_play', 0)) play_now = int(cm.get_state('play_now', 0)) # Arguments parser = argparse.ArgumentParser() filegroup = parser.add_mutually_exclusive_group() filegroup.add_argument('--playlist', default=_PLAYLIST_PATH, help='Playlist to choose song from.') filegroup.add_argument('--file', help='path to the song to play (required if no' 'playlist is designated)') parser.add_argument('--readcache', type=int, default=1, help='read light timing from cache if available. Default: true') args = parser.parse_args() # Make sure one of --playlist or --file was specified if args.file == None and args.playlist == None: print "One of --playlist or --file must be specified" sys.exit() # Initialize Lights hc.initialize() # Handle the pre-show if not play_now: result = Preshow().execute() if result == Preshow.PlayNowInterrupt: play_now = True # Determine the next file to play song_filename = args.file if args.playlist != None and args.file == None: most_votes = [None, None, []] current_song = None with open(args.playlist, 'rb') as playlist_fp: fcntl.lockf(playlist_fp, fcntl.LOCK_SH) playlist = csv.reader(playlist_fp, delimiter='\t') songs = [] for song in playlist: if len(song) < 2 or len(song) > 4: logging.error('Invalid playlist. Each line should be in the form: ' '<song name><tab><path to song>') sys.exit() elif len(song) == 2: song.append(set()) else: song[2] = set(song[2].split(',')) if len(song) == 3 and len(song[2]) >= len(most_votes[2]): most_votes = song songs.append(song) fcntl.lockf(playlist_fp, fcntl.LOCK_UN) if most_votes[0] != None: logging.info("Most Votes: " + str(most_votes)) current_song = most_votes # Update playlist with latest votes with open(args.playlist, 'wb') as playlist_fp: fcntl.lockf(playlist_fp, fcntl.LOCK_EX) writer = csv.writer(playlist_fp, delimiter='\t') for song in songs: if current_song == song and len(song) == 3: song.append("playing!") if len(song[2]) > 0: song[2] = ",".join(song[2]) else: del song[2] writer.writerows(songs) fcntl.lockf(playlist_fp, fcntl.LOCK_UN) else: # Get a "play now" requested song if play_now > 0 and play_now <= len(songs): current_song = songs[play_now - 1] # Get random song elif _RANDOMIZE_PLAYLIST: current_song = songs[random.randint(0, len(songs) - 1)] # Play next song in the lineup else: song_to_play = song_to_play if (song_to_play <= len(songs) - 1) else 0 current_song = songs[song_to_play] next_song = (song_to_play + 1) if ((song_to_play + 1) <= len(songs) - 1) else 0 cm.update_state('song_to_play', next_song) # Get filename to play and store the current song playing in state cfg song_filename = current_song[1] cm.update_state('current_song', songs.index(current_song)) song_filename = song_filename.replace("$SYNCHRONIZED_LIGHTS_HOME", cm.HOME_DIR) # Ensure play_now is reset before beginning playback if play_now: cm.update_state('play_now', 0) play_now = 0 # Initialize FFT stats matrix = [0 for _ in range(hc.GPIOLEN)] # Set up audio if song_filename.endswith('.wav'): musicfile = wave.open(song_filename, 'r') else: musicfile = decoder.open(song_filename) sample_rate = musicfile.getframerate() num_channels = musicfile.getnchannels() if _usefm=='true': logging.info("Sending output as fm transmission") with open(os.devnull, "w") as dev_null: fm_process = subprocess.Popen(["sudo",cm.HOME_DIR + "/bin/pifm","-",str(frequency),"44100", "stereo" if play_stereo else "mono"], stdin=music_pipe_r, stdout=dev_null) else: output = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL) output.setchannels(num_channels) output.setrate(sample_rate) output.setformat(aa.PCM_FORMAT_S16_LE) output.setperiodsize(CHUNK_SIZE) logging.info("Playing: " + song_filename + " (" + str(musicfile.getnframes() / sample_rate) + " sec)") # Output a bit about what we're about to play to the logs song_filename = os.path.abspath(song_filename) cache = [] cache_found = False cache_filename = os.path.dirname(song_filename) + "/." + os.path.basename(song_filename) \ + ".sync.gz" # The values 12 and 1.5 are good estimates for first time playing back (i.e. before we have # the actual mean and standard deviations calculated for each channel). mean = [12.0 for _ in range(hc.GPIOLEN)] std = [1.5 for _ in range(hc.GPIOLEN)] if args.readcache: # Read in cached fft try: with gzip.open(cache_filename, 'rb') as playlist_fp: cachefile = csv.reader(playlist_fp, delimiter=',') for row in cachefile: cache.append([0.0 if np.isinf(float(item)) else float(item) for item in row]) cache_found = True # TODO(todd): Optimize this and / or cache it to avoid delay here cache_matrix = np.array(cache) for i in range(0, hc.GPIOLEN): std[i] = np.std([item for item in cache_matrix[:, i] if item > 0]) mean[i] = np.mean([item for item in cache_matrix[:, i] if item > 0]) logging.debug("std: " + str(std) + ", mean: " + str(mean)) except IOError: logging.warn("Cached sync data song_filename not found: '" + cache_filename + ". One will be generated.") # Process audio song_filename row = 0 data = musicfile.readframes(CHUNK_SIZE) frequency_limits = calculate_channel_frequency(_MIN_FREQUENCY, _MAX_FREQUENCY, _CUSTOM_CHANNEL_MAPPING, _CUSTOM_CHANNEL_FREQUENCIES) while data != '' and not play_now: if _usefm=='true': os.write(music_pipe_w, data) else: output.write(data) # Control lights with cached timing values if they exist matrix = None if cache_found and args.readcache: if row < len(cache): matrix = cache[row] else: logging.warning("Ran out of cached FFT values, will update the cache.") cache_found = False if matrix == None: # No cache - Compute FFT in this chunk, and cache results matrix = fft.calculate_levels(data, CHUNK_SIZE, sample_rate, frequency_limits) cache.append(matrix) update_lights(matrix, mean, std) # Read next chunk of data from music song_filename data = musicfile.readframes(CHUNK_SIZE) row = row + 1 # Load new application state in case we've been interrupted cm.load_state() play_now = int(cm.get_state('play_now', 0)) if not cache_found: with gzip.open(cache_filename, 'wb') as playlist_fp: writer = csv.writer(playlist_fp, delimiter=',') writer.writerows(cache) logging.info("Cached sync data written to '." + cache_filename + "' [" + str(len(cache)) + " rows]") # Cleanup the pifm process if _usefm=='true': fm_process.kill() # We're done, turn it all off and clean up things ;) hc.clean_up()
r = requests.get(url, stream=True) d = r.headers["Content-Disposition"] filename = re.findall("filename=(.+)", d)[0] if os.path.isfile(filename): r.connection.close() return filename with open(filename, "wb") as f: for chunk in r.iter_content(chunk_size=4096): if chunk: # filter out keep-alive new chunks f.write(chunk) return filename song_filename = download_file("http://intense.io:9001/get-track") cache_filename = download_file("http://intense.io:9001/get-fft") music_file = decoder.open(song_filename) num_channels = music_file.getnchannels() sample_rate = music_file.getframerate() init(num_channels, sample_rate) # load cache from file using numpy loadtxt cache_matrix = np.loadtxt(cache_filename) # get std from matrix / located at index 0 std = np.array(cache_matrix[0]) # get mean from matrix / located at index 1 mean = np.array(cache_matrix[1]) # delete mean and std from the array cache_matrix = np.delete(cache_matrix, 0, axis=0)
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)
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()
import decoder import time import sys import struct import pyaudio import numpy import math filename = "song.mp3" if len(sys.argv) > 1: filename = sys.argv[1] mp3 = decoder.open(filename) framerate = mp3.getframerate() samplewidth = mp3.getsampwidth() channels = mp3.getnchannels() chunksize = 2048 # Loudness contour data, might want to adjust output accordingly #f = [20, 25, 31.5, 40, 50, 63, 80, 100, 125, 160, 200, 250, 315, 400, 500, 630, 800, # 1000, 1250, 1600, 2000, 2500, 3150, 4000, 5000, 6300, 8000, 10000, 12500]; # #Tf = [ 78.5, 68.7, 59.5, 51.1, 44.0, 37.5, 31.5, 26.5, 22.1, 17.9, 14.4, # 11.4, 8.6, 6.2, 4.4, 3.0, 2.2, 2.4, 3.5, 1.7, -1.3, -4.2, # -6.0, -5.4, -1.5, 6.0, 12.6, 13.9, 12.3]; # #contourfactors = [pow(10, (x/10)) for x in Tf] p = pyaudio.PyAudio() stream = p.open(format=p.get_format_from_width(samplewidth), channels=channels, rate=framerate,
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 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()