Skip to content

Instantly share code, notes, and snippets.

@Splines
Created February 19, 2022 21:31
Show Gist options
  • Save Splines/a5b915b1b743705242ada9e398aef4ee to your computer and use it in GitHub Desktop.
Save Splines/a5b915b1b743705242ada9e398aef4ee to your computer and use it in GitHub Desktop.
A very basic mono synthesizer using PyAudio and Matplotlib
# Installation
# Run `pip install pyaudio` first
# If that throws an error, you need to install a "wheel" first (pre-packaged binary)
# Follow the instructions here: https://stackoverflow.com/a/71073645/9655481
# e.g. I use Python 3.9.6 and installed the file "PyAudio‑0.2.11‑cp39‑cp39‑win_amd64.whl"
# from here: https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyaudio
import time
import matplotlib.pyplot as plt
import numpy as np
import pyaudio
from matplotlib.widgets import Button, Slider
print('🎈 Started')
########################## Generate sound data ##################################
SAMPLE_RATE = 44100 # sample rate in hertz
FRAMES_PER_BUFFER = int(SAMPLE_RATE * 0.1) # corresponds to 100ms
init_amplitude = 0.5 # signal amplitude scaler
init_frequency = 600 # signal frequency in hertz
FRAME_DURATION = 1 / SAMPLE_RATE
T = FRAMES_PER_BUFFER * FRAME_DURATION
t = np.linspace(0, T, FRAMES_PER_BUFFER) # discrete signal time points
sound_data = {
'clipped': None,
'scaled': None
}
def update_sound_data(amplitude, frequency):
signal = amplitude * np.sin(2 * np.pi * frequency * t)
sound_data['clipped'] = signal.clip(-1, 1)
# Convert from float [-1, +1] to int16 [-32768, +32767]
sound_data['scaled'] = ((sound_data['clipped'] * 32767.5) - 0.5)\
.astype(np.int16)
update_sound_data(init_amplitude, init_frequency)
################################ Plot ##########################################
fig, ax = plt.subplots()
line, = plt.plot(t, sound_data['clipped'])
ax.set_title('Synth')
ax.set_xlabel('Time [s]')
ax.set_xlim(left=0, right=10e-3)
ax.set_ylim(bottom=-2, top=2)
# Adjust the main plot to make room for the sliders
plt.subplots_adjust(left=0.25, bottom=0.25)
# Horizontal slider to control the frequency.
# TODO: logarithmic scale
ax_freq = plt.axes([0.25, 0.1, 0.65, 0.03])
freq_slider = Slider(
ax=ax_freq,
label='Frequency [Hz]',
valmin=0.1,
valmax=20000,
valinit=init_frequency,
)
# Vertical slider to control the amplitude
ax_amp = plt.axes([0.1, 0.25, 0.0225, 0.63])
amp_slider = Slider(
ax=ax_amp,
label="Amplitude",
valmin=0,
valmax=3,
valinit=init_amplitude,
orientation="vertical"
)
def update_plot(event):
update_sound_data(amp_slider.val, freq_slider.val)
line.set_ydata(sound_data['clipped'])
fig.canvas.draw_idle()
freq_slider.on_changed(update_plot)
amp_slider.on_changed(update_plot)
ax_reset = plt.axes([0.8, 0.025, 0.1, 0.04])
button = Button(ax_reset, 'Reset', hovercolor='0.975')
def reset(event):
freq_slider.reset()
amp_slider.reset()
button.on_clicked(reset)
################################ PyAudio #######################################
def get_data(in_data, frame_count, time_info, status):
# sound_data gets changed dynamically
out = sound_data['scaled']
return (out.tobytes(), pyaudio.paContinue)
# PyAudio documentation: http://people.csail.mit.edu/hubert/pyaudio/
p = pyaudio.PyAudio()
stream = p.open(
# int16 (number of bytes per audio frame)
format=p.get_format_from_width(width=2),
channels=1, # mono
rate=SAMPLE_RATE,
frames_per_buffer=FRAMES_PER_BUFFER,
output=True,
stream_callback=get_data)
stream.start_stream()
plt.show()
# while stream.is_active():
# # Sleep to keep the stream active, since
# # the main thread must not terminate.
# time.sleep(0.1)
stream.stop_stream()
stream.close()
p.terminate()
@Splines
Copy link
Author

Splines commented Feb 19, 2022

⚠ Right now, there's some crackling going on every time PyAudio reads from the next buffer. It even gets worse when you change the frequency. If anybody knows how to fix this, please let me know. The same problem occurs when you comment out all the lines for the Matplotlib GUI, so it's probably a PyAudio problem or rather I am doing something wrong with the buffer size.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment