92 lines
3.0 KiB
Python
92 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
|
|
|
# Copyright (C) 2021 harrysentonbury
|
|
# GNU General Public License v3.0
|
|
|
|
|
|
from audio2numpy import open_audio
|
|
import numpy as np
|
|
import scipy.io.wavfile as wf
|
|
import sounddevice as sd
|
|
|
|
|
|
class ThyRandomAudioSlices():
|
|
"""
|
|
Slices and randomly rearange audio arrays.
|
|
data - numpy array.
|
|
window_size - int, optional size of slice in samples. default=2**14
|
|
flip - boolean, optional reverse output. default=False.
|
|
fade_length - int, optional fade in and fade out length of slices in samples
|
|
default=300
|
|
"""
|
|
def __init__(self, data, window_size=2**14, flip=False, fade_length=500):
|
|
self.data = data
|
|
self.window_size = window_size
|
|
self.flip = flip
|
|
self.fade_length = fade_length
|
|
|
|
def chop_and_chuck(self):
|
|
self.fade_size = self.fade_length if self.fade_length < \
|
|
self.window_size else self.window_size
|
|
|
|
self.extra_size = np.size(self.data) % self.window_size
|
|
self.extra = np.zeros(self. window_size - self.extra_size)
|
|
self.data = np.concatenate((self.data, self.extra))
|
|
self.range_size = np.size(self.data) // self.window_size
|
|
self.fade_in = np.linspace(0, 1, self.fade_size)
|
|
self.fade_out = np.flip(self.fade_in)
|
|
self.bitties = np.zeros((self.range_size, self.window_size + self.fade_size))
|
|
self.result = np.zeros_like(self.data)
|
|
|
|
for i in range(self.range_size):
|
|
if i == 0:
|
|
self.bitties[i, :] = self.data[:self.window_size + self.fade_size]
|
|
else:
|
|
self.bitties[i, :] = self.data[(i*self.window_size) - \
|
|
self.fade_size:(i*self.window_size)+self.window_size]
|
|
# shuffle
|
|
rng = np.random.default_rng()
|
|
rng.shuffle(self.bitties)
|
|
|
|
self.bitties[1:, :self.fade_size] *= self.fade_in
|
|
self.bitties[1:, -self.fade_size:] *= self.fade_out
|
|
for i in range(self.range_size):
|
|
if i == 0:
|
|
self.result[:self.window_size + self.fade_size] += self.bitties[i, :]
|
|
else:
|
|
self.result[(i * self.window_size) - \
|
|
self.fade_size:(i * self.window_size) + \
|
|
self.window_size] += self.bitties[i, :]
|
|
return self.result
|
|
|
|
|
|
path_to_file = "audio/brown_8.mp3"
|
|
|
|
# read mp3 or wav with appropriate package.
|
|
if path_to_file.endswith(".mp3"):
|
|
sound, sample_rate = open_audio(path_to_file)
|
|
else:
|
|
sample_rate, sound = wf.read(path_to_file)
|
|
|
|
# if stereo convert to mono else carry on.
|
|
try:
|
|
sound = sound[:, 0] + sound[:, 1]
|
|
except IndexError:
|
|
pass
|
|
|
|
# convert to float64 then normalize.
|
|
sound = np.float64(sound)
|
|
sound = sound / np.max(np.abs(sound))
|
|
|
|
# repeat the infile a few times just to make this example longer.
|
|
sound = np.concatenate((sound, sound, sound))
|
|
|
|
window_size = 2**12
|
|
shuffle_object = ThyRandomAudioSlices(sound, window_size, fade_length=1500)
|
|
shuffle_object.flip = True
|
|
result = shuffle_object.chop_and_chuck()
|
|
|
|
# play
|
|
sd.play(result, sample_rate)
|
|
sd.wait()
|