From 476899efb17bf440f70a122a9c4deed5e1ede081 Mon Sep 17 00:00:00 2001 From: moon2 <moon2protonmail@protonmail> Date: Fri, 18 Aug 2023 18:35:09 +0200 Subject: [PATCH] Revert "bl00mbox: not quite sampler patch file save/load but almost" This reverts commit 96686b95fb471e77327917eb919ee5b91084c2f3. broke sampler file loading, fix approach in sampler_fix branch, not there yet --- .../plugins/bl00mbox/bl00mbox_line_in.c | 2 +- components/bl00mbox/plugins/sampler.c | 79 +++------ components/bl00mbox/plugins/sampler.h | 3 + python_payload/apps/tiny_sampler/__init__.py | 4 +- python_payload/bl00mbox/_patches.py | 154 ++++-------------- 5 files changed, 55 insertions(+), 187 deletions(-) diff --git a/components/bl00mbox/plugins/bl00mbox/bl00mbox_line_in.c b/components/bl00mbox/plugins/bl00mbox/bl00mbox_line_in.c index 2aa14d33da..a977a158cb 100644 --- a/components/bl00mbox/plugins/bl00mbox/bl00mbox_line_in.c +++ b/components/bl00mbox/plugins/bl00mbox/bl00mbox_line_in.c @@ -1,7 +1,7 @@ #include "bl00mbox_line_in.h" radspa_descriptor_t bl00mbox_line_in_desc = { - .name = "bl00mbox_line_in", + .name = "line_in", .id = 4001, .description = "connects to the line input of bl00mbox", .create_plugin_instance = bl00mbox_line_in_create, diff --git a/components/bl00mbox/plugins/sampler.c b/components/bl00mbox/plugins/sampler.c index 46b0a04a45..e4ff33b72d 100644 --- a/components/bl00mbox/plugins/sampler.c +++ b/components/bl00mbox/plugins/sampler.c @@ -4,9 +4,7 @@ radspa_t * sampler_create(uint32_t init_var); radspa_descriptor_t sampler_desc = { .name = "_sampler_ram", .id = 696969, - .description = "simple sampler that stores a copy of the sample in ram and has basic recording functionality." - "\ninit_var: length of pcm sample memory\ntable layout: [0:2] read head position (uint32_t), [2:4] sample start (uint32_t), " - "[4:6] sample length (uint32_t), [6] new record event (bool), [7:init_var+7] pcm sample data (int16_t)", + .description = "simple sampler that stores a copy of the sample in ram and has basic recording functionality", .create_plugin_instance = sampler_create, .destroy_plugin_instance = radspa_standard_plugin_destroy }; @@ -16,29 +14,6 @@ radspa_descriptor_t sampler_desc = { #define SAMPLER_TRIGGER 1 #define SAMPLER_REC_IN 2 #define SAMPLER_REC_TRIGGER 3 -#define READ_HEAD_POS 0 -#define SAMPLE_START 2 -#define SAMPLE_LEN 4 -#define BUFFER_OFFSET 7 - -static inline int16_t define_behavior(uint32_t in){ - int32_t ret = in & 0xFFFF; - if(ret > 32767) return ret - 65535; - return ret; -} - -static inline void write_uint32_to_buffer_pos(int16_t * buf, uint32_t pos, uint32_t input){ - buf[pos] = define_behavior(input); - buf[pos+1] = define_behavior(input>>16); -} - -static inline uint32_t read_uint32_from_buffer_pos(int16_t * buf, uint32_t pos){ - int32_t lsb = buf[pos]; - if(lsb < 0) lsb += 65535; - int32_t msb = buf[pos+1]; - if(msb < 0) msb += 65535; - return (((uint32_t) msb) << 16) + ((uint32_t) lsb); -} void sampler_run(radspa_t * sampler, uint16_t num_samples, uint32_t render_pass_id){ radspa_signal_t * output_sig = radspa_signal_get_by_index(sampler, SAMPLER_OUTPUT); @@ -51,11 +26,7 @@ void sampler_run(radspa_t * sampler, uint16_t num_samples, uint32_t render_pass_ static int32_t ret = 0; - uint32_t read_head_pos = read_uint32_from_buffer_pos(buf, READ_HEAD_POS); - uint32_t sample_start = read_uint32_from_buffer_pos(buf, SAMPLE_START); - uint32_t sample_len = read_uint32_from_buffer_pos(buf, SAMPLE_LEN); - - uint32_t buffer_size = sampler->plugin_table_len - 6; + uint32_t buffer_size = sampler->plugin_table_len; for(uint16_t i = 0; i < num_samples; i++){ int16_t rec_trigger = radspa_trigger_get(rec_trigger_sig->get_value(rec_trigger_sig, i, num_samples, render_pass_id), &(data->rec_trigger_prev)); @@ -63,52 +34,41 @@ void sampler_run(radspa_t * sampler, uint16_t num_samples, uint32_t render_pass_ if(rec_trigger > 0){ if(!(data->rec_active)){ - read_head_pos = sample_len; - write_uint32_to_buffer_pos(buf, READ_HEAD_POS, read_head_pos); + data->read_head_position = data->sample_len; // reset sample player into off data->write_head_position = 0; - sample_len = 0; - write_uint32_to_buffer_pos(buf, SAMPLE_LEN, sample_len); + data->sample_len = 0; data->rec_active = true; } } else if(rec_trigger < 0){ if(data->rec_active){ - if(sample_len == buffer_size){ - sample_start = data->write_head_position; - write_uint32_to_buffer_pos(buf, SAMPLE_START, sample_start); + if(data->sample_len == buffer_size){ + data->sample_start = data->write_head_position; } else { - sample_start = 0; - write_uint32_to_buffer_pos(buf, SAMPLE_START, sample_start); + data->sample_start = 0; } - buf[6] = 1; data->rec_active = false; } } if(data->rec_active){ int16_t rec_in = rec_in_sig->get_value(rec_in_sig, i, num_samples, render_pass_id); - buf[data->write_head_position + BUFFER_OFFSET] = rec_in; + buf[data->write_head_position] = rec_in; data->write_head_position++; if(data->write_head_position >= buffer_size) data->write_head_position = 0; - if(sample_len < buffer_size){ - sample_len++; - write_uint32_to_buffer_pos(buf, SAMPLE_LEN, sample_len); - } + if(data->sample_len < buffer_size) data->sample_len++; } else { if(trigger > 0){ - read_head_pos = 0; - write_uint32_to_buffer_pos(buf, READ_HEAD_POS, read_head_pos); + data->read_head_position = 0; data->volume = trigger; } else if(trigger < 0){ - read_head_pos = sample_len; - write_uint32_to_buffer_pos(buf, READ_HEAD_POS, read_head_pos); + data->read_head_position = data->sample_len; } - if(read_head_pos < sample_len){ - uint32_t sample_offset_pos = read_head_pos + sample_start; - if(sample_offset_pos >= sample_len) sample_offset_pos -= sample_len; - ret = radspa_mult_shift(buf[sample_offset_pos + BUFFER_OFFSET], data->volume); - read_head_pos++; - write_uint32_to_buffer_pos(buf, READ_HEAD_POS, read_head_pos); + if(data->read_head_position < data->sample_len){ + uint32_t sample_offset_pos = data->read_head_position + data->sample_start; + if(sample_offset_pos >= data->sample_len) sample_offset_pos -= data->sample_len; + ret = radspa_mult_shift(buf[sample_offset_pos], data->volume); + data->read_head_position++; } else { //ret = (ret * 255)>>8; // avoid dc clicks with bad samples ret = 0; @@ -121,7 +81,7 @@ void sampler_run(radspa_t * sampler, uint16_t num_samples, uint32_t render_pass_ radspa_t * sampler_create(uint32_t init_var){ if(init_var == 0) return NULL; //doesn't make sense uint32_t buffer_size = init_var; - radspa_t * sampler = radspa_standard_plugin_create(&sampler_desc, SAMPLER_NUM_SIGNALS, sizeof(sampler_data_t), buffer_size + BUFFER_OFFSET); + radspa_t * sampler = radspa_standard_plugin_create(&sampler_desc, SAMPLER_NUM_SIGNALS, sizeof(sampler_data_t), buffer_size); if(sampler == NULL) return NULL; sampler->render = sampler_run; radspa_signal_set(sampler, SAMPLER_OUTPUT, "output", RADSPA_SIGNAL_HINT_OUTPUT, 0); @@ -132,8 +92,7 @@ radspa_t * sampler_create(uint32_t init_var){ data->trigger_prev = 0; data->rec_trigger_prev = 0; data->rec_active = false; - //int16_t * buf = sampler->plugin_table; - //write_uint32_to_buffer_pos(buf, SAMPLE_START, 0); - //write_uint32_to_buffer_pos(buf, SAMPLE_LEN, sampler->plugin_table_len); + data->sample_start = 0; + data->sample_len = sampler->plugin_table_len; return sampler; } diff --git a/components/bl00mbox/plugins/sampler.h b/components/bl00mbox/plugins/sampler.h index 8483f31cb5..9958d9b9f2 100644 --- a/components/bl00mbox/plugins/sampler.h +++ b/components/bl00mbox/plugins/sampler.h @@ -3,7 +3,10 @@ #include <radspa_helpers.h> typedef struct { + uint32_t read_head_position; uint32_t write_head_position; + uint32_t sample_start; + uint32_t sample_len; int16_t trigger_prev; int16_t rec_trigger_prev; int16_t volume; diff --git a/python_payload/apps/tiny_sampler/__init__.py b/python_payload/apps/tiny_sampler/__init__.py index 6f139be92b..7b2f38c337 100644 --- a/python_payload/apps/tiny_sampler/__init__.py +++ b/python_payload/apps/tiny_sampler/__init__.py @@ -16,13 +16,13 @@ class TinySampler(Application): self.blm = bl00mbox.Channel("tiny sampler") self.samplers: List[bl00mbox.patches._Patch | Any] = [None] * 5 - self.line_in = self.blm.new(bl00mbox.plugins.bl00mbox_line_in) + self.line_in = self.blm.new(bl00mbox.plugins.line_in) self.blm.volume = ( 30000 # TODO: increase onboard mic line in gain and remove this ) self.line_in.signals.gain = 30000 for i in range(5): - self.samplers[i] = self.blm.new(bl00mbox.patches.sampler, 1000) + self.samplers[i] = self.blm.new(bl00mbox.patches.sampler, 5000) self.samplers[i].signals.output = self.blm.mixer self.samplers[i].signals.rec_in = self.line_in.signals.right self.is_recording = [False] * 5 diff --git a/python_payload/bl00mbox/_patches.py b/python_payload/bl00mbox/_patches.py index 9a4f9fbbc6..3942399fe1 100644 --- a/python_payload/bl00mbox/_patches.py +++ b/python_payload/bl00mbox/_patches.py @@ -1,6 +1,5 @@ # SPDX-License-Identifier: CC0-1.0 import math -import os import bl00mbox import cpython.wave as wave @@ -101,152 +100,61 @@ class tinysynth_fm(tinysynth): class sampler(_Patch): """ - requires a wave file (str) or max sample length in milliseconds (int). default path: /sys/samples/ + requires a wave file. default path: /sys/samples/ """ def __init__(self, chan, init_var): # init can be filename to load into ram super().__init__(chan) - self._filename = "" if type(init_var) == str: filename = init_var - f = wave.open(self._convert_filename(filename), "r") + if filename.startswith("/flash/") or filename.startswith("/sd/"): + f = wave.open(filename, "r") + elif filename.startswith("/"): + f = wave.open("/flash/" + filename, "r") + else: + f = wave.open("/flash/sys/samples/" + filename, "r") - self._len_frames = f.getnframes() + self.len_frames = f.getnframes() self.plugins.sampler = chan.new( - bl00mbox.plugins._sampler_ram, self._len_frames + bl00mbox.plugins._sampler_ram, self.len_frames ) + assert f.getsampwidth() == 2 + assert f.getnchannels() in (1, 2) + assert f.getcomptype() == "NONE" + + if f.getnchannels() == 1: + # fast path for mono + table = self.plugins.sampler.table_bytearray + for i in range(0, self.len_frames * 2, 100): + table[i : i + 100] = f.readframes(50) + else: + # somewhat fast path for stereo + table = self.plugins.sampler.table_int16_array + for i in range(self.len_frames): + frame = f.readframes(1) + value = int.from_bytes(frame[0:2], "little") + table[i] = value + f.close() - self.load(filename) + self._filename = filename else: - self._len_frames = int(48 * init_var) + self.len_frames = int(48 * init_var) self.plugins.sampler = chan.new( - bl00mbox.plugins._sampler_ram, self._len_frames + bl00mbox.plugins._sampler_ram, self.len_frames ) + self._filename = "" self.signals.trigger = self.plugins.sampler.signals.trigger self.signals.output = self.plugins.sampler.signals.output self.signals.rec_in = self.plugins.sampler.signals.rec_in self.signals.rec_trigger = self.plugins.sampler.signals.rec_trigger - def _convert_filename(self, filename): - # TODO: waht if filename doesn't exist? - if filename.startswith("/flash/") or filename.startswith("/sd/"): - return filename - elif filename.startswith("/"): - return "/flash/" + filename - else: - return "/flash/sys/samples/" + filename - - def load(self, filename): - f = wave.open(self._convert_filename(filename), "r") - - assert f.getsampwidth() == 2 - assert f.getnchannels() in (1, 2) - assert f.getcomptype() == "NONE" - self.sample_start = 0 - frames = f.getnframes() - if frames > self.memory_len: - frames = self.memory_len - self.sample_len = frames - - if f.getnchannels() == 1: - # fast path for mono - table = self.plugins.sampler.table_bytearray - for i in range(0, self.memory_len * 2, 100): - table[i : i + 100] = f.readframes(50) - else: - # somewhat fast path for stereo - table = self.plugins.sampler.table_int16_array - for i in range(self._len_frames): - frame = f.readframes(1) - value = int.from_bytes(frame[0:2], "little") - table[i] = value - f.close() - - def save(self, filename, overwrite=True): - # remove when we actually write - return False - if os.path.exists(filename): - if overwrite: - os.remove(filename) - else: - return False - f = wave.open(self._convert_filename(filename), "w") - for i in range(self.sample_len): - data = self.plugins.sampler.table[_offset_index(i)] - # TODO: figure out python bytes - # note: index wraps around, ringbuffer! - # f.writeframesraw??? - f.close() - return True - - def _offset_index(self, index): - index += self.sample_start - if index >= self.memory_len: - index -= self.memory_len - index += 6 - return index - - @property - def memory_len(self): - return self._len_frames - - def _decode_uint32(self, pos): - lsb = self.plugins.sampler.table[pos] - msb = self.plugins.sampler.table[pos + 1] - if lsb < 0: - lsb += 65535 - if msb < 0: - msb += 65535 - return lsb + (msb * (1 << 16)) - - def _encode_uint32(self, pos, num): - msb = num // (1 << 16) - lsb = num - if lsb > 32767: - lsb -= 65535 - if msb > 32767: - msb -= 65535 - self.plugins.sampler.table[pos] = lsb - self.plugins.sampler.table[pos + 1] = msb - - @property - def read_head_position(self): - return self._decode_uint32(0) - - @read_head_position.setter - def read_head_position(self, val): - self._encode_uint32(0, val) - - @property - def sample_start(self): - return self._decode_uint32(2) - - @sample_start.setter - def sample_start(self, val): - self._encode_uint32(2, val) - - @property - def sample_len(self): - return self._decode_uint32(4) - - @sample_len.setter - def sample_len(self, val): - self._encode_uint32(4, val) - @property def filename(self): return self._filename - @property - def rec_event_autoclear(self): - if self.plugins.sampler_table[6]: - self.plugins.sampler_table[6] = 0 - return True - return False - class sequencer(_Patch): def __init__(self, chan, num_tracks, num_steps): @@ -282,7 +190,6 @@ class sequencer(_Patch): + str(self.signals.step_len.value) ) ret += "\n [tracks]" - """ for x, seq in enumerate(self.seqs): ret += ( "\n " @@ -291,7 +198,6 @@ class sequencer(_Patch): + "".join(["X " if x > 0 else ". " for x in seq.table[1:]]) + "]" ) - """ ret += "\n" + "\n".join(super().__repr__().split("\n")[1:]) return ret -- GitLab