# Messing with sounds

I want to create a simple tool for generating sound, altered on user input. I figured out that I can generate wave with numpy, I can get user input with pygame and use pygame.sndarray and pygame.mixer to make the noise. Lets see part one, that

#### We are the noise generation!

I found a nice tutorial about generating sine wave with numpy, and I used that as a starting point. I defined a frequency and a sample rate, but instead of sine, I tried to make a simple saw, like this:
`[0, 0.25, 0.5, 0.75, 1, 0, 0.25, 0.5, 0.75, 1, ...]`

I used the linspace function from numpy to create one edge of the saw, which is as long as the frequencieth part of the sample:
```fs = 44100 freq=440. np.linspace(-1,1,fs/freq)```

After reading the pygame documetation, I realized I should initialize the mixer module first, and check its properties:
``` import pygame import pygame.mixer as mixer import pygame.snarray as snd```

mixer.init()
mixer.get_init()
# (22050, -16, 2)
So I needed to create an array with two coloumns. How?

First, I had to find a way to create two parallel saw edge. I achieved this with the tile and reshape funcion: a made two edges in line, thed cut it half.
```import numpy as np fs = mixer.get_init() chs = mixer.get_init() tst_sound = np.tile(np.linspace(-1,1,fs/freq), chs).reshape(fs/freq, chs)```

I had already one short instance of sound, I only needed to repeat it freq times to make it last 1 sec. The problem is that the tile function repeats the number of coloumns, which is understandable, and made me found the transpose function. I swapped the rows and coloumns, repeated the new columns, and swapped them back.
```tst_second = np.transpose(np.tile(np.transpose(tst_sound), freq)) tst_snd = snd.make_sound(tst_second)```

If the shape of he sound is OK, the make_sound function should not return any problem. After I sorted out coloumn issue, it worked for me. Then I only needed to play it with the mixer module:
`tst_snd.play()`

Hell no! It's not the sound I want. It turns out, the mixer.get_init() returns three values with a purpose: it expects integers with the given numer of bits. So the type in which we want the array is:
`new_type = "int"+str(abs(mixer.get_init()))`

To convert between types, there is the astype method:
`tst_snd = tst_snd.astype(new_type)`

#### Improvement

Getting more into documenttion, I found out that last argument of tile() accepted more than one dimension, so the code looks like this:
`tst_sound = np.tile(np.linspace(-1.,1.,fs/frequency), (chs, 1))`

Also, instead of transposing, I can use the axis argument of repeat(), to make it much more simple:
`tst_second = np.repeat(tst_sound, frequency, 1)`

We can pack all this into a function which takes the sample frequency and the number of channels directly from the mixer module:
```def create_snd(frequency=440., duration=.3):     # mixer init props:     fs = mixer.get_init()     new_type = "int"+str(abs(mixer.get_init()))     chs = mixer.get_init()     # one wave     tst_sound = np.tile(np.linspace(-1.,1.,fs/frequency), (chs, 1))     # make in one second     tst_second = np.repeat(tst_sound, frequency*duration, 1)     # convert it to a sound and return it     return(snd.make_sound(tst_second.astype(new_type)))```

It cannot be played without a proper initializaton os pygame (I don't know why, probably reading the docs would help :D), so here are two short functions for playing a sine wave and manipulate its frequency:

```def create_sin(frequency=440., duration=.3, amplitude=100):     # mixer init props     fs = mixer.get_init()     chs = mixer.get_init()     snd_type = "int"+str(abs(mixer.get_init()))     # time     t = np.arange(0, duration, 1./fs)     freq = np.array((frequency, frequency))     sig = np.sin(2 * np.pi * freq * t.reshape(-1, 1))*amplitude     return(snd.make_sound(sig.astype(snd_type)))   # program for changing notes def int_snd_test():     # program for changing notes     #     pg.init()     pg.display.set_mode([50,50])     mixer.pre_init(frequency=44100)     mixer.init(frequency=44100, channels=1)     #     freq=440.     done = False     print("press w to increase, s to decrease frequency")     #     while not done:         e = pg.event.wait()         print("Waiting for user event")         # create sound and play it:         create_sin(frequency=freq, duration=1, amplitude=250).play()         print("sound began to play...")         # check user's events:         if e.type == pg.KEYDOWN:             if e.key == pg.K_ESCAPE:                 done = True                 pg.display.quit()                 break             elif e.key == pg.K_w:                 freq += 100                 print("frequency up; ", freq)             elif e.key == pg.K_s:                 freq -= 100                 print("frequency down;", freq)```

However, some of the waves consist of two different sounds. Anyone has any idea why? I did not found in ot stack overflow.

Note that I added a duration argument to the function to be able make it shorter than one sec and amplitude variable becaouse the original was too silent.