Search code examples
pythonnumpypyaudio

Pyaudio / Numpy Concatenated Notes Merely Extend the First Note


I'm trying to generate my own notes using pyaudio, but I'm running into what is surely a beginner's mistake. I can generate pure sin wav tones and play them sequentially, but if I try to concatenate them, I don't get three notes in a row, I get the original note played three times as long.

import numpy as np
import pyaudio

def play_note(note):
    p = pyaudio.PyAudio()
    stream = p.open(format=pyaudio.paFloat32,
                      channels=1,
                      rate=44100,
                      output=True)
    stream.write(np.asarray(note, dtype=np.float32))
    stream.stop_stream()
    stream.close()
    p.terminate()

sampling_rate = 44100
seconds = 1
x = np.arange(sampling_rate * seconds)

freqs = [440,660,880]

notes = []

for freq in freqs:
    note = 100*np.sin(2 * np.pi * freq * x /sampling_rate)
    notes.append(note)

# This is the behavior I want
for note in notes:
    play_note(note)

# I would expect this to behave similarly, but without the gaps. It doesn't.
note = np.concatenate(notes)
play_note(note)

Solution

  • I get the same result with pyaudio 0.2.11 (running on Mac OS 10.12.6). I was able to fix it by adding the argument frames_per_buffer=1 to p.open() and num_frames=len(note) to stream.write():

    def play_note(note):
        p = pyaudio.PyAudio()
        stream = p.open(format=pyaudio.paFloat32,
                          channels=1,
                          rate=44100,
                          output=True,
                          frames_per_buffer=1)
        stream.write(np.asarray(note, dtype=np.float32), num_frames=len(note))
        stream.stop_stream()
        stream.close()
        p.terminate()
    

    I haven't investigated further to answer why the original version doesn't work or why this change fixes it. Perhaps a pyaudio guru will give a more thorough answer.