Initial commit
This commit is contained in:
		
						commit
						e8bfb981cc
					
				|  | @ -0,0 +1 @@ | |||
| out.wav | ||||
|  | @ -0,0 +1,34 @@ | |||
| import soundbox | ||||
| import numpy as np | ||||
| import scipy.signal as sig | ||||
| import matplotlib | ||||
| import matplotlib.pyplot as plt | ||||
| 
 | ||||
| signal = soundbox.load_signal('out.wav') | ||||
| 
 | ||||
| freq, time, fts = sig.stft(signal, soundbox.samp_rate, nperseg=soundbox.samp_rate * 0.5) | ||||
| 
 | ||||
| fig, ax = plt.subplots() | ||||
| ax.pcolormesh( | ||||
|     time, freq, | ||||
|     np.abs(fts), | ||||
|     cmap='plasma', | ||||
|     shading='gouraud') | ||||
| 
 | ||||
| 
 | ||||
| def freq_format(value, pos): | ||||
|     return f'{value:.0f} Hz' | ||||
| 
 | ||||
| 
 | ||||
| def time_format(value, pos): | ||||
|     return f'{value:.0f} s' | ||||
| 
 | ||||
| 
 | ||||
| ax.set_xlabel('Temps') | ||||
| ax.xaxis.set_major_formatter(matplotlib.ticker.FuncFormatter(time_format)) | ||||
| 
 | ||||
| ax.set_ylabel('Fréquence') | ||||
| ax.set_ylim(0, 800) | ||||
| ax.yaxis.set_major_formatter(matplotlib.ticker.FuncFormatter(freq_format)) | ||||
| 
 | ||||
| plt.show() | ||||
|  | @ -0,0 +1,23 @@ | |||
| import soundbox | ||||
| import numpy as np | ||||
| import matplotlib | ||||
| import matplotlib.pyplot as plt | ||||
| 
 | ||||
| signal = soundbox.load_signal('out.wav') | ||||
| freqs = np.fft.fft(signal) | ||||
| scale = soundbox.samp_rate / len(signal) | ||||
| 
 | ||||
| fig, ax = plt.subplots() | ||||
| ax.plot(np.absolute(freqs)) | ||||
| 
 | ||||
| 
 | ||||
| def freq_format(value, pos): | ||||
|     return f'{value * scale:.0f} Hz' | ||||
| 
 | ||||
| 
 | ||||
| ax.set_xlabel('Fréquence') | ||||
| ax.set_xlim(0 / scale, 800 / scale) | ||||
| ax.xaxis.set_major_formatter(matplotlib.ticker.FuncFormatter(freq_format)) | ||||
| ax.xaxis.set_major_locator(plt.MultipleLocator(100 / scale)) | ||||
| 
 | ||||
| plt.show() | ||||
|  | @ -0,0 +1,53 @@ | |||
| import soundbox | ||||
| 
 | ||||
| 
 | ||||
| def sine(dur, freq, value=1): | ||||
|     return soundbox.envelope( | ||||
|         attack=.01, decay=.2, release=.2, | ||||
|         signal=soundbox.sine(dur, freq, value)) | ||||
| 
 | ||||
| 
 | ||||
| def square(dur, freq, value=1): | ||||
|     return soundbox.envelope( | ||||
|         attack=.01, decay=.2, release=.2, | ||||
|         signal=soundbox.square(dur, freq, value)) | ||||
| 
 | ||||
| 
 | ||||
| signal = soundbox.silence(9) | ||||
| 
 | ||||
| chords_l = ( | ||||
|     (('do', 2),), | ||||
|     (('sol', 2),), | ||||
|     (('la', 2),), | ||||
|     (('sol', 2),), | ||||
|     (('do', 2),), | ||||
|     (('sol', 2),), | ||||
|     (('la', 2),), | ||||
|     (('sol', 2),), | ||||
| ) | ||||
| 
 | ||||
| chords_r = ( | ||||
|     (('do', 3), ('mi', 3), ('sol', 3)), | ||||
|     (('sol', 3), ('si', 3), ('re', 4)), | ||||
|     (('la', 3), ('do', 4), ('mi', 4)), | ||||
|     (('fa', 3), ('la', 3), ('do', 4)), | ||||
| ) | ||||
| 
 | ||||
| for shift in (.5, 4.5): | ||||
|     for i in range(len(chords_l)): | ||||
|         soundbox.add_signal(signal, start=i / 2 + shift, | ||||
|             source=soundbox.chord( | ||||
|                 instr=square, dur=.6, | ||||
|                 freqs=soundbox.note_freqs(chords_l[i]), | ||||
|                 value=.05 | ||||
|             )) | ||||
| 
 | ||||
|     for i in range(len(chords_r)): | ||||
|         soundbox.add_signal(signal, start=i + shift, | ||||
|             source=soundbox.chord( | ||||
|                 instr=sine, dur=1.1, | ||||
|                 freqs=soundbox.note_freqs(chords_r[i]), | ||||
|                 value=.6 | ||||
|             )) | ||||
| 
 | ||||
| soundbox.save_signal('out.wav', signal) | ||||
|  | @ -0,0 +1,97 @@ | |||
| import wave | ||||
| import numpy as np | ||||
| import scipy.signal as sig | ||||
| import math | ||||
| 
 | ||||
| # Nombre d’octets par échantillon | ||||
| samp_width = 2 | ||||
| 
 | ||||
| # Valeur maximale d’un échantillon | ||||
| max_val = 2 ** (8 * samp_width - 1) - 1 | ||||
| 
 | ||||
| # Fréquence d’échantillonnage (Hertz) | ||||
| samp_rate = 44100 | ||||
| 
 | ||||
| 
 | ||||
| def add_signal(dest, start, source): | ||||
|     dest[int(samp_rate * start):int(samp_rate * start) + len(source)] += source | ||||
| 
 | ||||
| 
 | ||||
| def silence(dur): | ||||
|     return np.zeros((int(samp_rate * dur),)) | ||||
| 
 | ||||
| 
 | ||||
| def sine(dur, freq, value=1): | ||||
|     x = np.arange(int(samp_rate * dur)) | ||||
|     return value * max_val * np.sin(2 * np.pi * freq * x / samp_rate) | ||||
| 
 | ||||
| 
 | ||||
| def square(dur, freq, value=1): | ||||
|     x = np.arange(int(samp_rate * dur)) | ||||
|     return value * max_val * sig.square(2 * np.pi * freq * x / samp_rate) | ||||
| 
 | ||||
| 
 | ||||
| def envelope(attack, decay, release, signal): | ||||
|     total = len(signal) | ||||
|     attack = int(attack * total) | ||||
|     decay = int(decay * total) | ||||
|     release = int(release * total) | ||||
|     sustain = total - attack - decay - release | ||||
| 
 | ||||
|     return signal * np.concatenate(( | ||||
|         np.linspace(start=0, stop=1, num=attack, endpoint=False), | ||||
|         np.linspace(start=1, stop=2/3, num=decay, endpoint=False), | ||||
|         np.linspace(start=2/3, stop=2/3, num=sustain, endpoint=False), | ||||
|         np.linspace(start=2/3, stop=0, num=release, endpoint=True), | ||||
|     )) | ||||
| 
 | ||||
| notes = { | ||||
|     'do': 0, 'si#': 0, | ||||
|     'do#': 1, 'reb': 1, | ||||
|     're': 2, | ||||
|     're#': 3, 'mib': 3, | ||||
|     'mi': 4, 'fab': 4, | ||||
|     'fa': 5, 'mi#': 5, | ||||
|     'fa#': 6, 'solb': 6, | ||||
|     'sol': 7, | ||||
|     'sol#': 8, 'lab': 8, | ||||
|     'la': 9, | ||||
|     'la#': 10, 'sib': 10, | ||||
|     'si': 11, 'dob': 11, | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| def note_freq(note, octave): | ||||
|     return (440 | ||||
|         * (2 ** (octave - 3)) | ||||
|         * math.pow(2, (notes[note] - 9) / 12)) | ||||
| 
 | ||||
| 
 | ||||
| def note_freqs(notes): | ||||
|     return list(map(lambda info: note_freq(*info), notes)) | ||||
| 
 | ||||
| 
 | ||||
| def chord(instr, dur, freqs, value=1): | ||||
|     signal = np.zeros((int(samp_rate * dur),)) | ||||
| 
 | ||||
|     for freq in freqs: | ||||
|         signal += instr(dur, freq, value / len(freqs)) | ||||
| 
 | ||||
|     return signal | ||||
| 
 | ||||
| 
 | ||||
| def save_signal(out_name, signal): | ||||
|     with wave.open(out_name, 'w') as file: | ||||
|         file.setnchannels(1) | ||||
|         file.setsampwidth(samp_width) | ||||
|         file.setframerate(samp_rate) | ||||
|         file.writeframesraw(signal.astype('<h').tostring()) | ||||
| 
 | ||||
| 
 | ||||
| def load_signal(in_name): | ||||
|     with wave.open(in_name, 'r') as file: | ||||
|         assert file.getnchannels() == 1 | ||||
|         assert file.getsampwidth() == samp_width | ||||
|         assert file.getframerate() == samp_rate | ||||
|         size = file.getnframes() | ||||
|         return np.ndarray((size,), '<h', file.readframes(size)) | ||||
		Loading…
	
		Reference in New Issue