Compare commits

..

No commits in common. "a9cd4d2207ffd035300b52fd7811f2aee61be0ea" and "093a341dd22cd2d4cba2d937b2b1125e10af651f" have entirely different histories.

6 changed files with 23 additions and 122 deletions

View File

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

View File

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -1,4 +1,3 @@
import numpy as np
import soundbox
import sys
@ -11,20 +10,13 @@ Génère un morceau au synthétiseur dans le fichier [output].""")
output_file = sys.argv[1]
def sharp_sine(dur, freq, value=1):
def sine(dur, freq, value=1):
return soundbox.envelope(
attack=.005, decay=.1, release=.1,
signal=soundbox.sine(dur + .2, freq, value))
attack=.01, decay=.2, release=.2,
signal=soundbox.sine(dur, freq, value))
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)
signal = soundbox.silence(10)
chords_l = (
(('do', 2),),
@ -44,11 +36,11 @@ chords_r = (
(('fa', 3), ('la', 3), ('do', 4)),
)
for shift in np.arange(.5, length - 4, 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=smooth_sine, dur=.6 if shift == 4.5 and i == 7 else .4,
instr=sine, dur=.8 if shift == 4.5 and i == 7 else .4,
freqs=soundbox.note_freqs(chords_l[i]),
value=.4
))
@ -56,7 +48,7 @@ for shift in np.arange(.5, length - 4, 4):
for i in range(len(chords_r)):
soundbox.add_signal(signal, start=i + shift,
source=soundbox.chord(
instr=sharp_sine, dur=0.9,
instr=sine, dur=1.1,
freqs=soundbox.note_freqs(chords_r[i]),
value=.4
))

View File

@ -12,57 +12,24 @@ 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 dattaque 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 * samp_rate)
decay = int(decay * samp_rate)
release = int(release * samp_rate)
if attack + decay + release > total:
raise ValueError('Note trop courte pour lapplication de lenveloppe')
attack = int(attack * total)
decay = int(decay * total)
release = int(release * total)
sustain = total - attack - decay - release
return signal * np.concatenate((
@ -72,46 +39,6 @@ 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,
@ -129,34 +56,25 @@ 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 loctave
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 save_signal(out_name, signal):
"""
Écrit un signal dans un fichier au format WAV.
def chord(instr, dur, freqs, value=1):
signal = np.zeros((int(samp_rate * dur),))
Paramètres:
out_name (str): Chemin vers le fichier
signal (ndarray): Signal à enregistrer
"""
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)
@ -165,15 +83,6 @@ 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