experimentations/shaped_filtered_square.py

94 lines
3.3 KiB
Python

#!/usr/bin/env python3
# Copyright (C) 2021 harrysentonbury
# GNU General Public License v3.0
import numpy as np
# import scipy.io.wavfile as wf
import scipy.signal as sig
import simpleaudio as sa
class ThyRythmer():
"""
apply a percussion envelope to audio numpy array
data - 1d numpy array
bar_size - int, number of samples per bar Eg:
for 1 second of 48kH audio bar_size=48000
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, <= 1, sets decay_log_base of minimum decay value, default=-1
patern - python list of integers, 1=beat 0=miss a beat, default=[1, 1, 1, 1]
"""
def __init__(self, data, bar_size, attack=100, flip=False,
decay_min=-1, decay_log_base=10, pattern=[1, 1, 1, 1]):
self.data = data
self.bar_size = bar_size
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
self.pattern = pattern
if type(self.pattern) is not list:
raise TypeError(f"pattern must be a list of integers {type(self.pattern)}")
self.beats_per_bar = np.size(self.pattern)
def beater(self):
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, np.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([])
self.decay_end_val = np.zeros(np.size(self.ramp_decay))
self.decay_end_val[:] = self.ramp_decay[-1]
for i in range(int(self.beats_duration / self.beats_per_bar)):
self.pattern.extend(self.pattern)
for i in range(self.beats_duration):
if self.pattern[i] >= 1:
self.beats = np.concatenate((self.beats, self.ramp_decay))
else:
self.beats = np.concatenate((self.beats, self.decay_end_val))
if self.flip:
self.beats = np.flip(self.beats)
self.beats = np.concatenate((self.beats, self.extra))
return self.data * self.beats
def sqwer(f):
y = sig.square(x * f, 0.5)
return y * 0.2
def filtering_butter(data, cru):
sos1 = sig.butter(10, cru, btype='highpass', fs=sample_rate, output='sos')
filt = sig.sosfilt(sos1, data)
return filt
sample_rate = 44100
duration = 10
x = np.linspace(0, 2 * np.pi * duration, int(sample_rate * duration))
sound = sqwer(330) + sqwer(664) + sqwer(333)
sound = filtering_butter(sound, 3000)
sound_beat = ThyRythmer(data=sound, bar_size=sample_rate, decay_min=-1, flip=True,
decay_log_base=5, pattern=[1, 1, 0])
sound_1 = sound_beat.beater()
sound_1 = np.int16(sound_1 * 32767)
play_object = sa.play_buffer(sound_1, 1, 2, sample_rate)
play_object.wait_done()
# wf.write("woteva.wav", sample_rate, sound_1)