Documentation sur soundbox
This commit is contained in:
parent
093a341dd2
commit
7cbd48b9ad
22
generate.py
22
generate.py
|
@ -1,3 +1,4 @@
|
|||
import numpy as np
|
||||
import soundbox
|
||||
import sys
|
||||
|
||||
|
@ -10,13 +11,20 @@ Génère un morceau au synthétiseur dans le fichier [output].""")
|
|||
output_file = sys.argv[1]
|
||||
|
||||
|
||||
def sine(dur, freq, value=1):
|
||||
def sharp_sine(dur, freq, value=1):
|
||||
return soundbox.envelope(
|
||||
attack=.01, decay=.2, release=.2,
|
||||
signal=soundbox.sine(dur, freq, value))
|
||||
attack=.005, decay=.1, release=.1,
|
||||
signal=soundbox.sine(dur + .2, freq, value))
|
||||
|
||||
|
||||
signal = soundbox.silence(10)
|
||||
def smooth_sine(dur, freq, value=1):
|
||||
return soundbox.envelope(
|
||||
attack=.1, decay=.2, release=.1,
|
||||
signal=soundbox.sine(dur + .2, freq, value))
|
||||
|
||||
|
||||
length = 9
|
||||
signal = soundbox.silence(length)
|
||||
|
||||
chords_l = (
|
||||
(('do', 2),),
|
||||
|
@ -36,11 +44,11 @@ chords_r = (
|
|||
(('fa', 3), ('la', 3), ('do', 4)),
|
||||
)
|
||||
|
||||
for shift in (.5, 4.5):
|
||||
for shift in np.arange(.5, length - 4, 4):
|
||||
for i in range(len(chords_l)):
|
||||
soundbox.add_signal(signal, start=i / 2 + shift,
|
||||
source=soundbox.chord(
|
||||
instr=sine, dur=.8 if shift == 4.5 and i == 7 else .4,
|
||||
instr=smooth_sine, dur=.6 if shift == 4.5 and i == 7 else .4,
|
||||
freqs=soundbox.note_freqs(chords_l[i]),
|
||||
value=.4
|
||||
))
|
||||
|
@ -48,7 +56,7 @@ for shift in (.5, 4.5):
|
|||
for i in range(len(chords_r)):
|
||||
soundbox.add_signal(signal, start=i + shift,
|
||||
source=soundbox.chord(
|
||||
instr=sine, dur=1.1,
|
||||
instr=sharp_sine, dur=0.9,
|
||||
freqs=soundbox.note_freqs(chords_r[i]),
|
||||
value=.4
|
||||
))
|
||||
|
|
123
soundbox.py
123
soundbox.py
|
@ -12,24 +12,57 @@ max_val = 2 ** (8 * samp_width - 1) - 1
|
|||
samp_rate = 44100
|
||||
|
||||
|
||||
def add_signal(dest, start, source):
|
||||
dest[int(samp_rate * start):int(samp_rate * start) + len(source)] += source
|
||||
|
||||
|
||||
def silence(dur):
|
||||
"""
|
||||
Génère un signal silencieux.
|
||||
|
||||
Paramètres:
|
||||
dur (float): Durée en secondes
|
||||
|
||||
Retourne:
|
||||
(ndarray): Signal généré
|
||||
"""
|
||||
return np.zeros((int(samp_rate * dur),))
|
||||
|
||||
|
||||
def sine(dur, freq, value=1):
|
||||
"""
|
||||
Génère un signal sinusoïdal.
|
||||
|
||||
Paramètres:
|
||||
dur (float): Durée en secondes
|
||||
freq (float): Fréquence de la sinusoïde en hertz
|
||||
value (float): Amplitude du signal (valeur relative entre 0 et 1)
|
||||
|
||||
Retourne:
|
||||
(ndarray): Signal généré
|
||||
"""
|
||||
x = np.arange(int(samp_rate * dur))
|
||||
return value * max_val * np.sin(2 * np.pi * freq * x / samp_rate)
|
||||
|
||||
|
||||
def envelope(attack, decay, release, signal):
|
||||
"""
|
||||
Applique une enveloppe à une note.
|
||||
|
||||
Paramètres:
|
||||
attack (float): Temps d’attaque de la note (en secondes)
|
||||
decay (float): Temps de chute de la note vers la phase
|
||||
de maintien (en secondes)
|
||||
release (float): Temps de relâche à la fin de la note (en secondes)
|
||||
signal (ndarray): Signal original
|
||||
|
||||
Retourne:
|
||||
(ndarray): Signal généré
|
||||
"""
|
||||
total = len(signal)
|
||||
attack = int(attack * total)
|
||||
decay = int(decay * total)
|
||||
release = int(release * total)
|
||||
attack = int(attack * samp_rate)
|
||||
decay = int(decay * samp_rate)
|
||||
release = int(release * samp_rate)
|
||||
|
||||
if attack + decay + release > total:
|
||||
raise ValueError('Note trop courte pour l’application de l’enveloppe')
|
||||
|
||||
sustain = total - attack - decay - release
|
||||
|
||||
return signal * np.concatenate((
|
||||
|
@ -39,6 +72,46 @@ def envelope(attack, decay, release, signal):
|
|||
np.linspace(start=2/3, stop=0, num=release, endpoint=True),
|
||||
))
|
||||
|
||||
|
||||
def add_signal(dest, start, source):
|
||||
"""
|
||||
Ajoute un signal source dans un autre signal.
|
||||
|
||||
Paramètres:
|
||||
dest (ndarray): Signal dans lequel le signal source sera ajouté
|
||||
start (float): Temps en secondes à partir duquel le signal est ajouté
|
||||
source (ndarray): Signal source
|
||||
|
||||
Retourne: None
|
||||
"""
|
||||
dest[int(samp_rate * start):int(samp_rate * start) + len(source)] += source
|
||||
|
||||
|
||||
def chord(instr, dur, freqs, value=1):
|
||||
"""
|
||||
Construit un accord de notes.
|
||||
|
||||
Paramètres:
|
||||
instr (function): Instrument à utiliser pour générer les notes
|
||||
dur (float): Durée de chaque note (en secondes)
|
||||
freqs (list): Fréquence de chaque note
|
||||
value (float): Amplitude totale partagée par les notes
|
||||
|
||||
Retourne:
|
||||
(ndarray): Signal généré
|
||||
"""
|
||||
signal = np.ndarray(0)
|
||||
|
||||
for freq in freqs:
|
||||
new_signal = instr(dur, freq, value / len(freqs))
|
||||
|
||||
if len(new_signal) > len(signal):
|
||||
signal.resize(len(new_signal))
|
||||
|
||||
signal += new_signal
|
||||
|
||||
return signal
|
||||
|
||||
notes = {
|
||||
'do': 0, 'si#': 0,
|
||||
'do#': 1, 'reb': 1,
|
||||
|
@ -56,25 +129,34 @@ notes = {
|
|||
|
||||
|
||||
def note_freq(note, octave):
|
||||
"""
|
||||
Calcule la fréquence correspondant à une note.
|
||||
|
||||
Paramètres:
|
||||
note (str): Nom de la note
|
||||
octave (int): Numéro de l’octave
|
||||
|
||||
Retourne:
|
||||
(float): Fréquence correspondante en hertz
|
||||
"""
|
||||
return (440
|
||||
* (2 ** (octave - 3))
|
||||
* math.pow(2, (notes[note] - 9) / 12))
|
||||
|
||||
|
||||
def note_freqs(notes):
|
||||
"""Calcule la fréquence correspondant à un ensemble de 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):
|
||||
"""
|
||||
Écrit un signal dans un fichier au format WAV.
|
||||
|
||||
Paramètres:
|
||||
out_name (str): Chemin vers le fichier
|
||||
signal (ndarray): Signal à enregistrer
|
||||
"""
|
||||
with wave.open(out_name, 'w') as file:
|
||||
file.setnchannels(1)
|
||||
file.setsampwidth(samp_width)
|
||||
|
@ -83,6 +165,15 @@ def save_signal(out_name, signal):
|
|||
|
||||
|
||||
def load_signal(in_name):
|
||||
"""
|
||||
Charge un signal depuis un fichier au format WAV.
|
||||
|
||||
Paramètres:
|
||||
in_name (str): Chemin vers le fichier
|
||||
|
||||
Retourne:
|
||||
(ndarray): Signal décodé
|
||||
"""
|
||||
with wave.open(in_name, 'r') as file:
|
||||
assert file.getnchannels() == 1
|
||||
assert file.getsampwidth() == samp_width
|
||||
|
|
Loading…
Reference in New Issue