class WinampApplication(displayio.Group): """ WinampApplication Helper class that manages song playback and UI components. :param playlist_file: json file containing the playlist of songs :param skin_image: BMP image file for skin background :param skin_config_file: json file containing color values :param pyportal_titano: boolean value. True if using Titano, False otherwise. """ STATE_PLAYING = 0 STATE_PAUSED = 1 # pylint: disable=too-many-statements,too-many-branches def __init__( self, playlist_file="playlist.json", skin_image="/base_240x320.bmp", skin_config_file="base_config.json", pyportal_titano=False, ): self.SKIN_IMAGE = skin_image self.SKIN_CONFIG_FILE = skin_config_file self.PLAYLIST_FILE = playlist_file # read the skin config data into variable f = open(self.SKIN_CONFIG_FILE, "r") self.CONFIG_DATA = json.loads(f.read()) f.close() if self.PLAYLIST_FILE: try: # read the playlist data into variable f = open(self.PLAYLIST_FILE, "r") self.PLAYLIST = json.loads(f.read()) f.close() except OSError: # file not found self.auto_find_tracks() except ValueError: # json parse error self.auto_find_tracks() else: # playlist file argument was None self.auto_find_tracks() if self.PLAYLIST: try: if len(self.PLAYLIST["playlist"]["files"]) == 0: # valid playlist json data, but no tracks self.auto_find_tracks() except KeyError: self.auto_find_tracks() # initialize clock display self.clock_display = ClockDisplay( text_color=self.CONFIG_DATA["time_color"]) if not pyportal_titano: # standard PyPortal and pynt clock display location # and playlist display parameters self.clock_display.x = 44 self.clock_display.y = 22 _max_playlist_display_chars = 30 _rows = 3 else: # PyPortal Titano clock display location # and playlist display parameters self.clock_display.x = 65 self.clock_display.y = 37 _max_playlist_display_chars = 42 _rows = 4 # initialize playlist display self.playlist_display = PlaylistDisplay( text_color=self.CONFIG_DATA["text_color"], max_chars=_max_playlist_display_chars, rows=_rows, ) if not pyportal_titano: # standard PyPortal and pynt playlist display location self.playlist_display.x = 13 self.playlist_display.y = 234 else: # PyPortal Titano playlist display location self.playlist_display.x = 20 self.playlist_display.y = 354 # set playlist into playlist display self.playlist_display.from_files_list( self.PLAYLIST["playlist"]["files"]) self.playlist_display.current_track_number = 1 # get name of current song self.current_song_file_name = self.PLAYLIST["playlist"]["files"][ self.playlist_display.current_track_number - 1] if not pyportal_titano: # standard PyPortal and pynt max characters for track title _max_chars = 22 else: # PyPortal Titano max characters for track title _max_chars = 29 # initialize ScrollingLabel for track name self.current_song_lbl = scrolling_label.ScrollingLabel( terminalio.FONT, text=self.playlist_display.current_track_title, color=self.CONFIG_DATA["text_color"], max_characters=_max_chars, ) self.current_song_lbl.anchor_point = (0, 0) if not pyportal_titano: # standard PyPortal and pynt track title location self.current_song_lbl.anchored_position = (98, 19) else: # PyPortal Titano track title location self.current_song_lbl.anchored_position = (130, 33) # Setup the skin image file as the bitmap data source self.background_bitmap = displayio.OnDiskBitmap(self.SKIN_IMAGE) # Create a TileGrid to hold the bitmap self.background_tilegrid = displayio.TileGrid( self.background_bitmap, pixel_shader=self.background_bitmap.pixel_shader) # initialize parent displayio.Group super().__init__() # Add the TileGrid to the Group self.append(self.background_tilegrid) # add other UI componenets self.append(self.current_song_lbl) self.append(self.clock_display) self.append(self.playlist_display) # Start playing first track self.current_song_file = open(self.current_song_file_name, "rb") self.decoder = MP3Decoder(self.current_song_file) self.audio = AudioOut(board.SPEAKER) self.audio.play(self.decoder) self.CURRENT_STATE = self.STATE_PLAYING # behavior variables. self._start_time = time.monotonic() self._cur_time = time.monotonic() self._pause_time = None self._pause_elapsed = 0 self._prev_time = None self._seconds_elapsed = 0 self._last_increment_time = 0 def auto_find_tracks(self): """ Initialize the song_list by searching for all MP3's within two layers of directories on the SDCard. e.g. It will find all of: /sd/Amazing Song.mp3 /sd/[artist_name]/Amazing Song.mp3 /sd/[artist_name]/[album_name]/Amazing Song.mp3 but won't find: /sd/my_music/[artist_name]/[album_name]/Amazing Song.mp3 :return: None """ # list that holds all files in the root of SDCard _root_sd_all_files = os.listdir("/sd/") # list that will hold all directories in the root of the SDCard. _root_sd_dirs = [] # list that will hold all subdirectories inside of root level directories _second_level_dirs = [] # list that will hold all MP3 file songs that we find _song_list = [] # loop over all files found on SDCard for _file in _root_sd_all_files: try: # Check if the current file is a directory os.listdir("/sd/{}".format(_file)) # add it to a list to look at later _root_sd_dirs.append(_file) except OSError: # current file was not a directory, nothing to do. pass # if current file is an MP3 file if _file.endswith(".mp3"): # we found an MP3 file, add it to the list that will become our playlist _song_list.append("/sd/{}".format(_file)) # loop over root level directories for _dir in _root_sd_dirs: # loop over all files inside of root level directory for _file in os.listdir("/sd/{}".format(_dir)): # check if current file is a directory try: # if it is a directory, loop over all files inside of it for _inner_file in os.listdir("/sd/{}/{}".format( _dir, _file)): # check if inner file is an MP3 if _inner_file.endswith(".mp3"): # we found an MP3 file, add it to the list that will become our playlist _song_list.append("/sd/{}/{}/{}".format( _dir, _file, _inner_file)) except OSError: # current file is not a directory pass # if the current file is an MP3 file if _file.endswith(".mp3"): # we found an MP3 file, add it to the list that will become our playlist _song_list.append("/sd/{}/{}".format(_dir, _file)) # format the songs we found into the PLAYLIST data structure self.PLAYLIST = {"playlist": {"files": _song_list}} # print message to user letting them know we auto-generated the playlist print("Auto Generated Playlist from MP3's found on SDCard:") print(json.dumps(self.PLAYLIST)) def update(self): """ Must be called each iteration from the main loop. Responsible for updating all sub UI components and managing song playback :return: None """ self._cur_time = time.monotonic() if self.CURRENT_STATE == self.STATE_PLAYING: # if it's time to increase the time on the ClockDisplay if self._cur_time >= self._last_increment_time + 1: # increase ClockDisplay by 1 second self._seconds_elapsed += 1 self._last_increment_time = self._cur_time self.clock_display.seconds = int(self._seconds_elapsed) # update the track label (scrolling) self.current_song_lbl.update() if self.CURRENT_STATE == self.STATE_PLAYING: # if we are supposed to be playing but aren't # it means the track ended. if not self.audio.playing: # start the next track self.next_track() # store time for comparison later self._prev_time = self._cur_time def play_current_track(self): """ Update the track label and begin playing the song for current track in the playlist. :return: None """ # set the track title self.current_song_lbl.full_text = self.playlist_display.current_track_title # save start time in a variable self._start_time = self._cur_time # if previous song is still playing if self.audio.playing: # stop playing self.audio.stop() # close previous song file self.current_song_file.close() # open new song file self.current_song_file_name = self.PLAYLIST["playlist"]["files"][ self.playlist_display.current_track_number - 1] self.current_song_file = open(self.current_song_file_name, "rb") self.decoder.file = self.current_song_file # play new song file self.audio.play(self.decoder) # if user paused the playback if self.CURRENT_STATE == self.STATE_PAUSED: # pause so it's loaded, and ready to resume self.audio.pause() def next_track(self): """ Advance to the next track. :return: None """ # reset ClockDisplay to 0 self._seconds_elapsed = 0 self.clock_display.seconds = int(self._seconds_elapsed) # increment current track number self.playlist_display.current_track_number += 1 try: # start playing track self.play_current_track() except OSError as e: # file not found print("Error playing: {}".format(self.current_song_file_name)) print(e) self.next_track() return def previous_track(self): """ Go back to previous track. :return: None """ # reset ClockDisplay to 0 self._seconds_elapsed = 0 self.clock_display.seconds = int(self._seconds_elapsed) # decrement current track number self.playlist_display.current_track_number -= 1 try: # start playing track self.play_current_track() except OSError as e: # file not found print("Error playing: {}".format(self.current_song_file_name)) print(e) self.previous_track() return def pause(self): """ Stop playing song and wait until resume function. :return: None """ if self.audio.playing: self.audio.pause() self.CURRENT_STATE = self.STATE_PAUSED def resume(self): """ Resume playing song after having been paused. :return: None """ self._last_increment_time = self._cur_time if self.audio.paused: self.audio.resume() self.CURRENT_STATE = self.STATE_PLAYING
try: from audiopwmio import PWMAudioOut as AudioOut except ImportError: pass # not always supported by every board! FREQUENCY = 440 # 440 Hz middle 'A' SAMPLERATE = 8000 # 8000 samples/second, recommended! # Generate one period of sine wav. length = SAMPLERATE // FREQUENCY sine_wave = array.array("H", [0] * length) for i in range(length): sine_wave[i] = int(math.sin(math.pi * 2 * i / length) * (2**15) + 2**15) # Enable the speaker speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE) speaker_enable.direction = digitalio.Direction.OUTPUT speaker_enable.value = True audio = AudioOut(board.SPEAKER) sine_wave_sample = RawSample(sine_wave) # A single sine wave sample is hundredths of a second long. If you set loop=False, it will play # a single instance of the sample (a quick burst of sound) and then silence for the rest of the # duration of the time.sleep(). If loop=True, it will play the single instance of the sample # continuously for the duration of the time.sleep(). audio.play(sine_wave_sample, loop=True) # Play the single sine_wave sample continuously... time.sleep(1) # for the duration of the sleep (in seconds) audio.stop() # and then stop.
new_mode = True switch_timer = 0 if mode is "waiting": if new_mode is True: display.brightness = 0 if USE_AUDIO is True: audio.stop() display.refresh() new_mode = False if mode is "laughing": if new_mode is True: display.brightness = 1.0 if USE_AUDIO is True: audio.play(decoder, loop=True) display.refresh() new_mode = False # Make the skull laugh open/close once if USE_ANIMATION is True: if (time.monotonic() - last_frame_time) > LAUGHING_SPEED: current_frame = (current_frame + 1) % 4 if current_frame is 0: group.remove(skull_middle) group.append(skull_close) elif current_frame is 1: group.remove(skull_close) group.append(skull_middle) elif current_frame is 2:
led.value = False time.sleep(0.1) led.value = True time.sleep(0.1) led.value = False while ble.connected or not ble_enabled.value: if not central.value: led.value = True print("Running") while True: i = random.randint(0, upper) if not buttons[i].value: break audio.play(wave) if i == 0: print("Button 1") pixels.fill((0, 0, 255)) if ble_enabled.value: kl.write("Button 1") elif i == 1: print("Button 2") pixels.fill((0, 255, 0)) if ble_enabled.value: kl.write("Button 2") elif i == 2: print("Button 3") pixels.fill((255, 255, 255)) if ble_enabled.value: kl.write("Button 3")
# make a sawtooth wave between +/- each value in volumes # phase shifted so it starts and ends near midpoint vol = 32767 sample_len = 10 waveraw = array.array("H", [ midpoint + round(vol * sawtooth((idx + 0.5) / sample_len * twopi + math.pi)) for idx in range(sample_len) ]) beep = RawSample(waveraw, sample_rate=sample_len * A4refhz) # play something to get things inside audio libraries initialised audio.play(beep, loop=True) time.sleep(0.1) audio.stop() audio.play(beep) # brightness 1.0 saves memory by removing need for a second buffer # 10 is number of NeoPixels on CPX/CPB numpixels = 10 pixels = neopixel.NeoPixel(board.NEOPIXEL, numpixels, brightness=1.0) # B is right (usb at top) button_right = digitalio.DigitalInOut(board.BUTTON_B) button_right.switch_to_input(pull=digitalio.Pull.DOWN) def wait_finger_off_and_random_delay():
from audiopwmio import PWMAudioOut as AudioOut except ImportError: pass # not always supported by every board! # The mp3 files on the sd card will be played in alphabetical order mp3files = sorted("/sd/" + filename for filename in os.listdir("/sd") if filename.lower().endswith("mp3")) voodoo = [1, 2, 3] # You have to specify some mp3 file when creating the decoder mp3 = open(mp3files[0], "rb") decoder = MP3Decoder(mp3) audio = AudioOut(board.A0, right_channel=board.A1) speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE) speaker_enable.switch_to_output(True) while True: for filename in mp3files: print("Playing", filename) # Updating the .file property of the existing decoder # helps avoid running out of memory (MemoryError exception) decoder.file = open(filename, "rb") audio.play(decoder) # This allows you to do other things while the audio plays! while audio.playing: time.sleep(1)
samples_signed=True) wav_files = ("scanner-left-16k.wav", "scanner-right-16k.wav") ### Use same pins which would be used on a Feather M4 with real DACs AUDIO_PIN_L = board.A0 AUDIO_PIN_R = board.A1 audio_out = AudioOut(AUDIO_PIN_L, right_channel=AUDIO_PIN_R) wav_fh = [open(fn, "rb") for fn in wav_files] wavs = [WaveFile(fh) for fh in wav_fh] ### Voice 0 behaves strangely ### https://github.com/adafruit/circuitpython/issues/3210 mixer.voice[0].level = 0.0 mixer.voice[1].level = 1.0 audio_out.play(mixer) audio = mixer.voice[1] uart = busio.UART(board.TX, board.RX, baudrate=115200) rx_bytes = bytearray(1) while True: if uart.readinto(rx_bytes) and rx_bytes[0]: try: wav_obj = wavs[rx_bytes[0] - 1] audio.play(wav_obj) except IndexError: print("No wav file for:", rx_bytes[0])
except ImportError: from audioio import RawSample try: from audioio import AudioOut except ImportError: try: from audiopwmio import PWMAudioOut as AudioOut except ImportError: pass # not always supported by every board! button = digitalio.DigitalInOut(board.A1) button.switch_to_input(pull=digitalio.Pull.UP) tone_volume = 0.1 # Increase this to increase the volume of the tone. frequency = 440 # Set this to the Hz of the tone you want to generate. length = 8000 // frequency sine_wave = array.array("H", [0] * length) for i in range(length): sine_wave[i] = int( (1 + math.sin(math.pi * 2 * i / length)) * tone_volume * (2**15 - 1)) audio = AudioOut(board.A0) sine_wave_sample = RawSample(sine_wave) while True: if not button.value: audio.play(sine_wave_sample, loop=True) time.sleep(1) audio.stop()
pull=True) event = keypad.Event() # Single key event for re-use keys.events.clear() # Load all the WAV files from the pin_to_wave list, and one more for the # mode selector, sharing a common buffer since only one is used at a time. # Also, play a startup sound. audio_buf = bytearray(1024) waves = [ WaveFile(open(sound_folder + "/" + x[1], "rb"), audio_buf) for x in pin_to_wave ] active_sound = 0 # Index of waves[] to play when trigger is pressed selector_wave = WaveFile(open(sound_folder + "/" + "click.wav", "rb"), audio_buf) audio.play(WaveFile(open(sound_folder + "/" + "startup.wav", "rb"), audio_buf)) # MAIN LOOP --------- repeat forever ---- while True: # Process the mode selector slider, check if moved into a new position. # This is currently just used to make click noises, it doesn't actually # change any "mode" in the operation of the prop, but it could if we # really wanted, with additional code (e.g. different sound sets). selector_pos = analog_in.value if not bounds[0] < selector_pos < bounds[1]: # Moved out of mode range? # New mode, new bounds. +/-512 adds a little hysteresis to selection. mode = (selector_pos * (num_modes - 1) + 32768) // 65536 bounds = ( (mode * 65535 - 32768) // (num_modes - 1) - 512,
cur_note = "{}{}".format(note_letter, octave) # add wave file to dictionary key = "{}{}".format(wave_type, cur_note) notes[key] = WaveFile( open("notes/{}/{}.wav".format(wave_type, cur_note), "rb")) # main audio object audio = AudioOut(left_channel=board.A0, right_channel=board.A1) # mixer to allow pylyphonic playback mixer = Mixer(voice_count=8, sample_rate=8000, channel_count=2, bits_per_sample=16, samples_signed=True) audio.play(mixer) # turn on the rainbow lights for i, color in enumerate(colors): trellis.pixels[i, 0] = color trellis.pixels[i, 1] = color trellis.pixels[i, 2] = color # list of keys pressed on the previous iteration prev_pressed = [] # voice recycling variables available_voices = [1, 2, 3, 4, 5, 6, 7] # key to voice dictionary e.g. {... (1,2):4, (1,3):3, ...} used_voices = {}
try: from audioio import AudioOut except ImportError: try: from audiopwmio import PWMAudioOut as AudioOut except ImportError: pass # not always supported by every board! FREQUENCY = 440 # 440 Hz middle 'A' SAMPLERATE = 8000 # 8000 samples/second, recommended! # Generate one period of sine wav. length = SAMPLERATE // FREQUENCY sine_wave = array.array("H", [0] * length) for i in range(length): sine_wave[i] = int(math.sin(math.pi * 2 * i / 18) * (2**15) + 2**15) # Enable the speaker speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE) speaker_enable.direction = digitalio.Direction.OUTPUT speaker_enable.value = True audio = AudioOut(board.SPEAKER) sine_wave_sample = RawSample(sine_wave) audio.play(sine_wave_sample, loop=True) # Keep playing the sample over and over time.sleep(1) # until... audio.stop() # We tell the board to stop
channel = 2 elif (scale == 3): noteIndex = notesG[note] channel = 3 else: print("Invalid scale, must be 0-3") formattedNote = str(noteIndex) note = WaveFile( open(instrument + "/Marker #" + formattedNote + ".wav", "rb")) mixer.play(note, voice=channel) a.play(mixer) #print("audio setup done") note = 0 ''' while True: print("play " + str(note)) playNote("G", note); note += 1; if(note >= 9): note = 0; ''' i2c = I2C(SCL, SDA, frequency=100000, timeout=20000)
try: from audiocore import WaveFile except ImportError: from audioio import WaveFile #check if the audio out is digital-to-analog, true analog voltage out, or Pulse-Width-Modulated, pseudo-analog binary voltage out. try: from audioio import AudioOut except ImportError: try: from audiopwmio import PWMAudioOut as AudioOut except ImportError: print("NO audio out possible") pass # not always supported by every board! # CPX requires: speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE) speaker_enable.switch_to_output(value=True) #more setup: open the file, load it into memory wave_file = open("yikes.wav", "rb") wave = WaveFile(wave_file) audio = AudioOut(board.A0) #loop #a loop is too annoying to use with an audio file. audio.play(wave) # start playing, non-blocking # wait till finished while audio.playing: pass
stereo_dac = AudioOut(board.A0, right_channel=board.A1, quiescent_value=0) frames = [locals()[f"frame_{i}"] for i in range(18)] buffers = [] for c, points in enumerate(frames): print(f"Building frame {c}") print(f"Number of array cells {((len(points) - 1) * samples) * 2}") print(f"Num lines {len(points) - 1}") line_array = array("H", [0] * ((len(points) - 1) * samples * 2)) i = 0 for pt1, pt2 in pairwise(points): pt1 = int(pt1[0] * 65535 / 400), int(pt1[1] * 65535 / 400) pt2 = int(pt2[0] * 65535 / 400), int(pt2[1] * 65535 / 400) temp = sum(gen_line(pt1, pt2), ()) for val in temp: line_array[i] = val i += 1 buffers.append( RawSample(line_array, channel_count=2, sample_rate=1_000_000)) i = 0 while True: stereo_dac.play(buffers[i], loop=True) sleep(0.08) stereo_dac.stop() i = (i + 1) % len(buffers)