Valeria S.
Valeria S.

Reputation: 348

Writing wav file gives clicking sounds

I have this code for creating wav file:

class Note:
    def __init__(self,name,octave,duration):
        self.name = name
        self.octave = octave
        self.duration = duration

SAMPLE_RATE = 44100

wav = wave.open("starw.wav", mode="w")
wav.setparams((1, 2, SAMPLE_RATE , SAMPLE_RATE*4, 'NONE', 'noncompressed'))

def generate_sample(note,volume):
    freq = FREQUENCIES[note.octave][note.name]
    duration = note.duration
    total_samples = int(SAMPLE_RATE * duration)
    for i in range(0,total_samples):
        s = np.clip(int((volume)*math.sin(freq*math.pi*float(i)/float(SAMPLE_RATE))),-32768,32768)    
        data = struct.pack('<h',s)
        wav.writeframes(data)

    return data

for i,m in enumerate(melody):

    if i<len(melody)/5:
        generate_sample(m, 100+i*500)
    elif len(melody)/5<=i<=4*len(melody)/5:
        generate_sample(m, 8800)
    else:
        generate_sample(m, 8800-i*20)

Here FREQUENCIES is a dict describing notes and their frequencies. melody is an array of Note objects. Everything works fine except for the fact that strange clicking sounds are produced during pauses. As far as I understood, it has something to do with the sample rate. But I don't know how to change my code in order to avoid pauses. Here is the melody: https://drive.google.com/open?id=1e03VC90w5WF4QdU-VxrV906dl_c3UvSQ

Upvotes: 3

Views: 676

Answers (2)

Andrey Tyukin
Andrey Tyukin

Reputation: 44918

If you mean the unpleasant clicking sounds between the notes: that's entirely normal.

Even in programming languages that have been designed specifically for generating music, this effect occurs if you simply juxtapose blocks with (sine) waves that have some fixed amplitude during the entire duration.

Here is a sketch of what it looks like when one note ends, and the next note begins:

enter image description here

Unless the first node ends exactly at zero by accident, you will hear this "jump" that is plotted at 2.

In order to avoid this, you have to implement better note synthesis.

A simple solution would be to smoothly ramp up the amplitude of each note, and then let it smoothly decay in the end. Here is what a transition between two notes would look like if you do this:

enter image description here

This graph has been generated by the following formula:

  if x > 2:
    return sin((x-2) * 60) * (1 - exp(-(x-2)**2 / 0.5))
  else:
    return sin(x * 30) * (1- exp(-(x-2)**2 / 0.5))

Hopefully, you can recognize the (1 - exp(-(x - tClick) ** 2 / smoothness)) "mollifier" factors that are responsible for the ramp up / decay.

But then again you see that the gaps between the notes appear to become larger, and if you then try to make the intervals of the different notes intersect, it all becomes rather messy and complicated, and you're probably better off taking a programming language or a library that already knows how to do it properly. I think Chuck manual contained a rather detailed explanation of how to get increasingly better sounding notes.

Upvotes: 6

UrhoKarila
UrhoKarila

Reputation: 354

If I were to guess, I'd think it may be related to the fact that the note lengths are sometimes not even multiples of the note's frequency. As a result, sin(end_time) of the note may sometimes result in a relatively large value: 32768 in the worst case. The sudden drop in volume at that frequency may be responsible for the clicking/chuckling noise you're hearing.

Terminating notes only when int((volume)*math.sin(freq*math.pi*float(i)/float(SAMPLE_RATE))) evaluates to a value near 0 would eliminate much of that noise, should this be the case.

Upvotes: 1

Related Questions