@@ -88,6 +88,20 @@ namespace crone {
BufDiskWorker::requestClear(bufIdx[chan], start, dur);
}

void copyBuffer(int srcChan, int dstChan,
float srcStart=0.f, float dstStart=0.f, float dur=-1,
float fadeTime=0.f, float preserve=0.f, bool reverse=false) {
if (srcChan < 0 || srcChan > 1 || dstChan < 0 || dstChan > 1) { return; }
BufDiskWorker::requestCopy(bufIdx[srcChan], bufIdx[dstChan],
srcStart, dstStart, dur,
fadeTime, preserve, reverse);
}

void renderSamples(int chan, float start, float dur, int count, BufDiskWorker::RenderCallback callback) {
if (chan < 0 || chan > 1 || count < 1) { return; }
BufDiskWorker::requestRender(bufIdx[chan], start, dur, count, callback);
}

// check if quantized phase has changed for a given voice
// returns true
bool checkVoiceQuantPhase(int i) {
@@ -84,6 +84,8 @@ _norns.vu = function(in1, in2, out1, out2) end
-- softcut phase
_norns.softcut_phase = function(id, value) end

_norns.softcut_render = function(ch, start, sec_per_sample, samples) end

-- default readings for battery
norns.battery_percent = 0
norns.battery_current = 0
@@ -297,6 +297,28 @@ SC.buffer_clear_region_channel = function(ch, start, dur)
_norns.cut_buffer_clear_region_channel(ch, start, dur)
end

--- copy region from one point in a buffer to another
-- @tparam int src_ch : source buffer index (1-based)
-- @tparam int dst_ch : destination buffer index (1-based)
-- @tparam number start_src : start point in source, in seconds
-- @tparam number start_dst : start point in destination, in seconds
-- @tparam number dur : duration in seconds. if -1, copy as much as possible.
-- @tparam number fade_time : fade time in seconds.
-- @tparam int reverse : nonzero to reverse while copying. when reversing, overlap between source and destination regions is not handled.
SC.buffer_copy_mono = function(src_ch, dst_ch, start_src, start_dst, dur, fade_time, preserve, reverse)
_norns.cut_buffer_copy_mono(src_ch, dst_ch, start_src, start_dst, dur, fade_time or 0, preserve or 0, reverse or 0)
end

--- copy region of both buffers to another point
-- @tparam number start_src : start point in source, in seconds
-- @tparam number start_dst : start point in destination, in seconds
-- @tparam number dur : duration in seconds. if -1, copy as much as possible.
-- @tparam number fade_time : fade time in seconds.
-- @tparam int reverse : nonzero to reverse while copying.
SC.buffer_copy_stereo = function(start_src, start_dst, dur, fade_time, preserve, reverse)
_norns.cut_buffer_copy_stereo(start_src, start_dst, dur, fade_time or 0, preserve or 0, reverse or 0)
end

--- read mono soundfile to arbitrary region of single buffer
-- @tparam string file : input file path
-- @tparam number start_src : start point in source, in seconds
@@ -338,6 +360,17 @@ end
-- @tparam function func : callback function. this function should take two parameters (voice, phase)
SC.event_phase = function(func) _norns.softcut_phase = func end

--- request snapshot of buffer content for region.
-- @tparam integer ch : buffer channel index (1-based)
-- @tparam number start : beginning of region in seconds
-- @tparam number dur : length of region in seconds
-- @tparam integer samples : max number of samples to retrieve. if less than the number of frames in the region, content will be downsampled
-- @tparam function callback : called when buffer content is ready. args: (ch, start, sec_per_frame, samples)
SC.render_buffer = function(ch, start, dur, samples, callback)
_norns.softcut_render = callback
_norns.cut_buffer_render(ch, start, dur, samples)
end


-------------------------------
-- @section utilities
@@ -80,7 +80,9 @@ typedef enum {
// crow remove
EVENT_CROW_REMOVE,
// crow event
EVENT_CROW_EVENT
EVENT_CROW_EVENT,
// softcut buffer content callback
EVENT_SOFTCUT_RENDER,
} event_t;

// a packed data structure for four volume levels
@@ -289,6 +291,15 @@ struct event_system_cmd {
char *capture;
};

struct event_softcut_render {
struct event_common common;
int idx;
float sec_per_sample;
float start;
size_t size;
float* data;
};

union event_data {
uint32_t type;
struct event_exec_code_line exec_code_line;
@@ -322,4 +333,5 @@ union event_data {
struct event_crow_remove crow_remove;
struct event_crow_event crow_event;
struct event_system_cmd system_cmd;
struct event_softcut_render softcut_render;
};
@@ -124,6 +124,9 @@ void event_data_free(union event_data *ev) {
case EVENT_SYSTEM_CMD:
free(ev->system_cmd.capture);
break;
case EVENT_SOFTCUT_RENDER:
free(ev->softcut_render.data);
break;
}
free(ev);
}
@@ -284,6 +287,9 @@ static void handle_event(union event_data *ev) {
case EVENT_CROW_EVENT:
w_handle_crow_event(ev->crow_event.dev, ev->crow_event.id);
break;
case EVENT_SOFTCUT_RENDER:
w_handle_softcut_render(ev->softcut_render.idx, ev->softcut_render.sec_per_sample, ev->softcut_render.start, ev->softcut_render.size, ev->softcut_render.data);
break;
} /* switch */

event_data_free(ev);
@@ -108,6 +108,9 @@ static int handle_poll_io_levels(const char *path, const char *types, lo_arg **a
static int handle_poll_softcut_phase(const char *path, const char *types, lo_arg **argv, int argc, void *data,
void *user_data);

static int handle_softcut_render(const char *path, const char *types, lo_arg **argv, int argc, void *data,
void *user_data);

static int handle_tape_play_state(const char *path, const char *types, lo_arg **argv, int argc, void *data,
void *user_data);

@@ -174,6 +177,9 @@ void o_init(void) {
// tape reports
lo_server_thread_add_method(st, "/tape/play/state", "s", handle_tape_play_state, NULL);

// softcut buffer content
lo_server_thread_add_method(st, "/softcut/buffer/render_callback", "iffb", handle_softcut_render, NULL);

lo_server_thread_start(st);
}

@@ -560,6 +566,19 @@ void o_cut_buffer_clear_region_channel(int ch, float start, float end) {
lo_send(crone_addr, "/softcut/buffer/clear_region_channel", "iff", ch, start, end);
}

void o_cut_buffer_copy_mono(int srcCh, int dstCh,
float srcStart, float dstStart, float dur,
float fadeTime, float preserve, int reverse) {
lo_send(crone_addr, "/softcut/buffer/copy_mono", "iifffffi",
srcCh, dstCh, srcStart, dstStart, dur, fadeTime, preserve, reverse);
}

void o_cut_buffer_copy_stereo(float srcStart, float dstStart, float dur,
float fadeTime, float preserve, int reverse) {
lo_send(crone_addr, "/softcut/buffer/copy_stereo", "fffffi",
srcStart, dstStart, dur, fadeTime, preserve, reverse);
}

void o_cut_buffer_read_mono(char *file, float start_src, float start_dst, float dur, int ch_src, int ch_dst) {
lo_send(crone_addr, "/softcut/buffer/read_mono", "sfffii", file, start_src, start_dst, dur, ch_src, ch_dst);
}
@@ -576,6 +595,10 @@ void o_cut_buffer_write_stereo(char *file, float start, float dur) {
lo_send(crone_addr, "/softcut/buffer/write_stereo", "sff", file, start, dur);
}

void o_cut_buffer_render(int ch, float start, float dur, int samples) {
lo_send(crone_addr, "/softcut/buffer/render", "iffi", ch, start, dur, samples);
}

void o_cut_reset() {
lo_send(crone_addr, "/softcut/reset", "");
}
@@ -781,6 +804,22 @@ int handle_tape_play_state(const char *path, const char *types, lo_arg **argv, i
return 0;
}

int handle_softcut_render(const char *path, const char *types, lo_arg **argv, int argc, void *data, void *user_data) {
assert(argc > 2);
union event_data *ev = event_data_new(EVENT_SOFTCUT_RENDER);
ev->softcut_render.idx = argv[0]->i;
ev->softcut_render.sec_per_sample = argv[1]->f;
ev->softcut_render.start = argv[2]->f;

int sz = lo_blob_datasize((lo_blob)argv[3]);
float *samples = (float*)lo_blob_dataptr((lo_blob)argv[3]);
ev->softcut_render.size = sz / sizeof(float);
ev->softcut_render.data = calloc(1, sz);
memcpy(ev->softcut_render.data, samples, sz);
event_post(ev);
return 0;
}

void lo_error_handler(int num, const char *m, const char *path) {
fprintf(stderr, "liblo error %d in path %s: %s\n", num, path, m);
}
@@ -137,10 +137,13 @@ extern void o_cut_buffer_clear();
extern void o_cut_buffer_clear_channel(int ch);
extern void o_cut_buffer_clear_region(float start, float end);
extern void o_cut_buffer_clear_region_channel(int ch, float start, float end);
extern void o_cut_buffer_copy_mono(int srcCh, int dstCh, float srcStart, float dstStart, float dur, float fadeTime, float preserve, int reverse);
extern void o_cut_buffer_copy_stereo(float srcStart, float dstStart, float dur, float fadeTime, float preserve, int reverse);
extern void o_cut_buffer_read_mono(char *file, float start_src, float start_dst, float dur, int ch_src, int ch_dst);
extern void o_cut_buffer_read_stereo(char *file, float start_src, float start_dst, float dur);
extern void o_cut_buffer_write_mono(char *file, float start, float dur, int ch);
extern void o_cut_buffer_write_stereo(char *file, float start, float dur);
extern void o_cut_buffer_render(int ch, float start, float dur, int samples);
extern void o_cut_reset();
// most softcut parameter changs take single voice index...
extern void o_set_cut_param(const char *name, int voice, float value);
@@ -179,10 +179,13 @@ static int _cut_buffer_clear(lua_State *l);
static int _cut_buffer_clear_channel(lua_State *l);
static int _cut_buffer_clear_region(lua_State *l);
static int _cut_buffer_clear_region_channel(lua_State *l);
static int _cut_buffer_copy_mono(lua_State *l);
static int _cut_buffer_copy_stereo(lua_State *l);
static int _cut_buffer_read_mono(lua_State *l);
static int _cut_buffer_read_stereo(lua_State *l);
static int _cut_buffer_write_mono(lua_State *l);
static int _cut_buffer_write_stereo(lua_State *l);
static int _cut_buffer_render(lua_State *l);
static int _cut_reset(lua_State *l);
static int _set_cut_param(lua_State *l);
static int _set_cut_param_ii(lua_State *l);
@@ -313,10 +316,13 @@ void w_init(void) {
lua_register_norns("cut_buffer_clear_channel", &_cut_buffer_clear_channel);
lua_register_norns("cut_buffer_clear_region", &_cut_buffer_clear_region);
lua_register_norns("cut_buffer_clear_region_channel", &_cut_buffer_clear_region_channel);
lua_register_norns("cut_buffer_copy_mono", &_cut_buffer_copy_mono);
lua_register_norns("cut_buffer_copy_stereo", &_cut_buffer_copy_stereo);
lua_register_norns("cut_buffer_read_mono", &_cut_buffer_read_mono);
lua_register_norns("cut_buffer_read_stereo", &_cut_buffer_read_stereo);
lua_register_norns("cut_buffer_write_mono", &_cut_buffer_write_mono);
lua_register_norns("cut_buffer_write_stereo", &_cut_buffer_write_stereo);
lua_register_norns("cut_buffer_render", &_cut_buffer_render);
lua_register_norns("cut_reset", &_cut_reset);
lua_register_norns("cut_param", &_set_cut_param);
lua_register_norns("cut_param_ii", &_set_cut_param_ii);
@@ -1920,6 +1926,21 @@ void w_handle_poll_softcut_phase(int idx, float val) {
l_report(lvm, l_docall(lvm, 2, 0));
}

void w_handle_softcut_render(int idx, float sec_per_sample, float start, size_t size, float* data) {
lua_getglobal(lvm, "_norns");
lua_getfield(lvm, -1, "softcut_render");
lua_remove(lvm, -2);
lua_pushinteger(lvm, idx + 1);
lua_pushnumber(lvm, start);
lua_pushnumber(lvm, sec_per_sample);
lua_createtable(lvm, size, 0);
for (size_t i = 0; i < size; ++i) {
lua_pushnumber(lvm, data[i]);
lua_rawseti(lvm, -2, i + 1);
}
l_report(lvm, l_docall(lvm, 4, 0));
}

// handle system command capture
void w_handle_system_cmd(char *capture) {
lua_getglobal(lvm, "_norns");
@@ -2177,6 +2198,32 @@ int _cut_buffer_clear_region_channel(lua_State *l) {
return 0;
}

int _cut_buffer_copy_mono(lua_State *l) {
lua_check_num_args(8);
int srcCh = (int)luaL_checkinteger(l, 1) - 1;
int dstCh = (int)luaL_checkinteger(l, 2) - 1;
float srcStart = (float)luaL_checknumber(l, 3);
float dstStart = (float)luaL_checknumber(l, 4);
float dur = (float)luaL_checknumber(l, 5);
float fadeTime = (float)luaL_checknumber(l, 6);
float preserve = (float)luaL_checknumber(l, 7);
int reverse = (int)luaL_checkinteger(l, 8);
o_cut_buffer_copy_mono(srcCh, dstCh, srcStart, dstStart, dur, fadeTime, preserve, reverse);
return 0;
}

int _cut_buffer_copy_stereo(lua_State *l) {
lua_check_num_args(6);
float srcStart = (float)luaL_checknumber(l, 1);
float dstStart = (float)luaL_checknumber(l, 2);
float dur = (float)luaL_checknumber(l, 3);
float fadeTime = (float)luaL_checknumber(l, 4);
float preserve = (float)luaL_checknumber(l, 5);
int reverse = (int)luaL_checkinteger(l, 6);
o_cut_buffer_copy_stereo(srcStart, dstStart, dur, fadeTime, preserve, reverse);
return 0;
}

int _cut_buffer_read_mono(lua_State *l) {
lua_check_num_args(6);
const char *s = luaL_checkstring(l, 1);
@@ -2218,6 +2265,16 @@ int _cut_buffer_write_stereo(lua_State *l) {
return 0;
}

int _cut_buffer_render(lua_State *l) {
lua_check_num_args(4);
int ch = (int)luaL_checkinteger(l, 1) - 1;
float start = (float)luaL_checknumber(l, 2);
float dur = (float)luaL_checknumber(l, 3);
int samples = (int)luaL_checknumber(l, 4);
o_cut_buffer_render(ch, start, dur, samples);
return 0;
}

int _cut_reset(lua_State *l) {
o_cut_reset();
return 0;
@@ -81,6 +81,7 @@ extern void w_handle_poll_data(int idx, int size, uint8_t *data);
extern void w_handle_poll_wave(int idx, uint8_t *data);
extern void w_handle_poll_io_levels(uint8_t *levels);
extern void w_handle_poll_softcut_phase(int idx, float val);
extern void w_handle_softcut_render(int idx, float sec_per_sample, float start, size_t size, float* data);

extern void w_handle_engine_loaded();