experimentations/december_wave.py

134 lines
4.4 KiB
Python

# Copyright (C) 2020 harrysentonbury
# GNU General Public License v3.0
import numpy as np
import sounddevice as sd
class ThyBeatifier():
"""
data - 1d numpy array
bar_size - int, number of samples per bar Eg:
for 1 second of 48kH audio bar_size=48000
beats_per_bar - int, number of beats per bar
attack - int, attack size in samples default=100
flip - boolean, reverse beat shape, default=False
decay_log_base - float, log base of decay
decay_min - float, sets decay_log_base of minimum decay value, default=-1
"""
def __init__(self, data, bar_size, beats_per_bar, attack=100, flip=False,
decay_log_base=10, decay_min=-1):
self.data = data
self.bar_size = bar_size
self.beats_per_bar = beats_per_bar
self.flip = flip
self.attack = attack
if decay_min > 1:
raise ValueError("Must be <= 1")
self.decay_min = decay_min
self.decay_log_base = decay_log_base
def beater(self):
"""stick sound array into drumming like envelope thingy"""
self.data = self.data / np.max(np.absolute(self.data))
self.duration = np.size(self.data) / self.bar_size
self.beats_duration = int(self.duration * self.beats_per_bar)
self.extra = np.zeros(np.size(self.data) % self.beats_duration)
self.ramp_decay = np.logspace(1, self.decay_min, int(np.size(
self.data) // self.beats_duration),
base=self.decay_log_base) / self.decay_log_base
self.ramp_attack = np.linspace(self.ramp_decay[-1], 1, self.attack)
self.ramp_decay[:self.attack] *= self.ramp_attack
self.beats = np.array([])
for i in range(self.beats_duration):
self.beats = np.concatenate((self.beats, self.ramp_decay))
if self.flip:
self.beats = np.flip(self.beats)
self.beats = np.concatenate((self.beats, self.extra))
return self.data * self.beats
def lfo(frequency=0.5):
y = (np.sin(x * frequency))
return y
def ranlfo(frequency=0.5):
y = (np.sin(xx * frequency))
return y
def bass_waveform2(bass_f):
""" fm """
y = (np.sin(bass_f * x + ((lfo(1) * 2) + 2) * np.sin((bass_f / 2) * x))) * 0.6
return y
def bass_waveform(bass_f):
y = (np.sin(bass_f * x + ((lfo(1) * 2) + 2) * np.sin((bass_f / 2) * x))) * 0.4
y = np.concatenate((y, bass_waveform2(bass_freq2)))
return y
def thorny_waveform():
def thorn(freq):
"""thorn wave"""
y = 2 / np.pi * np.arcsin(np.sin(freq * x))
y = np.exp(y) * 0.2
return y
thorns_0 = (thorn((bass_freq * 4) - 2) + thorn((bass_freq * 4) - 1) + \
thorn(bass_freq * 4) + thorn(bass_freq * 4)) * 0.1
thorns_1 = (thorn((bass_freq2 * 4) - 2) + thorn((bass_freq2 * 4) - 1) + \
thorn(bass_freq2 * 4) + thorn(bass_freq2 * 4)) * 0.1
ramp_2 = np.linspace(0, 1, np.size(thorns_0))
thorns_0[:] *= ramp_2
thorns_1[:] *= ramp_2
thorns = np.concatenate((thorns_0, thorns_1))
return thorns
def mordant_evolution_wave():
y = np.sin(((freq_0 * 4) + ranlfo()) * x) + np.sin(((freq_0 * 4) + ranlfo()) * x
) + (np.sin(freq_0 * x + ramp_1 * np.sin(freq_0 * 0.5 * x)))
y[:fade_size] *= fade_in
y[-fade_size:] *= fade_out
y = np.concatenate((y, np.flip(y)))
return y * 0.08
sample_rate = 48000
duration = 8
freq_0 = 110.0
beats_per_bar = 4
bass_freq = (2**(6 / 12)) * freq_0
bass_freq2 = (2**(5 / 12)) * freq_0
fade_size = 150
x = np.linspace(0, duration * 2 * np.pi, int(duration * sample_rate))
xx = np.arange(np.size(x))
fade_in = np.linspace(0, 1, fade_size)
fade_out = np.flip(fade_in)
ramp_0 = np.logspace(1, -1, np.size(x)) * 0.6
ramp_1 = (np.flip(ramp_0) * 5) - 0.3
waveform = mordant_evolution_wave()
bass_wave = bass_waveform(bass_freq)
# turns waveforms into purcussion
bass = ThyBeatifier(data=bass_wave, bar_size=sample_rate, beats_per_bar=0.5, flip=False)
sound = ThyBeatifier(data=waveform, bar_size=sample_rate, beats_per_bar=beats_per_bar)
a_result_0 = (sound.beater() * 0.13) + (bass.beater() * 0.25) + thorny_waveform()
# split into two channels and delay one channel a bit
roll_size = 600
a_result_1 = np.roll(a_result_0, roll_size)
a_result_1[:roll_size] = 0
a_result_stereo = np.vstack((a_result_0, a_result_1)).T
# repeat a couple of times
for i in range(2):
a_result_stereo = np.concatenate((a_result_stereo, a_result_stereo), axis=0)
sd.play(a_result_stereo, sample_rate)
sd.wait()