//#include #include #include #include #include #include #define CHANNELS 2 #define TAPS 6 #define BUFFER_SECONDS 10 struct Tap { float *t_gain[TAPS + CHANNELS]; float *gain; float *delay; }; static const int CONTROLS_PER_TAP = sizeof (Tap) / sizeof (float *); struct PTap { double sample_rate; size_t buffer_max; Tap tap[TAPS + CHANNELS]; float *buffers[TAPS]; ///< Tap audio buffers const float *buffers_end[TAPS]; ///< Audio buffer end points float *wp[TAPS]; ///< Write pointers float *in_l; float *in_r; float *out_l; float *out_r; }; static LV2_Handle ptap_instantiate( const LV2_Descriptor *descriptor, double sample_rate, const char *bundle_path, const LV2_Feature *const *host_features) { /* Allocate local data */ PTap *ptap = new PTap(); ptap->sample_rate = sample_rate; ptap->buffer_max = ptap->sample_rate * BUFFER_SECONDS + 1; for (int i = 0; i < TAPS; i++) { ptap->buffers[i] = new float[ptap->buffer_max](); if (ptap->buffers[i] == NULL) { for (int j = 0; j < i; j++) { delete ptap->buffers[i]; } delete ptap; printf("Failed to allocate buffers\n"); return NULL; } ptap->buffers_end[i] = ptap->buffers[i] + ptap->buffer_max; ptap->wp[i] = ptap->buffers[i]; } return (LV2_Handle)ptap; } static void ptap_connect_port(LV2_Handle lv2instance, uint32_t port, void *data) { PTap *ptap = (PTap *)lv2instance; Tap *tap; float *fdata = (float *)data; /* Audio ports */ if (port < 4) { switch (port) { case 0: ptap->in_l = fdata; break; case 1: ptap->in_r = fdata; break; case 2: ptap->out_l = fdata; break; case 3: ptap->out_r = fdata; break; } return; } port -= 4; int tap_index = port / CONTROLS_PER_TAP; if (tap_index < TAPS + CHANNELS) { tap = &ptap->tap[tap_index]; } else { return; } int tap_port = port % CONTROLS_PER_TAP; if (tap_port < TAPS + CHANNELS) { tap->t_gain[tap_port] = fdata; } else if (tap_port - TAPS == 2) { tap->gain = fdata; } else if (tap_port - TAPS == 3) { tap->delay = fdata; } else { return; } } static void ptap_run(LV2_Handle lv2instance, uint32_t sample_count) { PTap *ptap = (PTap *)lv2instance; const float *readp[TAPS + CHANNELS]; float gain[TAPS + CHANNELS]; bool check_overflow = false; /* Position read pointers behind write pointers */ for (int i = 0; i < TAPS; i++) { int delay = *ptap->tap[i].delay * ptap->sample_rate; /* Zero delay results in processing order dependencies. Tapiir doesn't permit it either... */ if (delay < 1) delay = 1; readp[i] = ptap->wp[i] - delay; if (readp[i] < ptap->buffers[i]) { readp[i] += ptap->buffer_max; } if (ptap->wp[i] + sample_count >= ptap->buffers_end[i]) check_overflow = true; if (readp[i] + sample_count >= ptap->buffers_end[i]) check_overflow = true; gain[i] = *ptap->tap[i].gain; } float *out_l = ptap->out_l; float *out_r = ptap->out_r; readp[TAPS ] = ptap->in_l; readp[TAPS + 1] = ptap->in_r; gain[TAPS ] = *ptap->tap[TAPS ].gain; gain[TAPS + 1] = *ptap->tap[TAPS + 1].gain; while (sample_count--) { float rp[TAPS + CHANNELS]; float wp[TAPS + CHANNELS]; /* Read input to local buffers */ for (int i = 0; i < TAPS + CHANNELS; i++) rp[i] = *readp[i]++; /* Process delays */ for (int i = 0; i < TAPS + CHANNELS; i++) { const Tap *tap = &ptap->tap[i]; float sample = 0; for (int j = 0; j < TAPS + CHANNELS; j++) sample += rp[j] * *tap->t_gain[j]; wp[i] = sample * gain[i]; } /* Write outputs */ for (int i = 0; i < TAPS; i++) *ptap->wp[i]++ = wp[i]; *out_l++ = wp[TAPS]; *out_r++ = wp[TAPS + 1]; /* Check read/write pointers */ if (check_overflow) { for (int i = 0; i < TAPS; i++) { if (ptap->wp[i] >= ptap->buffers_end[i]) ptap->wp[i] = ptap->buffers[i]; if (readp[i] >= ptap->buffers_end[i]) readp[i] = ptap->buffers[i]; } } } } static void ptap_cleanup(LV2_Handle lv2instance) { PTap *ptap = (PTap *)lv2instance; for (int i = 0; i < TAPS; i++) { delete ptap->buffers[i]; } delete ptap; } static const LV2_Descriptor s_lv2descriptor = { "urn:fuzzle:ptap", &ptap_instantiate, &ptap_connect_port, NULL, &ptap_run, NULL, &ptap_cleanup, NULL, }; const LV2_Descriptor *lv2_descriptor(uint32_t index) { if (index == 0) { return &s_lv2descriptor; } return NULL; }