Skip to content

Instantly share code, notes, and snippets.

@grinsted
Last active April 18, 2021 22:14
Show Gist options
  • Save grinsted/3fb39aa61accbe8ef2c1aa678c074153 to your computer and use it in GitHub Desktop.
Save grinsted/3fb39aa61accbe8ef2c1aa678c074153 to your computer and use it in GitHub Desktop.
Tool for quickly making drumkits for the novation circuit
# This file contains code to save 64 samples as a syx file for Novation Circuit
#
# * It reads a single long wave file.
# * splits it into 64 equal chunks
# * crops the sound
# * adds a super fast fadein and "an appropriate fadeout". To remove clicks
# * normalizes every sample
# * Saves the whole thing as a syx file.
#
# Aslak Grinsted 2020
finput = 'fast_circuit_drumkit2.wav' # this has to be 48khz! There is no resampling.
foutput = finput.replace('.wav','.syx')
from scipy.io import wavfile
samplerate, data = wavfile.read(finput)
import matplotlib.pyplot as plt
import numpy as np
import binascii
import math
import struct
#Utility functions to write the NOVATION CIRCUIT sysex file
def crcToNybbles(crc):
n = [0, 0, 0, 0, 0, 0, 0, 0]
for t in range(8):
n[t] = 15 & crc >> 4 * (7 - t)
return bytearray(n)
# def sevenToEight(packet):
# packet = [b for b in packet]
# data = bytearray(b"")
# n = len(packet)
# r = 0
# while r < n:
# i = packet[r : r + 8]
# c = i[1:]
# for a in range(len(c)):
# hi = (i[0] & (1 << a)) >> a
# c[a] += hi << 7
# r += 8
# data.extend(c)
# return data
def eightToSeven(e):
n = len(e)
r = n + math.ceil(n / 7)
a = [0] * r
o = 0
i = 0
while o < n:
for t in range(7):
c = 0
if o + t < n:
a[i + t + 1] = 127 & e[o + t]
c = (128 & e[o + t]) >> 7 - t
a[i] |= c
o += 7
i += 8
return bytearray(a)
#THIS IS A SAMPLE IN THE CIRCUIT SAMPLE BANK
class Sample:
def __init__(self):
self.sampledata = [] # has to be 48khz
def set_sampledata(self, data):
if len(data.shape)>1:
data = np.mean(data, axis=1) #make mono
data = data / np.max(np.abs(data))
self.sampledata = (data * 32767).astype(np.int16)
def blob(self):
n = len(self.sampledata) * 2
sampleblob = [1, 16, 128, 187, 0, 0]
sampleblob.extend([n & 0xFF, (n >> 8) & 0xFF, (n >> 16) & 0xFF, n >> 24])
if n > 0:
raw = struct.pack(">{}h".format(len(self.sampledata)), *self.sampledata)
sampleblob.extend(raw)
return sampleblob
#CLASS HOLDING 64 SAMPLES
class SampleBank:
samples = []
def __init__(self):
self.samples = [Sample() for count in range(64)]
def blob(self):
blob = [64]
for sample in self.samples:
blob.extend(sample.blob())
# length must be 5763072
if len(blob) > 5763072:
raise Exception("insufficient space!")
blob.extend([0] * (5763072 - len(blob)))
return blob
def sysex(self):
blob = self.blob()
crc = binascii.crc32(bytearray(blob))
sysex = list(b'\xF0\x00\x20\x29\x00\x77\x00\x00\x02\x03\x0B\x00\x00\x00\x00\x00\x05\x07\x0F\x00\x00\x00\xF7')
for offset in range(0,5763072,256):
row = blob[offset:offset+256]
row = eightToSeven(row)
sysex.extend(list(b'\xF0\x00\x20\x29\x00\x79'))
sysex.extend(row)
sysex.extend(list(b'\xF7'))
sysex.extend(list(b'\xF0\x00\x20\x29\x00\x7A'))
sysex.extend(crcToNybbles(crc))
sysex.extend(list(b'\xF7'))
return bytearray(sysex)
Bank = SampleBank()
#THIS CODE reads a single long wave file and splits it into 64 individual samples
N=64 #
Ns = int(data.shape[0]/N)
fadein =8
for ix in range(N):
s = data[Ns*ix:Ns*(ix+1)-20,:]
s = s/np.max(s)
#s = s.mean(axis=1)
lastix = np.where(np.abs(s)>0.02)[0][-1]
fadeout=int(lastix/10)
for c in range(s.shape[1]):
s[0:fadein,c] = s[0:fadein,c] * np.linspace(0,1,fadein)
s[lastix-fadeout:lastix,c] = s[lastix-fadeout:lastix,c] * np.linspace(1,0,fadeout)
s = s[0:lastix,:]
Bank.samples[ix].set_sampledata(s)
#THESE lines saves individual samples.
# kit = int(ix/8) + 1
# sample = (ix) % 8 +1
# fname = 'drum{}_kit{}.wav'.format(sample,kit)
# s = (s*32767).astype(data.dtype)
# wavfile.write(fname,samplerate,s)
syxfile = open(foutput, "wb")
syxfile.write(Bank.sysex())
syxfile.close()
@mungewell
Copy link

You inspired me, I create a Python module/app to pack/unpack samples and to experiment what the circuit is actually capable of.
https://github.com/mungewell/circuit_samples

Example use would be

$ python3 circuit_samples.py -u test/ Gabe\ Miller\ Drums.syx 
$ python3 circuit_samples.py -p test/ -o my_pack.syx
$ python3 circuit_samples.py -i my_pack.syx

@grinsted
Copy link
Author

grinsted commented Dec 6, 2020

Nice .. I can see you have also cleaned up the code alot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment