Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Velocity sensitive #10

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
@@ -0,0 +1,4 @@
build/
*.c
*.so
local_config.*
2 changes: 2 additions & 0 deletions 0 Saw/definition.txt
@@ -0,0 +1,2 @@
%midinote.wav
%%velocitysensitivity=1
2 changes: 2 additions & 0 deletions Makefile
@@ -0,0 +1,2 @@
samplerbox_audio.so: samplerbox_audio.pyx samplerbox_audio_neon.pyx
python setup.py build_ext --inplace
7 changes: 3 additions & 4 deletions README.md
Expand Up @@ -22,9 +22,8 @@ SamplerBox works with the RaspberryPi's built-in soundcard, but it is recommende
2. Download SamplerBox and build it with:

~~~
git clone https://github.com/josephernest/SamplerBox.git
cd SamplerBox ; sudo python setup.py build_ext --inplace
~~~
git clone https://github.com/josephernest/SamplerBox.git ;
cd SamplerBox ; make

3. Run the soft with `python samplerbox.py`.

Expand Down Expand Up @@ -54,4 +53,4 @@ Author : Joseph Ernest (twitter: [@JosephErnest](http:/twitter.com/JosephErnest)
[License](#license)
----

[Creative Commons BY-SA 3.0](http://creativecommons.org/licenses/by-sa/3.0/)
[Creative Commons BY-SA 3.0](http://creativecommons.org/licenses/by-sa/3.0/)
127 changes: 127 additions & 0 deletions peripherals/hd44780.py
@@ -0,0 +1,127 @@
# The wiring for the LCD is as follows:
# 1 : GND
# 2 : 5V
# 3 : Contrast (0-5V)*
# 4 : RS (Register Select)
# 5 : R/W (Read Write) - GROUND THIS PIN
# 6 : Enable or Strobe
# 7 : Data Bit 0 - NOT USED
# 8 : Data Bit 1 - NOT USED
# 9 : Data Bit 2 - NOT USED
# 10: Data Bit 3 - NOT USED
# 11: Data Bit 4
# 12: Data Bit 5
# 13: Data Bit 6
# 14: Data Bit 7
# 15: LCD Backlight +5V**
# 16: LCD Backlight GND

#import
import RPi.GPIO as GPIO
import time

# Define GPIO to LCD mapping
LCD_RS = 25
LCD_E = 24
LCD_D4 = 23
LCD_D5 = 22
LCD_D6 = 27
LCD_D7 = 17


# Define some device constants
LCD_WIDTH = 16 # Maximum characters per line
LCD_CHR = True
LCD_CMD = False

LCD_LINE_1 = 0x80 # LCD RAM address for the 1st line
LCD_LINE_2 = 0xC0 # LCD RAM address for the 2nd line

# Timing constants
E_PULSE = 0.0005
E_DELAY = 0.0005

GPIO.setwarnings(True)
GPIO.setmode(GPIO.BCM) # Use BCM GPIO numbers
GPIO.setup(LCD_E, GPIO.OUT) # E
GPIO.setup(LCD_RS, GPIO.OUT) # RS
GPIO.setup(LCD_D4, GPIO.OUT) # DB4
GPIO.setup(LCD_D5, GPIO.OUT) # DB5
GPIO.setup(LCD_D6, GPIO.OUT) # DB6
GPIO.setup(LCD_D7, GPIO.OUT) # DB7



def lcd_init():
# Initialise display
lcd_byte(0x33,LCD_CMD) # 110011 Initialise
lcd_byte(0x32,LCD_CMD) # 110010 Initialise
lcd_byte(0x06,LCD_CMD) # 000110 Cursor move direction
lcd_byte(0x0C,LCD_CMD) # 001100 Display On,Cursor Off, Blink Off
lcd_byte(0x28,LCD_CMD) # 101000 Data length, number of lines, font size
lcd_byte(0x01,LCD_CMD) # 000001 Clear display

time.sleep(E_DELAY)

def lcd_byte(bits, mode):
# Send byte to data pins
# bits = data
# mode = True for character
# False for command

GPIO.output(LCD_RS, mode) # RS

# High bits
GPIO.output(LCD_D4, False)
GPIO.output(LCD_D5, False)
GPIO.output(LCD_D6, False)
GPIO.output(LCD_D7, False)
if bits&0x10==0x10:
GPIO.output(LCD_D4, True)
if bits&0x20==0x20:
GPIO.output(LCD_D5, True)
if bits&0x40==0x40:
GPIO.output(LCD_D6, True)
if bits&0x80==0x80:
GPIO.output(LCD_D7, True)

# Toggle 'Enable' pin
lcd_toggle_enable()

# Low bits
GPIO.output(LCD_D4, False)
GPIO.output(LCD_D5, False)
GPIO.output(LCD_D6, False)
GPIO.output(LCD_D7, False)
if bits&0x01==0x01:
GPIO.output(LCD_D4, True)
if bits&0x02==0x02:
GPIO.output(LCD_D5, True)
if bits&0x04==0x04:
GPIO.output(LCD_D6, True)
if bits&0x08==0x08:
GPIO.output(LCD_D7, True)

# Toggle 'Enable' pin
lcd_toggle_enable()

def lcd_toggle_enable():
# Toggle enable
time.sleep(E_DELAY)
GPIO.output(LCD_E, True)
time.sleep(E_PULSE)
GPIO.output(LCD_E, False)
time.sleep(E_DELAY)

def lcd_string(message,line):
# Send string to display




message = message.ljust(LCD_WIDTH," ")

lcd_byte(line, LCD_CMD)

for i in range(LCD_WIDTH):
lcd_byte(ord(message[i]),LCD_CHR)
58 changes: 40 additions & 18 deletions samplerbox.py
Expand Up @@ -10,17 +10,23 @@


#########################################
# LOCAL
# CONFIG
#########################################

AUDIO_DEVICE_ID = 2 # change this number to use another soundcard
SAMPLES_DIR = "." # The root directory containing the sample-sets. Example: "/media/" to look for samples on a USB stick / SD card
USE_SERIALPORT_MIDI = False # Set to True to enable MIDI IN via SerialPort (e.g. RaspberryPi's GPIO UART pins)
USE_I2C_7SEGMENTDISPLAY = False # Set to True to use a 7-segment display via I2C
USE_HD44780DISPLAY = False # Set to True to use HD44780 display
USE_BUTTONS = False # Set to True to use momentary buttons (connected to RaspberryPi's GPIO pins) to change preset
MAX_POLYPHONY = 80 # This can be set higher, but 80 is a safe value
LOCAL_CONFIG = 'local_config.py' # Local config filename
DEBUG = False # Enable to switch verbose logging on

# Load local config if available
import os.path
if os.path.isfile(LOCAL_CONFIG):
execfile(LOCAL_CONFIG)

#########################################
# IMPORT
Expand All @@ -37,8 +43,8 @@
from chunk import Chunk
import struct
import rtmidi_python as rtmidi
import samplerbox_audio

import samplerbox_audio # legacy audio (pre RPi-2 models)
#import samplerbox_audio_neon as samplerbox_audio # ARM NEON instruction set

#########################################
# SLIGHT MODIFICATION OF PYTHON'S WAVE MODULE
Expand Down Expand Up @@ -105,12 +111,13 @@ def getloops(self):

class PlayingSound:

def __init__(self, sound, note):
def __init__(self, sound, note, velocity):
self.sound = sound
self.pos = 0
self.fadeoutpos = 0
self.isfadeout = False
self.note = note
self.velocity = velocity

def fadeout(self, i):
self.isfadeout = True
Expand Down Expand Up @@ -140,8 +147,9 @@ def __init__(self, filename, midinote, velocity):

wf.close()

def play(self, note):
snd = PlayingSound(self, note)
def play(self, note, velocity):
actual_velocity = 1-globalvelocitysensitivity + (globalvelocitysensitivity * (velocity/127.0))
snd = PlayingSound(self, note, actual_velocity)
playingsounds.append(snd)
return snd

Expand Down Expand Up @@ -178,7 +186,7 @@ def AudioCallback(outdata, frame_count, time_info, status):
global playingsounds
rmlist = []
playingsounds = playingsounds[-MAX_POLYPHONY:]
b = samplerbox_audio.mixaudiobuffers(playingsounds, rmlist, frame_count, FADEOUT, FADEOUTLENGTH, SPEED)
b = samplerbox_audio.mixaudiobuffers(playingsounds, rmlist, frame_count, FADEOUT, FADEOUTLENGTH, SPEED, globalvolume)
for e in rmlist:
try:
playingsounds.remove(e)
Expand All @@ -202,7 +210,7 @@ def MidiCallback(message, time_stamp):
if messagetype == 9: # Note on
midinote += globaltranspose
try:
playingnotes.setdefault(midinote, []).append(samples[midinote, velocity].play(midinote))
playingnotes.setdefault(midinote, []).append(samples[midinote, velocity].play(midinote, velocity))
except:
pass

Expand Down Expand Up @@ -262,10 +270,12 @@ def ActuallyLoad():
global samples
global playingsounds
global globalvolume, globaltranspose
global globalvelocitysensitivity
playingsounds = []
samples = {}
globalvolume = 10 ** (-12.0/20) # -12dB default global volume
globaltranspose = 0
globalvelocitysensitivity = 0 # default midi velocity sensitivity

samplesdir = SAMPLES_DIR if os.listdir(SAMPLES_DIR) else '.' # use current folder (containing 0 Saw) if no user media containing samples has been found

Expand All @@ -274,10 +284,10 @@ def ActuallyLoad():
dirname = os.path.join(samplesdir, basename)
if not basename:
print 'Preset empty: %s' % preset
display("E%03d" % preset)
display("E%03d" % preset, [str(preset), 'EMPTY'])
return
print 'Preset loading: %s (%s)' % (preset, basename)
display("L%03d" % preset)
display("L%03d" % preset, [basename, 'Loading'])

definitionfname = os.path.join(dirname, "definition.txt")
if os.path.isfile(definitionfname):
Expand All @@ -290,6 +300,9 @@ def ActuallyLoad():
if r'%%transpose' in pattern:
globaltranspose = int(pattern.split('=')[1].strip())
continue
if r'%%velocitysensitivity' in pattern:
globalvelocitysensitivity = float(pattern.split('=')[1].strip())
continue
defaultparams = {'midinote': '0', 'velocity': '127', 'notename': ''}
if len(pattern.split(',')) > 1:
defaultparams.update(dict([item.split('=') for item in pattern.split(',', 1)[1].replace(' ', '').replace('%', '').split(',')]))
Expand Down Expand Up @@ -339,10 +352,10 @@ def ActuallyLoad():
pass
if len(initial_keys) > 0:
print 'Preset loaded: ' + str(preset)
display("%04d" % preset)
display("%04d" % preset, [basename, 'Ready'])
else:
print 'Preset empty: ' + str(preset)
display("E%03d" % preset)
display("E%03d" % preset, [str(preset), 'EMPTY'])


#########################################
Expand Down Expand Up @@ -371,19 +384,19 @@ def ActuallyLoad():

def Buttons():
GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(26, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(16, GPIO.IN, pull_up_down=GPIO.PUD_UP)
global preset, lastbuttontime
while True:
now = time.time()
if not GPIO.input(18) and (now - lastbuttontime) > 0.2:
if not GPIO.input(26) and (now - lastbuttontime) > 0.2:
lastbuttontime = now
preset -= 1
if preset < 0:
preset = 127
LoadSamples()

elif not GPIO.input(17) and (now - lastbuttontime) > 0.2:
elif not GPIO.input(16) and (now - lastbuttontime) > 0.2:
lastbuttontime = now
preset += 1
if preset > 127:
Expand All @@ -407,7 +420,7 @@ def Buttons():

bus = smbus.SMBus(1) # using I2C

def display(s):
def display(s, lines):
for k in '\x76\x79\x00' + s: # position cursor at 0
try:
bus.write_byte(0x71, ord(k))
Expand All @@ -421,9 +434,17 @@ def display(s):
display('----')
time.sleep(0.5)

if USE_HD44780DISPLAY:
execfile('peripherals/hd44780.py')
lcd_init()

def display(s, lines):
lcd_string(lines[0], LCD_LINE_1)
lcd_string(lines[1], LCD_LINE_2)

else:

def display(s):
def display(s, l):
pass


Expand All @@ -443,6 +464,7 @@ def MidiSerialCallback():
i = 0
while i < 3:
data = ord(ser.read(1)) # read a byte
print data
if data >> 7 != 0:
i = 0 # status byte! this is the beginning of a midi message: http://www.midi.org/techspecs/midimessages.php
message[i] = data
Expand Down