I am trying to create a .wav file which contains a 440Hz sine wave tone, with 10Hz vibrato that varies the pitch between 430Hz and 450Hz. Something must be wrong with my approach, because when I listen to the generated .wav file, it sounds like the "amplitude" of the vibrato (e.g. the highest/lowest pitch reached by the peaks and troughs of the waveform of the vibrato) just progressively increases over time, instead of staying between 430-450Hz. What is wrong with my approach here? Here is some minimal python code which illustrates the issue:
import math
import wave
import struct
SAMPLE_RATE = 44100
NOTE_PITCH_HZ = 440.0 # Note pitch, Hz
VIBRATO_HZ = 10.0 # Vibrato frequency, Hz
VIBRATO_VARIANCE_HZ = 10.0 # Vibrato +/- variance from note pitch, Hz
NOTE_LENGTH_SECS = 2.0 # Length of .wav file to generate, in seconds
NUM_SAMPLES = int(SAMPLE_RATE * NOTE_LENGTH_SECS)
# Generates a single point on a sine wave
def _sine_sample(freq: float, sine_index: int):
return math.sin(2.0 * math.pi * float(freq) * (float(sine_index) / SAMPLE_RATE))
samples = []
for i in range(NUM_SAMPLES):
# Generate sine point for vibrato, map to range -VIBRATO_VARIANCE_HZ:VIBRATO_VARIANCE_HZ
vibrato_level = _sine_sample(VIBRATO_HZ, i)
vibrato_change = vibrato_level * VIBRATO_VARIANCE_HZ
# Mofidy note pitch based on vibrato state
note_pitch = NOTE_PITCH_HZ + vibrato_change
sample = _sine_sample(note_pitch, i) * 32767.0
# Turn amplitude down to 80%
samples.append(int(sample * 0.8))
# Create mono .wav file with a 2 second 440Hz tone, with 10Hz vibrato that varies the
# pitch by +/- 10Hz (between 430Hz and 450Hz)
with wave.open("vibrato.wav", "w") as wavfile:
wavfile.setparams((1, 2, SAMPLE_RATE, NUM_SAMPLES, "NONE", "not compressed"))
for sample in samples:
wavfile.writeframes(struct.pack('h', sample))