├── NEWS ├── ChangeLog ├── README ├── py ├── __init__.py ├── sfzparser.py ├── nullbox.py └── nocturn.py ├── AUTHORS ├── autogen.sh ├── cleanpythonbuild.sh ├── background_example.py ├── synthbass.sfz ├── .gitignore ├── fifo.c ├── scripting.h ├── hwcfg.h ├── experiments ├── simplerPlayback.py ├── simplePlayback.py ├── printAllMidiEvents.py ├── printAllMidiEventsSpecificPort.py ├── playPatternsAsMeasures.py ├── interactive.py ├── testmetadata.py └── printNotesOnlyDuringPlayback.py ├── sampler_api_example4.py ├── sfzloader.h ├── jack_scene_routing.py ├── meter.h ├── eq.h ├── ui.h ├── testcpp.cpp ├── stm.h ├── blob.h ├── ioenv.h ├── auxbus.h ├── app.h ├── sampler_api_example2.py ├── adhoc_example.py ├── errors.h ├── tests.h ├── ui.c ├── sfzparser.h ├── send_pattern_to_midi_out_example.py ├── pattern-maker.h ├── wav2c.py ├── sampler_api_example.py ├── usb_api_example.py ├── jack_output_routing.py ├── menu.h ├── song.h ├── track.h ├── pattern.h ├── seq-adhoc.c ├── instr.h ├── README.md ├── layer.h ├── jack_audio_routing.py ├── errors.c ├── sampler_impl.h ├── song_api_example.py ├── recsrc.h ├── tarfile.h ├── config-api.h ├── sampler_prevoice.c ├── song_api_example2.py ├── cmd.h ├── mididest.h ├── wavebank.h ├── prefetch_pipe.h ├── skel.c ├── engine.h ├── master.h ├── Makefile.am ├── scene.h ├── midi.c ├── onepole-int.h ├── auxbus.c ├── novabox.py ├── midi.h ├── setup.py ├── sampler_nif.c ├── distortion.c ├── blob.c ├── delay.c ├── meter.c ├── fifo.h ├── tonectl.c ├── menuitem.h ├── sampler_api_load_stress_test.py ├── API └── recsrc.c /NEWS: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | README.md -------------------------------------------------------------------------------- /py/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Krzysztof Foltman 2 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | aclocal --force || exit 1 2 | libtoolize --force --automake --copy || exit 1 3 | autoheader --force || exit 1 4 | autoconf --force || exit 1 5 | automake --add-missing --copy || exit 1 6 | 7 | -------------------------------------------------------------------------------- /cleanpythonbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | make clean 3 | make distclean 4 | rm build -rf 5 | set -e 6 | sh autogen.sh 7 | ./configure --prefix=/usr --without-python 8 | make CFLAGS="-O0 -g" 9 | python3 setup.py build 10 | sudo python3 setup.py install 11 | sudo make install 12 | -------------------------------------------------------------------------------- /background_example.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from py import cbox 5 | def cmd_dumper(cmd, fb, args): 6 | print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) 7 | cbox.init_engine("") #empty string so cbox doesn't look for the .cboxrc file 8 | cbox.start_audio(cmd_dumper) 9 | 10 | import time 11 | while True: 12 | cbox.do_cmd("/on_idle", None, []) 13 | time.sleep(0.1) 14 | -------------------------------------------------------------------------------- /synthbass.sfz: -------------------------------------------------------------------------------- 1 | 2 | sample=*sine loop_mode=loop_continuous tune=0 transpose=0 volume=-12 amp_velcurve_1=1 amp_velcurve_127=1 ampeg_release=0.1 3 | 4 | 5 | sample=*saw loop_mode=loop_continuous cutoff=220 cutoff_cc74=3600 resonance=14 fileg_sustain=10 fileg_decay=0.5 fileg_depth=3600 volume=-18 amp_velcurve_1=1 amp_velcurve_127=1 ampeg_release=0.1 fil_keytrack=100 6 | tonectl_freq=3000 tonectl_cc84=24 7 | 8 | 9 | tune=-5 fil_veltrack=1200 10 | 11 | 12 | tune=+5 transpose=+12 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | aclocal.m4 2 | autom4te.cache 3 | config.h.in 4 | config.h.in~ 5 | config.h 6 | configure 7 | configure~ 8 | compile 9 | depcomp 10 | install-sh 11 | ltmain.sh 12 | missing 13 | *.o 14 | *.lo 15 | .libs/ 16 | .deps/ 17 | libcalfbox.la 18 | libtool 19 | Makefile.in 20 | Makefile 21 | config.log 22 | config.status 23 | stamp-h1 24 | calfbox 25 | calfbox_tests 26 | test.wav 27 | testcpp 28 | !testcpp.cpp 29 | 30 | __pycache__/ 31 | *.py[cod] 32 | *$py.class 33 | build/ 34 | config.guess 35 | config.sub 36 | CalfBox.egg-info/ 37 | dist/ 38 | -------------------------------------------------------------------------------- /fifo.c: -------------------------------------------------------------------------------- 1 | #include "fifo.h" 2 | #include 3 | 4 | struct cbox_fifo *cbox_fifo_new(uint32_t size) 5 | { 6 | struct cbox_fifo *fifo = calloc(1, sizeof(struct cbox_fifo) + size); 7 | if (!fifo) 8 | return NULL; 9 | fifo->data = (uint8_t *)(fifo + 1); 10 | fifo->size = size; 11 | fifo->write_count = 0; 12 | fifo->write_offset= 0; 13 | fifo->read_count = 0; 14 | fifo->read_offset = 0; 15 | return fifo; 16 | } 17 | 18 | void cbox_fifo_destroy(struct cbox_fifo *fifo) 19 | { 20 | free(fifo); 21 | } 22 | 23 | -------------------------------------------------------------------------------- /scripting.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | extern void cbox_script_run(const char *name); 20 | -------------------------------------------------------------------------------- /hwcfg.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_HWCFG_H 20 | #define CBOX_HWCFG_H 21 | 22 | /** 23 | * Autodetect JACK config based on cbox configuration vs ALSA devices present. 24 | * @retval 1 if OK 25 | */ 26 | extern int cbox_hwcfg_setup_jack(void); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /experiments/simplerPlayback.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) 5 | 6 | This code is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | """ 19 | 20 | from meta import ly2Track, initCbox, start 21 | 22 | scene, cbox, eventLoop = initCbox("test01") 23 | 24 | ly2Track(trackName="someInstrument", lyString="c'4 d' e' f'2 g' c''") 25 | start() 26 | -------------------------------------------------------------------------------- /sampler_api_example4.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from calfbox import cbox 5 | 6 | def cmd_dumper(cmd, fb, args): 7 | print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) 8 | 9 | cbox.init_engine("") #empty string so cbox doesn't look for the .cboxrc file 10 | cbox.start_audio(cmd_dumper) 11 | 12 | global Document 13 | Document = cbox.Document 14 | 15 | scene = Document.get_scene() 16 | scene.clear() 17 | instrument = scene.add_new_instrument_layer("test_sampler", "sampler").get_instrument() 18 | pgm_no = instrument.engine.get_unused_program() 19 | pgm = instrument.engine.load_patch_from_tar(pgm_no, 'sonatina.sbtar', 'Brass - Horn Solo.sfz', 'HornSolo') 20 | pgm.add_control_init(7, 127) 21 | pgm.add_control_init(10, 0) 22 | print (pgm.load_file('Brass - Horn Solo.sfz').readlines()) 23 | print (pgm.status()) 24 | print (list(pgm.get_control_inits())) 25 | instrument.engine.set_patch(1, pgm_no) 26 | 27 | print("Ready!") 28 | 29 | while True: 30 | cbox.call_on_idle(cmd_dumper) 31 | -------------------------------------------------------------------------------- /sfzloader.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_SFZLOADER_H 20 | #define CBOX_SFZLOADER_H 21 | 22 | #include "sampler.h" 23 | 24 | gboolean sampler_module_load_program_sfz(struct sampler_module *m, struct sampler_program *prg, const char *sfz, int is_from_string, GError **error); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /jack_scene_routing.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from calfbox import cbox 5 | import time 6 | 7 | def cmd_dumper(cmd, fb, args): 8 | print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) 9 | 10 | cbox.init_engine("") #empty string so cbox doesn't look for the .cboxrc file 11 | 12 | cbox.Config.set("io", "use_usb", 0) 13 | cbox.start_audio(cmd_dumper) 14 | 15 | global Document 16 | Document = cbox.Document 17 | 18 | status = cbox.JackIO.status() 19 | client_name = status.client_name 20 | print ("Client name: %s" % client_name) 21 | print ("Audio inputs: %d, outputs: %d" % (status.audio_inputs, status.audio_outputs)) 22 | print ("Sample rate: %d frames/sec" % (status.sample_rate)) 23 | print ("JACK period: %d frames" % (status.buffer_size)) 24 | 25 | uuid_ext1 = cbox.JackIO.create_midi_output('ext1') 26 | uuid_ext2 = cbox.JackIO.create_midi_output('ext2') 27 | 28 | scene = Document.get_scene() 29 | scene.clear() 30 | layer = scene.add_new_midi_layer(uuid_ext2) 31 | #layer.set_external_output(uuid_ext1) 32 | 33 | print("Ready!") 34 | 35 | while True: 36 | events = cbox.get_new_events() 37 | if events: 38 | print (events) 39 | time.sleep(0.05) 40 | -------------------------------------------------------------------------------- /experiments/simplePlayback.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) 5 | 6 | This code is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | """ 19 | 20 | from meta import ly2cbox, cboxSetTrack, initCbox, start 21 | 22 | scene, cbox, eventLoop = initCbox("test01") 23 | 24 | #Generate Music 25 | music = "c'4 d' e' f'2 g' c''" 26 | cboxBlob, durationInTicks = ly2cbox(music) 27 | pattern = cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks) 28 | cboxSetTrack("someInstrument", durationInTicks, pattern) 29 | 30 | start() 31 | -------------------------------------------------------------------------------- /meter.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_METER_H 20 | #define CBOX_METER_H 21 | 22 | #include "recsrc.h" 23 | 24 | struct cbox_meter 25 | { 26 | struct cbox_recorder recorder; 27 | 28 | float volume[2]; // lowpassed squared 29 | float peak[2]; 30 | float last_peak[2]; 31 | int srate; 32 | int channels; 33 | int smpcounter; 34 | }; 35 | 36 | extern struct cbox_meter *cbox_meter_new(struct cbox_document *document, int srate); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /eq.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | struct eq_band 22 | { 23 | gboolean active; 24 | float center; 25 | float q; 26 | float gain; 27 | }; 28 | 29 | extern float cbox_eq_get_band_param(const char *cfg_section, int band, const char *param, float defvalue); 30 | extern float cbox_eq_get_band_param_db(const char *cfg_section, int band, const char *param, float defvalue); 31 | extern void cbox_eq_reset_bands(struct cbox_biquadf_state state[1][2], int bands); 32 | -------------------------------------------------------------------------------- /ui.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_UI_H 20 | #define CBOX_UI_H 21 | 22 | #include "config.h" 23 | 24 | #if USE_NCURSES 25 | 26 | struct cbox_ui_page 27 | { 28 | void *user_data; 29 | void (*draw)(struct cbox_ui_page *page); 30 | int (*on_key)(struct cbox_ui_page *page, int ch); 31 | int (*on_idle)(struct cbox_ui_page *page); 32 | }; 33 | 34 | extern void cbox_ui_start(void); 35 | extern int cbox_ui_run(struct cbox_ui_page *page); 36 | extern void cbox_ui_stop(void); 37 | 38 | #endif 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /testcpp.cpp: -------------------------------------------------------------------------------- 1 | 2 | //g++ testcpp.cpp -o testcpp $(pkg-config --cflags --libs glib-2.0) 3 | 4 | #include 5 | 6 | extern "C" { 7 | 8 | #include 9 | 10 | #include "app.h" 11 | #include "auxbus.h" 12 | #include "blob.h" 13 | #include "cmd.h" 14 | #include "config-api.h" 15 | #include "dom.h" 16 | #include "engine.h" 17 | #include "eq.h" 18 | #include "errors.h" 19 | #include "fifo.h" 20 | #include "hwcfg.h" 21 | #include "instr.h" 22 | #include "io.h" 23 | #include "layer.h" 24 | #include "master.h" 25 | #include "meter.h" 26 | #include "midi.h" 27 | #include "mididest.h" 28 | #include "module.h" 29 | #include "pattern.h" 30 | #include "pattern-maker.h" 31 | #include "prefetch_pipe.h" 32 | #include "recsrc.h" 33 | #include "rt.h" 34 | #include "sampler.h" 35 | #include "sampler_layer.h" 36 | #include "sampler_prg.h" 37 | #include "scene.h" 38 | #include "scripting.h" 39 | #include "seq.h" 40 | #include "sfzloader.h" 41 | #include "sfzparser.h" 42 | #include "song.h" 43 | #include "tarfile.h" 44 | #include "track.h" 45 | #include "wavebank.h" 46 | } 47 | 48 | int main() { 49 | // Example function call - replace this with actual usage 50 | std::cout << "Hello World!\n"; 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /experiments/printAllMidiEvents.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) 5 | 6 | This code is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | """ 19 | 20 | from meta import initCbox, start, connectPhysicalKeyboards 21 | 22 | def processMidiIn(eventLoop): 23 | eventList = cbox.get_new_events() 24 | if eventList: 25 | for (address, uninterestingStuff, event) in eventList: #we are only interested in the event, which is a list again. 26 | print (address, "event:", event, "playback:", cbox.Transport.status().playing) 27 | eventLoop.call_later(0.1, processMidiIn, eventLoop) 28 | 29 | scene, cbox, eventLoop = initCbox("test01", internalEventProcessor=False) 30 | eventLoop.call_later(0.1, processMidiIn, eventLoop) #100ms 31 | connectPhysicalKeyboards() 32 | start() 33 | -------------------------------------------------------------------------------- /stm.h: -------------------------------------------------------------------------------- 1 | #ifndef CBOX_STM_H 2 | #define CBOX_STM_H 3 | 4 | #include 5 | #include 6 | 7 | static inline void **stm_array_clone_insert(void **old_array, int old_count, int index, void *data) 8 | { 9 | size_t ps = sizeof(void *); 10 | if (index == -1) 11 | index = old_count; 12 | void **new_array = malloc(ps * (old_count + 1)); 13 | memcpy(&new_array[0], &old_array[0], ps * index); 14 | new_array[index] = data; 15 | if (index != old_count) 16 | memcpy(&new_array[index + 1], &old_array[index], ps * (old_count - index)); 17 | return new_array; 18 | } 19 | 20 | static inline void **stm_array_clone_remove(void **old_array, int old_count, int index) 21 | { 22 | size_t ps = sizeof(void *); 23 | if (old_count == 1) 24 | return NULL; 25 | void **new_array = malloc(ps * (old_count - 1)); 26 | memcpy(&new_array[0], &old_array[0], ps * index); 27 | memcpy(&new_array[index], &old_array[index + 1], ps * (old_count - index - 1)); 28 | return new_array; 29 | } 30 | 31 | #define STM_ARRAY_FREE(old_array, count, destructor) \ 32 | for (uint32_t i = 0; i < (count); i++) \ 33 | destructor((old_array)[i]); \ 34 | free(old_array); 35 | 36 | #define STM_ARRAY_FREE_OBJS(old_array, count) \ 37 | do { \ 38 | for (uint32_t i = 0; i < (count); i++) \ 39 | cbox_object_destroy(&(old_array)[i]->_obj_hdr); \ 40 | free(old_array); \ 41 | } while(0) 42 | 43 | #endif -------------------------------------------------------------------------------- /blob.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_BLOB_H 20 | #define CBOX_BLOB_H 21 | 22 | #include 23 | #include 24 | 25 | struct cbox_blob 26 | { 27 | void *data; 28 | size_t size; 29 | }; 30 | 31 | struct cbox_tarfile; 32 | 33 | extern struct cbox_blob *cbox_blob_new(size_t size); 34 | extern struct cbox_blob *cbox_blob_new_from_file(const char *context_name, struct cbox_tarfile *tarfile, const char *path, const char *name, size_t max_size, GError **error); 35 | extern struct cbox_blob *cbox_blob_new_copy_data(const void *data, size_t size); 36 | extern struct cbox_blob *cbox_blob_new_acquire_data(void *data, size_t size); 37 | extern void cbox_blob_destroy(struct cbox_blob *blob); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /ioenv.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2013 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_IOENV_H 20 | #define CBOX_IOENV_H 21 | 22 | struct cbox_io_env 23 | { 24 | int srate; 25 | uint32_t buffer_size; 26 | uint32_t input_count, output_count; 27 | }; 28 | 29 | static inline void cbox_io_env_clear(struct cbox_io_env *env) 30 | { 31 | env->srate = 0; 32 | env->buffer_size = 0; 33 | env->input_count = 0; 34 | env->output_count = 0; 35 | } 36 | 37 | static inline void cbox_io_env_copy(struct cbox_io_env *dest, const struct cbox_io_env *src) 38 | { 39 | dest->srate = src->srate; 40 | dest->buffer_size = src->buffer_size; 41 | dest->input_count = src->input_count; 42 | dest->output_count = src->output_count; 43 | } 44 | 45 | #endif 46 | 47 | -------------------------------------------------------------------------------- /auxbus.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_AUXBUS_H 20 | #define CBOX_AUXBUS_H 21 | 22 | #include "dom.h" 23 | #include "module.h" 24 | 25 | struct cbox_scene; 26 | 27 | CBOX_EXTERN_CLASS(cbox_aux_bus) 28 | 29 | struct cbox_aux_bus 30 | { 31 | CBOX_OBJECT_HEADER() 32 | struct cbox_command_target cmd_target; 33 | struct cbox_scene *owner; 34 | 35 | gchar *name; 36 | struct cbox_module *module; 37 | int refcount; 38 | 39 | float *input_bufs[2]; 40 | float *output_bufs[2]; 41 | }; 42 | 43 | extern struct cbox_aux_bus *cbox_aux_bus_load(struct cbox_scene *scene, const char *name, struct cbox_rt *rt, GError **error); 44 | extern void cbox_aux_bus_ref(struct cbox_aux_bus *bus); 45 | extern void cbox_aux_bus_unref(struct cbox_aux_bus *bus); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /app.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_APP_H 20 | #define CBOX_APP_H 21 | 22 | #include "cmd.h" 23 | #include "dom.h" 24 | #include "io.h" 25 | #include "rt.h" 26 | #include 27 | 28 | struct cbox_song; 29 | struct cbox_tarpool; 30 | 31 | struct cbox_app 32 | { 33 | struct cbox_io io; 34 | struct cbox_document *document; 35 | struct cbox_rt *rt; 36 | struct cbox_engine *engine; 37 | struct cbox_command_target cmd_target; 38 | struct cbox_command_target config_cmd_target; 39 | gchar *current_scene_name; 40 | struct cbox_tarpool *tarpool; 41 | }; 42 | 43 | struct cbox_menu; 44 | 45 | extern struct cbox_app app; 46 | 47 | struct cbox_menu *create_main_menu(void); 48 | 49 | extern gboolean cbox_app_on_idle(struct cbox_command_target *fb, GError **error); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /sampler_api_example2.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from calfbox import cbox 5 | 6 | def cmd_dumper(cmd, fb, args): 7 | print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) 8 | 9 | cbox.init_engine("") #empty string so cbox doesn't look for the .cboxrc file 10 | cbox.start_audio(cmd_dumper) 11 | 12 | global Document 13 | Document = cbox.Document 14 | 15 | scene = Document.get_scene() 16 | scene.clear() 17 | instrument = scene.add_new_instrument_layer("test_sampler", "sampler").get_instrument() 18 | pgm_no = instrument.engine.get_unused_program() 19 | pgm = instrument.engine.load_patch_from_file(pgm_no, 'synthbass.sfz', 'SynthBass') 20 | 21 | # This is not needed, it's here to test for a bug where reloading a program at 22 | # the same slot didn't return the newly loaded program object. 23 | pgm = instrument.engine.load_patch_from_file(pgm_no, 'synthbass.sfz', 'SynthBass') 24 | assert pgm 25 | 26 | instrument.engine.set_patch(1, pgm_no) 27 | print (instrument.engine.get_patches()) 28 | print (instrument.get_output_slot(0)) 29 | print (instrument.get_output_slot(0).status()) 30 | instrument.get_output_slot(0).set_insert_engine("reverb") 31 | print (instrument.get_output_slot(0).status()) 32 | instrument.get_output_slot(0).engine.cmd("/wet_amt", None, 1.0) 33 | for i in pgm.get_global().get_children()[0].get_children(): 34 | print ("", i.as_string()) 35 | for j in i.get_children(): 36 | print ("", j.as_string()) 37 | 38 | print("Ready!") 39 | 40 | while True: 41 | cbox.call_on_idle(cmd_dumper) 42 | -------------------------------------------------------------------------------- /adhoc_example.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import sys 6 | import struct 7 | import time 8 | import unittest 9 | 10 | sys.path = ["./py"] + sys.path 11 | 12 | import cbox 13 | 14 | def cmd_dumper(cmd, fb, args): 15 | print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) 16 | cbox.init_engine("") #empty string so cbox doesn't look for the .cboxrc file 17 | cbox.start_audio(cmd_dumper) 18 | 19 | global Document 20 | global Transport 21 | Document = cbox.Document 22 | Transport = cbox.Transport 23 | 24 | song = Document.get_song() 25 | 26 | # Delete all the tracks and patterns 27 | song.clear() 28 | 29 | # Create a binary blob that contains the MIDI events 30 | pblob = bytes() 31 | for noteindex in range(20): 32 | # note on 33 | pblob += cbox.Pattern.serialize_event(noteindex * 12, 0x90, 36+noteindex*3, 127) 34 | # note off 35 | pblob += cbox.Pattern.serialize_event(noteindex * 12 + 11, 0x90, 36+noteindex*3, 0) 36 | 37 | # This will be the length of the pattern (in pulses). It should be large enough 38 | # to fit all the events 39 | pattern_len = 10 * 24 * 2 40 | 41 | # Create a new pattern object using events from the blob 42 | pattern = song.pattern_from_blob(pblob, pattern_len) 43 | 44 | retrig = 10 45 | i = 0 46 | while i < 50: 47 | i += 1 48 | retrig -= 1 49 | if retrig <= 0: 50 | print ("Triggering adhoc pattern with ID 1") 51 | Document.get_scene().play_pattern(pattern, 240, 0) 52 | retrig = 5 53 | # Query JACK ports, new USB devices etc. 54 | cbox.call_on_idle() 55 | time.sleep(0.1) 56 | -------------------------------------------------------------------------------- /errors.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_ERRORS_H 20 | #define CBOX_ERRORS_H 21 | 22 | #include 23 | #include "cmd.h" 24 | 25 | #define CBOX_MODULE_ERROR cbox_module_error_quark() 26 | 27 | enum CboxModuleError 28 | { 29 | CBOX_MODULE_ERROR_FAILED, 30 | CBOX_MODULE_ERROR_INVALID_COMMAND, 31 | CBOX_MODULE_ERROR_OUT_OF_RANGE, 32 | }; 33 | 34 | struct cbox_osc_command; 35 | 36 | extern GQuark cbox_module_error_quark(void); 37 | extern void cbox_force_error(GError **error); 38 | extern void cbox_print_error(GError *error); 39 | extern void cbox_print_error_if(GError *error); 40 | extern gboolean cbox_set_command_error(GError **error, const struct cbox_osc_command *cmd); 41 | extern gboolean cbox_set_command_error_with_msg(GError **error, const struct cbox_osc_command *cmd, const char *extra_msg); 42 | extern gboolean cbox_set_range_error(GError **error, const char *param, double minv, double maxv); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /tests.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | struct test_env 7 | { 8 | struct cbox_document *doc; 9 | struct cbox_engine *engine; 10 | void *arg; 11 | gchar *context; 12 | jmp_buf on_fail; 13 | }; 14 | 15 | #define test_assert(condition) \ 16 | if (!(condition)) \ 17 | test_assert_failed(env, __FILE__, __LINE__, #condition); 18 | 19 | #define STR_FORMAT_int "%d" 20 | #define STR_FORMAT_unsigned "%u" 21 | #define STR_FORMAT_uint32_t PRIu32 22 | 23 | #define test_assert_equal(type, val1, val2) \ 24 | do { \ 25 | type _v1 = (val1), _v2 = (val2); \ 26 | if ((_v1) != (_v2)) \ 27 | test_assert_failed_free(env, __FILE__, __LINE__, g_strdup_printf("%s equal to " STR_FORMAT_##type ", not " STR_FORMAT_##type, #val1, _v1, _v2)); \ 28 | } while(0) 29 | 30 | #define test_assert_equal_str(val1, val2) \ 31 | do { \ 32 | const char *_v1 = (val1), *_v2 = (val2); \ 33 | if (strcmp(_v1, _v2)) \ 34 | test_assert_failed_free(env, __FILE__, __LINE__, g_strdup_printf("%s equal to '%s', not '%s'", #val1, _v1, _v2)); \ 35 | } while(0) 36 | 37 | #define test_assert_no_error(error) \ 38 | do { \ 39 | if (error) { \ 40 | gchar *copy = g_strdup(error->message); \ 41 | g_error_free(error); \ 42 | test_assert_failed_free(env, __FILE__, __LINE__, copy); \ 43 | } \ 44 | } while(0) 45 | 46 | extern void test_assert_failed(struct test_env *env, const char *file, int line, const char *check); 47 | extern void test_assert_failed_free(struct test_env *env, const char *file, int line, gchar *check); 48 | 49 | -------------------------------------------------------------------------------- /ui.c: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include "ui.h" 20 | 21 | #if USE_NCURSES 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | void cbox_ui_start() 28 | { 29 | initscr(); 30 | cbreak(); 31 | noecho(); 32 | start_color(); 33 | keypad(stdscr, TRUE); 34 | } 35 | 36 | int cbox_ui_run(struct cbox_ui_page *page) 37 | { 38 | int ch, res; 39 | if (page->draw) 40 | page->draw(page); 41 | if (page->on_idle) 42 | halfdelay(1); 43 | while(1) 44 | { 45 | ch = getch(); 46 | if (ch == ERR && page->on_idle) 47 | { 48 | res = page->on_idle(page); 49 | if (res != 0) 50 | return res; 51 | continue; 52 | } 53 | res = page->on_key(page, ch); 54 | if (res != 0) 55 | return res; 56 | } 57 | cbreak(); 58 | } 59 | 60 | void cbox_ui_stop() 61 | { 62 | endwin(); 63 | } 64 | 65 | #endif -------------------------------------------------------------------------------- /sfzparser.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_SFZPARSER_H 20 | #define CBOX_SFZPARSER_H 21 | 22 | #include 23 | 24 | #define CBOX_SFZPARSER_ERROR cbox_sfz_parser_error_quark() 25 | 26 | enum CboxSfzParserError 27 | { 28 | CBOX_SFZ_PARSER_ERROR_FAILED, 29 | CBOX_SFZ_PARSER_ERROR_INVALID_CHAR, 30 | CBOX_SFZ_PARSER_ERROR_INVALID_HEADER, 31 | }; 32 | 33 | struct cbox_tarfile; 34 | 35 | struct sfz_parser_client 36 | { 37 | void *user_data; 38 | gboolean (*token)(struct sfz_parser_client *client, const char *token, GError **error); 39 | gboolean (*key_value)(struct sfz_parser_client *client, const char *key, const char *value); 40 | }; 41 | 42 | extern gboolean load_sfz(const char *name, struct cbox_tarfile *tarfile, struct sfz_parser_client *c, GError **error); 43 | extern gboolean load_sfz_from_string(const char *buf, int len, struct sfz_parser_client *c, GError **error); 44 | 45 | extern GQuark cbox_sfz_parser_error_quark(void); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /send_pattern_to_midi_out_example.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from calfbox import cbox 5 | 6 | def cmd_dumper(cmd, fb, args): 7 | print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) 8 | 9 | cbox.init_engine("") #empty string so cbox doesn't look for the .cboxrc file 10 | cbox.start_audio(cmd_dumper) 11 | 12 | outportname = "CboxSendPattern" 13 | cboxMidiOutUuid = cbox.JackIO.create_midi_output(outportname) #Add a named midi out port 14 | cbox.JackIO.rename_midi_output(cboxMidiOutUuid, outportname) #For good measure. 15 | outputScene = cbox.Document.get_engine().new_scene() #Create a new scene that will play the pattern. The pattern is not saved in the scene, it is not a track or so. 16 | outputScene.clear() #For good measure. 17 | outputScene.add_new_midi_layer(cboxMidiOutUuid) #Connect the scene to our midi output port. Without this there will be no midi out. 18 | 19 | 20 | # Send 8 pitches 0x90 with velocity 0 21 | # Create a binary blob that contains the MIDI events 22 | pblob = bytes() 23 | for pitch in range(0,8): 24 | # note on 25 | pblob += cbox.Pattern.serialize_event(1, 0x90, pitch, 0) #tick in pattern, midi, pitch, velocity 26 | # Create a new pattern object using events from the blob 27 | allNoteOnZeroPattern = cbox.Document.get_song().pattern_from_blob(pblob, 0) #0 ticks. 28 | 29 | 30 | print ("\nThis example sends midi events from a pattern without any tracks. Rolling transport or not doesn't matter.") 31 | print("Ready!") 32 | counter = 0 #To add delay 33 | while True: 34 | cbox.call_on_idle(cmd_dumper) 35 | if counter > 10**5 * 4 : 36 | print ("Send pattern") 37 | outputScene.play_pattern(allNoteOnZeroPattern, 150.0) #150 tempo 38 | counter = 0 39 | counter += 1 40 | -------------------------------------------------------------------------------- /experiments/printAllMidiEventsSpecificPort.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) 5 | 6 | This code is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | """ 19 | 20 | from meta import initCbox, start, connectPhysicalKeyboards 21 | 22 | def processMidiIn(eventLoop): 23 | eventList = cbox.JackIO.get_new_events(cboxMidiPortUid) 24 | if eventList: 25 | for (address, uninterestingStuff, event) in eventList: #we are only interested in the event, which is a list again. 26 | print (address, "event:", event, "playback:", cbox.Transport.status().playing) 27 | eventLoop.call_later(0.1, processMidiIn, eventLoop) 28 | 29 | scene, cbox, eventLoop = initCbox("test01", internalEventProcessor=False, commonMidiInput=False) 30 | cboxMidiPortUid = cbox.JackIO.create_midi_input("customInput") 31 | cbox.JackIO.set_appsink_for_midi_input(cboxMidiPortUid, True) #This sounds like a program wide sink, but it is needed for every port. 32 | cbox.JackIO.route_midi_input(cboxMidiPortUid, scene.uuid) 33 | eventLoop.call_later(0.1, processMidiIn, eventLoop) #100ms 34 | connectPhysicalKeyboards(port="customInput") 35 | start() 36 | -------------------------------------------------------------------------------- /pattern-maker.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_PATTERN_MAKER_H 20 | #define CBOX_PATTERN_MAKER_H 21 | 22 | #include 23 | 24 | CBOX_EXTERN_CLASS(cbox_midi_pattern_maker) 25 | 26 | struct cbox_midi_pattern; 27 | struct cbox_midi_pattern_maker; 28 | struct cbox_song; 29 | 30 | extern struct cbox_midi_pattern_maker *cbox_midi_pattern_maker_new(uint64_t ppqn_factor); 31 | extern void cbox_midi_pattern_maker_destroy(struct cbox_midi_pattern_maker *maker); 32 | 33 | extern gboolean cbox_midi_pattern_maker_load_smf(struct cbox_midi_pattern_maker *maker, const char *filename, int *length, GError **error); 34 | 35 | extern void cbox_midi_pattern_maker_add(struct cbox_midi_pattern_maker *maker, uint32_t time, uint8_t cmd, uint8_t val1, uint8_t val2); 36 | extern void cbox_midi_pattern_maker_add_mem(struct cbox_midi_pattern_maker *maker, uint32_t time, const uint8_t *src, uint32_t len); 37 | 38 | extern struct cbox_midi_pattern *cbox_midi_pattern_maker_create_pattern(struct cbox_midi_pattern_maker *maker, struct cbox_song *owner, gchar *name); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /wav2c.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import array 3 | import sys 4 | import wave 5 | 6 | def u2s(value): 7 | return value if value < 32768 else value - 65536 8 | 9 | parser = argparse.ArgumentParser(description="Convert a WAV file into a C function") 10 | parser.add_argument('input', type=str, help="WAV file to convert") 11 | parser.add_argument('function', type=str, help="Function to generate") 12 | parser.add_argument('name', type=str, help="Name for registering the waveform in the wavebank") 13 | 14 | args = parser.parse_args() 15 | name = args.name 16 | funcname = args.function 17 | f = wave.open(args.input, "rb") 18 | channels = f.getnchannels() 19 | width = f.getsampwidth() 20 | rate = f.getframerate() 21 | frames = f.getnframes() 22 | data = f.readframes(frames) 23 | f.close() 24 | 25 | if width == 2: 26 | data = array.array("h", data) 27 | elif width == 3: 28 | # Convert 24 bits to 16 29 | idata = array.array("B", data) 30 | data = [] 31 | for i in range(0, len(idata), 3): 32 | data.append(u2s(idata[i + 1] + 256 * idata[i + 2])) 33 | else: 34 | sys.stderr.write(f"Unexpected data format - {8 * width} bits instead of 16\n") 35 | sys.exit(1) 36 | 37 | data = list(data) 38 | 39 | print ('#include ') 40 | print ('#include ') 41 | print ('struct cbox_waveform;') 42 | print ('extern struct cbox_waveform *cbox_wavebank_add_mem_waveform(const char *name, void *data, uint32_t frames, int sample_rate, int channels, bool looped, uint32_t loop_start, uint32_t loop_end);') 43 | print ("static short sample_data[] = {") 44 | for i in range(0, frames * channels, 16): 45 | print (repr(data[i : i + 16])[1:-1] + ",") 46 | 47 | args = f'"{name}", sample_data, {frames}, {rate}, {channels}, 0, -1, {frames}' 48 | 49 | print ("""\ 50 | }; 51 | 52 | struct cbox_waveform *FUNCTION(void) { 53 | return cbox_wavebank_add_mem_waveform(ARGS); 54 | } 55 | """.replace("ARGS", args).replace("FUNCTION", funcname)) 56 | 57 | -------------------------------------------------------------------------------- /py/sfzparser.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | class SFZRegion(dict): 5 | def __init__(self, group): 6 | dict.__init__(self) 7 | self.group = group 8 | 9 | def merged(self): 10 | if self.group is None: 11 | return dict(self) 12 | v = dict(self) 13 | v.update(self.group) 14 | return v 15 | 16 | def __str__(self): 17 | return "(" + str(self.group) + ") : " + dict.__str__(self.merged()) 18 | 19 | class SFZContext: 20 | def __init__(self): 21 | self.group = None 22 | self.region = None 23 | 24 | class SFZ: 25 | def __init__(self): 26 | self.regions = [] 27 | 28 | def load(self, fname): 29 | self.parse(open(fname, "r").read()) 30 | 31 | def parse(self, data): 32 | context = SFZContext() 33 | for ptype, pdata in re.findall("<(region|group)>\s*([^<]*)", data, re.S): 34 | self.parse_part(ptype, pdata.strip(), context) 35 | 36 | def parse_part(self, ptype, pdata, context): 37 | if ptype == 'group': 38 | context.group = {} 39 | context.region = None 40 | target = context.group 41 | else: 42 | context.region = SFZRegion(context.group) 43 | target = context.region 44 | 45 | pairs = re.split("\s+([a-zA-Z_0-9]+)=", " "+pdata)[1:] 46 | for i in range(0, len(pairs), 2): 47 | target[pairs[i]] = pairs[i + 1] 48 | if ptype == 'region': 49 | self.regions.append(target) 50 | 51 | def find_sample_in_path(path, sample): 52 | jpath = os.path.join(path, sample) 53 | if os.path.exists(jpath): 54 | return jpath 55 | path, sample = os.path.split(jpath) 56 | sample = sample.lower() 57 | files = os.path.listdir(path) 58 | for f in files: 59 | if f.lower() == sample: 60 | return os.path.join(path, f) 61 | return None 62 | 63 | -------------------------------------------------------------------------------- /sampler_api_example.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import sys 6 | import struct 7 | import time 8 | import unittest 9 | 10 | sys.path = ["./py"] + sys.path 11 | 12 | import cbox 13 | 14 | def cmd_dumper(cmd, fb, args): 15 | print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) 16 | 17 | cbox.init_engine("") #empty string so cbox doesn't look for the .cboxrc file 18 | cbox.start_audio(cmd_dumper) 19 | 20 | global Document 21 | Document = cbox.Document 22 | 23 | scene = Document.get_scene() 24 | scene.clear() 25 | instrument = scene.add_new_instrument_layer("test_sampler", "sampler").get_instrument() 26 | 27 | npfs = instrument.engine.load_patch_from_string(0, '.', '', 'new_patch') 28 | instrument.engine.set_patch(1, 0) 29 | 30 | mgrp = npfs.get_global().get_children()[0] 31 | g1 = mgrp.new_child() 32 | g1.set_param("cutoff", "100") 33 | g1.set_param("resonance", "6") 34 | g1.set_param("fil_type", "lpf_4p") 35 | g1.set_param("fileg_start", "50") 36 | g1.set_param("fileg_attack", "0.01") 37 | g1.set_param("fileg_decay", "0.2") 38 | g1.set_param("fileg_sustain", "20") 39 | g1.set_param("fileg_depth", "5400") 40 | g1.set_param("fileg_release", "10") 41 | g1.set_param("ampeg_release", "0.1") 42 | g1.set_param("amp_veltrack", "0") 43 | g1.set_param("volume", "-12") 44 | g1.set_param("fileg_depthcc14", "-5400") 45 | 46 | #g1.set_param("cutoff", "1000") 47 | #g1.set_param("fillfo_freq", "4") 48 | #g1.set_param("fillfo_depth", "2400") 49 | #g1.set_param("fillfo_wave", "12") 50 | #g1.set_param("fillfo_freqcc2", "4") 51 | 52 | r1 = g1.new_child() 53 | r1.set_param("sample", "*saw") 54 | r1.set_param("transpose", "0") 55 | r1.set_param("tune", "5") 56 | r1.set_param("gain_cc17", "12") 57 | 58 | r2 = g1.new_child() 59 | r2.set_param("sample", "*sqr") 60 | r2.set_param("transpose", "12") 61 | r2.set_param("gain_cc17", "-12") 62 | 63 | print(instrument.engine.status()) 64 | 65 | print("Ready!") 66 | 67 | while True: 68 | cbox.call_on_idle(cmd_dumper) 69 | -------------------------------------------------------------------------------- /usb_api_example.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from calfbox import cbox 5 | 6 | def cmd_dumper(cmd, fb, args): 7 | print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) 8 | 9 | cbox.init_engine() 10 | 11 | cbox.Config.add_section("drumpattern:pat1", """ 12 | title=Straight - Verse 13 | beats=4 14 | track1=bd 15 | track2=sd 16 | track3=hh 17 | track4=ho 18 | bd_note=c1 19 | sd_note=d1 20 | hh_note=f#1 21 | ho_note=a#1 22 | bd_trigger=9... .... 9.6. .... 23 | sd_trigger=.... 9..5 .2.. 9... 24 | hh_trigger=9353 7353 7353 73.3 25 | ho_trigger=.... .... .... ..3. 26 | """) 27 | 28 | cbox.Config.set("io", "use_usb", 1) 29 | cbox.start_audio(cmd_dumper) 30 | 31 | global Document 32 | Document = cbox.Document 33 | 34 | status = cbox.JackIO.status() 35 | client_name = status.client_name 36 | print ("Client type: %s" % status.client_type) 37 | print ("Client name: %s" % client_name) 38 | print ("Audio inputs: %d, outputs: %d" % (status.audio_inputs, status.audio_outputs)) 39 | print ("Period: %d frames" % (status.buffer_size)) 40 | print ("Sample rate: %d frames/sec" % (status.sample_rate)) 41 | print ("Output resolution: %d bits/sample" % (status.output_resolution)) 42 | print ("MIDI input devices: %s" % (status.midi_input)) 43 | #cbox.JackIO.create_midi_output('drums', 'system:midi_playback_1') 44 | 45 | scene = Document.get_scene() 46 | scene.clear() 47 | instrument = scene.add_new_instrument_layer("test_sampler", "sampler").get_instrument() 48 | pgm_no = instrument.engine.get_unused_program() 49 | pgm = instrument.engine.load_patch_from_file(pgm_no, 'synthbass.sfz', 'SynthBass') 50 | instrument.engine.set_patch(1, pgm_no) 51 | instrument.engine.set_patch(10, pgm_no) 52 | 53 | song = Document.get_song() 54 | track = song.add_track() 55 | pattern = song.load_drum_pattern("pat1") 56 | track.add_clip(0, 0, pattern.status().loop_end, pattern) 57 | song.set_loop(0, pattern.status().loop_end) 58 | song.update_playback() 59 | cbox.Transport.play() 60 | 61 | print("Ready!") 62 | 63 | while True: 64 | cbox.call_on_idle(cmd_dumper) 65 | -------------------------------------------------------------------------------- /jack_output_routing.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from calfbox import cbox 5 | import time 6 | 7 | def cmd_dumper(cmd, fb, args): 8 | print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) 9 | 10 | cbox.init_engine("") #empty string so cbox doesn't look for the .cboxrc file 11 | 12 | cbox.Config.set("io", "use_usb", 0) 13 | cbox.Config.set("io", "midi", "*.*") 14 | 15 | cbox.start_audio(cmd_dumper) 16 | 17 | global Document 18 | Document = cbox.Document 19 | 20 | status = cbox.JackIO.status() 21 | client_name = status.client_name 22 | print ("Client name: %s" % client_name) 23 | print ("Audio inputs: %d, outputs: %d" % (status.audio_inputs, status.audio_outputs)) 24 | print ("Sample rate: %d frames/sec" % (status.sample_rate)) 25 | print ("JACK period: %d frames" % (status.buffer_size)) 26 | 27 | left_dry = cbox.JackIO.create_audio_output('left_dry') 28 | right_dry = cbox.JackIO.create_audio_output('right_dry') 29 | left_wet = cbox.JackIO.create_audio_output('left_wet', '#1') 30 | right_wet = cbox.JackIO.create_audio_output('right_wet', '#2') 31 | router_dry = cbox.JackIO.create_audio_output_router(left_dry, right_dry) 32 | assert type(router_dry) is cbox.DocRecorder 33 | router_wet = cbox.JackIO.create_audio_output_router(left_wet, right_wet) 34 | assert type(router_wet) is cbox.DocRecorder 35 | 36 | scene = Document.get_scene() 37 | scene.clear() 38 | instrument = scene.add_new_instrument_layer("test_sampler", "tonewheel_organ").get_instrument() 39 | instrument.get_output_slot(0).set_insert_engine("delay") 40 | instrument.get_output_slot(0).rec_dry.attach(router_dry) 41 | instrument.get_output_slot(0).rec_wet.attach(router_wet) 42 | assert router_dry.uuid == instrument.get_output_slot(0).rec_dry.status().handler[0].uuid 43 | assert router_wet.uuid == instrument.get_output_slot(0).rec_wet.status().handler[0].uuid 44 | router_wet.set_gain(-3.0) 45 | assert router_wet.status().gain == -3 46 | 47 | print("Ready!") 48 | 49 | while True: 50 | events = cbox.get_new_events() 51 | if events: 52 | print (events) 53 | time.sleep(0.05) 54 | -------------------------------------------------------------------------------- /menu.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_MENU_H 20 | #define CBOX_MENU_H 21 | 22 | #include "config.h" 23 | 24 | #if USE_NCURSES 25 | 26 | #include 27 | #include 28 | 29 | #include "menuitem.h" 30 | #include "ui.h" 31 | 32 | struct cbox_menu; 33 | struct cbox_menu_item; 34 | struct cbox_menu_page; 35 | 36 | struct cbox_menu_state 37 | { 38 | struct cbox_menu_page *page; 39 | struct cbox_menu *menu; 40 | guint cursor; 41 | int yoffset, yspace; 42 | struct cbox_menu_measure size; 43 | WINDOW *window; 44 | void *context; 45 | struct cbox_menu_state *caller; 46 | int menu_is_temporary; 47 | }; 48 | 49 | extern struct cbox_menu *cbox_menu_new(void); 50 | extern struct cbox_menu_item *cbox_menu_add_item(struct cbox_menu *menu, struct cbox_menu_item *item); 51 | extern void cbox_menu_destroy(struct cbox_menu *menu); 52 | 53 | extern struct cbox_menu_state *cbox_menu_state_new(struct cbox_menu_page *page, struct cbox_menu *menu, WINDOW *window, void *context); 54 | extern void cbox_menu_state_destroy(struct cbox_menu_state *st); 55 | 56 | struct cbox_menu_page 57 | { 58 | struct cbox_ui_page page; 59 | struct cbox_menu_state *state; 60 | }; 61 | 62 | extern struct cbox_menu_page *cbox_menu_page_new(void); 63 | extern void cbox_menu_page_destroy(struct cbox_menu_page *st); 64 | 65 | #endif 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /song.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_SONG_H 20 | #define CBOX_SONG_H 21 | 22 | #include "dom.h" 23 | #include "pattern.h" 24 | 25 | CBOX_EXTERN_CLASS(cbox_song) 26 | 27 | struct cbox_track; 28 | 29 | struct cbox_master_track_item 30 | { 31 | uint32_t duration_ppqn; 32 | // May be zero (= no change) 33 | double tempo; 34 | // Either both are zero (= no change) or both are non-zero 35 | int timesig_num, timesig_denom; 36 | }; 37 | 38 | struct cbox_master_track 39 | { 40 | GList *items; 41 | }; 42 | 43 | struct cbox_song 44 | { 45 | CBOX_OBJECT_HEADER() 46 | struct cbox_command_target cmd_target; 47 | GList *master_track_items; 48 | GList *tracks; 49 | GList *patterns; 50 | gchar *lyrics_sheet, *chord_sheet; 51 | uint32_t loop_start_ppqn, loop_end_ppqn; 52 | }; 53 | 54 | extern struct cbox_song *cbox_song_new(struct cbox_document *document); 55 | extern void cbox_song_add_track(struct cbox_song *song, struct cbox_track *track); 56 | extern void cbox_song_remove_track(struct cbox_song *song, struct cbox_track *track); 57 | extern void cbox_song_clear(struct cbox_song *song); 58 | extern void cbox_song_use_looped_pattern(struct cbox_song *song, struct cbox_midi_pattern *pattern); 59 | extern void cbox_song_set_mti(struct cbox_song *song, uint32_t pos, double tempo, int timesig_num, int timesig_denom); 60 | extern void cbox_song_destroy(struct cbox_song *song); 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /experiments/playPatternsAsMeasures.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) 5 | 6 | This code is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | """ 19 | 20 | from meta import ly2cbox, cboxSetTrack, initCbox, start, D4 21 | 22 | scene, cbox, eventLoop = initCbox("test02") 23 | 24 | #Generate Music 25 | music = "c'4 d' e' f'" 26 | cboxBlob, durationInTicks = ly2cbox(music) 27 | #cboxSetTrack("someInstrument", durationInTicks, pattern) 28 | 29 | oneMeasureInTicks = D4 * 4 30 | patternList = [ 31 | cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks), 32 | cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks), 33 | cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks), 34 | cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks), 35 | cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks), 36 | cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks), 37 | cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks), 38 | cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks), 39 | ] 40 | 41 | cboxSetTrack("metronome", oneMeasureInTicks, patternList) 42 | 43 | def userfunction(): 44 | D4 = 210 * 2**8 45 | MEASURE = 4 * D4 46 | cbox.Transport.stop() 47 | cbox.Document.get_song().update_playback() 48 | cbox.Transport.seek_ppqn(4 * MEASURE + 2 * D4 ) #4th measure in the middle 49 | cbox.Transport.play() 50 | 51 | start(userfunction=userfunction) 52 | -------------------------------------------------------------------------------- /track.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_TRACK_H 20 | #define CBOX_TRACK_H 21 | 22 | #include "dom.h" 23 | 24 | CBOX_EXTERN_CLASS(cbox_track_item) 25 | CBOX_EXTERN_CLASS(cbox_track) 26 | 27 | struct cbox_midi_pattern; 28 | struct cbox_track; 29 | 30 | struct cbox_track_item 31 | { 32 | CBOX_OBJECT_HEADER() 33 | struct cbox_command_target cmd_target; 34 | struct cbox_track *owner; 35 | uint32_t time; 36 | struct cbox_midi_pattern *pattern; 37 | uint32_t offset; 38 | uint32_t length; 39 | }; 40 | 41 | struct cbox_track 42 | { 43 | CBOX_OBJECT_HEADER() 44 | struct cbox_command_target cmd_target; 45 | gchar *name; 46 | gboolean external_output_set; 47 | struct cbox_uuid external_output; 48 | GList *items; 49 | struct cbox_song *owner; 50 | struct cbox_track_playback *pb; 51 | uint32_t generation; 52 | gboolean mute; 53 | }; 54 | 55 | extern struct cbox_track *cbox_track_new(struct cbox_document *document); 56 | extern struct cbox_track_item *cbox_track_add_item(struct cbox_track *track, uint32_t time, struct cbox_midi_pattern *pattern, uint32_t offset, uint32_t length); 57 | extern void cbox_track_update_playback(struct cbox_track *track, struct cbox_master *master); 58 | extern void cbox_track_clear_clips(struct cbox_track *track); 59 | extern void cbox_track_set_dirty(struct cbox_track *track); 60 | extern void cbox_track_item_set_dirty(struct cbox_track_item *track_item); 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /pattern.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_PATTERN_H 20 | #define CBOX_PATTERN_H 21 | 22 | #include "dom.h" 23 | #include "master.h" 24 | #include "midi.h" 25 | 26 | CBOX_EXTERN_CLASS(cbox_midi_pattern) 27 | 28 | struct cbox_blob; 29 | struct cbox_song; 30 | 31 | struct cbox_midi_pattern 32 | { 33 | CBOX_OBJECT_HEADER() 34 | struct cbox_command_target cmd_target; 35 | struct cbox_song *owner; 36 | gchar *name; 37 | struct cbox_midi_event *events; 38 | uint32_t event_count; 39 | int loop_end; 40 | }; 41 | 42 | struct cbox_blob_serialized_event 43 | { 44 | int32_t time; 45 | unsigned char len, cmd, byte1, byte2; 46 | }; 47 | 48 | extern struct cbox_midi_pattern *cbox_midi_pattern_new_metronome(struct cbox_song *song, int ts, uint64_t ppqn_factor); 49 | extern struct cbox_midi_pattern *cbox_midi_pattern_load(struct cbox_song *song, const char *name, int is_drum, uint64_t ppqn_factor); 50 | extern struct cbox_midi_pattern *cbox_midi_pattern_load_track(struct cbox_song *song, const char *name, int is_drum, uint64_t ppqn_factor); 51 | extern struct cbox_midi_pattern *cbox_midi_pattern_new_from_blob(struct cbox_song *song, const struct cbox_blob *blob, int length, uint64_t ppqn_factor); 52 | 53 | extern struct cbox_blob *cbox_midi_pattern_to_blob(struct cbox_midi_pattern *pat, int *length); 54 | 55 | extern gboolean cbox_midi_pattern_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /seq-adhoc.c: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2013 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include "master.h" 20 | #include "seq.h" 21 | 22 | struct cbox_adhoc_pattern *cbox_adhoc_pattern_new(struct cbox_engine *engine, int id, struct cbox_midi_pattern *pattern) 23 | { 24 | struct cbox_adhoc_pattern *ap = calloc(1, sizeof(struct cbox_adhoc_pattern)); 25 | ap->next = NULL; 26 | ap->pattern = pattern; 27 | ap->pattern_playback = cbox_midi_pattern_playback_new(pattern); 28 | ap->master = cbox_master_new(engine); 29 | cbox_midi_playback_active_notes_init(&ap->active_notes); 30 | cbox_midi_clip_playback_init(&ap->playback, &ap->active_notes, ap->master); 31 | cbox_midi_buffer_init(&ap->output_buffer); 32 | ap->id = id; 33 | ap->completed = FALSE; 34 | 35 | return ap; 36 | } 37 | 38 | void cbox_adhoc_pattern_render(struct cbox_adhoc_pattern *ap, uint32_t offset, uint32_t nsamples) 39 | { 40 | if (ap->completed) 41 | { 42 | cbox_midi_playback_active_notes_release(&ap->active_notes, &ap->output_buffer, NULL); 43 | return; 44 | } 45 | if (ap->playback.pos >= ap->playback.pattern->event_count) 46 | ap->completed = TRUE; 47 | cbox_midi_clip_playback_render(&ap->playback, &ap->output_buffer, offset, nsamples, FALSE); 48 | } 49 | 50 | void cbox_adhoc_pattern_destroy(struct cbox_adhoc_pattern *ap) 51 | { 52 | // XXXKF decide on pattern ownership and general object lifetime issues 53 | cbox_midi_pattern_playback_destroy(ap->playback.pattern); 54 | cbox_master_destroy(ap->master); 55 | free(ap); 56 | } 57 | -------------------------------------------------------------------------------- /instr.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_INSTR_H 20 | #define CBOX_INSTR_H 21 | 22 | #include "dspmath.h" 23 | #include "recsrc.h" 24 | 25 | CBOX_EXTERN_CLASS(cbox_instrument) 26 | 27 | struct cbox_module; 28 | struct cbox_rt; 29 | struct cbox_scene; 30 | struct cbox_instruments; 31 | 32 | struct cbox_instrument_output 33 | { 34 | struct cbox_module *insert; 35 | int output_bus; 36 | struct cbox_gain gain_obj; 37 | struct cbox_recording_source rec_dry, rec_wet; 38 | }; 39 | 40 | struct cbox_instrument 41 | { 42 | CBOX_OBJECT_HEADER() 43 | struct cbox_command_target cmd_target; 44 | struct cbox_module *module; 45 | struct cbox_instrument_output *outputs; 46 | struct cbox_scene *scene; 47 | int refcount; 48 | gchar **aux_output_names; 49 | struct cbox_aux_bus **aux_outputs; 50 | uint32_t aux_output_count; 51 | }; 52 | 53 | extern void cbox_instrument_unref_aux_buses(struct cbox_instrument *instrument); 54 | extern void cbox_instrument_disconnect_aux_bus(struct cbox_instrument *instrument, struct cbox_aux_bus *bus); 55 | extern void cbox_instrument_destroy_if_unused(struct cbox_instrument *instrument); 56 | extern gboolean cbox_instrument_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error); 57 | 58 | extern void cbox_instrument_output_init(struct cbox_instrument_output *output, struct cbox_scene *scene, uint32_t max_numsamples); 59 | extern void cbox_instrument_output_uninit(struct cbox_instrument_output *output); 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Calfbox 2 | 3 | Website: https://github.com/kfoltman/calfbox 4 | 5 | Calfbox, the "open source musical instrument", offers assorted music-related code. 6 | 7 | Originally intended as a standalone instrument for Linux and embedded devices (USB TV Sticks) 8 | it can be used as Python module as well. 9 | 10 | # Packaging 11 | If you are a packager and want to create a binary package for your distribution please package only the python module. 12 | The binary executable is not maintained and untested at the moment. It should not be used by anyone. 13 | 14 | 15 | # Calfbox as Python Module 16 | Calfbox can be used as a Python module that can be imported to create short scripts or 17 | full fledged programs ( https://www.laborejo.org/software ). 18 | 19 | Most notably it features a midi sequencer and an audio sampler (for sfz files and sf2 via fluidsynth). 20 | 21 | ## Building 22 | 23 | A convenience script `cleanpythonbuild.py` has been supplied to quickly build and install the cbox python module. 24 | 25 | ``` 26 | make clean 27 | rm build -rf 28 | sh autogen.sh 29 | ./configure 30 | make 31 | python3 setup.py build 32 | sudo python3 setup.py install 33 | ``` 34 | 35 | ## How to write programs with cbox 36 | You can find several `.py` files in the main directory, such as `sampler_api_example.py` or 37 | `song_api_example.py`. 38 | 39 | Also there is a directory `/experiments` which contains a small example framework. 40 | 41 | 42 | # Using Calfbox as standalone instrument 43 | 44 | Using Calfbox as standalone instrument requires a .cfg config file. 45 | 46 | This part of the program is currently unmaintained and untested. Please do not use it. 47 | 48 | # License 49 | 50 | This code is free software: you can redistribute it and/or modify 51 | it under the terms of the GNU General Public License as published by 52 | the Free Software Foundation, either version 3 of the License, or 53 | (at your option) any later version. 54 | 55 | This program is distributed in the hope that it will be useful, 56 | but WITHOUT ANY WARRANTY; without even the implied warranty of 57 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 58 | GNU General Public License for more details. 59 | 60 | You should have received a copy of the GNU General Public License 61 | along with this program. If not, see . 62 | 63 | For the full license see the file COPYING 64 | -------------------------------------------------------------------------------- /experiments/interactive.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env -S python3 -i 2 | # -*- coding: utf-8 -*- 3 | """ 4 | This is a minimal calfbox python example. It is meant as a starting 5 | point to find bugs and test performance. 6 | 7 | Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) 8 | 9 | This code is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License 20 | along with this program. If not, see . 21 | """ 22 | 23 | import atexit 24 | from pprint import pprint 25 | from calfbox import cbox 26 | 27 | 28 | cbox.init_engine("") 29 | cbox.Config.set("io", "outputs", 8) 30 | NAME = "Cbox Interactive" 31 | cbox.Config.set("io", "client_name", NAME) 32 | 33 | cbox.start_audio() 34 | scene = cbox.Document.get_engine().new_scene() 35 | scene.clear() 36 | 37 | trackName = "trackOne" 38 | cboxMidiOutUuid = cbox.JackIO.create_midi_output(trackName) 39 | calfboxTrack = cbox.Document.get_song().add_track() 40 | 41 | 42 | pblob = bytes() 43 | pblob += cbox.Pattern.serialize_event(0, 0x90, 60, 100) # note on 44 | pblob += cbox.Pattern.serialize_event(383, 0x80, 60, 100) # note off 45 | pattern = cbox.Document.get_song().pattern_from_blob(pblob, 384) 46 | calfboxTrack.add_clip(0, 0, 384, pattern) #pos, offset, length(and not end-position, but is the same for the complete track), pattern 47 | cbox.Document.get_song().set_loop(384, 384) #set playback length for the entire score. Why is the first value not zero? That would create an actual loop from the start to end. We want the song to play only once. The cbox way of doing that is to set the loop range to zero at the end of the track. Zero length is stop. 48 | cbox.Document.get_song().update_playback() 49 | 50 | print() 51 | 52 | def exit_handler(): 53 | #Restore initial state and stop the engine 54 | cbox.Transport.stop() 55 | cbox.stop_audio() 56 | cbox.shutdown_engine() 57 | atexit.register(exit_handler) 58 | -------------------------------------------------------------------------------- /layer.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_LAYER_H 20 | #define CBOX_LAYER_H 21 | 22 | #include "dom.h" 23 | #include "midi.h" 24 | #include 25 | #include 26 | 27 | struct cbox_module; 28 | struct cbox_rt; 29 | 30 | CBOX_EXTERN_CLASS(cbox_layer) 31 | 32 | struct cbox_layer 33 | { 34 | CBOX_OBJECT_HEADER() 35 | struct cbox_scene *scene; 36 | struct cbox_instrument *instrument; 37 | struct cbox_command_target cmd_target; 38 | gboolean enabled; 39 | int8_t in_channel; // -1 for Omni 40 | int8_t out_channel; // -1 for Omni 41 | uint8_t low_note; 42 | uint8_t high_note; 43 | int8_t transpose; 44 | int8_t fixed_note; 45 | gboolean disable_aftertouch; 46 | gboolean invert_sustain; 47 | gboolean consume; 48 | gboolean ignore_scene_transpose; 49 | gboolean ignore_program_changes; 50 | gboolean external_output_set; 51 | struct cbox_uuid external_output; 52 | struct cbox_midi_buffer output_buffer; 53 | struct cbox_midi_merger *external_merger; 54 | }; 55 | 56 | extern struct cbox_layer *cbox_layer_new(struct cbox_scene *scene); 57 | extern struct cbox_layer *cbox_layer_new_with_instrument(struct cbox_scene *scene, const char *instrument_name, GError **error); 58 | extern struct cbox_layer *cbox_layer_new_from_config(struct cbox_scene *scene, const char *instrument_name, GError **error); 59 | extern gboolean cbox_layer_load(struct cbox_layer *layer, const char *name, GError **error); 60 | extern void cbox_layer_set_instrument(struct cbox_layer *layer, struct cbox_instrument *instrument); 61 | extern void cbox_layer_destroy(struct cbox_layer *layer); 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /jack_audio_routing.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from calfbox import cbox 5 | import time 6 | 7 | def cmd_dumper(cmd, fb, args): 8 | print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) 9 | 10 | cbox.init_engine("") #empty string so cbox doesn't look for the .cboxrc file 11 | cbox.Config.set("io", "use_usb", 0) 12 | cbox.start_audio(cmd_dumper) 13 | 14 | global Document 15 | Document = cbox.Document 16 | 17 | status = cbox.JackIO.status() 18 | client_name = status.client_name 19 | print ("Client name: %s" % client_name) 20 | print ("Audio inputs: %d, outputs: %d" % (status.audio_inputs, status.audio_outputs)) 21 | print ("Sample rate: %d frames/sec" % (status.sample_rate)) 22 | print ("JACK period: %d frames" % (status.buffer_size)) 23 | 24 | inputs = cbox.JackIO.get_ports(".*", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SOURCE | cbox.JackIO.PORT_IS_PHYSICAL) 25 | outputs = cbox.JackIO.get_ports(".*", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SINK | cbox.JackIO.PORT_IS_PHYSICAL) 26 | 27 | scene = Document.get_scene() 28 | scene.clear() 29 | instrument = scene.add_new_instrument_layer("test_sampler", "sampler").get_instrument() 30 | pgm_no = instrument.engine.get_unused_program() 31 | pgm = instrument.engine.load_patch_from_file(pgm_no, 'synthbass.sfz', 'SynthBass') 32 | instrument.engine.set_patch(1, pgm_no) 33 | instrument.engine.set_patch(10, pgm_no) 34 | 35 | print ("Connecting") 36 | 37 | uuid = cbox.JackIO.create_audio_output('noises') 38 | router = cbox.JackIO.create_audio_output_router(uuid, uuid) 39 | assert type(router) is cbox.DocRecorder 40 | router2 = cbox.JackIO.create_audio_output_router(uuid, uuid) 41 | assert type(router2) is cbox.DocRecorder 42 | instrument.get_output_slot(0).rec_wet.attach(router) 43 | instrument.get_output_slot(0).rec_wet.attach(router2) 44 | 45 | exc = None 46 | try: 47 | instrument.get_output_slot(0).rec_wet.attach(router2) 48 | except Exception as e: 49 | exc = e 50 | assert "Router already attached" in str(exc) 51 | 52 | instrument.get_output_slot(0).rec_wet.detach(router2) 53 | 54 | try: 55 | instrument.get_output_slot(0).rec_wet.detach(router2) 56 | except Exception as e: 57 | exc = e 58 | assert "Recorder is not attached" in str(exc) 59 | 60 | router.delete() 61 | 62 | print("Ready!") 63 | 64 | while True: 65 | events = cbox.get_new_events() 66 | if events: 67 | print (events) 68 | time.sleep(0.05) 69 | -------------------------------------------------------------------------------- /errors.c: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include "errors.h" 20 | 21 | GQuark cbox_module_error_quark() 22 | { 23 | return g_quark_from_string("cbox-module-error-quark"); 24 | } 25 | 26 | void cbox_force_error(GError **error) 27 | { 28 | if (error && !*error) 29 | g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "unknown error"); 30 | } 31 | 32 | void cbox_print_error(GError *error) 33 | { 34 | if (!error) 35 | { 36 | g_warning("Unspecified error"); 37 | return; 38 | } 39 | g_warning("%s", error->message); 40 | g_error_free(error); 41 | } 42 | 43 | void cbox_print_error_if(GError *error) 44 | { 45 | if (!error) 46 | return; 47 | g_warning("%s", error->message); 48 | g_error_free(error); 49 | } 50 | 51 | gboolean cbox_set_command_error(GError **error, const struct cbox_osc_command *cmd) 52 | { 53 | if (error && !*error) 54 | g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_OUT_OF_RANGE, "Invalid command '%s' with args '%s'", cmd->command, cmd->arg_types); 55 | return FALSE; 56 | } 57 | 58 | gboolean cbox_set_command_error_with_msg(GError **error, const struct cbox_osc_command *cmd, const char *extra_msg) 59 | { 60 | if (error && !*error) 61 | g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_OUT_OF_RANGE, "Invalid command '%s' with args '%s': %s", cmd->command, cmd->arg_types, extra_msg); 62 | return FALSE; 63 | } 64 | 65 | gboolean cbox_set_range_error(GError **error, const char *param, double minv, double maxv) 66 | { 67 | if (error && !*error) 68 | g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_OUT_OF_RANGE, "Parameter %s not within a valid range of [%f, %f]", param, minv, maxv); 69 | return FALSE; 70 | } 71 | -------------------------------------------------------------------------------- /sampler_impl.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_SAMPLER_IMPL_H 20 | #define CBOX_SAMPLER_IMPL_H 21 | 22 | extern void sampler_gen_reset(struct sampler_gen *v); 23 | extern uint32_t sampler_gen_sample_playback(struct sampler_gen *v, float *leftright, uint32_t limit); 24 | extern void sampler_program_change_byidx(struct sampler_module *m, struct sampler_channel *c, int program_idx); 25 | extern void sampler_program_change(struct sampler_module *m, struct sampler_channel *c, int program); 26 | 27 | static inline int sfz_note_from_string(const char *note) 28 | { 29 | static const int semis[] = {9, 11, 0, 2, 4, 5, 7}; 30 | int pos; 31 | int nn = tolower(note[0]); 32 | int nv; 33 | if ((nn >= '0' && nn <= '9') || nn == '-') 34 | { 35 | int nv = atoi(note); 36 | if (nv >= -1 && nv <= 127) 37 | return nv; 38 | return -2; 39 | } 40 | if (nn < 'a' || nn > 'g') 41 | return -2; 42 | nv = semis[nn - 'a']; 43 | 44 | for (pos = 1; tolower(note[pos]) == 'b' || note[pos] == '#'; pos++) 45 | nv += (note[pos] != '#') ? -1 : +1; 46 | 47 | if ((note[pos] == '-' && note[pos + 1] == '1' && note[pos + 2] == '\0') || (note[pos] >= '0' && note[pos] <= '9' && note[pos + 1] == '\0')) 48 | { 49 | return nv + 12 * (1 + atoi(note + pos)); 50 | } 51 | 52 | return -2; 53 | } 54 | 55 | static inline gboolean atof_C_verify(const char *key, const char *value, double *result, GError **error) 56 | { 57 | char *endptr = NULL; 58 | double res = g_ascii_strtod(value, &endptr); 59 | if (endptr && !*endptr && endptr != value) 60 | { 61 | *result = res; 62 | return TRUE; 63 | } 64 | g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "'%s' is not a correct numeric value for %s", value, key); 65 | return FALSE; 66 | } 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /song_api_example.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import sys 6 | import struct 7 | import time 8 | import unittest 9 | 10 | sys.path = ["./py"] + sys.path 11 | 12 | import cbox 13 | 14 | def cmd_dumper(cmd, fb, args): 15 | print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) 16 | cbox.init_engine("") #empty string so cbox doesn't look for the .cboxrc file 17 | cbox.start_audio(cmd_dumper) 18 | 19 | global Document 20 | global Transport 21 | Document = cbox.Document 22 | Transport = cbox.Transport 23 | 24 | # Make sure playback doesn't start prematurely 25 | Transport.stop() 26 | 27 | song = Document.get_song() 28 | 29 | # Delete all the tracks and patterns 30 | song.clear() 31 | 32 | # Add the first track 33 | trk = song.add_track() 34 | trk.clear_clips() 35 | 36 | # Create a binary blob that contains the MIDI events 37 | pblob = bytes() 38 | for noteindex in range(20): 39 | # note on 40 | pblob += cbox.Pattern.serialize_event(noteindex * 24, 0x90, 36+noteindex*3, 127) 41 | # note off 42 | pblob += cbox.Pattern.serialize_event(noteindex * 24 + 23, 0x90, 36+noteindex*3, 0) 43 | 44 | # This will be the length of the pattern (in pulses). It should be large enough 45 | # to fit all the events 46 | pattern_len = 10 * 24 * 2 47 | 48 | # Create a new pattern object using events from the blob 49 | pattern = song.pattern_from_blob(pblob, pattern_len) 50 | 51 | # Add an instance (clip) of the pattern to the track at position 0 52 | # The clip will contain the whole pattern (it is also possible to insert 53 | # a single slice of the pattern) 54 | clip = trk.add_clip(0, 0, pattern_len, pattern) 55 | 56 | # Stop the song at the end 57 | #song.set_loop(pattern_len, pattern_len) 58 | song.set_loop(pattern_len, pattern_len) 59 | 60 | # Set tempo - the argument must be a float 61 | Transport.set_tempo(160.0) 62 | 63 | # Send the updated song data to the realtime thread 64 | song.update_playback() 65 | 66 | # Flush 67 | Transport.stop() 68 | 69 | print ("Song length (seconds) is %f" % (cbox.Transport.ppqn_to_samples(pattern_len) * 1.0 / Transport.status().sample_rate)) 70 | 71 | # The /master object API doesn't have any nice Python wrapper yet, so accessing 72 | # it is a bit ugly, still - it works 73 | 74 | # Start playback 75 | Transport.play() 76 | print ("Playing") 77 | 78 | while True: 79 | # Get transport information - current position (samples and pulses), current tempo etc. 80 | master = Transport.status() 81 | print (master.pos_ppqn) 82 | # Query JACK ports, new USB devices etc. 83 | cbox.call_on_idle() 84 | time.sleep(0.1) 85 | -------------------------------------------------------------------------------- /recsrc.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_RECSRC_H 20 | #define CBOX_RECSRC_H 21 | 22 | #include "cmd.h" 23 | #include "dom.h" 24 | 25 | struct cbox_recording_source; 26 | struct cbox_rt; 27 | struct cbox_engine; 28 | 29 | CBOX_EXTERN_CLASS(cbox_recorder) 30 | 31 | struct cbox_recorder 32 | { 33 | CBOX_OBJECT_HEADER() 34 | void *user_data; 35 | struct cbox_command_target cmd_target; 36 | 37 | gboolean (*attach)(struct cbox_recorder *handler, struct cbox_recording_source *src, GError **error); 38 | void (*record_block)(struct cbox_recorder *handler, const float **buffers, uint32_t offset, uint32_t numsamples); 39 | gboolean (*detach)(struct cbox_recorder *handler, GError **error); 40 | void (*destroy)(struct cbox_recorder *handler); 41 | }; 42 | 43 | struct cbox_recording_source 44 | { 45 | struct cbox_command_target cmd_target; 46 | struct cbox_scene *scene; 47 | 48 | struct cbox_recorder **handlers; 49 | uint32_t handler_count; 50 | uint32_t max_numsamples; 51 | int channels; 52 | }; 53 | 54 | #define IS_RECORDING_SOURCE_CONNECTED(src) ((src).handler_count != 0) 55 | 56 | extern void cbox_recording_source_init(struct cbox_recording_source *src, struct cbox_scene *scene, uint32_t max_numsamples, int channels); 57 | extern gboolean cbox_recording_source_attach(struct cbox_recording_source *src, struct cbox_recorder *rec, GError **error); 58 | extern int cbox_recording_source_detach(struct cbox_recording_source *src, struct cbox_recorder *rec, GError **error); 59 | extern void cbox_recording_source_push(struct cbox_recording_source *src, const float **buffers, uint32_t offset, uint32_t numsamples); 60 | extern void cbox_recording_source_uninit(struct cbox_recording_source *src); 61 | 62 | extern struct cbox_recorder *cbox_recorder_new_stream(struct cbox_engine *engine, struct cbox_rt *rt, const char *filename); 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /tarfile.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2013 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_TARFILE_H 20 | #define CBOX_TARFILE_H 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | struct cbox_taritem 27 | { 28 | gchar *filename; 29 | gchar *filename_nc; 30 | uint64_t offset; 31 | uint64_t size; 32 | int refs; 33 | }; 34 | 35 | struct cbox_tarfile 36 | { 37 | int fd; 38 | int refs; 39 | GHashTable *items_byname; 40 | GHashTable *items_byname_nc; 41 | char *file_pathname; //full path to the .tar file with filename.ext 42 | }; 43 | 44 | struct cbox_tarpool 45 | { 46 | GHashTable *files; 47 | }; 48 | 49 | struct cbox_tarfile_sndstream 50 | { 51 | struct cbox_tarfile *file; 52 | struct cbox_taritem *item; 53 | uint64_t filepos; 54 | }; 55 | 56 | extern struct SF_VIRTUAL_IO cbox_taritem_virtual_io; 57 | 58 | extern struct cbox_tarfile *cbox_tarfile_open(const char *pathname, GError **error); 59 | 60 | extern struct cbox_taritem *cbox_tarfile_get_item_by_name(struct cbox_tarfile *tarfile, const char *item_filename, gboolean ignore_case); 61 | extern int cbox_tarfile_openitem(struct cbox_tarfile *tarfile, struct cbox_taritem *item); 62 | extern void cbox_tarfile_closeitem(struct cbox_tarfile *tarfile, struct cbox_taritem *item, int fd); 63 | 64 | extern SNDFILE *cbox_tarfile_opensndfile(struct cbox_tarfile *tarfile, struct cbox_taritem *item, struct cbox_tarfile_sndstream *stream, SF_INFO *sfinfo); 65 | // No need to close - it reuses the cbox_tarfile file descriptor 66 | 67 | extern void cbox_tarfile_destroy(struct cbox_tarfile *tf); 68 | 69 | extern struct cbox_tarpool *cbox_tarpool_new(void); 70 | extern struct cbox_tarfile *cbox_tarpool_get_tarfile(struct cbox_tarpool *pool, const char *name, GError **error); 71 | extern void cbox_tarpool_release_tarfile(struct cbox_tarpool *pool, struct cbox_tarfile *file); 72 | extern void cbox_tarpool_destroy(struct cbox_tarpool *pool); 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /py/nullbox.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import importlib 5 | 6 | class NullCalfbox(str): #iterable 7 | """A drop-in replacement for calfboxs python module. 8 | Use this for testing and development. 9 | 10 | At the start of your program, first file, insert: 11 | import prefix.calfbox.nullbox 12 | or 13 | from SOMETHING import nullbox 14 | 15 | All further 16 | from prefix.calfbox import cbox 17 | will use the null module. 18 | 19 | Even additional 20 | import prefix.calfbox 21 | will use the nullbox module. 22 | """ 23 | 24 | def __init__(self, *args, **kwargs): 25 | self.client_name = "" 26 | self.pos_ppqn = 0 27 | self.ignore_program_changes = False 28 | self.patch = {i:(0, "nullbox") for i in range(1,17)} #catches status().patch 29 | self.frame_rate = 48000 30 | self.frame = 0 31 | 32 | def __getattr__(self, *args, **kwargs): 33 | return __class__() 34 | 35 | def __call__(self, *args, **kwargs): 36 | return __class__() 37 | 38 | def __getitem__(self, key): 39 | return __class__() 40 | 41 | def serialize_event(self, *args, **kwargs): 42 | return b'' 43 | 44 | def get_patches(self, *args): 45 | """sf2 compatibility""" 46 | return { 47 | 0 : "nullbox", 48 | } 49 | 50 | def set_ignore_program_changes(self, state): 51 | self.ignore_program_changes = state 52 | 53 | 54 | #Operators 55 | 56 | def __and__(self, *args): 57 | return 1 58 | 59 | __add__ = __sub__ = __mul__ = __floordiv__ = __div__ = __truediv__ = __mod__ = __divmod__ = __pow__ = __lshift__ = __rshift__ = __or__ = __xor__ = __ror__ = __ior__ = __rand__ = __iand__ = __rxor__ = __ixor__ = __invert__ = __and__ 60 | 61 | 62 | import sys 63 | 64 | try: 65 | import nullbox 66 | except ModuleNotFoundError: 67 | from . import nullbox 68 | 69 | for key, value in sys.modules.items(): 70 | if "nullbox" in key: 71 | r = key 72 | break 73 | else: 74 | raise ValueError("Nullbox Module not found") 75 | 76 | #r is the actual name of the calfbox parent modul. We cannot assume it to be "calfbox". 77 | calfboxModuleName = r[:-len(".nullbox")] #remove suffix 78 | sys.modules[calfboxModuleName] = sys.modules[r] #e.g. sys.modules["calfbox"] is now nullbox 79 | 80 | #Hack 'from prefix.calfbox import cbox' 81 | importlib.import_module(calfboxModuleName) #Imported once here, all modules will import this variant later. 82 | #import calfbox 83 | cbox = NullCalfbox("fake cbox null client") 84 | 85 | #Hack direct call 'import cbox' 86 | sys.modules["cbox"] = cbox 87 | import cbox #Imported once here, all modules will import this variant later. 88 | -------------------------------------------------------------------------------- /config-api.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_CONFIG_API_H 20 | #define CBOX_CONFIG_API_H 21 | 22 | #include 23 | 24 | struct cbox_sectref; 25 | 26 | extern void cbox_config_init(const char *override_file); 27 | extern int cbox_config_has_section(const char *section); 28 | extern char *cbox_config_get_string(const char *section, const char *key); 29 | extern char *cbox_config_get_string_with_default(const char *section, const char *key, char *def_value); 30 | extern int cbox_config_get_int(const char *section, const char *key, int def_value); 31 | extern float cbox_config_get_float(const char *section, const char *key, float def_value); 32 | extern float cbox_config_get_gain(const char *section, const char *key, float def_value); 33 | extern float cbox_config_get_gain_db(const char *section, const char *key, float def_value); 34 | extern void cbox_config_foreach_section(void (*process)(void *user_data, const char *section), void *user_data); 35 | extern void cbox_config_foreach_key(void (*process)(void *user_data, const char *key), const char *section, void *user_data); 36 | extern char *cbox_config_permify(const char *temporary); 37 | 38 | extern void cbox_config_set_string(const char *section, const char *key, const char *value); 39 | extern void cbox_config_set_int(const char *section, const char *key, int value); 40 | extern void cbox_config_set_float(const char *section, const char *key, double value); 41 | extern int cbox_config_remove_section(const char *section); 42 | extern int cbox_config_remove_key(const char *section, const char *key); 43 | 44 | extern gboolean cbox_config_save(const char *filename, GError **error); 45 | 46 | extern struct cbox_sectref *cbox_config_sectref(struct cbox_sectref *def_sect, const char *prefix, const char *refname); 47 | extern struct cbox_sectref *cbox_config_get_sectref(struct cbox_sectref *sect, const char *prefix, const char *key); 48 | extern struct cbox_sectref *cbox_config_get_sectref_n(struct cbox_sectref *sect, const char *prefix, const char *key, int index); 49 | extern struct cbox_sectref *cbox_config_get_sectref_suffix(struct cbox_sectref *sect, const char *prefix, const char *key, const char *suffix); 50 | 51 | extern void cbox_config_close(void); 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /sampler_prevoice.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include "config-api.h" 3 | #include "dspmath.h" 4 | #include "errors.h" 5 | #include "midi.h" 6 | #include "module.h" 7 | #include "rt.h" 8 | #include "sampler.h" 9 | #include "sampler_impl.h" 10 | 11 | void sampler_prevoice_start(struct sampler_prevoice *pv, struct sampler_channel *channel, struct sampler_layer_data *l, int note, int vel) 12 | { 13 | pv->channel = channel; 14 | pv->layer_data = l; 15 | pv->note = note; 16 | pv->vel = vel; 17 | pv->age = 0; 18 | pv->delay_computed = 0.f; 19 | pv->sync_beats = -1; 20 | pv->sync_initial_time = -1; 21 | pv->sync_trigger_time = -1; 22 | 23 | for(struct sampler_noteinitfunc *nif = pv->layer_data->prevoice_nifs; nif; nif = nif->next) 24 | nif->key.notefunc_prevoice(nif, pv); 25 | sampler_prevoice_unlink(&channel->module->prevoices_free, pv); 26 | sampler_prevoice_link(&channel->module->prevoices_running, pv); 27 | } 28 | 29 | void sampler_prevoice_link(struct sampler_prevoice **pv, struct sampler_prevoice *v) 30 | { 31 | v->prev = NULL; 32 | v->next = *pv; 33 | if (*pv) 34 | (*pv)->prev = v; 35 | *pv = v; 36 | } 37 | 38 | void sampler_prevoice_unlink(struct sampler_prevoice **pv, struct sampler_prevoice *v) 39 | { 40 | if (*pv == v) 41 | *pv = v->next; 42 | if (v->prev) 43 | v->prev->next = v->next; 44 | if (v->next) 45 | v->next->prev = v->prev; 46 | v->prev = NULL; 47 | v->next = NULL; 48 | } 49 | 50 | int sampler_prevoice_process(struct sampler_prevoice *pv, struct sampler_module *m) 51 | { 52 | struct sampler_layer_data *layer_data = pv->layer_data; 53 | if (pv->sync_beats != -1) 54 | { 55 | double cur_beat = sampler_get_current_beat(m); 56 | 57 | if (cur_beat < pv->sync_initial_time - 0.001 || cur_beat >= pv->sync_trigger_time + 1) 58 | { 59 | gboolean backward_jump = cur_beat < pv->sync_initial_time; 60 | // printf("Recalc: time %f, initial %f, delta %f, trigger %f\n", cur_beat, pv->sync_initial_time, cur_beat - pv->sync_initial_time, pv->sync_trigger_time); 61 | // Recalculate after seek/looping etc 62 | pv->sync_initial_time = cur_beat; 63 | double cur_rel_beat = fmod(cur_beat, pv->sync_beats); 64 | double bar_start = cur_beat - cur_rel_beat; 65 | if (pv->layer_data->sync_offset <= cur_rel_beat && !backward_jump) // trigger in next bar 66 | pv->sync_trigger_time = bar_start + pv->sync_beats + pv->layer_data->sync_offset; 67 | else // trigger in the same bar 68 | pv->sync_trigger_time = bar_start + pv->layer_data->sync_offset; 69 | } 70 | if (cur_beat < pv->sync_trigger_time) 71 | return 0; 72 | // Let the other logic (note delay etc.) take over 73 | pv->sync_beats = -1; 74 | } 75 | pv->age += CBOX_BLOCK_SIZE; 76 | if (pv->age >= (layer_data->delay + pv->delay_computed) * m->module.srate) 77 | return 1; 78 | 79 | return 0; 80 | } 81 | 82 | -------------------------------------------------------------------------------- /song_api_example2.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import sys 6 | import struct 7 | import time 8 | import unittest 9 | 10 | sys.path = ["./py"] + sys.path 11 | 12 | import cbox 13 | 14 | def cmd_dumper(cmd, fb, args): 15 | print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) 16 | cbox.init_engine("") #empty string so cbox doesn't look for the .cboxrc file 17 | cbox.start_audio(cmd_dumper) 18 | 19 | global Document 20 | global Transport 21 | Document = cbox.Document 22 | Transport = cbox.Transport 23 | 24 | # Make sure playback doesn't start prematurely 25 | Transport.stop() 26 | 27 | song = Document.get_song() 28 | 29 | # Delete all the tracks and patterns 30 | song.clear() 31 | 32 | # Add the first track 33 | trk = song.add_track() 34 | 35 | # Create a binary blob that contains zero MIDI events 36 | emptyblob = bytes() 37 | 38 | # Create a new empty pattern 39 | empty_pattern = song.pattern_from_blob(emptyblob, 16) 40 | 41 | # This will be the length of the pattern (in pulses). It should be large enough 42 | # to fit all the events 43 | pattern_len = 10 * 24 * 2 44 | 45 | # Add an instance (clip) of the empty pattern to the track 46 | clip1 = trk.add_clip(pattern_len, 0, 16, empty_pattern) 47 | 48 | # Add another instance after it 49 | clip2 = trk.add_clip(2 * pattern_len, 0, 16, empty_pattern) 50 | 51 | # Create a binary blob that contains the MIDI events 52 | pblob = bytes() 53 | for noteindex in range(20): 54 | # note on 55 | pblob += cbox.Pattern.serialize_event(noteindex * 24, 0x90, 36+noteindex*3, 127) 56 | # note off 57 | pblob += cbox.Pattern.serialize_event(noteindex * 24 + 23, 0x90, 36+noteindex*3, 0) 58 | 59 | # Create a new pattern object using events from the blob 60 | pattern = song.pattern_from_blob(pblob, pattern_len) 61 | 62 | # Update all attributes of the second clip, rearranging the order 63 | clip2.set_pattern(pattern) 64 | clip2.set_pos(0) 65 | clip2.set_offset(0) 66 | clip2.set_length(pattern_len) 67 | 68 | # Verify that the clips have been reordered 69 | clips = [o.clip for o in trk.status().clips] 70 | assert clips == [clip2, clip1] 71 | 72 | # Stop the song at the end 73 | song.set_loop(pattern_len, pattern_len) 74 | 75 | # Set tempo - the argument must be a float 76 | Transport.set_tempo(160.0) 77 | 78 | # Send the updated song data to the realtime thread 79 | song.update_playback() 80 | 81 | # Flush 82 | Transport.stop() 83 | 84 | print ("Song length (seconds) is %f" % (cbox.Transport.ppqn_to_samples(pattern_len) * 1.0 / Transport.status().sample_rate)) 85 | 86 | # The /master object API doesn't have any nice Python wrapper yet, so accessing 87 | # it is a bit ugly, still - it works 88 | 89 | # Start playback 90 | Transport.play() 91 | print ("Playing") 92 | 93 | while True: 94 | # Get transport information - current position (samples and pulses), current tempo etc. 95 | master = Transport.status() 96 | print (master.pos_ppqn) 97 | # Query JACK ports, new USB devices etc. 98 | cbox.call_on_idle() 99 | time.sleep(0.1) 100 | -------------------------------------------------------------------------------- /cmd.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_CMD_H 20 | #define CBOX_CMD_H 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #define CBOX_ARG_I(cmd, idx) (*(int *)(cmd)->arg_values[(idx)]) 27 | #define CBOX_ARG_S(cmd, idx) ((const char *)(cmd)->arg_values[(idx)]) 28 | #define CBOX_ARG_B(cmd, idx) ((const struct cbox_blob *)(cmd)->arg_values[(idx)]) 29 | #define CBOX_ARG_F(cmd, idx) (*(double *)(cmd)->arg_values[(idx)]) 30 | #define CBOX_ARG_O(cmd, idx, src, class, error) cbox_document_get_object_by_text_uuid(CBOX_GET_DOCUMENT(src), (const char *)(cmd)->arg_values[(idx)], &CBOX_CLASS(class), (error)) 31 | #define CBOX_ARG_S_ISNULL(cmd, idx) (0 == (const char *)(cmd)->arg_values[(idx)]) 32 | 33 | struct cbox_command_target; 34 | 35 | struct cbox_osc_command 36 | { 37 | const char *command; 38 | const char *arg_types; 39 | void **arg_values; 40 | }; 41 | 42 | typedef gboolean (*cbox_process_cmd)(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error); 43 | 44 | struct cbox_command_target 45 | { 46 | void *user_data; 47 | cbox_process_cmd process_cmd; 48 | }; 49 | 50 | void cbox_command_target_init(struct cbox_command_target *ct, cbox_process_cmd cmd, void *user_data); 51 | 52 | extern gboolean cbox_check_fb_channel(struct cbox_command_target *fb, const char *command, GError **error); 53 | 54 | extern gboolean cbox_execute_sub(struct cbox_command_target *ct, struct cbox_command_target *fb, const struct cbox_osc_command *cmd, const char *new_command, GError **error); 55 | extern gboolean cbox_execute_on(struct cbox_command_target *ct, struct cbox_command_target *fb, const char *cmd, const char *args, GError **error, ...); 56 | extern gboolean cbox_execute_on_v(struct cbox_command_target *ct, struct cbox_command_target *fb, const char *cmd, const char *args, va_list va, GError **error); 57 | 58 | extern gboolean cbox_osc_command_dump(const struct cbox_osc_command *cmd); 59 | 60 | // Note: this sets *subcommand to NULL on parse error; requires "/path/" as path 61 | extern gboolean cbox_parse_path_part_int(const struct cbox_osc_command *cmd, const char *path, const char **subcommand, int *index, int min_index, int max_index, GError **error); 62 | extern gboolean cbox_parse_path_part_str(const struct cbox_osc_command *cmd, const char *path, const char **subcommand, char **path_element, GError **error); 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /mididest.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2013 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_MIDIDEST_H 20 | #define CBOX_MIDIDEST_H 21 | 22 | #include "midi.h" 23 | #include 24 | 25 | struct cbox_command_target; 26 | struct cbox_rt; 27 | 28 | struct cbox_midi_source 29 | { 30 | struct cbox_midi_source *next; 31 | struct cbox_midi_buffer *data; 32 | uint32_t bpos; 33 | gboolean streaming; 34 | struct cbox_midi_merger **merger_ptr; 35 | }; 36 | 37 | struct cbox_midi_merger 38 | { 39 | struct cbox_midi_source *inputs; 40 | struct cbox_midi_buffer *output; 41 | }; 42 | 43 | void cbox_midi_merger_init(struct cbox_midi_merger *dest, struct cbox_midi_buffer *output); 44 | void cbox_midi_merger_render_to(struct cbox_midi_merger *dest, struct cbox_midi_buffer *output); 45 | static inline void cbox_midi_merger_render(struct cbox_midi_merger *dest) 46 | { 47 | if (dest->output) 48 | cbox_midi_merger_render_to(dest, dest->output); 49 | } 50 | struct cbox_midi_source **cbox_midi_merger_find_source(struct cbox_midi_merger *dest, struct cbox_midi_buffer *buffer); 51 | void cbox_midi_merger_connect(struct cbox_midi_merger *dest, struct cbox_midi_buffer *buffer, struct cbox_rt *rt, struct cbox_midi_merger **dest_ptr); 52 | void cbox_midi_merger_disconnect(struct cbox_midi_merger *dest, struct cbox_midi_buffer *buffer, struct cbox_rt *rt); 53 | void cbox_midi_merger_push(struct cbox_midi_merger *dest, struct cbox_midi_buffer *buffer, struct cbox_rt *rt); 54 | void cbox_midi_merger_close(struct cbox_midi_merger *dest, struct cbox_rt *rt); 55 | 56 | struct cbox_time_mapper 57 | { 58 | uint32_t (*map_time)(struct cbox_time_mapper *, uint32_t free_running_counter); 59 | }; 60 | 61 | #define GET_RT_FROM_cbox_midi_appsink(appsink) ((appsink)->rt) 62 | 63 | struct cbox_midi_appsink 64 | { 65 | struct cbox_rt *rt; 66 | struct cbox_time_mapper *tmap; 67 | struct cbox_midi_buffer midibufs[2]; 68 | int current_buffer; 69 | }; 70 | 71 | extern void cbox_midi_appsink_init(struct cbox_midi_appsink *appsink, struct cbox_rt *rt, struct cbox_time_mapper *tmap); 72 | extern void cbox_midi_appsink_supply(struct cbox_midi_appsink *appsink, struct cbox_midi_buffer *buffer, uint32_t time_offset); 73 | extern const struct cbox_midi_buffer *cbox_midi_appsink_get_input_midi_data(struct cbox_midi_appsink *appsink); 74 | extern gboolean cbox_midi_appsink_send_to(struct cbox_midi_appsink *appsink, struct cbox_command_target *fb, GError **error); 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /wavebank.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_WAVEBANK_H 20 | #define CBOX_WAVEBANK_H 21 | 22 | #include 23 | #include 24 | #include 25 | #include "tarfile.h" 26 | 27 | #define MAX_INTERPOLATION_ORDER 3 28 | 29 | #define CBOX_WAVEFORM_ERROR cbox_waveform_error_quark() 30 | 31 | enum CboxWaveformError 32 | { 33 | CBOX_WAVEFORM_ERROR_FAILED, 34 | }; 35 | 36 | struct cbox_waveform_level 37 | { 38 | int16_t *data; 39 | uint64_t max_rate; 40 | }; 41 | 42 | struct cbox_waveform_generate_data 43 | { 44 | float (*getfunc)(struct cbox_waveform_generate_data *generate, float v); 45 | void *user_data; 46 | int levels; 47 | }; 48 | 49 | struct cbox_waveform 50 | { 51 | int16_t *data; 52 | SF_INFO info; 53 | int id; 54 | int refcount; 55 | size_t bytes; 56 | size_t preloaded_frames; 57 | gchar *canonical_name; 58 | gchar *display_name; 59 | gboolean has_loop; 60 | uint32_t loop_start, loop_end; 61 | struct cbox_tarfile *tarfile; 62 | struct cbox_taritem *taritem; 63 | struct cbox_tarfile_sndstream sndstream; 64 | 65 | struct cbox_waveform_level *levels; 66 | int level_count; 67 | gboolean is_static; 68 | struct cbox_waveform_generate_data *generate; 69 | }; 70 | 71 | extern struct cbox_command_target cbox_waves_cmd_target; 72 | 73 | extern void cbox_wavebank_init(void); 74 | extern struct cbox_waveform *cbox_wavebank_get_waveform(const char *context_name, struct cbox_tarfile *tf, const char *sample_dir, const char *filename, GError **error); 75 | extern struct cbox_waveform *cbox_wavebank_peek_waveform_by_id(int id); 76 | extern void cbox_wavebank_foreach(void (*cb)(void *user_data, struct cbox_waveform *waveform), void *user_data); 77 | extern void cbox_wavebank_add_std_waveform(const char *name, float (*getfunc)(struct cbox_waveform_generate_data *generate, float v), void *user_data, int levels); 78 | extern struct cbox_waveform *cbox_wavebank_add_mem_waveform(const char *name, void *data, uint32_t frames, int sample_rate, int channels, bool looped, uint32_t loop_start, uint32_t loop_end); 79 | extern int cbox_wavebank_get_count(void); 80 | extern int64_t cbox_wavebank_get_bytes(void); 81 | extern int64_t cbox_wavebank_get_maxbytes(void); 82 | extern void cbox_wavebank_close(void); 83 | 84 | extern void cbox_waveform_ref(struct cbox_waveform *waveform); 85 | extern void cbox_waveform_unref(struct cbox_waveform *waveform); 86 | 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /prefetch_pipe.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2013 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_PREFETCH_PIPE_H 20 | #define CBOX_PREFETCH_PIPE_H 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "tarfile.h" 28 | 29 | #define PIPE_MIN_PREFETCH_SIZE_FRAMES 2048 30 | 31 | struct cbox_waveform; 32 | 33 | enum cbox_prefetch_pipe_state 34 | { 35 | pps_free, 36 | pps_opening, 37 | pps_active, 38 | pps_finished, 39 | pps_error, 40 | pps_closing, 41 | pps_closed, 42 | }; 43 | 44 | struct cbox_prefetch_pipe 45 | { 46 | union { 47 | volatile enum cbox_prefetch_pipe_state state; 48 | uint64_t atomic1; 49 | }; 50 | struct cbox_waveform *waveform; 51 | struct cbox_tarfile_sndstream sndstream; 52 | int16_t *data; 53 | uint32_t buffer_size; 54 | uint32_t min_buffer_frames; 55 | SF_INFO info; 56 | SNDFILE *sndfile; 57 | uint32_t file_pos_frame; 58 | uint32_t file_loop_start; 59 | uint32_t file_loop_end; 60 | uint32_t buffer_loop_end; 61 | uint32_t play_count, loop_count; 62 | size_t write_ptr; 63 | size_t produced; 64 | size_t consumed; 65 | gboolean finished; 66 | gboolean returned; 67 | }; 68 | 69 | extern void cbox_prefetch_pipe_init(struct cbox_prefetch_pipe *pipe, uint32_t buffer_size, uint32_t min_buffer_frames); 70 | extern void cbox_prefetch_pipe_consumed(struct cbox_prefetch_pipe *pipe, uint32_t frames); 71 | extern void cbox_prefetch_pipe_close(struct cbox_prefetch_pipe *pipe); 72 | 73 | static inline uint32_t cbox_prefetch_pipe_get_remaining(struct cbox_prefetch_pipe *pipe) 74 | { 75 | assert(pipe->consumed <= pipe->produced); 76 | return pipe->produced - pipe->consumed; 77 | } 78 | 79 | struct cbox_prefetch_stack 80 | { 81 | struct cbox_prefetch_pipe *pipes; 82 | int *next_free_pipe; 83 | int pipe_count; 84 | pthread_t thr_prefetch; 85 | int last_free_pipe; 86 | gboolean finished; 87 | }; 88 | 89 | extern struct cbox_prefetch_stack *cbox_prefetch_stack_new(int npipes, uint32_t buffer_size, uint32_t min_buffer_frames); 90 | extern struct cbox_prefetch_pipe *cbox_prefetch_stack_pop(struct cbox_prefetch_stack *stack, struct cbox_waveform *waveform, uint32_t file_loop_start, uint32_t file_loop_end, uint32_t loop_count); 91 | extern void cbox_prefetch_stack_push(struct cbox_prefetch_stack *stack, struct cbox_prefetch_pipe *pipe); 92 | extern int cbox_prefetch_stack_get_active_pipe_count(struct cbox_prefetch_stack *stack); 93 | extern void cbox_prefetch_stack_destroy(struct cbox_prefetch_stack *stack); 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /skel.c: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include "config.h" 20 | #include "config-api.h" 21 | #include "dspmath.h" 22 | #include "module.h" 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #define MODULE_PARAMS {name}_params 32 | 33 | struct {name}_params 34 | { 35 | }; 36 | 37 | struct {name}_module 38 | { 39 | struct cbox_module module; 40 | 41 | struct {name}_params *params, *old_params; 42 | }; 43 | 44 | gboolean {name}_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) 45 | { 46 | struct {name}_module *m = (struct {name}_module *)ct->user_data; 47 | 48 | // EFFECT_PARAM("/wet_dry", "f", wet_dry, double, , 0, 1) else 49 | if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) 50 | { 51 | if (!cbox_check_fb_channel(fb, cmd->command, error)) 52 | return FALSE; 53 | // return cbox_execute_on(fb, NULL, "/wet_dry", "f", error, m->params->wet_dry); 54 | return CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error); 55 | } 56 | else 57 | return cbox_object_default_process_cmd(ct, fb, cmd, error); 58 | return TRUE; 59 | } 60 | 61 | void {name}_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) 62 | { 63 | struct {name}_module *m = module->user_data; 64 | } 65 | 66 | void {name}_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) 67 | { 68 | struct {name}_module *m = module->user_data; 69 | 70 | if (m->params != m->old_params) 71 | { 72 | // update calculated values 73 | } 74 | } 75 | 76 | MODULE_SIMPLE_DESTROY_FUNCTION({name}) 77 | 78 | MODULE_CREATE_FUNCTION({name}) 79 | { 80 | static int inited = 0; 81 | if (!inited) 82 | { 83 | inited = 1; 84 | } 85 | 86 | struct {name}_module *m = malloc(sizeof(struct {name}_module)); 87 | CALL_MODULE_INIT(m, 0, 2, {name}); 88 | m->module.process_event = {name}_process_event; 89 | m->module.process_block = {name}_process_block; 90 | struct {name}_params *p = malloc(sizeof(struct {name}_params)); 91 | m->params = p; 92 | m->old_params = NULL; 93 | 94 | return &m->module; 95 | } 96 | 97 | 98 | struct cbox_module_keyrange_metadata {name}_keyranges[] = { 99 | }; 100 | 101 | struct cbox_module_livecontroller_metadata {name}_controllers[] = { 102 | }; 103 | 104 | DEFINE_MODULE({name}, 0, 2) 105 | 106 | -------------------------------------------------------------------------------- /engine.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2013 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_ENGINE_H 20 | #define CBOX_ENGINE_H 21 | 22 | #include "cmd.h" 23 | #include "dom.h" 24 | #include "io.h" 25 | #include "midi.h" 26 | #include "rt.h" 27 | 28 | CBOX_EXTERN_CLASS(cbox_engine) 29 | 30 | #define GET_RT_FROM_cbox_engine(ptr) ((ptr)->rt) 31 | 32 | struct cbox_engine 33 | { 34 | CBOX_OBJECT_HEADER() 35 | struct cbox_command_target cmd_target; 36 | struct cbox_io_env io_env; 37 | struct cbox_rt *rt; 38 | struct cbox_scene **scenes; 39 | uint32_t scene_count; 40 | struct cbox_song_playback *spb; 41 | struct cbox_module *effect; 42 | struct cbox_master *master; 43 | struct cbox_midi_buffer midibuf_aux, midibuf_jack, midibuf_song; 44 | struct cbox_song_time_mapper *stmap; 45 | struct cbox_midi_appsink appsink; 46 | 47 | int spb_lock, spb_retry; 48 | 49 | uint32_t frame_start_song_pos, song_pos_offset; // samples 50 | }; 51 | 52 | // These use an RT command internally 53 | extern struct cbox_engine *cbox_engine_new(struct cbox_document *doc, struct cbox_rt *rt); 54 | extern void cbox_engine_update_song_playback(struct cbox_engine *engine); 55 | extern void cbox_engine_update_input_connections(struct cbox_engine *engine); 56 | extern void cbox_engine_update_output_connections(struct cbox_engine *engine); 57 | extern void cbox_engine_add_scene(struct cbox_engine *engine, struct cbox_scene *scene); 58 | void cbox_engine_remove_scene(struct cbox_engine *engine, struct cbox_scene *scene); 59 | extern struct cbox_song *cbox_engine_set_song(struct cbox_engine *engine, struct cbox_song *song, int new_pos); 60 | extern struct cbox_song *cbox_engine_set_pattern(struct cbox_engine *engine, struct cbox_midi_pattern *pattern, int new_pos); 61 | extern void cbox_engine_set_pattern_and_destroy(struct cbox_engine *engine, struct cbox_midi_pattern *pattern); 62 | extern void cbox_engine_send_events_to(struct cbox_engine *engine, struct cbox_midi_merger *merger, struct cbox_midi_buffer *buffer); 63 | extern void cbox_engine_process(struct cbox_engine *engine, struct cbox_io *io, uint32_t nframes, float **output_buffers, uint32_t output_channels); 64 | extern gboolean cbox_engine_on_transport_sync(struct cbox_engine *engine, enum cbox_transport_state state, uint32_t frame); 65 | extern void cbox_engine_on_tempo_sync(struct cbox_engine *engine, double beats_per_minute); 66 | extern struct cbox_midi_merger *cbox_engine_get_midi_output(struct cbox_engine *engine, struct cbox_uuid *uuid); 67 | extern uint32_t cbox_engine_current_pos_samples(struct cbox_engine *engine); 68 | 69 | extern int cbox_engine_get_sample_rate(struct cbox_engine *engine); 70 | extern int cbox_engine_get_buffer_size(struct cbox_engine *engine); 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /experiments/testmetadata.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | This is a minimal calfbox python example. It is meant as a starting 5 | point to find bugs and test performance. 6 | 7 | Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) 8 | 9 | This code is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License 20 | along with this program. If not, see . 21 | """ 22 | 23 | import atexit 24 | from pprint import pprint 25 | from calfbox import cbox 26 | 27 | 28 | cbox.init_engine("") 29 | cbox.Config.set("io", "outputs", 8) 30 | NAME = "Cbox Interactive" 31 | cbox.Config.set("io", "client_name", NAME) 32 | 33 | cbox.start_audio() 34 | scene = cbox.Document.get_engine().new_scene() 35 | scene.clear() 36 | 37 | 38 | print("Setting a pretty name directly on our client") 39 | cbox.JackIO.Metadata.client_set_property("http://jackaudio.org/metadata/pretty-name", NAME+" Pretty Client Name") 40 | 41 | print("Setting nonsense meta data to our first two ports and midi port") 42 | cbox.JackIO.Metadata.set_property("Cbox Interactive:out_1", "foo", "bar") 43 | cbox.JackIO.Metadata.set_property("Cbox Interactive:out_1", "faz", "baz") 44 | cbox.JackIO.Metadata.set_property("Cbox Interactive:out_2", "rolf", "hello") 45 | cbox.JackIO.Metadata.set_property("Cbox Interactive:out_2", "rolf", "hello") 46 | cbox.JackIO.Metadata.set_property("Cbox Interactive:midi", "wolf", "world", "stryng") 47 | cbox.JackIO.Metadata.set_property("Cbox Interactive:midi", "asd", "qwe", "") 48 | 49 | 50 | print ("Setting port order for all 8 ports") 51 | 52 | portOrderDict = { 53 | "Cbox Interactive:out_1": 50, 54 | "Cbox Interactive:out_2": 40, 55 | "Cbox Interactive:out_3": 3, 56 | "Cbox Interactive:out_4": 5, 57 | "Cbox Interactive:out_5": 7, 58 | "Cbox Interactive:out_6": 999, 59 | "Cbox Interactive:out_7": 4, 60 | "Cbox Interactive:out_8": 4, 61 | } 62 | 63 | try: 64 | cbox.JackIO.Metadata.set_all_port_order(portOrderDict) 65 | print ("Test to catch non-unique indices failed!. Quitting") 66 | quit() 67 | except ValueError as e: 68 | print ("Caught expected ValueError for double index entry.\nAdjusting value and try again to set all ports.") 69 | 70 | portOrderDict["Cbox Interactive:out_8"] = 0 71 | cbox.JackIO.Metadata.set_all_port_order(portOrderDict) 72 | 73 | print("List of all metadata follows") 74 | pprint (cbox.JackIO.Metadata.get_all_properties()) 75 | 76 | print() 77 | print ("Now check your port order in QJackCtl or similar. Press [Return] to quit") 78 | input() #wait for key to confirm order visually in qjackctl 79 | 80 | print("Removing the pretty name from our client") 81 | cbox.JackIO.Metadata.client_remove_property("http://jackaudio.org/metadata/pretty-name") 82 | 83 | print("Second time. This will fail: Removing the pretty name from our client") 84 | try: 85 | cbox.JackIO.Metadata.client_remove_property("http://jackaudio.org/metadata/pretty-name") 86 | except Exception as e: 87 | print ("Caught expected error:", e) 88 | 89 | quit() 90 | 91 | def exit_handler(): 92 | #Restore initial state and stop the engine 93 | cbox.Transport.stop() 94 | cbox.stop_audio() 95 | cbox.shutdown_engine() 96 | atexit.register(exit_handler) 97 | -------------------------------------------------------------------------------- /master.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_MASTER_H 20 | #define CBOX_MASTER_H 21 | 22 | #include 23 | #include "cmd.h" 24 | 25 | extern uint64_t PPQN; 26 | 27 | struct cbox_song; 28 | struct cbox_rt; 29 | 30 | #define GET_RT_FROM_cbox_master(ptr) ((ptr)->engine->rt) 31 | 32 | enum cbox_master_transport_state 33 | { 34 | CMTS_STOP, 35 | CMTS_ROLLING, 36 | CMTS_STOPPING, 37 | }; 38 | 39 | struct cbox_master 40 | { 41 | int srate; 42 | float tempo, new_tempo; 43 | int timesig_num; 44 | int timesig_denom; // must be 4 for now 45 | uint64_t ppqn_factor; 46 | enum cbox_master_transport_state state; 47 | struct cbox_engine *engine; 48 | struct cbox_song *song; 49 | struct cbox_song_playback *spb; 50 | struct cbox_command_target cmd_target; 51 | }; 52 | 53 | struct cbox_bbt 54 | { 55 | uint32_t bar; 56 | uint32_t beat; 57 | uint32_t tick; 58 | uint32_t offset_samples; 59 | }; 60 | 61 | struct cbox_master_track_item; 62 | 63 | static inline void cbox_bbt_add(struct cbox_bbt *accum, uint32_t ticks, uint32_t ppqn_factor, uint32_t timesig_num, uint32_t timesig_denom) 64 | { 65 | uint32_t ticks_per_beat = ppqn_factor * 4 / timesig_denom; 66 | uint32_t beats_per_bar = timesig_num; 67 | 68 | accum->tick += ticks % ticks_per_beat; 69 | if (accum->tick >= ticks_per_beat) 70 | { 71 | accum->tick -= ticks_per_beat; 72 | accum->beat++; 73 | } 74 | uint32_t inc_beats = ticks / ticks_per_beat; 75 | accum->beat += inc_beats % beats_per_bar; 76 | if (accum->beat >= beats_per_bar) 77 | { 78 | accum->beat -= beats_per_bar; 79 | accum->bar++; 80 | } 81 | accum->bar += inc_beats / beats_per_bar; 82 | } 83 | 84 | extern struct cbox_master *cbox_master_new(struct cbox_engine *engine); 85 | extern void cbox_master_set_sample_rate(struct cbox_master *master, int srate); 86 | extern void cbox_master_set_tempo(struct cbox_master *master, float tempo); 87 | extern void cbox_master_set_timesig(struct cbox_master *master, int beats, int unit); 88 | extern void cbox_master_ppqn_to_bbt(const struct cbox_master *master, struct cbox_bbt *bbt, int time_ppqn, struct cbox_master_track_item *mti); 89 | //extern uint32_t cbox_master_song_pos_from_bbt(struct cbox_master *master, const struct cbox_bbt *bbt); 90 | extern void cbox_master_play(struct cbox_master *master); 91 | extern void cbox_master_stop(struct cbox_master *master); 92 | extern void cbox_master_panic(struct cbox_master *master); 93 | extern void cbox_master_seek_ppqn(struct cbox_master *master, uint32_t pos_ppqn); 94 | extern void cbox_master_seek_samples(struct cbox_master *master, uint32_t pos_samples); 95 | extern void cbox_master_destroy(struct cbox_master *master); 96 | 97 | uint32_t cbox_master_ppqn_to_samples(struct cbox_master *master, uint32_t time_ppqn); 98 | uint32_t cbox_master_samples_to_ppqn(struct cbox_master *master, uint32_t time_samples); 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CPPFLAGS = -I$(srcdir) -Wall -Wsign-compare -D_GNU_SOURCE 2 | 3 | AM_CFLAGS = $(JACK_DEPS_CFLAGS) $(GLIB_DEPS_CFLAGS) $(FLUIDSYNTH_DEPS_CFLAGS) $(PYTHON_DEPS_CFLAGS) $(LIBSMF_DEPS_CFLAGS) $(LIBSNDFILE_DEPS_CFLAGS) $(LIBUSB_DEPS_CFLAGS) $(ARCH_OPT_CFLAGS) $(NCURSES_DEPS_CFLAGS) 4 | 5 | lib_LTLIBRARIES = libcalfbox.la 6 | 7 | bin_PROGRAMS = calfbox 8 | noinst_PROGRAMS = calfbox_tests 9 | 10 | calfbox_SOURCES = \ 11 | appmenu.c \ 12 | main.c \ 13 | menu.c \ 14 | menuitem.c \ 15 | ui.c 16 | 17 | calfbox_LDADD = libcalfbox.la $(JACK_DEPS_LIBS) $(GLIB_DEPS_LIBS) $(FLUIDSYNTH_DEPS_LIBS) $(PYTHON_DEPS_LIBS) $(LIBSMF_DEPS_LIBS) $(LIBSNDFILE_DEPS_LIBS) $(LIBUSB_DEPS_LIBS) $(NCURSES_DEPS_LIBS) -lpthread -luuid -lm -lrt 18 | 19 | calfbox_tests_SOURCES = \ 20 | tests.c 21 | 22 | calfbox_tests_LDADD = libcalfbox.la $(GLIB_DEPS_LIBS) -lpthread -lm -lrt 23 | 24 | libcalfbox_la_SOURCES = \ 25 | app.c \ 26 | auxbus.c \ 27 | blob.c \ 28 | chorus.c \ 29 | cmd.c \ 30 | compressor.c \ 31 | config-api.c \ 32 | delay.c \ 33 | distortion.c \ 34 | dom.c \ 35 | engine.c \ 36 | eq.c \ 37 | errors.c \ 38 | fbr.c \ 39 | fifo.c \ 40 | fluid.c \ 41 | fuzz.c \ 42 | fxchain.c \ 43 | gate.c \ 44 | hwcfg.c \ 45 | instr.c \ 46 | io.c \ 47 | jackinput.c \ 48 | jackio.c \ 49 | layer.c \ 50 | limiter.c \ 51 | master.c \ 52 | meter.c \ 53 | midi.c \ 54 | mididest.c \ 55 | module.c \ 56 | pattern.c \ 57 | pattern-maker.c \ 58 | phaser.c \ 59 | prefetch_pipe.c \ 60 | recsrc.c \ 61 | reverb.c \ 62 | rt.c \ 63 | sampler.c \ 64 | sampler_channel.c \ 65 | sampler_gen.c \ 66 | sampler_layer.c \ 67 | sampler_nif.c \ 68 | sampler_prevoice.c \ 69 | sampler_prg.c \ 70 | sampler_rll.c \ 71 | sampler_voice.c \ 72 | scene.c \ 73 | scripting.c \ 74 | seq.c \ 75 | seq-adhoc.c \ 76 | sfzloader.c \ 77 | sfzparser.c \ 78 | song.c \ 79 | streamplay.c \ 80 | streamrec.c \ 81 | tarfile.c \ 82 | tonectl.c \ 83 | tonewheel.c \ 84 | track.c \ 85 | usbaudio.c \ 86 | usbio.c \ 87 | usbmidi.c \ 88 | usbprobe.c \ 89 | wavebank.c 90 | 91 | libcalfbox_la_LIBADD = $(JACK_DEPS_LIBS) $(GLIB_DEPS_LIBS) $(FLUIDSYNTH_DEPS_LIBS) $(PYTHON_DEPS_LIBS) $(LIBSMF_DEPS_LIBS) $(LIBSNDFILE_DEPS_LIBS) $(LIBUSB_DEPS_LIBS) -lpthread -luuid -lm -lrt 92 | 93 | if USE_SSE 94 | ARCH_OPT_CFLAGS=-msse -ffast-math 95 | else 96 | if USE_NEON 97 | ARCH_OPT_CFLAGS=-mfloat-abi=hard -mfpu=neon -ffast-math 98 | endif 99 | endif 100 | 101 | noinst_HEADERS = \ 102 | app.h \ 103 | auxbus.h \ 104 | biquad-float.h \ 105 | blob.h \ 106 | cmd.h \ 107 | config-api.h \ 108 | dom.h \ 109 | dspmath.h \ 110 | envelope.h \ 111 | engine.h \ 112 | eq.h \ 113 | errors.h \ 114 | fifo.h \ 115 | hwcfg.h \ 116 | instr.h \ 117 | io.h \ 118 | ioenv.h \ 119 | layer.h \ 120 | master.h \ 121 | menu.h \ 122 | menuitem.h \ 123 | meter.h \ 124 | midi.h \ 125 | mididest.h \ 126 | module.h \ 127 | onepole-int.h \ 128 | onepole-float.h \ 129 | pattern.h \ 130 | pattern-maker.h \ 131 | prefetch_pipe.h \ 132 | recsrc.h \ 133 | rt.h \ 134 | sampler.h \ 135 | sampler_impl.h \ 136 | sampler_layer.h \ 137 | sampler_prg.h \ 138 | scene.h \ 139 | scripting.h \ 140 | seq.h \ 141 | sfzloader.h \ 142 | sfzparser.h \ 143 | song.h \ 144 | stm.h \ 145 | tarfile.h \ 146 | tests.h \ 147 | track.h \ 148 | ui.h \ 149 | usbio_impl.h \ 150 | wavebank.h 151 | 152 | EXTRA_DIST = cboxrc-example 153 | -------------------------------------------------------------------------------- /scene.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_SCENE_H 20 | #define CBOX_SCENE_H 21 | 22 | #include "cmd.h" 23 | #include "dom.h" 24 | #include "mididest.h" 25 | 26 | CBOX_EXTERN_CLASS(cbox_scene) 27 | 28 | struct cbox_aux_bus; 29 | struct cbox_instrument; 30 | struct cbox_midi_buffer; 31 | struct cbox_recording_source; 32 | struct cbox_song_playback; 33 | 34 | struct cbox_scene 35 | { 36 | CBOX_OBJECT_HEADER() 37 | struct cbox_command_target cmd_target; 38 | gchar *name; 39 | gchar *title; 40 | 41 | GHashTable *instrument_hash; 42 | struct cbox_rt *rt; 43 | struct cbox_layer **layers; 44 | uint32_t layer_count; 45 | struct cbox_instrument **instruments; 46 | uint32_t instrument_count; 47 | struct cbox_aux_bus **aux_buses; 48 | uint32_t aux_bus_count; 49 | int transpose; 50 | struct cbox_engine *engine; 51 | struct cbox_midi_merger scene_input_merger; 52 | struct cbox_midi_buffer midibuf_total; 53 | 54 | struct cbox_midi_input **connected_inputs; 55 | uint32_t connected_input_count; 56 | 57 | gboolean enable_default_song_input, enable_default_external_input; 58 | 59 | struct cbox_recording_source *rec_mono_inputs, *rec_mono_outputs; 60 | struct cbox_recording_source *rec_stereo_inputs, *rec_stereo_outputs; 61 | 62 | struct cbox_adhoc_pattern *adhoc_patterns, *retired_adhoc_patterns; 63 | }; 64 | 65 | extern struct cbox_scene *cbox_scene_new(struct cbox_document *document, struct cbox_engine *engine); 66 | extern gboolean cbox_scene_add_layer(struct cbox_scene *scene, struct cbox_layer *layer, GError **error); 67 | extern gboolean cbox_scene_insert_layer(struct cbox_scene *scene, struct cbox_layer *layer, int pos, GError **error); 68 | extern struct cbox_layer *cbox_scene_remove_layer(struct cbox_scene *scene, int pos); 69 | extern void cbox_scene_move_layer(struct cbox_scene *scene, unsigned int oldpos, unsigned int newpos); 70 | extern gboolean cbox_scene_load(struct cbox_scene *scene, const char *section, GError **error); 71 | extern gboolean cbox_scene_remove_instrument(struct cbox_scene *scene, struct cbox_instrument *instrument); 72 | extern struct cbox_aux_bus *cbox_scene_get_aux_bus(struct cbox_scene *scene, const char *name, int allow_load, GError **error); 73 | extern void cbox_scene_render(struct cbox_scene *scene, uint32_t nframes, float *output_buffers[], uint32_t output_channels); 74 | extern void cbox_scene_clear(struct cbox_scene *scene); 75 | extern void cbox_scene_update_connected_inputs(struct cbox_scene *scene); 76 | extern void cbox_scene_update_connected_outputs(struct cbox_scene *scene); 77 | extern gboolean cbox_scene_move_instrument_to(struct cbox_scene *scene, struct cbox_instrument *instrument, struct cbox_scene *new_scene, int dstpos, GError **error); 78 | extern struct cbox_instrument *cbox_scene_get_instrument_by_name(struct cbox_scene *scene, const char *name, gboolean load, GError **error); 79 | extern struct cbox_instrument *cbox_scene_create_instrument(struct cbox_scene *scene, const char *instrument_name, const char *engine_name, GError **error); 80 | extern void cbox_scene_play_adhoc_pattern(struct cbox_scene *scene, struct cbox_adhoc_pattern *ap); 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /midi.c: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include "config-api.h" 20 | #include "midi.h" 21 | #include 22 | 23 | int cbox_midi_buffer_write_inline(struct cbox_midi_buffer *buffer, uint32_t time, ...) 24 | { 25 | uint8_t buf[4]; 26 | va_list va; 27 | va_start(va, time); 28 | buf[0] = va_arg(va, int); 29 | int size = midi_cmd_size(buf[0]); 30 | for (int i = 1; i < size; i++) 31 | buf[i] = va_arg(va, int); 32 | return cbox_midi_buffer_write_event(buffer, time, buf, size); 33 | } 34 | 35 | int cbox_midi_buffer_write_event(struct cbox_midi_buffer *buffer, uint32_t time, uint8_t *data, uint32_t size) 36 | { 37 | struct cbox_midi_event *evt; 38 | 39 | if (buffer->count >= CBOX_MIDI_MAX_EVENTS) 40 | return 0; 41 | if (size > 4 && size > CBOX_MIDI_MAX_LONG_DATA - buffer->long_data_size) 42 | return 0; 43 | evt = &buffer->events[buffer->count++]; 44 | evt->time = time; 45 | evt->size = size; 46 | if (size <= 4) 47 | { 48 | memcpy(evt->data_inline, data, size); 49 | } 50 | else 51 | { 52 | evt->data_ext = buffer->long_data + buffer->long_data_size; 53 | memcpy(evt->data_ext, data, size); 54 | buffer->long_data_size += size; 55 | } 56 | return 1; 57 | } 58 | 59 | int cbox_midi_buffer_copy_event(struct cbox_midi_buffer *buffer, const struct cbox_midi_event *event, int new_time) 60 | { 61 | struct cbox_midi_event *evt; 62 | 63 | if (buffer->count >= CBOX_MIDI_MAX_EVENTS) 64 | return 0; 65 | if (event->size > 4 && event->size > CBOX_MIDI_MAX_LONG_DATA - buffer->long_data_size) 66 | return 0; 67 | evt = &buffer->events[buffer->count++]; 68 | evt->time = new_time; 69 | evt->size = event->size; 70 | if (event->size <= 4) 71 | { 72 | memcpy(evt->data_inline, event->data_inline, event->size); 73 | } 74 | else 75 | { 76 | evt->data_ext = buffer->long_data + buffer->long_data_size; 77 | memcpy(evt->data_ext, event->data_ext, event->size); 78 | buffer->long_data_size += event->size; 79 | } 80 | return 1; 81 | } 82 | 83 | int note_from_string(const char *note) 84 | { 85 | static const int semis[] = {9, 11, 0, 2, 4, 5, 7}; 86 | int pos; 87 | int nn = tolower(note[0]); 88 | int nv; 89 | if (nn >= '0' && nn <= '9') 90 | return atoi(note); 91 | if (nn < 'a' && nn > 'g') 92 | return -1; 93 | nv = semis[nn - 'a']; 94 | 95 | for (pos = 1; note[pos] == 'b' || note[pos] == '#'; pos++) 96 | nv += (note[pos] == 'b') ? -1 : +1; 97 | 98 | if ((note[pos] == '-' && note[pos + 1] >= '1' && note[pos + 1] <= '2' && note[pos + 2] == '\0') || (note[pos] >= '0' && note[pos] <= '9' && note[pos + 1] == '\0')) 99 | { 100 | return nv + 12 * (2 + atoi(note + pos)); 101 | } 102 | 103 | return -1; 104 | } 105 | 106 | int cbox_config_get_note(const char *cfg_section, const char *key, int def_value) 107 | { 108 | const char *cv = cbox_config_get_string(cfg_section, key); 109 | if (cv) 110 | return note_from_string(cv); 111 | return def_value; 112 | } 113 | 114 | -------------------------------------------------------------------------------- /onepole-int.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_ONEPOLE_INT_H 20 | #define CBOX_ONEPOLE_INT_H 21 | 22 | #include "dspmath.h" 23 | 24 | struct cbox_onepole_state 25 | { 26 | int32_t x1; 27 | int32_t y1; 28 | }; 29 | 30 | struct cbox_onepole_coeffs 31 | { 32 | int32_t a0; 33 | int32_t a1; 34 | int32_t b1; 35 | int shift; 36 | }; 37 | 38 | static inline void cbox_onepole_reset(struct cbox_onepole_state *state) 39 | { 40 | state->x1 = state->y1 = 0; 41 | } 42 | 43 | static inline void cbox_onepole_set_lowpass(struct cbox_onepole_coeffs *coeffs, float w) 44 | { 45 | float x = tan (w); 46 | float q = 1 / (1 + x); 47 | float a01 = x*q; 48 | float b1 = a01 - q; 49 | int shift = 28; 50 | float scaler = (1 << shift); 51 | 52 | coeffs->a1 = coeffs->a0 = (int32_t)(a01 * scaler); 53 | coeffs->b1 = (int32_t)(b1 * scaler); 54 | coeffs->shift = shift; 55 | } 56 | 57 | static inline void cbox_onepole_set_highpass(struct cbox_onepole_coeffs *coeffs, float w) 58 | { 59 | float x = tan (w); 60 | float q = 1 / (1 + x); 61 | float a01 = x*q; 62 | float b1 = a01 - q; 63 | int shift = 28; 64 | float scaler = (1 << shift)-1; 65 | 66 | coeffs->a0 = (int32_t)(a01 * scaler); 67 | coeffs->a1 = -coeffs->a0; 68 | coeffs->b1 = (int32_t)(b1 * scaler); 69 | coeffs->shift = shift; 70 | } 71 | 72 | static inline void cbox_onepole_process(struct cbox_onepole_state *state, struct cbox_onepole_coeffs *coeffs, int32_t *buffer) 73 | { 74 | int i; 75 | int64_t a0 = coeffs->a0; 76 | int64_t a1 = coeffs->a1; 77 | int64_t b1 = coeffs->b1; 78 | int shift = coeffs->shift; 79 | int64_t maxint = ((int64_t)0x7FFFFFF) << shift; 80 | int32_t round = 1 << (shift - 1); 81 | 82 | for (i = 0; i < CBOX_BLOCK_SIZE; i++) 83 | { 84 | int32_t in = buffer[i]; 85 | int64_t v = a0 * in + a1 * state->x1 - b1 * state->y1 + round; 86 | int32_t out = (llabs(v) >= maxint) ? (v > 0 ? 0x7FFFFFFF : -0x7FFFFFFF) : (v >> shift); 87 | 88 | buffer[i] = out; 89 | state->x1 = in; 90 | state->y1 = out; 91 | } 92 | if (state->y1 > 0 && state->y1 < round) 93 | state->y1--; 94 | if (state->y1 < 0 && state->y1 > -round) 95 | state->y1++; 96 | } 97 | 98 | static inline void cbox_onepole_process_to(struct cbox_onepole_state *state, struct cbox_onepole_coeffs *coeffs, int32_t *buffer_in, int32_t *buffer_out) 99 | { 100 | int i; 101 | int64_t a0 = coeffs->a0; 102 | int64_t a1 = coeffs->a1; 103 | int64_t b1 = coeffs->b1; 104 | int shift = coeffs->shift; 105 | int64_t maxint = ((int64_t)0x7FFFFFF) << shift; 106 | int64_t round = 1 << (shift - 1); 107 | 108 | for (i = 0; i < CBOX_BLOCK_SIZE; i++) 109 | { 110 | int32_t in = buffer_in[i]; 111 | int64_t v = a0 * in + a1 * state->x1 - b1 * state->y1 + round; 112 | int32_t out = (llabs(v) >= maxint) ? (v > 0 ? 0x7FFFFFFF : -0x7FFFFFFF) : (v >> shift); 113 | 114 | buffer_out[i] = out; 115 | state->x1 = in; 116 | state->y1 = out; 117 | } 118 | } 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /experiments/printNotesOnlyDuringPlayback.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | from meta import ly2Track, initCbox, start, connectPhysicalKeyboards 4 | 5 | def processMidiIn(eventLoop): 6 | """cbox event: ("event address", None, [firstByte, pitch, velocit]) 7 | 8 | Cbox event come in pairs 9 | 10 | first is the normal midi-event + channel 11 | channel = first & 0x0F 12 | mode = first & 0xF0 13 | 14 | Of course it doesn't need to be pitch and velocity. 15 | But this is easier to document than "data2, data3" 16 | 17 | Either call cbox.call_on_idle or cbox.get_new_events. 18 | Both will clean the event queue but only the latter will give us 19 | the results as python data. 20 | """ 21 | eventList = cbox.get_new_events() 22 | lenList = len(eventList) 23 | if lenList >= 2 and lenList % 2 == 0: 24 | for (address, uninterestingStuff, event) in eventList: #we are only interested in the event, which is a list again. 25 | #print (address, "event:", event, "playback:", cbox.Transport.status().playing) 26 | if address in ("/io/midi/event_time_samples", "/io/midi/event_time_ppqn", ): 27 | assert len(event) == 1 28 | #We are very strict at the moment. A timestamp is only allowed if there wasn't another timestamp waiting. It is strict because only simple_event with len==3 eat up the timestamp. Any other event will trigger an error. 29 | if processMidiIn.lastTimestamp: 30 | raise NotImplementedError("the previous event didn't eat up the timestamp") 31 | else: 32 | if address == "/io/midi/event_time_ppqn": 33 | assert cbox.Transport.status().playing == 1 34 | during_recording = True 35 | else: 36 | assert not cbox.Transport.status().playing == 1 37 | during_recording = False 38 | processMidiIn.lastTimestamp = event[0] 39 | 40 | elif address == "/io/midi/simple_event" and len(event) == 3: #we can only unpack the event after knowing its length. 41 | assert processMidiIn.lastTimestamp # Midi events are always preceded by timestamps 42 | first, second, third = event 43 | channel = first & 0x0F 44 | mode = first & 0xF0 #0x90 note on, 0x80 note off and so on. 45 | 46 | if mode == 0x90: #Note On. 144 in decimal 47 | midipitch = second 48 | velocity = third 49 | if during_recording: 50 | print("ON: {}, Channel: {}, Pitch: {}, Velocity: {}".format(processMidiIn.lastTimestamp, channel, midipitch, velocity)) 51 | #else: 52 | # print("ON Time-Samples: {}, Channel: {}, Pitch: {}, Velocity: {}".format(processMidiIn.lastTimestamp, channel, midipitch, velocity)) 53 | 54 | elif mode == 0x80: #Note Off. 128 in decimal 55 | midipitch = second 56 | velocity = third 57 | if during_recording: 58 | print("OFF: {}, Channel: {}, Pitch: {}, Velocity: {}".format(processMidiIn.lastTimestamp, channel, midipitch, velocity)) 59 | 60 | #elif mode == 0xB0: #CC 61 | # ccNumber = second 62 | # ccValue = third 63 | #else: 64 | #discard the events 65 | 66 | processMidiIn.lastTimestamp = None 67 | 68 | else: 69 | raise NotImplementedError("Address type {} unknown".format(address)) 70 | 71 | 72 | eventLoop.call_later(0.1, processMidiIn, eventLoop) 73 | processMidiIn.lastTimestamp = None 74 | 75 | scene, cbox, eventLoop = initCbox("test01", internalEventProcessor=False) 76 | #ly2Track(trackName="doWeNeedThis", lyString="c8") 77 | eventLoop.call_later(0.1, processMidiIn, eventLoop) #100ms 78 | connectPhysicalKeyboards() 79 | start() 80 | -------------------------------------------------------------------------------- /auxbus.c: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include "auxbus.h" 20 | #include "scene.h" 21 | #include 22 | #include 23 | 24 | extern gboolean cbox_scene_insert_aux_bus(struct cbox_scene *scene, struct cbox_aux_bus *aux_bus); 25 | extern void cbox_scene_remove_aux_bus(struct cbox_scene *scene, struct cbox_aux_bus *bus); 26 | 27 | CBOX_CLASS_DEFINITION_ROOT(cbox_aux_bus) 28 | 29 | static gboolean cbox_aux_bus_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) 30 | { 31 | struct cbox_aux_bus *aux_bus = ct->user_data; 32 | struct cbox_rt *rt = (struct cbox_rt *)cbox_document_get_service(CBOX_GET_DOCUMENT(aux_bus), "rt"); 33 | if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) 34 | { 35 | if (!cbox_check_fb_channel(fb, cmd->command, error)) 36 | return FALSE; 37 | 38 | return cbox_execute_on(fb, NULL, "/name", "s", error, aux_bus->name) && 39 | CBOX_OBJECT_DEFAULT_STATUS(aux_bus, fb, error); 40 | } 41 | else 42 | if (!strncmp(cmd->command, "/slot/", 6)) 43 | { 44 | return cbox_module_slot_process_cmd(&aux_bus->module, fb, cmd, cmd->command + 5, CBOX_GET_DOCUMENT(aux_bus), rt, aux_bus->owner->engine, error); 45 | } 46 | else 47 | return cbox_object_default_process_cmd(ct, fb, cmd, error); 48 | } 49 | 50 | struct cbox_aux_bus *cbox_aux_bus_load(struct cbox_scene *scene, const char *name, struct cbox_rt *rt, GError **error) 51 | { 52 | struct cbox_module *module = cbox_module_new_from_fx_preset(name, CBOX_GET_DOCUMENT(scene), rt, scene->engine, error); 53 | if (!module) 54 | return NULL; 55 | 56 | struct cbox_aux_bus *p = malloc(sizeof(struct cbox_aux_bus)); 57 | CBOX_OBJECT_HEADER_INIT(p, cbox_aux_bus, CBOX_GET_DOCUMENT(scene)); 58 | cbox_command_target_init(&p->cmd_target, cbox_aux_bus_process_cmd, p); 59 | p->name = g_strdup(name); 60 | p->owner = scene; 61 | p->module = module; 62 | p->refcount = 0; 63 | // XXXKF this work up to buffer size of 8192 floats, this should be determined from JACK settings and updated when 64 | // JACK buffer size changes 65 | p->input_bufs[0] = malloc(8192 * sizeof(float)); 66 | p->input_bufs[1] = malloc(8192 * sizeof(float)); 67 | p->output_bufs[0] = malloc(8192 * sizeof(float)); 68 | p->output_bufs[1] = malloc(8192 * sizeof(float)); 69 | CBOX_OBJECT_REGISTER(p); 70 | cbox_scene_insert_aux_bus(scene, p); 71 | 72 | return p; 73 | } 74 | 75 | void cbox_aux_bus_ref(struct cbox_aux_bus *bus) 76 | { 77 | ++bus->refcount; 78 | } 79 | 80 | void cbox_aux_bus_unref(struct cbox_aux_bus *bus) 81 | { 82 | assert(bus->refcount > 0); 83 | --bus->refcount; 84 | } 85 | 86 | void cbox_aux_bus_destroyfunc(struct cbox_objhdr *objhdr) 87 | { 88 | struct cbox_aux_bus *bus = CBOX_H2O(objhdr); 89 | if (bus->owner) 90 | { 91 | cbox_scene_remove_aux_bus(bus->owner, bus); 92 | bus->owner = NULL; 93 | } 94 | CBOX_DELETE(bus->module); 95 | bus->module = NULL; 96 | assert(!bus->refcount); 97 | g_free(bus->name); 98 | free(bus->input_bufs[0]); 99 | free(bus->input_bufs[1]); 100 | free(bus); 101 | } 102 | 103 | -------------------------------------------------------------------------------- /novabox.py: -------------------------------------------------------------------------------- 1 | # A primitive drum machine interface for Novation Nocturn. 2 | # 3 | # Usage: 4 | # - make sure that Novation Nocturn is connected *and* that the USB device 5 | # that corresponds to it can be opened by the current user 6 | # - create an ini file containing a scene with a single instrument using 7 | # a sampler or fluidsynth engine + some drum mappings in SFZ or SF2 8 | # - ensure that the ini file contains 7 drum patterns, pat1..pat7, these 9 | # can be copied from cboxrc-example 10 | # - user buttons 1..7 start drum patterns 11 | # - user button 8 stops the playback 12 | # - encoder knob 1 adjusts the volume 13 | # - mixer button exits the application 14 | 15 | import math 16 | import sys 17 | 18 | sys.path = ["./py"] + sys.path 19 | 20 | import nocturn 21 | import cbox 22 | 23 | quit = False 24 | 25 | instr_name = cbox.Document.get_scene().status().instruments.keys()[0] 26 | 27 | def clamp(val, min, max): 28 | if val < min: 29 | val = min 30 | elif val > max: 31 | val = max 32 | return val 33 | 34 | class NovaBox: 35 | def __init__(self): 36 | self.nocturn = nocturn.Nocturn() 37 | self.cur_pattern = None 38 | self.handlers = {} 39 | self.handlers[83] = self.on_xfade_touch 40 | for i in range(7): 41 | self.handlers[112 + i] = lambda cmd, val: self.on_buttonN_press(cmd - 112) if val > 0 else None 42 | self.handlers[112 + 7] = lambda cmd, val: self.on_button8_press() if val > 0 else None 43 | self.handlers[64] = self.on_knob1_change 44 | self.handlers[65] = self.on_knob2_change 45 | self.handlers[127] = lambda cmd, val: self.on_mixer_press() if val > 0 else None 46 | 47 | def on_knob1_change(self, cmd, val): 48 | scene = cbox.Document.get_scene() 49 | instr = scene.status().instruments[instr_name][1] 50 | gain = instr.get_things('/output/1/status', ['gain']).gain 51 | if val > 63: 52 | val = -128 + val 53 | instr.cmd('/output/1/gain', None, gain + val * 0.5) 54 | 55 | def on_knob2_change(self, cmd, val): 56 | tempo = cbox.GetThings("/master/status", ['tempo'], []).tempo 57 | if val > 63: 58 | val = -128 + val 59 | tempo = clamp(tempo + val * 0.5, 30, 300) 60 | cbox.do_cmd('/master/set_tempo', None, [tempo]) 61 | 62 | def on_buttonN_press(self, button): 63 | cbox.do_cmd("/master/stop", None, []) 64 | song = cbox.Document.get_song() 65 | song.loop_single_pattern(lambda: song.load_drum_pattern('pat%d' % (button + 1))) 66 | cbox.do_cmd("/master/seek_ppqn", None, [0]) 67 | cbox.do_cmd("/master/play", None, []) 68 | self.cur_pattern = button 69 | 70 | def on_button8_press(self): 71 | self.cur_pattern = None 72 | cbox.do_cmd("/master/stop", None, []) 73 | 74 | def on_mixer_press(self): 75 | global quit 76 | quit = True 77 | 78 | def on_xfade_touch(self, cmd, val): 79 | if val > 0: 80 | print "Do not touch" 81 | 82 | def handler(self, cmd, val): 83 | if cmd in self.handlers: 84 | self.handlers[cmd](cmd, val) 85 | return 86 | 87 | def poll(self): 88 | self.nocturn.poll(self.handler) 89 | 90 | def update(self): 91 | scene = cbox.Document.get_scene() 92 | cmds = nocturn.NocturnCommands() 93 | master = cbox.GetThings("/master/status", ['playing'], []) 94 | for i in range(7): 95 | cmds.setModeButtonLight(i, self.cur_pattern == i) 96 | gain = scene.status().instruments[instr_name][1].get_things('/output/1/status', ['gain']).gain 97 | cmds.setEncoderMode(0, 0) 98 | cmds.setEncoderValue(0, clamp(int(gain * 2 + 64), 0, 127)) 99 | 100 | cmds.setModeButtonLight(7, self.cur_pattern is None) 101 | self.nocturn.execute(cmds) 102 | 103 | nb = NovaBox() 104 | while not quit: 105 | nb.poll() 106 | nb.update() 107 | nb.nocturn.reset() 108 | -------------------------------------------------------------------------------- /midi.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2013 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_MIDI_H 20 | #define CBOX_MIDI_H 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | struct cbox_midi_event 28 | { 29 | uint32_t time; 30 | uint32_t size; 31 | union { 32 | uint8_t data_inline[4]; /* up to 4 bytes */ 33 | uint8_t *data_ext; /* if larger than 4 bytes */ 34 | }; 35 | }; 36 | 37 | #define CBOX_MIDI_MAX_EVENTS 256 38 | #define CBOX_MIDI_MAX_LONG_DATA 256 39 | 40 | struct cbox_midi_buffer 41 | { 42 | uint32_t count; 43 | uint32_t long_data_size; 44 | struct cbox_midi_event events[CBOX_MIDI_MAX_EVENTS]; 45 | uint8_t long_data[CBOX_MIDI_MAX_LONG_DATA]; 46 | }; 47 | 48 | static inline void cbox_midi_buffer_init(struct cbox_midi_buffer *buffer) 49 | { 50 | buffer->count = 0; 51 | buffer->long_data_size = 0; 52 | } 53 | 54 | static inline void cbox_midi_buffer_clear(struct cbox_midi_buffer *buffer) 55 | { 56 | buffer->count = 0; 57 | buffer->long_data_size = 0; 58 | } 59 | 60 | static inline void cbox_midi_buffer_copy(struct cbox_midi_buffer *dst, const struct cbox_midi_buffer *src) 61 | { 62 | dst->count = src->count; 63 | dst->long_data_size = src->long_data_size; 64 | memcpy(dst->events, src->events, src->count * sizeof(struct cbox_midi_event)); 65 | memcpy(dst->long_data, src->long_data, src->long_data_size); 66 | // for any long events, update data pointers 67 | for (uint32_t i = 0; i < src->count; i++) 68 | { 69 | if (dst->events[i].size > 4) 70 | dst->events[i].data_ext += &dst->long_data[0] - &src->long_data[0]; 71 | } 72 | } 73 | 74 | static inline uint32_t cbox_midi_buffer_get_count(struct cbox_midi_buffer *buffer) 75 | { 76 | return buffer->count; 77 | } 78 | 79 | static inline uint32_t cbox_midi_buffer_get_last_event_time(struct cbox_midi_buffer *buffer) 80 | { 81 | if (!buffer->count) 82 | return 0; 83 | return buffer->events[buffer->count - 1].time; 84 | } 85 | 86 | static inline int cbox_midi_buffer_can_store_msg(struct cbox_midi_buffer *buffer, int size) 87 | { 88 | if (buffer->count >= CBOX_MIDI_MAX_EVENTS) 89 | return 0; 90 | if (size < 4) 91 | return 1; 92 | return buffer->long_data_size + size <= CBOX_MIDI_MAX_LONG_DATA; 93 | } 94 | 95 | static inline const struct cbox_midi_event *cbox_midi_buffer_get_event(const struct cbox_midi_buffer *buffer, uint32_t pos) 96 | { 97 | if (pos >= buffer->count) 98 | return NULL; 99 | return &buffer->events[pos]; 100 | } 101 | 102 | static inline const uint8_t *cbox_midi_event_get_data(const struct cbox_midi_event *evt) 103 | { 104 | return evt->size > 4 ? evt->data_ext : evt->data_inline; 105 | } 106 | 107 | static inline int midi_cmd_size(uint8_t cmd) 108 | { 109 | static const int sizes[] = { 3, 3, 3, 3, 2, 2, 3, 1 }; 110 | if (cmd < 128) 111 | return 0; 112 | return sizes[(cmd >> 4) - 8]; 113 | } 114 | 115 | extern int cbox_midi_buffer_write_event(struct cbox_midi_buffer *buffer, uint32_t time, uint8_t *data, uint32_t size); 116 | 117 | extern int cbox_midi_buffer_write_inline(struct cbox_midi_buffer *buffer, uint32_t time, ...); 118 | 119 | extern int cbox_midi_buffer_copy_event(struct cbox_midi_buffer *buffer, const struct cbox_midi_event *event, int new_time); 120 | 121 | extern int note_from_string(const char *note); 122 | 123 | extern int cbox_config_get_note(const char *cfg_section, const char *key, int def_value); 124 | 125 | #endif 126 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from distutils.core import setup, Extension 5 | import glob 6 | import os 7 | import sys 8 | 9 | support_ext_module = False 10 | 11 | ext_modules = [] 12 | if support_ext_module: 13 | if sys.version_info[0] < 3: 14 | raise Exception("Python 3 required.") 15 | 16 | packages = ['glib-2.0', 'sndfile'] 17 | 18 | if '#define USE_FLUIDSYNTH 1' in open('config.h').read(): 19 | packages.append('fluidsynth') 20 | if '#define USE_JACK 1' in open('config.h').read(): 21 | packages.append('jack') 22 | if '#define USE_LIBUSB 1' in open('config.h').read(): 23 | packages.append('libusb-1.0') 24 | if '#define USE_LIBSMF 1' in open('config.h').read(): 25 | packages.append('smf') 26 | 27 | eargs = os.popen("pkg-config --cflags %s" % (" ".join(packages)), "r").read().split() 28 | eargs.append("-std=c99") 29 | # Workaround for Python3.4 headers 30 | eargs.append("-Wno-error=declaration-after-statement") 31 | 32 | libs = os.popen("pkg-config --libs %s" % (" ".join(packages)), "r").read().split() 33 | libs.append("-luuid") 34 | 35 | csources = [ 36 | "app.c", 37 | "auxbus.c", 38 | "blob.c", 39 | "@chorus.c", 40 | "cmd.c", 41 | "@compressor.c", 42 | "config-api.c", 43 | "@delay.c", 44 | "@distortion.c", 45 | "dom.c", 46 | "eq.c", 47 | "engine.c", 48 | "errors.c", 49 | "@fbr.c", 50 | "fifo.c", 51 | "@fluid.c", 52 | "@fuzz.c", 53 | "@fxchain.c", 54 | "@gate.c", 55 | "hwcfg.c", 56 | "instr.c", 57 | "io.c", 58 | "@jackinput.c", 59 | "@jackio.c", 60 | "layer.c", 61 | "@limiter.c", 62 | "master.c", 63 | "meter.c", 64 | "midi.c", 65 | "mididest.c", 66 | "module.c", 67 | "pattern.c", 68 | "pattern-maker.c", 69 | "@phaser.c", 70 | "prefetch_pipe.c", 71 | "recsrc.c", 72 | "@reverb.c", 73 | "rt.c", 74 | "sampler.c", 75 | "@sampler_channel.c", 76 | "@sampler_gen.c", 77 | "sampler_layer.c", 78 | "@sampler_nif.c", 79 | "@sampler_prevoice.c", 80 | "sampler_prg.c", 81 | "@sampler_rll.c", 82 | "@sampler_voice.c", 83 | "scene.c", 84 | "scripting.c", 85 | "seq.c", 86 | "@seq-adhoc.c", 87 | "sfzloader.c", 88 | "sfzparser.c", 89 | "song.c", 90 | "@streamplay.c", 91 | "@streamrec.c", 92 | "tarfile.c", 93 | "@tonectl.c", 94 | "@tonewheel.c", 95 | "track.c", 96 | "@usbaudio.c", 97 | "@usbio.c", 98 | "@usbmidi.c", 99 | "@usbprobe.c", 100 | "wavebank.c", 101 | ] 102 | 103 | headers = [ 104 | "biquad-float.h", 105 | "config.h", 106 | "dspmath.h", 107 | "envelope.h", 108 | "ioenv.h", 109 | "onepole-float.h", 110 | "onepole-int.h", 111 | "sampler_impl.h", 112 | "stm.h", 113 | "usbio_impl.h", 114 | ] 115 | 116 | headers += [fn[:-2] + ".h" for fn in csources if fn.endswith(".c") and not fn.startswith("@")] 117 | csources = [fn.lstrip("@") for fn in csources] 118 | 119 | if '#define USE_SSE 1' in open('config.h').read(): 120 | eargs.append('-msse') 121 | eargs.append('-ffast-math') 122 | if '#define USE_NEON 1' in open('config.h').read(): 123 | eargs.append('-mfloat-abi=hard') 124 | eargs.append('-mfpu=neon') 125 | eargs.append('-ffast-math') 126 | 127 | ext_modules.append( 128 | Extension('_cbox', csources, 129 | extra_compile_args = eargs, 130 | include_dirs=['.'], 131 | extra_link_args=libs, 132 | define_macros=[("_GNU_SOURCE","1"),("_POSIX_C_SOURCE", "199309L"),("USE_PYTHON","1"),("CALFBOX_AS_MODULE", "1")], 133 | undef_macros=['NDEBUG'], 134 | depends = ['setup.py'] + headers 135 | ) 136 | ) 137 | setup(name="CalfBox", 138 | version="0.0.0.2", description="Assorted music-related code", 139 | author="Krzysztof Foltman", author_email="wdev@foltman.com", 140 | url="https://github.com/kfoltman/calfbox", 141 | packages=["calfbox"], 142 | package_dir={'calfbox':'py'}, 143 | ext_modules=ext_modules, 144 | ) 145 | -------------------------------------------------------------------------------- /py/nocturn.py: -------------------------------------------------------------------------------- 1 | # Novation Nocturn driver 2 | # Based on DWTFYW code by De Wet van Niekert (dewert). However, I had to 3 | # put reading of the input endpoint in a separate thread because the only 4 | # reliable way to read it is by using large timeouts (1s or so). With shorter 5 | # timeouts, some events are lost/replaced by crossfader value. 6 | 7 | import array 8 | import binascii 9 | import fcntl 10 | import os 11 | import usb.core 12 | import usb.util 13 | import sys 14 | import time 15 | import threading 16 | 17 | class NocturnCommands: 18 | def __init__(self): 19 | self.pkt = "" 20 | def setModeButtonLight(self, button, state): 21 | self.pkt += chr(0x70 + button) + ('\x01' if state else '\x00') 22 | def setUserButtonLight(self, button, state): 23 | self.pkt += chr(0x78 + button) + ('\x01' if state else '\x00') 24 | def setEncoderMode(self, encoder, mode): 25 | self.pkt += chr(0x48 + encoder) + chr(mode << 4) 26 | def setEncoderValue(self, encoder, value): 27 | self.pkt += chr(0x40 + encoder) + chr(value) 28 | def setSpeedDialMode(self, mode): 29 | self.pkt += chr(0x51) + chr(mode << 4) 30 | def setSpeedDialValue(self, value): 31 | self.pkt += chr(0x50) + chr(value) 32 | def clear(self): 33 | for i in range(8): 34 | self.setModeButtonLight(i, False) 35 | self.setUserButtonLight(i, False) 36 | if i & 1: 37 | self.setEncoderMode(i, 3) 38 | else: 39 | self.setEncoderMode(i, 4) 40 | self.setEncoderValue(i, 64) 41 | self.setSpeedDialMode(5) 42 | self.setSpeedDialValue(64) 43 | 44 | class NocturnHandler(threading.Thread): 45 | def __init__(self, n): 46 | threading.Thread.__init__(self) 47 | self.nocturn = n 48 | self.rpipefd, self.wpipefd = os.pipe() 49 | self.rpipe = os.fdopen(self.rpipefd, "rb") 50 | self.wpipe = os.fdopen(self.wpipefd, "wb") 51 | flags = fcntl.fcntl(self.rpipe, fcntl.F_GETFL) 52 | fcntl.fcntl(self.rpipe, fcntl.F_SETFL, flags | os.O_NONBLOCK) 53 | self.setDaemon(True) 54 | def run(self): 55 | while True: 56 | pkt = self.nocturn.read() 57 | if pkt is not None: 58 | self.wpipe.write(pkt) 59 | self.wpipe.flush() 60 | def poll(self, handler): 61 | try: 62 | data = array.array('B', self.rpipe.read()) 63 | i = 0 64 | # For longer sequences, Nocturn skips the control change message 65 | while i < len(data): 66 | if data[i] == 176: 67 | i += 1 68 | continue 69 | handler(data[i], data[i + 1]) 70 | i += 2 71 | except IOError as e: 72 | pass 73 | def get_poll_fd(self): 74 | return self.rpipefd 75 | 76 | class Nocturn: 77 | vendorID = 0x1235 78 | productID = 0x000a 79 | def __init__(self): 80 | dev = usb.core.find(idVendor=self.vendorID, idProduct=self.productID) 81 | # The values in here don't seem to matter THAT much 82 | initPackets=["b00000","28002b4a2c002e35","2a022c722e30"] 83 | #This is a minimum set that enables the device, but then it doesn't 84 | #really work reliably, at least the touch sensing 85 | #initPackets=["b00000", "2800"] 86 | 87 | if dev is None: 88 | raise ValueError('Device not found') 89 | sys.exit() 90 | 91 | self.dev = dev 92 | cfg = dev[1] 93 | intf = cfg[(0,0)] 94 | 95 | self.ep = intf[1] 96 | self.ep2 = intf[0] 97 | dev.set_configuration(2) 98 | for packet in initPackets: 99 | self.ep.write(binascii.unhexlify(packet)) 100 | 101 | self.reset() 102 | self.reader = NocturnHandler(self) 103 | self.reader.start() 104 | 105 | def reset(self): 106 | cmd = NocturnCommands() 107 | cmd.clear() 108 | self.execute(cmd) 109 | 110 | def execute(self, cmd): 111 | self.ep.write(cmd.pkt) 112 | def read(self): 113 | try: 114 | data = self.ep2.read(8, None) 115 | return buffer(data) 116 | except usb.core.USBError as e: 117 | return None 118 | def poll(self, handler): 119 | return self.reader.poll(handler) 120 | def get_poll_fd(self): 121 | return self.reader.get_poll_fd() 122 | 123 | -------------------------------------------------------------------------------- /sampler_nif.c: -------------------------------------------------------------------------------- 1 | #include "config-api.h" 2 | #include "dspmath.h" 3 | #include "errors.h" 4 | #include "midi.h" 5 | #include "module.h" 6 | #include "rt.h" 7 | #include "sampler.h" 8 | #include "sampler_impl.h" 9 | 10 | ////////////////////////////////////////////////////////////////////////// 11 | // Note initialisation functions 12 | 13 | void sampler_nif_cc2delay(struct sampler_noteinitfunc *nif, struct sampler_prevoice *pv) 14 | { 15 | float cc = sampler_channel_getcc_prevoice(pv->channel, pv, nif->key.variant, nif->value.curve_id, nif->value.step); 16 | pv->delay_computed += nif->value.value * cc; 17 | } 18 | 19 | void sampler_nif_addrandomdelay(struct sampler_noteinitfunc *nif, struct sampler_prevoice *pv) 20 | { 21 | pv->delay_computed += nif->value.value * rand() * (1.0 / RAND_MAX); 22 | } 23 | 24 | void sampler_nif_syncbeats(struct sampler_noteinitfunc *nif, struct sampler_prevoice *pv) 25 | { 26 | if (nif->value.value > 0) 27 | { 28 | pv->sync_beats = nif->value.value; 29 | double cur_beat = sampler_get_current_beat(pv->channel->module); 30 | pv->sync_initial_time = cur_beat; 31 | double cur_rel_beat = fmod(cur_beat, pv->sync_beats); 32 | double bar_start = cur_beat - cur_rel_beat; 33 | if (pv->layer_data->sync_offset <= cur_rel_beat) // trigger in next bar 34 | pv->sync_trigger_time = bar_start + pv->sync_beats + pv->layer_data->sync_offset; 35 | else // trigger in the same bar 36 | pv->sync_trigger_time = bar_start + pv->layer_data->sync_offset; 37 | // printf("cur_beat %f trigger %f offset %f\n", cur_beat, pv->sync_trigger_time, pv->layer_data->sync_offset); 38 | } 39 | } 40 | 41 | void sampler_nif_vel2pitch(struct sampler_noteinitfunc *nif, struct sampler_voice *v) 42 | { 43 | v->pitch_shift += nif->value.value * v->vel * (1.0 / 127.0); 44 | } 45 | 46 | void sampler_nif_vel2offset(struct sampler_noteinitfunc *nif, struct sampler_voice *v) 47 | { 48 | v->offset += nif->value.value * v->vel * (1.0 / 127.0); 49 | } 50 | 51 | void sampler_nif_cc2offset(struct sampler_noteinitfunc *nif, struct sampler_voice *v) 52 | { 53 | v->offset += nif->value.value * sampler_channel_getcc_mod(v->channel, v, nif->key.variant, nif->value.curve_id, nif->value.step); 54 | } 55 | 56 | void sampler_nif_vel2reloffset(struct sampler_noteinitfunc *nif, struct sampler_voice *v) 57 | { 58 | v->reloffset += nif->value.value * v->vel * (1.0 / 127.0); 59 | } 60 | 61 | void sampler_nif_cc2reloffset(struct sampler_noteinitfunc *nif, struct sampler_voice *v) 62 | { 63 | v->reloffset += nif->value.value * sampler_channel_getcc_mod(v->channel, v, nif->key.variant, nif->value.curve_id, nif->value.step); 64 | } 65 | 66 | void sampler_nif_addrandom(struct sampler_noteinitfunc *nif, struct sampler_voice *v) 67 | { 68 | float rnd = rand() * 1.0 / RAND_MAX; 69 | switch(nif->key.variant) 70 | { 71 | case 0: 72 | v->gain_shift += rnd * nif->value.value; 73 | break; 74 | case 1: 75 | v->cutoff_shift += rnd * nif->value.value; 76 | break; 77 | case 2: 78 | v->pitch_shift += rnd * nif->value.value; // this is in cents 79 | break; 80 | } 81 | } 82 | 83 | static void modify_env_stage_by_nif(struct sampler_noteinitfunc *nif, struct sampler_voice *v, uint32_t variant, float value) 84 | { 85 | int env_type = variant >> 4; 86 | struct cbox_envelope *env = NULL; 87 | switch(env_type) 88 | { 89 | case 0: 90 | env = &v->amp_env; 91 | break; 92 | case 1: 93 | env = &v->filter_env; 94 | break; 95 | case 2: 96 | env = &v->pitch_env; 97 | break; 98 | default: 99 | assert(0); 100 | } 101 | if (env->shape != &v->vel_envs[env_type]) 102 | { 103 | memcpy(&v->vel_envs[env_type], env->shape, sizeof(struct cbox_envelope_shape)); 104 | env->shape = &v->vel_envs[env_type]; 105 | } 106 | float param = nif->value.value * value; 107 | if ((variant & 15) == snif_env_sustain || (variant & 15) == snif_env_start) 108 | param *= 0.01; 109 | cbox_envelope_modify_dahdsr(env->shape, variant & 15, param, v->channel->module->module.srate * (1.0 / CBOX_BLOCK_SIZE)); 110 | } 111 | 112 | void sampler_nif_vel2env(struct sampler_noteinitfunc *nif, struct sampler_voice *v) 113 | { 114 | modify_env_stage_by_nif(nif, v, nif->key.variant, v->vel * (1.0 / 127.0)); 115 | } 116 | -------------------------------------------------------------------------------- /distortion.c: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include "config.h" 20 | #include "config-api.h" 21 | #include "dspmath.h" 22 | #include "module.h" 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #define MODULE_PARAMS distortion_params 32 | 33 | struct distortion_params 34 | { 35 | float drive; 36 | float shape; 37 | }; 38 | 39 | struct distortion_module 40 | { 41 | struct cbox_module module; 42 | 43 | struct distortion_params *params, *old_params; 44 | }; 45 | 46 | gboolean distortion_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) 47 | { 48 | struct distortion_module *m = (struct distortion_module *)ct->user_data; 49 | 50 | EFFECT_PARAM("/drive", "f", drive, double, dB2gain_simple, -36, 36) else 51 | EFFECT_PARAM("/shape", "f", shape, double, , -1, 2) else 52 | if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) 53 | { 54 | if (!cbox_check_fb_channel(fb, cmd->command, error)) 55 | return FALSE; 56 | return cbox_execute_on(fb, NULL, "/drive", "f", error, gain2dB_simple(m->params->drive)) 57 | && cbox_execute_on(fb, NULL, "/shape", "f", error, m->params->shape) 58 | && CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error) 59 | ; 60 | } 61 | else 62 | return cbox_object_default_process_cmd(ct, fb, cmd, error); 63 | return TRUE; 64 | } 65 | 66 | void distortion_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) 67 | { 68 | // struct distortion_module *m = module->user_data; 69 | } 70 | 71 | void distortion_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) 72 | { 73 | struct distortion_module *m = module->user_data; 74 | 75 | if (m->params != m->old_params) 76 | { 77 | // update calculated values 78 | } 79 | 80 | float drive = m->params->drive; 81 | float shape = m->params->shape; 82 | 83 | float a0 = shape; 84 | float a1 = -2 * shape - 0.5; 85 | float a2 = 1.5 + shape; 86 | 87 | float post = pow(drive, -0.7); 88 | 89 | for (int i = 0; i < CBOX_BLOCK_SIZE; i++) 90 | { 91 | for (int c = 0; c < 2; c++) 92 | { 93 | float val = inputs[c][i]; 94 | 95 | val *= drive; 96 | 97 | if (fabs(val) > 1.0) 98 | val = (val > 0) ? 1 : -1; 99 | else 100 | val = a0 * val * val * val * val * val + a1 * val * val * val + a2 * val; 101 | 102 | outputs[c][i] = val * post; 103 | } 104 | } 105 | } 106 | 107 | MODULE_SIMPLE_DESTROY_FUNCTION(distortion) 108 | 109 | MODULE_CREATE_FUNCTION(distortion) 110 | { 111 | static int inited = 0; 112 | if (!inited) 113 | { 114 | inited = 1; 115 | } 116 | 117 | struct distortion_module *m = malloc(sizeof(struct distortion_module)); 118 | CALL_MODULE_INIT(m, 2, 2, distortion); 119 | m->module.process_event = distortion_process_event; 120 | m->module.process_block = distortion_process_block; 121 | struct distortion_params *p = malloc(sizeof(struct distortion_params)); 122 | p->drive = cbox_config_get_gain_db(cfg_section, "drive", 0.f); 123 | p->shape = cbox_config_get_gain_db(cfg_section, "shape", 0.f); 124 | m->params = p; 125 | m->old_params = NULL; 126 | 127 | return &m->module; 128 | } 129 | 130 | 131 | struct cbox_module_keyrange_metadata distortion_keyranges[] = { 132 | }; 133 | 134 | struct cbox_module_livecontroller_metadata distortion_controllers[] = { 135 | }; 136 | 137 | DEFINE_MODULE(distortion, 2, 2) 138 | 139 | -------------------------------------------------------------------------------- /blob.c: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include "blob.h" 20 | #include "tarfile.h" 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | struct cbox_blob *cbox_blob_new(size_t size) 30 | { 31 | struct cbox_blob *p = malloc(sizeof(struct cbox_blob)); 32 | if (!p) 33 | return NULL; 34 | p->data = size ? malloc(size) : NULL; 35 | p->size = size; 36 | return p; 37 | } 38 | 39 | struct cbox_blob *cbox_blob_new_copy_data(const void *data, size_t size) 40 | { 41 | struct cbox_blob *p = cbox_blob_new(size); 42 | if (!p) 43 | return NULL; 44 | memcpy(p, data, size); 45 | return p; 46 | } 47 | 48 | struct cbox_blob *cbox_blob_new_acquire_data(void *data, size_t size) 49 | { 50 | struct cbox_blob *p = malloc(sizeof(struct cbox_blob)); 51 | if (!p) 52 | return NULL; 53 | p->data = data; 54 | p->size = size; 55 | return p; 56 | } 57 | 58 | static struct cbox_blob *read_from_fd(const char *context_name, const char *pathname, int fd, size_t size, GError **error) 59 | { 60 | struct cbox_blob *blob = cbox_blob_new(size + 1); 61 | if (!blob) 62 | { 63 | g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "%s: cannot allocate memory for file '%s'", context_name, pathname); 64 | return NULL; 65 | } 66 | uint8_t *data = blob->data; 67 | size_t nread = 0; 68 | do { 69 | size_t chunk = size - nread; 70 | if (chunk > 131072) 71 | chunk = 131072; 72 | size_t nv = read(fd, data + nread, chunk); 73 | if (nv == (size_t)-1) 74 | { 75 | if (errno == EINTR) 76 | continue; 77 | g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "%s: cannot read '%s': %s", context_name, pathname, strerror(errno)); 78 | cbox_blob_destroy(blob); 79 | return NULL; 80 | } 81 | nread += nv; 82 | } while(nread < size); 83 | // Make sure that the content is 0-padded but still has the original size 84 | // (without extra zero byte) 85 | data[nread] = '\0'; 86 | blob->size = nread; 87 | return blob; 88 | } 89 | 90 | struct cbox_blob *cbox_blob_new_from_file(const char *context_name, struct cbox_tarfile *tarfile, const char *path, const char *name, size_t max_size, GError **error) 91 | { 92 | gchar *fullpath = g_build_filename(path, name, NULL); 93 | struct cbox_blob *blob = NULL; 94 | if (tarfile) 95 | { 96 | struct cbox_taritem *item = cbox_tarfile_get_item_by_name(tarfile, fullpath, TRUE); 97 | if (item) 98 | { 99 | int fd = cbox_tarfile_openitem(tarfile, item); 100 | if (fd >= 0) 101 | { 102 | blob = read_from_fd(context_name, fullpath, fd, item->size, error); 103 | cbox_tarfile_closeitem(tarfile, item, fd); 104 | } 105 | } 106 | } 107 | else 108 | { 109 | int fd = open(fullpath, O_RDONLY | O_LARGEFILE); 110 | if (fd >= 0) 111 | { 112 | uint64_t size = lseek64(fd, 0, SEEK_END); 113 | if (size <= max_size) 114 | blob = read_from_fd(context_name, fullpath, fd, size, error); 115 | else 116 | g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "%s: file '%s' too large (%llu while max size is %u)", context_name, fullpath, (unsigned long long)size, (unsigned)max_size); 117 | close(fd); 118 | } 119 | else 120 | g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "%s: cannot open '%s': %s", context_name, fullpath, strerror(errno)); 121 | } 122 | g_free(fullpath); 123 | return blob; 124 | } 125 | 126 | void cbox_blob_destroy(struct cbox_blob *blob) 127 | { 128 | free(blob->data); 129 | free(blob); 130 | } 131 | -------------------------------------------------------------------------------- /delay.c: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include "config.h" 20 | #include "config-api.h" 21 | #include "dspmath.h" 22 | #include "module.h" 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #define MAX_DELAY_LENGTH 65536 32 | 33 | struct delay_params 34 | { 35 | float time; 36 | float wet_dry, fb_amt; 37 | }; 38 | 39 | struct delay_module 40 | { 41 | struct cbox_module module; 42 | 43 | float storage[MAX_DELAY_LENGTH][2]; 44 | struct delay_params *params; 45 | int pos; 46 | }; 47 | 48 | #define MODULE_PARAMS delay_params 49 | 50 | MODULE_PROCESSCMD_FUNCTION(delay) 51 | { 52 | struct delay_module *m = (struct delay_module *)ct->user_data; 53 | 54 | EFFECT_PARAM("/time", "f", time, double, , 1, 1000) else 55 | EFFECT_PARAM("/fb_amt", "f", fb_amt, double, , 0, 1) else 56 | EFFECT_PARAM("/wet_dry", "f", wet_dry, double, , 0, 1) else 57 | if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) 58 | { 59 | if (!cbox_check_fb_channel(fb, cmd->command, error)) 60 | return FALSE; 61 | return cbox_execute_on(fb, NULL, "/time", "f", error, m->params->time) 62 | && cbox_execute_on(fb, NULL, "/fb_amt", "f", error, m->params->fb_amt) 63 | && cbox_execute_on(fb, NULL, "/wet_dry", "f", error, m->params->wet_dry) 64 | && CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error) 65 | ; 66 | } 67 | else 68 | return cbox_object_default_process_cmd(ct, fb, cmd, error); 69 | return TRUE; 70 | } 71 | 72 | void delay_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) 73 | { 74 | // struct delay_module *m = (struct delay_module *)module; 75 | } 76 | 77 | void delay_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) 78 | { 79 | struct delay_module *m = (struct delay_module *)module; 80 | 81 | int pos = m->pos; 82 | int dv = m->params->time * m->module.srate / 1000.0; 83 | float dryamt = 1 - m->params->wet_dry; 84 | float wetamt = m->params->wet_dry; 85 | float fbamt = m->params->fb_amt; 86 | 87 | for (int i = 0; i < CBOX_BLOCK_SIZE; i++) 88 | { 89 | float dry[2] = { inputs[0][i], inputs[1][i] }; 90 | float *delayed = &m->storage[pos & (MAX_DELAY_LENGTH - 1)][0]; 91 | 92 | float wet[2] = { dryamt * dry[0] + wetamt * delayed[0], dryamt * dry[1] + wetamt * delayed[1] }; 93 | float fb[2] = { dry[0] + fbamt * delayed[0], dry[1] + fbamt * delayed[1] }; 94 | outputs[0][i] = sanef(wet[0]); 95 | outputs[1][i] = sanef(wet[1]); 96 | 97 | float *wcell = &m->storage[(pos + dv) & (MAX_DELAY_LENGTH - 1)][0]; 98 | wcell[0] = sanef(fb[0]); 99 | wcell[1] = sanef(fb[1]); 100 | pos++; 101 | } 102 | m->pos = pos; 103 | } 104 | 105 | MODULE_SIMPLE_DESTROY_FUNCTION(delay) 106 | 107 | MODULE_CREATE_FUNCTION(delay) 108 | { 109 | static int inited = 0; 110 | int i; 111 | if (!inited) 112 | { 113 | inited = 1; 114 | } 115 | 116 | struct delay_module *m = malloc(sizeof(struct delay_module)); 117 | CALL_MODULE_INIT(m, 2, 2, delay); 118 | struct delay_params *p = malloc(sizeof(struct delay_params)); 119 | m->params = p; 120 | m->module.process_event = delay_process_event; 121 | m->module.process_block = delay_process_block; 122 | m->pos = 0; 123 | p->time = cbox_config_get_float(cfg_section, "delay", 250); 124 | p->wet_dry = cbox_config_get_float(cfg_section, "wet_dry", 0.3); 125 | p->fb_amt = cbox_config_get_gain_db(cfg_section, "feedback_gain", -12.f); 126 | for (i = 0; i < MAX_DELAY_LENGTH; i++) 127 | m->storage[i][0] = m->storage[i][1] = 0.f; 128 | 129 | return &m->module; 130 | } 131 | 132 | struct cbox_module_keyrange_metadata delay_keyranges[] = { 133 | }; 134 | 135 | struct cbox_module_livecontroller_metadata delay_controllers[] = { 136 | }; 137 | 138 | DEFINE_MODULE(delay, 2, 2) 139 | 140 | -------------------------------------------------------------------------------- /meter.c: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include "dspmath.h" 20 | #include "errors.h" 21 | #include "meter.h" 22 | #include 23 | #include 24 | #include 25 | 26 | static void clear_meter(struct cbox_meter *m) 27 | { 28 | for (int i = 0; i < 2; i++) 29 | { 30 | m->volume[i] = 0.f; 31 | m->peak[i] = 0.f; 32 | m->last_peak[i] = 0.f; 33 | } 34 | m->smpcounter = 0; 35 | } 36 | 37 | gboolean cbox_meter_attach(struct cbox_recorder *handler, struct cbox_recording_source *src, GError **error) 38 | { 39 | struct cbox_meter *m = handler->user_data; 40 | m->channels = src->channels; 41 | clear_meter(m); 42 | return TRUE; 43 | } 44 | 45 | void cbox_meter_record_block(struct cbox_recorder *handler, const float **buffers, uint32_t offset, uint32_t numsamples) 46 | { 47 | struct cbox_meter *m = handler->user_data; 48 | for (int c = 0; c < m->channels; c++) 49 | { 50 | float peak = m->peak[c]; 51 | float volume = m->volume[c]; 52 | for (uint32_t i = 0; i < numsamples; i++) 53 | { 54 | float s = buffers[c][i]; 55 | if (fabs(s) > peak) 56 | peak = fabs(s); 57 | volume += (s * s - volume) * 0.01; // XXXKF this is too simplistic, needs sample rate and proper time constant 58 | } 59 | m->peak[c] = peak; 60 | m->volume[c] = sanef(volume); 61 | } 62 | m->smpcounter += numsamples; 63 | if (m->smpcounter > m->srate) 64 | { 65 | for (int c = 0; c < m->channels; c++) 66 | { 67 | m->last_peak[c] = m->peak[c]; 68 | m->peak[c] = 0; 69 | } 70 | m->smpcounter = 0; 71 | } 72 | } 73 | 74 | gboolean cbox_meter_detach(struct cbox_recorder *handler, GError **error) 75 | { 76 | struct cbox_meter *m = handler->user_data; 77 | m->channels = 0; 78 | clear_meter(m); 79 | return TRUE; 80 | } 81 | 82 | void cbox_meter_destroy(struct cbox_recorder *handler) 83 | { 84 | struct cbox_meter *m = handler->user_data; 85 | free(m); 86 | } 87 | 88 | static gboolean cbox_meter_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) 89 | { 90 | struct cbox_meter *m = ct->user_data; 91 | if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) 92 | { 93 | return CBOX_OBJECT_DEFAULT_STATUS(&m->recorder, fb, error); 94 | } 95 | if (!strcmp(cmd->command, "/get_peak") && !strcmp(cmd->arg_types, "")) 96 | { 97 | if (!cbox_check_fb_channel(fb, cmd->command, error)) 98 | return FALSE; 99 | 100 | float peak[2]; 101 | for (int c = 0; c < 2; c++) 102 | { 103 | float v = m->peak[c], w = m->last_peak[c]; 104 | if (v < w) 105 | v = w; 106 | peak[c] = v; 107 | } 108 | 109 | return cbox_execute_on(fb, NULL, "/peak", "ff", error, peak[0], peak[1]); 110 | } 111 | else 112 | if (!strcmp(cmd->command, "/get_rms") && !strcmp(cmd->arg_types, "")) 113 | { 114 | if (!cbox_check_fb_channel(fb, cmd->command, error)) 115 | return FALSE; 116 | 117 | return cbox_execute_on(fb, NULL, "/rms", "ff", error, sqrt(m->volume[0]), sqrt(m->volume[1])); 118 | } 119 | else 120 | return cbox_object_default_process_cmd(ct, fb, cmd, error); 121 | } 122 | 123 | struct cbox_meter *cbox_meter_new(struct cbox_document *document, int srate) 124 | { 125 | struct cbox_meter *m = malloc(sizeof(struct cbox_meter)); 126 | CBOX_OBJECT_HEADER_INIT(&m->recorder, cbox_recorder, document); 127 | m->recorder.user_data = m; 128 | cbox_command_target_init(&m->recorder.cmd_target, cbox_meter_process_cmd, m); 129 | m->recorder.attach = cbox_meter_attach; 130 | m->recorder.detach = cbox_meter_detach; 131 | m->recorder.record_block = cbox_meter_record_block; 132 | m->recorder.destroy = cbox_meter_destroy; 133 | m->srate = srate; 134 | clear_meter(m); 135 | CBOX_OBJECT_REGISTER(&m->recorder); 136 | return m; 137 | } -------------------------------------------------------------------------------- /fifo.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2013 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_FIFO_H 20 | #define CBOX_FIFO_H 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | struct cbox_fifo 28 | { 29 | uint8_t *data; 30 | uint32_t size; 31 | uint64_t pad; // ensure the write-related and read-related structs are on 64 bit boundary 32 | uint32_t write_count; 33 | uint32_t write_offset; 34 | uint32_t read_count; 35 | uint32_t read_offset; 36 | }; 37 | 38 | extern struct cbox_fifo *cbox_fifo_new(uint32_t size); 39 | 40 | static inline uint32_t cbox_fifo_readsize(struct cbox_fifo *fifo); 41 | static inline uint32_t cbox_fifo_writespace(struct cbox_fifo *fifo); 42 | static inline gboolean cbox_fifo_read_atomic(struct cbox_fifo *fifo, void *dest, uint32_t bytes); 43 | static inline gboolean cbox_fifo_write_atomic(struct cbox_fifo *fifo, const void *src, uint32_t bytes); 44 | static inline gboolean cbox_fifo_peek(struct cbox_fifo *fifo, void *dest, uint32_t bytes); 45 | static inline gboolean cbox_fifo_consume(struct cbox_fifo *fifo, uint32_t bytes); 46 | 47 | extern void cbox_fifo_destroy(struct cbox_fifo *fifo); 48 | 49 | 50 | static inline uint32_t cbox_fifo_readsize(struct cbox_fifo *fifo) 51 | { 52 | return fifo->write_count - fifo->read_count; 53 | } 54 | 55 | static inline uint32_t cbox_fifo_writespace(struct cbox_fifo *fifo) 56 | { 57 | return fifo->size - (fifo->write_count - fifo->read_count); 58 | } 59 | 60 | static inline gboolean cbox_fifo_read_impl(struct cbox_fifo *fifo, void *dest, uint32_t bytes, gboolean advance) 61 | { 62 | __sync_synchronize(); 63 | if (fifo->write_count - fifo->read_count < bytes) 64 | return FALSE; 65 | 66 | if (dest) 67 | { 68 | uint32_t ofs = fifo->read_count - fifo->read_offset; 69 | assert(ofs >= 0 && ofs < fifo->size); 70 | if (ofs + bytes > fifo->size) 71 | { 72 | uint8_t *dstb = (uint8_t *)dest; 73 | uint32_t firstpart = fifo->size - ofs; 74 | memcpy(dstb, fifo->data + ofs, firstpart); 75 | memcpy(dstb + firstpart, fifo->data, bytes - firstpart); 76 | } 77 | else 78 | memcpy(dest, fifo->data + ofs, bytes); 79 | } 80 | 81 | if (advance) 82 | { 83 | __sync_synchronize(); 84 | // Make sure data are copied before signalling that they can be overwritten 85 | fifo->read_count += bytes; 86 | if (fifo->read_count - fifo->read_offset >= fifo->size) 87 | fifo->read_offset += fifo->size; 88 | } 89 | __sync_synchronize(); 90 | 91 | return TRUE; 92 | } 93 | 94 | static inline gboolean cbox_fifo_read_atomic(struct cbox_fifo *fifo, void *dest, uint32_t bytes) 95 | { 96 | return cbox_fifo_read_impl(fifo, dest, bytes, TRUE); 97 | } 98 | 99 | static inline gboolean cbox_fifo_peek(struct cbox_fifo *fifo, void *dest, uint32_t bytes) 100 | { 101 | return cbox_fifo_read_impl(fifo, dest, bytes, FALSE); 102 | } 103 | 104 | static inline gboolean cbox_fifo_consume(struct cbox_fifo *fifo, uint32_t bytes) 105 | { 106 | return cbox_fifo_read_impl(fifo, NULL, bytes, TRUE); 107 | } 108 | 109 | static inline gboolean cbox_fifo_write_atomic(struct cbox_fifo *fifo, const void *src, uint32_t bytes) 110 | { 111 | if (fifo->size - (fifo->write_count - fifo->read_count) < bytes) 112 | return FALSE; 113 | 114 | uint32_t ofs = fifo->write_count - fifo->write_offset; 115 | assert(ofs >= 0 && ofs < fifo->size); 116 | if (ofs + bytes > fifo->size) 117 | { 118 | const uint8_t *srcb = (const uint8_t *)src; 119 | uint32_t firstpart = fifo->size - ofs; 120 | memcpy(fifo->data + ofs, srcb, firstpart); 121 | memcpy(fifo->data, srcb + firstpart, bytes - firstpart); 122 | } 123 | else 124 | memcpy(fifo->data + ofs, src, bytes); 125 | 126 | // Make sure data are in the buffer before announcing the availability 127 | __sync_synchronize(); 128 | fifo->write_count += bytes; 129 | if (fifo->write_count - fifo->write_offset >= fifo->size) 130 | fifo->write_offset += fifo->size; 131 | 132 | return TRUE; 133 | } 134 | 135 | 136 | #endif 137 | -------------------------------------------------------------------------------- /tonectl.c: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include "config.h" 20 | #include "config-api.h" 21 | #include "dspmath.h" 22 | #include "module.h" 23 | #include "onepole-float.h" 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #define MODULE_PARAMS tone_control_params 33 | 34 | struct tone_control_params 35 | { 36 | float lowpass, highpass; 37 | }; 38 | 39 | struct tone_control_module 40 | { 41 | struct cbox_module module; 42 | 43 | struct tone_control_params *params, *old_params; 44 | 45 | struct cbox_onepolef_coeffs lowpass_coeffs, highpass_coeffs; 46 | 47 | struct cbox_onepolef_state lowpass_state[2], highpass_state[2]; 48 | 49 | float tpdsr; // 2 pi / sr 50 | }; 51 | 52 | gboolean tone_control_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) 53 | { 54 | struct tone_control_module *m = (struct tone_control_module *)ct->user_data; 55 | 56 | EFFECT_PARAM("/lowpass", "f", lowpass, double, , 5, 20000) else 57 | EFFECT_PARAM("/highpass", "f", highpass, double, , 5, 20000) else 58 | if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) 59 | { 60 | if (!cbox_check_fb_channel(fb, cmd->command, error)) 61 | return FALSE; 62 | return cbox_execute_on(fb, NULL, "/lowpass", "f", error, m->params->lowpass) 63 | && cbox_execute_on(fb, NULL, "/highpass", "f", error, m->params->highpass) 64 | && CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error) 65 | ; 66 | } 67 | else 68 | return cbox_object_default_process_cmd(ct, fb, cmd, error); 69 | return TRUE; 70 | } 71 | 72 | void tone_control_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) 73 | { 74 | // struct tone_control_module *m = (struct tone_control_module *)module; 75 | } 76 | 77 | void tone_control_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) 78 | { 79 | struct tone_control_module *m = (struct tone_control_module *)module; 80 | 81 | if (m->params != m->old_params) 82 | { 83 | cbox_onepolef_set_lowpass(&m->lowpass_coeffs, m->params->lowpass * m->tpdsr); 84 | cbox_onepolef_set_highpass(&m->highpass_coeffs, m->params->highpass * m->tpdsr); 85 | m->old_params = m->params; 86 | } 87 | 88 | cbox_onepolef_process_to(&m->lowpass_state[0], &m->lowpass_coeffs, inputs[0], outputs[0]); 89 | cbox_onepolef_process_to(&m->lowpass_state[1], &m->lowpass_coeffs, inputs[1], outputs[1]); 90 | cbox_onepolef_process(&m->highpass_state[0], &m->highpass_coeffs, outputs[0]); 91 | cbox_onepolef_process(&m->highpass_state[1], &m->highpass_coeffs, outputs[1]); 92 | } 93 | 94 | MODULE_SIMPLE_DESTROY_FUNCTION(tone_control) 95 | 96 | MODULE_CREATE_FUNCTION(tone_control) 97 | { 98 | static int inited = 0; 99 | if (!inited) 100 | { 101 | inited = 1; 102 | } 103 | 104 | struct tone_control_module *m = malloc(sizeof(struct tone_control_module)); 105 | CALL_MODULE_INIT(m, 2, 2, tone_control); 106 | m->module.process_event = tone_control_process_event; 107 | m->module.process_block = tone_control_process_block; 108 | 109 | m->tpdsr = 2 * M_PI * m->module.srate_inv; 110 | 111 | m->old_params = NULL; 112 | m->params = malloc(sizeof(struct tone_control_params)); 113 | 114 | m->params->lowpass = cbox_config_get_float(cfg_section, "lowpass", 8000.f); 115 | m->params->highpass = cbox_config_get_float(cfg_section, "highpass", 75.f); 116 | 117 | cbox_onepolef_reset(&m->lowpass_state[0]); 118 | cbox_onepolef_reset(&m->lowpass_state[1]); 119 | cbox_onepolef_reset(&m->highpass_state[0]); 120 | cbox_onepolef_reset(&m->highpass_state[1]); 121 | 122 | return &m->module; 123 | } 124 | 125 | 126 | struct cbox_module_keyrange_metadata tone_control_keyranges[] = { 127 | }; 128 | 129 | struct cbox_module_livecontroller_metadata tone_control_controllers[] = { 130 | }; 131 | 132 | DEFINE_MODULE(tone_control, 2, 2) 133 | 134 | -------------------------------------------------------------------------------- /menuitem.h: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CBOX_MENUITEM_H 20 | #define CBOX_MENUITEM_H 21 | 22 | #if USE_NCURSES 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | struct cbox_menu; 29 | struct cbox_menu_item; 30 | struct cbox_menu_item_command; 31 | struct cbox_menu_item_static; 32 | struct cbox_menu_item_menu; 33 | struct cbox_menu_state; 34 | 35 | struct cbox_menu_measure 36 | { 37 | int label_width; 38 | int value_width; 39 | int height; 40 | }; 41 | 42 | struct cbox_menu_item_class 43 | { 44 | void (*measure)(struct cbox_menu_item *item, struct cbox_menu_state *state); 45 | void (*draw)(struct cbox_menu_item *item, struct cbox_menu_state *state, gchar *value, int hilited); 46 | gchar *(*format_value)(const struct cbox_menu_item *item, struct cbox_menu_state *state); 47 | int (*on_key)(struct cbox_menu_item *item, struct cbox_menu_state *state, int key); 48 | int (*on_idle)(struct cbox_menu_item *item, struct cbox_menu_state *state); 49 | void (*destroy)(struct cbox_menu_item *item); 50 | 51 | }; 52 | 53 | typedef int (*cbox_menu_item_execute_func)(struct cbox_menu_item_command *item, void *context); 54 | typedef char *(*cbox_menu_item_format_value)(const struct cbox_menu_item_static *item, void *context); 55 | 56 | struct cbox_menu_item 57 | { 58 | gchar *label; 59 | struct cbox_menu_item_class *item_class; 60 | void *item_context; 61 | uint32_t flags; 62 | int x, y; 63 | /* TODO: is_active? */ 64 | }; 65 | 66 | struct cbox_menu_item_command 67 | { 68 | struct cbox_menu_item item; 69 | int (*execute)(struct cbox_menu_item_command *item, void *menu_context); 70 | }; 71 | 72 | struct cbox_menu_item_int 73 | { 74 | struct cbox_menu_item item; 75 | int *value; 76 | int vmin, vmax; 77 | const char *fmt; 78 | int (*on_change)(struct cbox_menu_item_int *item, void *menu_context); 79 | }; 80 | 81 | struct cbox_menu_item_double 82 | { 83 | struct cbox_menu_item item; 84 | double *value; 85 | double vmin, vmax; 86 | const char *fmt; 87 | double step_arg; 88 | double (*step)(struct cbox_menu_item_double *item, int where); 89 | int (*on_change)(struct cbox_menu_item_double *item, void *menu_context); 90 | }; 91 | 92 | struct cbox_menu_item_static 93 | { 94 | struct cbox_menu_item item; 95 | char *(*format_value)(const struct cbox_menu_item_static *item, void *menu_context); 96 | }; 97 | 98 | typedef struct cbox_menu *(*create_menu_func)(struct cbox_menu_item_menu *item, void *menu_context); 99 | 100 | struct cbox_menu_item_context 101 | { 102 | void (*destroy_func)(void *menu_context); 103 | }; 104 | 105 | struct cbox_menu_item_menu 106 | { 107 | struct cbox_menu_item item; 108 | struct cbox_menu *menu; 109 | create_menu_func create_menu; 110 | }; 111 | 112 | enum { 113 | mif_free_label = 1, // release the label on destroy 114 | mif_free_context = 2, // release the context on destroy 115 | mif_dup_label = 4 | mif_free_label, // clone the label, release the clone on destroy 116 | mif_context_is_struct = 8, // cast context to cbox_menu_item_context and call destroy_func on destroy (it may or may not free() itself) 117 | }; 118 | 119 | extern struct cbox_menu_item *cbox_menu_item_new_command(const char *label, cbox_menu_item_execute_func exec, void *item_context, uint32_t flags); 120 | extern struct cbox_menu_item *cbox_menu_item_new_static(const char *label, cbox_menu_item_format_value fmt, void *item_context, uint32_t flags); 121 | extern struct cbox_menu_item *cbox_menu_item_new_int(const char *label, int *value, int vmin, int vmax, void *item_context, uint32_t flags); 122 | extern struct cbox_menu_item *cbox_menu_item_new_menu(const char *label, struct cbox_menu *menu, void *item_context, uint32_t flags); 123 | extern struct cbox_menu_item *cbox_menu_item_new_dynamic_menu(const char *label, create_menu_func func, void *item_context, uint32_t flags); 124 | extern void cbox_menu_item_destroy(struct cbox_menu_item *); 125 | 126 | static inline struct cbox_menu_item *cbox_menu_item_new_ok(void) 127 | { 128 | return cbox_menu_item_new_menu("OK", NULL, NULL, 0); 129 | } 130 | 131 | #endif 132 | 133 | #endif 134 | -------------------------------------------------------------------------------- /sampler_api_load_stress_test.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | NUMBER_OF_INSTRUMENTS = 240 5 | """ 6 | 2021-11-13 Benchmark: 7 | NumberOfInstruments,StartInSeconds,QuitInSeconds 8 | 30, 5, 3 9 | 60, 10, 6 10 | 120, 21, 12 11 | 240, 42, 25 12 | 13 | Conclusion: Linear time. 14 | """ 15 | 16 | from calfbox import cbox 17 | 18 | import atexit 19 | from datetime import datetime 20 | 21 | #Capture Ctlr+C / SIGINT and let @atexit handle the rest. 22 | import signal 23 | import sys 24 | def signal_handler(sig, frame): 25 | sys.exit(0) #atexit will trigger 26 | signal.signal(signal.SIGINT, signal_handler) 27 | 28 | 29 | def cmd_dumper(cmd, fb, args): 30 | #print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) 31 | pass 32 | 33 | def stopSession(): 34 | """This got registered with atexit in the nsm new or open callback above. 35 | will handle all python exceptions, but not segfaults of C modules. """ 36 | print() 37 | print("Starting Quit through @atexit, stopSession") 38 | starttime = datetime.now() 39 | #Don't do that. We are just a client. 40 | #cbox.Transport.stop() 41 | #print("@atexit: Calfbox Transport stopped ") 42 | cbox.stop_audio() 43 | print("@atexit: Calfbox Audio stopped ") 44 | cbox.shutdown_engine() 45 | print("@atexit: Calfbox Engine shutdown ") 46 | endtime = datetime.now() - starttime 47 | print (f"Shutdown took {endtime.seconds} seconds for {NUMBER_OF_INSTRUMENTS} instruments") 48 | 49 | cbox.init_engine("") #empty string so cbox doesn't look for the .cboxrc file 50 | cbox.start_audio(cmd_dumper) 51 | atexit.register(stopSession) #this will handle all python exceptions, but not segfaults of C modules. 52 | 53 | scenes = {} 54 | jackAudioOutLefts = {} 55 | jackAudioOutRights = {} 56 | outputMergerRouters = {} 57 | routerToGlobalSummingStereoMixers = {} 58 | lmixUuid = cbox.JackIO.create_audio_output('left_mix', "#1") #add "#1" as second parameter for auto-connection to system out 1 59 | rmixUuid = cbox.JackIO.create_audio_output('right_mix', "#2") #add "#2" as second parameter for auto-connection to system out 2 60 | cboxMidiPortUids = {} 61 | sfzSamplerLayers = {} 62 | instrumentLayers = {}\ 63 | 64 | 65 | print (f"Creating {NUMBER_OF_INSTRUMENTS} instruments") 66 | starttime = datetime.now() 67 | 68 | for i in range(NUMBER_OF_INSTRUMENTS): 69 | scenes[i] = cbox.Document.get_engine().new_scene() 70 | scenes[i].clear() 71 | 72 | #instrumentLayer = scenes[i].status().layers[0].get_instrument() 73 | sfzSamplerLayers[i] = scenes[i].add_new_instrument_layer(str(i), "sampler") #"sampler" is the cbox sfz engine 74 | scenes[i].status().layers[0].get_instrument().engine.load_patch_from_string(0, "", "", "") #fill with null instruments 75 | 76 | jackAudioOutLefts[i] = cbox.JackIO.create_audio_output(str(i) +"_L") 77 | jackAudioOutRights[i] = cbox.JackIO.create_audio_output(str(i) +"_R") 78 | 79 | outputMergerRouters[i] = cbox.JackIO.create_audio_output_router(jackAudioOutLefts[i], jackAudioOutRights[i]) 80 | outputMergerRouters[i].set_gain(-3.0) 81 | #instrumentLayer = sfzSamplerLayers[i].get_instrument() 82 | instrumentLayers[i] = scenes[i].status().layers[0].get_instrument() 83 | instrumentLayers[i].get_output_slot(0).rec_wet.attach(outputMergerRouters[i]) #output_slot is 0 based and means a pair. Most sfz instrument have only one stereo pair. #TODO: And what if not? 84 | 85 | routerToGlobalSummingStereoMixers[i] = cbox.JackIO.create_audio_output_router(lmixUuid, rmixUuid) 86 | routerToGlobalSummingStereoMixers[i].set_gain(-3.0) 87 | instrument = sfzSamplerLayers[i].get_instrument() 88 | instrument.get_output_slot(0).rec_wet.attach(routerToGlobalSummingStereoMixers[i]) 89 | 90 | #Create Midi Input Port 91 | cboxMidiPortUids[i] = cbox.JackIO.create_midi_input(str(i) + "midi_in") 92 | cbox.JackIO.set_appsink_for_midi_input(cboxMidiPortUids[i], True) #This sounds like a program wide sink, but it is needed for every port. 93 | cbox.JackIO.route_midi_input(cboxMidiPortUids[i], scenes[i].uuid) #Route midi input to the scene. Without this we have no sound, but the python processor would still work. 94 | 95 | #Actually load the instrument 96 | programNumber = 0 97 | #program = instrumentLayers[i].engine.load_patch_from_tar(programNumber, "bug.tar", f'Saw{1}.sfz', f'Saw{i+1}') #tar_name, sfz_name, display_name 98 | program = instrumentLayers[i].engine.load_patch_from_string(programNumber, ".", "", " sample=*saw") #fill with null instruments 99 | print (f"[{i}]", program.status()) 100 | instrumentLayers[i].engine.set_patch(1, programNumber) #1 is the channel, counting from 1. #TODO: we want this to be on all channels. 101 | 102 | endtime = datetime.now() - starttime 103 | print (f"Creation took {endtime.seconds} seconds for {NUMBER_OF_INSTRUMENTS} instruments") 104 | 105 | print() 106 | print("Press Ctrl+C for a controlled shutdown.") 107 | print() 108 | 109 | while True: 110 | cbox.call_on_idle(cmd_dumper) 111 | -------------------------------------------------------------------------------- /API: -------------------------------------------------------------------------------- 1 | @module >> @chorus, @phaser, ... 2 | 3 | @moduleslot/status() -> 4 | /insert_engine(string engine), 5 | /insert_preset(string preset), 6 | /bypass(int bypassed) 7 | @moduleslot/insert_engine(string engine) 8 | @moduleslot/insert_preset(string engine) 9 | @moduleslot/engine/{add: @module} 10 | @moduleslot/set_bypass(int bypassed) 11 | 12 | @track/add_clip(int pos, int offset, int length, string pattern) 13 | 14 | /master/ 15 | /master/status() -> /sample_rate, /tempo, /timesig, /playing, /pos, /pos_ppqn 16 | /master/tell() -> /playing, /pos, /pos_ppqn 17 | /master/set_tempo(float tempo) 18 | /master/set_timesig(int num, int denom) 19 | /master/play() 20 | /master/stop() 21 | /master/seek_samples(int samples) 22 | /master/seek_ppqn(int ppqn) 23 | 24 | /meter/ 25 | /meter/get_peak() -> /peak(float left, float right) 26 | /meter/get_rms() -> /rms(float left, float right) 27 | 28 | /config/ 29 | /config/sections([string prefix]) -> [/section(string name)] 30 | /config/keys(string section, string ?prefix) -> [/section(string name)] 31 | /config/get(string section, string key) -> /value(string value) 32 | /config/set(string section, string key, string value) 33 | /config/delete(string section, string key) 34 | /config/delete_section(string section) 35 | /config/save(string ?filename) 36 | 37 | /engine/ 38 | /engine/status() -> /scene(object scene) 39 | /engine/render_stereo(int nframes) 40 | /engine/master_effect/{add: @moduleslot} 41 | /engine/new_scene() -> uuid 42 | /engine/new_recorder() -> uuid 43 | 44 | /scene/ 45 | /scene/transpose(int semitones) 46 | /scene/clear() 47 | /scene/load(string scene_name) 48 | /scene/add_layer(int layer_pos, string layer_name) 49 | /scene/add_instrument_layer(int layer_pos, string instrument_name) 50 | /scene/delete_layer(int pos) 51 | /scene/move_layer(int oldpos, int newpos) 52 | /scene/instr/ 53 | /scene/instr//status() -> 54 | /engine(string name), 55 | /aux_offset(int first_aux_output_no), 56 | /outputs(int stereo_output_count) 57 | /scene/instr//output//status() -> 58 | /gain_linear(float gain), 59 | /gain(float gain_dB), 60 | /output(int output_bus), 61 | {add: @moduleslot/status()} 62 | /scene/instr//output//gain(float gain_dB), 63 | /scene/instr//output//output(int output_bus) 64 | /scene/instr//output//{add: @moduleslot} 65 | /scene/instr//aux//status() -> 66 | /gain_linear(float gain), 67 | /gain(float gain_dB), 68 | /bus(string output_bus), 69 | {add: @moduleslot/status()} XXXKF ???? 70 | /scene/instr//aux//gain(float gain_dB) 71 | /scene/instr//aux//bus(string bus) 72 | /scene/instr//aux//{add: @moduleslot} 73 | /scene/layer// 74 | /scene/layer//status() -> 75 | /enable(int), 76 | /instrument_name(string iname), 77 | /instrument_uuid(string uuid), 78 | /consume(int consume), 79 | /ignore_scene_transpose(int ignore), 80 | /disable_aftertouch(int disable), 81 | /transpose(int semitones), 82 | /fixed_note(int note), 83 | /low_note(int note), 84 | /high_note(int note), 85 | /in_channel(int channel), 86 | /out_channel(int channel) 87 | /scene/layer//enable(int) 88 | /scene/layer//instrument_name(string iname) 89 | /scene/layer//consume(int consume) 90 | /scene/layer//ignore_scene_transpose(int ignore) 91 | /scene/layer//disable_aftertouch(int disable) 92 | /scene/layer//transpose(int semitones) 93 | /scene/layer//fixed_note(int note) 94 | /scene/layer//low_note(int note) 95 | /scene/layer//high_note(int note) 96 | /scene/layer//in_channel(int channel) 97 | /scene/layer//out_channel(int channel) 98 | /scene/aux//status 99 | /scene/aux//slot/{add: @module} 100 | /scene/load_aux(string name) 101 | /scene/delete_aux(string name) 102 | /scene/status() -> 103 | /name(string), 104 | /title(string), 105 | /transpose(int semitones), 106 | [/layer(string uuid)], 107 | [/instrument(string instance, string engine)], 108 | [/aux(string name, string uuid)] 109 | 110 | /rt/ 111 | /rt/status() -> /audio_channels(int inputs, int outputs) 112 | /song/ 113 | /song/status() -> [/track(int index, string name, int items)], [/pattern(int index, string name, int length)] 114 | /waves/ 115 | /waves/status() -> /bytes(int bytes), /max_bytes(int max_bytes), /count(int count) 116 | /waves/list() -> [/waveform(int id)] 117 | /waves/info(int id) -> /filename(string), /name(string), /bytes(int) 118 | /on_idle() -> {any} 119 | /send_event_to(string output, int) 120 | /send_event_to(string output, int, int) 121 | /send_event_to(string output, int, int, int) 122 | /play_note(int ch, int note, int velocity) (plays a note with duration=1 on the next buffer) 123 | /play_drum_pattern(string pattern) 124 | /play_drum_track(string track) 125 | /play_blob(blob serialized_pattern, int length_ticks) 126 | /stop_pattern() 127 | /get_pattern() -> /pattern(blob serialized_pattern, int length_ticks) 128 | /print_s(string) 129 | /print_i(int) 130 | /print_f(float) 131 | /new_meter() -> /uuid 132 | /new_recorder(string filename) -> /uuid 133 | 134 | -------------------------------------------------------------------------------- /recsrc.c: -------------------------------------------------------------------------------- 1 | /* 2 | Calf Box, an open source musical instrument. 3 | Copyright (C) 2010-2011 Krzysztof Foltman 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include "app.h" 20 | #include "errors.h" 21 | #include "recsrc.h" 22 | #include "rt.h" 23 | #include "scene.h" 24 | #include "stm.h" 25 | 26 | static void cbox_recorder_destroyfunc(struct cbox_objhdr *objhdr) 27 | { 28 | struct cbox_recorder *rec = CBOX_H2O(objhdr); 29 | if (rec->destroy) 30 | rec->destroy(rec); 31 | free(rec); 32 | } 33 | 34 | CBOX_CLASS_DEFINITION_ROOT(cbox_recorder) 35 | 36 | static gboolean cbox_recording_source_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error); 37 | 38 | void cbox_recording_source_init(struct cbox_recording_source *src, struct cbox_scene *scene, uint32_t max_numsamples, int channels) 39 | { 40 | src->scene = scene; 41 | src->handlers = NULL; 42 | src->handler_count = 0; 43 | src->max_numsamples = max_numsamples; 44 | src->channels = channels; 45 | cbox_command_target_init(&src->cmd_target, cbox_recording_source_process_cmd, src); 46 | } 47 | 48 | gboolean cbox_recording_source_attach(struct cbox_recording_source *src, struct cbox_recorder *rec, GError **error) 49 | { 50 | if (rec->attach && !rec->attach(rec, src, error)) 51 | return FALSE; 52 | cbox_rt_array_insert(app.rt, (void ***)&src->handlers, &src->handler_count, 0, rec); 53 | return TRUE; 54 | } 55 | 56 | int cbox_recording_source_detach(struct cbox_recording_source *src, struct cbox_recorder *rec, GError **error) 57 | { 58 | int index = -1; 59 | for (uint32_t i = 0; i < src->handler_count; i++) 60 | { 61 | if (src->handlers[i] == rec) 62 | { 63 | index = i; 64 | break; 65 | } 66 | } 67 | if (index == -1) 68 | { 69 | if (error) 70 | g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Recorder is not attached to this source"); 71 | return 0; 72 | } 73 | 74 | cbox_rt_array_remove(app.rt, (void ***)&src->handlers, &src->handler_count, index); 75 | // XXXKF: when converting to async API, the array_remove must be done synchronously or 76 | // detach needs to be called in the cleanup part of the remove command, otherwise detach 77 | // may be called on 'live' recorder, which may cause unpredictable results. 78 | return rec->detach ? rec->detach(rec, error) : 1; 79 | } 80 | 81 | void cbox_recording_source_push(struct cbox_recording_source *src, const float **buffers, uint32_t offset, uint32_t numsamples) 82 | { 83 | for (uint32_t i = 0; i < src->handler_count; i++) 84 | src->handlers[i]->record_block(src->handlers[i], buffers, offset, numsamples); 85 | } 86 | 87 | void cbox_recording_source_uninit(struct cbox_recording_source *src) 88 | { 89 | STM_ARRAY_FREE_OBJS(src->handlers, src->handler_count); 90 | src->handlers = NULL; 91 | src->handler_count = 0; 92 | } 93 | 94 | gboolean cbox_recording_source_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) 95 | { 96 | struct cbox_recording_source *src = ct->user_data; 97 | if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) 98 | { 99 | if (!cbox_check_fb_channel(fb, cmd->command, error)) 100 | return FALSE; 101 | 102 | for (uint32_t i = 0; i < src->handler_count; i++) 103 | { 104 | if (!cbox_execute_on(fb, NULL, "/handler", "o", error, src->handlers[i])) 105 | return FALSE; 106 | } 107 | return TRUE; 108 | } 109 | else 110 | if (!strcmp(cmd->command, "/attach") && !strcmp(cmd->arg_types, "s")) 111 | { 112 | struct cbox_objhdr *objhdr = CBOX_ARG_O(cmd, 0, src->scene, cbox_recorder, error); 113 | if (!objhdr) 114 | return FALSE; 115 | struct cbox_recorder *rec = CBOX_H2O(objhdr); 116 | return cbox_recording_source_attach(src, rec, error); 117 | } 118 | else 119 | if (!strcmp(cmd->command, "/detach") && !strcmp(cmd->arg_types, "s")) 120 | { 121 | struct cbox_objhdr *objhdr = CBOX_ARG_O(cmd, 0, src->scene, cbox_recorder, error); 122 | if (!objhdr) 123 | return FALSE; 124 | struct cbox_recorder *rec = CBOX_H2O(objhdr); 125 | return cbox_recording_source_detach(src, rec, error); 126 | } 127 | else 128 | return cbox_set_command_error(error, cmd); 129 | } 130 | 131 | --------------------------------------------------------------------------------