Skip to content
Snippets Groups Projects
Commit 476899ef authored by moon2's avatar moon2
Browse files

Revert "bl00mbox: not quite sampler patch file save/load but almost"

This reverts commit 96686b95.

broke sampler file loading, fix approach in sampler_fix branch, not there yet
parent c1d46611
No related branches found
No related tags found
No related merge requests found
Pipeline #7118 passed
#include "bl00mbox_line_in.h" #include "bl00mbox_line_in.h"
radspa_descriptor_t bl00mbox_line_in_desc = { radspa_descriptor_t bl00mbox_line_in_desc = {
.name = "bl00mbox_line_in", .name = "line_in",
.id = 4001, .id = 4001,
.description = "connects to the line input of bl00mbox", .description = "connects to the line input of bl00mbox",
.create_plugin_instance = bl00mbox_line_in_create, .create_plugin_instance = bl00mbox_line_in_create,
......
...@@ -4,9 +4,7 @@ radspa_t * sampler_create(uint32_t init_var); ...@@ -4,9 +4,7 @@ radspa_t * sampler_create(uint32_t init_var);
radspa_descriptor_t sampler_desc = { radspa_descriptor_t sampler_desc = {
.name = "_sampler_ram", .name = "_sampler_ram",
.id = 696969, .id = 696969,
.description = "simple sampler that stores a copy of the sample in ram and has basic recording functionality." .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)",
.create_plugin_instance = sampler_create, .create_plugin_instance = sampler_create,
.destroy_plugin_instance = radspa_standard_plugin_destroy .destroy_plugin_instance = radspa_standard_plugin_destroy
}; };
...@@ -16,29 +14,6 @@ radspa_descriptor_t sampler_desc = { ...@@ -16,29 +14,6 @@ radspa_descriptor_t sampler_desc = {
#define SAMPLER_TRIGGER 1 #define SAMPLER_TRIGGER 1
#define SAMPLER_REC_IN 2 #define SAMPLER_REC_IN 2
#define SAMPLER_REC_TRIGGER 3 #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){ 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); 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_ ...@@ -51,11 +26,7 @@ void sampler_run(radspa_t * sampler, uint16_t num_samples, uint32_t render_pass_
static int32_t ret = 0; static int32_t ret = 0;
uint32_t read_head_pos = read_uint32_from_buffer_pos(buf, READ_HEAD_POS); uint32_t buffer_size = sampler->plugin_table_len;
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;
for(uint16_t i = 0; i < num_samples; i++){ 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)); 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_ ...@@ -63,52 +34,41 @@ void sampler_run(radspa_t * sampler, uint16_t num_samples, uint32_t render_pass_
if(rec_trigger > 0){ if(rec_trigger > 0){
if(!(data->rec_active)){ if(!(data->rec_active)){
read_head_pos = sample_len; data->read_head_position = data->sample_len; // reset sample player into off
write_uint32_to_buffer_pos(buf, READ_HEAD_POS, read_head_pos);
data->write_head_position = 0; data->write_head_position = 0;
sample_len = 0; data->sample_len = 0;
write_uint32_to_buffer_pos(buf, SAMPLE_LEN, sample_len);
data->rec_active = true; data->rec_active = true;
} }
} else if(rec_trigger < 0){ } else if(rec_trigger < 0){
if(data->rec_active){ if(data->rec_active){
if(sample_len == buffer_size){ if(data->sample_len == buffer_size){
sample_start = data->write_head_position; data->sample_start = data->write_head_position;
write_uint32_to_buffer_pos(buf, SAMPLE_START, sample_start);
} else { } else {
sample_start = 0; data->sample_start = 0;
write_uint32_to_buffer_pos(buf, SAMPLE_START, sample_start);
} }
buf[6] = 1;
data->rec_active = false; data->rec_active = false;
} }
} }
if(data->rec_active){ if(data->rec_active){
int16_t rec_in = rec_in_sig->get_value(rec_in_sig, i, num_samples, render_pass_id); 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++; data->write_head_position++;
if(data->write_head_position >= buffer_size) data->write_head_position = 0; if(data->write_head_position >= buffer_size) data->write_head_position = 0;
if(sample_len < buffer_size){ if(data->sample_len < buffer_size) data->sample_len++;
sample_len++;
write_uint32_to_buffer_pos(buf, SAMPLE_LEN, sample_len);
}
} else { } else {
if(trigger > 0){ if(trigger > 0){
read_head_pos = 0; data->read_head_position = 0;
write_uint32_to_buffer_pos(buf, READ_HEAD_POS, read_head_pos);
data->volume = trigger; data->volume = trigger;
} else if(trigger < 0){ } else if(trigger < 0){
read_head_pos = sample_len; data->read_head_position = data->sample_len;
write_uint32_to_buffer_pos(buf, READ_HEAD_POS, read_head_pos);
} }
if(read_head_pos < sample_len){ if(data->read_head_position < data->sample_len){
uint32_t sample_offset_pos = read_head_pos + sample_start; uint32_t sample_offset_pos = data->read_head_position + data->sample_start;
if(sample_offset_pos >= sample_len) sample_offset_pos -= sample_len; if(sample_offset_pos >= data->sample_len) sample_offset_pos -= data->sample_len;
ret = radspa_mult_shift(buf[sample_offset_pos + BUFFER_OFFSET], data->volume); ret = radspa_mult_shift(buf[sample_offset_pos], data->volume);
read_head_pos++; data->read_head_position++;
write_uint32_to_buffer_pos(buf, READ_HEAD_POS, read_head_pos);
} else { } else {
//ret = (ret * 255)>>8; // avoid dc clicks with bad samples //ret = (ret * 255)>>8; // avoid dc clicks with bad samples
ret = 0; ret = 0;
...@@ -121,7 +81,7 @@ void sampler_run(radspa_t * sampler, uint16_t num_samples, uint32_t render_pass_ ...@@ -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){ radspa_t * sampler_create(uint32_t init_var){
if(init_var == 0) return NULL; //doesn't make sense if(init_var == 0) return NULL; //doesn't make sense
uint32_t buffer_size = init_var; 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; if(sampler == NULL) return NULL;
sampler->render = sampler_run; sampler->render = sampler_run;
radspa_signal_set(sampler, SAMPLER_OUTPUT, "output", RADSPA_SIGNAL_HINT_OUTPUT, 0); radspa_signal_set(sampler, SAMPLER_OUTPUT, "output", RADSPA_SIGNAL_HINT_OUTPUT, 0);
...@@ -132,8 +92,7 @@ radspa_t * sampler_create(uint32_t init_var){ ...@@ -132,8 +92,7 @@ radspa_t * sampler_create(uint32_t init_var){
data->trigger_prev = 0; data->trigger_prev = 0;
data->rec_trigger_prev = 0; data->rec_trigger_prev = 0;
data->rec_active = false; data->rec_active = false;
//int16_t * buf = sampler->plugin_table; data->sample_start = 0;
//write_uint32_to_buffer_pos(buf, SAMPLE_START, 0); data->sample_len = sampler->plugin_table_len;
//write_uint32_to_buffer_pos(buf, SAMPLE_LEN, sampler->plugin_table_len);
return sampler; return sampler;
} }
...@@ -3,7 +3,10 @@ ...@@ -3,7 +3,10 @@
#include <radspa_helpers.h> #include <radspa_helpers.h>
typedef struct { typedef struct {
uint32_t read_head_position;
uint32_t write_head_position; uint32_t write_head_position;
uint32_t sample_start;
uint32_t sample_len;
int16_t trigger_prev; int16_t trigger_prev;
int16_t rec_trigger_prev; int16_t rec_trigger_prev;
int16_t volume; int16_t volume;
......
...@@ -16,13 +16,13 @@ class TinySampler(Application): ...@@ -16,13 +16,13 @@ class TinySampler(Application):
self.blm = bl00mbox.Channel("tiny sampler") self.blm = bl00mbox.Channel("tiny sampler")
self.samplers: List[bl00mbox.patches._Patch | Any] = [None] * 5 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 = ( self.blm.volume = (
30000 # TODO: increase onboard mic line in gain and remove this 30000 # TODO: increase onboard mic line in gain and remove this
) )
self.line_in.signals.gain = 30000 self.line_in.signals.gain = 30000
for i in range(5): 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.output = self.blm.mixer
self.samplers[i].signals.rec_in = self.line_in.signals.right self.samplers[i].signals.rec_in = self.line_in.signals.right
self.is_recording = [False] * 5 self.is_recording = [False] * 5
......
# SPDX-License-Identifier: CC0-1.0 # SPDX-License-Identifier: CC0-1.0
import math import math
import os
import bl00mbox import bl00mbox
import cpython.wave as wave import cpython.wave as wave
...@@ -101,152 +100,61 @@ class tinysynth_fm(tinysynth): ...@@ -101,152 +100,61 @@ class tinysynth_fm(tinysynth):
class sampler(_Patch): 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): def __init__(self, chan, init_var):
# init can be filename to load into ram # init can be filename to load into ram
super().__init__(chan) super().__init__(chan)
self._filename = ""
if type(init_var) == str: if type(init_var) == str:
filename = init_var 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( 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() f.close()
self.load(filename) self._filename = filename
else: else:
self._len_frames = int(48 * init_var) self.len_frames = int(48 * init_var)
self.plugins.sampler = chan.new( 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.trigger = self.plugins.sampler.signals.trigger
self.signals.output = self.plugins.sampler.signals.output self.signals.output = self.plugins.sampler.signals.output
self.signals.rec_in = self.plugins.sampler.signals.rec_in self.signals.rec_in = self.plugins.sampler.signals.rec_in
self.signals.rec_trigger = self.plugins.sampler.signals.rec_trigger 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 @property
def filename(self): def filename(self):
return self._filename 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): class sequencer(_Patch):
def __init__(self, chan, num_tracks, num_steps): def __init__(self, chan, num_tracks, num_steps):
...@@ -282,7 +190,6 @@ class sequencer(_Patch): ...@@ -282,7 +190,6 @@ class sequencer(_Patch):
+ str(self.signals.step_len.value) + str(self.signals.step_len.value)
) )
ret += "\n [tracks]" ret += "\n [tracks]"
"""
for x, seq in enumerate(self.seqs): for x, seq in enumerate(self.seqs):
ret += ( ret += (
"\n " "\n "
...@@ -291,7 +198,6 @@ class sequencer(_Patch): ...@@ -291,7 +198,6 @@ class sequencer(_Patch):
+ "".join(["X " if x > 0 else ". " for x in seq.table[1:]]) + "".join(["X " if x > 0 else ". " for x in seq.table[1:]])
+ "]" + "]"
) )
"""
ret += "\n" + "\n".join(super().__repr__().split("\n")[1:]) ret += "\n" + "\n".join(super().__repr__().split("\n")[1:])
return ret return ret
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment