Search code examples
pythonaudioscipywav

Sound wave created by Python gives plays background beats along with notes


I have created a script to play twinkle twinkle litte star with reference to this article (https://towardsdatascience.com/mathematics-of-music-in-python-b7d838c84f72) and created some of my changes. The music plays correctly, but there are some annoying beats in the background. Can some please help how to remove that annoying beats sound from the background?

My code:


import numpy as np
import pandas as pd
import os
from scipy.io.wavfile import write

class Music:
    
    def __init__(self,notes,tempo,base_freq = 'C4'):
        self.notes = notes
        self.tempo = tempo
        self.base_freq = base_freq
        
    def generate_freq(self):
        octave = ['C', 'c', 'D', 'd', 'E', 'F', 'f', 'G', 'g', 'A', 'a', 'B']

        num = int(self.base_freq[1])
        delta = 4 - num
        bf = 261.63 * 2**(delta)
        note_freqs = {octave[i]: bf * pow(2,(i/12)) for i in range(len(octave))}
        note_freqs = {key : round(note_freqs[key],2) for key in note_freqs}
        note_freqs[''] = 1.0 # silent note
        return note_freqs
    
    def generate_wave(self, freq):
        sample_rate = 44100
        div = int(sample_rate/freq)
        freq = round(sample_rate/div,2)
        amplitude = 4096
        time = 60/self.tempo
        t = np.linspace(0, time, int(sample_rate * time), endpoint=False)
        wave = np.sin(2 * np.pi * freq * t)
        wave *= 32767
        return wave
    
    def generate_digital_note(self):
        note_freqs = self.generate_freq()
        all_notes =[]
        for i in range(len(self.notes)):
            freq = note_freqs[self.notes[i]]
            wave = [self.generate_wave(freq)]
            all_notes = all_notes + wave
        all_notes = np.concatenate(all_notes)
        return all_notes

music_notes = 'C-C-G-G-A-A-G--F-F-E-E-D-D-C--G-G-F-F-E-E-D--G-G-F-F-E-E-D--C-C-G-G-A-A-G--F-F-E-E-D-D-C'
music_notes = music_notes.split('-')

twinkle_normal = Music(music_notes,120)
twinkle_normal_wave = twinkle_normal.generate_digital_note()

write('twinkle-twinkle_normal.wav', 44100, twinkle_normal_wave.astype(np.int16))

Thanks,


Solution

  • The signal that you generate has discontinuties at each note transition. Each discontinuity results in a "pop" of the speakers. A simple way to fix that is to multiply the array associated with each note by an envelope that rises from 0 to 1 at the beginning and falls from 1 to 0 at the end (i.e. provide an "attack" and "release" for the sound of each note).

    Synth and DSP gurus will probably have much better suggestions--the possibilities are endless--but one quick way to do that is to use the Tukey window provided by scipy.signal.windows.tukey. To try it, make two changes to your script:

    • Add
      from scipy.signal.windows import tukey
      
      at the beginning.
    • In the function generate_wave(), change this line:
            wave = np.sin(2 * np.pi * freq * t)
      
      to
            wave = np.sin(2 * np.pi * freq * t) * tukey(len(t))