Skip to content

Instantly share code, notes, and snippets.

@usworked
Created June 18, 2019 14:28
Show Gist options
  • Save usworked/ae406b299705a6deeaba7d2ed7ebfb79 to your computer and use it in GitHub Desktop.
Save usworked/ae406b299705a6deeaba7d2ed7ebfb79 to your computer and use it in GitHub Desktop.
d
import glob
import os, random
import sys
import math
from datetime import datetime
from prompt_toolkit.shortcuts import button_dialog
#from random import seed
import sox
from pydub import AudioSegment
from pydub.silence import split_on_silence
import pydub.scipy_effects
#seed()
"""
start_trim = detect_leading_silence(normalized_sound)
end_trim = detect_leading_silence(normalized_sound.reverse())
trimmed_sound = normalized_sound[start_trim:len(normalized_sound)-end_trim]
--- COMPRESSIONS ---
It sounds like you need to train your ears a bit to know what you are looking for. BPM matching is more about the release than attack, although both parameters interact.
The following may help to train your ears:
1. take any typical 4 on the floor house loop
2. divide 60 by the BPM.
For example if the BPM of the loop is 128:
1/4 notes are 469ms in duration (kick drums, every other snare)
1/8 notes are 234ms in duration (hi hats)
1/16 notes are 117ms in duration (closed hats)
1/32 notes are 56ms in duration
3. Set your attack and release to lowest setting. Then lower your threshold until you see significant gain reduction (-12db is a good starting place). Set a hard knee if the option is present.
4. Slowly raise your attack from its lowest value to 60ms. You should hear the transient of the kick start to get louder and louder relative to the rest of the signal, since the first x ms (the value of the attack) of the kick does not get compressed. Now for the BPM related release..
5. Using the ms to note relationships above try setting your release at just under 1/4, just under 1/8 etc. etc. You should start to hear the mix pump as a result of your release setting. This is because the release dictates when the compressor releases the volume back to normal.
6. Once you have a an attack and release that fits the 'groove' you want to obtain raise the threshold until your gain reduction is anywhere from -1db to -3db. Then level match your signal using the makeup gain so that your compressed signal is identical in perceived volume to your dry signal. The idea here is to make the compression more transparent so its more felt then heard.
Go grab a drink, then come back and A/B the two. Unless you are seeking an obvious effect, the difference should be subtle, but the groove should feel a bit flat when you disable compression. Do this on all sorts of material and you should start to develop an ear for release and attack settings relative to BPM.
#compressed_sound = sliced_sound.compress_dynamic_range(-60, 4.0, slice_time-(slice_time*.1), slice_time/1.5)
"""
def match_target_amplitude(sound, target_dBFS):
change_in_dBFS = target_dBFS - sound.max_dBFS
return sound.apply_gain(change_in_dBFS)
def reverb(sound):
sound = sound.fade_out(50)
pre_file = os.path.join('src', 'tmp') + '/pre-reverb.wav'
post_file = os.path.join('src', 'tmp') + '/post-reverb.wav'
if os.path.exists(pre_file):
os.remove(pre_file)
if os.path.exists(post_file):
os.remove(post_file)
sound.export(pre_file, format='wav')
tfm = sox.Transformer()
tfm.reverb(reverberance=75, pre_delay=15, wet_gain=3)
tfm.build(pre_file, post_file)
new_sound = AudioSegment.from_wav(post_file)
return new_sound
def pitch(sound, rate):
pre_file = os.path.join('src', 'tmp') + '/pre-pitch' + str(rate) + '.wav'
post_file = os.path.join('src', 'tmp') + '/post-pitch' + str(rate) + '.wav'
if os.path.exists(pre_file):
os.remove(pre_file)
if os.path.exists(post_file):
os.remove(post_file)
sound.export(pre_file, format='wav')
tfm = sox.Transformer()
tfm.pitch(n_semitones=rate)
tfm.build(pre_file, post_file)
new_sound = AudioSegment.from_wav(post_file)
return new_sound
def polish(sound, slice_time):
duration = len(sound)
if duration > slice_time:
sliced_sound = sound[:slice_time]
else:
time_left = min(slice_time - duration, 500)
second_of_silence = AudioSegment.silent(time_left)
sliced_sound = sound + second_of_silence
final_slice_duration = len(sliced_sound)
normalized_sound = match_target_amplitude(sliced_sound, -1.0)
faded_sound = normalized_sound.fade_out(min(int(final_slice_duration/3), 200))
return faded_sound
def build():
incoming_folder = 'incoming'
kit_type = button_dialog(
title='Type of kit: ',
text='Please select the type of kit to make: ',
buttons=[
('Drummer', 'drummer')
]
)
if kit_type == 'drummer':
folder_kick = os.path.join(os.path.join('src', incoming_folder, 'Drummer', 'kick'))
folder_ch = os.path.join(os.path.join('src', incoming_folder, 'Drummer', 'ch'))
folder_oh = os.path.join(os.path.join('src', incoming_folder, 'Drummer', 'oh'))
folder_snare = os.path.join(os.path.join('src', incoming_folder, 'Drummer', 'snare'))
random_kick = os.path.join(folder_kick, random.choice(os.listdir(folder_kick)))
print('random_kick: ', random_kick)
random_ch = os.path.join(folder_ch, random.choice(os.listdir(folder_ch)))
print('random_ch: ', random_ch)
random_oh = os.path.join(folder_oh, random.choice(os.listdir(folder_oh)))
print('random_oh: ', random_oh)
random_snare = os.path.join(folder_snare, random.choice(os.listdir(folder_snare)))
print('random_snare: ', random_snare)
file_kick = AudioSegment.from_wav(random_kick)
file_ch = AudioSegment.from_wav(random_ch)
file_oh = AudioSegment.from_wav(random_oh)
file_snare = AudioSegment.from_wav(random_snare)
playlist = AudioSegment.empty()
playlist.set_channels(2)
playlist_filename = kit_type + '-' + datetime.now().strftime('%Y-%m-%d-%H-%M-%S') + '.wav'
total_time = input('Total time (milliseconds): ') or 4000
total_time = int(total_time)
slices = 16
slice_time = math.ceil(total_time/slices)
silence = AudioSegment.silent(duration=slice_time)
playlist += silence
kick = file_kick.set_channels(1)
kick = match_target_amplitude(kick, -1.0)
ch = file_ch.set_channels(1)
ch = match_target_amplitude(ch, -1.0)
oh = file_oh.set_channels(1)
oh = match_target_amplitude(oh, -1.0)
snare = file_snare.set_channels(1)
snare = match_target_amplitude(snare, -1.0)
kick = split_on_silence(kick, min_silence_len=200, silence_thresh=-40, keep_silence=10)
kick = kick[0]
ch = split_on_silence(ch, min_silence_len=200, silence_thresh=-40, keep_silence=10)
ch = ch[0]
oh = split_on_silence(oh, min_silence_len=200, silence_thresh=-40, keep_silence=10)
oh = oh[0]
snare = split_on_silence(snare, min_silence_len=200, silence_thresh=-40, keep_silence=10)
snare = snare[0]
kick = kick - 2
ch = ch - 2
oh = oh - 2
snare = snare - 2
kick = kick.low_pass_filter(10000)
ch = ch.band_pass_filter(300, 16000)
ch = ch.apply_gain_stereo(-1, -5)
oh = oh.band_pass_filter(500, 16000)
ch = ch.apply_gain_stereo(-5, -1)
snare = snare.high_pass_filter(60)
snare = snare.apply_gain_stereo(-3, -1)
kit = [kick, ch, oh, snare]
for sound in kit:
polished_sound = polish(sound, slice_time)
playlist += polished_sound
kick_with_ch = silence.overlay(ch)
kick_with_ch = kick_with_ch.overlay(kick, gain_during_overlay=-5)
kick_with_ch = polish(kick_with_ch, slice_time)
playlist += kick_with_ch
ch_reverb = reverb(ch)
ch_reverb = polish(ch_reverb, slice_time)
playlist += ch_reverb
oh_pitchMinus = pitch(oh, -6)
oh_pitchMinus = polish(oh_pitchMinus, slice_time)
playlist += oh_pitchMinus
snare_reverb = reverb(snare)
snare_reverb = polish(snare_reverb, slice_time)
playlist += snare_reverb
kick_with_snare = silence.overlay(kick)
kick_with_snare = kick_with_snare.overlay(snare, gain_during_overlay=-5)
kick_with_snare = polish(kick_with_snare, slice_time)
playlist += kick_with_snare
ch_pitchPlus = pitch(ch, +6)
kick_with_ch_pitchPlus = silence.overlay(ch_pitchPlus)
kick_with_ch_pitchPlus = kick_with_ch_pitchPlus.overlay(kick, gain_during_overlay=-5)
kick_with_ch_pitchPlus = polish(kick_with_ch_pitchPlus, slice_time)
playlist += kick_with_ch_pitchPlus
oh_reverb = reverb(oh)
oh_reverb = polish(oh_reverb, slice_time)
playlist += oh_reverb
snare_with_ch = silence.overlay(ch)
snare_with_ch = snare_with_ch.overlay(snare, gain_during_overlay=-5)
snare_with_ch = polish(snare_with_ch, slice_time)
playlist += snare_with_ch
kick_with_oh = silence.overlay(oh)
kick_with_oh = kick_with_oh.overlay(kick, gain_during_overlay=-5)
kick_with_oh = polish(kick_with_oh, slice_time)
playlist += kick_with_oh
ch_pitchMinus = pitch(ch, -6)
kick_with_ch_pitchMinus = silence.overlay(ch_pitchMinus)
kick_with_ch_pitchMinus = kick_with_ch_pitchMinus.overlay(kick, gain_during_overlay=-5)
kick_with_ch_pitchMinus = polish(kick_with_ch_pitchMinus, slice_time)
playlist += kick_with_ch_pitchMinus
oh_reversed = oh.reverse()
oh_reversed = polish(oh_reversed, slice_time)
playlist += oh_reversed
snare_with_oh = silence.overlay(oh)
snare_with_oh = snare_with_oh.overlay(snare, gain_during_overlay=-5)
snare_with_oh = polish(snare_with_oh, slice_time)
playlist += snare_with_oh
outgoing_folder = os.path.join('src', 'outgoing', 'Drummer')
playlist += silence
playlist.export(outgoing_folder + '/' + playlist_filename, format='wav')
print(playlist_filename + ' saved.', len(playlist))
'''
ordering:
1 Kick
2 CH
3 OH
4 Snare
5 Kick (with CH)
6 CH (slight reverb)
7 OH (pitch -1)
8 Snare (reverbed)
9 Kick (with snare)
10 Kick (with CH, pitch +1)
11 OH (slight reverb)
12 Snare (with CH)
13 Kick (with OH)
14 Kick (with CH, pitch -1)
15 OH (reversed)
16 Snare (with OH)
default_filename = kit_type + '-' + datetime.date.today().strftime('%Y%b%d') + str(randint(11111, 99999)) + '.wav'
playlist_filename = input('Playlist filename: ') or default_filename
total_time = input('Total time (milliseconds): ') or 4000
total_time = int(total_time)
slices = input('Number of slices: ') or 16
slices = int(slices)
slice_time = math.ceil(total_time/slices)
preferredWidth = 250
print()
print('#slm')
print()
playlist = AudioSegment.empty()
fileList = []
for file in glob.glob(incoming_folder + '/**/*.wav'):
folder = os.path.basename(os.path.dirname(file))
if (kit_type == 'drums'):
if (folder == 'Kicks') or (folder == 'Snares'): fileList.append(file)
if (kit_type == 'percs'):
if (folder == 'Hats') or (folder == 'Percs'): fileList.append(file)
if (kit_type == 'instruments'):
if (folder == 'Instruments'): fileList.append(file)
if (len(fileList) != slices):
print('There must be ' + str(slices) + ' samples for your chosen kit type. I only see: ', len(fileList))
sys.exit()
for file in fileList:
folder = os.path.basename(os.path.dirname(file))
filename = os.path.basename(file)
preSound = AudioSegment.from_wav(file)
sound = eq_process(folder, preSound)
duration = len(sound)
prefix = folder + '/' + filename + ": "
wrapper = textwrap.TextWrapper(initial_indent=prefix, width=preferredWidth)
print(wrapper.fill('Initial duration at time of loading: ' + str(duration) + '/' + str(slice_time)))
messages = []
if duration > slice_time:
sliced_sound = sound[:slice_time]
else:
time_left = min(slice_time - duration, 500)
second_of_silence = AudioSegment.silent(time_left)
messages.append('Silence to add: ' + str(len(second_of_silence)))
sliced_sound = sound + second_of_silence
final_slice_duration = len(sliced_sound)
messages.append('With final slice:' + filename + ' : ' + str(final_slice_duration) + '/' + str(slice_time))
faded_sound = sliced_sound.fade_out(min(int(final_slice_duration/3), 200))
normalized_sound = match_target_amplitude(faded_sound, -1.0)
playlist += normalized_sound
summaryWrapper = textwrap.TextWrapper(initial_indent=' '*len(prefix), width=preferredWidth)
for m in messages:
print(summaryWrapper.fill(m))
print()
playlist.export(outgoing_folder + '/' + playlist_filename + '.wav')
print(playlist_filename + '.wav saved.', len(playlist))
'''
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment