├── README.md └── source ├── Makefile ├── Makefile.mk ├── cabsim-IR-loader.c ├── cabsim-IR-loader.lv2 ├── cabsim-IR-loader.ttl ├── forward-audio_AliceInBones.wav ├── manifest.ttl ├── modgui.ttl └── modgui │ ├── bg │ ├── cabinet-back-bottom.png │ ├── cabinet-back-middle.png │ ├── cabinet-back-top.png │ └── cabinet-face-silver.png │ ├── icon-cabsim-IR-loader.html │ ├── knobs │ └── knob.png │ ├── mod_cabsim_IR_loader_ss.png │ ├── mod_cabsim_IR_loader_tn.png │ ├── stylesheet-cabsim-IR-loader.css │ └── switches │ └── switch-cabinet.png ├── circular_buffer.c ├── circular_buffer.h └── uris.h /README.md: -------------------------------------------------------------------------------- 1 | # mod-cabsim-IR-loader 2 | 3 | An LV2 cabinet simulator plugin that loads impulse response (IR) files. 4 | 5 | This plugin is specifically created for handling speaker cabinet IRs, 6 | this plugin is not optimized for handling larger files like reverb IRs. 7 | 8 | Currently it only uses the first 42.7 ms (2048 samples at 48 kHz sampling rate) of the loaded IR file. 9 | IR files at different sample rates are resampled to 48 kHz by the plugin. 10 | It is recommended to trim any silence at the start of the IR file for optimal results. 11 | 12 | Default IR file provided by forward audio. 13 | -------------------------------------------------------------------------------- /source/Makefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # Makefile for TEMPLATE_PLUGIN.lv2 # 3 | # --------------------------------- # 4 | 5 | include Makefile.mk 6 | 7 | NAME = cabsim-IR-loader 8 | 9 | PREFIX ?= /usr/local 10 | 11 | # -------------------------------------------------------------- 12 | # Default target is to build all plugins 13 | 14 | all: build 15 | build: $(NAME)-build 16 | 17 | # -------------------------------------------------------------- 18 | # Build rules 19 | 20 | $(NAME)-build: $(NAME).lv2/$(NAME)$(LIB_EXT) 21 | 22 | $(NAME).lv2/$(NAME)$(LIB_EXT): $(NAME).c circular_buffer.c 23 | $(CC) $^ $(BUILD_C_FLAGS) $(LINK_FLAGS) -lm $(SHARED) -o $@ 24 | 25 | # -------------------------------------------------------------- 26 | 27 | clean: 28 | rm -f $(NAME).lv2/$(NAME)$(LIB_EXT) 29 | 30 | # -------------------------------------------------------------- 31 | 32 | install: build 33 | install -d $(DESTDIR)$(PREFIX)/lib/lv2/$(NAME).lv2 34 | 35 | install -m 644 $(NAME).lv2/*.so $(DESTDIR)$(PREFIX)/lib/lv2/$(NAME).lv2/ 36 | install -m 644 $(NAME).lv2/*.ttl $(DESTDIR)$(PREFIX)/lib/lv2/$(NAME).lv2/ 37 | install -m 644 $(NAME).lv2/*.wav $(DESTDIR)$(PREFIX)/lib/lv2/$(NAME).lv2/ 38 | 39 | cp -r $(NAME).lv2/modgui $(DESTDIR)$(PREFIX)/lib/lv2/$(NAME).lv2/ 40 | 41 | # -------------------------------------------------------------- 42 | 43 | -------------------------------------------------------------------------------- /source/Makefile.mk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # Makefile for cabsim-impulse-loader.lv2 # 3 | # ----------------------- # 4 | # 5 | 6 | AR ?= ar 7 | CC ?= gcc 8 | CXX ?= g++ 9 | 10 | # -------------------------------------------------------------- 11 | # Fallback to Linux if no other OS defined 12 | 13 | ifneq ($(MACOS),true) 14 | ifneq ($(WIN32),true) 15 | LINUX=true 16 | endif 17 | endif 18 | 19 | # -------------------------------------------------------------- 20 | # Set build and link flags 21 | 22 | BASE_FLAGS = -Wall -Wextra -pipe -Wno-unused-parameter 23 | BASE_OPTS = -O3 -ffast-math 24 | BASE_OPTS += -flto -ffat-lto-objects 25 | BASE_OPTS += -fdata-sections -ffunction-sections 26 | 27 | ifeq ($(MACOS),true) 28 | # MacOS linker flags 29 | LINK_OPTS = -Wl,-dead_strip,-dead_strip_dylibs 30 | else 31 | # Common linker flags 32 | LINK_OPTS = -Wl,-O1,--as-needed,--strip-all 33 | LINK_OPTS = -Wl,--gc-sections 34 | endif 35 | 36 | ifneq ($(WIN32),true) 37 | # not needed for Windows 38 | BASE_FLAGS += -fPIC -DPIC 39 | endif 40 | 41 | ifeq ($(DEBUG),true) 42 | BASE_FLAGS += -DDEBUG -O0 -g 43 | LINK_OPTS = 44 | else 45 | BASE_FLAGS += -DNDEBUG $(BASE_OPTS) -fvisibility=hidden 46 | CXXFLAGS += -fvisibility-inlines-hidden 47 | endif 48 | 49 | LINK_OPTS += $(shell pkg-config --libs sndfile fftw3f samplerate) 50 | 51 | BUILD_C_FLAGS = $(BASE_FLAGS) -std=c99 -std=gnu99 $(CFLAGS) 52 | BUILD_CXX_FLAGS = $(BASE_FLAGS) -std=c++11 $(CXXFLAGS) $(CPPFLAGS) 53 | 54 | ifeq ($(MACOS),true) 55 | # 'no-undefined' is always enabled on MacOS 56 | LINK_FLAGS = $(LINK_OPTS) $(LDFLAGS) 57 | else 58 | # add 'no-undefined' 59 | LINK_FLAGS = $(LINK_OPTS) -Wl,--no-undefined $(LDFLAGS) -lsndfile $(shell pkg-config --libs fftw3f) -lsamplerate 60 | endif 61 | 62 | # -------------------------------------------------------------- 63 | # Set shared lib extension 64 | 65 | LIB_EXT = .so 66 | 67 | ifeq ($(MACOS),true) 68 | LIB_EXT = .dylib 69 | endif 70 | 71 | ifeq ($(WIN32),true) 72 | LIB_EXT = .dll 73 | endif 74 | 75 | # -------------------------------------------------------------- 76 | # Set shared library CLI arg 77 | 78 | SHARED = -shared 79 | 80 | ifeq ($(MACOS),true) 81 | SHARED = -dynamiclib 82 | endif 83 | 84 | # -------------------------------------------------------------- 85 | -------------------------------------------------------------------------------- /source/cabsim-IR-loader.c: -------------------------------------------------------------------------------- 1 | /* 2 | The file loading is using code from the LV2 Sampler Example Plugin 3 | Copyright 2011-2012 David Robillard 4 | Copyright 2011 Gabriel M. Beddingfield 5 | Copyright 2011 James Morris 6 | 7 | Resampler function is taken from https://github.com/cpuimage/resampler/ 8 | Copyright (c) 2019 Zhihan Gao 9 | 10 | Permission to use, copy, modify, and/or distribute this software for any 11 | purpose with or without fee is hereby granted, provided that the above 12 | copyright notice and this permission notice appear in all copies. 13 | 14 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 15 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 17 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 20 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "fftw3.h" 29 | #include 30 | #include 31 | #include 32 | 33 | #ifndef __cplusplus 34 | # include 35 | #endif 36 | 37 | #include 38 | 39 | #include "lv2/lv2plug.in/ns/ext/atom/forge.h" 40 | #include "lv2/lv2plug.in/ns/ext/atom/util.h" 41 | #include "lv2/lv2plug.in/ns/ext/log/log.h" 42 | #include "lv2/lv2plug.in/ns/ext/log/logger.h" 43 | #include "lv2/lv2plug.in/ns/ext/midi/midi.h" 44 | #include "lv2/lv2plug.in/ns/ext/patch/patch.h" 45 | #include "lv2/lv2plug.in/ns/ext/state/state.h" 46 | #include "lv2/lv2plug.in/ns/ext/urid/urid.h" 47 | #include "lv2/lv2plug.in/ns/ext/worker/worker.h" 48 | #include "lv2/lv2plug.in/ns/lv2core/lv2.h" 49 | 50 | #include "./uris.h" 51 | #include "./circular_buffer.h" 52 | 53 | #define REAL 0 54 | #define IMAG 1 55 | 56 | #define MAX_FFT_SIZE 2048 57 | 58 | //macro for Volume in DB to a coefficient 59 | #define DB_CO(g) ((g) > -90.0f ? powf(10.0f, (g) * 0.05f) : 0.0f) 60 | 61 | enum { 62 | CABSIM_CONTROL = 0, 63 | CABSIM_NOTIFY = 1, 64 | CABSIM_IN = 2, 65 | CABSIM_OUT = 3, 66 | ATTENUATE = 4 67 | }; 68 | 69 | //static const char* default_sample_file = "Orange_PPC412_V30_412_C_Hi-Gn_121+57_Celestion.wav"; 70 | 71 | typedef struct { 72 | SF_INFO info; // Info about sample from sndfile 73 | float* data; // ImpulseResponse data in float 74 | char* path; // Path of file 75 | uint32_t path_len; // Length of path 76 | } ImpulseResponse; 77 | 78 | typedef struct { 79 | // Features 80 | LV2_URID_Map* map; 81 | LV2_Worker_Schedule* schedule; 82 | LV2_Log_Log* log; 83 | 84 | // Forge for creating atoms 85 | LV2_Atom_Forge forge; 86 | 87 | // Logger convenience API 88 | LV2_Log_Logger logger; 89 | 90 | // ImpulseResponse 91 | ImpulseResponse* ir; 92 | 93 | // Ports 94 | const LV2_Atom_Sequence* control_port; 95 | LV2_Atom_Sequence* notify_port; 96 | float* output_port; 97 | float* input_port; 98 | 99 | // Forge frame for notify port (for writing worker replies) 100 | LV2_Atom_Forge_Frame notify_frame; 101 | 102 | // URIs 103 | CabsimURIs uris; 104 | 105 | // Current position in run() 106 | uint32_t frame_offset; 107 | 108 | // Playback state 109 | sf_count_t frame; 110 | bool play; 111 | bool new_ir; 112 | bool ir_loaded; 113 | 114 | double samplerate; 115 | 116 | //CABSIM ======================================== 117 | 118 | float *outbuf; 119 | float *inbuf; 120 | float *IR; 121 | 122 | uint32_t prev_buffer_size; 123 | const float *attenuation; 124 | uint32_t valid_IR_data_length; 125 | 126 | fftwf_complex *outComplex; 127 | fftwf_complex *IRout; 128 | fftwf_complex *convolved; 129 | 130 | fftwf_plan fft; 131 | fftwf_plan ifft; 132 | fftwf_plan IRfft; 133 | 134 | int overlap_add_buffers; 135 | ringbuffer_t overlap_buffer; 136 | } Cabsim; 137 | 138 | typedef struct { 139 | LV2_Atom atom; 140 | ImpulseResponse* ir; 141 | } ImpulseResponseMessage; 142 | 143 | static uint64_t 144 | Resample_f32(const float *input, float *output, int inSampleRate, 145 | int outSampleRate, uint64_t inputSize, uint32_t channels) 146 | { 147 | if (input == NULL) 148 | return 0; 149 | uint64_t outputSize = inputSize * outSampleRate / inSampleRate; 150 | if (output == NULL) 151 | return outputSize; 152 | double stepDist = ((double) inSampleRate / (double) outSampleRate); 153 | const uint64_t fixedFraction = (1LL << 32); 154 | const double normFixed = (1.0 / (1LL << 32)); 155 | uint64_t step = ((uint64_t) (stepDist * fixedFraction + 0.5)); 156 | uint64_t curOffset = 0; 157 | for (uint32_t i = 0; i < outputSize; i += 1) { 158 | for (uint32_t c = 0; c < channels; c += 1) { 159 | *output++ = (float) (input[c] + (input[c + channels] - input[c]) * ( 160 | (double) (curOffset >> 32) + ((curOffset & (fixedFraction - 1)) * normFixed))); 161 | } 162 | curOffset += step; 163 | input += (curOffset >> 32) * channels; 164 | curOffset &= (fixedFraction - 1); 165 | } 166 | return outputSize; 167 | } 168 | 169 | static sf_count_t 170 | convert_to_mono(float *data, sf_count_t num_input_frames, uint32_t channels) 171 | { 172 | sf_count_t mono_index = 0; 173 | for (sf_count_t i = 0; i < num_input_frames * channels; i+=channels) { 174 | data[mono_index++] = data[i]; 175 | } 176 | 177 | sf_count_t num_output_frames = mono_index; 178 | 179 | return num_output_frames; 180 | } 181 | 182 | /** 183 | Load a new ir and return it. 184 | 185 | Since this is of course not a real-time safe action, this is called in the 186 | worker thread only. The ir is loaded and returned only, plugin state is 187 | not modified. 188 | */ 189 | static ImpulseResponse* 190 | load_ir(Cabsim* self, const char* path, uint32_t path_len) 191 | { 192 | char* irpath = (char*)malloc(path_len + 1); 193 | memcpy(irpath, path, path_len); 194 | irpath[path_len] = 0; 195 | 196 | lv2_log_trace(&self->logger, "Loading ir %s\n", irpath); 197 | 198 | ImpulseResponse* const ir = (ImpulseResponse*)calloc(1, sizeof(ImpulseResponse)); 199 | SF_INFO* const info = &ir->info; 200 | SNDFILE* const sndfile = sf_open(irpath, SFM_READ, info); 201 | 202 | if (!sndfile || !info->frames) { 203 | lv2_log_error(&self->logger, "Failed to open ir '%s'\n", irpath); 204 | free(ir); 205 | return NULL; 206 | } 207 | 208 | // Read data 209 | float* const data = malloc(sizeof(float) * (info->frames * info->channels)); 210 | if (!data) { 211 | lv2_log_error(&self->logger, "Failed to allocate memory for ir\n"); 212 | return NULL; 213 | } 214 | sf_seek(sndfile, 0ul, SEEK_SET); 215 | sf_read_float(sndfile, data, info->frames * info->channels); 216 | sf_close(sndfile); 217 | 218 | //When IR has multiple channels, only use first channel 219 | if (info->channels != 1) { 220 | info->frames = convert_to_mono(data, info->frames, info->channels); 221 | info->channels = 1; 222 | } 223 | 224 | //apply samplerate conversion if needed 225 | if (info->samplerate == (int)self->samplerate) { 226 | ir->data = data; 227 | } else { 228 | uint64_t targetSampleCount = Resample_f32(data, 0, info->samplerate, (int)self->samplerate, (uint64_t)info->frames, 1); 229 | float* const resampled_data = malloc(targetSampleCount * sizeof(float)); 230 | info->frames = Resample_f32(data, resampled_data, info->samplerate, (int)self->samplerate, (uint64_t)info->frames, 1); 231 | free(data); 232 | ir->data = resampled_data; 233 | } 234 | 235 | // Fill ir struct and return it 236 | ir->path = irpath; 237 | ir->path_len = path_len; 238 | return ir; 239 | } 240 | 241 | static void 242 | free_ir(Cabsim* self, ImpulseResponse* ir) 243 | { 244 | if (ir) { 245 | lv2_log_trace(&self->logger, "Freeing %s\n", ir->path); 246 | free(ir->path); 247 | free(ir->data); 248 | free(ir); 249 | } 250 | } 251 | 252 | /** 253 | Do work in a non-realtime thread. 254 | 255 | This is called for every piece of work scheduled in the audio thread using 256 | self->schedule->schedule_work(). A reply can be sent back to the audio 257 | thread using the provided respond function. 258 | */ 259 | static LV2_Worker_Status 260 | work(LV2_Handle instance, 261 | LV2_Worker_Respond_Function respond, 262 | LV2_Worker_Respond_Handle handle, 263 | uint32_t size, 264 | const void* data) 265 | { 266 | Cabsim* self = (Cabsim*)instance; 267 | const LV2_Atom* atom = (const LV2_Atom*)data; 268 | if (atom->type == self->uris.cab_freeImpulseResponse) { 269 | // Free old ir 270 | const ImpulseResponseMessage* msg = (const ImpulseResponseMessage*)data; 271 | free_ir(self, msg->ir); 272 | } else { 273 | // Handle set message (load ir). 274 | const LV2_Atom_Object* obj = (const LV2_Atom_Object*)data; 275 | 276 | // Get file path from message 277 | const LV2_Atom* file_path = read_set_file(&self->uris, obj); 278 | if (!file_path) { 279 | return LV2_WORKER_ERR_UNKNOWN; 280 | } 281 | 282 | // Load ir. 283 | ImpulseResponse* ir = load_ir(self, LV2_ATOM_BODY_CONST(file_path), file_path->size); 284 | if (ir) { 285 | // Loaded ir, send it to run() to be applied. 286 | respond(handle, sizeof(ir), &ir); 287 | } 288 | } 289 | 290 | return LV2_WORKER_SUCCESS; 291 | } 292 | 293 | /** 294 | Handle a response from work() in the audio thread. 295 | 296 | When running normally, this will be called by the host after run(). When 297 | freewheeling, this will be called immediately at the point the work was 298 | scheduled. 299 | */ 300 | static LV2_Worker_Status 301 | work_response(LV2_Handle instance, 302 | uint32_t size, 303 | const void* data) 304 | { 305 | Cabsim* self = (Cabsim*)instance; 306 | 307 | ImpulseResponseMessage msg = { { sizeof(ImpulseResponse*), self->uris.cab_freeImpulseResponse }, 308 | self->ir }; 309 | 310 | // Send a message to the worker to free the current ir 311 | self->schedule->schedule_work(self->schedule->handle, sizeof(msg), &msg); 312 | 313 | // Install the new ir 314 | self->ir = *(ImpulseResponse*const*)data; 315 | 316 | self->new_ir = true; 317 | self->ir_loaded = false; 318 | 319 | return LV2_WORKER_SUCCESS; 320 | } 321 | 322 | static void 323 | connect_port(LV2_Handle instance, 324 | uint32_t port, 325 | void* data) 326 | { 327 | Cabsim* self = (Cabsim*)instance; 328 | switch (port) { 329 | case CABSIM_CONTROL: 330 | self->control_port = (const LV2_Atom_Sequence*)data; 331 | break; 332 | case CABSIM_NOTIFY: 333 | self->notify_port = (LV2_Atom_Sequence*)data; 334 | break; 335 | case CABSIM_IN: 336 | self->input_port = (float*)data; 337 | break; 338 | case CABSIM_OUT: 339 | self->output_port = (float*)data; 340 | break; 341 | case ATTENUATE: 342 | self->attenuation = (const float*) data; 343 | break; 344 | default: 345 | break; 346 | } 347 | } 348 | 349 | static LV2_Handle 350 | instantiate(const LV2_Descriptor* descriptor, 351 | double rate, 352 | const char* path, 353 | const LV2_Feature* const* features) 354 | { 355 | // Allocate and initialise instance structure. 356 | Cabsim* self = (Cabsim*)calloc(1, sizeof(Cabsim)); 357 | if (!self) { 358 | return NULL; 359 | } 360 | 361 | self->samplerate = rate; 362 | // Get host features 363 | for (int i = 0; features[i]; ++i) { 364 | if (!strcmp(features[i]->URI, LV2_URID__map)) { 365 | self->map = (LV2_URID_Map*)features[i]->data; 366 | } else if (!strcmp(features[i]->URI, LV2_WORKER__schedule)) { 367 | self->schedule = (LV2_Worker_Schedule*)features[i]->data; 368 | } else if (!strcmp(features[i]->URI, LV2_LOG__log)) { 369 | self->log = (LV2_Log_Log*)features[i]->data; 370 | } 371 | } 372 | if (!self->map) { 373 | lv2_log_error(&self->logger, "Missing feature urid:map\n"); 374 | goto fail; 375 | } else if (!self->schedule) { 376 | lv2_log_error(&self->logger, "Missing feature work:schedule\n"); 377 | goto fail; 378 | } 379 | 380 | // Map URIs and initialise forge/logger 381 | map_cabsim_uris(self->map, &self->uris); 382 | lv2_atom_forge_init(&self->forge, self->map); 383 | lv2_log_logger_init(&self->logger, self->map, self->log); 384 | 385 | self->outComplex = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex)*(MAX_FFT_SIZE)); 386 | self->IRout = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex)*(MAX_FFT_SIZE)); 387 | self->convolved = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex)*(MAX_FFT_SIZE)); 388 | self->outbuf = (float *) calloc((MAX_FFT_SIZE),sizeof(float)); 389 | self->inbuf = (float *) calloc((MAX_FFT_SIZE),sizeof(float)); 390 | self->IR = (float *) calloc((MAX_FFT_SIZE),sizeof(float)); 391 | 392 | if (fftwf_import_system_wisdom() != 0) 393 | { 394 | self->fft = fftwf_plan_dft_r2c_1d(MAX_FFT_SIZE, self->inbuf, self->outComplex, FFTW_WISDOM_ONLY|FFTW_ESTIMATE); 395 | self->IRfft = fftwf_plan_dft_r2c_1d(MAX_FFT_SIZE, self->IR, self->IRout, FFTW_WISDOM_ONLY|FFTW_ESTIMATE); 396 | self->ifft = fftwf_plan_dft_c2r_1d(MAX_FFT_SIZE, self->convolved, self->outbuf, FFTW_WISDOM_ONLY|FFTW_ESTIMATE); 397 | lv2_log_note(&self->logger, "wisdom file loaded from system\n"); 398 | } else { 399 | self->fft = fftwf_plan_dft_r2c_1d(MAX_FFT_SIZE, self->inbuf, self->outComplex, FFTW_ESTIMATE); 400 | self->IRfft = fftwf_plan_dft_r2c_1d(MAX_FFT_SIZE, self->IR, self->IRout, FFTW_ESTIMATE); 401 | self->ifft = fftwf_plan_dft_c2r_1d(MAX_FFT_SIZE, self->convolved, self->outbuf, FFTW_ESTIMATE); 402 | lv2_log_warning(&self->logger, "failed to import system wisdom file\n"); 403 | } 404 | 405 | self->new_ir = false; 406 | self->ir_loaded = false; 407 | self->prev_buffer_size = UINT32_MAX; 408 | 409 | for (int i = 0; i < MAX_FFT_SIZE; i++) 410 | self->inbuf[i] = 0.0f; 411 | 412 | return (LV2_Handle)self; 413 | 414 | fail: 415 | free(self); 416 | return 0; 417 | } 418 | 419 | static void 420 | cleanup(LV2_Handle instance) 421 | { 422 | Cabsim* self = (Cabsim*)instance; 423 | 424 | fftwf_destroy_plan(self->fft); 425 | fftwf_destroy_plan(self->IRfft); 426 | fftwf_destroy_plan(self->ifft); 427 | fftwf_free(self->outComplex); 428 | fftwf_free(self->IRout); 429 | fftwf_free(self->convolved); 430 | free(self->outbuf); 431 | free(self->inbuf); 432 | free(self->IR); 433 | free_ir(self, self->ir); 434 | free(self); 435 | } 436 | 437 | /** Define a macro for converting a gain in dB to a coefficient. */ 438 | #define DB_CO(g) ((g) > -90.0f ? powf(10.0f, (g) * 0.05f) : 0.0f) 439 | 440 | static void 441 | run(LV2_Handle instance, 442 | uint32_t n_frames) 443 | { 444 | Cabsim* self = (Cabsim*)instance; 445 | CabsimURIs* uris = &self->uris; 446 | float* input = self->input_port; 447 | float* output = self->output_port; 448 | 449 | float *outbuf = self->outbuf; 450 | float *inbuf = self->inbuf; 451 | float *IR = self->IR; 452 | 453 | if (n_frames > MAX_FFT_SIZE) { 454 | // unsupported 455 | memset(output, 0, sizeof(float)*n_frames); 456 | return; 457 | } 458 | 459 | // Set up forge to write directly to notify output port. 460 | const uint32_t notify_capacity = self->notify_port->atom.size; 461 | lv2_atom_forge_set_buffer(&self->forge, 462 | (uint8_t*)self->notify_port, 463 | notify_capacity); 464 | 465 | // Start a sequence in the notify output port. 466 | lv2_atom_forge_sequence_head(&self->forge, &self->notify_frame, 0); 467 | 468 | // Read incoming events 469 | LV2_ATOM_SEQUENCE_FOREACH(self->control_port, ev) { 470 | self->frame_offset = ev->time.frames; 471 | if (lv2_atom_forge_is_object_type(&self->forge, ev->body.type)) { 472 | const LV2_Atom_Object* obj = (const LV2_Atom_Object*)&ev->body; 473 | if (obj->body.otype == uris->patch_Set) { 474 | // Get the property and value of the set message 475 | const LV2_Atom* property = NULL; 476 | const LV2_Atom* value = NULL; 477 | lv2_atom_object_get(obj, 478 | uris->patch_property, &property, 479 | uris->patch_value, &value, 480 | 0); 481 | if (!property) { 482 | lv2_log_error(&self->logger, 483 | "patch:Set message with no property\n"); 484 | continue; 485 | } else if (property->type != uris->atom_URID) { 486 | lv2_log_error(&self->logger, 487 | "patch:Set property is not a URID\n"); 488 | continue; 489 | } 490 | 491 | const uint32_t key = ((const LV2_Atom_URID*)property)->body; 492 | if (key == uris->cab_ir) { 493 | // ImpulseResponse change, send it to the worker. 494 | lv2_log_trace(&self->logger, "Queueing set message\n"); 495 | self->schedule->schedule_work(self->schedule->handle, 496 | lv2_atom_total_size(&ev->body), 497 | &ev->body); 498 | } 499 | } else { 500 | lv2_log_trace(&self->logger, 501 | "Unknown object type %d\n", obj->body.otype); 502 | } 503 | } else { 504 | lv2_log_trace(&self->logger, 505 | "Unknown event type %d\n", ev->body.type); 506 | } 507 | } 508 | 509 | // CABSIM ================================================================= 510 | 511 | const float attenuation = *self->attenuation; 512 | 513 | const float coef = DB_CO(attenuation); 514 | 515 | uint32_t i, j, m; 516 | 517 | if (n_frames != self->prev_buffer_size) { 518 | switch (n_frames) { 519 | case 64: 520 | self->overlap_add_buffers = 32; 521 | break; 522 | case 128: 523 | self->overlap_add_buffers = 16; 524 | break; 525 | case 256: 526 | self->overlap_add_buffers = 8; 527 | break; 528 | case 512: 529 | self->overlap_add_buffers = 4; 530 | break; 531 | default: 532 | lv2_log_warning(&self->logger, "Non standard buffer size: '%i'. alliasing may happen\n", n_frames); 533 | 534 | self->overlap_add_buffers = 16; 535 | break; 536 | } 537 | 538 | self->prev_buffer_size = n_frames; 539 | 540 | //reset ringbuffer 541 | //max ringbuffer size is defined in ringbuffer.h, it should always be > max_overlap_add_buffers * MAX_FFT_SZIE 542 | ringbuffer_clear(&self->overlap_buffer, self->overlap_add_buffers * MAX_FFT_SIZE); 543 | 544 | memset(self->outbuf, 0, MAX_FFT_SIZE*sizeof(float)); 545 | memset(self->inbuf, 0, MAX_FFT_SIZE*sizeof(float)); 546 | } 547 | 548 | //copy inputbuffer and IR buffer with zero padding. 549 | if (self->new_ir) 550 | { 551 | self->valid_IR_data_length = (MAX_FFT_SIZE - n_frames) < self->ir->info.frames ? 552 | (MAX_FFT_SIZE - n_frames) : self->ir->info.frames; 553 | memcpy(IR, self->ir->data, self->valid_IR_data_length*sizeof(float)); 554 | memset(IR+self->valid_IR_data_length, 0, (MAX_FFT_SIZE - self->valid_IR_data_length)*sizeof(float)); 555 | 556 | fftwf_execute(self->IRfft); 557 | 558 | lv2_log_trace(&self->logger, "Responding to get request\n"); 559 | lv2_atom_forge_frame_time(&self->forge, self->frame_offset); 560 | write_set_file(&self->forge, &self->uris, 561 | self->ir->path, 562 | self->ir->path_len); 563 | 564 | self->new_ir = false; 565 | self->ir_loaded = true; 566 | 567 | ringbuffer_clear(&self->overlap_buffer, self->overlap_add_buffers * MAX_FFT_SIZE); 568 | 569 | memset(self->outbuf, 0, MAX_FFT_SIZE*sizeof(float)); 570 | memset(self->inbuf, 0, MAX_FFT_SIZE*sizeof(float)); 571 | } 572 | 573 | for (i = 0; i < n_frames; i++) 574 | inbuf[i] = input[i] * coef * 0.2f; 575 | 576 | fftwf_execute(self->fft); 577 | 578 | if (self->ir_loaded) { 579 | 580 | //complex multiplication 581 | for (m = 0; m < self->valid_IR_data_length; m++) { 582 | self->convolved[m][REAL] = self->outComplex[m][REAL] * self->IRout[m][REAL] - self->outComplex[m][IMAG] * self->IRout[m][IMAG]; 583 | self->convolved[m][IMAG] = self->outComplex[m][REAL] * self->IRout[m][IMAG] + self->outComplex[m][IMAG] * self->IRout[m][REAL]; 584 | } 585 | memset(self->convolved+m, 0, (MAX_FFT_SIZE-m)*sizeof(fftwf_complex)); 586 | 587 | fftwf_execute(self->ifft); 588 | 589 | for (j = 0; j < MAX_FFT_SIZE; j++) 590 | ringbuffer_push_sample(&self->overlap_buffer , outbuf[j] / MAX_FFT_SIZE); 591 | 592 | //normalize output with overlap add. 593 | for (j = 0; j < n_frames; j++) { 594 | float overlap_value = 0.0f; 595 | 596 | for (uint8_t Oa = 0; Oa < self->overlap_add_buffers-1; Oa++) 597 | overlap_value += ringbuffer_get_relative_val(&self->overlap_buffer, (Oa * MAX_FFT_SIZE) + ((self->overlap_add_buffers - Oa - 1) * n_frames) + j + 1); 598 | 599 | output[j] = ((outbuf[j] / MAX_FFT_SIZE) + overlap_value); 600 | } 601 | } else { 602 | memset(output, 0, sizeof(float)*n_frames); 603 | } 604 | } 605 | 606 | static LV2_State_Status 607 | save(LV2_Handle instance, 608 | LV2_State_Store_Function store, 609 | LV2_State_Handle handle, 610 | uint32_t flags, 611 | const LV2_Feature* const* features) 612 | { 613 | Cabsim* self = (Cabsim*)instance; 614 | 615 | if (!self->ir) { 616 | return LV2_STATE_SUCCESS; 617 | } 618 | 619 | LV2_State_Map_Path* map_path = NULL; 620 | for (int i = 0; features[i]; ++i) { 621 | if (!strcmp(features[i]->URI, LV2_STATE__mapPath)) { 622 | map_path = (LV2_State_Map_Path*)features[i]->data; 623 | } 624 | } 625 | 626 | if (map_path) { 627 | char* apath = map_path->abstract_path(map_path->handle, self->ir->path); 628 | store(handle, 629 | self->uris.cab_ir, 630 | apath, 631 | strlen(self->ir->path) + 1, 632 | self->uris.atom_Path, 633 | LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); 634 | free(apath); 635 | return LV2_STATE_SUCCESS; 636 | } else { 637 | return LV2_STATE_ERR_NO_FEATURE; 638 | } 639 | } 640 | 641 | static LV2_State_Status 642 | restore(LV2_Handle instance, 643 | LV2_State_Retrieve_Function retrieve, 644 | LV2_State_Handle handle, 645 | uint32_t flags, 646 | const LV2_Feature* const* features) 647 | { 648 | Cabsim* self = (Cabsim*)instance; 649 | 650 | size_t size; 651 | uint32_t type; 652 | uint32_t valflags; 653 | 654 | const void* value = retrieve( 655 | handle, 656 | self->uris.cab_ir, 657 | &size, &type, &valflags); 658 | 659 | if (value) { 660 | const char* path = (const char*)value; 661 | ImpulseResponse *ir = load_ir(self, path, size); 662 | if (ir) { 663 | lv2_log_trace(&self->logger, "Restoring file %s\n", path); 664 | free_ir(self, self->ir); 665 | self->ir = ir; 666 | self->new_ir = true; 667 | } else { 668 | lv2_log_error(&self->logger, "File %s couldn't be loaded\n", path); 669 | return LV2_STATE_ERR_UNKNOWN; 670 | } 671 | } 672 | 673 | return LV2_STATE_SUCCESS; 674 | } 675 | 676 | static const void* 677 | extension_data(const char* uri) 678 | { 679 | static const LV2_State_Interface state = { save, restore }; 680 | static const LV2_Worker_Interface worker = { work, work_response, NULL }; 681 | if (!strcmp(uri, LV2_STATE__interface)) { 682 | return &state; 683 | } else if (!strcmp(uri, LV2_WORKER__interface)) { 684 | return &worker; 685 | } 686 | return NULL; 687 | } 688 | 689 | static const LV2_Descriptor descriptor = { 690 | CABSIM_URI, 691 | instantiate, 692 | connect_port, 693 | NULL, // activate, 694 | run, 695 | NULL, // deactivate, 696 | cleanup, 697 | extension_data 698 | }; 699 | 700 | LV2_SYMBOL_EXPORT 701 | const LV2_Descriptor* lv2_descriptor(uint32_t index) 702 | { 703 | switch (index) { 704 | case 0: 705 | return &descriptor; 706 | default: 707 | return NULL; 708 | } 709 | } 710 | -------------------------------------------------------------------------------- /source/cabsim-IR-loader.lv2/cabsim-IR-loader.ttl: -------------------------------------------------------------------------------- 1 | @prefix atom: . 2 | @prefix doap: . 3 | @prefix lv2: . 4 | @prefix patch: . 5 | @prefix rdfs: . 6 | @prefix state: . 7 | @prefix urid: . 8 | @prefix work: . 9 | @prefix param: . 10 | @prefix foaf: . 11 | @prefix mod: . 12 | @prefix bsize: . 13 | @prefix units: . 14 | 15 | 16 | a lv2:Parameter ; 17 | mod:fileTypes "cabsim" ; 18 | rdfs:label "Impulse Response" ; 19 | rdfs:range atom:Path . 20 | 21 | 22 | a lv2:Plugin, lv2:SimulatorPlugin; 23 | doap:name "IR loader cabsim"; 24 | lv2:optionalFeature lv2:hardRTCapable; 25 | lv2:requiredFeature bsize:powerOf2BlockLength; 26 | 27 | doap:license "GPL"; 28 | 29 | rdfs:comment """ 30 | A cabinet simulator plugin that loads impulse response (IR) files. 31 | 32 | In order for your personal IR files to show up in the list of available files for this plugin, please place the files in the “Speaker Cabinet IRs” folder of your device. 33 | 34 | This plugin is specifically created for handling speaker cabinet IRs, this plugin is not optimized for handling larger files like reverb IRs. 35 | 36 | Currently it only uses the first 42.7 ms (2048 samples at 48 kHz sampling rate) of the loaded IR file. 37 | IR files at different sample rates are resampled to 48 kHz by the plugin. 38 | It is recommended to trim any silence at the start of the IR file for optimal results. 39 | 40 | Features: 41 | Plugin by MOD Devices 42 | Default IR file by forward audio 43 | """; 44 | 45 | doap:developer [ 46 | foaf:name "Jarno Verheesen & Bram Giesen"; 47 | foaf:homepage <>; 48 | foaf:mbox ; 49 | ]; 50 | 51 | doap:maintainer [ 52 | foaf:name "MOD"; 53 | foaf:homepage ; 54 | foaf:mbox ; 55 | ]; 56 | 57 | lv2:minorVersion 1; 58 | lv2:microVersion 0; 59 | 60 | doap:license ; 61 | lv2:project ; 62 | lv2:requiredFeature urid:map , 63 | work:schedule ; 64 | lv2:optionalFeature lv2:hardRTCapable , 65 | state:loadDefaultState ; 66 | lv2:extensionData state:interface , 67 | work:interface ; 68 | patch:writable ; 69 | lv2:port [ 70 | a lv2:InputPort , 71 | atom:AtomPort ; 72 | atom:bufferType atom:Sequence ; 73 | atom:supports patch:Message ; 74 | lv2:designation lv2:control ; 75 | lv2:index 0 ; 76 | lv2:symbol "control" ; 77 | lv2:name "Control" 78 | ] , [ 79 | a lv2:OutputPort , 80 | atom:AtomPort ; 81 | atom:bufferType atom:Sequence ; 82 | atom:supports patch:Message ; 83 | lv2:designation lv2:control ; 84 | lv2:index 1 ; 85 | lv2:symbol "notify" ; 86 | lv2:name "Notify" 87 | ] , [ 88 | a lv2:AudioPort , 89 | lv2:InputPort ; 90 | lv2:index 2 ; 91 | lv2:symbol "in" ; 92 | lv2:name "In" 93 | ] , [ 94 | a lv2:AudioPort , 95 | lv2:OutputPort ; 96 | lv2:index 3 ; 97 | lv2:symbol "out" ; 98 | lv2:name "Out" 99 | ] ; 100 | 101 | lv2:port [ 102 | a lv2:InputPort , 103 | lv2:ControlPort ; 104 | lv2:index 4 ; 105 | lv2:symbol "Gain"; 106 | lv2:name "Gain"; 107 | lv2:default 0; 108 | lv2:minimum -90; 109 | lv2:maximum 0; 110 | units:unit units:db ; 111 | ] ; 112 | 113 | state:state [ 114 | 115 | ] . 116 | -------------------------------------------------------------------------------- /source/cabsim-IR-loader.lv2/forward-audio_AliceInBones.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mod-audio/mod-cabsim-IR-loader/6fcc59f57f19b93db94cf74a5f529421a7fb4c16/source/cabsim-IR-loader.lv2/forward-audio_AliceInBones.wav -------------------------------------------------------------------------------- /source/cabsim-IR-loader.lv2/manifest.ttl: -------------------------------------------------------------------------------- 1 | @prefix lv2: . 2 | @prefix rdfs: . 3 | 4 | 5 | a lv2:Plugin ; 6 | lv2:binary ; 7 | rdfs:seeAlso , . 8 | -------------------------------------------------------------------------------- /source/cabsim-IR-loader.lv2/modgui.ttl: -------------------------------------------------------------------------------- 1 | @prefix modgui: . 2 | @prefix lv2: . 3 | 4 | 5 | modgui:gui [ 6 | modgui:resourcesDirectory ; 7 | modgui:iconTemplate ; 8 | modgui:stylesheet ; 9 | modgui:screenshot ; 10 | modgui:thumbnail ; 11 | modgui:brand "MOD" ; 12 | modgui:label "IR loader" ; 13 | modgui:model "boxy" ; 14 | modgui:panel "1-select-1-knob" ; 15 | modgui:color "brown" ; 16 | modgui:knob "black" ; 17 | modgui:port [ 18 | lv2:index 0 ; 19 | lv2:symbol "Gain" ; 20 | lv2:name "Gain" ; 21 | ] ; 22 | ] . 23 | -------------------------------------------------------------------------------- /source/cabsim-IR-loader.lv2/modgui/bg/cabinet-back-bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mod-audio/mod-cabsim-IR-loader/6fcc59f57f19b93db94cf74a5f529421a7fb4c16/source/cabsim-IR-loader.lv2/modgui/bg/cabinet-back-bottom.png -------------------------------------------------------------------------------- /source/cabsim-IR-loader.lv2/modgui/bg/cabinet-back-middle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mod-audio/mod-cabsim-IR-loader/6fcc59f57f19b93db94cf74a5f529421a7fb4c16/source/cabsim-IR-loader.lv2/modgui/bg/cabinet-back-middle.png -------------------------------------------------------------------------------- /source/cabsim-IR-loader.lv2/modgui/bg/cabinet-back-top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mod-audio/mod-cabsim-IR-loader/6fcc59f57f19b93db94cf74a5f529421a7fb4c16/source/cabsim-IR-loader.lv2/modgui/bg/cabinet-back-top.png -------------------------------------------------------------------------------- /source/cabsim-IR-loader.lv2/modgui/bg/cabinet-face-silver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mod-audio/mod-cabsim-IR-loader/6fcc59f57f19b93db94cf74a5f529421a7fb4c16/source/cabsim-IR-loader.lv2/modgui/bg/cabinet-face-silver.png -------------------------------------------------------------------------------- /source/cabsim-IR-loader.lv2/modgui/icon-cabsim-IR-loader.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

{{brand}}

6 |

{{label}}

7 |
8 |
9 | {{#effect.ports.audio.input}} 10 |
11 |
12 |
13 | {{/effect.ports.audio.input}} 14 | {{#effect.ports.midi.input}} 15 |
16 |
17 |
18 | {{/effect.ports.midi.input}} 19 | {{#effect.ports.cv.input}} 20 |
21 |
22 |
23 | {{/effect.ports.cv.input}} 24 |
25 |
26 | {{#effect.ports.audio.output}} 27 |
28 |
29 |
30 | {{/effect.ports.audio.output}} 31 | {{#effect.ports.midi.output}} 32 |
33 |
34 |
35 | {{/effect.ports.midi.output}} 36 | {{#effect.ports.cv.output}} 37 |
38 |
39 |
40 | {{/effect.ports.cv.output}} 41 |
42 |
43 | 44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | {{#effect.parameters}} 55 | {{#path}} 56 |
57 |
58 |
59 | {{#files}} 60 |
{{basename}}
61 | {{/files}} 62 |
63 |
64 | {{/path}} 65 | {{/effect.parameters}} 66 |
67 | {{#controls.0}} 68 |
69 |
70 | {{name}} 71 |
72 | {{/controls.0}} 73 |
74 |
75 |
76 |
77 |
78 | -------------------------------------------------------------------------------- /source/cabsim-IR-loader.lv2/modgui/knobs/knob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mod-audio/mod-cabsim-IR-loader/6fcc59f57f19b93db94cf74a5f529421a7fb4c16/source/cabsim-IR-loader.lv2/modgui/knobs/knob.png -------------------------------------------------------------------------------- /source/cabsim-IR-loader.lv2/modgui/mod_cabsim_IR_loader_ss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mod-audio/mod-cabsim-IR-loader/6fcc59f57f19b93db94cf74a5f529421a7fb4c16/source/cabsim-IR-loader.lv2/modgui/mod_cabsim_IR_loader_ss.png -------------------------------------------------------------------------------- /source/cabsim-IR-loader.lv2/modgui/mod_cabsim_IR_loader_tn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mod-audio/mod-cabsim-IR-loader/6fcc59f57f19b93db94cf74a5f529421a7fb4c16/source/cabsim-IR-loader.lv2/modgui/mod_cabsim_IR_loader_tn.png -------------------------------------------------------------------------------- /source/cabsim-IR-loader.lv2/modgui/stylesheet-cabsim-IR-loader.css: -------------------------------------------------------------------------------- 1 | /* SKINS */ 2 | .mod-cabinet{{{cns}}} { 3 | font-family: 'Cooper Hewitt', Helvetica,Arial,Sans-serif; 4 | position: relative; 5 | width: 374px; 6 | height: 585px; 7 | } 8 | 9 | .mod-cabinet{{{cns}}} .mod-drag-handle { 10 | width: 374px; 11 | height: 374px; 12 | } 13 | 14 | /* CABINET FACE */ 15 | .mod-cabinet{{{cns}}} .mod-cabinet-face { 16 | background-position: center center; 17 | background-repeat: no-repeat; 18 | width: 374px; 19 | height: 374px; 20 | position: relative; 21 | } 22 | 23 | .mod-cabinet{{{cns}}}.mod-cabinet-model-1 .mod-cabinet-face { 24 | background-image: url(/resources/bg/cabinet-face-gold.png{{{ns}}}); 25 | } 26 | .mod-cabinet{{{cns}}}.mod-cabinet-model-2 .mod-cabinet-face { 27 | background-image: url(/resources/bg/cabinet-face-silver.png{{{ns}}}); 28 | } 29 | 30 | .mod-cabinet{{{cns}}} .mod-cabinet-info { 31 | display: table; 32 | vertical-align: middle; 33 | height: 100%; 34 | width: 100% 35 | } 36 | /* AUTHOR'S NAME */ 37 | .mod-cabinet{{{cns}}} .mod-plugin-brand { 38 | display: table-cell; 39 | vertical-align: middle; 40 | height: 100%; 41 | width: 100%; 42 | text-align: center; 43 | } 44 | .mod-cabinet{{{cns}}} .mod-plugin-brand h1{ 45 | background: #2b2422; 46 | 47 | display: inline; 48 | text-transform: uppercase; 49 | font-size: 30px; 50 | padding: 2px 5px; 51 | border-radius: 7px; 52 | margin: 0; 53 | line-height: 33px; 54 | -webkit-box-shadow: 0px 3px 8px 2px rgba(0,0,0,0.64); 55 | box-shadow: 0px 3px 8px 2px rgba(0,0,0,0.64); 56 | } 57 | .mod-cabinet{{{cns}}}.mod-cabinet-model-1 .mod-plugin-brand h1{ 58 | color: #aba375; 59 | border: 2px solid #aba375; 60 | } 61 | 62 | .mod-cabinet{{{cns}}}.mod-cabinet-model-2 .mod-plugin-brand h1{ 63 | color: #bcbcbc; 64 | border: 2px solid #bcbcbc; 65 | } 66 | 67 | /* PLUGIN'S NAME */ 68 | .mod-cabinet{{{cns}}} .mod-plugin-name { 69 | display: table-row; 70 | text-align: right 71 | } 72 | .mod-cabinet{{{cns}}} .mod-plugin-name h1{ 73 | text-transform: uppercase; 74 | font-weight: bold; 75 | font-size: 25px; 76 | display: inline-block; 77 | text-align: right; 78 | padding: 15px 30px; 79 | margin-top: -30px; 80 | text-shadow: 0px 2px 5px rgba(0,0,0,0.78); 81 | } 82 | 83 | .mod-cabinet{{{cns}}}.mod-cabinet-model-1 .mod-plugin-name h1{ 84 | color: #aba375; 85 | } 86 | .mod-cabinet{{{cns}}}.mod-cabinet-model-2 .mod-plugin-name h1{ 87 | color: #bcbcbc; 88 | } 89 | 90 | /* CONTROLS */ 91 | .mod-cabinet{{{cns}}} .mod-cabinet-controls { 92 | position: absolute; 93 | } 94 | .mod-cabinet{{{cns}}} .mod-cabinet-controls header { 95 | background-image: url(/resources/bg/cabinet-back-top.png{{{ns}}}); 96 | background-position: center center; 97 | background-repeat: no-repeat; 98 | height: 30px; 99 | width: 374px; 100 | display: block 101 | } 102 | .mod-cabinet{{{cns}}} .mod-cabinet-controls footer { 103 | background-image: url(/resources/bg/cabinet-back-bottom.png{{{ns}}}); 104 | background-position: center center; 105 | background-repeat: no-repeat; 106 | height: 30px; 107 | width: 374px; 108 | display: block 109 | } 110 | 111 | .mod-cabinet{{{cns}}} .mod-cabinet-controls main { 112 | background-image: url(/resources/bg/cabinet-back-middle.png{{{ns}}}); 113 | background-position: center center; 114 | background-repeat: repeat-y; 115 | width: 374px; 116 | display: block; 117 | padding: 10px 30px; 118 | } 119 | 120 | .mod-cabinet{{{cns}}} .mod-control-group.group1{ 121 | width: 110px; 122 | display: inline-block; 123 | padding: 16px; 124 | vertical-align: top 125 | } 126 | 127 | .mod-cabinet{{{cns}}} .mod-control-group.group2{ 128 | width: 200px; 129 | display: inline-block; 130 | } 131 | 132 | /* SWITCH ON/OFF GENERAL */ 133 | .mod-cabinet{{{cns}}} .mod-cabinet-controls .mod-switch { 134 | cursor: pointer; 135 | float: left; 136 | height: 70px; 137 | width: 70px; 138 | z-index: 100000000 139 | } 140 | 141 | .mod-cabinet{{{cns}}} .mod-cabinet-controls .mod-switch .mod-switch-image { 142 | background-image: url(/resources/switches/switch-cabinet.png{{{ns}}}); 143 | background-repeat: no-repeat; 144 | background-size: auto 100%; 145 | height: 70px; 146 | width: 70px; 147 | } 148 | 149 | .mod-cabinet{{{cns}}} .mod-cabinet-controls .mod-switch-image.on { 150 | background-position: 100% 0; 151 | } 152 | 153 | .mod-cabinet{{{cns}}} .mod-cabinet-controls .mod-switch-image.off { 154 | background-position: 0 0; 155 | } 156 | 157 | /* ENUMERATED LIST */ 158 | 159 | 160 | .mod-cabinet{{{cns}}} .mod-enumerated-group { 161 | height:31px; 162 | margin:20px auto 0 !important; 163 | position:relative; 164 | width:190px; 165 | top:-10px; 166 | z-index:35; 167 | } 168 | 169 | .mod-cabinet{{{cns}}} .mod-enumerated { 170 | color: #000; 171 | display: inline-block; 172 | width: 100%; 173 | cursor: pointer 174 | } 175 | 176 | .mod-cabinet{{{cns}}} .mod-enumerated .mod-enumerated-selected { 177 | background: rgba(255,255,255,.85); 178 | border-radius: 4px; 179 | box-shadow:inset 1px 1px 10px #000; 180 | font-size: 14px; 181 | height: 34px; 182 | line-height: 2.5; 183 | margin-right: 8px; 184 | overflow: hidden; 185 | padding: 0 9px; 186 | overflow: hidden; 187 | white-space: nowrap; 188 | text-overflow: ellipsis; 189 | width: 100%; 190 | } 191 | 192 | /* WHEN IS HOVER */ 193 | .mod-cabinet{{{cns}}} .mod-enumerated:hover .mod-enumerated-selected { 194 | border-radius: 4px 4px 0 0; 195 | } 196 | 197 | /* THE LIST OF OPTIONS */ 198 | .mod-cabinet{{{cns}}} .mod-enumerated .mod-enumerated-list { 199 | background: rgba(255,255,255,.85); 200 | display: none; 201 | font-weight: bold; 202 | height: 219px; 203 | overflow: auto; 204 | position: relative; 205 | position: absolute; 206 | width: 388px; 207 | z-index: 3; 208 | } 209 | 210 | .mod-cabinet{{{cns}}} .mod-enumerated .mod-enumerated-list > div { 211 | padding: 3px 9px; 212 | } 213 | 214 | .mod-cabinet{{{cns}}} .mod-enumerated .mod-enumerated-list > div:hover { 215 | background: rgba(0,0,0,.35); 216 | cursor: pointer; 217 | } 218 | 219 | /* INPUT / OUTPUT */ 220 | 221 | .mod-cabinet{{{cns}}} .mod-pedal-input , .mod-cabinet{{{cns}}} .mod-pedal-output { 222 | top: 40%; 223 | } 224 | 225 | /* KNOB */ 226 | .mod-cabinet{{{cns}}} .mod-knob { 227 | float: left; 228 | height: 75px; 229 | overflow: hidden; 230 | position: relative; 231 | text-align: center; 232 | width: 55px; 233 | margin-left: 60px; 234 | cursor: pointer 235 | } 236 | 237 | .mod-cabinet{{{cns}}} .mod-knob .mod-knob-image { 238 | background-image: url(/resources/knobs/knob.png{{{ns}}}); 239 | background-position: left 0px; 240 | background-repeat: no-repeat; 241 | background-size: auto 55px; 242 | height: 55px; 243 | margin-top: 5px; 244 | position: relative; 245 | width: 55px; 246 | z-index: 2; 247 | } 248 | 249 | .mod-cabinet{{{cns}}} .mod-knob .mod-knob-title { 250 | font-family: "Cooper Hewitt"; 251 | font-size: 12px; 252 | text-transform: uppercase; 253 | color: white; 254 | } 255 | 256 | 257 | -------------------------------------------------------------------------------- /source/cabsim-IR-loader.lv2/modgui/switches/switch-cabinet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mod-audio/mod-cabsim-IR-loader/6fcc59f57f19b93db94cf74a5f529421a7fb4c16/source/cabsim-IR-loader.lv2/modgui/switches/switch-cabinet.png -------------------------------------------------------------------------------- /source/circular_buffer.c: -------------------------------------------------------------------------------- 1 | #include "circular_buffer.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void ringbuffer_clear(ringbuffer_t *buffer, uint32_t size) 8 | { 9 | buffer->S=size; 10 | 11 | uint32_t q = 0; 12 | for ( q = 0; q < size; q++) 13 | { 14 | buffer->m_buffer[q] = 0.0f; 15 | } 16 | 17 | buffer->m_size = 0; 18 | buffer->m_front = 0; 19 | buffer->m_back = buffer->S - 1; 20 | buffer->power = 0.0f; 21 | } 22 | 23 | void ringbuffer_push(ringbuffer_t *buffer) 24 | { 25 | buffer->m_back = (buffer->m_back + 1) % buffer->S; 26 | 27 | if(buffer->m_size == buffer->S) 28 | { 29 | buffer->m_front = (buffer->m_front + 1) % buffer->S; 30 | } 31 | else 32 | { 33 | buffer->m_size++; 34 | } 35 | } 36 | 37 | void ringbuffer_push_sample(ringbuffer_t *buffer, const float x) 38 | { 39 | ringbuffer_push(buffer); 40 | buffer->m_buffer[buffer->m_back] = x; 41 | } 42 | 43 | void ringbuffer_pop(ringbuffer_t *buffer) 44 | { 45 | if(buffer->m_size > 0 ) 46 | { 47 | buffer->m_size--; 48 | buffer->m_front = (buffer->m_front + 1) % buffer->S; 49 | } 50 | } 51 | 52 | void ringbuffer_back_erase(ringbuffer_t *buffer, const uint32_t n) 53 | { 54 | if(n >= buffer->m_size) 55 | { 56 | ringbuffer_clear(buffer, buffer->S); 57 | } 58 | else 59 | { 60 | buffer->m_size -= n; 61 | buffer->m_back = (buffer->m_front + buffer->m_size - 1) % buffer->S; 62 | } 63 | } 64 | 65 | void ringbuffer_front_erase(ringbuffer_t *buffer, const uint32_t n) 66 | { 67 | if(n >= buffer->m_size) 68 | { 69 | ringbuffer_clear(buffer, buffer->S); 70 | } 71 | else 72 | { 73 | buffer->m_size -= n; 74 | buffer->m_front = (buffer->m_front + n) % buffer->S; 75 | } 76 | } 77 | 78 | int ringbuffer_peek_index(ringbuffer_t *buffer) 79 | { 80 | uint32_t peek_index = 0; 81 | float peek_value = 0; 82 | 83 | uint32_t i = 0; 84 | for (i = 0; i < buffer->S; i++) 85 | { 86 | if (peek_value < buffer->m_buffer[i]) 87 | { 88 | peek_value = buffer->m_buffer[i]; 89 | peek_index = i; 90 | } 91 | } 92 | 93 | return peek_index; 94 | } 95 | 96 | float ringbuffer_push_and_calculate_power(ringbuffer_t *buffer, const float input) 97 | { 98 | const float pow = (float)sqrt(input * input) * (1.0f / buffer->S); 99 | 100 | if (buffer->m_size < buffer->S) 101 | { 102 | //remove old sample and add new one to windowPower 103 | buffer->power += pow; 104 | ringbuffer_push_sample(buffer, pow); 105 | } 106 | else 107 | { 108 | //remove old sample and add new one to windowPower 109 | buffer->power += pow - ringbuffer_front(buffer); 110 | ringbuffer_pop(buffer); 111 | ringbuffer_push_sample(buffer, pow); 112 | } 113 | 114 | return buffer->power; 115 | } 116 | 117 | float ringbuffer_front(ringbuffer_t *buffer) 118 | { 119 | return buffer->m_buffer[buffer->m_front]; 120 | } 121 | 122 | float ringbuffer_back(ringbuffer_t *buffer) 123 | { 124 | return buffer->m_buffer[buffer->m_back]; 125 | } 126 | 127 | float ringbuffer_get_relative_val(ringbuffer_t *buffer, uint32_t index) 128 | { 129 | if (buffer->m_back + index >= buffer->S) 130 | return buffer->m_buffer[(buffer->m_back + index) % (buffer->S)]; 131 | else 132 | return buffer->m_buffer[buffer->m_back + index]; 133 | } 134 | 135 | float ringbuffer_get__direct_index_val(ringbuffer_t *buffer, uint32_t index) 136 | { 137 | if (index >= buffer->S) 138 | return 0; 139 | 140 | return buffer->m_buffer[index]; 141 | } 142 | 143 | int ringbuffer_empty(ringbuffer_t *buffer) 144 | { 145 | return buffer->m_size == 0; 146 | } 147 | 148 | int ringbuffer_full(ringbuffer_t *buffer) 149 | { 150 | return buffer->m_size == buffer->S; 151 | } 152 | 153 | float * ringbuffer_get_first_pointer(ringbuffer_t *buffer) 154 | { 155 | return &buffer->m_buffer[buffer->m_front]; 156 | } 157 | -------------------------------------------------------------------------------- /source/circular_buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCULAR_BUFFER_H 2 | #define CIRCULAR_BUFFER_H 3 | 4 | #include 5 | 6 | #define MAX_BUFFER_SIZE 65536 7 | 8 | typedef struct RINGBUFFER_T { 9 | float m_buffer[MAX_BUFFER_SIZE]; 10 | uint32_t S; 11 | uint32_t m_size; 12 | uint32_t m_front; 13 | uint32_t m_back; 14 | float power; 15 | } ringbuffer_t; 16 | 17 | void ringbuffer_clear(ringbuffer_t *buffer, uint32_t size); 18 | void ringbuffer_push(ringbuffer_t *buffer); 19 | void ringbuffer_push_sample(ringbuffer_t *buffer, const float x); 20 | void ringbuffer_pop(ringbuffer_t *buffer); 21 | void ringbuffer_back_erase(ringbuffer_t *buffer, const uint32_t n); 22 | void ringbuffer_front_erase(ringbuffer_t *buffer, const uint32_t n); 23 | int ringbuffer_peek_index(ringbuffer_t *buffer); 24 | float ringbuffer_push_and_calculate_power(ringbuffer_t *buffer, const float input); 25 | float ringbuffer_front(ringbuffer_t *buffer); 26 | float ringbuffer_back(ringbuffer_t *buffer); 27 | float ringbuffer_get_relative_val(ringbuffer_t *buffer, uint32_t index); 28 | float ringbuffer_get_index_val(ringbuffer_t *buffer, uint32_t index); 29 | int ringbuffer_empty(ringbuffer_t *buffer); 30 | int ringbuffer_full(ringbuffer_t *buffer); 31 | float * ringbuffer_get_first_pointer(ringbuffer_t *buffer); 32 | 33 | #endif // __RINGBUFFER_H__ 34 | -------------------------------------------------------------------------------- /source/uris.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file taken from the LV2 ImpulseResponser Example Plugin 3 | Copyright 2011-2012 David Robillard 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #ifndef CABSIM_URIS_H 19 | #define CABSIM_URIS_H 20 | 21 | #include "lv2/lv2plug.in/ns/ext/log/log.h" 22 | #include "lv2/lv2plug.in/ns/ext/midi/midi.h" 23 | #include "lv2/lv2plug.in/ns/ext/state/state.h" 24 | #include "lv2/lv2plug.in/ns/ext/parameters/parameters.h" 25 | 26 | #define CABSIM_URI "http://moddevices.com/plugins/mod-devel/cabsim-IR-loader" 27 | #define CABSIM__ir CABSIM_URI "#ir" 28 | #define CABSIM__applyImpulseResponse CABSIM_URI "#applyImpulseResponse" 29 | #define CABSIM__freeImpulseResponse CABSIM_URI "#freeImpulseResponse" 30 | 31 | typedef struct { 32 | LV2_URID atom_Float; 33 | LV2_URID atom_Path; 34 | LV2_URID atom_Resource; 35 | LV2_URID atom_Sequence; 36 | LV2_URID atom_URID; 37 | LV2_URID atom_eventTransfer; 38 | LV2_URID cab_applyImpulseResponse; 39 | LV2_URID cab_ir; 40 | LV2_URID cab_freeImpulseResponse; 41 | LV2_URID midi_Event; 42 | LV2_URID param_gain; 43 | LV2_URID patch_Get; 44 | LV2_URID patch_Set; 45 | LV2_URID patch_property; 46 | LV2_URID patch_value; 47 | } CabsimURIs; 48 | 49 | static inline void 50 | map_cabsim_uris(LV2_URID_Map* map, CabsimURIs* uris) 51 | { 52 | uris->atom_Float = map->map(map->handle, LV2_ATOM__Float); 53 | uris->atom_Path = map->map(map->handle, LV2_ATOM__Path); 54 | uris->atom_Resource = map->map(map->handle, LV2_ATOM__Resource); 55 | uris->atom_Sequence = map->map(map->handle, LV2_ATOM__Sequence); 56 | uris->atom_URID = map->map(map->handle, LV2_ATOM__URID); 57 | uris->atom_eventTransfer = map->map(map->handle, LV2_ATOM__eventTransfer); 58 | uris->cab_applyImpulseResponse = map->map(map->handle, CABSIM__applyImpulseResponse); 59 | uris->cab_freeImpulseResponse = map->map(map->handle, CABSIM__freeImpulseResponse); 60 | uris->cab_ir = map->map(map->handle, CABSIM__ir); 61 | uris->midi_Event = map->map(map->handle, LV2_MIDI__MidiEvent); 62 | uris->param_gain = map->map(map->handle, LV2_PARAMETERS__gain); 63 | uris->patch_Get = map->map(map->handle, LV2_PATCH__Get); 64 | uris->patch_Set = map->map(map->handle, LV2_PATCH__Set); 65 | uris->patch_property = map->map(map->handle, LV2_PATCH__property); 66 | uris->patch_value = map->map(map->handle, LV2_PATCH__value); 67 | } 68 | 69 | /** 70 | * Write a message like the following to @p forge: 71 | * [] 72 | * a patch:Set ; 73 | * patch:property eg:ir ; 74 | * patch:value . 75 | */ 76 | static inline LV2_Atom* 77 | write_set_file(LV2_Atom_Forge* forge, 78 | const CabsimURIs* uris, 79 | const char* filename, 80 | const uint32_t filename_len) 81 | { 82 | LV2_Atom_Forge_Frame frame; 83 | LV2_Atom* set = (LV2_Atom*)lv2_atom_forge_object( 84 | forge, &frame, 0, uris->patch_Set); 85 | 86 | lv2_atom_forge_key(forge, uris->patch_property); 87 | lv2_atom_forge_urid(forge, uris->cab_ir); 88 | lv2_atom_forge_key(forge, uris->patch_value); 89 | lv2_atom_forge_path(forge, filename, filename_len + 1); 90 | 91 | lv2_atom_forge_pop(forge, &frame); 92 | 93 | return set; 94 | } 95 | 96 | static inline const LV2_Atom* 97 | read_set_file(const CabsimURIs* uris, 98 | const LV2_Atom_Object* obj) 99 | { 100 | if (obj->body.otype != uris->patch_Set) { 101 | fprintf(stderr, "Ignoring unknown message type %d\n", obj->body.otype); 102 | return NULL; 103 | } 104 | 105 | /* Get property URI. */ 106 | const LV2_Atom* property = NULL; 107 | lv2_atom_object_get(obj, uris->patch_property, &property, 0); 108 | if (!property) { 109 | fprintf(stderr, "Malformed set message has no body.\n"); 110 | return NULL; 111 | } else if (property->type != uris->atom_URID) { 112 | fprintf(stderr, "Malformed set message has non-URID property.\n"); 113 | return NULL; 114 | } else if (((const LV2_Atom_URID*)property)->body != uris->cab_ir) { 115 | fprintf(stderr, "Set message for unknown property.\n"); 116 | return NULL; 117 | } 118 | 119 | /* Get value. */ 120 | const LV2_Atom* file_path = NULL; 121 | lv2_atom_object_get(obj, uris->patch_value, &file_path, 0); 122 | if (!file_path) { 123 | fprintf(stderr, "Malformed set message has no value.\n"); 124 | return NULL; 125 | } else if (file_path->type != uris->atom_Path) { 126 | fprintf(stderr, "Set message value is not a Path.\n"); 127 | return NULL; 128 | } 129 | 130 | return file_path; 131 | } 132 | 133 | #endif /* CABSIM_URIS_H */ 134 | --------------------------------------------------------------------------------