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"
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,
......
......@@ -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;
}
......@@ -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;
......
......@@ -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
......
# 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
......
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