Update project dependencies: Replace pygame with sounddevice

This commit is contained in:
2024-11-05 09:50:23 -05:00
parent b1edecc291
commit 91b25e22d1
2 changed files with 25 additions and 20 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ requires-python = ">=3.10"
dependencies = [
"pytuning",
"numeral",
"pygame",
"sounddevice",
"scipy",
]
+24 -19
View File
@@ -1,19 +1,17 @@
from enum import Enum
import numpy
import scipy.signal
import contextlib
with contextlib.redirect_stdout(None):
import pygame
import sounddevice as sd
from .tones import Tone
SAMPLE_RATE = 44_100
SAMPLE_PEAK = 4_096
def sine_wave(hz, peak=SAMPLE_PEAK, n_samples=SAMPLE_RATE):
"""Compute N samples of a sine wave with given frequency and peak amplitude.
Defaults to one second.
Defaults to one second.
"""
length = SAMPLE_RATE / float(hz)
omega = numpy.pi * 2 / length
@@ -21,41 +19,48 @@ def sine_wave(hz, peak=SAMPLE_PEAK, n_samples=SAMPLE_RATE):
onecycle = peak * numpy.sin(xvalues)
return numpy.resize(onecycle, (n_samples,)).astype(numpy.int16)
def sawtooth_wave(hz, peak=SAMPLE_PEAK, rising_ramp_width=1, n_samples=SAMPLE_RATE):
"""Compute N samples of a sine wave with given frequency and peak amplitude.
Defaults to one second.
rising_ramp_width is the percentage of the ramp spend rising:
.5 is a triangle wave with equal rising and falling times.
Defaults to one second.
rising_ramp_width is the percentage of the ramp spend rising:
.5 is a triangle wave with equal rising and falling times.
"""
t = numpy.linspace(0, 1, 500 * 440/hz, endpoint=False)
t = numpy.linspace(0, 1, 500 * 440 / hz, endpoint=False)
wave = scipy.signal.sawtooth(2 * numpy.pi * 5 * t, width=rising_ramp_width)
wave = numpy.resize(wave, (n_samples,))
# Sawtooth waves sound very quiet, so multiply peak by 4.
return (peak * 6 * wave.astype(numpy.int16))
return peak * 6 * wave.astype(numpy.int16)
def _play_for(sample_wave, ms):
"""Play the given NumPy array, as a sound, for ms milliseconds."""
sound = pygame.sndarray.make_sound(sample_wave)
sound.play(-1)
pygame.time.delay(ms)
sound.stop()
# Convert milliseconds to seconds
seconds = ms / 1000
# sounddevice expects float32 samples between -1 and 1
normalized_wave = sample_wave.astype(numpy.float32) / SAMPLE_PEAK
# Play the audio and wait
sd.play(normalized_wave, SAMPLE_RATE)
sd.wait()
class Synth(Enum):
SINE = sine_wave
SAW = sawtooth_wave
def play(tone_or_chord, temperament='equal', synth=Synth.SINE, t=1_000):
pygame.mixer.pre_init(SAMPLE_RATE, -16, 1)
pygame.mixer.init()
def play(tone_or_chord, temperament="equal", synth=Synth.SINE, t=1_000):
if isinstance(tone_or_chord, Tone):
chord = [synth(tone_or_chord.pitch(temperament=temperament, symbolic=True))]
else:
chord = [synth(tone.pitch(temperament=temperament, symbolic=True)) for tone in tone_or_chord.tones]
chord = [
synth(tone.pitch(temperament=temperament, symbolic=True))
for tone in tone_or_chord.tones
]
_play_for(sum(chord), ms=t)
# 69 + 12*np.log2(hz_nonneg/440.)