Skip to content

Instantly share code, notes, and snippets.

@jniimi
Created May 10, 2022 16:46
Show Gist options
  • Save jniimi/a72087b362de5f095814d9df07d30b22 to your computer and use it in GitHub Desktop.
Save jniimi/a72087b362de5f095814d9df07d30b22 to your computer and use it in GitHub Desktop.
import wave
import numpy as np
import pandas as pd
from IPython.display import Audio
class AudioDataFrame:
def __init__(self, bpm = 120, bars = 1, sampling_freq=44100, sub=False, subsub=False):
self.bpm = bpm
self.bars = bars
self.beats = 4 * bars
self.seconds = 60 / bpm * self.beats
self.secfreqperbeat = self.seconds * sampling_freq / self.beats
self.sampling_freq = sampling_freq
self.audio = self.generate_blank_signal()
self.add_metronome(sub=sub, subsub=subsub)
def generate_blank_signal(self):
t = np.arange(self.sampling_freq * self.seconds)
y = t * 0
d = pd.DataFrame({
't':t, 'y':y
})
return d
def add_metronome(self, sub=False, subsub=False):
audio = self.audio
audio['metronome'] = audio['t'] * 0
for i in range(self.beats): # 4th
l = i * self.secfreqperbeat
audio.loc[ audio['t'] == np.round(l), 'metronome'] = 0.5
if sub: # 8th
l8 = l + (self.secfreqperbeat / 2)
audio.loc[ audio['t'] == np.round(l8), 'metronome'] = 0.25
if subsub: # 16th
for j in range(4):
l16 = l + (self.secfreqperbeat / 4) * j
if j == 0:
continue
audio.loc[ self.audio['t'] == np.round(l16), 'metronome'] = 0.25
self.audio = audio
def show(self):
self.audio.plot(x='t', y='y', ylim=[-1,1])
#self.audio.plot(y='y', ylim=[-1,1], use_index=True)
def play(self, metronome=True, isLimit=True):
if metronome:
self.audio['y'] = self.audio['y'] + self.audio['metronome']
if isLimit:
self.audio.loc[ self.audio['y'] > 1, 'y' ] = 1
self.audio.loc[ self.audio['y'] < -1, 'y' ] = -1
display(Audio(self.audio['y'], rate=self.sampling_freq))
def load_drumsound(self, sound='CHH'):
drumkit = {
'CHH': '/content/drive/MyDrive/PyMusic/hihat.wav',
'KICK': '/content/drive/MyDrive/PyMusic/kick.wav',
'SNARE': '/content/drive/MyDrive/PyMusic/snare.wav'
}
f = drumkit[sound]
with wave.open(f, 'rb') as w:
params = w.getparams()
if self.sampling_freq != params[2]:
raise ValueError('Sampling Frequencies between loading sound and the project differ')
w.setpos(0)
s = w.readframes(nframes=params[3])
s = np.frombuffer(s, dtype=np.int16).astype(float)/(2**(8*params[1]-1))
return s, params[3]
def find_position(self, pos):
if pos == 0:
return 0
return np.round((self.sampling_freq * 60 / self.bpm) * pos)
def hit_sound_in_the_specific_point(self, sound='CHH', start_beats = [0], volume=1):
if (volume > 1) or (volume < 0):
raise ValueError('Volume must be in 0 - 1.')
audio = self.audio
sound, fr = self.load_drumsound(sound=sound)
for pos in start_beats:
pos = self.find_position(pos)
blank = self.generate_blank_signal().rename(columns={'y':'sound'}).drop(columns='t')
blank.loc[pos:pos+fr-1, 'sound'] = sound * volume
audio = audio.join(blank, how='outer').fillna(0)
audio['y'] = audio['y'] + audio['sound']
audio = audio.drop(columns='sound')
self.audio = audio
audio = AudioDataFrame(bpm = 90, bars = 1, sub=True)
audio.hit_sound_in_the_specific_point(sound='CHH', start_beats=[0, 0.333, 0.666, 1, 1.5, 2, 2.5, 3, 3.5, 3.75], volume=0.2)
audio.hit_sound_in_the_specific_point(sound='KICK', start_beats=[0, 1.75, 2.5], volume=0.3)
audio.hit_sound_in_the_specific_point(sound='SNARE', start_beats=[1, 3], volume=0.35)
audio.play(metronome=False)
audio.show()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment