1

I am trying to generate a "beep" sound with a constant tone of 2350 Hz. I am using the code (which I got here) below to generate a WAV file with this tone that has a duration of 0.5 seconds.

import math
import wave
import struct

# Audio will contain a long list of samples (i.e. floating point numbers describing the
# waveform).  If you were working with a very long sound you'd want to stream this to
# disk instead of buffering it all in memory list this.  But most sounds will fit in 
# memory.
audio = []
sample_rate = 44100.0


def append_silence(duration_milliseconds=500):
    """
    Adding silence is easy - we add zeros to the end of our array
    """
    num_samples = duration_milliseconds * (sample_rate / 1000.0)

    for x in range(int(num_samples)): 
        audio.append(0.0)

    return


def append_sinewave(
        freq=440.0, 
        duration_milliseconds=500, 
        volume=1.0):
    """
    The sine wave generated here is the standard beep.  If you want something
    more aggresive you could try a square or saw tooth waveform.   Though there
    are some rather complicated issues with making high quality square and
    sawtooth waves... which we won't address here :) 
    """ 

    global audio # using global variables isn't cool.

    num_samples = duration_milliseconds * (sample_rate / 1000.0)

    for x in range(int(num_samples)):
        audio.append(volume * math.sin(2 * math.pi * freq * ( x / sample_rate )))

    return


def save_wav(file_name):
    # Open up a wav file
    wav_file=wave.open(file_name,"w")

    # wav params
    nchannels = 1

    sampwidth = 2

    # 44100 is the industry standard sample rate - CD quality.  If you need to
    # save on file size you can adjust it downwards. The stanard for low quality
    # is 8000 or 8kHz.
    nframes = len(audio)
    comptype = "NONE"
    compname = "not compressed"
    wav_file.setparams((nchannels, sampwidth, sample_rate, nframes, comptype, compname))

    # WAV files here are using short, 16 bit, signed integers for the 
    # sample size.  So we multiply the floating point data we have by 32767, the
    # maximum value for a short integer.  NOTE: It is theortically possible to
    # use the floating point -1.0 to 1.0 data directly in a WAV file but not
    # obvious how to do that using the wave module in python.
    for sample in audio:
        wav_file.writeframes(struct.pack('h', int( sample * 32767.0 )))

    wav_file.close()

    return


append_sinewave(volume=1, freq=2350)
save_wav("output.wav")

When run the code below (using Librosa) to generate a spectrogram of the WAV file I see this:

Spectrogram:

Spectrogram

Code:

beepData,beep_sample_rate = librosa.load(beepSoundPath, sr=44100)
D = librosa.stft(beepData)
S_db = librosa.amplitude_to_db(np.abs(D), ref=np.max)
librosa.display.specshow(S_db)

The problem is the extra frequencies at the beginning and end of the spectrogram. How can I get rid of these unwanted frequencies?

coder
  • 381
  • 2
  • 22
  • Does the waveform look correct when you open it in an audio editing program? I am pretty sure this is just an artifact of how FFTs work, since the data is finite. – Karl Knechtel Mar 29 '21 at 12:32
  • Is there anyway to counteract this effect by adjusting the frequency? The waveform looks right when I display it using `_ = librosa.display.waveplot(beepData,sr=beep_sample_rate)`. – coder Mar 29 '21 at 12:49
  • 1
    FFT's will never give a singular frequency at the output unless you have an infinitely long signal. The smaller the sample length, the 'fatter' your frequency band will be. See this question as to why the result is mirrored about Fs/2: https://dsp.stackexchange.com/questions/4825/why-is-the-fft-mirrored/4827#4827 – jhso Mar 29 '21 at 13:48

1 Answers1

0

These are artifacts of the STFT / FFT process, because there are discontinuities at the start/end of a window. You can try to use librosa.stft(..., center=False), which should eliminate the one in the start. Then you may need to trim off / ignore the last output segments as well. At least half of n_fft parameter.

Jon Nordby
  • 5,494
  • 1
  • 21
  • 50