mirror of
https://github.com/kennethreitz/pytheory.git
synced 2026-06-05 23:00:20 +00:00
Update project dependencies: Replace pygame with sounddevice
This commit is contained in:
+1
-1
@@ -8,7 +8,7 @@ requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"pytuning",
|
||||
"numeral",
|
||||
"pygame",
|
||||
"sounddevice",
|
||||
"scipy",
|
||||
]
|
||||
|
||||
|
||||
+24
-19
@@ -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.)
|
||||
|
||||
Reference in New Issue
Block a user