From 29d3969b75947bf7e86da1a9cbd8c8edb6d16210 Mon Sep 17 00:00:00 2001 From: Paul A Date: Mon, 1 Feb 2016 21:41:40 +0000 Subject: [PATCH 01/13] Velocity sensitivity support and a Makefile --- .gitignore | 3 +++ 0 Saw/definition.txt | 2 ++ Makefile | 2 ++ 3 files changed, 7 insertions(+) create mode 100644 .gitignore create mode 100644 0 Saw/definition.txt create mode 100644 Makefile diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..52378e5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +samplerbox_audio.c +samplerbox_audio.so diff --git a/0 Saw/definition.txt b/0 Saw/definition.txt new file mode 100644 index 0000000..814d501 --- /dev/null +++ b/0 Saw/definition.txt @@ -0,0 +1,2 @@ +%midinote.wav +%%velocitysensitivity=1 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..51e3b10 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +samplerbox_audio.so: samplerbox_audio.pyx + sudo python setup.py build_ext --inplace From 9769fa4efaa310d7bd54e4ac009f50dff8f787ed Mon Sep 17 00:00:00 2001 From: Paul A Date: Mon, 1 Feb 2016 21:42:38 +0000 Subject: [PATCH 02/13] Velocity sensitivity support and build instructions simplification --- README.md | 2 +- samplerbox.py | 21 +++++++++++++++------ samplerbox_audio.pyx | 9 +++++++-- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 994c3a0..1e44d49 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ You need a RaspberryPi and a DAC (such as [this 6€ one](http://www.ebay.fr/itm ~~~ git clone https://github.com/josephernest/SamplerBox.git ; - cd SamplerBox ; sudo python setup.py build_ext --inplace + cd SamplerBox ; make ~~~ 3. Run the soft with `python samplerbox.py`. diff --git a/samplerbox.py b/samplerbox.py index 2130d4d..76a0c84 100644 --- a/samplerbox.py +++ b/samplerbox.py @@ -15,10 +15,10 @@ ######################################### 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 +SAMPLES_DIR = "/media/pi/Transcend" # 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_BUTTONS = False # Set to True to use momentary buttons (connected to RaspberryPi's GPIO pins) to change preset +USE_BUTTONS = True # 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 @@ -105,12 +105,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 @@ -140,8 +141,11 @@ 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)) + if actual_velocity > 1: + actual_velocity = 1 # safety check for out-of-bounds input velocity or sensitivity + snd = PlayingSound(self, note, actual_velocity) playingsounds.append(snd) return snd @@ -204,7 +208,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 @@ -264,10 +268,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 basename = next((f for f in os.listdir(SAMPLES_DIR) if f.startswith("%d " % preset)), None) # or next(glob.iglob("blah*"), None) if basename: @@ -290,6 +296,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(',')])) diff --git a/samplerbox_audio.pyx b/samplerbox_audio.pyx index d6725a9..39c9b36 100644 --- a/samplerbox_audio.pyx +++ b/samplerbox_audio.pyx @@ -16,7 +16,7 @@ cimport numpy def mixaudiobuffers(list playingsounds, list rmlist, int frame_count, numpy.ndarray FADEOUT, int FADEOUTLENGTH, numpy.ndarray SPEED): cdef int i, ii, k, l, N, length, looppos, fadeoutpos - cdef float speed, newsz, pos, j + cdef float speed, newsz, pos, j, velocity cdef numpy.ndarray b = numpy.zeros(2 * frame_count, numpy.float32) # output buffer cdef float* bb = (b.data) # and its pointer cdef numpy.ndarray z @@ -26,6 +26,7 @@ def mixaudiobuffers(list playingsounds, list rmlist, int frame_count, numpy.ndar for snd in playingsounds: pos = snd.pos fadeoutpos = snd.fadeoutpos + velocity = snd.velocity looppos = snd.sound.loop length = snd.sound.nframes speed = SPEED[snd.note - snd.sound.midinote] @@ -72,6 +73,10 @@ def mixaudiobuffers(list playingsounds, list rmlist, int frame_count, numpy.ndar bb[2 * i] += zz[2 * k] + (j - k) * (zz[2 * k + 2] - zz[2 * k]) # linear interpolation bb[2 * i + 1] += zz[2 * k + 1] + (j - k) * (zz[2 * k + 3] - zz[2 * k + 1]) + if velocity < 1: + for i in range(N*2): + bb[i] *= velocity + snd.pos += ii * speed return b @@ -83,4 +88,4 @@ def binary24_to_int16(char *data, int length): for i in range(length): b[2*i] = data[3*i+1] b[2*i+1] = data[3*i+2] - return res \ No newline at end of file + return res From 88fffe55c69ede7610ca3f1972107e0a33a6cf7d Mon Sep 17 00:00:00 2001 From: Paul T Date: Mon, 1 Feb 2016 22:49:45 +0000 Subject: [PATCH 03/13] Configuration loading improvements --- .gitignore | 1 + Makefile | 2 +- samplerbox.py | 11 +++++++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 52378e5..d6162f0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build/ samplerbox_audio.c samplerbox_audio.so +local_config.* diff --git a/Makefile b/Makefile index 51e3b10..b8d24f2 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,2 @@ samplerbox_audio.so: samplerbox_audio.pyx - sudo python setup.py build_ext --inplace + python setup.py build_ext --inplace diff --git a/samplerbox.py b/samplerbox.py index 76a0c84..c9f0da7 100644 --- a/samplerbox.py +++ b/samplerbox.py @@ -10,17 +10,22 @@ ######################################### -# LOCAL # CONFIG ######################################### AUDIO_DEVICE_ID = 2 # change this number to use another soundcard -SAMPLES_DIR = "/media/pi/Transcend" # The root directory containing the sample-sets. Example: "/media/" to look for samples on a USB stick / SD card +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_BUTTONS = True # 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 @@ -145,6 +150,8 @@ def play(self, note, velocity): actual_velocity = 1-globalvelocitysensitivity + (globalvelocitysensitivity * (velocity/127.0)) if actual_velocity > 1: actual_velocity = 1 # safety check for out-of-bounds input velocity or sensitivity + if DEBUG: + print 'MIDI vel %d actual vel %f' % (velocity, actual_velocity) snd = PlayingSound(self, note, actual_velocity) playingsounds.append(snd) return snd From 33d6de38c9476942b2b5c8d760de117c6af08491 Mon Sep 17 00:00:00 2001 From: Paul T Date: Tue, 2 Feb 2016 02:35:55 +0000 Subject: [PATCH 04/13] Remove unneccessary changes --- samplerbox.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/samplerbox.py b/samplerbox.py index c9f0da7..6cbcde2 100644 --- a/samplerbox.py +++ b/samplerbox.py @@ -17,7 +17,7 @@ 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_BUTTONS = True # Set to True to use momentary buttons (connected to RaspberryPi's GPIO pins) to change preset +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 @@ -148,10 +148,6 @@ def __init__(self, filename, midinote, velocity): def play(self, note, velocity): actual_velocity = 1-globalvelocitysensitivity + (globalvelocitysensitivity * (velocity/127.0)) - if actual_velocity > 1: - actual_velocity = 1 # safety check for out-of-bounds input velocity or sensitivity - if DEBUG: - print 'MIDI vel %d actual vel %f' % (velocity, actual_velocity) snd = PlayingSound(self, note, actual_velocity) playingsounds.append(snd) return snd From 2c4f48b5d944bb19d28a43a395eedf0d489c40ee Mon Sep 17 00:00:00 2001 From: Paul T Date: Tue, 2 Feb 2016 03:39:41 +0000 Subject: [PATCH 05/13] FPU performance optimisations --- samplerbox.py | 7 +++-- samplerbox_audio.pyx | 61 +++++++++++++++++--------------------------- 2 files changed, 27 insertions(+), 41 deletions(-) diff --git a/samplerbox.py b/samplerbox.py index 6cbcde2..414d48e 100644 --- a/samplerbox.py +++ b/samplerbox.py @@ -18,7 +18,7 @@ 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_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 +MAX_POLYPHONY = 50 # 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 @@ -185,13 +185,12 @@ def AudioCallback(in_data, 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) except: pass - b *= globalvolume odata = (b.astype(numpy.int16)).tostring() return (odata, pyaudio.paContinue) @@ -364,7 +363,7 @@ def ActuallyLoad(): p = pyaudio.PyAudio() try: - stream = p.open(format=pyaudio.paInt16, channels=2, rate=44100, frames_per_buffer=512, output=True, + stream = p.open(format=pyaudio.paInt16, channels=2, rate=44100, frames_per_buffer=256, output=True, input=False, output_device_index=AUDIO_DEVICE_ID, stream_callback=AudioCallback) print 'Opened audio: ' + p.get_device_info_by_index(AUDIO_DEVICE_ID)['name'] except: diff --git a/samplerbox_audio.pyx b/samplerbox_audio.pyx index 39c9b36..81bdda6 100644 --- a/samplerbox_audio.pyx +++ b/samplerbox_audio.pyx @@ -14,19 +14,20 @@ import cython import numpy cimport numpy -def mixaudiobuffers(list playingsounds, list rmlist, int frame_count, numpy.ndarray FADEOUT, int FADEOUTLENGTH, numpy.ndarray SPEED): +def mixaudiobuffers(list playingsounds, list rmlist, int frame_count, numpy.ndarray FADEOUT, int FADEOUTLENGTH, numpy.ndarray SPEED, double GLOBALVOLUME): cdef int i, ii, k, l, N, length, looppos, fadeoutpos cdef float speed, newsz, pos, j, velocity - cdef numpy.ndarray b = numpy.zeros(2 * frame_count, numpy.float32) # output buffer - cdef float* bb = (b.data) # and its pointer + cdef numpy.ndarray b = numpy.zeros(2 * frame_count, numpy.float64) # output buffer + cdef double* bb = (b.data) # and its pointer cdef numpy.ndarray z cdef short* zz cdef float* fadeout = (FADEOUT.data) + cdef double multiplier for snd in playingsounds: pos = snd.pos fadeoutpos = snd.fadeoutpos - velocity = snd.velocity + velocity = snd.velocity * GLOBALVOLUME looppos = snd.sound.loop length = snd.sound.nframes speed = SPEED[snd.note - snd.sound.midinote] @@ -40,42 +41,28 @@ def mixaudiobuffers(list playingsounds, list rmlist, int frame_count, numpy.ndar rmlist.append(snd) N = ((length - 4 - pos) / speed) - if snd.isfadeout: - if fadeoutpos > FADEOUTLENGTH: - rmlist.append(snd) - ii = 0 - for i in range(N): - j = pos + ii * speed - ii += 1 - k = j - if k > length - 2: - pos = looppos + 1 - snd.pos = pos - ii = 0 - j = pos + ii * speed - k = j - bb[2 * i] += (zz[2 * k] + (j - k) * (zz[2 * k + 2] - zz[2 * k])) * fadeout[fadeoutpos + i] # linear interpolation - bb[2 * i + 1] += (zz[2 * k + 1] + (j - k) * (zz[2 * k + 3] - zz[2 * k + 1])) * fadeout[fadeoutpos + i] - snd.fadeoutpos += i + multiplier = velocity - else: - ii = 0 - for i in range(N): - j = pos + ii * speed - ii += 1 + ii = 0 + for i in range(N): + j = pos + ii * speed + ii += 1 + k = j + if k > length - 2: + pos = looppos + 1 + snd.pos = pos + ii = 0 + j = pos + ii * speed k = j - if k > length - 2: - pos = looppos + 1 - snd.pos = pos - ii = 0 - j = pos + ii * speed - k = j - bb[2 * i] += zz[2 * k] + (j - k) * (zz[2 * k + 2] - zz[2 * k]) # linear interpolation - bb[2 * i + 1] += zz[2 * k + 1] + (j - k) * (zz[2 * k + 3] - zz[2 * k + 1]) + if snd.isfadeout: + multiplier = velocity * fadeout[fadeoutpos + i] + bb[2 * i] += (zz[2 * k] + (j - k) * (zz[2 * k + 2] - zz[2 * k])) * multiplier # linear interpolation + bb[2 * i + 1] += (zz[2 * k + 1] + (j - k) * (zz[2 * k + 3] - zz[2 * k + 1])) * multiplier - if velocity < 1: - for i in range(N*2): - bb[i] *= velocity + if snd.isfadeout: + if fadeoutpos > FADEOUTLENGTH: + rmlist.append(snd) + snd.fadeoutpos += N snd.pos += ii * speed From 19d37434ba0607f941ffba9fe068f1da4464dbe4 Mon Sep 17 00:00:00 2001 From: Paul T Date: Tue, 2 Feb 2016 16:19:33 +0000 Subject: [PATCH 06/13] Neon instructions --- samplerbox.py | 2 +- samplerbox_audio.pyx | 42 +++++++++++++++++++++++++++++++++++------- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/samplerbox.py b/samplerbox.py index 414d48e..69e8157 100644 --- a/samplerbox.py +++ b/samplerbox.py @@ -363,7 +363,7 @@ def ActuallyLoad(): p = pyaudio.PyAudio() try: - stream = p.open(format=pyaudio.paInt16, channels=2, rate=44100, frames_per_buffer=256, output=True, + stream = p.open(format=pyaudio.paInt16, channels=2, rate=44100, frames_per_buffer=512, output=True, input=False, output_device_index=AUDIO_DEVICE_ID, stream_callback=AudioCallback) print 'Opened audio: ' + p.get_device_info_by_index(AUDIO_DEVICE_ID)['name'] except: diff --git a/samplerbox_audio.pyx b/samplerbox_audio.pyx index 81bdda6..4b5f2c3 100644 --- a/samplerbox_audio.pyx +++ b/samplerbox_audio.pyx @@ -14,15 +14,26 @@ import cython import numpy cimport numpy +cdef extern from "arm_neon.h": + ctypedef float float32_t + ctypedef float float32x4_t + + float32x4_t vld1q_f32(float32_t* ptr) + void vst1q_f32(float32_t *ptr, float32x4_t val) + float32x4_t vmulq_n_f32(float32x4_t a, float32_t b) + def mixaudiobuffers(list playingsounds, list rmlist, int frame_count, numpy.ndarray FADEOUT, int FADEOUTLENGTH, numpy.ndarray SPEED, double GLOBALVOLUME): - cdef int i, ii, k, l, N, length, looppos, fadeoutpos + cdef int i, ii, k, l, N, length, looppos, fadeoutpos, mptr cdef float speed, newsz, pos, j, velocity - cdef numpy.ndarray b = numpy.zeros(2 * frame_count, numpy.float64) # output buffer - cdef double* bb = (b.data) # and its pointer + cdef numpy.ndarray b = numpy.zeros(2 * frame_count, numpy.float32) # output buffer + cdef float* bb = (b.data) # and its pointer cdef numpy.ndarray z cdef short* zz + cdef float32_t sample[4] + cdef float32x4_t sample_vector cdef float* fadeout = (FADEOUT.data) cdef double multiplier + cdef char is_fadeout for snd in playingsounds: pos = snd.pos @@ -30,6 +41,10 @@ def mixaudiobuffers(list playingsounds, list rmlist, int frame_count, numpy.ndar velocity = snd.velocity * GLOBALVOLUME looppos = snd.sound.loop length = snd.sound.nframes + if snd.isfadeout: + is_fadeout = 1 + else: + is_fadeout = 0 speed = SPEED[snd.note - snd.sound.midinote] newsz = frame_count * speed z = snd.sound.data @@ -54,12 +69,25 @@ def mixaudiobuffers(list playingsounds, list rmlist, int frame_count, numpy.ndar ii = 0 j = pos + ii * speed k = j - if snd.isfadeout: + if is_fadeout == 1: multiplier = velocity * fadeout[fadeoutpos + i] - bb[2 * i] += (zz[2 * k] + (j - k) * (zz[2 * k + 2] - zz[2 * k])) * multiplier # linear interpolation - bb[2 * i + 1] += (zz[2 * k + 1] + (j - k) * (zz[2 * k + 3] - zz[2 * k + 1])) * multiplier - if snd.isfadeout: + j -= k + + # vectorisation optimisations + mptr = 2 * k + sample[0] = zz[mptr] + sample[1] = zz[mptr + 1] + sample[2] = zz[mptr + 2] + sample[3] = zz[mptr + 3] + sample_vector = vld1q_f32(sample) + sample_vector = vmulq_n_f32(sample_vector, multiplier) + vst1q_f32(sample, sample_vector) + + bb[2 * i] += sample[0] + j * (sample[2] - sample[0]) # linear interpolation + bb[2 * i + 1] += sample[1] + j * (sample[3] - sample[1]) + + if is_fadeout == 1: if fadeoutpos > FADEOUTLENGTH: rmlist.append(snd) snd.fadeoutpos += N From 42c55bbd079a46b9a2670a94ca1dc69b1e2bd99e Mon Sep 17 00:00:00 2001 From: Paul T Date: Tue, 2 Feb 2016 23:17:13 +0000 Subject: [PATCH 07/13] Full NEON instruction set support; option to switch between legacy FPU and NEON --- .gitignore | 4 +- Makefile | 3 +- samplerbox.py | 9 +- samplerbox_audio.pyx | 40 ++------- samplerbox_audio_neon.pyx | 178 ++++++++++++++++++++++++++++++++++++++ setup.py | 8 +- 6 files changed, 200 insertions(+), 42 deletions(-) create mode 100644 samplerbox_audio_neon.pyx diff --git a/.gitignore b/.gitignore index d6162f0..e9e721f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ build/ -samplerbox_audio.c -samplerbox_audio.so +*.c +*.so local_config.* diff --git a/Makefile b/Makefile index b8d24f2..3126bc9 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,3 @@ -samplerbox_audio.so: samplerbox_audio.pyx +samplerbox_audio.so: export CFLAGS=-mcpu=cortex-a7 -mtune=arm1176jzf-s -mfloat-abi=hard -mfpu=neon-vfpv4 -ftree-vectorize -ffast-math -O3 +samplerbox_audio.so: samplerbox_audio.pyx samplerbox_audio_neon.pyx python setup.py build_ext --inplace diff --git a/samplerbox.py b/samplerbox.py index 69e8157..03fa944 100644 --- a/samplerbox.py +++ b/samplerbox.py @@ -18,7 +18,7 @@ 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_BUTTONS = False # Set to True to use momentary buttons (connected to RaspberryPi's GPIO pins) to change preset -MAX_POLYPHONY = 50 # This can be set higher, but 80 is a safe value +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 @@ -42,8 +42,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 @@ -191,7 +191,8 @@ def AudioCallback(in_data, frame_count, time_info, status): playingsounds.remove(e) except: pass - odata = (b.astype(numpy.int16)).tostring() +# odata = (b.astype(numpy.int16)).tostring() + odata = b.tostring() return (odata, pyaudio.paContinue) diff --git a/samplerbox_audio.pyx b/samplerbox_audio.pyx index 4b5f2c3..7c826b4 100644 --- a/samplerbox_audio.pyx +++ b/samplerbox_audio.pyx @@ -14,26 +14,15 @@ import cython import numpy cimport numpy -cdef extern from "arm_neon.h": - ctypedef float float32_t - ctypedef float float32x4_t - - float32x4_t vld1q_f32(float32_t* ptr) - void vst1q_f32(float32_t *ptr, float32x4_t val) - float32x4_t vmulq_n_f32(float32x4_t a, float32_t b) - def mixaudiobuffers(list playingsounds, list rmlist, int frame_count, numpy.ndarray FADEOUT, int FADEOUTLENGTH, numpy.ndarray SPEED, double GLOBALVOLUME): - cdef int i, ii, k, l, N, length, looppos, fadeoutpos, mptr + cdef int i, ii, k, l, N, length, looppos, fadeoutpos cdef float speed, newsz, pos, j, velocity cdef numpy.ndarray b = numpy.zeros(2 * frame_count, numpy.float32) # output buffer cdef float* bb = (b.data) # and its pointer cdef numpy.ndarray z cdef short* zz - cdef float32_t sample[4] - cdef float32x4_t sample_vector cdef float* fadeout = (FADEOUT.data) cdef double multiplier - cdef char is_fadeout for snd in playingsounds: pos = snd.pos @@ -41,10 +30,6 @@ def mixaudiobuffers(list playingsounds, list rmlist, int frame_count, numpy.ndar velocity = snd.velocity * GLOBALVOLUME looppos = snd.sound.loop length = snd.sound.nframes - if snd.isfadeout: - is_fadeout = 1 - else: - is_fadeout = 0 speed = SPEED[snd.note - snd.sound.midinote] newsz = frame_count * speed z = snd.sound.data @@ -69,32 +54,19 @@ def mixaudiobuffers(list playingsounds, list rmlist, int frame_count, numpy.ndar ii = 0 j = pos + ii * speed k = j - if is_fadeout == 1: + if snd.isfadeout: multiplier = velocity * fadeout[fadeoutpos + i] + bb[2 * i] += (zz[2 * k] + (j - k) * (zz[2 * k + 2] - zz[2 * k])) * multiplier # linear interpolation + bb[2 * i + 1] += (zz[2 * k + 1] + (j - k) * (zz[2 * k + 3] - zz[2 * k + 1])) * multiplier - j -= k - - # vectorisation optimisations - mptr = 2 * k - sample[0] = zz[mptr] - sample[1] = zz[mptr + 1] - sample[2] = zz[mptr + 2] - sample[3] = zz[mptr + 3] - sample_vector = vld1q_f32(sample) - sample_vector = vmulq_n_f32(sample_vector, multiplier) - vst1q_f32(sample, sample_vector) - - bb[2 * i] += sample[0] + j * (sample[2] - sample[0]) # linear interpolation - bb[2 * i + 1] += sample[1] + j * (sample[3] - sample[1]) - - if is_fadeout == 1: + if snd.isfadeout: if fadeoutpos > FADEOUTLENGTH: rmlist.append(snd) snd.fadeoutpos += N snd.pos += ii * speed - return b + return b.astype(numpy.int16) def binary24_to_int16(char *data, int length): cdef int i diff --git a/samplerbox_audio_neon.pyx b/samplerbox_audio_neon.pyx new file mode 100644 index 0000000..d3a30f4 --- /dev/null +++ b/samplerbox_audio_neon.pyx @@ -0,0 +1,178 @@ +# +# SamplerBox +# +# author: Joseph Ernest (twitter: @JosephErnest, mail: contact@samplerbox.org) +# P.T. (NEON instruction set) +# url: http://www.samplerbox.org/ +# license: Creative Commons ShareAlike 3.0 (http://creativecommons.org/licenses/by-sa/3.0/) +# +# samplerbox_audio.pyx: Audio engine (Cython) +# + + + +import cython +import numpy +cimport numpy + +cdef extern from "arm_neon.h": + # "c type" here is effectively ignored by Cython + ctypedef short int16_t + ctypedef short int16x4_t + ctypedef short int16x8_t + ctypedef long int32x2_t + ctypedef long int32x4_t + ctypedef float float32_t + ctypedef float float32x2_t + ctypedef float float32x4_t + + float32x4_t vld1q_f32(float32_t* ptr) + void vst1_f32(float32_t *ptr, float32x2_t val) + void vst1q_f32(float32_t *ptr, float32x4_t val) + int16x4_t vld1_s16(int16_t* ptr) + int16x8_t vld1q_s16(int16_t* ptr) + void vst1q_s16(int16_t *ptr, int16x8_t val) + + float32x4_t vdupq_n_f32(float32_t value) + + int16x4_t vaddq_s16(int16x8_t a, int16x8_t b) + float32x2_t vadd_f32(float32x2_t a, float32x2_t b) + float32x4_t vaddq_f32(float32x4_t a, float32x4_t b) + float32x2_t vsub_f32(float32x2_t a, float32x2_t b) + float32x2_t vmul_n_f32(float32x2_t a, float32_t b) + float32x4_t vmulq_n_f32(float32x4_t a, float32_t b) + float32x2_t vget_high_f32(float32x4_t a) + float32x2_t vget_low_f32(float32x4_t a) + int16x8_t vcombine_s16(int16x4_t a, int16x4_t b) + int32x4_t vcombine_s32(int32x2_t a, int32x2_t b) + + int16x4_t vmovn_s32(int32x4_t a) + int32x4_t vmovl_s16(int16x4_t a) + int32x2_t vcvt_s32_f32(float32x2_t a) + float32x4_t vcvtq_f32_s32(int32x4_t a) + +def mixaudiobuffers(list playingsounds, list rmlist, int frame_count, numpy.ndarray FADEOUT, int FADEOUTLENGTH, numpy.ndarray SPEED, double GLOBALVOLUME): + cdef int i, ii, k, l, N, length, looppos, fadeoutpos + cdef float speed, newsz, pos, j, velocity + + cdef int channel_size = 2 * frame_count + cdef numpy.ndarray b = numpy.require(numpy.zeros(channel_size, numpy.int16), requirements=['A', 'C']) # output buffer + cdef short* bb = (b.data) # and its pointer + + # input sample buffers and calculation register references + cdef numpy.ndarray z + cdef short* zz + cdef int16x4_t zz_sample + cdef int32x4_t zz_sample32 + cdef float32_t sample[2] + cdef float32x4_t sample_vector + cdef float32x2_t sample_pair_vector[2] + cdef int32x2_t master + cdef int16x4_t low_combined, high_combined + cdef int16x8_t buffer_vector, full_combined + + # velocity / ADSR / fadeout variables + cdef float* fadeout = (FADEOUT.data) + cdef double multiplier + cdef char is_fadeout + + sound_no = 0 + for snd in playingsounds: + pos = snd.pos + fadeoutpos = snd.fadeoutpos + velocity = snd.velocity * GLOBALVOLUME + looppos = snd.sound.loop + length = snd.sound.nframes + if snd.isfadeout: + is_fadeout = 1 + else: + is_fadeout = 0 + speed = SPEED[snd.note - snd.sound.midinote] + newsz = frame_count * speed + z = snd.sound.data + zz = (z.data) + + N = frame_count + + if (pos + frame_count * speed > length - 4) and (looppos == -1): + rmlist.append(snd) + N = ((length - 4 - pos) / speed) + + multiplier = velocity + + ii = 0 + for i in range(N): + j = pos + ii * speed + ii += 1 + k = j + if k > length - 2: + pos = looppos + 1 + snd.pos = pos + ii = 0 + j = pos + ii * speed + k = j + if is_fadeout == 1: + multiplier = velocity * fadeout[fadeoutpos + i] + + j -= k + + # vectorise samples at the pointer location + zz_sample = vld1_s16(&zz[2 * k]) + # convert to floating-point format + zz_sample32 = vmovl_s16(zz_sample) + sample_vector = vcvtq_f32_s32(zz_sample32) + + # pre-mix multiplier + sample_vector = vmulq_n_f32(sample_vector, multiplier) + + # linear interpolation + sample_pair_vector[0] = vget_low_f32(sample_vector) # sample0, sample1 + sample_pair_vector[1] = vget_high_f32(sample_vector) # sample2, sample3 + sample_pair_vector[1] = vsub_f32(sample_pair_vector[1], sample_pair_vector[0]) # s2-s0, s3-s1 + sample_pair_vector[1] = vmul_n_f32(sample_pair_vector[1], j) # j*(s2-s0), ... + sample_pair_vector[1] = vadd_f32(sample_pair_vector[1], sample_pair_vector[0]) # s0 + j*(s2-s0), ... + + master = vcvt_s32_f32(sample_pair_vector[1]) + if i % 2 == 0: + previous_master = master + else: + if i % 4 == 1: + low_combined = vmovn_s32(vcombine_s32(previous_master, master)) + else: # i % 3 == 3 + high_combined = vmovn_s32(vcombine_s32(previous_master, master)) + full_combined = vcombine_s16(low_combined, high_combined) + buffer_vector = vld1q_s16(&bb[i * 2 - 6]) # load buffer at the position + buffer_vector = vaddq_s16(buffer_vector, full_combined) # add calculated samples to the buffer + vst1q_s16(&bb[i * 2 - 6], buffer_vector) + + if is_fadeout == 1: + if fadeoutpos > FADEOUTLENGTH: + rmlist.append(snd) + snd.fadeoutpos += N + + snd.pos += ii * speed + sound_no += 1 + + return b + +# Calculate how many mixing channels are needed +# +# Mixer mixes two channels at a time (2 x 32bit float frame x 2 channels = 128bit) +# so the result always have to be a multiply of two. +# +# Extra 8 channels are added to prevent race condition if more sounds are triggered since +# mixing buffer has been allocated. +# +def mixing_channels(int sounds): + if sounds % 2 > 0: + sounds += 1 + return sounds + 8 + +def binary24_to_int16(char *data, int length): + cdef int i + res = numpy.zeros(length, numpy.int16) + b = ((res).data) + for i in range(length): + b[2*i] = data[3*i+1] + b[2*i+1] = data[3*i+2] + return res diff --git a/setup.py b/setup.py index c329224..71cd27d 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,11 @@ from distutils.core import setup from Cython.Build import cythonize +from distutils.extension import Extension import numpy -setup(ext_modules = cythonize("samplerbox_audio.pyx"), include_dirs=[numpy.get_include()]) \ No newline at end of file +extensions = [ + Extension("samplerbox_audio", ["samplerbox_audio.pyx"]), + Extension("samplerbox_audio_neon", ["samplerbox_audio_neon.pyx"]), +] + +setup(ext_modules = cythonize(extensions), include_dirs=[numpy.get_include()]) From 59b02465a7752fa55602b08be10db8e658ec728a Mon Sep 17 00:00:00 2001 From: Paul T Date: Fri, 5 Feb 2016 04:53:51 +0000 Subject: [PATCH 08/13] HD44780 display support --- peripherals/hd44780.py | 126 +++++++++++++++++++++++++++++++++++++++++ samplerbox.py | 19 +++++-- 2 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 peripherals/hd44780.py diff --git a/peripherals/hd44780.py b/peripherals/hd44780.py new file mode 100644 index 0000000..966ec82 --- /dev/null +++ b/peripherals/hd44780.py @@ -0,0 +1,126 @@ +# 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 = 7 +LCD_E = 11 +LCD_D4 = 37 +LCD_D5 = 35 +LCD_D6 = 33 +LCD_D7 = 31 + + +# Define some device constants +LCD_WIDTH = 8 # 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.BOARD) # 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) diff --git a/samplerbox.py b/samplerbox.py index 2130d4d..52a21b9 100644 --- a/samplerbox.py +++ b/samplerbox.py @@ -18,6 +18,7 @@ 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 @@ -274,10 +275,10 @@ def ActuallyLoad(): dirname = os.path.join(SAMPLES_DIR, 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): @@ -339,10 +340,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']) ######################################### @@ -414,7 +415,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)) @@ -428,6 +429,14 @@ 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): From 68344f1ee0e01917e394a632de956da7cccdc68e Mon Sep 17 00:00:00 2001 From: Paul T Date: Sun, 7 Feb 2016 06:38:33 +0000 Subject: [PATCH 09/13] Change GCC settings to build legacy library correctly --- Makefile | 1 - setup.py | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 3126bc9..08338f8 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,2 @@ -samplerbox_audio.so: export CFLAGS=-mcpu=cortex-a7 -mtune=arm1176jzf-s -mfloat-abi=hard -mfpu=neon-vfpv4 -ftree-vectorize -ffast-math -O3 samplerbox_audio.so: samplerbox_audio.pyx samplerbox_audio_neon.pyx python setup.py build_ext --inplace diff --git a/setup.py b/setup.py index 71cd27d..396f191 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,8 @@ extensions = [ Extension("samplerbox_audio", ["samplerbox_audio.pyx"]), - Extension("samplerbox_audio_neon", ["samplerbox_audio_neon.pyx"]), + Extension("samplerbox_audio_neon", ["samplerbox_audio_neon.pyx"], + extra_compile_args=["-mcpu=cortex-a7", "-mtune=arm1176jzf-s", "-mfloat-abi=hard", "-mfpu=neon-vfpv4", "-ftree-vectorize", "-ffast-math", "-O3"]), ] setup(ext_modules = cythonize(extensions), include_dirs=[numpy.get_include()]) From 4972122ffd37abca8e687fa3bfe73270b4dc792a Mon Sep 17 00:00:00 2001 From: Paul T Date: Sun, 14 Feb 2016 18:16:54 +0000 Subject: [PATCH 10/13] Change buttons GPIO --- samplerbox.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/samplerbox.py b/samplerbox.py index 03fa944..ed160c9 100644 --- a/samplerbox.py +++ b/samplerbox.py @@ -42,8 +42,8 @@ from chunk import Chunk import struct import rtmidi_python as rtmidi -#import samplerbox_audio # legacy audio (pre RPi-2 models) -import samplerbox_audio_neon as samplerbox_audio # ARM NEON instruction set +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 @@ -390,19 +390,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(23, GPIO.IN, pull_up_down=GPIO.PUD_UP) + GPIO.setup(24, 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(23) 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(24) and (now - lastbuttontime) > 0.2: lastbuttontime = now preset += 1 if preset > 127: From e9edcaf48f11d6b81d6fd95c55c1787fff715b8a Mon Sep 17 00:00:00 2001 From: Paul T Date: Sun, 14 Feb 2016 18:40:27 +0000 Subject: [PATCH 11/13] Modify display interface pin numbers --- peripherals/hd44780.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/peripherals/hd44780.py b/peripherals/hd44780.py index 966ec82..58998e9 100644 --- a/peripherals/hd44780.py +++ b/peripherals/hd44780.py @@ -21,12 +21,12 @@ import time # Define GPIO to LCD mapping -LCD_RS = 7 -LCD_E = 11 -LCD_D4 = 37 -LCD_D5 = 35 -LCD_D6 = 33 -LCD_D7 = 31 +LCD_RS = 4 +LCD_E = 17 +LCD_D4 = 5 +LCD_D5 = 6 +LCD_D6 = 13 +LCD_D7 = 26 # Define some device constants @@ -42,7 +42,7 @@ E_DELAY = 0.0005 GPIO.setwarnings(True) -GPIO.setmode(GPIO.BOARD) # Use BCM GPIO numbers +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 From ee24f1120cf163ba84580b8f397361d13fbdfe40 Mon Sep 17 00:00:00 2001 From: Paul T Date: Fri, 3 Feb 2017 01:11:19 +0000 Subject: [PATCH 12/13] Change port settings to match hardware --- peripherals/hd44780.py | 19 ++++++++++--------- samplerbox.py | 13 +++++++------ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/peripherals/hd44780.py b/peripherals/hd44780.py index 58998e9..a41c14f 100644 --- a/peripherals/hd44780.py +++ b/peripherals/hd44780.py @@ -21,21 +21,21 @@ import time # Define GPIO to LCD mapping -LCD_RS = 4 -LCD_E = 17 -LCD_D4 = 5 -LCD_D5 = 6 -LCD_D6 = 13 -LCD_D7 = 26 +LCD_RS = 25 +LCD_E = 24 +LCD_D4 = 23 +LCD_D5 = 22 +LCD_D6 = 27 +LCD_D7 = 17 # Define some device constants -LCD_WIDTH = 8 # Maximum characters per line +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 +LCD_LINE_1 = 0x00 # LCD RAM address for the 1st line +LCD_LINE_2 = 0x40 # LCD RAM address for the 2nd line # Timing constants E_PULSE = 0.0005 @@ -60,6 +60,7 @@ def lcd_init(): 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): diff --git a/samplerbox.py b/samplerbox.py index c0db8e1..50cf743 100644 --- a/samplerbox.py +++ b/samplerbox.py @@ -365,7 +365,7 @@ def ActuallyLoad(): p = pyaudio.PyAudio() try: - stream = p.open(format=pyaudio.paInt16, channels=2, rate=44100, frames_per_buffer=512, output=True, + stream = p.open(format=pyaudio.paInt16, channels=2, rate=48000, frames_per_buffer=512, output=True, input=False, output_device_index=AUDIO_DEVICE_ID, stream_callback=AudioCallback) print 'Opened audio: ' + p.get_device_info_by_index(AUDIO_DEVICE_ID)['name'] except: @@ -391,19 +391,19 @@ def ActuallyLoad(): def Buttons(): GPIO.setmode(GPIO.BCM) - GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_UP) - GPIO.setup(24, 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(23) 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(24) and (now - lastbuttontime) > 0.2: + elif not GPIO.input(16) and (now - lastbuttontime) > 0.2: lastbuttontime = now preset += 1 if preset > 127: @@ -451,7 +451,7 @@ def display(s, lines): else: - def display(s): + def display(s, l): pass @@ -471,6 +471,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 From a36e1698b9fb810ba294d90413e90304b3fcc876 Mon Sep 17 00:00:00 2001 From: Paul T Date: Tue, 7 Feb 2017 10:00:35 +0000 Subject: [PATCH 13/13] Revert erroneus HD44780 driver change --- peripherals/hd44780.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/peripherals/hd44780.py b/peripherals/hd44780.py index a41c14f..c14cac6 100644 --- a/peripherals/hd44780.py +++ b/peripherals/hd44780.py @@ -34,8 +34,8 @@ LCD_CHR = True LCD_CMD = False -LCD_LINE_1 = 0x00 # LCD RAM address for the 1st line -LCD_LINE_2 = 0x40 # LCD RAM address for the 2nd line +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