def _audio_playback(self, pcm_stream): # thread 3: audio playback levelmeter = LevelMeter() def played(sample): if self.client.stream_title != self.stream_title: self.stream_title = self.client.stream_title if self.song_title_callback: self.song_title_callback(self.stream_title) else: print("\n\nNew Song:", self.stream_title, "\n") levelmeter.update(sample) if self.update_ui: self.update_ui(levelmeter, None) else: levelmeter.print(60, True) with Output(mixing="sequential", frames_per_chunk=44100//4) as output: output.register_notify_played(played) while not self._stop_playback: try: audio = pcm_stream.read(44100 * 2 * 2 // 20) if not audio: break except (IOError, ValueError): break else: if not self._stop_playback: sample = Sample.from_raw_frames(audio, 2, 44100, 2) output.play_sample(sample)
def chunked_frame_data(self, chunksize: int, repeat: bool=False, stopcondition: Callable[[], bool]=lambda: False) -> Generator[memoryview, None, None]: notes = itertools.cycle(self.title_music) if repeat else iter(self.title_music) attack, decay, sustain, release = self.adsr_times num_frames = chunksize // synth_params.norm_samplewidth // synth_params.norm_nchannels sample_residue = Sample(None, nchannels=2) for v1, v2 in notes: if stopcondition(): break vf1 = self.music_freq_table[v1] vf2 = self.music_freq_table[v2] osc1 = FastTriangle(vf1 * _sidfreq, amplitude=0.5) osc2 = FastTriangle(vf2 * _sidfreq, amplitude=0.5) f1 = EnvelopeFilter(osc1, attack, decay, sustain, 1.0, release, stop_at_end=True) f2 = EnvelopeFilter(osc2, attack, decay, sustain, 1.0, release, stop_at_end=True) sample1 = Sample.from_oscillator(f1, 1, synth_params.norm_samplerate) # length is max. 1 second sample2 = Sample.from_oscillator(f2, 1, synth_params.norm_samplerate) # length is max. 1 second sample_residue.join(sample1.stereo_mix(sample2, "R")) while len(sample_residue) >= num_frames: # TODO optimize this a bit by not using Samples but instead by looping over a memoryview of the frames (just like the super class does) yield sample_residue.view_frame_data()[:chunksize] sample_residue = Sample.from_raw_frames(sample_residue.view_frame_data()[chunksize:], sample_residue.samplewidth, sample_residue.samplerate, sample_residue.nchannels) if len(sample_residue): yield sample_residue.view_frame_data()
def play_console(filename_or_stream): with wave.open(filename_or_stream, 'r') as wav: samplewidth = wav.getsampwidth() samplerate = wav.getframerate() nchannels = wav.getnchannels() bar_width = 60 levelmeter = LevelMeter(rms_mode=False, lowest=-50.0) with Output(samplerate, samplewidth, nchannels, mixing="sequential") as out: print("Audio API used:", out.audio_api) if not out.supports_streaming: raise RuntimeError("need api that supports streaming") out.register_notify_played(levelmeter.update) while True: frames = wav.readframes(samplerate//update_rate) if not frames: break sample = Sample.from_raw_frames(frames, wav.getsampwidth(), wav.getframerate(), wav.getnchannels()) out.play_sample(sample) levelmeter.print(bar_width) while out.still_playing(): time.sleep(1/update_rate) levelmeter.print(bar_width) out.wait_all_played() print("\nDone. Enter to exit:") input()
def render_samples(self, osc: Oscillator, sample_residue: Sample, sample_chunksize: int, stopcondition: Callable[[], bool] = lambda: False, return_residue: bool = False) -> Generator[memoryview, None, Sample]: num_frames = sample_chunksize // synth_params.norm_samplewidth // synth_params.norm_nchannels blocks = osc.blocks() while not stopcondition(): try: block = next(blocks) except StopIteration: break sample = Sample.from_osc_block(block, osc.samplerate, 2 ** (8 * synth_params.norm_samplewidth - 1)).stereo() sample_residue.join(sample) while len(sample_residue) >= num_frames: yield sample_residue.view_frame_data()[:sample_chunksize] sample_residue = Sample.from_raw_frames(sample_residue.view_frame_data()[sample_chunksize:], sample_residue.samplewidth, sample_residue.samplerate, sample_residue.nchannels) if return_residue: return sample_residue yield sample_residue.view_frame_data() return sample_residue
def silence_audio(sid_or_name=None): sound_engine.silence(sid_or_name) def shutdown_audio(): sound_engine.close() def check_api(): a = best_api() a.close() if __name__ == "__main__": smp = Sample.from_raw_frames([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 2, 44100, 1) chunks = smp.chunked_frame_data(chunksize=51, repeat=True) for _ in range(60): print(next(chunks).tobytes()) if os.name == "nt": prepare_oggdec_exe() sound_engine = init_audio({ "explosion": ("explosion.ogg", 99), "amoeba": ("amoeba.ogg", 99), "game_over": ("game_over.ogg", 99) }) print("PLAY SAMPLED SOUNDS...") amoeba_sid = sound_engine.play_sample("amoeba", repeat=True) time.sleep(3) print("PLAY ANOTHER SOUND!") sid = sound_engine.play_sample("game_over", repeat=False)