Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ SamplerBox works with the RaspberryPi's built-in soundcard, but it is recommende

You can use a ready-to-use ISO image from the [Releases](https://github.com/josephernest/SamplerBox/releases) page or do a manual install:

0. Start with a standard RaspiOS intsall. The following steps have been tested with [2021-05-07-raspios-buster-armhf-lite.zip](https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2021-05-28/2021-05-07-raspios-buster-armhf-lite.zip).
0. Start with a standard RaspiOS intsall. The following steps have been tested with ADD NEW VERSION HERE!!! [2021-05-07-raspios-buster-armhf-lite.zip](https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2021-05-28/2021-05-07-raspios-buster-armhf-lite.zip).

1. Install the required dependencies (Python-related packages and audio libraries - the current version requires at least Python 3.7):
1. Install the required dependencies (Python-related packages and audio libraries - the current version required specifically Python 3.7 as rtmidi-python uses code which was removed in python 3.8):

~~~
sudo apt update
sudo apt -y install git python3-pip python3-smbus python3-numpy libportaudio2
sudo apt -y install git python3-pip python3-smbus python3-numpy libportaudio2 libasound2-dev
sudo apt -y install raspberrypi-kernel # quite long to install, do it only if necessary, it solves a "no sound before 25 second on boot" problem
sudo pip3 install cython rtmidi-python cffi sounddevice pyserial
sudo pip3 install cython python-rtmidi cffi sounddevice pyserial
~~~

2. Download SamplerBox and build it with:
Expand Down
46 changes: 30 additions & 16 deletions samplerbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import threading
from chunk import Chunk
import struct
import rtmidi_python as rtmidi
import rtmidi
import samplerbox_audio

#########################################
Expand Down Expand Up @@ -165,23 +165,31 @@ def AudioCallback(outdata, frame_count, time_info, status):
b *= globalvolume
outdata[:] = b.reshape(outdata.shape)

def MidiCallback(message, time_stamp):
def MidiCallback(message_t, time_stamp):
global playingnotes, sustain, sustainplayingnotes
global preset
# for details on MIDI Protocol see https://www.midi.org/specifications-old/item/table-1-summary-of-midi-message

message = message_t[0]
# split status bits (bin, 0b0000-0b1111) and MIDI channel number (dec, 1-16)
messagetype = message[0] >> 4
messagechannel = (message[0] & 15) + 1
messagechannel = (message[0] & 0b00001111) + 1

# read note and velocity depending on message length
note = message[1] if len(message) > 1 else None
midinote = note
velocity = message[2] if len(message) > 2 else None

# interpret messagetype
if messagetype == 9 and velocity == 0:
messagetype = 8
if messagetype == 9: # Note on
if messagetype == 9: # 0b1001: Note on
midinote += globaltranspose
try:
playingnotes.setdefault(midinote, []).append(samples[midinote, velocity].play(midinote))
except:
pass
elif messagetype == 8: # Note off
elif messagetype == 8: # 0b1000: Note off
midinote += globaltranspose
if midinote in playingnotes:
for n in playingnotes[midinote]:
Expand All @@ -190,16 +198,16 @@ def MidiCallback(message, time_stamp):
else:
n.fadeout(50)
playingnotes[midinote] = []
elif messagetype == 12: # Program change
elif messagetype == 12: # 0b1100: Program change
print('Program change ' + str(note))
preset = note
LoadSamples()
elif (messagetype == 11) and (note == 64) and (velocity < 64): # sustain pedal off
elif (messagetype == 11) and (note == 64) and (velocity < 64): # 0b1011: sustain pedal off
for n in sustainplayingnotes:
n.fadeout(50)
sustainplayingnotes = []
sustain = False
elif (messagetype == 11) and (note == 64) and (velocity >= 64): # sustain pedal on
elif (messagetype == 11) and (note == 64) and (velocity >= 64): # 0b1011: sustain pedal on
sustain = True

#########################################
Expand Down Expand Up @@ -424,14 +432,20 @@ def MidiSerialCallback():
# MAIN LOOP
#########################################

midi_in = [rtmidi.MidiIn(b'in')]
previous = []
midi_in = [rtmidi.MidiIn(name=b'rtmidi in')]

if midi_in[0].get_current_api() == rtmidi.API_LINUX_ALSA:
print("Using ALSA API for MIDI input.")

# collect all available device port numbers
previousPorts = []

while True:
for port in midi_in[0].ports:
if port not in previous and b'Midi Through' not in port:
midi_in.append(rtmidi.MidiIn(b'in'))
midi_in[-1].callback = MidiCallback
for port, name in enumerate(midi_in[0].get_ports()):
if port not in previousPorts and "Midi Through" not in name:
midi_in.append(rtmidi.MidiIn(name=b'rtmidi in'))
midi_in[-1].set_callback(MidiCallback)
midi_in[-1].open_port(port)
print('Opened MIDI: ' + str(port))
previous = midi_in[0].ports
print('Opened MIDI ' + str(name) + ' on port ' + str(port))
previousPorts = [*range(midi_in[0].get_port_count())]
time.sleep(2)