diff --git a/template/calfbox/.gitignore b/template/calfbox/.gitignore deleted file mode 100644 index 920aea8..0000000 --- a/template/calfbox/.gitignore +++ /dev/null @@ -1,32 +0,0 @@ -aclocal.m4 -autom4te.cache -config.h.in -config.h.in~ -config.h -configure -configure~ -compile -depcomp -install-sh -ltmain.sh -missing -*.o -*.lo -.libs/ -.deps/ -libcalfbox.la -libtool -Makefile.in -Makefile -config.log -config.status -stamp-h1 -calfbox -calfbox_tests - -__pycache__/ -*.py[cod] -*$py.class -build/ -config.guess -config.sub diff --git a/template/calfbox/API b/template/calfbox/API deleted file mode 100644 index 07f9522..0000000 --- a/template/calfbox/API +++ /dev/null @@ -1,133 +0,0 @@ -@module >> @chorus, @phaser, ... - -@moduleslot/status() -> - /insert_engine(string engine), - /insert_preset(string preset), - /bypass(int bypassed) -@moduleslot/insert_engine(string engine) -@moduleslot/insert_preset(string engine) -@moduleslot/engine/{add: @module} -@moduleslot/set_bypass(int bypassed) - -@track/add_clip(int pos, int offset, int length, string pattern) - -/master/ -/master/status() -> /sample_rate, /tempo, /timesig, /playing, /pos, /pos_ppqn -/master/tell() -> /playing, /pos, /pos_ppqn -/master/set_tempo(float tempo) -/master/set_timesig(int num, int denom) -/master/play() -/master/stop() -/master/seek_samples(int samples) -/master/seek_ppqn(int ppqn) - -/meter/ -/meter/get_peak() -> /peak(float left, float right) -/meter/get_rms() -> /rms(float left, float right) - -/config/ -/config/sections([string prefix]) -> [/section(string name)] -/config/keys(string section, string ?prefix) -> [/section(string name)] -/config/get(string section, string key) -> /value(string value) -/config/set(string section, string key, string value) -/config/delete(string section, string key) -/config/delete_section(string section) -/config/save(string ?filename) - -/engine/ -/engine/status() -> /scene(object scene) -/engine/render_stereo(int nframes) -/engine/master_effect/{add: @moduleslot} -/engine/new_scene() -> uuid -/engine/new_recorder() -> uuid - -/scene/ -/scene/transpose(int semitones) -/scene/clear() -/scene/load(string scene_name) -/scene/add_layer(int layer_pos, string layer_name) -/scene/add_instrument_layer(int layer_pos, string instrument_name) -/scene/delete_layer(int pos) -/scene/move_layer(int oldpos, int newpos) -/scene/instr/ -/scene/instr//status() -> - /engine(string name), - /aux_offset(int first_aux_output_no), - /outputs(int stereo_output_count) -/scene/instr//output//status() -> - /gain_linear(float gain), - /gain(float gain_dB), - /output(int output_bus), - {add: @moduleslot/status()} -/scene/instr//output//gain(float gain_dB), -/scene/instr//output//output(int output_bus) -/scene/instr//output//{add: @moduleslot} -/scene/instr//aux//status() -> - /gain_linear(float gain), - /gain(float gain_dB), - /bus(string output_bus), - {add: @moduleslot/status()} XXXKF ???? -/scene/instr//aux//gain(float gain_dB) -/scene/instr//aux//bus(string bus) -/scene/instr//aux//{add: @moduleslot} -/scene/layer// -/scene/layer//status() -> - /enable(int), - /instrument_name(string iname), - /instrument_uuid(string uuid), - /consume(int consume), - /ignore_scene_transpose(int ignore), - /disable_aftertouch(int disable), - /transpose(int semitones), - /fixed_note(int note), - /low_note(int note), - /high_note(int note), - /in_channel(int channel), - /out_channel(int channel) -/scene/layer//enable(int) -/scene/layer//instrument_name(string iname) -/scene/layer//consume(int consume) -/scene/layer//ignore_scene_transpose(int ignore) -/scene/layer//disable_aftertouch(int disable) -/scene/layer//transpose(int semitones) -/scene/layer//fixed_note(int note) -/scene/layer//low_note(int note) -/scene/layer//high_note(int note) -/scene/layer//in_channel(int channel) -/scene/layer//out_channel(int channel) -/scene/aux//status -/scene/aux//slot/{add: @module} -/scene/load_aux(string name) -/scene/delete_aux(string name) -/scene/status() -> - /name(string), - /title(string), - /transpose(int semitones), - [/layer(string uuid)], - [/instrument(string instance, string engine)], - [/aux(string name, string uuid)] - -/rt/ -/rt/status() -> /audio_channels(int inputs, int outputs) -/song/ -/song/status() -> [/track(int index, string name, int items)], [/pattern(int index, string name, int length)] -/waves/ -/waves/status() -> /bytes(int bytes), /max_bytes(int max_bytes), /count(int count) -/waves/list() -> [/waveform(int id)] -/waves/info(int id) -> /filename(string), /name(string), /bytes(int) -/on_idle() -> {any} -/send_event_to(string output, int) -/send_event_to(string output, int, int) -/send_event_to(string output, int, int, int) -/play_note(int ch, int note, int velocity) (plays a note with duration=1 on the next buffer) -/play_drum_pattern(string pattern) -/play_drum_track(string track) -/play_blob(blob serialized_pattern, int length_ticks) -/stop_pattern() -/get_pattern() -> /pattern(blob serialized_pattern, int length_ticks) -/print_s(string) -/print_i(int) -/print_f(float) -/new_meter() -> /uuid -/new_recorder(string filename) -> /uuid - diff --git a/template/calfbox/AUTHORS b/template/calfbox/AUTHORS deleted file mode 100644 index 7c52993..0000000 --- a/template/calfbox/AUTHORS +++ /dev/null @@ -1 +0,0 @@ -Krzysztof Foltman diff --git a/template/calfbox/COPYING b/template/calfbox/COPYING deleted file mode 100644 index 94a9ed0..0000000 --- a/template/calfbox/COPYING +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/template/calfbox/ChangeLog b/template/calfbox/ChangeLog deleted file mode 100644 index e69de29..0000000 diff --git a/template/calfbox/INSTALL b/template/calfbox/INSTALL deleted file mode 100644 index 7d1c323..0000000 --- a/template/calfbox/INSTALL +++ /dev/null @@ -1,365 +0,0 @@ -Installation Instructions -************************* - -Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, -2006, 2007, 2008, 2009 Free Software Foundation, Inc. - - Copying and distribution of this file, with or without modification, -are permitted in any medium without royalty provided the copyright -notice and this notice are preserved. This file is offered as-is, -without warranty of any kind. - -Basic Installation -================== - - Briefly, the shell commands `./configure; make; make install' should -configure, build, and install this package. The following -more-detailed instructions are generic; see the `README' file for -instructions specific to this package. Some packages provide this -`INSTALL' file but do not implement all of the features documented -below. The lack of an optional feature in a given package is not -necessarily a bug. More recommendations for GNU packages can be found -in *note Makefile Conventions: (standards)Makefile Conventions. - - The `configure' shell script attempts to guess correct values for -various system-dependent variables used during compilation. It uses -those values to create a `Makefile' in each directory of the package. -It may also create one or more `.h' files containing system-dependent -definitions. Finally, it creates a shell script `config.status' that -you can run in the future to recreate the current configuration, and a -file `config.log' containing compiler output (useful mainly for -debugging `configure'). - - It can also use an optional file (typically called `config.cache' -and enabled with `--cache-file=config.cache' or simply `-C') that saves -the results of its tests to speed up reconfiguring. Caching is -disabled by default to prevent problems with accidental use of stale -cache files. - - If you need to do unusual things to compile the package, please try -to figure out how `configure' could check whether to do them, and mail -diffs or instructions to the address given in the `README' so they can -be considered for the next release. If you are using the cache, and at -some point `config.cache' contains results you don't want to keep, you -may remove or edit it. - - The file `configure.ac' (or `configure.in') is used to create -`configure' by a program called `autoconf'. You need `configure.ac' if -you want to change it or regenerate `configure' using a newer version -of `autoconf'. - - The simplest way to compile this package is: - - 1. `cd' to the directory containing the package's source code and type - `./configure' to configure the package for your system. - - Running `configure' might take a while. While running, it prints - some messages telling which features it is checking for. - - 2. Type `make' to compile the package. - - 3. Optionally, type `make check' to run any self-tests that come with - the package, generally using the just-built uninstalled binaries. - - 4. Type `make install' to install the programs and any data files and - documentation. When installing into a prefix owned by root, it is - recommended that the package be configured and built as a regular - user, and only the `make install' phase executed with root - privileges. - - 5. Optionally, type `make installcheck' to repeat any self-tests, but - this time using the binaries in their final installed location. - This target does not install anything. Running this target as a - regular user, particularly if the prior `make install' required - root privileges, verifies that the installation completed - correctly. - - 6. You can remove the program binaries and object files from the - source code directory by typing `make clean'. To also remove the - files that `configure' created (so you can compile the package for - a different kind of computer), type `make distclean'. There is - also a `make maintainer-clean' target, but that is intended mainly - for the package's developers. If you use it, you may have to get - all sorts of other programs in order to regenerate files that came - with the distribution. - - 7. Often, you can also type `make uninstall' to remove the installed - files again. In practice, not all packages have tested that - uninstallation works correctly, even though it is required by the - GNU Coding Standards. - - 8. Some packages, particularly those that use Automake, provide `make - distcheck', which can by used by developers to test that all other - targets like `make install' and `make uninstall' work correctly. - This target is generally not run by end users. - -Compilers and Options -===================== - - Some systems require unusual options for compilation or linking that -the `configure' script does not know about. Run `./configure --help' -for details on some of the pertinent environment variables. - - You can give `configure' initial values for configuration parameters -by setting variables in the command line or in the environment. Here -is an example: - - ./configure CC=c99 CFLAGS=-g LIBS=-lposix - - *Note Defining Variables::, for more details. - -Compiling For Multiple Architectures -==================================== - - You can compile the package for more than one kind of computer at the -same time, by placing the object files for each architecture in their -own directory. To do this, you can use GNU `make'. `cd' to the -directory where you want the object files and executables to go and run -the `configure' script. `configure' automatically checks for the -source code in the directory that `configure' is in and in `..'. This -is known as a "VPATH" build. - - With a non-GNU `make', it is safer to compile the package for one -architecture at a time in the source code directory. After you have -installed the package for one architecture, use `make distclean' before -reconfiguring for another architecture. - - On MacOS X 10.5 and later systems, you can create libraries and -executables that work on multiple system types--known as "fat" or -"universal" binaries--by specifying multiple `-arch' options to the -compiler but only a single `-arch' option to the preprocessor. Like -this: - - ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ - CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ - CPP="gcc -E" CXXCPP="g++ -E" - - This is not guaranteed to produce working output in all cases, you -may have to build one architecture at a time and combine the results -using the `lipo' tool if you have problems. - -Installation Names -================== - - By default, `make install' installs the package's commands under -`/usr/local/bin', include files under `/usr/local/include', etc. You -can specify an installation prefix other than `/usr/local' by giving -`configure' the option `--prefix=PREFIX', where PREFIX must be an -absolute file name. - - You can specify separate installation prefixes for -architecture-specific files and architecture-independent files. If you -pass the option `--exec-prefix=PREFIX' to `configure', the package uses -PREFIX as the prefix for installing programs and libraries. -Documentation and other data files still use the regular prefix. - - In addition, if you use an unusual directory layout you can give -options like `--bindir=DIR' to specify different values for particular -kinds of files. Run `configure --help' for a list of the directories -you can set and what kinds of files go in them. In general, the -default for these options is expressed in terms of `${prefix}', so that -specifying just `--prefix' will affect all of the other directory -specifications that were not explicitly provided. - - The most portable way to affect installation locations is to pass the -correct locations to `configure'; however, many packages provide one or -both of the following shortcuts of passing variable assignments to the -`make install' command line to change installation locations without -having to reconfigure or recompile. - - The first method involves providing an override variable for each -affected directory. For example, `make install -prefix=/alternate/directory' will choose an alternate location for all -directory configuration variables that were expressed in terms of -`${prefix}'. Any directories that were specified during `configure', -but not in terms of `${prefix}', must each be overridden at install -time for the entire installation to be relocated. The approach of -makefile variable overrides for each directory variable is required by -the GNU Coding Standards, and ideally causes no recompilation. -However, some platforms have known limitations with the semantics of -shared libraries that end up requiring recompilation when using this -method, particularly noticeable in packages that use GNU Libtool. - - The second method involves providing the `DESTDIR' variable. For -example, `make install DESTDIR=/alternate/directory' will prepend -`/alternate/directory' before all installation names. The approach of -`DESTDIR' overrides is not required by the GNU Coding Standards, and -does not work on platforms that have drive letters. On the other hand, -it does better at avoiding recompilation issues, and works well even -when some directory options were not specified in terms of `${prefix}' -at `configure' time. - -Optional Features -================= - - If the package supports it, you can cause programs to be installed -with an extra prefix or suffix on their names by giving `configure' the -option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. - - Some packages pay attention to `--enable-FEATURE' options to -`configure', where FEATURE indicates an optional part of the package. -They may also pay attention to `--with-PACKAGE' options, where PACKAGE -is something like `gnu-as' or `x' (for the X Window System). The -`README' should mention any `--enable-' and `--with-' options that the -package recognizes. - - For packages that use the X Window System, `configure' can usually -find the X include and library files automatically, but if it doesn't, -you can use the `configure' options `--x-includes=DIR' and -`--x-libraries=DIR' to specify their locations. - - Some packages offer the ability to configure how verbose the -execution of `make' will be. For these packages, running `./configure ---enable-silent-rules' sets the default to minimal output, which can be -overridden with `make V=1'; while running `./configure ---disable-silent-rules' sets the default to verbose, which can be -overridden with `make V=0'. - -Particular systems -================== - - On HP-UX, the default C compiler is not ANSI C compatible. If GNU -CC is not installed, it is recommended to use the following options in -order to use an ANSI C compiler: - - ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" - -and if that doesn't work, install pre-built binaries of GCC for HP-UX. - - On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot -parse its `' header file. The option `-nodtk' can be used as -a workaround. If GNU CC is not installed, it is therefore recommended -to try - - ./configure CC="cc" - -and if that doesn't work, try - - ./configure CC="cc -nodtk" - - On Solaris, don't put `/usr/ucb' early in your `PATH'. This -directory contains several dysfunctional programs; working variants of -these programs are available in `/usr/bin'. So, if you need `/usr/ucb' -in your `PATH', put it _after_ `/usr/bin'. - - On Haiku, software installed for all users goes in `/boot/common', -not `/usr/local'. It is recommended to use the following options: - - ./configure --prefix=/boot/common - -Specifying the System Type -========================== - - There may be some features `configure' cannot figure out -automatically, but needs to determine by the type of machine the package -will run on. Usually, assuming the package is built to be run on the -_same_ architectures, `configure' can figure that out, but if it prints -a message saying it cannot guess the machine type, give it the -`--build=TYPE' option. TYPE can either be a short name for the system -type, such as `sun4', or a canonical name which has the form: - - CPU-COMPANY-SYSTEM - -where SYSTEM can have one of these forms: - - OS - KERNEL-OS - - See the file `config.sub' for the possible values of each field. If -`config.sub' isn't included in this package, then this package doesn't -need to know the machine type. - - If you are _building_ compiler tools for cross-compiling, you should -use the option `--target=TYPE' to select the type of system they will -produce code for. - - If you want to _use_ a cross compiler, that generates code for a -platform different from the build platform, you should specify the -"host" platform (i.e., that on which the generated programs will -eventually be run) with `--host=TYPE'. - -Sharing Defaults -================ - - If you want to set default values for `configure' scripts to share, -you can create a site shell script called `config.site' that gives -default values for variables like `CC', `cache_file', and `prefix'. -`configure' looks for `PREFIX/share/config.site' if it exists, then -`PREFIX/etc/config.site' if it exists. Or, you can set the -`CONFIG_SITE' environment variable to the location of the site script. -A warning: not all `configure' scripts look for a site script. - -Defining Variables -================== - - Variables not defined in a site shell script can be set in the -environment passed to `configure'. However, some packages may run -configure again during the build, and the customized values of these -variables may be lost. In order to avoid this problem, you should set -them in the `configure' command line, using `VAR=value'. For example: - - ./configure CC=/usr/local2/bin/gcc - -causes the specified `gcc' to be used as the C compiler (unless it is -overridden in the site shell script). - -Unfortunately, this technique does not work for `CONFIG_SHELL' due to -an Autoconf bug. Until the bug is fixed you can use this workaround: - - CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash - -`configure' Invocation -====================== - - `configure' recognizes the following options to control how it -operates. - -`--help' -`-h' - Print a summary of all of the options to `configure', and exit. - -`--help=short' -`--help=recursive' - Print a summary of the options unique to this package's - `configure', and exit. The `short' variant lists options used - only in the top level, while the `recursive' variant lists options - also present in any nested packages. - -`--version' -`-V' - Print the version of Autoconf used to generate the `configure' - script, and exit. - -`--cache-file=FILE' - Enable the cache: use and save the results of the tests in FILE, - traditionally `config.cache'. FILE defaults to `/dev/null' to - disable caching. - -`--config-cache' -`-C' - Alias for `--cache-file=config.cache'. - -`--quiet' -`--silent' -`-q' - Do not print messages saying which checks are being made. To - suppress all normal output, redirect it to `/dev/null' (any error - messages will still be shown). - -`--srcdir=DIR' - Look for the package's source code in directory DIR. Usually - `configure' can determine that directory automatically. - -`--prefix=DIR' - Use DIR as the installation prefix. *note Installation Names:: - for more details, including other options available for fine-tuning - the installation locations. - -`--no-create' -`-n' - Run the configure checks, but stop before creating any output - files. - -`configure' also accepts some other, not widely useful, options. Run -`configure --help' for more details. - diff --git a/template/calfbox/Makefile.am b/template/calfbox/Makefile.am deleted file mode 100644 index 32dcce2..0000000 --- a/template/calfbox/Makefile.am +++ /dev/null @@ -1,152 +0,0 @@ -AM_CPPFLAGS = -I$(srcdir) -Wall -Wsign-compare -D_GNU_SOURCE - -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) - -lib_LTLIBRARIES = libcalfbox.la - -bin_PROGRAMS = calfbox -noinst_PROGRAMS = calfbox_tests - -calfbox_SOURCES = \ - appmenu.c \ - main.c \ - menu.c \ - menuitem.c \ - ui.c - -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 - -calfbox_tests_SOURCES = \ - tests.c - -calfbox_tests_LDADD = libcalfbox.la $(GLIB_DEPS_LIBS) -lpthread -lm -lrt - -libcalfbox_la_SOURCES = \ - app.c \ - auxbus.c \ - blob.c \ - chorus.c \ - cmd.c \ - compressor.c \ - config-api.c \ - delay.c \ - distortion.c \ - dom.c \ - engine.c \ - eq.c \ - errors.c \ - fbr.c \ - fifo.c \ - fluid.c \ - fuzz.c \ - fxchain.c \ - gate.c \ - hwcfg.c \ - instr.c \ - io.c \ - jackinput.c \ - jackio.c \ - layer.c \ - limiter.c \ - master.c \ - meter.c \ - midi.c \ - mididest.c \ - module.c \ - pattern.c \ - pattern-maker.c \ - phaser.c \ - prefetch_pipe.c \ - recsrc.c \ - reverb.c \ - rt.c \ - sampler.c \ - sampler_channel.c \ - sampler_gen.c \ - sampler_layer.c \ - sampler_nif.c \ - sampler_prevoice.c \ - sampler_prg.c \ - sampler_rll.c \ - sampler_voice.c \ - scene.c \ - scripting.c \ - seq.c \ - seq-adhoc.c \ - sfzloader.c \ - sfzparser.c \ - song.c \ - streamplay.c \ - streamrec.c \ - tarfile.c \ - tonectl.c \ - tonewheel.c \ - track.c \ - usbaudio.c \ - usbio.c \ - usbmidi.c \ - usbprobe.c \ - wavebank.c - -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 - -if USE_SSE -ARCH_OPT_CFLAGS=-msse -ffast-math -else -if USE_NEON -ARCH_OPT_CFLAGS=-mfloat-abi=hard -mfpu=neon -ffast-math -endif -endif - -noinst_HEADERS = \ - app.h \ - auxbus.h \ - biquad-float.h \ - blob.h \ - cmd.h \ - config-api.h \ - dom.h \ - dspmath.h \ - envelope.h \ - engine.h \ - eq.h \ - errors.h \ - fifo.h \ - hwcfg.h \ - instr.h \ - io.h \ - ioenv.h \ - layer.h \ - master.h \ - menu.h \ - menuitem.h \ - meter.h \ - midi.h \ - mididest.h \ - module.h \ - onepole-int.h \ - onepole-float.h \ - pattern.h \ - pattern-maker.h \ - prefetch_pipe.h \ - recsrc.h \ - rt.h \ - sampler.h \ - sampler_impl.h \ - sampler_layer.h \ - sampler_prg.h \ - scene.h \ - scripting.h \ - seq.h \ - sfzloader.h \ - sfzparser.h \ - song.h \ - stm.h \ - tarfile.h \ - tests.h \ - track.h \ - ui.h \ - usbio_impl.h \ - wavebank.h - -EXTRA_DIST = cboxrc-example diff --git a/template/calfbox/NEWS b/template/calfbox/NEWS deleted file mode 100644 index e69de29..0000000 diff --git a/template/calfbox/README b/template/calfbox/README deleted file mode 120000 index 42061c0..0000000 --- a/template/calfbox/README +++ /dev/null @@ -1 +0,0 @@ -README.md \ No newline at end of file diff --git a/template/calfbox/README.md b/template/calfbox/README.md deleted file mode 100644 index de03056..0000000 --- a/template/calfbox/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# Calfbox - -Website: https://github.com/kfoltman/calfbox - -Calfbox, the "open source musical instrument", offers assorted music-related code. - -Originally intended as a standalone instrument for Linux and embedded devices (USB TV Sticks) -it can be used as Python module as well. - -# Packaging -If you are a packager and want to create a binary package for your distribution please package only the python module. -The binary executable is not maintained and untested at the moment. It should not be used by anyone. - - -# Calfbox as Python Module -Calfbox can be used as a Python module that can be imported to create short scripts or -full fledged programs ( https://www.laborejo.org/software ). - -Most notably it features a midi sequencer and an audio sampler (for sfz files and sf2 via fluidsynth). - -## Building - -A convenience script `cleanpythonbuild.py` has been supplied to quickly build and install the cbox python module. - -``` -make clean -rm build -rf -sh autogen.sh -./configure -make -python3 setup.py build -sudo python3 setup.py install -``` - -## How to write programs with cbox -You can find several `.py` files in the main directory, such as `sampler_api_example.py` or -`song_api_example.py`. - -Also there is a directory `/experiments` which contains a small example framework. - - -# Using Calfbox as standalone instrument - -Using Calfbox as standalone instrument requires a .cfg config file. - -This part of the program is currently unmaintained and untested. Please do not use it. - -# License - -This code is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -For the full license see the file COPYING diff --git a/template/calfbox/py/__init__.py b/template/calfbox/__init__.py similarity index 100% rename from template/calfbox/py/__init__.py rename to template/calfbox/__init__.py diff --git a/template/calfbox/py/_cbox2.py b/template/calfbox/_cbox2.py similarity index 88% rename from template/calfbox/py/_cbox2.py rename to template/calfbox/_cbox2.py index 94cd182..7128be9 100644 --- a/template/calfbox/py/_cbox2.py +++ b/template/calfbox/_cbox2.py @@ -96,13 +96,23 @@ class PyCmdTarget(CmdTarget): def find_calfbox(): if "CALFBOXLIBABSPATH" in os.environ: - assert os.path.exists(os.environ["CALFBOXLIBABSPATH"]) + logging.info("Searching for calfbox shared library with absolute path: %s", (os.environ["CALFBOXLIBABSPATH"])) + assert os.path.exists(os.environ["CALFBOXLIBABSPATH"]), os.environ["CALFBOXLIBABSPATH"] cblib = os.environ["CALFBOXLIBABSPATH"] + elif "CALFBOXLIBFILENAME" in os.environ: + #This should be just name of the lib, not a filename and no "lib"-prefix. For example "calfbox" if the real name is libcalfbox.so.1 + logging.info("Searching for calfbox shared library with just the filename: %s", (os.environ["CALFBOXLIBFILENAME"])) + cblib = find_library(os.environ["CALFBOXLIBFILENAME"]) else: cblib = find_library('calfbox') - logging.info("Loading calfbox shared library: %s" % (cblib)) - cb = cdll.LoadLibrary(cblib) - return cb + + if cblib: + logging.info("Loading calfbox shared library: %s" % (cblib)) + cb = cdll.LoadLibrary(cblib) + return cb + else: + raise FileNotFoundError("Calfbox shared library not found: %s" % (cblib)) + cb = find_calfbox() cb.cbox_embed_get_cmd_root.restype = CmdTargetPtr @@ -120,7 +130,7 @@ class WrapCmdTarget(PyCmdTarget): self.fb = fb def process(self, cmd, args): self.fb(cmd, None, args) - + def init_engine(config=None): gptr = GErrorPtr() if not cb.cbox_embed_init_engine(config, byref(gptr)): @@ -182,6 +192,6 @@ def do_cmd_on(target, cmd, fb, args): raise Exception(gptr.contents.message.decode()) else: raise Exception("Unknown error") - + def do_cmd(cmd, fb, args): do_cmd_on(cb.cbox_embed_get_cmd_root(), cmd, fb, args) diff --git a/template/calfbox/adhoc_example.py b/template/calfbox/adhoc_example.py deleted file mode 100644 index 04a96ff..0000000 --- a/template/calfbox/adhoc_example.py +++ /dev/null @@ -1,47 +0,0 @@ -import os -import sys -import struct -import time -import unittest - -sys.path = ["./py"] + sys.path - -import cbox - -global Document -global Transport -Document = cbox.Document -Transport = cbox.Transport - -song = Document.get_song() - -# Delete all the tracks and patterns -song.clear() - -# Create a binary blob that contains the MIDI events -pblob = bytes() -for noteindex in range(20): - # note on - pblob += cbox.Pattern.serialize_event(noteindex * 12, 0x90, 36+noteindex*3, 127) - # note off - pblob += cbox.Pattern.serialize_event(noteindex * 12 + 11, 0x90, 36+noteindex*3, 0) - -# This will be the length of the pattern (in pulses). It should be large enough -# to fit all the events -pattern_len = 10 * 24 * 2 - -# Create a new pattern object using events from the blob -pattern = song.pattern_from_blob(pblob, pattern_len) - -retrig = 10 -i = 0 -while i < 50: - i += 1 - retrig -= 1 - if retrig <= 0: - print ("Triggering adhoc pattern with ID 1") - Document.get_scene().play_pattern(pattern, 240, 0) - retrig = 5 - # Query JACK ports, new USB devices etc. - cbox.call_on_idle() - time.sleep(0.1) diff --git a/template/calfbox/app.c b/template/calfbox/app.c deleted file mode 100644 index cf778a0..0000000 --- a/template/calfbox/app.c +++ /dev/null @@ -1,391 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "app.h" -#include "blob.h" -#include "config-api.h" -#include "engine.h" -#include "instr.h" -#include "io.h" -#include "layer.h" -#include "menu.h" -#include "menuitem.h" -#include "meter.h" -#include "midi.h" -#include "module.h" -#include "scene.h" -#include "seq.h" -#include "song.h" -#include "track.h" -#include "ui.h" -#include "wavebank.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static gboolean lookup_midi_merger(const char *output, struct cbox_midi_merger **pmerger, GError **error) -{ - if (*output) - { - struct cbox_uuid uuid; - if (!cbox_uuid_fromstring(&uuid, output, error)) - return FALSE; - - *pmerger = cbox_rt_get_midi_output(app.rt, &uuid); - if (!*pmerger) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown MIDI output UUID: '%s'", output); - return FALSE; - } - } - else - { - if (!app.engine->scene_count) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Scene not set"); - return FALSE; - } - *pmerger = &app.engine->scenes[0]->scene_input_merger; - } - return TRUE; -} - -static gboolean app_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - if (!cmd->command) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "NULL command"); - return FALSE; - } - if (cmd->command[0] != '/') - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid global command path '%s'", cmd->command); - return FALSE; - } - const char *obj = &cmd->command[1]; - const char *pos = strchr(obj, '/'); - if (pos) - { - if (!strncmp(obj, "master/", 7)) - return cbox_execute_sub(&app.engine->master->cmd_target, fb, cmd, pos, error); - else - if (!strncmp(obj, "config/", 7)) - return cbox_execute_sub(&app.config_cmd_target, fb, cmd, pos, error); - else - if (!strncmp(obj, "scene/", 6)) - return cbox_execute_sub(&app.engine->scenes[0]->cmd_target, fb, cmd, pos, error); - else - if (!strncmp(obj, "engine/", 7)) - return cbox_execute_sub(&app.engine->cmd_target, fb, cmd, pos, error); - else - if (!strncmp(obj, "rt/", 3)) - return cbox_execute_sub(&app.rt->cmd_target, fb, cmd, pos, error); - else - if (!strncmp(obj, "io/", 3)) - return cbox_execute_sub(&app.io.cmd_target, fb, cmd, pos, error); - else - if (!strncmp(obj, "song/", 5) && app.engine->master->song) - return cbox_execute_sub(&app.engine->master->song->cmd_target, fb, cmd, pos, error); - else - if (!strncmp(obj, "waves/", 6)) - return cbox_execute_sub(&cbox_waves_cmd_target, fb, cmd, pos, error); - else - if (!strncmp(obj, "doc/", 4)) - return cbox_execute_sub(cbox_document_get_cmd_target(app.document), fb, cmd, pos, error); - else - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown combination of target path and argument: '%s', '%s'", cmd->command, cmd->arg_types); - return FALSE; - } - } - else - if (!strcmp(obj, "on_idle") && !strcmp(cmd->arg_types, "")) - { - return cbox_app_on_idle(fb, error); - } - else - if (!strcmp(obj, "send_event_to") && (!strcmp(cmd->arg_types, "siii") || !strcmp(cmd->arg_types, "sii") || !strcmp(cmd->arg_types, "si"))) - { - const char *output = CBOX_ARG_S(cmd, 0); - struct cbox_midi_merger *merger = NULL; - if (!lookup_midi_merger(output, &merger, error)) - return FALSE; - int mcmd = CBOX_ARG_I(cmd, 1); - int arg1 = 0, arg2 = 0; - if (cmd->arg_types[2] == 'i') - { - arg1 = CBOX_ARG_I(cmd, 2); - if (cmd->arg_types[3] == 'i') - arg2 = CBOX_ARG_I(cmd, 3); - } - struct cbox_midi_buffer buf; - cbox_midi_buffer_init(&buf); - cbox_midi_buffer_write_inline(&buf, 0, mcmd, arg1, arg2); - cbox_engine_send_events_to(app.engine, merger, &buf); - return TRUE; - } - else - if (!strcmp(obj, "send_sysex_to") && !strcmp(cmd->arg_types, "sb")) - { - const char *output = CBOX_ARG_S(cmd, 0); - struct cbox_midi_merger *merger = NULL; - if (!lookup_midi_merger(output, &merger, error)) - return FALSE; - const struct cbox_blob *blob = CBOX_ARG_B(cmd, 1); - struct cbox_midi_buffer buf; - cbox_midi_buffer_init(&buf); - cbox_midi_buffer_write_event(&buf, 0, blob->data, blob->size); - cbox_engine_send_events_to(app.engine, merger, &buf); - return TRUE; - } - else - if (!strcmp(obj, "update_playback") && !strcmp(cmd->arg_types, "")) - { - cbox_engine_update_song_playback(app.engine); - return TRUE; - } - else - if (!strcmp(obj, "get_pattern") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - if (app.engine->master->song && app.engine->master->song->tracks) - { - struct cbox_track *track = app.engine->master->song->tracks->data; - if (track) - { - struct cbox_track_item *item = track->items->data; - struct cbox_midi_pattern *pattern = item->pattern; - int length = 0; - struct cbox_blob *blob = cbox_midi_pattern_to_blob(pattern, &length); - gboolean res = cbox_execute_on(fb, NULL, "/pattern", "bi", error, blob, length); - cbox_blob_destroy(blob); - if (!res) - return FALSE; - } - } - return TRUE; - } - else - if (!strcmp(obj, "new_meter") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - struct cbox_meter *meter = cbox_meter_new(app.document, app.rt->io_env.srate); - - return cbox_execute_on(fb, NULL, "/uuid", "o", error, meter); - } - else - if (!strcmp(obj, "new_engine") && !strcmp(cmd->arg_types, "ii")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - struct cbox_engine *e = cbox_engine_new(app.document, NULL); - e->io_env.srate = CBOX_ARG_I(cmd, 0); - e->io_env.buffer_size = CBOX_ARG_I(cmd, 1); - - return e ? cbox_execute_on(fb, NULL, "/uuid", "o", error, e) : FALSE; - } - else - if (!strcmp(obj, "print_s") && !strcmp(cmd->arg_types, "s")) - { - g_message("Print: %s", CBOX_ARG_S(cmd, 0)); - return TRUE; - } - else - if (!strcmp(obj, "print_i") && !strcmp(cmd->arg_types, "i")) - { - g_message("Print: %d", CBOX_ARG_I(cmd, 0)); - return TRUE; - } - else - if (!strcmp(obj, "print_f") && !strcmp(cmd->arg_types, "f")) - { - g_message("Print: %f", CBOX_ARG_F(cmd, 0)); - return TRUE; - } - else - if (!strcmp(obj, "print_b") && !strcmp(cmd->arg_types, "b")) - { - g_message("Print: %s", (char *)CBOX_ARG_B(cmd, 0)->data); - return TRUE; - } - else - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown combination of target path and argument: '%s', '%s'", cmd->command, cmd->arg_types); - return FALSE; - } -} - -struct config_foreach_data -{ - const char *prefix; - const char *command; - struct cbox_command_target *fb; - GError **error; - gboolean success; -}; - -void api_config_cb(void *user_data, const char *key) -{ - struct config_foreach_data *cfd = user_data; - if (!cfd->success) - return; - if (cfd->prefix && strncmp(cfd->prefix, key, strlen(cfd->prefix))) - return; - - if (!cbox_execute_on(cfd->fb, NULL, cfd->command, "s", cfd->error, key)) - { - cfd->success = FALSE; - return; - } -} - -static gboolean config_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - if (!strcmp(cmd->command, "/sections") && (!strcmp(cmd->arg_types, "") || !strcmp(cmd->arg_types, "s"))) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - struct config_foreach_data cfd = {cmd->arg_types[0] == 's' ? CBOX_ARG_S(cmd, 0) : NULL, "/section", fb, error, TRUE}; - cbox_config_foreach_section(api_config_cb, &cfd); - return cfd.success; - } - else if (!strcmp(cmd->command, "/keys") && (!strcmp(cmd->arg_types, "s") || !strcmp(cmd->arg_types, "ss"))) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - struct config_foreach_data cfd = {cmd->arg_types[1] == 's' ? CBOX_ARG_S(cmd, 1) : NULL, "/key", fb, error, TRUE}; - cbox_config_foreach_key(api_config_cb, CBOX_ARG_S(cmd, 0), &cfd); - return cfd.success; - } - else if (!strcmp(cmd->command, "/get") && !strcmp(cmd->arg_types, "ss")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - const char *value = cbox_config_get_string(CBOX_ARG_S(cmd, 0), CBOX_ARG_S(cmd, 1)); - if (!value) - return TRUE; - return cbox_execute_on(fb, NULL, "/value", "s", error, value); - } - else if (!strcmp(cmd->command, "/set") && !strcmp(cmd->arg_types, "sss")) - { - cbox_config_set_string(CBOX_ARG_S(cmd, 0), CBOX_ARG_S(cmd, 1), CBOX_ARG_S(cmd, 2)); - return TRUE; - } - else if (!strcmp(cmd->command, "/delete") && !strcmp(cmd->arg_types, "ss")) - { - cbox_config_remove_key(CBOX_ARG_S(cmd, 0), CBOX_ARG_S(cmd, 1)); - return TRUE; - } - else if (!strcmp(cmd->command, "/delete_section") && !strcmp(cmd->arg_types, "s")) - { - cbox_config_remove_section(CBOX_ARG_S(cmd, 0)); - return TRUE; - } - else if (!strcmp(cmd->command, "/save") && !strcmp(cmd->arg_types, "")) - { - return cbox_config_save(NULL, error); - } - else if (!strcmp(cmd->command, "/save") && !strcmp(cmd->arg_types, "s")) - { - return cbox_config_save(CBOX_ARG_S(cmd, 0), error); - } - else - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown combination of target path and argument: '%s', '%s'", cmd->command, cmd->arg_types); - return FALSE; - } -} - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -gboolean cbox_app_on_idle(struct cbox_command_target *fb, GError **error) -{ - if (app.rt->io) - { - GError *error2 = NULL; - if (cbox_io_get_disconnect_status(&app.io, &error2)) - cbox_io_poll_ports(&app.io, fb); - else - { - if (error2) - g_error_free(error2); - int auto_reconnect = cbox_config_get_int("io", "auto_reconnect", 0); - if (auto_reconnect > 0) - { - sleep(auto_reconnect); - GError *error2 = NULL; - if (!cbox_io_cycle(&app.io, fb, &error2)) - { - gboolean suppress = FALSE; - if (fb) - suppress = cbox_execute_on(fb, NULL, "/io/cycle_failed", "s", NULL, error2->message); - if (!suppress) - g_warning("Cannot cycle the I/O: %s", (error2 && error2->message) ? error2->message : "Unknown error"); - g_error_free(error2); - } - else - { - if (fb) - cbox_execute_on(fb, NULL, "/io/cycled", "", NULL); - } - } - } - } - if (app.rt) - { - // Process results of asynchronous commands - cbox_rt_handle_cmd_queue(app.rt); - - if (!cbox_midi_appsink_send_to(&app.engine->appsink, fb, error)) - return FALSE; - } - return TRUE; -} - -struct cbox_app app = -{ - .rt = NULL, - .current_scene_name = NULL, - .cmd_target = - { - .process_cmd = app_process_cmd, - .user_data = &app - }, - .config_cmd_target = - { - .process_cmd = config_process_cmd, - .user_data = &app - }, -}; - diff --git a/template/calfbox/app.h b/template/calfbox/app.h deleted file mode 100644 index df20506..0000000 --- a/template/calfbox/app.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_APP_H -#define CBOX_APP_H - -#include "cmd.h" -#include "dom.h" -#include "io.h" -#include "rt.h" -#include - -struct cbox_song; -struct cbox_tarpool; - -struct cbox_app -{ - struct cbox_io io; - struct cbox_document *document; - struct cbox_rt *rt; - struct cbox_engine *engine; - struct cbox_command_target cmd_target; - struct cbox_command_target config_cmd_target; - gchar *current_scene_name; - struct cbox_tarpool *tarpool; -}; - -struct cbox_menu; - -extern struct cbox_app app; - -struct cbox_menu *create_main_menu(void); - -extern gboolean cbox_app_on_idle(struct cbox_command_target *fb, GError **error); - -#endif diff --git a/template/calfbox/appmenu.c b/template/calfbox/appmenu.c deleted file mode 100644 index 7fb7cce..0000000 --- a/template/calfbox/appmenu.c +++ /dev/null @@ -1,466 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "app.h" -#include "blob.h" -#include "config-api.h" -#include "engine.h" -#include "instr.h" -#include "io.h" -#include "layer.h" -#include "menu.h" -#include "menuitem.h" -#include "meter.h" -#include "midi.h" -#include "module.h" -#include "scene.h" -#include "seq.h" -#include "song.h" -#include "track.h" -#include "ui.h" -#include "wavebank.h" - -#if USE_NCURSES - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int cmd_quit(struct cbox_menu_item_command *item, void *context) -{ - return 1; -} - -static void set_current_scene_name(gchar *name) -{ - if (app.current_scene_name) - g_free(app.current_scene_name); - app.current_scene_name = name; -} - -int cmd_load_scene(struct cbox_menu_item_command *item, void *context) -{ - GError *error = NULL; - struct cbox_scene *scene = app.engine->scenes[0]; - cbox_scene_clear(scene); - if (!cbox_scene_load(scene, item->item.item_context, &error)) - cbox_print_error(error); - set_current_scene_name(g_strdup_printf("scene:%s", (const char *)item->item.item_context)); - return 0; -} - -int cmd_load_instrument(struct cbox_menu_item_command *item, void *context) -{ - GError *error = NULL; - struct cbox_scene *scene = app.engine->scenes[0]; - cbox_scene_clear(scene); - struct cbox_layer *layer = cbox_layer_new_with_instrument(scene, (char *)item->item.item_context, &error); - - if (layer) - { - if (!cbox_scene_add_layer(scene, layer, &error)) - cbox_print_error(error); - set_current_scene_name(g_strdup_printf("instrument:%s", (const char *)item->item.item_context)); - } - else - { - cbox_print_error(error); - } - return 0; -} - -int cmd_load_layer(struct cbox_menu_item_command *item, void *context) -{ - GError *error = NULL; - struct cbox_scene *scene = app.engine->scenes[0]; - cbox_scene_clear(scene); - struct cbox_layer *layer = cbox_layer_new_from_config(scene, (char *)item->item.item_context, &error); - - if (layer) - { - if (!cbox_scene_add_layer(scene, layer, &error)) - cbox_print_error(error); - set_current_scene_name(g_strdup_printf("layer:%s", (const char *)item->item.item_context)); - } - else - { - cbox_print_error(error); - CBOX_DELETE(scene); - } - return 0; -} - -gchar *scene_format_value(const struct cbox_menu_item_static *item, void *context) -{ - if (app.current_scene_name) - return g_strdup(app.current_scene_name); - else - return g_strdup("- No scene -"); -} - -gchar *transport_format_value(const struct cbox_menu_item_static *item, void *context) -{ - // XXXKF - // struct cbox_bbt bbt; - // cbox_master_to_bbt(app.engine->master, &bbt); - if (app.engine->master->spb == NULL) - return g_strdup("N/A"); - if (!strcmp((const char *)item->item.item_context, "pos")) - return g_strdup_printf("%d", (int)app.engine->master->spb->song_pos_samples); - else - return g_strdup_printf("%d", (int)app.engine->master->spb->song_pos_ppqn); -} - -struct cbox_config_section_cb_data -{ - struct cbox_menu *menu; - cbox_menu_item_execute_func func; - const char *prefix; -}; - -static void config_key_process(void *user_data, const char *key) -{ - struct cbox_config_section_cb_data *data = user_data; - - if (!strncmp(key, data->prefix, strlen(data->prefix))) - { - char *title = cbox_config_get_string(key, "title"); - if (title) - cbox_menu_add_item(data->menu, cbox_menu_item_new_command(title, data->func, strdup(key + strlen(data->prefix)), mif_dup_label | mif_free_context)); - else - cbox_menu_add_item(data->menu, cbox_menu_item_new_command(key, data->func, strdup(key + strlen(data->prefix)), mif_dup_label | mif_free_context)); - } -} - -struct cbox_menu *create_scene_menu(struct cbox_menu_item_menu *item, void *menu_context) -{ - struct cbox_menu *scene_menu = cbox_menu_new(); - struct cbox_config_section_cb_data cb = { .menu = scene_menu }; - - cbox_menu_add_item(scene_menu, cbox_menu_item_new_static("Scenes", NULL, NULL, 0)); - cb.prefix = "scene:"; - cb.func = cmd_load_scene; - cbox_config_foreach_section(config_key_process, &cb); - cbox_menu_add_item(scene_menu, cbox_menu_item_new_static("Layers", NULL, NULL, 0)); - cb.prefix = "layer:"; - cb.func = cmd_load_layer; - cbox_config_foreach_section(config_key_process, &cb); - cbox_menu_add_item(scene_menu, cbox_menu_item_new_static("Instruments", NULL, NULL, 0)); - cb.prefix = "instrument:"; - cb.func = cmd_load_instrument; - cbox_config_foreach_section(config_key_process, &cb); - - cbox_menu_add_item(scene_menu, cbox_menu_item_new_ok()); - return scene_menu; -} - -/////////////////////////////////////////////////////////////////////////////// - -static struct cbox_command_target *find_module_target(const struct cbox_menu_item_command *item) -{ - struct cbox_instrument *instr = item->item.item_context; - return &instr->module->cmd_target; - -} - -int cmd_stream_rewind(struct cbox_menu_item_command *item, void *context) -{ - GError *error = NULL; - struct cbox_command_target *target = find_module_target(item); - if (target) - cbox_execute_on(target, NULL, "/seek", "i", &error, 0); - cbox_print_error_if(error); - return 0; -} - -int cmd_stream_play(struct cbox_menu_item_command *item, void *context) -{ - GError *error = NULL; - struct cbox_command_target *target = find_module_target(item); - if (target) - cbox_execute_on(target, NULL, "/play", "", &error); - cbox_print_error_if(error); - return 0; -} - -int cmd_stream_stop(struct cbox_menu_item_command *item, void *context) -{ - GError *error = NULL; - struct cbox_command_target *target = find_module_target(item); - if (target) - cbox_execute_on(target, NULL, "/stop", "", &error); - cbox_print_error_if(error); - return 0; -} - -int cmd_stream_unload(struct cbox_menu_item_command *item, void *context) -{ - GError *error = NULL; - struct cbox_command_target *target = find_module_target(item); - if (target) - cbox_execute_on(target, NULL, "/unload", "", &error); - cbox_print_error_if(error); - return 0; -} - -struct stream_response_data -{ - gchar *filename; - uint32_t pos, length, sample_rate, channels; -}; - -gboolean result_parser_status(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct stream_response_data *res = ct->user_data; - if (!strcmp(cmd->command, "/filename")) - res->filename = g_strdup(cmd->arg_values[0]); - if (!strcmp(cmd->command, "/pos")) - res->pos = *((uint32_t **)cmd->arg_values)[0]; - if (!strcmp(cmd->command, "/length")) - res->length = *((uint32_t **)cmd->arg_values)[0]; - if (!strcmp(cmd->command, "/sample_rate")) - res->sample_rate = *((uint32_t **)cmd->arg_values)[0]; - if (!strcmp(cmd->command, "/channels")) - res->channels = *((uint32_t **)cmd->arg_values)[0]; - //cbox_osc_command_dump(cmd); - return TRUE; -} - -char *cmd_stream_status(const struct cbox_menu_item_static *item, void *context) -{ - struct stream_response_data data = { NULL, 0, 0, 0, 0 }; - struct cbox_command_target response = { &data, result_parser_status }; - GError *error = NULL; - struct cbox_command_target *target = find_module_target((struct cbox_menu_item_command *)item); - if (target) - cbox_execute_on(target, &response, "/status", "", &error); - cbox_print_error_if(error); - gchar *res = NULL; - if (data.filename && data.length && data.sample_rate) - { - double duration = data.length * 1.0 / data.sample_rate; - res = g_strdup_printf("%s (%um%0.2fs, %uch, %uHz) (%0.2f%%)", data.filename, (unsigned)floor(duration / 60), duration - 60 * floor(duration / 60), (unsigned)data.channels, (unsigned)data.sample_rate, data.pos * 100.0 / data.length); - } - else - res = g_strdup("-"); - g_free(data.filename); - return res; -} - -struct load_waveform_context -{ - struct cbox_menu_item_context header; - struct cbox_instrument *instrument; - char *filename; -}; - -static void destroy_load_waveform_context(void *p) -{ - struct load_waveform_context *context = p; - g_free(context->filename); - free(context); -} - -int cmd_stream_load(struct cbox_menu_item_command *item, void *context) -{ - struct load_waveform_context *ctx = item->item.item_context; - GError *error = NULL; - struct cbox_command_target *target = &ctx->instrument->module->cmd_target; - if (target) - cbox_execute_on(target, NULL, "/load", "si", &error, ctx->filename, 0); - cbox_print_error_if(error); - return 0; -} - -struct cbox_menu *create_streamplay_menu(struct cbox_menu_item_menu *item, void *menu_context) -{ - struct cbox_menu *menu = cbox_menu_new(); - struct cbox_instrument *instr = item->item.item_context; - - assert(instr); - cbox_menu_add_item(menu, cbox_menu_item_new_static("Current stream", NULL, NULL, 0)); - cbox_menu_add_item(menu, cbox_menu_item_new_static("File", cmd_stream_status, instr, 0)); - cbox_menu_add_item(menu, cbox_menu_item_new_static("Module commands", NULL, NULL, 0)); - cbox_menu_add_item(menu, cbox_menu_item_new_command("Play stream", cmd_stream_play, instr, 0)); - cbox_menu_add_item(menu, cbox_menu_item_new_command("Stop stream", cmd_stream_stop, instr, 0)); - cbox_menu_add_item(menu, cbox_menu_item_new_command("Rewind stream", cmd_stream_rewind, instr, 0)); - cbox_menu_add_item(menu, cbox_menu_item_new_command("Unload stream", cmd_stream_unload, instr, 0)); - - cbox_menu_add_item(menu, cbox_menu_item_new_static("Files available", NULL, NULL, 0)); - glob_t g; - gboolean found = (glob("*.wav", GLOB_TILDE_CHECK, NULL, &g) == 0); - found = glob("*.ogg", GLOB_TILDE_CHECK | (found ? GLOB_APPEND : 0), NULL, &g) || found; - if (found) - { - for (size_t i = 0; i < g.gl_pathc; i++) - { - struct load_waveform_context *context = calloc(1, sizeof(struct load_waveform_context)); - context->header.destroy_func = destroy_load_waveform_context; - context->instrument = instr; - context->filename = g_strdup(g.gl_pathv[i]); - cbox_menu_add_item(menu, cbox_menu_item_new_command(g_strdup_printf("Load: %s", g.gl_pathv[i]), cmd_stream_load, context, mif_free_label | mif_context_is_struct)); - } - } - globfree(&g); - - cbox_menu_add_item(menu, cbox_menu_item_new_ok()); - return menu; -} - -/////////////////////////////////////////////////////////////////////////////// - -struct cbox_menu *create_module_menu(struct cbox_menu_item_menu *item, void *menu_context) -{ - struct cbox_menu *menu = cbox_menu_new(); - - cbox_menu_add_item(menu, cbox_menu_item_new_static("Scene instruments", NULL, NULL, 0)); - struct cbox_scene *scene = app.engine->scenes[0]; - for (uint32_t i = 0; i < scene->instrument_count; ++i) - { - struct cbox_instrument *instr = scene->instruments[i]; - create_menu_func menufunc = NULL; - - if (!strcmp(instr->module->engine_name, "stream_player")) - menufunc = create_streamplay_menu; - if (menufunc) - cbox_menu_add_item(menu, cbox_menu_item_new_dynamic_menu(g_strdup_printf("%s (%s)", instr->module->instance_name, instr->module->engine_name), menufunc, instr, mif_free_label)); - else - cbox_menu_add_item(menu, cbox_menu_item_new_static(g_strdup_printf("%s (%s)", instr->module->instance_name, instr->module->engine_name), NULL, NULL, mif_free_label)); - } - cbox_menu_add_item(menu, cbox_menu_item_new_ok()); - return menu; -} - -/////////////////////////////////////////////////////////////////////////////// - -static void restart_song() -{ - cbox_master_stop(app.engine->master); - cbox_master_seek_ppqn(app.engine->master, 0); - cbox_master_play(app.engine->master); -} - -int cmd_pattern_none(struct cbox_menu_item_command *item, void *context) -{ - cbox_song_clear(app.engine->master->song); - cbox_engine_update_song_playback(app.engine); - return 0; -} - -int cmd_pattern_simple(struct cbox_menu_item_command *item, void *context) -{ - cbox_song_use_looped_pattern(app.engine->master->song, cbox_midi_pattern_new_metronome(app.engine->master->song, 1, app.engine->master->ppqn_factor)); - restart_song(); - return 0; -} - -int cmd_pattern_normal(struct cbox_menu_item_command *item, void *context) -{ - cbox_song_use_looped_pattern(app.engine->master->song, cbox_midi_pattern_new_metronome(app.engine->master->song, app.engine->master->timesig_num, app.engine->master->ppqn_factor)); - restart_song(); - return 0; -} - -int cmd_load_drumpattern(struct cbox_menu_item_command *item, void *context) -{ - cbox_song_use_looped_pattern(app.engine->master->song, cbox_midi_pattern_load(app.engine->master->song, item->item.item_context, 1, app.engine->master->ppqn_factor)); - restart_song(); - return 0; -} - -int cmd_load_drumtrack(struct cbox_menu_item_command *item, void *context) -{ - cbox_song_use_looped_pattern(app.engine->master->song, cbox_midi_pattern_load_track(app.engine->master->song, item->item.item_context, 1, app.engine->master->ppqn_factor)); - restart_song(); - return 0; -} - -int cmd_load_pattern(struct cbox_menu_item_command *item, void *context) -{ - cbox_song_use_looped_pattern(app.engine->master->song, cbox_midi_pattern_load(app.engine->master->song, item->item.item_context, 0, app.engine->master->ppqn_factor)); - restart_song(); - return 0; -} - -int cmd_load_track(struct cbox_menu_item_command *item, void *context) -{ - cbox_song_use_looped_pattern(app.engine->master->song, cbox_midi_pattern_load_track(app.engine->master->song, item->item.item_context, 0, app.engine->master->ppqn_factor)); - restart_song(); - return 0; -} - -struct cbox_menu *create_pattern_menu(struct cbox_menu_item_menu *item, void *menu_context) -{ - struct cbox_menu *menu = cbox_menu_new(); - struct cbox_config_section_cb_data cb = { .menu = menu }; - - cbox_menu_add_item(menu, cbox_menu_item_new_static("Pattern commands", NULL, NULL, 0)); - cbox_menu_add_item(menu, cbox_menu_item_new_command("No pattern", cmd_pattern_none, NULL, 0)); - cbox_menu_add_item(menu, cbox_menu_item_new_command("Simple metronome", cmd_pattern_simple, NULL, 0)); - cbox_menu_add_item(menu, cbox_menu_item_new_command("Normal metronome", cmd_pattern_normal, NULL, 0)); - - cbox_menu_add_item(menu, cbox_menu_item_new_static("Drum tracks", NULL, NULL, 0)); - cb.prefix = "drumtrack:"; - cb.func = cmd_load_drumtrack; - cbox_config_foreach_section(config_key_process, &cb); - - cbox_menu_add_item(menu, cbox_menu_item_new_static("Melodic tracks", NULL, NULL, 0)); - cb.prefix = "track:"; - cb.func = cmd_load_track; - cbox_config_foreach_section(config_key_process, &cb); - - cbox_menu_add_item(menu, cbox_menu_item_new_static("Drum patterns", NULL, NULL, 0)); - cb.prefix = "drumpattern:"; - cb.func = cmd_load_drumpattern; - cbox_config_foreach_section(config_key_process, &cb); - - cbox_menu_add_item(menu, cbox_menu_item_new_static("Melodic patterns", NULL, NULL, 0)); - cb.prefix = "pattern:"; - cb.func = cmd_load_pattern; - cbox_config_foreach_section(config_key_process, &cb); - - cbox_menu_add_item(menu, cbox_menu_item_new_ok()); - return menu; -} - -struct cbox_menu *create_main_menu() -{ - struct cbox_menu *main_menu = cbox_menu_new(); - cbox_menu_add_item(main_menu, cbox_menu_item_new_static("Current scene:", scene_format_value, NULL, 0)); - cbox_menu_add_item(main_menu, cbox_menu_item_new_dynamic_menu("Set scene", create_scene_menu, NULL, 0)); - cbox_menu_add_item(main_menu, cbox_menu_item_new_dynamic_menu("Module control", create_module_menu, NULL, 0)); - cbox_menu_add_item(main_menu, cbox_menu_item_new_dynamic_menu("Pattern control", create_pattern_menu, NULL, 0)); - - cbox_menu_add_item(main_menu, cbox_menu_item_new_static("Variables", NULL, NULL, 0)); - // cbox_menu_add_item(main_menu, cbox_menu_item_new_int("foo:", &var1, 0, 127, NULL)); - // cbox_menu_add_item(main_menu, "bar:", menu_item_value_double, &mx_double_var2, &var2); - //cbox_menu_add_item(main_menu, cbox_menu_item_new_static("pos:", transport_format_value, "pos", 0)); - //cbox_menu_add_item(main_menu, cbox_menu_item_new_static("bbt:", transport_format_value, "bbt", 0)); - cbox_menu_add_item(main_menu, cbox_menu_item_new_static("Commands", NULL, NULL, 0)); - cbox_menu_add_item(main_menu, cbox_menu_item_new_command("Quit", cmd_quit, NULL, 0)); - return main_menu; -} - -#endif diff --git a/template/calfbox/autogen.sh b/template/calfbox/autogen.sh deleted file mode 100755 index 13a0322..0000000 --- a/template/calfbox/autogen.sh +++ /dev/null @@ -1,6 +0,0 @@ -aclocal --force || exit 1 -libtoolize --force --automake --copy || exit 1 -autoheader --force || exit 1 -autoconf --force || exit 1 -automake --add-missing --copy || exit 1 - diff --git a/template/calfbox/auxbus.c b/template/calfbox/auxbus.c deleted file mode 100644 index da43be1..0000000 --- a/template/calfbox/auxbus.c +++ /dev/null @@ -1,102 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "auxbus.h" -#include "scene.h" -#include -#include - -extern gboolean cbox_scene_insert_aux_bus(struct cbox_scene *scene, struct cbox_aux_bus *aux_bus); -extern void cbox_scene_remove_aux_bus(struct cbox_scene *scene, struct cbox_aux_bus *bus); - -CBOX_CLASS_DEFINITION_ROOT(cbox_aux_bus) - -static gboolean cbox_aux_bus_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct cbox_aux_bus *aux_bus = ct->user_data; - struct cbox_rt *rt = (struct cbox_rt *)cbox_document_get_service(CBOX_GET_DOCUMENT(aux_bus), "rt"); - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - return cbox_execute_on(fb, NULL, "/name", "s", error, aux_bus->name) && - CBOX_OBJECT_DEFAULT_STATUS(aux_bus, fb, error); - } - else - if (!strncmp(cmd->command, "/slot/", 6)) - { - 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); - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); -} - -struct cbox_aux_bus *cbox_aux_bus_load(struct cbox_scene *scene, const char *name, struct cbox_rt *rt, GError **error) -{ - struct cbox_module *module = cbox_module_new_from_fx_preset(name, CBOX_GET_DOCUMENT(scene), rt, scene->engine, error); - if (!module) - return NULL; - - struct cbox_aux_bus *p = malloc(sizeof(struct cbox_aux_bus)); - CBOX_OBJECT_HEADER_INIT(p, cbox_aux_bus, CBOX_GET_DOCUMENT(scene)); - cbox_command_target_init(&p->cmd_target, cbox_aux_bus_process_cmd, p); - p->name = g_strdup(name); - p->owner = scene; - p->module = module; - p->refcount = 0; - // XXXKF this work up to buffer size of 8192 floats, this should be determined from JACK settings and updated when - // JACK buffer size changes - p->input_bufs[0] = malloc(8192 * sizeof(float)); - p->input_bufs[1] = malloc(8192 * sizeof(float)); - p->output_bufs[0] = malloc(8192 * sizeof(float)); - p->output_bufs[1] = malloc(8192 * sizeof(float)); - CBOX_OBJECT_REGISTER(p); - cbox_scene_insert_aux_bus(scene, p); - - return p; -} - -void cbox_aux_bus_ref(struct cbox_aux_bus *bus) -{ - ++bus->refcount; -} - -void cbox_aux_bus_unref(struct cbox_aux_bus *bus) -{ - assert(bus->refcount > 0); - --bus->refcount; -} - -void cbox_aux_bus_destroyfunc(struct cbox_objhdr *objhdr) -{ - struct cbox_aux_bus *bus = CBOX_H2O(objhdr); - if (bus->owner) - { - cbox_scene_remove_aux_bus(bus->owner, bus); - bus->owner = NULL; - } - CBOX_DELETE(bus->module); - bus->module = NULL; - assert(!bus->refcount); - g_free(bus->name); - free(bus->input_bufs[0]); - free(bus->input_bufs[1]); - free(bus); -} - diff --git a/template/calfbox/auxbus.h b/template/calfbox/auxbus.h deleted file mode 100644 index 63159b5..0000000 --- a/template/calfbox/auxbus.h +++ /dev/null @@ -1,47 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_AUXBUS_H -#define CBOX_AUXBUS_H - -#include "dom.h" -#include "module.h" - -struct cbox_scene; - -CBOX_EXTERN_CLASS(cbox_aux_bus) - -struct cbox_aux_bus -{ - CBOX_OBJECT_HEADER() - struct cbox_command_target cmd_target; - struct cbox_scene *owner; - - gchar *name; - struct cbox_module *module; - int refcount; - - float *input_bufs[2]; - float *output_bufs[2]; -}; - -extern struct cbox_aux_bus *cbox_aux_bus_load(struct cbox_scene *scene, const char *name, struct cbox_rt *rt, GError **error); -extern void cbox_aux_bus_ref(struct cbox_aux_bus *bus); -extern void cbox_aux_bus_unref(struct cbox_aux_bus *bus); - -#endif diff --git a/template/calfbox/background_example.py b/template/calfbox/background_example.py deleted file mode 100644 index 7addb7c..0000000 --- a/template/calfbox/background_example.py +++ /dev/null @@ -1,6 +0,0 @@ -import _cbox -import time -while True: - _cbox.do_cmd("/on_idle", None, []) - time.sleep(0.1) - diff --git a/template/calfbox/biquad-float.h b/template/calfbox/biquad-float.h deleted file mode 100644 index f2789ab..0000000 --- a/template/calfbox/biquad-float.h +++ /dev/null @@ -1,398 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_BIQUAD_FLOAT_H -#define CBOX_BIQUAD_FLOAT_H - -#include "config.h" -#include "dspmath.h" - -struct cbox_biquadf_state -{ - float x1; - float y1; - float x2; - float y2; -}; - -struct cbox_biquadf_coeffs -{ - float a0; - float a1; - float a2; - float b1; - float b2; -}; - -static inline void cbox_biquadf_reset(struct cbox_biquadf_state *state) -{ - state->x1 = state->y1 = state->x2 = state->y2 = 0.f; -} - -static inline float cbox_biquadf_is_audible(struct cbox_biquadf_state *state, float level) -{ - return fabs(state->x1) + fabs(state->x2) + fabs(state->y1) + fabs(state->y2) >= level; -} - -// Based on filter coefficient equations by Robert Bristow-Johnson -static inline void cbox_biquadf_set_lp_rbj(struct cbox_biquadf_coeffs *coeffs, float fc, float q, float sr) -{ - float omega=(float)(2*M_PI*fc/sr); - float sn=sin(omega); - float cs=cos(omega); - float alpha=(float)(sn/(2*q)); - float inv=(float)(1.0/(1.0+alpha)); - - coeffs->a2 = coeffs->a0 = (float)(inv*(1 - cs)*0.5f); - coeffs->a1 = coeffs->a0 + coeffs->a0; - coeffs->b1 = (float)(-2*cs*inv); - coeffs->b2 = (float)((1 - alpha)*inv); -} - -static inline void cbox_biquadf_set_lp_rbj_lookup(struct cbox_biquadf_coeffs *coeffs, const struct cbox_sincos *sincos, float q) -{ - float sn=sincos->sine; - float cs=sincos->cosine; - float alpha=(float)(sn/(2*q)); - float inv=(float)(1.0/(1.0+alpha)); - - coeffs->a2 = coeffs->a0 = (float)(inv*(1 - cs)*0.5f); - coeffs->a1 = coeffs->a0 + coeffs->a0; - coeffs->b1 = (float)(-2*cs*inv); - coeffs->b2 = (float)((1 - alpha)*inv); -} - -// Based on filter coefficient equations by Robert Bristow-Johnson -static inline void cbox_biquadf_set_hp_rbj(struct cbox_biquadf_coeffs *coeffs, float fc, float q, float sr) -{ - float omega=(float)(2*M_PI*fc/sr); - float sn=sin(omega); - float cs=cos(omega); - float alpha=(float)(sn/(2*q)); - float inv=(float)(1.0/(1.0+alpha)); - - coeffs->a2 = coeffs->a0 = (float)(inv*(1 + cs)*0.5f); - coeffs->a1 = -2 * coeffs->a0; - coeffs->b1 = (float)(-2*cs*inv); - coeffs->b2 = (float)((1 - alpha)*inv); -} - -static inline void cbox_biquadf_set_hp_rbj_lookup(struct cbox_biquadf_coeffs *coeffs, const struct cbox_sincos *sincos, float q) -{ - float sn=sincos->sine; - float cs=sincos->cosine; - float alpha=(float)(sn/(2*q)); - float inv=(float)(1.0/(1.0+alpha)); - - coeffs->a2 = coeffs->a0 = (float)(inv*(1 + cs)*0.5f); - coeffs->a1 = -2 * coeffs->a0; - coeffs->b1 = (float)(-2*cs*inv); - coeffs->b2 = (float)((1 - alpha)*inv); -} - -// Based on filter coefficient equations by Robert Bristow-Johnson -static inline void cbox_biquadf_set_bp_rbj(struct cbox_biquadf_coeffs *coeffs, float fc, float q, float sr) -{ - float omega=(float)(2*M_PI*fc/sr); - float sn=sin(omega); - float cs=cos(omega); - float alpha=(float)(sn/(2*q)); - float inv=(float)(1.0/(1.0+alpha)); - - coeffs->a0 = (float)(inv*alpha); - coeffs->a1 = 0.f; - coeffs->a2 = -coeffs->a0; - coeffs->b1 = (float)(-2*cs*inv); - coeffs->b2 = (float)((1 - alpha)*inv); -} - -static inline void cbox_biquadf_set_bp_rbj_lookup(struct cbox_biquadf_coeffs *coeffs, const struct cbox_sincos *sincos, float q) -{ - float sn=sincos->sine; - float cs=sincos->cosine; - float alpha=(float)(sn/(2*q)); - float inv=(float)(1.0/(1.0+alpha)); - - coeffs->a0 = (float)(inv*alpha); - coeffs->a1 = 0.f; - coeffs->a2 = -coeffs->a0; - coeffs->b1 = (float)(-2*cs*inv); - coeffs->b2 = (float)((1 - alpha)*inv); -} - - -// Based on filter coefficient equations by Robert Bristow-Johnson -static inline void cbox_biquadf_set_peakeq_rbj(struct cbox_biquadf_coeffs *coeffs, float freq, float q, float peak, float sr) -{ - float A = sqrt(peak); - float w0 = freq * 2 * M_PI * (1.0 / sr); - float alpha = sin(w0) / (2 * q); - float ib0 = 1.0 / (1 + alpha/A); - coeffs->a1 = coeffs->b1 = -2*cos(w0) * ib0; - coeffs->a0 = ib0 * (1 + alpha*A); - coeffs->a2 = ib0 * (1 - alpha*A); - coeffs->b2 = ib0 * (1 - alpha/A); -} - -static inline void cbox_biquadf_set_peakeq_rbj_scaled(struct cbox_biquadf_coeffs *coeffs, float freq, float q, float A, float sr) -{ - float w0 = freq * 2 * M_PI * (1.0 / sr); - float alpha = sin(w0) / (2 * q); - float ib0 = 1.0 / (1 + alpha/A); - coeffs->a1 = coeffs->b1 = -2*cos(w0) * ib0; - coeffs->a0 = ib0 * (1 + alpha*A); - coeffs->a2 = ib0 * (1 - alpha*A); - coeffs->b2 = ib0 * (1 - alpha/A); -} - -// This is my math, and it's rather suspect -static inline void cbox_biquadf_set_1plp(struct cbox_biquadf_coeffs *coeffs, float freq, float sr) -{ - float w = hz2w(freq, sr); - float x = tan (w * 0.5f); - float q = 1 / (1 + x); - float a01 = x*q; - float b1 = a01 - q; - - coeffs->a0 = a01; - coeffs->a1 = a01; - coeffs->b1 = b1; - coeffs->a2 = 0; - coeffs->b2 = 0; -} - -static inline void cbox_biquadf_set_1php(struct cbox_biquadf_coeffs *coeffs, float freq, float sr) -{ - float w = hz2w(freq, sr); - float x = tan (w * 0.5f); - float q = 1 / (1 + x); - float a01 = x*q; - float b1 = a01 - q; - - coeffs->a0 = q; - coeffs->a1 = -q; - coeffs->b1 = b1; - coeffs->a2 = 0; - coeffs->b2 = 0; -} - -static inline void cbox_biquadf_set_1p(struct cbox_biquadf_coeffs *coeffs, float a0, float a1, float b1, int two_copies) -{ - if (two_copies) - { - // (a0 + a1z) * (a0 + a1z) = a0^2 + 2*a0*a1*z + a1^2*z^2 - // (1 - b1z) * (1 - b1z) = 1 - 2b1*z + b1^2*z^2 - coeffs->a0 = a0*a0; - coeffs->a1 = 2*a0*a1; - coeffs->b1 = 2 * b1; - coeffs->a2 = a1*a1; - coeffs->b2 = b1*b1; - } - else - { - coeffs->a0 = a0; - coeffs->a1 = a1; - coeffs->b1 = b1; - coeffs->a2 = 0; - coeffs->b2 = 0; - } -} - -static inline void cbox_biquadf_set_1plp_lookup(struct cbox_biquadf_coeffs *coeffs, const struct cbox_sincos *sincos, int two_copies) -{ - float x = sincos->prewarp; - float q = sincos->prewarp2; - float a01 = x*q; - float b1 = a01 - q; - - cbox_biquadf_set_1p(coeffs, a01, a01, b1, two_copies); -} - -static inline void cbox_biquadf_set_1php_lookup(struct cbox_biquadf_coeffs *coeffs, const struct cbox_sincos *sincos, int two_copies) -{ - float x = sincos->prewarp; - float q = sincos->prewarp2; - float a01 = x*q; - float b1 = a01 - q; - - cbox_biquadf_set_1p(coeffs, q, -q, b1, two_copies); -} - -#if USE_NEON - -#include - -static inline void cbox_biquadf_process(struct cbox_biquadf_state *state, struct cbox_biquadf_coeffs *coeffs, float *buffer) -{ - int i; - float32x2_t c0 = {coeffs->a0, 0}; - float32x2_t c1 = {coeffs->a1, -coeffs->b1}; - float32x2_t c2 = {coeffs->a2, -coeffs->b2}; - float32x2_t s1 = {state->x1, state->y1}; - float32x2_t s2 = {state->x2, state->y2}; - - for (i = 0; i < CBOX_BLOCK_SIZE; i ++) - { - float32x2_t in12 = {buffer[i], 0.f}; - - float32x2_t out12 = vmla_f32(vmla_f32(vmul_f32(c1, s1), c2, s2), in12, c0); // [a1 * x1 + a2 * x2 + a0 * in, -b1 * y1 - b2 * y2 + 0] - float32x2x2_t trn = vtrn_f32(out12, in12); // [[a1 * x1 + a2 * x2 + a0 * in, in12], [-b1 * y1 - b2 * y2, 0]] - float32x2_t out120 = vadd_f32(trn.val[0], trn.val[1]); - - s2 = s1; - s1 = vrev64_f32(out120); - buffer[i] = out120[0]; - } - state->x1 = s1[0]; - state->y1 = s1[1]; - state->x2 = s2[0]; - state->y2 = s2[1]; -} - -#else - -static inline void cbox_biquadf_process(struct cbox_biquadf_state *state, struct cbox_biquadf_coeffs *coeffs, float *buffer) -{ - int i; - float a0 = coeffs->a0; - float a1 = coeffs->a1; - float a2 = coeffs->a2; - float b1 = coeffs->b1; - float b2 = coeffs->b2; - double y1 = state->y1; - double y2 = state->y2; - - for (i = 0; i < CBOX_BLOCK_SIZE; i++) - { - float in = buffer[i]; - double out = a0 * in + a1 * state->x1 + a2 * state->x2 - b1 * y1 - b2 * y2; - - buffer[i] = out; - state->x2 = state->x1; - state->x1 = in; - y2 = y1; - y1 = out; - } - state->y2 = sanef(y2); - state->y1 = sanef(y1); -} - -#endif - -static inline void cbox_biquadf_process_stereo(struct cbox_biquadf_state *lstate, struct cbox_biquadf_state *rstate, struct cbox_biquadf_coeffs *coeffs, float *buffer) -{ - int i; - float a0 = coeffs->a0; - float a1 = coeffs->a1; - float a2 = coeffs->a2; - float b1 = coeffs->b1; - float b2 = coeffs->b2; - double ly1 = lstate->y1; - double ly2 = lstate->y2; - double ry1 = rstate->y1; - double ry2 = rstate->y2; - - for (i = 0; i < 2 * CBOX_BLOCK_SIZE; i += 2) - { - float inl = buffer[i], inr = buffer[i + 1]; - float outl = a0 * inl + a1 * lstate->x1 + a2 * lstate->x2 - b1 * ly1 - b2 * ly2; - float outr = a0 * inr + a1 * rstate->x1 + a2 * rstate->x2 - b1 * ry1 - b2 * ry2; - - lstate->x2 = lstate->x1; - lstate->x1 = inl; - ly2 = ly1; - ly1 = outl; - buffer[i] = outl; - - rstate->x2 = rstate->x1; - rstate->x1 = inr; - ry2 = ry1; - ry1 = outr; - buffer[i + 1] = outr; - } - lstate->y2 = sanef(ly2); - lstate->y1 = sanef(ly1); - rstate->y2 = sanef(ry2); - rstate->y1 = sanef(ry1); -} - -static inline double cbox_biquadf_process_sample(struct cbox_biquadf_state *state, struct cbox_biquadf_coeffs *coeffs, double in) -{ - double out = sanef(coeffs->a0 * sanef(in) + coeffs->a1 * state->x1 + coeffs->a2 * state->x2 - coeffs->b1 * state->y1 - coeffs->b2 * state->y2); - - state->x2 = state->x1; - state->x1 = in; - state->y2 = state->y1; - state->y1 = out; - - return out; -} - -static inline void cbox_biquadf_process_to(struct cbox_biquadf_state *state, struct cbox_biquadf_coeffs *coeffs, float *buffer_in, float *buffer_out) -{ - int i; - float a0 = coeffs->a0; - float a1 = coeffs->a1; - float a2 = coeffs->a2; - float b1 = coeffs->b1; - float b2 = coeffs->b2; - double y1 = state->y1; - double y2 = state->y2; - - for (i = 0; i < CBOX_BLOCK_SIZE; i++) - { - float in = buffer_in[i]; - double out = a0 * in + a1 * state->x1 + a2 * state->x2 - b1 * y1 - b2 * y2; - - buffer_out[i] = out; - state->x2 = state->x1; - state->x1 = in; - y2 = y1; - y1 = out; - } - state->y2 = sanef(y2); - state->y1 = sanef(y1); -} - -static inline void cbox_biquadf_process_adding(struct cbox_biquadf_state *state, struct cbox_biquadf_coeffs *coeffs, float *buffer_in, float *buffer_out) -{ - int i; - float a0 = coeffs->a0; - float a1 = coeffs->a1; - float a2 = coeffs->a2; - float b1 = coeffs->b1; - float b2 = coeffs->b2; - double y1 = state->y1; - double y2 = state->y2; - - for (i = 0; i < CBOX_BLOCK_SIZE; i++) - { - float in = buffer_in[i]; - double out = a0 * in + a1 * state->x1 + a2 * state->x2 - b1 * y1 - b2 * y2; - - buffer_out[i] += out; - state->x2 = state->x1; - state->x1 = in; - y2 = y1; - y1 = out; - } - state->y2 = sanef(y2); - state->y1 = sanef(y1); -} - -#endif diff --git a/template/calfbox/blob.c b/template/calfbox/blob.c deleted file mode 100644 index e8d65f6..0000000 --- a/template/calfbox/blob.c +++ /dev/null @@ -1,130 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "blob.h" -#include "tarfile.h" -#include -#include -#include -#include -#include -#include -#include - -struct cbox_blob *cbox_blob_new(size_t size) -{ - struct cbox_blob *p = malloc(sizeof(struct cbox_blob)); - if (!p) - return NULL; - p->data = size ? malloc(size) : NULL; - p->size = size; - return p; -} - -struct cbox_blob *cbox_blob_new_copy_data(const void *data, size_t size) -{ - struct cbox_blob *p = cbox_blob_new(size); - if (!p) - return NULL; - memcpy(p, data, size); - return p; -} - -struct cbox_blob *cbox_blob_new_acquire_data(void *data, size_t size) -{ - struct cbox_blob *p = malloc(sizeof(struct cbox_blob)); - if (!p) - return NULL; - p->data = data; - p->size = size; - return p; -} - -static struct cbox_blob *read_from_fd(const char *context_name, const char *pathname, int fd, size_t size, GError **error) -{ - struct cbox_blob *blob = cbox_blob_new(size + 1); - if (!blob) - { - g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "%s: cannot allocate memory for file '%s'", context_name, pathname); - return NULL; - } - uint8_t *data = blob->data; - size_t nread = 0; - do { - size_t chunk = size - nread; - if (chunk > 131072) - chunk = 131072; - size_t nv = read(fd, data + nread, chunk); - if (nv == (size_t)-1) - { - if (errno == EINTR) - continue; - g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "%s: cannot read '%s': %s", context_name, pathname, strerror(errno)); - cbox_blob_destroy(blob); - return NULL; - } - nread += nv; - } while(nread < size); - // Make sure that the content is 0-padded but still has the original size - // (without extra zero byte) - data[nread] = '\0'; - blob->size = nread; - return blob; -} - -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) -{ - gchar *fullpath = g_build_filename(path, name, NULL); - struct cbox_blob *blob = NULL; - if (tarfile) - { - struct cbox_taritem *item = cbox_tarfile_get_item_by_name(tarfile, fullpath, TRUE); - if (item) - { - int fd = cbox_tarfile_openitem(tarfile, item); - if (fd >= 0) - { - blob = read_from_fd(context_name, fullpath, fd, item->size, error); - cbox_tarfile_closeitem(tarfile, item, fd); - } - } - } - else - { - int fd = open(fullpath, O_RDONLY | O_LARGEFILE); - if (fd >= 0) - { - uint64_t size = lseek64(fd, 0, SEEK_END); - if (size <= max_size) - blob = read_from_fd(context_name, fullpath, fd, size, error); - else - 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); - close(fd); - } - else - g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "%s: cannot open '%s': %s", context_name, fullpath, strerror(errno)); - } - g_free(fullpath); - return blob; -} - -void cbox_blob_destroy(struct cbox_blob *blob) -{ - free(blob->data); - free(blob); -} diff --git a/template/calfbox/blob.h b/template/calfbox/blob.h deleted file mode 100644 index 4999b0c..0000000 --- a/template/calfbox/blob.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_BLOB_H -#define CBOX_BLOB_H - -#include -#include - -struct cbox_blob -{ - void *data; - size_t size; -}; - -struct cbox_tarfile; - -extern struct cbox_blob *cbox_blob_new(size_t size); -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); -extern struct cbox_blob *cbox_blob_new_copy_data(const void *data, size_t size); -extern struct cbox_blob *cbox_blob_new_acquire_data(void *data, size_t size); -extern void cbox_blob_destroy(struct cbox_blob *blob); - -#endif diff --git a/template/calfbox/py/cbox.py b/template/calfbox/cbox.py similarity index 99% rename from template/calfbox/py/cbox.py rename to template/calfbox/cbox.py index 9f0d456..1b2fbff 100644 --- a/template/calfbox/py/cbox.py +++ b/template/calfbox/cbox.py @@ -1,9 +1,16 @@ -from calfbox._cbox2 import * from io import BytesIO import struct import sys import traceback -import calfbox.metadata as metadata #local file metadata.py + +try: + from _cbox2 import * #local file _cbox2.py + import metadata #local file metadata.py +except ModuleNotFoundError: + from ._cbox2 import * + from . import metadata #local file metadata.py + + type_wrapper_debug = False is_python3 = not sys.version.startswith("2") diff --git a/template/calfbox/cboxrc-example b/template/calfbox/cboxrc-example deleted file mode 100644 index fba4902..0000000 --- a/template/calfbox/cboxrc-example +++ /dev/null @@ -1,574 +0,0 @@ -[io] -inputs=2 -in_1=#1 -in_2=#2 -out_1=#1 -out_2=#2 - -;#midi=alsa_pcm:E-MU-XMidi2X2/midi_capture_2;alsa_pcm:E-MU-Xboard25/midi_capture_1 -;midi=alsa_pcm:E-MU-XMidi2X2/midi_capture_2;alsa_pcm:E-MU-XMidi2X2/midi_capture_1 -;midi=~alsa_pcm:in-.*-E-MU-XMidi2X2-MIDI-1;~alsa_pcm:in-.*-E-MU-XMidi2X2-MIDI-2;~alsa_pcm:in-.*-padKONTROL-MIDI-2 -midi=*.* - -[master] -tempo=100 -beats_per_bar=4 -;effect=cbox_reverb - -[fxpreset:cbox_reverb] -engine=reverb -reverb_time=800 -wet_gain=-12 -dry_gain=0 -stereo=-12 - -[scene:example] -layer1=organ -layer2=piano - -[layer:piano] -instrument=progmega -out_channel=1 -low_note=C3 - -[layer:organ] -instrument=default -out_channel=1 -high_note=B2 - -[instrument:default] -engine=tonewheel_organ -percussion=1 -percussion_3rd=1 -upper_drawbars=888000000 -;upper_drawbars=888888888 -;upper_drawbars=888000008 -;upper_drawbars=800000888 -;upper_drawbars=800064000 -;upper_drawbars=802244220 -lower_drawbars=838000000 -pedal_drawbars=80 -vibrato_upper=1 -vibrato_lower=1 -vibrato_mode=c3 - -[instrument:progmega] -engine=fluidsynth -sf2=ProgMegaBank.sf2 -reverb=1 -chorus=1 -channel1=SP250 -channel2=jRhodes3a -channel3=P5 Brass - -[instrument:progmega_cheap] -engine=fluidsynth -sf2=ProgMegaBank.sf2 -reverb=0 -chorus=0 -channel1=SP250 -channel2=jRhodes3a -channel3=P5 Brass - -[instrument:progmega_fx] -engine=fluidsynth -sf2=ProgMegaBank.sf2 -reverb=0 -chorus=0 -insert=chain -channel1=SP250 -channel2=jRhodes3a -channel3=P5 Brass - -[fxpreset:chain] -engine=fxchain -effect1=stream_phaser -effect2=stream_delay - -[fxpreset:stream_phaser] -engine=phaser -center=500 -mod_depth=300 -lfo_freq=3 - -[fxpreset:stream_delay] -engine=delay -delay=250 -wet_amt=0.25 -feedback_gain=-12 - -[autojack] -soundcard0=Omega -soundcard1=STA -soundcard2=Intel -jack_options=-r -T -alsa_options=-p 128 -n 3 -X raw -jackd=/usr/bin/jackd - -[soundcard:Omega] -usbid=1210:0002 - -[soundcard:STA] -device=DSP24 - -[soundcard:Intel] -device=Intel - -[pattern:walkingminor] -title=Walking-minor -beats=4 -resolution=1 -track1=bass -bass_channel=2 -bass_vel=80 -bass_notes=c1,d1,eb1,g1 - -[track:two] -title=Cm/Ebm -pos1=walkingminor -pos2=walkingminor+3 - -[drumpattern:pat1] -title=Straight - Verse -beats=4 -track1=bd -track2=sd -track3=hh -track4=ho -bd_note=c1 -sd_note=d1 -hh_note=f#1 -ho_note=a#1 -bd_trigger=9... .... 9.6. .... -sd_trigger=.... 9..5 .2.. 9... -hh_trigger=9353 7353 7353 73.3 -ho_trigger=.... .... .... ..3. - -[drumpattern:pat1fill] -title=Straight - Fill -beats=4 -track1=bd -track2=sd -track3=hh -track4=ho -bd_note=c1 -sd_note=d1 -hh_note=f#1 -ho_note=a#1 -bd_trigger=9... .... 9.6. .... -sd_trigger=..64 9..5 .2.7 99.9 -hh_trigger=9353 7353 7353 73.3 -ho_trigger=.... .... .... ..3. - -[drumpattern:pat2] -title=Enigma - Verse -beats=4 -track1=bd -track2=sd -track3=hh -track4=ho -bd_note=c1 -sd_note=d1 -hh_note=f#1 -ho_note=a#1 -bd_trigger=9..7 ..7. ..6. .... -sd_trigger=.... 9... .... 9... -hh_trigger=9353 7353 7353 7353 -ho_trigger=.... .... ..3. .... - -[drumpattern:pat2fill] -title=Enigma - Fill -beats=4 -resolution=4 -track1=bd -track2=sd -track3=hh -track4=ho -track5=ht -track6=mt -bd_note=c1 -sd_note=d1 -hh_note=f#1 -ho_note=a#1 -ht_note=d2 -mt_note=b1 -bd_trigger=9..7 ..7. ..6. .... -sd_trigger=.... 9... .... 9..9 -hh_trigger=9353 7353 7353 7353 -ho_trigger=.... .... ..3. .... -ht_trigger=.5.. .... ...7 .... -mt_trigger=.... ...5 .... ..5. - -[drumpattern:pat3] -title=Shuffle - Verse -beats=4 -swing=6 -track1=bd -track2=sd -track3=hh -track4=ho -track5=ht -track6=mt -bd_note=c1 -sd_note=d1 -hh_note=f#1 -ho_note=a#1 -ht_note=d2 -mt_note=b1 -bd_trigger=9.8. ...8 ..6. .... -sd_trigger=.... 94D. .54. 9..D -hh_trigger=9353 7353 7353 7353 -ho_trigger=.... .... .... .... -ht_trigger=.... .... .... .... -mt_trigger=.... .... .... .... - -[drumpattern:pat3fill] -title=Shuffle - Fill -beats=4 -swing=6 -track1=bd -track2=sd -track3=hh -track4=ho -track5=ht -track6=mt -bd_note=c1 -sd_note=d1 -hh_note=f#1 -ho_note=a#1 -ht_note=d2 -mt_note=b1 -bd_trigger=9.8. ...8 ..6. .... -sd_trigger=.... 9... F... F..9 -hh_trigger=9353 7353 7353 7353 -ho_trigger=.... .... .... .... -ht_trigger=.... .... ..9. .5.. -mt_trigger=.... .... ...7 ..7. - -[drumpattern:pat4] -title=Swing -beats=4 -resolution=3 -track1=bd -track2=sd -track3=hp -track4=rc -track5=rb -bd_note=c1 -sd_note=d1 -hp_note=g#1 -rc_note=d#2 -rb_note=f2 -bd_trigger=... ... ... ..3 -sd_trigger=... ..3 ..2 .3. -hp_trigger=... 6.. ... 6.. -rc_trigger=2.. ..4 4.. ..3 -rb_trigger=3.. ... 3.. ... - -[drumpattern:pat4fill] -title=Swing -beats=4 -resolution=3 -track1=bd -track2=sd -track3=hp -track4=rc -track5=rb -bd_note=c1 -sd_note=d1 -hp_note=g#1 -rc_note=d#2 -rb_note=f2 -bd_trigger=7.. 5.. ... ..5 -sd_trigger=..5 ..3 .25 43. -hp_trigger=... 6.. ... 6.. -rc_trigger=2.. ..4 4.. ... -rb_trigger=4.. ... 2.. ... - -[drumpattern:pat5] -title=6/8 Blues - Verse -beats=4 -resolution=3 -track1=bd -track2=sd -track3=hh -track4=ho -bd_note=c1 -sd_note=d1 -hh_note=f#1 -ho_note=a#1 -bd_trigger=7.. ... 7.. .44 -sd_trigger=... 8.. ... 8.. -hh_trigger=7.5 6.4 7.5 6.. -ho_trigger=... ... ... ..3 - -[drumpattern:pat5fill] -title=6/8 Blues - Fill -beats=4 -resolution=3 -track1=bd -track2=sd -track3=hh -track4=ho -track5=ht -track6=mt -bd_note=c1 -sd_note=d1 -hh_note=f#1 -ho_note=a#1 -ht_note=d2 -mt_note=b1 -bd_trigger=7.. ... ... ..4 -sd_trigger=... 8.D 75. 85. -hh_trigger=7.5 6.. 7.. ... -ho_trigger=.3. ... ... ... -ht_trigger=... ... ..7 ... -mt_trigger=... ... ... ..5 - -[drumpattern:pat6] -title=Pompopom - Verse -beats=4 -track1=bd -track2=sd -track3=hh -track4=ho -bd_note=c1 -sd_note=d1 -hh_note=f#1 -ho_note=a#1 -bd_trigger=9.97 ...9 .996 .... -sd_trigger=.... 9... .... 9... -hh_trigger=9.7. 7.7. 9.7. 7.7. -ho_trigger=.... .3.. .... .... - -[drumpattern:pat6fill] -title=Pompopom - Fill -beats=4 -track1=bd -track2=sd -track3=hh -track4=ho -bd_note=c1 -sd_note=d1 -hh_note=f#1 -ho_note=a#1 -bd_trigger=9..7 ...9 .9.6 .... -sd_trigger=.6.. 9.7. ..7D 97.9 -hh_trigger=9.7. 7.7. 9.7. 7.7. -ho_trigger=.... .3.. .... .... - -[drumpattern:pat7] -title=Rockish -beats=4 -track1=bd -track2=sd -track3=hh -track4=ho -bd_note=c1 -sd_note=d1 -hh_note=f#1 -ho_note=a#1 -bd_trigger=98.6 ...7 .95. .... -sd_trigger=.... 9... .... 9..D -hh_trigger=7.5. 7.5. 7.5. 7.5. -ho_trigger=...3 .... .... .... - -[drumpattern:pat7fill] -title=Rockish - Fill -beats=4 -track1=bd -track2=sd -track3=hh -track4=ho -track5=mt -track6=ht -bd_note=c1 -sd_note=d1 -hh_note=f#1 -ho_note=a#1 -mt_note=b1 -ht_note=d2 -bd_trigger=98.6 ...7 .95. .... -sd_trigger=.... 9..7 ..9. 9..9 -hh_trigger=7.5. 7.5. 7.5. 7.5. -ho_trigger=.5.3 .... .... .... -mt_trigger=.... .... .... ..8. -ht_trigger=.... .... ...9 .... - -[drumpattern:pat8] -title=80s - Verse -beats=4 -track1=bd -track2=sd -track3=hh -track4=ho -bd_note=c1 -sd_note=d1 -hh_note=f#1 -ho_note=a#1 -bd_trigger=96.. ..96 ..6. .... -sd_trigger=.... 9... .... 9... -hh_trigger=7.7. 7.7. 7.7. 7.7. -ho_trigger=.... .... .... .... - -[drumpattern:pat8fill] -title=80s - Fill -beats=4 -track1=bd -track2=sd -track3=hh -track4=ho -track5=mt -track6=ht -bd_note=c1 -sd_note=d1 -hh_note=f#1 -ho_note=a#1 -mt_note=b1 -ht_note=d2 -bd_trigger=96.. ..96 ..6. .... -sd_trigger=..7. 9... 7.7. 9... -hh_trigger=7.7. 7.7. 7.7. 7... -ho_trigger=.... .... .... .... -ht_trigger=.... .... .9.. .97. -mt_trigger=.... .... ...7 ...5 - -[drumpattern:pat9] -title=Disco - Verse -beats=4 -swing=2 -track1=bd -track2=sd -track3=hh -track4=ho -track5=rs -bd_note=c1 -sd_note=d1 -hh_note=f#1 -ho_note=a#1 -rs_note=c#1 -bd_trigger=9... 9... 9... 9... -rs_trigger=..4. .4.. 4..4 ..4. -sd_trigger=.... 8..6 .... 8... -hh_trigger=74.7 74.7 74.7 74.7 -ho_trigger=..5. ..5. ..5. ..5. - -[drumpattern:pat9fill] -title=Disco - Fill -beats=4 -swing=2 -track1=bd -track2=sd -track3=hh -track4=ho -bd_note=c1 -sd_note=d1 -hh_note=f#1 -ho_note=a#1 -bd_trigger=9... 9... 9... 9... -sd_trigger=.6.. 8.58 .6.5 8688 -hh_trigger=74.7 74.7 74.7 74.7 -ho_trigger=..5. ..5. ..5. ..5. - -[drumpattern:crash] -title=Crash cymbal -beats=4 -track1=cc -cc_note=c#2 -cc_trigger=9... - -[drumpattern:crash2] -title=Kick+crash cymbal -beats=4 -track1=cc -track2=bd -cc_note=a#2 -bd_note=b0 -cc_trigger=5... -bd_trigger=5... - -[drumtrack:trk1] -title=Straight -pos1=pat1,crash -pos2=pat1 -pos3=pat1 -pos4=pat1fill - -[drumtrack:trk2] -title=Enigma -pos1=pat2,crash -pos2=pat2 -pos3=pat2 -pos4=pat2fill - -[drumtrack:trk3] -title=Shuffle -pos1=pat3,crash -pos2=pat3 -pos3=pat3 -pos4=pat3fill - -[drumtrack:trk4] -title=Swing -pos1=pat4,crash2 -pos2=pat4 -pos3=pat4 -pos4=pat4fill - -[drumtrack:trk5] -title=6/8 Blues -pos1=pat5,crash -pos2=pat5 -pos3=pat5 -pos4=pat5fill - -[drumtrack:trk6] -title=Pompopom -pos1=pat6,crash -pos2=pat6 -pos3=pat6 -pos4=pat6fill - -[drumtrack:trk7] -title=Rockish -pos1=pat7,crash -pos2=pat7 -pos3=pat7 -pos4=pat7fill - -[drumtrack:trk8] -title=80s -pos1=pat8,crash -pos2=pat8 -pos3=pat8 -pos4=pat8fill - -[drumtrack:trk9] -title=Disco -pos1=pat9,crash -pos2=pat9 -pos3=pat9 -pos4=pat9fill - -[layer:samplertest] -instrument=samplertest - -[instrument:samplertest] -engine=sampler -program0=prog - -[spgm:prog] -layer1=saw1 -layer2=saw2 - -[slayer:saw1] -sample=*saw -tune=-5 -pan=-30 -volume=-6 - -[slayer:saw2] -sample=*saw -tune=+5 -pan=30 -volume=-6 diff --git a/template/calfbox/chorus.c b/template/calfbox/chorus.c deleted file mode 100644 index 06945da..0000000 --- a/template/calfbox/chorus.c +++ /dev/null @@ -1,175 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config.h" -#include "config-api.h" -#include "dspmath.h" -#include "module.h" -#include -#include -#include -#include -#include -#include -#include - -#define MAX_CHORUS_LENGTH 4096 - -#define MODULE_PARAMS chorus_params - -static float sine_table[2049]; - -struct chorus_params -{ - float lfo_freq; - float min_delay; - float mod_depth; - float wet_dry; - float sphase; -}; - -struct chorus_module -{ - struct cbox_module module; - - float storage[MAX_CHORUS_LENGTH][2]; - struct chorus_params *params; - int pos; - float tp32dsr; - uint32_t phase; -}; - -MODULE_PROCESSCMD_FUNCTION(chorus) -{ - struct chorus_module *m = (struct chorus_module *)ct->user_data; - - EFFECT_PARAM("/min_delay", "f", min_delay, double, , 1, 20) else - EFFECT_PARAM("/mod_depth", "f", mod_depth, double, , 1, 20) else - EFFECT_PARAM("/lfo_freq", "f", lfo_freq, double, , 0, 20) else - EFFECT_PARAM("/stereo_phase", "f", sphase, double, , 0, 360) else - EFFECT_PARAM("/wet_dry", "f", wet_dry, double, , 0, 1) else - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - return cbox_execute_on(fb, NULL, "/min_delay", "f", error, m->params->min_delay) && - cbox_execute_on(fb, NULL, "/mod_depth", "f", error, m->params->mod_depth) && - cbox_execute_on(fb, NULL, "/lfo_freq", "f", error, m->params->lfo_freq) && - cbox_execute_on(fb, NULL, "/stereo_phase", "f", error, m->params->sphase) && - cbox_execute_on(fb, NULL, "/wet_dry", "f", error, m->params->wet_dry) && - CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error); - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); - return TRUE; -} - -void chorus_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) -{ - // struct chorus_module *m = (struct chorus_module *)module; -} - -void chorus_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) -{ - struct chorus_module *m = (struct chorus_module *)module; - struct chorus_params *p = m->params; - - float min_delay = p->min_delay; - float mod_depth = p->mod_depth; - float wet_dry = p->wet_dry; - int i, c; - int mask = MAX_CHORUS_LENGTH - 1; - uint32_t sphase = (uint32_t)(p->sphase * 65536.0 * 65536.0 / 360); - uint32_t dphase = (uint32_t)(p->lfo_freq * m->tp32dsr); - const int fracbits = 32 - 11; - const int fracscale = 1 << fracbits; - - for (c = 0; c < 2; c++) - { - int pos = m->pos; - uint32_t phase = m->phase + c * sphase; - for (i = 0; i < CBOX_BLOCK_SIZE; i++) - { - float dry = inputs[c][i]; - float v0 = sine_table[phase >> fracbits]; - float v1 = sine_table[1 + (phase >> fracbits)]; - float lfo = v0 + (v1 - v0) * ((phase & (fracscale - 1)) * (1.0 / fracscale)); - - m->storage[pos & mask][c] = dry; - - float dva = min_delay + mod_depth * lfo; - int dv = (int)dva; - float frac = dva - dv; - float smp0 = m->storage[(pos - dv) & mask][c]; - float smp1 = m->storage[(pos - dv - 1) & mask][c]; - - float smp = smp0 + (smp1 - smp0) * frac; - - outputs[c][i] = sanef(dry + (smp - dry) * wet_dry); - - pos++; - phase += dphase; - } - } - - m->phase += CBOX_BLOCK_SIZE * dphase; - m->pos += CBOX_BLOCK_SIZE; -} - -MODULE_SIMPLE_DESTROY_FUNCTION(chorus) - -MODULE_CREATE_FUNCTION(chorus) -{ - static int inited = 0; - int i; - if (!inited) - { - inited = 1; - for (i = 0; i < 2049; i++) - sine_table[i] = 1 + sin(i * M_PI / 1024); - } - - struct chorus_module *m = malloc(sizeof(struct chorus_module)); - CALL_MODULE_INIT(m, 2, 2, chorus); - m->module.process_event = chorus_process_event; - m->module.process_block = chorus_process_block; - m->pos = 0; - m->phase = 0; - m->tp32dsr = 65536.0 * 65536.0 * m->module.srate_inv; - struct chorus_params *p = malloc(sizeof(struct chorus_params)); - m->params = p; - p->sphase = cbox_config_get_float(cfg_section, "stereo_phase", 90.f); - p->lfo_freq = cbox_config_get_float(cfg_section, "lfo_freq", 1.f); - p->min_delay = cbox_config_get_float(cfg_section, "min_delay", 20.f); - p->mod_depth = cbox_config_get_float(cfg_section, "mod_depth", 15.f); - p->wet_dry = cbox_config_get_float(cfg_section, "wet_dry", 0.5f); - for (i = 0; i < MAX_CHORUS_LENGTH; i++) - m->storage[i][0] = m->storage[i][1] = 0.f; - - return &m->module; -} - - -struct cbox_module_keyrange_metadata chorus_keyranges[] = { -}; - -struct cbox_module_livecontroller_metadata chorus_controllers[] = { -}; - -DEFINE_MODULE(chorus, 2, 2) - diff --git a/template/calfbox/cleanpythonbuild.sh b/template/calfbox/cleanpythonbuild.sh deleted file mode 100755 index 602a0f4..0000000 --- a/template/calfbox/cleanpythonbuild.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -make clean -make distclean -rm build -rf -set -e -sh autogen.sh -./configure --prefix=/usr --without-python -make CFLAGS="-O0 -g" -python3 setup.py build -sudo python3 setup.py install -sudo make install diff --git a/template/calfbox/cmd.c b/template/calfbox/cmd.c deleted file mode 100644 index 775b24e..0000000 --- a/template/calfbox/cmd.c +++ /dev/null @@ -1,208 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "blob.h" -#include "cmd.h" -#include "dom.h" -#include "errors.h" -#include -#include -#include -#include -#include -#include - -void cbox_command_target_init(struct cbox_command_target *ct, cbox_process_cmd cmd, void *user_data) -{ - ct->process_cmd = cmd; - ct->user_data = user_data; -} - -gboolean cbox_execute_on(struct cbox_command_target *ct, struct cbox_command_target *fb, const char *cmd_name, const char *args, GError **error, ...) -{ - va_list av; - - va_start(av, error); - gboolean res = cbox_execute_on_v(ct, fb, cmd_name, args, av, error); - va_end(av); - return res; -} - -gboolean cbox_execute_on_v(struct cbox_command_target *ct, struct cbox_command_target *fb, const char *cmd_name, const char *args, va_list av, GError **error) -{ - int argcount = 0; - struct cbox_osc_command cmd; - uint8_t *extra_data; - // XXXKF might be not good enough for weird platforms - uint32_t unit_size = sizeof(double); - // this must be a power of 2 to guarantee proper alignment - assert(unit_size >= sizeof(int) && (unit_size == 4 || unit_size == 8)); - cmd.command = cmd_name; - cmd.arg_types = args; - for (int i = 0; args[i]; i++) - argcount = i + 1; - // contains pointers to all the values, plus values themselves in case of int/double - // (casting them to pointers is ugly, and va_arg does not return a lvalue) - cmd.arg_values = malloc(sizeof(void *) * argcount + unit_size * argcount); - extra_data = (uint8_t *)&cmd.arg_values[argcount]; - - for (int i = 0; i < argcount; i++) - { - int iv; - double fv; - void *pv = extra_data + unit_size * i; - switch(args[i]) - { - case 's': - cmd.arg_values[i] = va_arg(av, char *); - break; - case 'i': - iv = va_arg(av, int); - memcpy(pv, &iv, sizeof(int)); - cmd.arg_values[i] = pv; - break; - case 'f': // double really - fv = (double)va_arg(av, double); - memcpy(pv, &fv, sizeof(double)); - cmd.arg_values[i] = pv; - break; - case 'b': - cmd.arg_values[i] = va_arg(av, struct cbox_blob *); - break; - case 'o': - cmd.arg_values[i] = va_arg(av, struct cbox_objhdr *); - break; - case 'u': - cmd.arg_values[i] = va_arg(av, struct cbox_uuid *); - break; - default: - g_error("Invalid format character '%c' for command '%s'", args[i], cmd_name); - assert(0); - } - } - gboolean result = ct->process_cmd(ct, fb, &cmd, error); - free(cmd.arg_values); - return result; -} - -gboolean cbox_osc_command_dump(const struct cbox_osc_command *cmd) -{ - g_message("Command = %s, args = %s", cmd->command, cmd->arg_types); - for (int i = 0; cmd->arg_types[i]; i++) - { - switch(cmd->arg_types[i]) - { - case 's': - g_message("Args[%d] = '%s'", i, (const char *)cmd->arg_values[i]); - break; - case 'o': - { - struct cbox_objhdr *oh = cmd->arg_values[i]; - char buf[40]; - uuid_unparse(oh->instance_uuid.uuid, buf); - g_message("Args[%d] = uuid:'%s'", i, buf); - break; - } - case 'i': - g_message("Args[%d] = %d", i, *(int *)cmd->arg_values[i]); - break; - case 'f': - g_message("Args[%d] = %f", i, *(double *)cmd->arg_values[i]); - break; - case 'b': - { - struct cbox_blob *b = cmd->arg_values[i]; - g_message("Args[%d] = (%p, %d)", i, b->data, (int)b->size); - break; - } - default: - g_error("Invalid format character '%c' for command '%s'", cmd->arg_types[i], cmd->command); - assert(0); - return FALSE; - } - } - return TRUE; -} - -gboolean cbox_check_fb_channel(struct cbox_command_target *fb, const char *command, GError **error) -{ - if (fb) - return TRUE; - - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Feedback channel required for command '%s'", command); - return FALSE; -} - - -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) -{ - struct cbox_osc_command subcmd; - subcmd.command = new_command; - subcmd.arg_types = cmd->arg_types; - subcmd.arg_values = cmd->arg_values; - return ct->process_cmd(ct, fb, &subcmd, error); -} - -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) -{ - char *numcopy = NULL; - if (!cbox_parse_path_part_str(cmd, path, subcommand, &numcopy, error)) - return FALSE; - if (!*subcommand) - return TRUE; - char *endptr = NULL; - *index = strtol(numcopy, &endptr, 10); - if (!*numcopy && *endptr) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid index %s for command %s", numcopy, cmd->command); - g_free(numcopy); - *subcommand = NULL; - return TRUE; - } - if (*index < min_index || *index > max_index) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Index %s out of range [%d, %d] for command %s", numcopy, min_index, max_index, cmd->command); - g_free(numcopy); - *subcommand = NULL; - return TRUE; - } - g_free(numcopy); - return TRUE; -} - -gboolean cbox_parse_path_part_str(const struct cbox_osc_command *cmd, const char *path, const char **subcommand, char **path_element, GError **error) -{ - *path_element = NULL; - *subcommand = NULL; - int plen = strlen(path); - if (!strncmp(cmd->command, path, plen)) - { - const char *num = cmd->command + plen; - const char *slash = strchr(num, '/'); - if (!slash) - { - cbox_set_command_error_with_msg(error, cmd, "needs at least one extra path element"); - return TRUE; - } - - *path_element = g_strndup(num, slash-num); - *subcommand = slash; - return TRUE; - } - return FALSE; -} diff --git a/template/calfbox/cmd.h b/template/calfbox/cmd.h deleted file mode 100644 index fa34cdb..0000000 --- a/template/calfbox/cmd.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_CMD_H -#define CBOX_CMD_H - -#include -#include -#include - -#define CBOX_ARG_I(cmd, idx) (*(int *)(cmd)->arg_values[(idx)]) -#define CBOX_ARG_S(cmd, idx) ((const char *)(cmd)->arg_values[(idx)]) -#define CBOX_ARG_B(cmd, idx) ((const struct cbox_blob *)(cmd)->arg_values[(idx)]) -#define CBOX_ARG_F(cmd, idx) (*(double *)(cmd)->arg_values[(idx)]) -#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)) -#define CBOX_ARG_S_ISNULL(cmd, idx) (0 == (const char *)(cmd)->arg_values[(idx)]) - -struct cbox_command_target; - -struct cbox_osc_command -{ - const char *command; - const char *arg_types; - void **arg_values; -}; - -typedef gboolean (*cbox_process_cmd)(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error); - -struct cbox_command_target -{ - void *user_data; - cbox_process_cmd process_cmd; -}; - -void cbox_command_target_init(struct cbox_command_target *ct, cbox_process_cmd cmd, void *user_data); - -extern gboolean cbox_check_fb_channel(struct cbox_command_target *fb, const char *command, GError **error); - -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); -extern gboolean cbox_execute_on(struct cbox_command_target *ct, struct cbox_command_target *fb, const char *cmd, const char *args, GError **error, ...); -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); - -extern gboolean cbox_osc_command_dump(const struct cbox_osc_command *cmd); - -// Note: this sets *subcommand to NULL on parse error; requires "/path/" as path -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); -extern gboolean cbox_parse_path_part_str(const struct cbox_osc_command *cmd, const char *path, const char **subcommand, char **path_element, GError **error); - -#endif diff --git a/template/calfbox/compressor.c b/template/calfbox/compressor.c deleted file mode 100644 index 891b27b..0000000 --- a/template/calfbox/compressor.c +++ /dev/null @@ -1,155 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config.h" -#include "config-api.h" -#include "dspmath.h" -#include "module.h" -#include "onepole-float.h" -#include -#include -#include -#include -#include -#include -#include - -#define MODULE_PARAMS compressor_params - -struct compressor_params -{ - float threshold; - float ratio; - float attack; - float release; - float makeup; -}; - -struct compressor_module -{ - struct cbox_module module; - - struct compressor_params *params, *old_params; - struct cbox_onepolef_coeffs attack_lp, release_lp, fast_attack_lp; - struct cbox_onepolef_state tracker; - struct cbox_onepolef_state tracker2; -}; - -MODULE_PROCESSCMD_FUNCTION(compressor) -{ - struct compressor_module *m = (struct compressor_module *)ct->user_data; - - EFFECT_PARAM("/makeup", "f", makeup, double, dB2gain_simple, -100, 100) else - EFFECT_PARAM("/threshold", "f", threshold, double, dB2gain_simple, -100, 100) else - EFFECT_PARAM("/ratio", "f", ratio, double, , 1, 100) else - EFFECT_PARAM("/attack", "f", attack, double, , 1, 1000) else - EFFECT_PARAM("/release", "f", release, double, , 1, 1000) else - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - return cbox_execute_on(fb, NULL, "/makeup", "f", error, gain2dB_simple(m->params->makeup)) - && cbox_execute_on(fb, NULL, "/threshold", "f", error, gain2dB_simple(m->params->threshold)) - && cbox_execute_on(fb, NULL, "/ratio", "f", error, m->params->ratio) - && cbox_execute_on(fb, NULL, "/attack", "f", error, m->params->attack) - && cbox_execute_on(fb, NULL, "/release", "f", error, m->params->release) - && CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error) - ; - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); - return TRUE; -} - -void compressor_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) -{ - // struct compressor_module *m = module->user_data; -} - -void compressor_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) -{ - struct compressor_module *m = module->user_data; - - if (m->params != m->old_params) - { - float scale = M_PI * 1000 / m->module.srate; - cbox_onepolef_set_lowpass(&m->fast_attack_lp, 2 * scale / m->params->attack); - cbox_onepolef_set_lowpass(&m->attack_lp, scale / m->params->attack); - cbox_onepolef_set_lowpass(&m->release_lp, scale / m->params->release); - m->old_params = m->params; - } - - float threshold = m->params->threshold, invratio = 1.0 / m->params->ratio; - for (int i = 0; i < CBOX_BLOCK_SIZE; i++) - { - float left = inputs[0][i], right = inputs[1][i]; - float sig = 0.5 * (fabs(left) > fabs(right) ? fabs(left) : fabs(right)); - - int falling = sig < m->tracker.y1 && sig < m->tracker.x1; - int rising_fast = sig > 4 * m->tracker.y1 && sig > 4 * m->tracker.x1; - sig = cbox_onepolef_process_sample(&m->tracker, falling ? &m->release_lp : (rising_fast && m->tracker.y1 ? &m->fast_attack_lp : &m->attack_lp), sig); - sig = cbox_onepolef_process_sample(&m->tracker2, falling ? &m->release_lp : (rising_fast && m->tracker2.y1 ? &m->fast_attack_lp : &m->attack_lp), sig); - float gain = 1.0; - if (sig > threshold) - gain = threshold * powf(sig / threshold, invratio) / sig; - gain *= m->params->makeup; - - outputs[0][i] = left * gain; - outputs[1][i] = right * gain; - } -} - -MODULE_SIMPLE_DESTROY_FUNCTION(compressor) - -MODULE_CREATE_FUNCTION(compressor) -{ - static int inited = 0; - if (!inited) - { - inited = 1; - } - - struct compressor_module *m = malloc(sizeof(struct compressor_module)); - CALL_MODULE_INIT(m, 2, 2, compressor); - m->module.process_event = compressor_process_event; - m->module.process_block = compressor_process_block; - - struct compressor_params *p = malloc(sizeof(struct compressor_params)); - p->threshold = cbox_config_get_gain_db(cfg_section, "threshold", -12.0); - p->ratio = cbox_config_get_float(cfg_section, "ratio", 2.0); - p->attack = cbox_config_get_float(cfg_section, "attack", 5.0); - p->release = cbox_config_get_float(cfg_section, "release", 100.0); - p->makeup = cbox_config_get_gain_db(cfg_section, "makeup", 6.0); - m->params = p; - m->old_params = NULL; - - cbox_onepolef_reset(&m->tracker); - cbox_onepolef_reset(&m->tracker2); - - return &m->module; -} - - -struct cbox_module_keyrange_metadata compressor_keyranges[] = { -}; - -struct cbox_module_livecontroller_metadata compressor_controllers[] = { -}; - -DEFINE_MODULE(compressor, 2, 2) - diff --git a/template/calfbox/config-api.c b/template/calfbox/config-api.c deleted file mode 100644 index 885635f..0000000 --- a/template/calfbox/config-api.c +++ /dev/null @@ -1,357 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config-api.h" - -#include -#include -#include -#include -#include - -static GKeyFile *config_keyfile; -static gchar *keyfile_name; -static GStringChunk *cfg_strings = NULL; -static GHashTable *config_sections_hash = NULL; - -void cbox_config_init(const char *override_file) -{ - const gchar *keyfiledirs[3]; - const gchar *keyfilename = ".cboxrc"; - GKeyFileFlags flags = G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS; - GError *error = NULL; - if (config_keyfile) - return; - - config_sections_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); - - cfg_strings = g_string_chunk_new(100); - config_keyfile = g_key_file_new(); - - // Allow virtual (in-memory) config by passing empty string - if (override_file && !*override_file) - { - keyfile_name = g_strdup(""); - return; - } - - keyfiledirs[0] = getenv("HOME"); - keyfiledirs[1] = NULL; - // XXXKF add proper error handling - - if (override_file) - { - if (!g_key_file_load_from_file(config_keyfile, override_file, flags, &error)) - { - g_warning("Could not read user config: %s", error->message); - g_error_free(error); - } - else - { - keyfile_name = g_strdup(override_file); - g_message("User config pathname is %s", keyfile_name); - return; - } - } - - if (!g_key_file_load_from_dirs(config_keyfile, keyfilename, keyfiledirs, &keyfile_name, flags, &error)) - { - g_warning("Could not read cboxrc: %s, search dir = %s, filename = %s", error->message, keyfiledirs[0], keyfilename); - g_error_free(error); - } - else - { - g_message("Config pathname is %s", keyfile_name); - } -} - -int cbox_config_has_section(const char *section) -{ - return section && g_key_file_has_group(config_keyfile, section); -} - -char *cbox_config_get_string(const char *section, const char *key) -{ - return cbox_config_get_string_with_default(section, key, NULL); -} - -void cbox_config_set_string(const char *section, const char *key, const char *value) -{ - g_key_file_set_string(config_keyfile, section, key, value); -} - -char *cbox_config_permify(const char *temporary) -{ - return g_string_chunk_insert(cfg_strings, temporary); -} - -char *cbox_config_get_string_with_default(const char *section, const char *key, char *def_value) -{ - if (section && key && g_key_file_has_key(config_keyfile, section, key, NULL)) - { - gchar *tmp = g_key_file_get_string(config_keyfile, section, key, NULL); - gchar *perm = g_string_chunk_insert(cfg_strings, tmp); - g_free(tmp); - return perm; - } - else - { - return def_value ? g_string_chunk_insert(cfg_strings, def_value) : NULL; - } -} - -int cbox_config_get_int(const char *section, const char *key, int def_value) -{ - GError *error = NULL; - int result; - - if (!section || !key) - return def_value; - result = g_key_file_get_integer(config_keyfile, section, key, &error); - if (error) - { - g_error_free(error); - return def_value; - } - return result; -} - -void cbox_config_set_int(const char *section, const char *key, int value) -{ - g_key_file_set_integer(config_keyfile, section, key, value); -} - -float cbox_config_get_float(const char *section, const char *key, float def_value) -{ - GError *error = NULL; - float result; - - if (!section || !key) - return def_value; - result = g_key_file_get_double(config_keyfile, section, key, &error); - if (error) - { - g_error_free(error); - return def_value; - } - return result; -} - -void cbox_config_set_float(const char *section, const char *key, double value) -{ - g_key_file_set_double(config_keyfile, section, key, value); -} - -float cbox_config_get_gain(const char *section, const char *key, float def_value) -{ - GError *error = NULL; - float result; - - if (!section || !key) - return def_value; - result = g_key_file_get_double(config_keyfile, section, key, &error); - if (error) - { - g_error_free(error); - return def_value; - } - return pow(2.0, result / 6.0); -} - -float cbox_config_get_gain_db(const char *section, const char *key, float def_value) -{ - return cbox_config_get_gain(section, key, pow(2.0, def_value / 6.0)); -} - -void cbox_config_foreach_section(void (*process)(void *user_data, const char *section), void *user_data) -{ - gsize i, length = 0; - gchar **groups = g_key_file_get_groups (config_keyfile, &length); - if (!groups) - return; - for (i = 0; i < length; i++) - { - process(user_data, groups[i]); - } - g_strfreev(groups); -} - -void cbox_config_foreach_key(void (*process)(void *user_data, const char *key), const char *section, void *user_data) -{ - gsize i, length = 0; - gchar **keys = g_key_file_get_keys (config_keyfile, section, &length, NULL); - if (!keys) - return; - for (i = 0; i < length; i++) - { - process(user_data, keys[i]); - } - g_strfreev(keys); -} - -int cbox_config_remove_section(const char *section) -{ - return 0 != g_key_file_remove_group(config_keyfile, section, NULL); -} - -int cbox_config_remove_key(const char *section, const char *key) -{ - return 0 != g_key_file_remove_key(config_keyfile, section, key, NULL); -} - -gboolean cbox_config_save(const char *filename, GError **error) -{ - gsize len = 0; - gchar *data = g_key_file_to_data(config_keyfile, &len, error); - if (!data) - return FALSE; - - if (filename == NULL) - filename = keyfile_name; - - gboolean ok = g_file_set_contents(filename, data, len, error); - g_free(data); - return ok; -} - -struct cbox_cfgfile -{ - gchar *libname; - gchar *filename; - GKeyFile *keyfile; -}; - -struct cbox_cfgfile *cbox_cfgfile_get_by_libname(const char *name) -{ - // XXXKF implement - return NULL; -} - -struct cbox_sectref -{ - struct cbox_cfgfile *cfgfile; - gchar *section; -}; - -static struct cbox_sectref *cbox_sectref_lookup(const char *name) -{ - struct cbox_sectref *sr = g_hash_table_lookup(config_sections_hash, name); - return sr; -} - -static void cbox_sectref_keep(struct cbox_sectref *sect) -{ - gchar *tmp = g_strdup_printf("%s@%s", sect->section, sect->cfgfile->libname); - g_hash_table_insert(config_sections_hash, tmp, sect); - g_free(tmp); -} - -static void cbox_sectref_set_from_string(struct cbox_sectref *sr, const gchar *refname, struct cbox_cfgfile *default_lib) -{ - const gchar *p = strchr(refname, '@'); - if (p) - { - sr->section = g_strndup(refname, p - refname); - sr->cfgfile = cbox_cfgfile_get_by_libname(p + 1); - } - else - { - sr->section = g_strndup(refname, p - refname); - sr->cfgfile = default_lib; - } -} - -// find the section 'prefix+refname.section' in the config file - either the one referenced by sect, or refname.file -struct cbox_sectref *cbox_config_sectref(struct cbox_sectref *sect, const char *prefix, const char *refname) -{ - if (!prefix) - prefix = ""; - gchar *tmpsect = NULL; - const char *p = strchr(refname, '@'); - if (p) - tmpsect = g_strdup_printf("%s%s", prefix, refname); - else - tmpsect = g_strdup_printf("%s%s@%s", prefix, refname, sect->cfgfile->libname); - struct cbox_sectref *sr = cbox_sectref_lookup(tmpsect); - if (sr) - { - g_free(tmpsect); - return sr; - } - sr = malloc(sizeof(struct cbox_sectref)); - cbox_sectref_set_from_string(sr, tmpsect, sect ? sect->cfgfile : NULL); - g_free(tmpsect); - cbox_sectref_keep(sr); - return sr; -} - -struct cbox_sectref *cbox_config_get_sectref(struct cbox_sectref *sect, const char *prefix, const char *key) -{ - if (!key || !sect) - return NULL; - //const char *sectname = cbox_config_get_string(sect, key); - const char *sectname = cbox_config_get_string(sect->section, key); - if (!sectname) - return NULL; - return cbox_config_sectref(sect, prefix, sectname); -} - -struct cbox_sectref *cbox_config_get_sectref_n(struct cbox_sectref *sect, const char *prefix, const char *key, int index) -{ - if (!key || !sect) - return NULL; - gchar *tmp = g_strdup_printf("%s%d", key, index); - struct cbox_sectref *sr = cbox_config_get_sectref(sect, prefix, tmp); - g_free(tmp); - return sr; -} - -struct cbox_sectref *cbox_config_get_sectref_suffix(struct cbox_sectref *sect, const char *prefix, const char *key, const char *suffix) -{ - if (!key || !sect) - return NULL; - gchar *tmp = g_strdup_printf("%s%s", key, suffix ? suffix : ""); - struct cbox_sectref *sr = cbox_config_get_sectref(sect, prefix, tmp); - g_free(tmp); - return sr; -} - -void cbox_config_close() -{ - if (config_sections_hash) - { - g_hash_table_destroy(config_sections_hash); - config_sections_hash = NULL; - } - if (config_keyfile) - { - g_key_file_free(config_keyfile); - config_keyfile = NULL; - } - if (cfg_strings) - { - g_string_chunk_free(cfg_strings); - cfg_strings = NULL; - } - if (keyfile_name) - { - g_free(keyfile_name); - keyfile_name = NULL; - } -} - diff --git a/template/calfbox/config-api.h b/template/calfbox/config-api.h deleted file mode 100644 index d021cd5..0000000 --- a/template/calfbox/config-api.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_CONFIG_API_H -#define CBOX_CONFIG_API_H - -#include - -struct cbox_sectref; - -extern void cbox_config_init(const char *override_file); -extern int cbox_config_has_section(const char *section); -extern char *cbox_config_get_string(const char *section, const char *key); -extern char *cbox_config_get_string_with_default(const char *section, const char *key, char *def_value); -extern int cbox_config_get_int(const char *section, const char *key, int def_value); -extern float cbox_config_get_float(const char *section, const char *key, float def_value); -extern float cbox_config_get_gain(const char *section, const char *key, float def_value); -extern float cbox_config_get_gain_db(const char *section, const char *key, float def_value); -extern void cbox_config_foreach_section(void (*process)(void *user_data, const char *section), void *user_data); -extern void cbox_config_foreach_key(void (*process)(void *user_data, const char *key), const char *section, void *user_data); -extern char *cbox_config_permify(const char *temporary); - -extern void cbox_config_set_string(const char *section, const char *key, const char *value); -extern void cbox_config_set_int(const char *section, const char *key, int value); -extern void cbox_config_set_float(const char *section, const char *key, double value); -extern int cbox_config_remove_section(const char *section); -extern int cbox_config_remove_key(const char *section, const char *key); - -extern gboolean cbox_config_save(const char *filename, GError **error); - -extern struct cbox_sectref *cbox_config_sectref(struct cbox_sectref *def_sect, const char *prefix, const char *refname); -extern struct cbox_sectref *cbox_config_get_sectref(struct cbox_sectref *sect, const char *prefix, const char *key); -extern struct cbox_sectref *cbox_config_get_sectref_n(struct cbox_sectref *sect, const char *prefix, const char *key, int index); -extern struct cbox_sectref *cbox_config_get_sectref_suffix(struct cbox_sectref *sect, const char *prefix, const char *key, const char *suffix); - -extern void cbox_config_close(void); - -#endif diff --git a/template/calfbox/configure.ac b/template/calfbox/configure.ac deleted file mode 100644 index f0893e9..0000000 --- a/template/calfbox/configure.ac +++ /dev/null @@ -1,162 +0,0 @@ -# -*- Autoconf -*- -# Process this file with autoconf to produce a configure script. - -AC_PREREQ([2.71]) -AC_INIT([calfbox],[0.0.3],[wdev@foltman.com]) -AC_CONFIG_HEADERS([config.h]) -LT_INIT([]) -LT_LANG([C]) - -AM_INIT_AUTOMAKE(1.8) - -if test "x$prefix" = "xNONE"; then - prefix=$ac_default_prefix -fi - -# Checks for programs. -AC_PROG_CC -AC_PROG_INSTALL -PKG_PROG_PKG_CONFIG - -# Checks for headers. -AC_CHECK_INCLUDES_DEFAULT -AC_PROG_EGREP - - -# Set initial parameters -PYTHON_ENABLED="yes" -NCURSES_ENABLED="yes" -JACK_ENABLED="yes" -FLUIDSYNTH_ENABLED="yes" -LIBSMF_ENABLED="yes" -LIBUSB_ENABLED="yes" -SSE_ENABLED="no" -NEON_ENABLED="no" - -# Check options - -AC_MSG_CHECKING([whether to enable Python embedding]) -AC_ARG_WITH(python, -AS_HELP_STRING([--without-python],[disable Python embedding]), - [if test "$withval" = "no"; then PYTHON_ENABLED="no"; fi],[]) -AC_MSG_RESULT($PYTHON_ENABLED) - -AC_MSG_CHECKING([whether to enable ncurses UI]) -AC_ARG_WITH(ncurses, -AS_HELP_STRING([--without-ncurses],[disable ncurses ui]), - [if test "$withval" = "no"; then NCURSES_ENABLED="no"; fi],[]) -AC_MSG_RESULT($NCURSES_ENABLED) - -AC_MSG_CHECKING([whether to enable JACK I/O]) -AC_ARG_WITH(jack, -AS_HELP_STRING([--without-jack],[disable JACK audio and MIDI]), - [if test "$withval" = "no"; then JACK_ENABLED="no"; fi],[]) -AC_MSG_RESULT($JACK_ENABLED) - -AC_MSG_CHECKING([whether to enable Fluidsynth]) -AC_ARG_WITH(fluidsynth, -AS_HELP_STRING([--without-fluidsynth],[disable use of Fluidsynth]), - [if test "$withval" = "no"; then FLUIDSYNTH_ENABLED="no"; fi],[]) -AC_MSG_RESULT($FLUIDSYNTH_ENABLED) - -AC_MSG_CHECKING([whether to enable libsmf]) -AC_ARG_WITH(libsmf, -AS_HELP_STRING([--without-libsmf],[disable use of libsmf]), - [if test "$withval" = "no"; then LIBSMF_ENABLED="no"; fi],[]) -AC_MSG_RESULT($LIBSMF_ENABLED) - -AC_MSG_CHECKING([whether to enable libusb]) -AC_ARG_WITH(libusb, -AS_HELP_STRING([--without-libusb],[disable use of libusb]), - [if test "$withval" = "no"; then LIBUSB_ENABLED="no"; fi],[]) -AC_MSG_RESULT($LIBUSB_ENABLED) - -AC_MSG_CHECKING([whether to enable SSE (x86 family only)]) -AC_ARG_WITH(sse, -AS_HELP_STRING([--with-sse],[enable use of SSE]), - [if test "$withval" = "yes"; then SSE_ENABLED="yes"; fi],[]) -AC_MSG_RESULT($SSE_ENABLED) - -AC_MSG_CHECKING([whether to enable NEON (ARM family only)]) -AC_ARG_WITH(neon, -AS_HELP_STRING([--with-neon],[enable use of NEON]), - [if test "$withval" = "yes"; then NEON_ENABLED="yes"; fi],[]) -AC_MSG_RESULT($NEON_ENABLED) - -# Check dependencies -AC_CHECK_HEADER(uuid/uuid.h, true, AC_MSG_ERROR([libuuid header (uuid/uuid.h) is required])) -AC_CHECK_LIB(uuid, uuid_unparse, true, AC_MSG_ERROR([libuuid is required])) -PKG_CHECK_MODULES(GLIB_DEPS, glib-2.0 >= 2.6, true, AC_MSG_ERROR([libglib-2.0 is required])) -if test "$LIBUSB_ENABLED" = "yes"; then - PKG_CHECK_MODULES(LIBUSB_DEPS, libusb-1.0 >= 1.0, true, AC_MSG_ERROR([libusb-1.0 is required])) -fi -PKG_CHECK_MODULES(LIBSNDFILE_DEPS, sndfile, true, AC_MSG_ERROR([libsndfile is required])) -if test "$NCURSES_ENABLED" = "yes"; then - PKG_CHECK_MODULES(NCURSES_DEPS, ncurses, true, AC_MSG_ERROR([libncurses is required])) -fi - -if test "$FLUIDSYNTH_ENABLED" = "yes"; then - PKG_CHECK_MODULES(FLUIDSYNTH_DEPS, fluidsynth >= 1.0.8, true, AC_MSG_ERROR([fluidsynth 1.0.8 is required])) -fi - -if test "$LIBSMF_ENABLED" = "yes"; then - PKG_CHECK_MODULES(LIBSMF_DEPS, smf >= 1.3, true, AC_MSG_ERROR([libsmf 1.3 is required (libsmf.sourceforge.net)])) -fi - -if test "$JACK_ENABLED" = "yes"; then - PKG_CHECK_MODULES(JACK_DEPS, jack >= 0.116.0, true, AC_MSG_ERROR([JACK is required (or use --without-jack)])) - AC_CHECK_HEADER(jack/jack.h, true, AC_MSG_ERROR([JACK is required (or use --without-jack)])) - PKG_CHECK_MODULES(JACK_RENAME_PORT, jack >= 0.124.2 jack < 1.9.0, JACK_HAS_RENAME="yes", JACK_HAS_RENAME="no") - PKG_CHECK_MODULES(JACK2_RENAME_PORT, jack >= 1.9.11, JACK_HAS_RENAME="yes", JACK_HAS_RENAME_DUMMY="no") -fi - -if test "$PYTHON_ENABLED" = "yes"; then - PKG_CHECK_MODULES(PYTHON_DEPS, python3-embed >= 3.0, true, AC_MSG_ERROR([python 3.0 or newer is required (or use --without-python)])) -fi - -# Generate Automake conditionals -AM_CONDITIONAL(USE_PYTHON, test "$PYTHON_ENABLED" = "yes") -AM_CONDITIONAL(USE_NCURSES, test "$NCURSES_ENABLED" = "yes") -AM_CONDITIONAL(USE_JACK, test "$JACK_ENABLED" = "yes") -AM_CONDITIONAL(USE_FLUIDSYNTH, test "$FLUIDSYNTH_ENABLED" = "yes") -AM_CONDITIONAL(USE_LIBSMF, test "$LIBSMF_ENABLED" = "yes") -AM_CONDITIONAL(USE_LIBUSB, test "$LIBUSB_ENABLED" = "yes") -AM_CONDITIONAL(USE_SSE, test "$SSE_ENABLED" = "yes") -AM_CONDITIONAL(USE_NEON, test "$NEON_ENABLED" = "yes") - -# Generate config.h conditionals -if test "$PYTHON_ENABLED" = "yes"; then - AC_DEFINE(USE_PYTHON, 1, [Python will be included]) -fi -if test "$NCURSES_ENABLED" = "yes"; then - AC_DEFINE(USE_NCURSES, 1, [ncurses will be included]) -fi -if test "$JACK_ENABLED" = "yes"; then - AC_DEFINE(USE_JACK, 1, [JACK I/O will be included]) -fi -if test "$JACK_HAS_RENAME" = "yes"; then - AC_DEFINE(JACK_HAS_RENAME, 1, [JACK function jack_port_rename should be used instead of jack_port_set_name]) -fi -if test "$FLUIDSYNTH_ENABLED" = "yes"; then - AC_DEFINE(USE_FLUIDSYNTH, 1, [Fluidsynth will be included]) -fi -if test "$LIBSMF_ENABLED" = "yes"; then - AC_DEFINE(USE_LIBSMF, 1, [libsmf will be used]) -fi -if test "$LIBUSB_ENABLED" = "yes"; then - AC_DEFINE(USE_LIBUSB, 1, [libusb will be used]) -fi -if test "$LIBUSB_ENABLED" = "no" && test "$JACK_ENABLED" = "no"; then - AC_MSG_ERROR([Neither JACK nor libusb are enabled, at least one is required]) -fi -if test "$SSE_ENABLED" = "yes"; then - AC_DEFINE(USE_SSE, 1, [x86 Streaming SIMD Extensions will be used]) -fi -if test "$NEON_ENABLED" = "yes"; then - AC_DEFINE(USE_NEON, 1, [ARM NEON SIMD Extensions will be used]) -fi - -# Generate files -AC_CONFIG_FILES([Makefile]) - -AC_OUTPUT diff --git a/template/calfbox/delay.c b/template/calfbox/delay.c deleted file mode 100644 index c48ad7f..0000000 --- a/template/calfbox/delay.c +++ /dev/null @@ -1,139 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config.h" -#include "config-api.h" -#include "dspmath.h" -#include "module.h" -#include -#include -#include -#include -#include -#include -#include - -#define MAX_DELAY_LENGTH 65536 - -struct delay_params -{ - float time; - float wet_dry, fb_amt; -}; - -struct delay_module -{ - struct cbox_module module; - - float storage[MAX_DELAY_LENGTH][2]; - struct delay_params *params; - int pos; -}; - -#define MODULE_PARAMS delay_params - -MODULE_PROCESSCMD_FUNCTION(delay) -{ - struct delay_module *m = (struct delay_module *)ct->user_data; - - EFFECT_PARAM("/time", "f", time, double, , 1, 1000) else - EFFECT_PARAM("/fb_amt", "f", fb_amt, double, , 0, 1) else - EFFECT_PARAM("/wet_dry", "f", wet_dry, double, , 0, 1) else - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - return cbox_execute_on(fb, NULL, "/time", "f", error, m->params->time) - && cbox_execute_on(fb, NULL, "/fb_amt", "f", error, m->params->fb_amt) - && cbox_execute_on(fb, NULL, "/wet_dry", "f", error, m->params->wet_dry) - && CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error) - ; - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); - return TRUE; -} - -void delay_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) -{ - // struct delay_module *m = (struct delay_module *)module; -} - -void delay_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) -{ - struct delay_module *m = (struct delay_module *)module; - - int pos = m->pos; - int dv = m->params->time * m->module.srate / 1000.0; - float dryamt = 1 - m->params->wet_dry; - float wetamt = m->params->wet_dry; - float fbamt = m->params->fb_amt; - - for (int i = 0; i < CBOX_BLOCK_SIZE; i++) - { - float dry[2] = { inputs[0][i], inputs[1][i] }; - float *delayed = &m->storage[pos & (MAX_DELAY_LENGTH - 1)][0]; - - float wet[2] = { dryamt * dry[0] + wetamt * delayed[0], dryamt * dry[1] + wetamt * delayed[1] }; - float fb[2] = { dry[0] + fbamt * delayed[0], dry[1] + fbamt * delayed[1] }; - outputs[0][i] = sanef(wet[0]); - outputs[1][i] = sanef(wet[1]); - - float *wcell = &m->storage[(pos + dv) & (MAX_DELAY_LENGTH - 1)][0]; - wcell[0] = sanef(fb[0]); - wcell[1] = sanef(fb[1]); - pos++; - } - m->pos = pos; -} - -MODULE_SIMPLE_DESTROY_FUNCTION(delay) - -MODULE_CREATE_FUNCTION(delay) -{ - static int inited = 0; - int i; - if (!inited) - { - inited = 1; - } - - struct delay_module *m = malloc(sizeof(struct delay_module)); - CALL_MODULE_INIT(m, 2, 2, delay); - struct delay_params *p = malloc(sizeof(struct delay_params)); - m->params = p; - m->module.process_event = delay_process_event; - m->module.process_block = delay_process_block; - m->pos = 0; - p->time = cbox_config_get_float(cfg_section, "delay", 250); - p->wet_dry = cbox_config_get_float(cfg_section, "wet_dry", 0.3); - p->fb_amt = cbox_config_get_gain_db(cfg_section, "feedback_gain", -12.f); - for (i = 0; i < MAX_DELAY_LENGTH; i++) - m->storage[i][0] = m->storage[i][1] = 0.f; - - return &m->module; -} - -struct cbox_module_keyrange_metadata delay_keyranges[] = { -}; - -struct cbox_module_livecontroller_metadata delay_controllers[] = { -}; - -DEFINE_MODULE(delay, 2, 2) - diff --git a/template/calfbox/distortion.c b/template/calfbox/distortion.c deleted file mode 100644 index 8251e89..0000000 --- a/template/calfbox/distortion.c +++ /dev/null @@ -1,138 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config.h" -#include "config-api.h" -#include "dspmath.h" -#include "module.h" -#include -#include -#include -#include -#include -#include -#include - -#define MODULE_PARAMS distortion_params - -struct distortion_params -{ - float drive; - float shape; -}; - -struct distortion_module -{ - struct cbox_module module; - - struct distortion_params *params, *old_params; -}; - -gboolean distortion_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct distortion_module *m = (struct distortion_module *)ct->user_data; - - EFFECT_PARAM("/drive", "f", drive, double, dB2gain_simple, -36, 36) else - EFFECT_PARAM("/shape", "f", shape, double, , -1, 2) else - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - return cbox_execute_on(fb, NULL, "/drive", "f", error, gain2dB_simple(m->params->drive)) - && cbox_execute_on(fb, NULL, "/shape", "f", error, m->params->shape) - && CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error) - ; - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); - return TRUE; -} - -void distortion_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) -{ - // struct distortion_module *m = module->user_data; -} - -void distortion_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) -{ - struct distortion_module *m = module->user_data; - - if (m->params != m->old_params) - { - // update calculated values - } - - float drive = m->params->drive; - float shape = m->params->shape; - - float a0 = shape; - float a1 = -2 * shape - 0.5; - float a2 = 1.5 + shape; - - float post = pow(drive, -0.7); - - for (int i = 0; i < CBOX_BLOCK_SIZE; i++) - { - for (int c = 0; c < 2; c++) - { - float val = inputs[c][i]; - - val *= drive; - - if (fabs(val) > 1.0) - val = (val > 0) ? 1 : -1; - else - val = a0 * val * val * val * val * val + a1 * val * val * val + a2 * val; - - outputs[c][i] = val * post; - } - } -} - -MODULE_SIMPLE_DESTROY_FUNCTION(distortion) - -MODULE_CREATE_FUNCTION(distortion) -{ - static int inited = 0; - if (!inited) - { - inited = 1; - } - - struct distortion_module *m = malloc(sizeof(struct distortion_module)); - CALL_MODULE_INIT(m, 2, 2, distortion); - m->module.process_event = distortion_process_event; - m->module.process_block = distortion_process_block; - struct distortion_params *p = malloc(sizeof(struct distortion_params)); - p->drive = cbox_config_get_gain_db(cfg_section, "drive", 0.f); - p->shape = cbox_config_get_gain_db(cfg_section, "shape", 0.f); - m->params = p; - m->old_params = NULL; - - return &m->module; -} - - -struct cbox_module_keyrange_metadata distortion_keyranges[] = { -}; - -struct cbox_module_livecontroller_metadata distortion_controllers[] = { -}; - -DEFINE_MODULE(distortion, 2, 2) - diff --git a/template/calfbox/dom.c b/template/calfbox/dom.c deleted file mode 100644 index b512623..0000000 --- a/template/calfbox/dom.c +++ /dev/null @@ -1,372 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2012 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "cmd.h" -#include "errors.h" -#include "dom.h" - -#include -#include -#include -#include - -static GHashTable *class_name_hash = NULL; - -struct cbox_class_per_document -{ - GList *instances; -}; - -struct cbox_document -{ - GHashTable *classes_per_document; - GHashTable *services_per_document; - GHashTable *uuids_per_document; - struct cbox_command_target cmd_target; - int item_ctr; - uint64_t generation_ctr; -}; - -//////////////////////////////////////////////////////////////////////////////////////// - -void cbox_dom_init() -{ - class_name_hash = g_hash_table_new(g_str_hash, g_str_equal); -} - -void cbox_dom_close() -{ - g_hash_table_destroy(class_name_hash); -} - -//////////////////////////////////////////////////////////////////////////////////////// - -struct cbox_class *cbox_class_find_by_name(const char *name) -{ - assert(class_name_hash != NULL); - return g_hash_table_lookup(class_name_hash, name); -} - -void cbox_class_register(struct cbox_class *class_ptr) -{ - assert(class_name_hash != NULL); - g_hash_table_insert(class_name_hash, (gpointer)class_ptr->name, class_ptr); -} - -static struct cbox_class_per_document *get_cpd_for_class(struct cbox_document *doc, struct cbox_class *class_ptr) -{ - struct cbox_class_per_document *p = g_hash_table_lookup(doc->classes_per_document, class_ptr); - if (p != NULL) - return p; - p = malloc(sizeof(struct cbox_class_per_document)); - p->instances = NULL; - g_hash_table_insert(doc->classes_per_document, class_ptr, p); - return p; -} - -//////////////////////////////////////////////////////////////////////////////////////// - -void cbox_uuid_clear(struct cbox_uuid *uuid) -{ - uuid_clear(uuid->uuid); -} - -guint cbox_uuid_hash(gconstpointer v) -{ - char buf[40]; - uuid_unparse_lower(((struct cbox_uuid *)v)->uuid, buf); - return g_str_hash(buf); -} - -gboolean cbox_uuid_equal(gconstpointer v1, gconstpointer v2) -{ - const struct cbox_uuid *p1 = v1; - const struct cbox_uuid *p2 = v2; - - return !uuid_compare(p1->uuid, p2->uuid); -} - -void cbox_uuid_copy(struct cbox_uuid *vto, const struct cbox_uuid *vfrom) -{ - uuid_copy(vto->uuid, vfrom->uuid); -} - -gboolean cbox_uuid_report_as(struct cbox_uuid *uuid, const char *cmd, struct cbox_command_target *fb, GError **error) -{ - if (!fb) - return TRUE; - return cbox_execute_on(fb, NULL, cmd, "u", error, uuid->uuid); -} - -gboolean cbox_uuid_report(struct cbox_uuid *uuid, struct cbox_command_target *fb, GError **error) -{ - return cbox_uuid_report_as(uuid, "/uuid", fb, error); -} - -gboolean cbox_uuid_fromstring(struct cbox_uuid *uuid, const char *str, GError **error) -{ - if (uuid_parse(str, uuid->uuid)) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Malformed UUID: '%s'", str); - return FALSE; - } - return TRUE; -} - -void cbox_uuid_tostring(struct cbox_uuid *uuid, char str[40]) -{ - uuid_unparse(uuid->uuid, str); -} - -void cbox_uuid_generate(struct cbox_uuid *uuid) -{ - uuid_generate(uuid->uuid); -} - -//////////////////////////////////////////////////////////////////////////////////////// - -void cbox_object_register_instance(struct cbox_document *doc, struct cbox_objhdr *obj) -{ - assert(obj != NULL); - - struct cbox_class_per_document *cpd = get_cpd_for_class(doc, obj->class_ptr); - cpd->instances = g_list_prepend(cpd->instances, obj); - obj->owner = doc; - obj->link_in_document = cpd->instances; - g_hash_table_insert(obj->owner->uuids_per_document, &obj->instance_uuid, obj); -} - -struct cbox_command_target *cbox_object_get_cmd_target(struct cbox_objhdr *hdr_ptr) -{ - if (!hdr_ptr->class_ptr->getcmdtargetfunc) - return NULL; - return hdr_ptr->class_ptr->getcmdtargetfunc(hdr_ptr); -} - -gboolean cbox_object_try_default_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, const char *subcmd, gboolean *result, GError **error) -{ - // XXXKF this assumes objhdr ptr == object ptr - needs to add the header offset in cmd target? - struct cbox_objhdr *obj = ct->user_data; - if (!strcmp(subcmd, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_object_default_status(obj, fb, error)) - { - *result = FALSE; - return TRUE; - } - return FALSE; - } - if (!strcmp(subcmd, "/delete") && !strcmp(cmd->arg_types, "")) - { - cbox_object_destroy(obj); - *result = TRUE; - return TRUE; - } - if (!strcmp(subcmd, "/get_uuid") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - { - *result = FALSE; - return TRUE; - } - - *result = cbox_uuid_report(&obj->instance_uuid, fb, error); - return TRUE; - } - if (!strcmp(subcmd, "/get_class_name") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - { - *result = FALSE; - return TRUE; - } - *result = cbox_execute_on(fb, NULL, "/class_name", "s", error, obj->class_ptr->name); - return TRUE; - } - return FALSE; -} - -gboolean cbox_object_default_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - gboolean result = FALSE; - if (cbox_object_try_default_process_cmd(ct, fb, cmd, cmd->command, &result, error)) - return result; - struct cbox_objhdr *obj = ct->user_data; - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown combination of target path and argument: '%s', '%s' for object class '%s'", cmd->command, cmd->arg_types, obj->class_ptr->name); - return FALSE; -} - -gboolean cbox_object_default_status(struct cbox_objhdr *objhdr, struct cbox_command_target *fb, GError **error) -{ - char buf[40]; - uuid_unparse(objhdr->instance_uuid.uuid, buf); - return cbox_execute_on(fb, NULL, "/uuid", "s", error, buf); -} - -void cbox_object_destroy(struct cbox_objhdr *hdr_ptr) -{ - struct cbox_class_per_document *cpd = get_cpd_for_class(hdr_ptr->owner, hdr_ptr->class_ptr); - cpd->instances = g_list_delete_link(cpd->instances, hdr_ptr->link_in_document); - hdr_ptr->link_in_document = NULL; - g_hash_table_remove(hdr_ptr->owner->uuids_per_document, &hdr_ptr->instance_uuid); - - hdr_ptr->class_ptr->destroyfunc(hdr_ptr); -} - -//////////////////////////////////////////////////////////////////////////////////////// - -static gboolean document_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - char *uuid; - const char *subcommand; - if (!strcmp(cmd->command, "/dump") && !strcmp(cmd->arg_types, "")) - { - struct cbox_document *doc = ct->user_data; - cbox_document_dump(doc); - return TRUE; - } - if (cbox_parse_path_part_str(cmd, "/uuid/", &subcommand, &uuid, error)) - { - struct cbox_document *doc = ct->user_data; - if (!subcommand) - return FALSE; - struct cbox_objhdr *obj = cbox_document_get_object_by_text_uuid(doc, uuid, NULL, error); - g_free(uuid); - if (!obj) - return FALSE; - struct cbox_command_target *ct2 = cbox_object_get_cmd_target(obj); - return cbox_execute_sub(ct2, fb, cmd, subcommand, error); - } - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown combination of target path and argument: '%s', '%s'", cmd->command, cmd->arg_types); - return FALSE; -} - -struct cbox_document *cbox_document_new() -{ - struct cbox_document *res = malloc(sizeof(struct cbox_document)); - res->classes_per_document = g_hash_table_new_full(NULL, NULL, NULL, g_free); - res->services_per_document = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); - res->uuids_per_document = g_hash_table_new(cbox_uuid_hash, cbox_uuid_equal); - res->cmd_target.process_cmd = document_process_cmd; - res->cmd_target.user_data = res; - res->item_ctr = 0; - res->generation_ctr = 1000; // start with non-zero value just to spot invalid values more easily - - return res; -} - -struct cbox_command_target *cbox_document_get_cmd_target(struct cbox_document *doc) -{ - return &doc->cmd_target; -} - -struct cbox_objhdr *cbox_document_get_service(struct cbox_document *document, const char *name) -{ - return g_hash_table_lookup(document->services_per_document, name); -} - -void cbox_document_set_service(struct cbox_document *document, const char *name, struct cbox_objhdr *obj) -{ - g_hash_table_insert(document->services_per_document, g_strdup(name), obj); -} - -struct cbox_objhdr *cbox_document_get_object_by_uuid(struct cbox_document *doc, const struct cbox_uuid *uuid) -{ - return g_hash_table_lookup(doc->uuids_per_document, uuid); -} - -struct cbox_objhdr *cbox_document_get_object_by_text_uuid(struct cbox_document *doc, const char *uuid, const struct cbox_class *class_ptr, GError **error) -{ - struct cbox_uuid uuidv; - if (!cbox_uuid_fromstring(&uuidv, uuid, error)) - return NULL; - struct cbox_objhdr *obj = cbox_document_get_object_by_uuid(doc, &uuidv); - if (!obj) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "UUID not found: '%s'", uuid); - return NULL; - } - if (class_ptr && !cbox_class_is_a(obj->class_ptr, class_ptr)) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unexpected object type '%s' for UUID '%s' (expected '%s')", obj->class_ptr->name, uuid, class_ptr->name); - return NULL; - } - return obj; -} - -static void iter_func(gpointer key, gpointer value, gpointer doc_) -{ -#ifndef NDEBUG - struct cbox_document *doc = (struct cbox_document *)doc_; -#endif - struct cbox_class *class_ptr = key; - struct cbox_class_per_document *cpd = value; - int first = 1; - printf("Class %s: ", class_ptr->name); - GList *l = cpd->instances; - while(l) { - if (!first) - printf(", "); - printf("%p", l->data); - fflush(stdout); - struct cbox_objhdr *hdr = (struct cbox_objhdr *)l->data; - char buf[40]; - uuid_unparse(hdr->instance_uuid.uuid, buf); - printf("[%s]", buf); - fflush(stdout); - assert(cbox_document_get_object_by_uuid(doc, &hdr->instance_uuid)); - l = l->next; - first = 0; - } - if (first) - printf(""); - printf("\n"); -} - -static void iter_func2(gpointer key, gpointer value, gpointer document) -{ - struct cbox_objhdr *oh = value; - char buf[40]; - uuid_unparse(oh->instance_uuid.uuid, buf); - printf("Service %s: %p", (const char *)key, value); - fflush(stdout); - printf("[%s]", buf); - fflush(stdout); - printf(" (%s)\n", oh->class_ptr->name); -} - -void cbox_document_dump(struct cbox_document *document) -{ - g_hash_table_foreach(document->classes_per_document, iter_func, document); - g_hash_table_foreach(document->services_per_document, iter_func2, document); -} - -uint64_t cbox_document_get_next_stamp(struct cbox_document *document) -{ - return document->generation_ctr; -} - -void cbox_document_destroy(struct cbox_document *document) -{ - g_hash_table_destroy(document->classes_per_document); - g_hash_table_destroy(document->services_per_document); - g_hash_table_destroy(document->uuids_per_document); - free(document); -} - - diff --git a/template/calfbox/dom.h b/template/calfbox/dom.h deleted file mode 100644 index 4f270bf..0000000 --- a/template/calfbox/dom.h +++ /dev/null @@ -1,155 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2012 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_DOM_H -#define CBOX_DOM_H - -#include -#include -#include - -struct cbox_command_target; -struct cbox_osc_command; -struct cbox_objhdr; -struct cbox_document; -struct GList; - -struct cbox_uuid -{ - uuid_t uuid; -}; - -extern void cbox_uuid_clear(struct cbox_uuid *uuid); -extern guint cbox_uuid_hash(gconstpointer v); -extern void cbox_uuid_copy(struct cbox_uuid *vto, const struct cbox_uuid *vfrom); -extern gboolean cbox_uuid_equal(gconstpointer v1, gconstpointer v2); -extern gboolean cbox_uuid_report(struct cbox_uuid *uuid, struct cbox_command_target *fb, GError **error); -extern gboolean cbox_uuid_report_as(struct cbox_uuid *uuid, const char *cmd, struct cbox_command_target *fb, GError **error); -extern gboolean cbox_uuid_fromstring(struct cbox_uuid *uuid, const char *str, GError **error); -extern void cbox_uuid_tostring(struct cbox_uuid *uuid, char str[40]); -extern void cbox_uuid_generate(struct cbox_uuid *uuid); - -struct cbox_class -{ - struct cbox_class *parent; - const char *name; - int hdr_offset; - void (*destroyfunc)(struct cbox_objhdr *objhdr); - struct cbox_command_target *(*getcmdtargetfunc)(struct cbox_objhdr *objhdr); -}; - -extern struct cbox_class *cbox_class_find_by_name(const char *name); -extern void cbox_class_register(struct cbox_class *class_ptr); - -struct cbox_objhdr -{ - struct cbox_class *class_ptr; - struct cbox_document *owner; - void *link_in_document; - struct cbox_uuid instance_uuid; - uint64_t stamp; -}; - -static inline int cbox_class_is_a(const struct cbox_class *c1, const struct cbox_class *c2) -{ - while(c1 != c2 && c1->parent) - c1 = c1->parent; - return c1 == c2; -} - -extern void cbox_object_register_instance(struct cbox_document *doc, struct cbox_objhdr *obj); -extern struct cbox_command_target *cbox_object_get_cmd_target(struct cbox_objhdr *hdr_ptr); -extern gboolean cbox_object_try_default_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, const char *subcmd, gboolean *result, GError **error); -extern gboolean cbox_object_default_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error); -extern gboolean cbox_object_default_status(struct cbox_objhdr *objhdr, struct cbox_command_target *fb, GError **error); - -extern void cbox_object_destroy(struct cbox_objhdr *hdr_ptr); - -extern struct cbox_document *cbox_document_new(void); -extern void cbox_document_dump(struct cbox_document *); -extern struct cbox_command_target *cbox_document_get_cmd_target(struct cbox_document *); -extern struct cbox_objhdr *cbox_document_get_service(struct cbox_document *doc, const char *name); -extern void cbox_document_set_service(struct cbox_document *doc, const char *name, struct cbox_objhdr *hdr_ptr); -extern struct cbox_objhdr *cbox_document_get_object_by_uuid(struct cbox_document *doc, const struct cbox_uuid *uuid); -extern struct cbox_objhdr *cbox_document_get_object_by_text_uuid(struct cbox_document *doc, const char *uuid, const struct cbox_class *class_ptr, GError **error); -extern uint64_t cbox_document_get_next_stamp(struct cbox_document *doc); -extern void cbox_document_destroy(struct cbox_document *); - -extern void cbox_dom_init(void); -extern void cbox_dom_close(void); - -// must be the first field in the object-compatible struct -#define CBOX_OBJECT_HEADER() \ - struct cbox_objhdr _obj_hdr; - -#define CBOX_CLASS(class) CBOX_CLASS_##class - -#define CBOX_EXTERN_CLASS(class) \ - extern struct cbox_class CBOX_CLASS(class); - -#define CBOX_GET_DOCUMENT(obj) \ - ((obj)->_obj_hdr.owner) - -#define CBOX_STAMP(obj) \ - ((obj)->_obj_hdr.stamp = cbox_document_get_next_stamp(CBOX_GET_DOCUMENT(obj))) - -#define CBOX_DELETE(obj) \ - ((obj) && (cbox_object_destroy(&(obj)->_obj_hdr), 1)) - -#define CBOX_IS_A(obj, class) \ - ((obj) && cbox_class_is_a((obj)->_obj_hdr.class_ptr, &CBOX_CLASS(class))) - -#define CBOX_OBJECT_HEADER_INIT(self, class, document) \ - do { \ - (self)->_obj_hdr.class_ptr = &CBOX_CLASS_##class; \ - (self)->_obj_hdr.owner = (document); \ - (self)->_obj_hdr.link_in_document = NULL; \ - (self)->_obj_hdr.stamp = cbox_document_get_next_stamp(document); \ - uuid_generate((self)->_obj_hdr.instance_uuid.uuid); \ - } while(0) - -#define CBOX_OBJECT_REGISTER(self) \ - (cbox_object_register_instance((self)->_obj_hdr.owner, &(self)->_obj_hdr)) - -#define CBOX_OBJECT_DEFAULT_STATUS(self, fb, error) \ - (cbox_object_default_status(&(self)->_obj_hdr, (fb), (error))) - -#define CBOX_CLASS_DEFINITION_ROOT(class) \ - static void class##_destroyfunc(struct cbox_objhdr *hdr_ptr); \ - static struct cbox_command_target *class##_getcmdtarget(struct cbox_objhdr *hdr) { \ - return &(((struct class *)hdr)->cmd_target);\ - }; \ - struct cbox_class CBOX_CLASS_##class = { \ - .parent = NULL, \ - .name = #class, \ - .hdr_offset = offsetof(struct class, _obj_hdr), \ - .destroyfunc = class##_destroyfunc, \ - .getcmdtargetfunc = class##_getcmdtarget \ - }; \ - -#define CBOX_RETURN_OBJECT(result) \ - return &(result)->_obj_hdr - -// Convert header to object, regardless of the relative position of the header. -#define CBOX_H2O(hdr) \ - (void *)(((char *)(hdr)) - (hdr)->class_ptr->hdr_offset) - -#define CBOX_O2H(obj) \ - (&(*(obj))._obj_hdr) - -#endif diff --git a/template/calfbox/py/drum_pattern_editor.py b/template/calfbox/drum_pattern_editor.py similarity index 100% rename from template/calfbox/py/drum_pattern_editor.py rename to template/calfbox/drum_pattern_editor.py diff --git a/template/calfbox/py/drumkit_editor.py b/template/calfbox/drumkit_editor.py similarity index 100% rename from template/calfbox/py/drumkit_editor.py rename to template/calfbox/drumkit_editor.py diff --git a/template/calfbox/drvjunk/miditest.py b/template/calfbox/drvjunk/miditest.py deleted file mode 100644 index a670167..0000000 --- a/template/calfbox/drvjunk/miditest.py +++ /dev/null @@ -1,152 +0,0 @@ -import array -import binascii -import usb.core -import usb.util -import time - -class USBMIDIConfiguration: - def __init__(self, cfg, ifno, ifalt): - self.cfg = cfg - self.ifno = ifno - self.ifalt = ifalt - def __str__(self): - return "cfg=%d ifno=%d ifalt=%d" % (self.cfg, self.ifno, self.ifalt) - def __repr__(self): - return "USBMIDIConfiguration(%d,%d,%d)" % (self.cfg, self.ifno, self.ifalt) - -class USBMIDIDeviceDescriptor: - def __init__(self, vendorID, productID, interfaces = None): - self.vendorID = vendorID - self.productID = productID - if interfaces is None: - self.interfaces = [] - else: - self.interfaces = interfaces - def add_interface(self, config, ifno, ifalt): - self.interfaces.append(USBMIDIConfiguration(config, ifno, ifalt)) - def has_interfaces(self): - return len(self.interfaces) - def __str__(self): - return "vid=%04x pid=%04x" % (self.vendorID, self.productID) - def __repr__(self): - return "USBMIDIDeviceDescriptor(0x%04x, 0x%04x, %s)" % (self.vendorID, self.productID, self.interfaces) - -class USBMIDI: - cin_sizes = [None, None, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1] - def __init__(self, mididev, midicfg, debug = False): - dev = usb.core.find(idVendor = mididev.vendorID, idProduct = mididev.productID) - self.dev = dev - intf = None - for cfgo in dev: - if cfgo.bConfigurationValue == midicfg.cfg: - cfgo.set() - intf = cfgo[(midicfg.ifno, midicfg.ifalt)] - if not intf: - raise ValueError, "Configuration %d not found" % midicfg.cfg - print intf.bNumEndpoints - self.epIn = None - self.epOut = None - for ep in intf: - if debug: - print "endpoint %x" % ep.bEndpointAddress - if ep.bEndpointAddress > 0x80: - if self.epIn is None: - self.epIn = ep - else: - if self.epOut is None: - self.epOut = ep - - def read(self): - try: - data = self.epIn.read(self.epIn.wMaxPacketSize) - if data is None: - return None - return array.array('B', data) - except usb.core.USBError, e: - return None - - def encode(self, port, msg): - a = array.array('B') - a.append(16 * port + (msg[0] >> 4)) - a.fromlist(msg) - return a - - def write(self, data): - self.epOut.write(data) - - def parse(self, data): - i = 0 - msgs = [] - while i < len(data): - if data[i] == 0: - break - cin, cable_id = data[i] & 15, data[i] >> 4 - msgs.append(data[i + 1 : i + 1 + KeyRig25.cin_sizes[cin]]) - i += 4 - return msgs - - @staticmethod - def findall(vendorID = None, productID = None, debug = False): - dev_list = [] - devices = usb.core.find(find_all = True) - for dev in devices: - if vendorID is not None and dev.idVendor != vendorID: - continue - if productID is not None and dev.idProduct != productID: - continue - thisdev = USBMIDIDeviceDescriptor(dev.idVendor, dev.idProduct) - if debug: - print "Device %04x:%04x, class %d" % (dev.idVendor, dev.idProduct, dev.bDeviceClass) - if dev.bDeviceClass == 0: # device defined at interface level - for cfg in dev: - if debug: - print "Configuration ", cfg.bConfigurationValue - for intf in cfg: - if debug: - print "Interface %d alternate-setting %d" % (intf.bInterfaceNumber, intf.bAlternateSetting) - print "Class %d subclass %d" % (intf.bInterfaceClass, intf.bInterfaceSubClass) - if intf.bInterfaceClass == 1 and intf.bInterfaceSubClass == 3: - if debug: - print "(%d,%d,%d): This is USB MIDI" % (cfg.bConfigurationValue, intf.bInterfaceNumber, intf.bAlternateSetting) - thisdev.add_interface(cfg.bConfigurationValue, intf.bInterfaceNumber, intf.bAlternateSetting) - if thisdev.has_interfaces(): - dev_list.append(thisdev) - return dev_list - - #print devices - -class KnownUSBMIDI(USBMIDI): - def __init__(self, vendorID, productID): - devlist = USBMIDI.findall(vendorID, productID, debug = False) - if not devlist: - raise ValueError - USBMIDI.__init__(self, devlist[0], devlist[0].interfaces[0]) - -class KeyRig25(KnownUSBMIDI): - def __init__(self): - KnownUSBMIDI.__init__(self, vendorID = 0x763, productID = 0x115) - -class XMidi2x2(KnownUSBMIDI): - def __init__(self): - KnownUSBMIDI.__init__(self, vendorID = 0x41e, productID = 0x3f08) - -class LexiconOmega(KnownUSBMIDI): - def __init__(self): - KnownUSBMIDI.__init__(self, vendorID = 0x1210, productID = 2) - -print USBMIDI.findall() -xmidi = XMidi2x2() -xmidi.write(xmidi.encode(1, [0x90, 36, 100])) -xmidi.write(xmidi.encode(1, [0x80, 36, 100])) - -#krig = KeyRig25() -krig = LexiconOmega() -while True: - data = krig.read() - if data is not None: - decoded = krig.parse(data) - reencoded = array.array('B') - for msg in decoded: - reencoded.extend(xmidi.encode(1, list(msg))) - xmidi.write(reencoded) - print decoded diff --git a/template/calfbox/drvjunk/omega.c b/template/calfbox/drvjunk/omega.c deleted file mode 100644 index 3611bb1..0000000 --- a/template/calfbox/drvjunk/omega.c +++ /dev/null @@ -1,255 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -// interface 1 altsetting 1 endpoint 01 (out) bits 16 channels 2 mps 192 -// interface 2 altsetting 1 endpoint 83 (in) bits 16 channels 2 mps 192 - -int samples_captured = 0; -int samples_played = 0; - -static struct libusb_context *usbctx; -static int omega_timeout = 1000; - -int epOUT = 0x01, epIN = 0x83; - -void init_usb() -{ - libusb_init(&usbctx); - libusb_set_debug(usbctx, 3); -} - -struct libusb_device_handle *open_omega() -{ - struct libusb_device_handle *handle; - handle = libusb_open_device_with_vid_pid(usbctx, 0x1210, 0x0002); - if (!handle) - { - printf("Lexicon Omega not found after reset.\n"); - return NULL; - } - if (libusb_set_configuration(handle, 1)) - { - libusb_close(handle); - return NULL; - } - - if (libusb_claim_interface(handle, 1)) - goto error; - if (libusb_claim_interface(handle, 2)) - goto error; - if (libusb_claim_interface(handle, 7)) - goto error; - if (libusb_set_interface_alt_setting(handle, 1, 1)) - goto error; - if (libusb_set_interface_alt_setting(handle, 2, 1)) - goto error; - - return handle; - -error: - libusb_close(handle); - return NULL; -} - -#define EP_CONTROL_UNDEFINED 0 -#define SAMPLING_FREQ_CONTROL 1 -#define PITCH_CONTROL 2 - -#define SET_CUR 0x01 -#define GET_CUR 0x81 -#define SET_MIN 0x02 -#define GET_MIN 0x82 -#define SET_MAX 0x03 -#define GET_MAX 0x83 -#define SET_RES 0x04 -#define GET_RES 0x84 -#define SET_MEM 0x05 -#define GET_MEM 0x85 -#define GET_STAT 0xFF - -int configure_omega(struct libusb_device_handle *h, int sample_rate) -{ - uint8_t freq_data[3]; - freq_data[0] = sample_rate & 0xFF; - freq_data[1] = (sample_rate & 0xFF00) >> 8; - freq_data[2] = (sample_rate & 0xFF0000) >> 16; - if (libusb_control_transfer(h, 0x22, 0x01, 256, epOUT, freq_data, 3, omega_timeout) != 3) - return -1; - if (libusb_control_transfer(h, 0x22, 0x01, 256, epIN, freq_data, 3, omega_timeout) != 3) - return -1; -// libusb_control_transfer(dev, 0x22, 0x01, - return 0; -} - -// 192 bytes = 1ms@48000 (48 samples) - -#define NUM_PLAY_BUFS 2 -#define PLAY_PACKET_COUNT 2 -#define PLAY_PACKET_SIZE 192 - -static float phase = 0; -static int phase2 = 0; - -static int desync = 0; -static int samples_sent = 0; - -static int srate = 48000; - -void play_callback(struct libusb_transfer *transfer) -{ - int i; - //printf("Play Callback! %d %p status %d\n", (int)transfer->length, transfer->buffer, (int)transfer->status); - - samples_played += transfer->length / 4; - int nsamps = srate / 1000; - if (desync >= 1000 * transfer->num_iso_packets && nsamps < PLAY_PACKET_SIZE/4) - nsamps++; - // printf("desync = %d nsamps = %d!\n", desync, nsamps); - int tlen = 0; - for (i = 0; i < transfer->num_iso_packets; i++) - { - tlen += transfer->iso_packet_desc[i].actual_length; - if (transfer->iso_packet_desc[i].status) - printf("ISO error: index = %d i = %d status = %d\n", (int)transfer->user_data, i, transfer->iso_packet_desc[i].status); - } - // printf("actual length = %d!\n", tlen); - - transfer->length = nsamps * transfer->num_iso_packets * 4; - libusb_set_iso_packet_lengths(transfer, nsamps * 4); - - desync += transfer->num_iso_packets * srate; - desync -= tlen / 4 * 1000; - - int16_t *data = (int16_t*)transfer->buffer; - for (i = 0; i < transfer->length / 4; i ++) - { - float v = 16000 * sin(phase); - //phase += (phase2 & 4096) ? 0.02 : 0.04; - phase += (phase2 & 16384) ? 0.04: 0.02; - //phase += 0.2 * cos(phase2 / 16384.0); - phase2++; - if (phase > 2 * M_PI) - phase -= 2 * M_PI; - int vi = (int)v; - data[i * 2] = vi; - data[i * 2 + 1] = vi; - } - libusb_submit_transfer(transfer); -} - -void play_stuff(struct libusb_device_handle *h, int index) -{ - struct libusb_transfer *t; - int i; - int packets = PLAY_PACKET_COUNT; - t = libusb_alloc_transfer(packets); - int tsize = srate * 4 / 1000; - uint8_t *buf = (uint8_t *)malloc(PLAY_PACKET_SIZE*packets); - //int ssf = 0; - - for (i = 0; i < tsize * packets; i++) - buf[i] = 0; - - libusb_fill_iso_transfer(t, h, epOUT, buf, tsize * packets, packets, play_callback, (void *)index, 20000); - libusb_set_iso_packet_lengths(t, tsize); - libusb_submit_transfer(t); -} - -#define NUM_RECORD_BUFS 2 -#define NUM_REC_PACKETS 2 -#define REC_PACKET_SIZE 192 - -static uint8_t *record_buffers[NUM_RECORD_BUFS]; -// struct libusb_transfer *record_transfers[NUM_RECORD_BUFS]; - -float filt = 0; - -void record_callback(struct libusb_transfer *transfer) -{ - int i; - // printf("Record callback! %p index %d len %d\n", transfer, (int)transfer->user_data, transfer->length); - samples_captured += transfer->length / 4; - - float avg = 0; - int16_t *bufz = (int16_t*)transfer->buffer; - int items = transfer->length / 4; - for (i = 0; i < items; i ++) - { - int16_t *buf = &bufz[i * 2]; - if (fabs(buf[0]) > avg) - avg = fabs(buf[0]); - if (fabs(buf[1]) > avg) - avg = fabs(buf[1]); - } - if (avg) - printf("%12.6f dBFS\r", 6 * log(avg / 32767 / items) / log(2.0)); - libusb_submit_transfer(transfer); -} - -void record_stuff(struct libusb_device_handle *h, int index) -{ - // 0x02 - struct libusb_transfer *t; - - record_buffers[index] = (uint8_t*)malloc(NUM_REC_PACKETS * REC_PACKET_SIZE); - memset(record_buffers[index], 0, NUM_REC_PACKETS * REC_PACKET_SIZE); - - t = libusb_alloc_transfer(NUM_REC_PACKETS); - libusb_fill_iso_transfer(t, h, epIN, record_buffers[index], NUM_REC_PACKETS * REC_PACKET_SIZE, NUM_REC_PACKETS, record_callback, (void *)index, 1000); - libusb_set_iso_packet_lengths(t, REC_PACKET_SIZE); - if (libusb_submit_transfer(t)) - goto error; - return; -error: - printf("Record setup failed for index=%d\n", index); -} - -void usb_main_loop() -{ - struct sched_param p; - p.sched_priority = 10; - sched_setscheduler(0, SCHED_FIFO, &p); - while(1) { - struct timeval tv = { - .tv_sec = 0, - .tv_usec = 100 - }; - libusb_handle_events_timeout(usbctx, &tv); - } -} - -int main(int argc, char *argv[]) -{ - struct libusb_device_handle *h; - int i; - - init_usb(); - h = open_omega(); - if (!h) - { - printf("Lexicon Omega could not be opened.\n"); - return 2; - } - - // 10: 4 3 3 - 16 bit - // 30: 2 2 1 2 2 2 1 - 24 bit - // 50: 4 3 3 - // 70: 2 2 1 2 2 2 1 - printf("Error = %d\n", configure_omega(h, srate)); - usleep(1); - for (i = 0; i < NUM_PLAY_BUFS; i++) - play_stuff(h, i); - for (i = 0; i < NUM_RECORD_BUFS; i++) - record_stuff(h, i); - usb_main_loop(); - return 0; -} - diff --git a/template/calfbox/dspmath.h b/template/calfbox/dspmath.h deleted file mode 100644 index 0fa0414..0000000 --- a/template/calfbox/dspmath.h +++ /dev/null @@ -1,243 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ -#ifndef CBOX_DSPMATH_H -#define CBOX_DSPMATH_H - -#define CBOX_BLOCK_SIZE 16 - -#include -#include -#include -#include -#include - -#ifndef M_PI -#include -#define M_PI G_PI -#endif - -typedef float cbox_sample_t; - -struct cbox_sincos -{ - float sine; - float cosine; - float prewarp; - float prewarp2; -}; - -static inline float hz2w(float hz, float sr) -{ - return M_PI * hz / (2 * sr); -} - -static inline float cerp_naive(float v0, float v1, float v2, float v3, float f) -{ - float x0 = -1; - float x1 = 0; - float x2 = 1; - float x3 = 2; - - float l0 = ((f - x1) * (f - x2) * (f - x3)) / ( (x0 - x1) * (x0 - x2) * (x0 - x3)); - float l1 = ((f - x0) * (f - x2) * (f - x3)) / ((x1 - x0) * (x1 - x2) * (x1 - x3)); - float l2 = ((f - x0) * (f - x1) * (f - x3)) / ((x2 - x0) * (x2 - x1) * (x2 - x3)); - float l3 = ((f - x0) * (f - x1) * (f - x2)) / ((x3 - x0) * (x3 - x1) * (x3 - x2) ); - - return v0 * l0 + v1 * l1 + v2 * l2 + v3 * l3; -} - -static inline float cerp(float v0, float v1, float v2, float v3, float f) -{ - f += 1; - - float d0 = (f - 0); - float d1 = (f - 1); - float d2 = (f - 2); - float d3 = (f - 3); - - float d03 = (d0 * d3) * (1.0 / 2.0); - float d12 = (d03 + 1) * (1.0 / 3.0); - - float l0 = -d12 * d3; - float l1 = d03 * d2; - float l2 = -d03 * d1; - float l3 = d12 * d0; - - float y = v0 * l0 + v1 * l1 + v2 * l2 + v3 * l3; - // printf("%f\n", y - cerp_naive(v0, v1, v2, v3, f - 1)); - return y; -} - -static inline float sanef(float v) -{ - if (fabs(v) < (1.0 / (65536.0 * 65536.0))) - return 0; - return v; -} - -static inline void sanebf(float *buf) -{ - for (int i = 0; i < CBOX_BLOCK_SIZE; ++i) - buf[i] = sanef(buf[i]); -} - -static inline void copybf(float *to, float *from) -{ - memcpy(to, from, sizeof(float) * CBOX_BLOCK_SIZE); -} - -static inline void zerobf(float *to) -{ - for (int i = 0; i < CBOX_BLOCK_SIZE; ++i) - to[i] = 0.f; -} - -static inline float cent2factor(float cent) -{ - return powf(2.0, cent * (1.f / 1200.f)); // I think this may be optimised using exp() -} - -static inline float dB2gain(float dB) -{ - return powf(2.f, dB * (1.f / 6.f)); -} - -static inline float dB2gain_simple(float dB) -{ - if (dB <= -96) - return 0; - return powf(2.f, dB * (1.f / 6.f)); -} - -static inline float gain2dB_simple(float gain) -{ - static const float sixoverlog2 = 8.656170245333781; // 6.0 / logf(2.f); - if (gain < (1.f / 65536.f)) - return -96.f; - return sixoverlog2 * logf(gain); -} - -static inline float deg2rad(float deg) -{ - return deg * (float)(M_PI / 180.f); -} - -static inline float rad2deg(float rad) -{ - return rad * (float)(180.f / M_PI); -} - -// Do a butterfly operation: -// dst1 = src1 + e^iw_1*src2 -// dst2 = src1 + e^iw_2*src2 (w = phase * 2pi / ANALYSIS_BUFFER_SIZE) -static inline void butterfly(complex float *dst1, complex float *dst2, complex float src1, complex float src2, complex float eiw1, complex float eiw2) -{ - *dst1 = src1 + eiw1 * src2; - *dst2 = src1 + eiw2 * src2; -} - -struct cbox_gain -{ - float db_gain; - float lin_gain; - float old_lin_gain; - float pos; - float delta; -}; - -static inline void cbox_gain_init(struct cbox_gain *gain) -{ - gain->db_gain = 0; - gain->lin_gain = 1; - gain->old_lin_gain = 1; - gain->pos = 1; - gain->delta = 1 / (44100 * 0.1); // XXXKF ballpark -} - -static inline void cbox_gain_set_db(struct cbox_gain *gain, float db) -{ - if (gain->db_gain == db) - return; - gain->db_gain = db; - gain->old_lin_gain = gain->old_lin_gain + (gain->lin_gain - gain->old_lin_gain) * gain->pos; - gain->lin_gain = dB2gain(db); - gain->pos = 0; -} - -#define CBOX_GAIN_APPLY_LOOP(gain, nsamples, code) \ -{ \ - double pos = (gain)->pos; \ - double span = (gain)->lin_gain - (gain)->old_lin_gain; \ - double start = (gain)->old_lin_gain; \ - double step = (gain)->delta; \ - if (pos >= 1) { \ - double tgain = gain->lin_gain; \ - for (uint32_t i = 0; i < (nsamples); ++i) { \ - code(i, tgain) \ - } \ - } else { \ - if (pos + (nsamples) * step < 1.0) { \ - for (uint32_t i = 0; i < (nsamples); ++i) { \ - double tgain = start + (pos + i * step) * span; \ - code(i, tgain) \ - } \ - gain->pos += (nsamples) * step; \ - } \ - else { \ - for (uint32_t i = 0; i < (nsamples); ++i) { \ - code(i, (start + pos * span)) \ - pos = (pos + step < 1.0 ? pos + step : 1.0); \ - } \ - gain->pos = 1.0; \ - } \ - } \ -} - -#define CBOX_GAIN_ADD_MONO(i, gain) \ - dest1[i] += src1[i] * gain; - -static inline void cbox_gain_add_mono(struct cbox_gain *gain, float *dest1, const float *src1, uint32_t nsamples) -{ - CBOX_GAIN_APPLY_LOOP(gain, nsamples, CBOX_GAIN_ADD_MONO); -} - -#define CBOX_GAIN_ADD_STEREO(i, gain) \ - dest1[i] += src1[i] * gain, dest2[i] += src2[i] * gain; - -static inline void cbox_gain_add_stereo(struct cbox_gain *gain, float *dest1, const float *src1, float *dest2, const float *src2, uint32_t nsamples) -{ - CBOX_GAIN_APPLY_LOOP(gain, nsamples, CBOX_GAIN_ADD_STEREO); -} - -#define CBOX_GAIN_COPY_MONO(i, gain) \ - dest1[i] = src1[i] * gain; - -static inline void cbox_gain_copy_mono(struct cbox_gain *gain, float *dest1, const float *src1, uint32_t nsamples) -{ - CBOX_GAIN_APPLY_LOOP(gain, nsamples, CBOX_GAIN_COPY_MONO); -} - -#define CBOX_GAIN_COPY_STEREO(i, gain) \ - dest1[i] = src1[i] * gain, dest2[i] = src2[i] * gain; - -static inline void cbox_gain_copy_stereo(struct cbox_gain *gain, float *dest1, const float *src1, float *dest2, const float *src2, uint32_t nsamples) -{ - CBOX_GAIN_APPLY_LOOP(gain, nsamples, CBOX_GAIN_COPY_STEREO); -} - -#endif \ No newline at end of file diff --git a/template/calfbox/engine.c b/template/calfbox/engine.c deleted file mode 100644 index 7d969d6..0000000 --- a/template/calfbox/engine.c +++ /dev/null @@ -1,445 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "blob.h" -#include "dom.h" -#include "engine.h" -#include "instr.h" -#include "io.h" -#include "layer.h" -#include "midi.h" -#include "mididest.h" -#include "module.h" -#include "rt.h" -#include "scene.h" -#include "seq.h" -#include "song.h" -#include "stm.h" -#include "track.h" -#include -#include -#include - -CBOX_CLASS_DEFINITION_ROOT(cbox_engine) - -static gboolean cbox_engine_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error); - -struct cbox_engine *cbox_engine_new(struct cbox_document *doc, struct cbox_rt *rt) -{ - struct cbox_engine *engine = malloc(sizeof(struct cbox_engine)); - CBOX_OBJECT_HEADER_INIT(engine, cbox_engine, doc); - - engine->rt = rt; - engine->scenes = NULL; - engine->scene_count = 0; - engine->effect = NULL; - engine->master = cbox_master_new(engine); - engine->master->song = cbox_song_new(doc); - engine->spb = NULL; - engine->spb_lock = 0; - engine->spb_retry = 0; - - if (rt) - cbox_io_env_copy(&engine->io_env, &rt->io_env); - else - { - engine->io_env.srate = 0; // must be overridden - engine->io_env.buffer_size = 256; - engine->io_env.input_count = 0; - engine->io_env.output_count = 2; - } - - cbox_midi_buffer_init(&engine->midibuf_aux); - cbox_midi_buffer_init(&engine->midibuf_jack); - cbox_midi_buffer_init(&engine->midibuf_song); - engine->stmap = malloc(sizeof(struct cbox_song_time_mapper)); - cbox_song_time_mapper_init(engine->stmap, engine); - cbox_midi_appsink_init(&engine->appsink, rt, &engine->stmap->tmap); - - cbox_command_target_init(&engine->cmd_target, cbox_engine_process_cmd, engine); - CBOX_OBJECT_REGISTER(engine); - - return engine; -} - -struct cbox_objhdr *cbox_engine_newfunc(struct cbox_class *class_ptr, struct cbox_document *doc) -{ - return NULL; -} - -void cbox_engine_destroyfunc(struct cbox_objhdr *obj_ptr) -{ - struct cbox_engine *engine = (struct cbox_engine *)obj_ptr; - while(engine->scene_count) - CBOX_DELETE(engine->scenes[0]); - if (engine->master->song) - { - CBOX_DELETE(engine->master->song); - engine->master->song = NULL; - } - cbox_master_destroy(engine->master); - engine->master = NULL; - free(engine->stmap); - engine->stmap = NULL; - - free(engine); -} - -static gboolean cbox_engine_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct cbox_engine *engine = ct->user_data; - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - for (uint32_t i = 0; i < engine->scene_count; i++) - { - if (!cbox_execute_on(fb, NULL, "/scene", "o", error, engine->scenes[i])) - return FALSE; - } - return CBOX_OBJECT_DEFAULT_STATUS(engine, fb, error); - } - else if (!strcmp(cmd->command, "/render_stereo") && !strcmp(cmd->arg_types, "i")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - if (engine->rt && engine->rt->io) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot use render function in real-time mode."); - return FALSE; - } - struct cbox_midi_buffer midibuf_song; - cbox_midi_buffer_init(&midibuf_song); - int nframes = CBOX_ARG_I(cmd, 0); - float *data = malloc(2 * nframes * sizeof(float)); - float *data_i = malloc(2 * nframes * sizeof(float)); - float *buffers[2] = { data, data + nframes }; - for (int i = 0; i < nframes; i++) - { - buffers[0][i] = 0.f; - buffers[1][i] = 0.f; - } - cbox_engine_process(engine, NULL, nframes, buffers, 2); - for (int i = 0; i < nframes; i++) - { - data_i[i * 2] = buffers[0][i]; - data_i[i * 2 + 1] = buffers[1][i]; - } - free(data); - - if (!cbox_execute_on(fb, NULL, "/data", "b", error, cbox_blob_new_acquire_data(data_i, nframes * 2 * sizeof(float)))) - return FALSE; - return TRUE; - } - else if (!strncmp(cmd->command, "/master_effect/",15)) - { - return cbox_module_slot_process_cmd(&engine->effect, fb, cmd, cmd->command + 14, CBOX_GET_DOCUMENT(engine), engine->rt, engine, error); - } - else if (!strcmp(cmd->command, "/new_scene") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - struct cbox_scene *s = cbox_scene_new(CBOX_GET_DOCUMENT(engine), engine); - - return s ? cbox_execute_on(fb, NULL, "/uuid", "o", error, s) : FALSE; - } - else if (!strcmp(cmd->command, "/new_recorder") && !strcmp(cmd->arg_types, "s")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - struct cbox_recorder *rec = cbox_recorder_new_stream(engine, engine->rt, CBOX_ARG_S(cmd, 0)); - - return rec ? cbox_execute_on(fb, NULL, "/uuid", "o", error, rec) : FALSE; - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); -} - -void cbox_engine_process(struct cbox_engine *engine, struct cbox_io *io, uint32_t nframes, float **output_buffers, uint32_t output_channels) -{ - struct cbox_module *effect = engine->effect; - uint32_t i, j; - - cbox_midi_buffer_clear(&engine->midibuf_aux); - cbox_midi_buffer_clear(&engine->midibuf_song); - if (io) - cbox_io_get_midi_data(io, &engine->midibuf_jack); - else - cbox_midi_buffer_clear(&engine->midibuf_jack); - - // Copy MIDI input to the app-sink - cbox_midi_appsink_supply(&engine->appsink, &engine->midibuf_jack, io->free_running_frame_counter); - - // Clear external track outputs - if (engine->spb) - cbox_song_playback_prepare_render(engine->spb); - - if (engine->rt) - cbox_rt_handle_rt_commands(engine->rt); - - // Combine various sources of events (song, non-RT thread, JACK input) - if (engine->spb) - { - engine->frame_start_song_pos = engine->spb->song_pos_samples; - cbox_song_playback_render(engine->spb, &engine->midibuf_song, nframes); - } - - for (uint32_t i = 0; i < engine->scene_count; i++) - cbox_scene_render(engine->scenes[i], nframes, output_buffers, output_channels); - - // Process "master" effect - if (effect) - { - for (i = 0; i < nframes; i += CBOX_BLOCK_SIZE) - { - cbox_sample_t left[CBOX_BLOCK_SIZE], right[CBOX_BLOCK_SIZE]; - cbox_sample_t *in_bufs[2] = {output_buffers[0] + i, output_buffers[1] + i}; - cbox_sample_t *out_bufs[2] = {left, right}; - (*effect->process_block)(effect, in_bufs, out_bufs); - for (j = 0; j < CBOX_BLOCK_SIZE; j++) - { - output_buffers[0][i + j] = left[j]; - output_buffers[1][i + j] = right[j]; - } - } - } -} - -//////////////////////////////////////////////////////////////////////////////////////// - -void cbox_engine_add_scene(struct cbox_engine *engine, struct cbox_scene *scene) -{ - assert(scene->engine == engine); - - cbox_rt_array_insert(engine->rt, (void ***)&engine->scenes, &engine->scene_count, -1, scene); -} - -void cbox_engine_remove_scene(struct cbox_engine *engine, struct cbox_scene *scene) -{ - assert(scene->engine == engine); - cbox_rt_array_remove_by_value(engine->rt, (void ***)&engine->scenes, &engine->scene_count, scene); -} - -//////////////////////////////////////////////////////////////////////////////////////// - -#define cbox_engine_set_song_playback_args(ARG) ARG(struct cbox_song_playback *, old_song) ARG(struct cbox_song_playback *, new_song) ARG(uint32_t, new_time_ppqn) - -DEFINE_ASYNC_RT_FUNC(cbox_engine, engine, cbox_engine_set_song_playback) -{ - // If there's no new song, silence all ongoing notes. Otherwise, copy the - // ongoing notes to the new playback structure so that the notes get released - // when playback is stopped (or possibly earlier). - if (engine->spb) - { - if (new_song) - cbox_song_playback_apply_old_state(new_song); - - if (cbox_song_playback_active_notes_release(engine->spb, new_song, new_time_ppqn == (uint32_t)-1 ? old_song->song_pos_ppqn : new_time_ppqn, &engine->midibuf_aux) < 0) - { - RT_CALL_AGAIN_LATER(); - return; - } - } - engine->spb = new_song; - engine->master->spb = new_song; - if (new_song) - { - if (new_time_ppqn == (uint32_t)-1) - { - int old_time_ppqn = old_song ? old_song->song_pos_ppqn : 0; - cbox_song_playback_seek_samples(engine->master->spb, old_song ? old_song->song_pos_samples : 0); - // If tempo change occurred anywhere before playback point, then - // sample-based position corresponds to a completely different location. - // So, if new sample-based position corresponds to different PPQN - // position, seek again using PPQN position. - if (old_song && abs(new_song->song_pos_ppqn - old_time_ppqn) > 1) - cbox_song_playback_seek_ppqn(engine->master->spb, old_time_ppqn, FALSE); - } - else - cbox_song_playback_seek_ppqn(engine->master->spb, new_time_ppqn, FALSE); - } -} - -ASYNC_PREPARE_FUNC(cbox_engine, engine, cbox_engine_set_song_playback) -{ - // If update is already in progress, reschedule another at the end of it - if (engine->spb_lock) - { - engine->spb_retry = 1; - return 1; - } - ++engine->spb_lock; - args->old_song = engine->spb; - args->new_song = cbox_song_playback_new(engine->master->song, engine->master, engine, args->old_song); - - return 0; -} - -ASYNC_CLEANUP_FUNC(cbox_engine, engine, cbox_engine_set_song_playback) -{ - --engine->spb_lock; - assert(!engine->spb_lock); - if (args->old_song) - cbox_song_playback_destroy(args->old_song); - // If another update was requested while this one was in progress, repeat - // the operation - if (engine->spb_retry) { - engine->spb_retry = 0; - cbox_engine_set_song_playback(engine, NULL, NULL, args->new_time_ppqn); - } -} - -void cbox_engine_update_song(struct cbox_engine *engine, int new_pos_ppqn) -{ - cbox_engine_set_song_playback(engine, NULL, NULL, new_pos_ppqn); -} - -//////////////////////////////////////////////////////////////////////////////////////// - -void cbox_engine_update_song_playback(struct cbox_engine *engine) -{ - cbox_engine_update_song(engine, -1); -} - -//////////////////////////////////////////////////////////////////////////////////////// - -uint32_t cbox_engine_current_pos_samples(struct cbox_engine *engine) -{ - uint32_t pos = engine->frame_start_song_pos + engine->song_pos_offset; - if (engine->spb && engine->spb->loop_start_ppqn < engine->spb->loop_end_ppqn) - pos = cbox_song_playback_correct_for_looping(engine->spb, pos); - return pos; -} - -//////////////////////////////////////////////////////////////////////////////////////// - -void cbox_engine_update_input_connections(struct cbox_engine *engine) -{ - for (uint32_t i = 0; i < engine->scene_count; i++) - cbox_scene_update_connected_inputs(engine->scenes[i]); -} - -void cbox_engine_update_output_connections(struct cbox_engine *engine) -{ - for (uint32_t i = 0; i < engine->scene_count; i++) - cbox_scene_update_connected_outputs(engine->scenes[i]); -} - -//////////////////////////////////////////////////////////////////////////////////////// - -void cbox_engine_send_events_to(struct cbox_engine *engine, struct cbox_midi_merger *merger, struct cbox_midi_buffer *buffer) -{ - if (!engine || !buffer) - return; - if (merger) - cbox_midi_merger_push(merger, buffer, engine->rt); - else - { - for (uint32_t i = 0; i < engine->scene_count; i++) - cbox_midi_merger_push(&engine->scenes[i]->scene_input_merger, buffer, engine->rt); - if (!engine->rt || !engine->rt->io) - return; - for (GSList *p = engine->rt->io->midi_outputs; p; p = p->next) - { - struct cbox_midi_output *midiout = p->data; - cbox_midi_merger_push(&midiout->merger, buffer, engine->rt); - } - } -} - -//////////////////////////////////////////////////////////////////////////////////////// - -void cbox_engine_on_tempo_sync(struct cbox_engine *engine, double beats_per_minute) -{ - if (!engine->master) - return; - if (beats_per_minute && beats_per_minute != engine->master->tempo && beats_per_minute != engine->master->new_tempo) { - engine->master->new_tempo = beats_per_minute; - } -} - -//////////////////////////////////////////////////////////////////////////////////////// - -gboolean cbox_engine_on_transport_sync(struct cbox_engine *engine, enum cbox_transport_state state, uint32_t frame) -{ - if (state == ts_stopping) - { - if (engine->master->state == CMTS_ROLLING) - engine->master->state = engine->spb ? CMTS_STOPPING : CMTS_STOP; - - return engine->master->state == CMTS_STOP; - } - if (state == ts_starting) - { - if (engine->master->state == CMTS_STOPPING) - return FALSE; - if (engine->master->state == CMTS_ROLLING) - { - if (engine->spb->song_pos_samples == frame) - return TRUE; - engine->master->state = CMTS_STOPPING; - return FALSE; - } - if (engine->spb && engine->spb->song_pos_samples != frame) - { - cbox_song_playback_seek_samples(engine->spb, frame); - } - engine->frame_start_song_pos = frame; - return TRUE; - } - if (state == ts_rolling) - { - // When starting with JACK transport rolling, there is no - // ts_starting message in first place (because there can't be without - // interfering with other applications). Seek immediately. - if (engine->spb && engine->spb->song_pos_samples != frame) - { - cbox_song_playback_seek_samples(engine->spb, frame); - } - else - engine->frame_start_song_pos = frame; - engine->master->state = CMTS_ROLLING; - return TRUE; - } - if (state == ts_stopped) - { - if (engine->master->state == CMTS_ROLLING) - engine->master->state = CMTS_STOPPING; - - if (engine->master->state == CMTS_STOP && engine->spb && engine->spb->song_pos_samples != frame) - { - cbox_song_playback_seek_samples(engine->spb, frame); - } - - return engine->master->state == CMTS_STOP; - } - return TRUE; -} - -//////////////////////////////////////////////////////////////////////////////////////// - -struct cbox_midi_merger *cbox_engine_get_midi_output(struct cbox_engine *engine, struct cbox_uuid *uuid) -{ - struct cbox_objhdr *objhdr = cbox_document_get_object_by_uuid(CBOX_GET_DOCUMENT(engine), uuid); - if (!objhdr) - return NULL; - struct cbox_scene *scene = (struct cbox_scene *)objhdr; - if (!CBOX_IS_A(scene, cbox_scene)) - return NULL; - return &scene->scene_input_merger; -} diff --git a/template/calfbox/engine.h b/template/calfbox/engine.h deleted file mode 100644 index d9b560c..0000000 --- a/template/calfbox/engine.h +++ /dev/null @@ -1,72 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_ENGINE_H -#define CBOX_ENGINE_H - -#include "cmd.h" -#include "dom.h" -#include "io.h" -#include "midi.h" -#include "rt.h" - -CBOX_EXTERN_CLASS(cbox_engine) - -#define GET_RT_FROM_cbox_engine(ptr) ((ptr)->rt) - -struct cbox_engine -{ - CBOX_OBJECT_HEADER() - struct cbox_command_target cmd_target; - struct cbox_io_env io_env; - struct cbox_rt *rt; - struct cbox_scene **scenes; - uint32_t scene_count; - struct cbox_song_playback *spb; - struct cbox_module *effect; - struct cbox_master *master; - struct cbox_midi_buffer midibuf_aux, midibuf_jack, midibuf_song; - struct cbox_song_time_mapper *stmap; - struct cbox_midi_appsink appsink; - - int spb_lock, spb_retry; - - uint32_t frame_start_song_pos, song_pos_offset; // samples -}; - -// These use an RT command internally -extern struct cbox_engine *cbox_engine_new(struct cbox_document *doc, struct cbox_rt *rt); -extern void cbox_engine_update_song_playback(struct cbox_engine *engine); -extern void cbox_engine_update_input_connections(struct cbox_engine *engine); -extern void cbox_engine_update_output_connections(struct cbox_engine *engine); -extern void cbox_engine_add_scene(struct cbox_engine *engine, struct cbox_scene *scene); -void cbox_engine_remove_scene(struct cbox_engine *engine, struct cbox_scene *scene); -extern struct cbox_song *cbox_engine_set_song(struct cbox_engine *engine, struct cbox_song *song, int new_pos); -extern struct cbox_song *cbox_engine_set_pattern(struct cbox_engine *engine, struct cbox_midi_pattern *pattern, int new_pos); -extern void cbox_engine_set_pattern_and_destroy(struct cbox_engine *engine, struct cbox_midi_pattern *pattern); -extern void cbox_engine_send_events_to(struct cbox_engine *engine, struct cbox_midi_merger *merger, struct cbox_midi_buffer *buffer); -extern void cbox_engine_process(struct cbox_engine *engine, struct cbox_io *io, uint32_t nframes, float **output_buffers, uint32_t output_channels); -extern gboolean cbox_engine_on_transport_sync(struct cbox_engine *engine, enum cbox_transport_state state, uint32_t frame); -extern void cbox_engine_on_tempo_sync(struct cbox_engine *engine, double beats_per_minute); -extern struct cbox_midi_merger *cbox_engine_get_midi_output(struct cbox_engine *engine, struct cbox_uuid *uuid); -extern uint32_t cbox_engine_current_pos_samples(struct cbox_engine *engine); - -extern int cbox_engine_get_sample_rate(struct cbox_engine *engine); -extern int cbox_engine_get_buffer_size(struct cbox_engine *engine); - -#endif diff --git a/template/calfbox/envelope.h b/template/calfbox/envelope.h deleted file mode 100644 index af26dae..0000000 --- a/template/calfbox/envelope.h +++ /dev/null @@ -1,324 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_ENVELOPE_H -#define CBOX_ENVELOPE_H - -#include -#include - -struct cbox_envstage -{ - double end_value; - int time; - int next_if_pressed, next_if_released, keep_last_value, break_on_release, is_exp; -}; - -#define MAX_ENV_STAGES 16 -#define EXP_NOISE_FLOOR (100.0 / 16384.0) - -struct cbox_envelope_shape -{ - double start_value; - struct cbox_envstage stages[MAX_ENV_STAGES]; -}; - -struct cbox_envelope -{ - struct cbox_envelope_shape *shape; - double stage_start_value, cur_value, exp_factor, inv_time, cur_time, orig_time, orig_target; - int cur_stage; -}; - -static inline void cbox_envelope_init_stage(struct cbox_envelope *env) -{ - struct cbox_envstage *es = &env->shape->stages[env->cur_stage]; - env->orig_time = es->time; - env->orig_target = es->end_value; - env->inv_time = es->time > 0 ? 1.0 / es->time : 1e6; - if (es->is_exp) - { - if (env->stage_start_value < EXP_NOISE_FLOOR) - env->stage_start_value = EXP_NOISE_FLOOR; - double ev = es->end_value; - if (ev < EXP_NOISE_FLOOR) - ev = EXP_NOISE_FLOOR; - env->exp_factor = log(ev / env->stage_start_value); - } -} - -static inline void cbox_envelope_go_to(struct cbox_envelope *env, int stage) -{ - env->stage_start_value = env->cur_value; - env->cur_stage = stage; - env->cur_time = 0; - cbox_envelope_init_stage(env); -} - -static inline void cbox_envelope_reset(struct cbox_envelope *env) -{ - env->cur_value = 0; - env->cur_stage = 0; - env->cur_time = 0; - cbox_envelope_init_stage(env); -} - -static inline void cbox_envelope_update_shape(struct cbox_envelope *env, struct cbox_envelope_shape *shape) -{ - struct cbox_envelope_shape *old_shape = env->shape; - env->shape = shape; - if (env->cur_stage < 0) - return; - struct cbox_envstage *ns = &env->shape->stages[env->cur_stage]; - struct cbox_envstage *os = &old_shape->stages[env->cur_stage]; - if (os->time > 0) - env->cur_time = env->cur_time * ns->time / os->time; - if (env->cur_time > ns->time) - env->cur_time = ns->time; -} - -static inline float cbox_envelope_get_value(struct cbox_envelope *env, const struct cbox_envelope_shape *shape) -{ - if (env->cur_stage < 0) - return env->cur_value; - const struct cbox_envstage *es = &shape->stages[env->cur_stage]; - double pos = es->time > 0 ? env->cur_time * env->inv_time : 0; - if (pos > 1) - pos = 1; - if (es->is_exp) - { - // instead of exp, may use 2**x which can be factored - // into a shift and a table lookup - env->cur_value = env->stage_start_value * expf(pos * env->exp_factor); - if (env->cur_value <= EXP_NOISE_FLOOR) - env->cur_value = 0; - } - else - env->cur_value = env->stage_start_value + (es->end_value - env->stage_start_value) * pos; - return env->cur_value; -} - -#define DEBUG_UPDATE_SHAPE(...) - -static inline void cbox_envelope_update_shape_after_modify(struct cbox_envelope *env, struct cbox_envelope_shape *shape, double sr) -{ - if (env->cur_stage < 0) - return; - struct cbox_envstage *es = &shape->stages[env->cur_stage]; - if (es->time != env->orig_time) - { - // Scale cur_time to reflect the same relative position within the stage - env->cur_time = env->cur_time * es->time / (env->orig_time > 0 ? env->orig_time : 1); - env->orig_time = es->time; - env->inv_time = es->time > 0 ? 1.0 / es->time : 1e6; - } - if (es->end_value != env->orig_target) - { - // Adjust the start value to keep the current value intact given the change in the slope - double pos = es->time > 0 ? env->cur_time * env->inv_time : 1; - if (pos < 1) - { - if (es->is_exp) - env->stage_start_value /= pow(es->end_value / (env->orig_target >= EXP_NOISE_FLOOR ? env->orig_target : EXP_NOISE_FLOOR), pos / (1 - pos)); // untested, likely never used - else - env->stage_start_value -= (es->end_value - env->orig_target) * pos / (1 - pos); - } - env->orig_target = es->end_value; - } -} - -static inline void cbox_envelope_advance(struct cbox_envelope *env, int released, const struct cbox_envelope_shape *shape) -{ - if (env->cur_stage < 0) - return; - const struct cbox_envstage *es = &shape->stages[env->cur_stage]; - double pos = es->time > 0 ? env->cur_time * env->inv_time : 1; - env->cur_time++; - if (pos >= 1 || (es->break_on_release && released)) - { - int next_stage = released ? es->next_if_released : es->next_if_pressed; - if (!es->keep_last_value || pos >= 1 || (es->keep_last_value == 2 && !released) || next_stage == env->cur_stage) - env->stage_start_value = es->end_value; - else - env->stage_start_value = env->cur_value; - env->cur_stage = next_stage; - env->cur_time = 0; - cbox_envelope_init_stage(env); - } -} - -struct cbox_adsr -{ - float attack; - float decay; - float sustain; - float release; -}; - -static inline void cbox_envelope_init_adsr(struct cbox_envelope_shape *env, const struct cbox_adsr *adsr, int sr) -{ - env->start_value = 0; - env->stages[0].end_value = 1; - env->stages[0].time = adsr->attack * sr; - env->stages[0].next_if_pressed = 1; - env->stages[0].next_if_released = 3; - env->stages[0].keep_last_value = 1; - env->stages[0].break_on_release = 0; - env->stages[0].is_exp = 0; - - env->stages[1].end_value = adsr->sustain; - env->stages[1].time = adsr->decay * sr; - env->stages[1].next_if_pressed = 2; - env->stages[1].next_if_released = 3; - env->stages[1].keep_last_value = 1; - env->stages[1].break_on_release = 0; - env->stages[1].is_exp = 0; - - env->stages[2].end_value = adsr->sustain; - env->stages[2].time = 1 * sr; - env->stages[2].next_if_pressed = 2; - env->stages[2].next_if_released = 3; - env->stages[2].keep_last_value = 0; - env->stages[2].break_on_release = 1; - env->stages[2].is_exp = 0; - - env->stages[3].end_value = 0; - env->stages[3].time = adsr->release * sr; - env->stages[3].next_if_pressed = -1; - env->stages[3].next_if_released = -1; - env->stages[3].keep_last_value = 0; - env->stages[3].break_on_release = 0; - env->stages[3].is_exp = 1; - - env->stages[15].end_value = 0; - env->stages[15].time = 0.01 * sr; - env->stages[15].next_if_pressed = -1; - env->stages[15].next_if_released = -1; - env->stages[15].keep_last_value = 0; - env->stages[15].break_on_release = 0; - env->stages[15].is_exp = 0; -} - -struct cbox_dahdsr -{ - float start; - float delay; - float attack; - float hold; - float decay; - float sustain; - float release; -}; - -static inline void cbox_dahdsr_init(struct cbox_dahdsr *dahdsr, float top_value) -{ - dahdsr->start = 0.f; - dahdsr->delay = 0.f; - dahdsr->attack = 0.f; - dahdsr->hold = 0.f; - dahdsr->decay = 0.f; - dahdsr->sustain = top_value; - dahdsr->release = 0.05f; -} - -static inline void cbox_envelope_init_dahdsr(struct cbox_envelope_shape *env, const struct cbox_dahdsr *dahdsr, int sr, float top_value, gboolean is_release_exp) -{ - env->start_value = dahdsr->start; - env->stages[0].end_value = dahdsr->start; - env->stages[0].time = dahdsr->delay * sr; - env->stages[0].next_if_pressed = 1; - env->stages[0].next_if_released = 5; - env->stages[0].keep_last_value = 1; - env->stages[0].break_on_release = 0; - env->stages[0].is_exp = 0; - - env->stages[1].end_value = top_value; - env->stages[1].time = dahdsr->attack * sr; - env->stages[1].next_if_pressed = 2; - env->stages[1].next_if_released = 5; - env->stages[1].keep_last_value = 2; - env->stages[1].break_on_release = 1; - env->stages[1].is_exp = 0; - - env->stages[2].end_value = top_value; - env->stages[2].time = dahdsr->hold * sr; - env->stages[2].next_if_pressed = 3; - env->stages[2].next_if_released = 5; - env->stages[2].keep_last_value = 2; - env->stages[2].break_on_release = 1; - env->stages[2].is_exp = 0; - - env->stages[3].end_value = dahdsr->sustain; - env->stages[3].time = dahdsr->decay * sr; - env->stages[3].next_if_pressed = 4; - env->stages[3].next_if_released = 5; - env->stages[3].keep_last_value = 1; - env->stages[3].break_on_release = 1; - env->stages[3].is_exp = 0; - - env->stages[4].end_value = dahdsr->sustain; - env->stages[4].time = 1 * sr; - env->stages[4].next_if_pressed = 4; - env->stages[4].next_if_released = 5; - env->stages[4].keep_last_value = 1; - env->stages[4].break_on_release = 1; - env->stages[4].is_exp = 0; - - env->stages[5].end_value = 0; - env->stages[5].time = dahdsr->release * sr; - env->stages[5].next_if_pressed = -1; - env->stages[5].next_if_released = -1; - env->stages[5].keep_last_value = 0; - env->stages[5].break_on_release = 0; - env->stages[5].is_exp = is_release_exp; - - env->stages[15].end_value = 0; - env->stages[15].time = 0.01 * sr; - env->stages[15].next_if_pressed = -1; - env->stages[15].next_if_released = -1; - env->stages[15].keep_last_value = 0; - env->stages[15].break_on_release = 0; - env->stages[15].is_exp = 0; -} - -static inline void cbox_envelope_modify_dahdsr(struct cbox_envelope_shape *env, int part, float value, int sr) -{ - switch(part) - { - case 0: // delay - case 1: // attack - case 2: // hold - case 3: // decay - case 5: // release - env->stages[part].time += value * sr; - // Allow negative times (deal with them in get_next) to make multiple signed modulations work correctly - break; - case 4: // sustain - env->stages[3].end_value += value; - env->stages[4].end_value += value; - env->stages[4].time = 0.02 * sr; // more rapid transition - break; - case 6: // start - env->stages[0].end_value += value; - env->start_value += value; - break; - } -} - - -#endif diff --git a/template/calfbox/eq.c b/template/calfbox/eq.c deleted file mode 100644 index f53c7ae..0000000 --- a/template/calfbox/eq.c +++ /dev/null @@ -1,194 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "biquad-float.h" -#include "config.h" -#include "config-api.h" -#include "dspmath.h" -#include "eq.h" -#include "module.h" -#include -#include -#include -#include -#include -#include -#include - -#define MODULE_PARAMS parametric_eq_params -#define MAX_EQ_BANDS 4 - -struct parametric_eq_params -{ - struct eq_band bands[MAX_EQ_BANDS]; -}; - -struct parametric_eq_module -{ - struct cbox_module module; - - struct parametric_eq_params *params, *old_params; - - struct cbox_biquadf_state state[MAX_EQ_BANDS][2]; - struct cbox_biquadf_coeffs coeffs[MAX_EQ_BANDS]; -}; - -static void redo_filters(struct parametric_eq_module *m) -{ - for (int i = 0; i < MAX_EQ_BANDS; i++) - { - struct eq_band *band = &m->params->bands[i]; - if (band->active) - { - cbox_biquadf_set_peakeq_rbj(&m->coeffs[i], band->center, band->q, band->gain, m->module.srate); - } - } - m->old_params = m->params; -} - -gboolean parametric_eq_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct parametric_eq_module *m = (struct parametric_eq_module *)ct->user_data; - - EFFECT_PARAM_ARRAY("/active", "i", bands, active, int, , 0, 1) else - EFFECT_PARAM_ARRAY("/center", "f", bands, center, double, , 10, 20000) else - EFFECT_PARAM_ARRAY("/q", "f", bands, q, double, , 0.01, 100) else - EFFECT_PARAM_ARRAY("/gain", "f", bands, gain, double, dB2gain_simple, -100, 100) else - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - for (int i = 0; i < MAX_EQ_BANDS; i++) - { - if (!cbox_execute_on(fb, NULL, "/active", "ii", error, i, (int)m->params->bands[i].active)) - return FALSE; - if (!cbox_execute_on(fb, NULL, "/center", "if", error, i, m->params->bands[i].center)) - return FALSE; - if (!cbox_execute_on(fb, NULL, "/q", "if", error, i, m->params->bands[i].q)) - return FALSE; - if (!cbox_execute_on(fb, NULL, "/gain", "if", error, i, gain2dB_simple(m->params->bands[i].gain))) - return FALSE; - } - // return cbox_execute_on(fb, NULL, "/wet_dry", "f", error, m->params->wet_dry); - return CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error); - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); - return TRUE; -} - -void parametric_eq_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) -{ - // struct parametric_eq_module *m = (struct parametric_eq_module *)module; -} - -void parametric_eq_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) -{ - struct parametric_eq_module *m = (struct parametric_eq_module *)module; - - if (m->params != m->old_params) - redo_filters(m); - - for (int c = 0; c < 2; c++) - { - gboolean first = TRUE; - for (int i = 0; i < MAX_EQ_BANDS; i++) - { - if (!m->params->bands[i].active) - continue; - if (first) - { - cbox_biquadf_process_to(&m->state[i][c], &m->coeffs[i], inputs[c], outputs[c]); - first = FALSE; - } - else - { - cbox_biquadf_process(&m->state[i][c], &m->coeffs[i], outputs[c]); - } - } - if (first) - memcpy(outputs[c], inputs[c], sizeof(float) * CBOX_BLOCK_SIZE); - } -} - -float cbox_eq_get_band_param(const char *cfg_section, int band, const char *param, float defvalue) -{ - gchar *s = g_strdup_printf("band%d_%s", band + 1, param); - float v = cbox_config_get_float(cfg_section, s, defvalue); - g_free(s); - - return v; -} - -float cbox_eq_get_band_param_db(const char *cfg_section, int band, const char *param, float defvalue) -{ - gchar *s = g_strdup_printf("band%d_%s", band + 1, param); - float v = cbox_config_get_gain_db(cfg_section, s, defvalue); - g_free(s); - - return v; -} - -void cbox_eq_reset_bands(struct cbox_biquadf_state state[1][2], int bands) -{ - for (int b = 0; b < MAX_EQ_BANDS; b++) - for (int c = 0; c < 2; c++) - cbox_biquadf_reset(&state[b][c]); -} - -MODULE_SIMPLE_DESTROY_FUNCTION(parametric_eq) - -MODULE_CREATE_FUNCTION(parametric_eq) -{ - static int inited = 0; - if (!inited) - { - inited = 1; - } - - struct parametric_eq_module *m = malloc(sizeof(struct parametric_eq_module)); - CALL_MODULE_INIT(m, 2, 2, parametric_eq); - m->module.process_event = parametric_eq_process_event; - m->module.process_block = parametric_eq_process_block; - struct parametric_eq_params *p = malloc(sizeof(struct parametric_eq_params)); - m->params = p; - m->old_params = NULL; - - for (int b = 0; b < MAX_EQ_BANDS; b++) - { - p->bands[b].active = cbox_eq_get_band_param(cfg_section, b, "active", 0) > 0; - p->bands[b].center = cbox_eq_get_band_param(cfg_section, b, "center", 50 * pow(4.0, b)); - p->bands[b].q = cbox_eq_get_band_param(cfg_section, b, "q", 0.707); - p->bands[b].gain = cbox_eq_get_band_param_db(cfg_section, b, "gain", 0); - } - - cbox_eq_reset_bands(m->state, MAX_EQ_BANDS); - - return &m->module; -} - - -struct cbox_module_keyrange_metadata parametric_eq_keyranges[] = { -}; - -struct cbox_module_livecontroller_metadata parametric_eq_controllers[] = { -}; - -DEFINE_MODULE(parametric_eq, 2, 2) - diff --git a/template/calfbox/eq.h b/template/calfbox/eq.h deleted file mode 100644 index f1f25ad..0000000 --- a/template/calfbox/eq.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include - -struct eq_band -{ - gboolean active; - float center; - float q; - float gain; -}; - -extern float cbox_eq_get_band_param(const char *cfg_section, int band, const char *param, float defvalue); -extern float cbox_eq_get_band_param_db(const char *cfg_section, int band, const char *param, float defvalue); -extern void cbox_eq_reset_bands(struct cbox_biquadf_state state[1][2], int bands); diff --git a/template/calfbox/errors.c b/template/calfbox/errors.c deleted file mode 100644 index 0fec3e0..0000000 --- a/template/calfbox/errors.c +++ /dev/null @@ -1,70 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "errors.h" - -GQuark cbox_module_error_quark() -{ - return g_quark_from_string("cbox-module-error-quark"); -} - -void cbox_force_error(GError **error) -{ - if (error && !*error) - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "unknown error"); -} - -void cbox_print_error(GError *error) -{ - if (!error) - { - g_warning("Unspecified error"); - return; - } - g_warning("%s", error->message); - g_error_free(error); -} - -void cbox_print_error_if(GError *error) -{ - if (!error) - return; - g_warning("%s", error->message); - g_error_free(error); -} - -gboolean cbox_set_command_error(GError **error, const struct cbox_osc_command *cmd) -{ - if (error && !*error) - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_OUT_OF_RANGE, "Invalid command '%s' with args '%s'", cmd->command, cmd->arg_types); - return FALSE; -} - -gboolean cbox_set_command_error_with_msg(GError **error, const struct cbox_osc_command *cmd, const char *extra_msg) -{ - if (error && !*error) - 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); - return FALSE; -} - -gboolean cbox_set_range_error(GError **error, const char *param, double minv, double maxv) -{ - if (error && !*error) - 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); - return FALSE; -} diff --git a/template/calfbox/errors.h b/template/calfbox/errors.h deleted file mode 100644 index ae8206d..0000000 --- a/template/calfbox/errors.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_ERRORS_H -#define CBOX_ERRORS_H - -#include -#include "cmd.h" - -#define CBOX_MODULE_ERROR cbox_module_error_quark() - -enum CboxModuleError -{ - CBOX_MODULE_ERROR_FAILED, - CBOX_MODULE_ERROR_INVALID_COMMAND, - CBOX_MODULE_ERROR_OUT_OF_RANGE, -}; - -struct cbox_osc_command; - -extern GQuark cbox_module_error_quark(void); -extern void cbox_force_error(GError **error); -extern void cbox_print_error(GError *error); -extern void cbox_print_error_if(GError *error); -extern gboolean cbox_set_command_error(GError **error, const struct cbox_osc_command *cmd); -extern gboolean cbox_set_command_error_with_msg(GError **error, const struct cbox_osc_command *cmd, const char *extra_msg); -extern gboolean cbox_set_range_error(GError **error, const char *param, double minv, double maxv); - -#endif diff --git a/template/calfbox/example.py b/template/calfbox/example.py deleted file mode 100644 index c50284c..0000000 --- a/template/calfbox/example.py +++ /dev/null @@ -1,554 +0,0 @@ -import sys -sys.argv = [] -import gi -gi.require_version('Gdk', '3.0') -gi.require_version('Gtk', '3.0') -from gi.repository import GObject, Gdk, Gtk -import math - -sys.path = ["./py"] + sys.path - -import cbox -from gui_tools import * -import fx_gui -import instr_gui -import drumkit_editor -#import drum_pattern_editor - -class SceneDialog(SelectObjectDialog): - title = "Select a scene" - def __init__(self, parent): - SelectObjectDialog.__init__(self, parent=parent) - def update_model(self, model): - for s in cbox.Config.sections("scene:"): - title = s["title"] - model.append((s.name[6:], "Scene", s.name, title)) - for s in cbox.Config.sections("instrument:"): - title = s["title"] - model.append((s.name[11:], "Instrument", s.name, title)) - for s in cbox.Config.sections("layer:"): - title = s["title"] - model.append((s.name[6:], "Layer", s.name, title)) - -class NewLayerDialog(SelectObjectDialog): - title = "Create a layer" - def __init__(self, parent): - SelectObjectDialog.__init__(self, parent=parent) - def update_model(self, model): - for engine_name, wclass in instr_gui.instrument_window_map.items(): - model.append((engine_name, "Engine", engine_name, "")) - -class LoadLayerDialog(SelectObjectDialog): - title = "Load a layer" - def __init__(self, parent): - SelectObjectDialog.__init__(self, parent=parent) - def update_model(self, model): - for s in cbox.Config.sections("instrument:"): - title = s["title"] - model.append((s.name[11:], "Instrument", s.name, title)) - for s in cbox.Config.sections("layer:"): - title = s["title"] - model.append((s.name[6:], "Layer", s.name, title)) - -class PlayPatternDialog(SelectObjectDialog): - title = "Play a drum pattern" - def __init__(self, parent): - SelectObjectDialog.__init__(self, parent) - def update_model(self, model): - model.append((None, "Stop", "", "")) - for s in cbox.Config.sections("drumpattern:"): - title = s["title"] - model.append((s.name[12:], "Pattern", s.name, title)) - for s in cbox.Config.sections("drumtrack:"): - title = s["title"] - model.append((s.name[10:], "Track", s.name, title)) - -in_channels_ls = Gtk.ListStore(GObject.TYPE_INT, GObject.TYPE_STRING) -in_channels_ls.append((0, "All")) -out_channels_ls = Gtk.ListStore(GObject.TYPE_INT, GObject.TYPE_STRING) -out_channels_ls.append((0, "Same")) -for i in range(1, 17): - in_channels_ls.append((i, str(i))) - out_channels_ls.append((i, str(i))) -notes_ls = Gtk.ListStore(GObject.TYPE_INT, GObject.TYPE_STRING) -opt_notes_ls = Gtk.ListStore(GObject.TYPE_INT, GObject.TYPE_STRING) -opt_notes_ls.append((-1, "N/A")) -for i in range(0, 128): - notes_ls.append((i, note_to_name(i))) - opt_notes_ls.append((i, note_to_name(i))) -transpose_ls = Gtk.ListStore(GObject.TYPE_INT, GObject.TYPE_STRING) -for i in range(-60, 61): - transpose_ls.append((i, str(i))) - -class SceneLayersModel(Gtk.ListStore): - def __init__(self): - Gtk.ListStore.__init__(self, GObject.TYPE_STRING, GObject.TYPE_BOOLEAN, - GObject.TYPE_INT, GObject.TYPE_INT, GObject.TYPE_BOOLEAN, - GObject.TYPE_INT, GObject.TYPE_INT, GObject.TYPE_INT, GObject.TYPE_INT, GObject.TYPE_STRING) - #def make_row_item(self, opath, tree_path): - # return opath % self[(1 + int(tree_path))] - def make_row_item(self, opath, tree_path): - return cbox.Document.uuid_cmd(self[int(tree_path)][-1], opath) - def refresh(self, scene_status): - self.clear() - for layer in scene_status.layers: - ls = layer.status() - self.append((ls.instrument_name, ls.enable != 0, ls.in_channel, ls.out_channel, ls.consume != 0, ls.low_note, ls.high_note, ls.fixed_note, ls.transpose, layer.uuid)) - -class SceneLayersView(Gtk.TreeView): - def __init__(self, model): - Gtk.TreeView.__init__(self, model=model) - self.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, [("text/plain", 0, 1)], Gdk.DragAction.MOVE) - self.enable_model_drag_dest([("text/plain", Gtk.TargetFlags.SAME_APP | Gtk.TargetFlags.SAME_WIDGET, 1)], Gdk.DragAction.MOVE) - self.connect('drag_data_get', self.drag_data_get) - self.connect('drag_data_received', self.drag_data_received) - self.insert_column_with_attributes(0, "On?", standard_toggle_renderer(model, "/enable", 1), active=1) - self.insert_column_with_attributes(1, "Name", Gtk.CellRendererText(), text=0) - self.insert_column_with_data_func(2, "In Ch#", standard_combo_renderer(model, in_channels_ls, "/in_channel", 2), lambda column, cell, model, iter, data: cell.set_property('text', "%s" % model[iter][2] if model[iter][2] != 0 else 'All'), None) - self.insert_column_with_data_func(3, "Out Ch#", standard_combo_renderer(model, out_channels_ls, "/out_channel", 3), lambda column, cell, model, iter, data: cell.set_property('text', "%s" % model[iter][3] if model[iter][3] != 0 else 'Same'), None) - self.insert_column_with_attributes(4, "Eat?", standard_toggle_renderer(model, "/consume", 4), active=4) - self.insert_column_with_data_func(5, "Lo N#", standard_combo_renderer(model, notes_ls, "/low_note", 5), lambda column, cell, model, iter, data: cell.set_property('text', note_to_name(model[iter][5])), None) - self.insert_column_with_data_func(6, "Hi N#", standard_combo_renderer(model, notes_ls, "/high_note", 6), lambda column, cell, model, iter, data: cell.set_property('text', note_to_name(model[iter][6])), None) - self.insert_column_with_data_func(7, "Fix N#", standard_combo_renderer(model, opt_notes_ls, "/fixed_note", 7), lambda column, cell, model, iter, data: cell.set_property('text', note_to_name(model[iter][7])), None) - self.insert_column_with_attributes(8, "Transpose", standard_combo_renderer(model, transpose_ls, "/transpose", 8), text=8) - def drag_data_get(self, treeview, context, selection, target_id, etime): - cursor = treeview.get_cursor() - if cursor is not None: - selection.set('text/plain', 8, str(cursor[0][0])) - def drag_data_received(self, treeview, context, x, y, selection, info, etime): - src_row = int(selection.data) - dest_row_info = treeview.get_dest_row_at_pos(x, y) - if dest_row_info is not None: - dest_row = dest_row_info[0][0] - #print src_row, dest_row, dest_row_info[1] - scene = cbox.Document.get_scene() - scene.move_layer(src_row, dest_row) - self.get_model().refresh(scene.status()) - -class SceneAuxBusesModel(Gtk.ListStore): - def __init__(self): - Gtk.ListStore.__init__(self, GObject.TYPE_STRING, GObject.TYPE_STRING) - def refresh(self, scene_status): - self.clear() - for aux_name, aux_obj in scene_status.auxes.items(): - slot = aux_obj.slot.status() - self.append((slot.insert_preset, slot.insert_engine)) - -class SceneAuxBusesView(Gtk.TreeView): - def __init__(self, model): - Gtk.TreeView.__init__(self, model=model) - self.insert_column_with_attributes(0, "Name", Gtk.CellRendererText(), text=0) - self.insert_column_with_attributes(1, "Engine", Gtk.CellRendererText(), text=1) - def get_current_row(self): - if self.get_cursor()[0] is None: - return None, None - row = self.get_cursor()[0][0] - return row + 1, self.get_model()[row] - -class StatusBar(Gtk.Statusbar): - def __init__(self): - Gtk.Statusbar.__init__(self) - self.sample_rate_label = Gtk.Label(label="") - self.pack_start(self.sample_rate_label, False, False, 2) - self.status = self.get_context_id("Status") - self.sample_rate = self.get_context_id("Sample rate") - self.push(self.status, "") - self.push(self.sample_rate, "-") - def update(self, status, sample_rate): - self.pop(self.status) - self.push(self.status, status) - self.sample_rate_label.set_text("%s Hz" % sample_rate) - -class MainWindow(Gtk.Window): - def __init__(self): - Gtk.Window.__init__(self, type = Gtk.WindowType.TOPLEVEL) - self.vbox = Gtk.VBox(spacing = 5) - self.add(self.vbox) - self.create() - set_timer(self, 30, self.update) - #self.drum_pattern_editor = None - self.drumkit_editor = None - - def create(self): - self.menu_bar = Gtk.MenuBar() - - self.menu_bar.append(create_menu("_Scene", [ - ("_Load", self.load_scene), - ("_Quit", self.quit), - ])) - self.menu_bar.append(create_menu("_Layer", [ - ("_New", self.layer_new), - ("_Load", self.layer_load), - ("_Remove", self.layer_remove), - ])) - self.menu_bar.append(create_menu("_AuxBus", [ - ("_Add", self.aux_bus_add), - ("_Edit", self.aux_bus_edit), - ("_Remove", self.aux_bus_remove), - ])) - self.menu_bar.append(create_menu("_Tools", [ - ("_Drum Kit Editor", self.tools_drumkit_editor), - ("_Play Drum Pattern", self.tools_play_drum_pattern), - #("_Edit Drum Pattern", self.tools_drum_pattern_editor), - ("_Un-zombify", self.tools_unzombify), - ("_Object list", self.tools_object_list), - ("_Wave bank dump", self.tools_wave_bank_dump), - ])) - - self.vbox.pack_start(self.menu_bar, False, False, 0) - rt_status = cbox.Document.get_rt().status() - scene = cbox.Document.get_scene() - self.nb = Gtk.Notebook() - self.vbox.add(self.nb) - self.nb.append_page(self.create_master(scene), Gtk.Label(label="Master")) - self.status_bar = StatusBar() - self.vbox.pack_start(self.status_bar, False, False, 0) - self.create_instrument_pages(scene.status(), rt_status) - - def create_master(self, scene): - scene_status = scene.status() - self.master_info = left_label("") - self.timesig_info = left_label("") - - t = Gtk.Grid() - t.set_column_spacing(5) - t.set_row_spacing(5) - - t.attach(bold_label("Scene"), 0, 0, 1, 1) - self.scene_label = left_label(scene_status.name) - t.attach(self.scene_label, 1, 0, 2, 1) - - self.title_label = left_label(scene_status.title) - t.attach(bold_label("Title"), 0, 1, 1, 1) - t.attach(self.title_label, 1, 1, 2, 1) - - t.attach(bold_label("Play pos"), 0, 2, 1, 1) - t.attach(self.master_info, 1, 2, 2, 1) - - t.attach(bold_label("Time sig"), 0, 3, 1, 1) - t.attach(self.timesig_info, 1, 3, 2, 1) - hb = Gtk.HButtonBox() - b = Gtk.Button(label="Play") - b.connect('clicked', lambda w: cbox.Transport.play()) - hb.pack_start(b, False, False, 5) - b = Gtk.Button(label="Stop") - b.connect('clicked', lambda w: cbox.Transport.stop()) - hb.pack_start(b, False, False, 5) - b = Gtk.Button(label="Rewind") - b.connect('clicked', lambda w: cbox.Transport.seek_ppqn(0)) - hb.pack_start(b, False, False, 5) - b = Gtk.Button(label="Panic") - b.connect('clicked', lambda w: cbox.Transport.panic()) - hb.pack_start(b, False, False, 5) - t.attach(hb, 2, 3, 1, 1) - - t.attach(bold_label("Tempo"), 0, 4, 1, 1) - self.tempo_adj = Gtk.Adjustment(value=40, lower=40, upper=300, step_increment=1, page_increment=5, page_size=0) - self.tempo_adj.connect('value_changed', adjustment_changed_float, cbox.VarPath("/master/set_tempo")) - t.attach(standard_hslider(self.tempo_adj), 1, 4, 2, 1) - - t.attach(bold_label("Transpose"), 0, 5, 1, 1) - self.transpose_adj = Gtk.Adjustment(value=scene_status.transpose, lower=-24, upper=24, step_increment=1, page_increment=5, page_size=0) - self.transpose_adj.connect('value_changed', adjustment_changed_int, cbox.VarPath('/scene/transpose')) - t.attach(standard_align(Gtk.SpinButton(adjustment = self.transpose_adj), 0, 0, 0, 0), 1, 5, 2, 1) - - self.layers_model = SceneLayersModel() - self.layers_view = SceneLayersView(self.layers_model) - t.attach(standard_vscroll_window(-1, 160, self.layers_view), 0, 7, 3, 1) - - self.auxes_model = SceneAuxBusesModel() - self.auxes_view = SceneAuxBusesView(self.auxes_model) - t.attach(standard_vscroll_window(-1, 120, self.auxes_view), 0, 8, 3, 1) - - me = cbox.Document.get_engine().master_effect - me_status = me.status() - - hb = Gtk.HBox(spacing = 5) - self.master_chooser = fx_gui.InsertEffectChooser(me.path, "slot", me_status.insert_engine, me_status.insert_preset, me_status.bypass, self) - hb.pack_start(self.master_chooser.fx_engine, True, True, 0) - hb.pack_start(self.master_chooser.fx_preset, True, True, 5) - hb.pack_start(self.master_chooser.fx_edit, False, False, 5) - hb.pack_start(self.master_chooser.fx_bypass, False, False, 5) - - t.attach(bold_label("Master effect"), 0, 6, 1, 1) - t.attach(standard_align(hb, 0, 0, 0, 0), 1, 6, 3, 1) - - self.layers_model.refresh(scene_status) - self.auxes_model.refresh(scene_status) - - return t - - def quit(self, w): - self.destroy() - - def load_scene(self, w): - d = SceneDialog(self) - response = d.run() - try: - if response == Gtk.ResponseType.OK: - scene = cbox.Document.get_scene() - item_name, item_type, item_key, item_label = d.get_selected_object() - if item_type == 'Scene': - scene.load(item_name) - elif item_type == 'Layer': - scene.clear() - scene.add_layer(item_name) - elif item_type == 'Instrument': - scene.clear() - scene.add_instrument_layer(item_name) - scene_status = scene.status() - self.scene_label.set_text(scene_status.name) - self.title_label.set_text(scene_status.title) - self.refresh_instrument_pages(scene_status) - finally: - d.destroy() - - def layer_load(self, w): - d = LoadLayerDialog(self) - response = d.run() - try: - if response == Gtk.ResponseType.OK: - scene = cbox.Document.get_scene() - item_name, item_type, item_key, item_label = d.get_selected_object() - if item_type == 'Layer': - scene.add_layer(item_name) - elif item_type == 'Instrument': - scene.add_instrument_layer(item_name) - self.refresh_instrument_pages() - finally: - d.destroy() - - def layer_new(self, w): - d = NewLayerDialog(self) - response = d.run() - try: - if response == Gtk.ResponseType.OK: - scene = cbox.Document.get_scene() - keys = scene.status().instruments.keys() - engine_name = d.get_selected_object()[0] - for i in range(1, 1001): - name = "%s%s" % (engine_name, i) - if name not in keys: - break - scene.add_new_instrument_layer(name, engine_name) - self.refresh_instrument_pages() - finally: - d.destroy() - - def layer_remove(self, w): - if self.layers_view.get_cursor()[0] is not None: - pos = self.layers_view.get_cursor()[0][0] - cbox.Document.get_scene().delete_layer(pos) - self.refresh_instrument_pages() - - def aux_bus_add(self, w): - d = fx_gui.LoadEffectDialog(self) - response = d.run() - try: - cbox.do_cmd("/scene/load_aux", None, [d.get_selected_object()[0]]) - self.refresh_instrument_pages() - finally: - d.destroy() - def aux_bus_remove(self, w): - rowid, row = self.auxes_view.get_current_row() - if rowid is None: - return - cbox.do_cmd("/scene/delete_aux", None, [row[0]]) - self.refresh_instrument_pages() - - def aux_bus_edit(self, w): - rowid, row = self.auxes_view.get_current_row() - if rowid is None: - return - wclass = fx_gui.effect_window_map[row[1]] - popup = wclass("Aux: %s" % row[0], self, "/scene/aux/%s/slot/engine" % row[0]) - popup.show_all() - popup.present() - - def tools_unzombify(self, w): - cbox.do_cmd("/rt/cycle", None, []) - - def tools_drumkit_editor(self, w): - if self.drumkit_editor is None: - self.drumkit_editor = drumkit_editor.EditorDialog(self) - self.refresh_instrument_pages() - self.drumkit_editor.connect('destroy', self.on_drumkit_editor_destroy) - self.drumkit_editor.show_all() - self.drumkit_editor.present() - - def on_drumkit_editor_destroy(self, w): - self.drumkit_editor = None - - def tools_object_list(self, w): - cbox.Document.dump() - - def tools_wave_bank_dump(self, w): - for w in cbox.get_thing('/waves/list', '/waveform', [str]): - info = cbox.GetThings("/waves/info", ["filename", "name", "bytes", "loop"], [w]) - print("%s: %d bytes, loop = %s" % (info.filename, info.bytes, info.loop)) - - def tools_play_drum_pattern(self, w): - d = PlayPatternDialog(self) - response = d.run() - try: - if response == Gtk.ResponseType.OK: - row = d.get_selected_object() - if row[1] == 'Pattern': - song = cbox.Document().get_song() - song.loop_single_pattern(lambda: song.load_drum_pattern(row[0])) - elif row[1] == 'Track': - song = cbox.Document().get_song() - song.loop_single_pattern(lambda: song.load_drum_track(row[0])) - elif row[1] == 'Stop': - song = cbox.Document().get_song() - song.clear() - song.update_playback() - tracks = song.status().tracks - if len(tracks): - for track_item in tracks: - track_item.track.set_external_output(cbox.Document.get_scene().uuid) - song.update_playback() - - finally: - d.destroy() - - def tools_drum_pattern_editor(self, w): - if self.drum_pattern_editor is None: - length = drum_pattern_editor.PPQN * 4 - pat_data = cbox.Pattern.get_pattern() - if pat_data is not None: - pat_data, length = pat_data - self.drum_pattern_editor = drum_pattern_editor.DrumSeqWindow(length, pat_data) - self.drum_pattern_editor.set_title("Drum pattern editor") - self.drum_pattern_editor.show_all() - self.drum_pattern_editor.connect('destroy', self.on_drum_pattern_editor_destroy) - self.drum_pattern_editor.pattern.connect('changed', self.on_drum_pattern_changed) - self.drum_pattern_editor.pattern.changed() - self.drum_pattern_editor.present() - - def on_drum_pattern_changed(self, pattern): - data = bytearray() - for i in pattern.items(): - ch = i.channel - 1 - data += cbox.Pattern.serialize_event(int(i.pos), 0x90 + ch, int(i.row), int(i.vel)) - if i.len > 1: - data += cbox.Pattern.serialize_event(int(i.pos + i.len - 1), 0x80 + ch, int(i.row), int(i.vel)) - else: - data += cbox.Pattern.serialize_event(int(i.pos + 1), 0x80 + ch, int(i.row), int(i.vel)) - - length = pattern.get_length() - - song = cbox.Document().get_song() - song.loop_single_pattern(lambda: song.pattern_from_blob(data, length)) - - def on_drum_pattern_editor_destroy(self, w): - self.drum_pattern_editor = None - - def refresh_instrument_pages(self, scene_status = None): - self.delete_instrument_pages() - rt_status = cbox.Document.get_rt().status() - if scene_status is None: - scene_status = cbox.Document.get_scene().status() - self.layers_model.refresh(scene_status) - self.auxes_model.refresh(scene_status) - self.create_instrument_pages(scene_status, rt_status) - self.nb.show_all() - self.title_label.set_text(scene_status.title) - - def create_instrument_pages(self, scene_status, rt_status): - self.path_widgets = {} - self.path_popups = {} - self.fx_choosers = {} - - outputs_ls = Gtk.ListStore(GObject.TYPE_STRING, GObject.TYPE_INT) - for out in range(0, rt_status.audio_channels[1]//2): - outputs_ls.append(("Out %s/%s" % (out * 2 + 1, out * 2 + 2), out)) - - auxbus_ls = Gtk.ListStore(GObject.TYPE_STRING, GObject.TYPE_STRING) - auxbus_ls.append(("", "")) - for bus_name in scene_status.auxes.keys(): - auxbus_ls.append(("Aux: %s" % bus_name, bus_name)) - - for iname, (iengine, iobj) in scene_status.instruments.items(): - ipath = "/scene/instr/%s" % iname - idata = iobj.status() - #attribs = cbox.GetThings("/scene/instr_info", ['engine', 'name'], [i]) - #markup += 'Instrument %d: engine %s, name %s\n' % (i, attribs.engine, attribs.name) - b = Gtk.VBox(spacing = 5) - b.set_border_width(5) - b.pack_start(Gtk.Label(label="Engine: %s" % iengine), False, False, 5) - b.pack_start(Gtk.HSeparator(), False, False, 5) - t = Gtk.Table(n_rows=1 + idata.outputs, n_columns=7) - t.attach(bold_label("Instr. output", 0.5), 0, 1, 0, 1, Gtk.AttachOptions.SHRINK, Gtk.AttachOptions.SHRINK) - t.attach(bold_label("Send to", 0.5), 1, 2, 0, 1, Gtk.AttachOptions.SHRINK, Gtk.AttachOptions.SHRINK) - t.attach(bold_label("Gain [dB]", 0.5), 2, 3, 0, 1, 0, Gtk.AttachOptions.SHRINK) - t.attach(bold_label("Effect", 0.5), 3, 4, 0, 1, 0, Gtk.AttachOptions.SHRINK) - t.attach(bold_label("Preset", 0.5), 4, 7, 0, 1, 0, Gtk.AttachOptions.SHRINK) - b.pack_start(t, False, False, 5) - - y = 1 - for o in range(1, idata.outputs + 1): - is_aux = o >= idata.aux_offset - if not is_aux: - opath = "%s/output/%s" % (ipath, o) - output_name = "Out %s" % o - else: - opath = "%s/aux/%s" % (ipath, o - idata.aux_offset + 1) - output_name = "Aux %s" % (o - idata.aux_offset + 1) - odata = cbox.GetThings(opath + "/status", ['gain', 'output', 'bus', 'insert_engine', 'insert_preset', 'bypass'], []) - engine = odata.insert_engine - preset = odata.insert_preset - bypass = odata.bypass - - t.attach(Gtk.Label(label=output_name), 0, 1, y, y + 1, Gtk.AttachOptions.SHRINK, Gtk.AttachOptions.SHRINK) - - if not is_aux: - cb = standard_combo(outputs_ls, odata.output - 1) - cb.connect('changed', combo_value_changed, cbox.VarPath(opath + '/output'), 1) - else: - cb = standard_combo(auxbus_ls, ls_index(auxbus_ls, odata.bus, 1)) - cb.connect('changed', combo_value_changed_use_column, cbox.VarPath(opath + '/bus'), 1) - t.attach(cb, 1, 2, y, y + 1, Gtk.AttachOptions.SHRINK, Gtk.AttachOptions.SHRINK) - - adj = Gtk.Adjustment(value=odata.gain, lower=-96, upper=24, step_increment=1, page_increment=6, page_size=0) - adj.connect('value_changed', adjustment_changed_float, cbox.VarPath(opath + '/gain')) - t.attach(standard_hslider(adj), 2, 3, y, y + 1, Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK) - - chooser = fx_gui.InsertEffectChooser(opath, "%s: %s" % (iname, output_name), engine, preset, bypass, self) - self.fx_choosers[opath] = chooser - t.attach(chooser.fx_engine, 3, 4, y, y + 1, 0, Gtk.AttachOptions.SHRINK) - t.attach(chooser.fx_preset, 4, 5, y, y + 1, 0, Gtk.AttachOptions.SHRINK) - t.attach(chooser.fx_edit, 5, 6, y, y + 1, 0, Gtk.AttachOptions.SHRINK) - t.attach(chooser.fx_bypass, 6, 7, y, y + 1, 0, Gtk.AttachOptions.SHRINK) - y += 1 - if iengine in instr_gui.instrument_window_map: - b.pack_start(Gtk.HSeparator(), False, False, 5) - b.pack_start(instr_gui.instrument_window_map[iengine](iname, iobj), True, True, 5) - self.nb.append_page(b, Gtk.Label(label=iname)) - self.update() - - def delete_instrument_pages(self): - while self.nb.get_n_pages() > 1: - self.nb.remove_page(self.nb.get_n_pages() - 1) - - def update(self): - cbox.call_on_idle() - master = cbox.Transport.status() - if master.tempo is not None: - self.master_info.set_markup('%s (%s)' % (master.pos, master.pos_ppqn)) - self.timesig_info.set_markup("%s/%s" % tuple(master.timesig)) - self.tempo_adj.set_value(master.tempo) - state = cbox.Document.get_rt().status().state - self.status_bar.update(state[1], master.sample_rate) - return True - -def do_quit(window): - Gtk.main_quit() - -w = MainWindow() -w.set_title("My UI") -w.show_all() -w.connect('destroy', do_quit) - -Gtk.main() - diff --git a/template/calfbox/experiments/interactive.py b/template/calfbox/experiments/interactive.py deleted file mode 100755 index e2d23cc..0000000 --- a/template/calfbox/experiments/interactive.py +++ /dev/null @@ -1,57 +0,0 @@ -#! /usr/bin/env -S python3 -i -# -*- coding: utf-8 -*- -""" -This is a minimal calfbox python example. It is meant as a starting -point to find bugs and test performance. - -Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) - -This code is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" - -import atexit -from pprint import pprint -from calfbox import cbox - - -cbox.init_engine("") -cbox.Config.set("io", "outputs", 8) -NAME = "Cbox Interactive" -cbox.Config.set("io", "client_name", NAME) - -cbox.start_audio() -scene = cbox.Document.get_engine().new_scene() -scene.clear() - -trackName = "trackOne" -cboxMidiOutUuid = cbox.JackIO.create_midi_output(trackName) -calfboxTrack = cbox.Document.get_song().add_track() - - -pblob = bytes() -pblob += cbox.Pattern.serialize_event(0, 0x90, 60, 100) # note on -pblob += cbox.Pattern.serialize_event(383, 0x80, 60, 100) # note off -pattern = cbox.Document.get_song().pattern_from_blob(pblob, 384) -calfboxTrack.add_clip(0, 0, 384, pattern) #pos, offset, length(and not end-position, but is the same for the complete track), pattern -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. -cbox.Document.get_song().update_playback() - -print() - -def exit_handler(): - #Restore initial state and stop the engine - cbox.Transport.stop() - cbox.stop_audio() - cbox.shutdown_engine() -atexit.register(exit_handler) diff --git a/template/calfbox/experiments/meta.py b/template/calfbox/experiments/meta.py deleted file mode 100644 index 3651b53..0000000 --- a/template/calfbox/experiments/meta.py +++ /dev/null @@ -1,602 +0,0 @@ -#! /usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) - -This code is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" - - -import re -from calfbox import cbox #use the globally installed calfbox -from asyncio import get_event_loop -from sys import stdout, maxsize -import os, signal - -D1024 =210 * 2**0 # = 210. The lcm of 2, 3, 5, 7 . according to www.informatics.indiana.edu/donbyrd/CMNExtremes.htm this is the real world limit. -D512 = 210 * 2**1 -D256 = 210 * 2**2 -D128 = 210 * 2**3 -D64 = 210 * 2**4 -D32 = 210 * 2**5 -D16 = 210 * 2**6 #16th 13440 ticks -D8 = 210 * 2**7 #eigth 26880 ticks -D4 = 210 * 2**8 #quarter 53760 ticks -D2 = 210 * 2**9 #half 107520 ticks -D1 = 210 * 2**10 #whole 215040 ticks -DB = 210 * 2**11 #brevis 430080 ticks -DL = 210 * 2**12 #longa -DM = 210 * 2**13 #maxima -#MAXIMUM = 0x7FFFFFFF # 31bit. maximum number of calfbox ticks allowed for its timeline, for example for the song duration -MAXIMUM = 100 * D1 -#max_pulses = min(2**31, 2**31 * ppqn * bpm / (60 * sample_rate)) - -ly2pitch = { - "ceses,,," : 00, - "ces,,," : 10, - "c,,," : 20, - "cis,,," : 30, - "cisis,,," : 40, - "deses,,," : 50, - "des,,," : 60, - "d,,," : 70, - "dis,,," : 80, - "disis,,," : 90, - "eeses,,," : 100, - "ees,,," : 110, - "e,,," : 120, - "eis,,," : 130, - "eisis,,," : 140, - "feses,,," : 150, - "fes,,," : 160, - "f,,," : 170, - "fis,,," : 180, - "fisis,,," : 190, - "geses,,," : 200, - "ges,,," : 210, - "g,,," : 220, - "gis,,," : 230, - "gisis,,," : 240, - "aeses,,," : 250, - "aes,,," : 260, - "a,,," : 270, - "ais,,," : 280, - "aisis,,," : 290, - "beses,,," : 300, - "bes,,," : 310, - "b,,," : 320, - "bis,,," : 330, - "bisis,,," : 340, - "ceses,," : 350, - "ces,," : 360, - "c,," : 370, - "cis,," : 380, - "cisis,," : 390, - "deses,," : 400, - "des,," : 410, - "d,," : 420, - "dis,," : 430, - "disis,," : 440, - "eeses,," : 450, - "ees,," : 460, - "e,," : 470, - "eis,," : 480, - "eisis,," : 490, - "feses,," : 500, - "fes,," : 510, - "f,," : 520, - "fis,," : 530, - "fisis,," : 540, - "geses,," : 550, - "ges,," : 560, - "g,," : 570, - "gis,," : 580, - "gisis,," : 590, - "aeses,," : 600, - "aes,," : 610, - "a,," : 620, - "ais,," : 630, - "aisis,," : 640, - "beses,," : 650, - "bes,," : 660, - "b,," : 670, - "bis,," : 680, - "bisis,," : 690, - "ceses," : 700, - "ces," : 710, - "c," : 720, - "cis," : 730, - "cisis," : 740, - "deses," : 750, - "des," : 760, - "d," : 770, - "dis," : 780, - "disis," : 790, - "eeses," : 800, - "ees," : 810, - "e," : 820, - "eis," : 830, - "eisis," : 840, - "feses," : 850, - "fes," : 860, - "f," : 870, - "fis," : 880, - "fisis," : 890, - "geses," : 900, - "ges," : 910, - "g," : 920, - "gis," : 930, - "gisis," : 940, - "aeses," : 950, - "aes," : 960, - "a," : 970, - "ais," : 980, - "aisis," : 990, - "beses," : 1000, - "bes," : 1010, - "b," : 1020, - "bis," : 1030, - "bisis," : 1040, - "ceses" : 1050, - "ces" : 1060, - "c" : 1070, - "cis" : 1080, - "cisis" : 1090, - "deses" : 1100, - "des" : 1110, - "d" : 1120, - "dis" : 1130, - "disis" : 1140, - "eeses" : 1150, - "ees" : 1160, - "e" : 1170, - "eis" : 1180, - "eisis" : 1190, - "feses" : 1200, - "fes" : 1210, - "f" : 1220, - "fis" : 1230, - "fisis" : 1240, - "geses" : 1250, - "ges" : 1260, - "g" : 1270, - "gis" : 1280, - "gisis" : 1290, - "aeses" : 1300, - "aes" : 1310, - "a" : 1320, - "ais" : 1330, - "aisis" : 1340, - "beses" : 1350, - "bes" : 1360, - "b" : 1370, - "bis" : 1380, - "bisis" : 1390, - "ceses'" : 1400, - "ces'" : 1410, - "c'" : 1420, - "cis'" : 1430, - "cisis'" : 1440, - "deses'" : 1450, - "des'" : 1460, - "d'" : 1470, - "dis'" : 1480, - "disis'" : 1490, - "eeses'" : 1500, - "ees'" : 1510, - "e'" : 1520, - "eis'" : 1530, - "eisis'" : 1540, - "feses'" : 1550, - "fes'" : 1560, - "f'" : 1570, - "fis'" : 1580, - "fisis'" : 1590, - "geses'" : 1600, - "ges'" : 1610, - "g'" : 1620, - "gis'" : 1630, - "gisis'" : 1640, - "aeses'" : 1650, - "aes'" : 1660, - "a'" : 1670, - "ais'" : 1680, - "aisis'" : 1690, - "beses'" : 1700, - "bes'" : 1710, - "b'" : 1720, - "bis'" : 1730, - "bisis'" : 1740, - "ceses''" : 1750, - "ces''" : 1760, - "c''" : 1770, - "cis''" : 1780, - "cisis''" : 1790, - "deses''" : 1800, - "des''" : 1810, - "d''" : 1820, - "dis''" : 1830, - "disis''" : 1840, - "eeses''" : 1850, - "ees''" : 1860, - "e''" : 1870, - "eis''" : 1880, - "eisis''" : 1890, - "feses''" : 1900, - "fes''" : 1910, - "f''" : 1920, - "fis''" : 1930, - "fisis''" : 1940, - "geses''" : 1950, - "ges''" : 1960, - "g''" : 1970, - "gis''" : 1980, - "gisis''" : 1990, - "aeses''" : 2000, - "aes''" : 2010, - "a''" : 2020, - "ais''" : 2030, - "aisis''" : 2040, - "beses''" : 2050, - "bes''" : 2060, - "b''" : 2070, - "bis''" : 2080, - "bisis''" : 2090, - "ceses'''" : 2100, - "ces'''" : 2110, - "c'''" : 2120, - "cis'''" : 2130, - "cisis'''" : 2140, - "deses'''" : 2150, - "des'''" : 2160, - "d'''" : 2170, - "dis'''" : 2180, - "disis'''" : 2190, - "eeses'''" : 2200, - "ees'''" : 2210, - "e'''" : 2220, - "eis'''" : 2230, - "eisis'''" : 2240, - "feses'''" : 2250, - "fes'''" : 2260, - "f'''" : 2270, - "fis'''" : 2280, - "fisis'''" : 2290, - "geses'''" : 2300, - "ges'''" : 2310, - "g'''" : 2320, - "gis'''" : 2330, - "gisis'''" : 2340, - "aeses'''" : 2350, - "aes'''" : 2360, - "a'''" : 2370, - "ais'''" : 2380, - "aisis'''" : 2390, - "beses'''" : 2400, - "bes'''" : 2410, - "b'''" : 2420, - "bis'''" : 2430, - "bisis'''" : 2440, - "ceses''''" : 2450, - "ces''''" : 2460, - "c''''" : 2470, - "cis''''" : 2480, - "cisis''''" : 2490, - "deses''''" : 2500, - "des''''" : 2510, - "d''''" : 2520, - "dis''''" : 2530, - "disis''''" : 2540, - "eeses''''" : 2550, - "ees''''" : 2560, - "e''''" : 2570, - "eis''''" : 2580, - "eisis''''" : 2590, - "feses''''" : 2600, - "fes''''" : 2610, - "f''''" : 2620, - "fis''''" : 2630, - "fisis''''" : 2640, - "geses''''" : 2650, - "ges''''" : 2660, - "g''''" : 2670, - "gis''''" : 2680, - "gisis''''" : 2690, - "aeses''''" : 2700, - "aes''''" : 2710, - "a''''" : 2720, - "ais''''" : 2730, - "aisis''''" : 2740, - "beses''''" : 2750, - "bes''''" : 2760, - "b''''" : 2770, - "bis''''" : 2780, - "bisis''''" : 2790, - "ceses'''''" : 2800, - "ces'''''" : 2810, - "c'''''" : 2820, - "cis'''''" : 2830, - "cisis'''''" : 2840, - "deses'''''" : 2850, - "des'''''" : 2860, - "d'''''" : 2870, - "dis'''''" : 2880, - "disis'''''" : 2890, - "eeses'''''" : 2900, - "ees'''''" : 2910, - "e'''''" : 2920, - "eis'''''" : 2930, - "eisis'''''" : 2940, - "feses'''''" : 2950, - "fes'''''" : 2960, - "f'''''" : 2970, - "fis'''''" : 2980, - "fisis'''''" : 2990, - "geses'''''" : 3000, - "ges'''''" : 3010, - "g'''''" : 3020, - "gis'''''" : 3030, - "gisis'''''" : 3040, - "aeses'''''" : 3050, - "aes'''''" : 3060, - "a'''''" : 3070, - "ais'''''" : 3080, - "aisis'''''" : 3090, - "beses'''''" : 3100, - "bes'''''" : 3110, - "b'''''" : 3120, - "bis'''''" : 3130, - "bisis'''''" : 3140, - #"r" : float('inf'), a rest is not a pitch - } - -def plain(pitch): - """ Extract the note from a note-number, without any octave but with the tailing zero. - This means we double-use the lowest octave as abstract version.""" - #Dividing through the octave, 350, results in the number of the octave and the note as remainder. - return divmod(pitch, 350)[1] - -def octave(pitch): - """Return the octave of given note. Lowest 0 is X,,,""" - return divmod(pitch, 350)[0] - -def halfToneDistanceFromC(pitch): - """Return the half-tone step distance from C. The "sounding" interval""" - return { - #00 : 10, # ceses,,, -> bes - #10 : 11, # ces,,, -> b - - 00 : -2, # ceses,,, -> bes - 10 : -1, # ces,,, -> b - 20 : 0, # c,,, - 30 : 1, # cis,,, - 40 : 2, # cisis,,, -> d ... - 50 : 0, # deses,,, - 60 : 1, # des,,, - 70 : 2, # d,,, - 80 : 3, # dis,,, - 90 : 4, # disis,,, - 100 : 2, # eeses,,, - 110 : 3, # ees,,, - 120 : 4, # e,,, - 130 : 5, # eis,,, - 140 : 6, # eisis,,, - 150 : 3, # feses,,, - 160 : 4, # fes,,, - 170 : 5, # f,,, - 180 : 6, # fis,,, - 190 : 7, # fisis,,, - 200 : 5, # geses,,, - 210 : 6, # ges,,, - 220 : 7, # g,,, - 230 : 8, # gis,,, - 240 : 9, # gisis,,, - 250 : 7, # aeses,,, - 260 : 8, # aes,,, - 270 : 9, # a,,, - 280 : 10, # ais,,, - 290 : 11, # aisis,,, - 300 : 9, # beses,,, - 310 : 10, # bes,,, - 320 : 11, # b,,, - 330 : 12, # bis,,, - 340 : 13, # bisis,,, - #330 : 0, # bis,,, - #340 : 1, # bisis,,, - }[plain(pitch)] - - -lyToMidi = {} #filled for all pitches on startup, below -for ly, pitch in ly2pitch.items(): - octOffset = (octave(pitch) +1) * 12 #twelve tones per midi octave - lyToMidi[ly] = octOffset + halfToneDistanceFromC(pitch) - - -lyToTicks = { - "16" : D16, - "8" : D8, - "4" : D4, - "2" : D2, - "1" : D1, - } - - - -def ly(lilypondString): - """Take string of simple lilypond notes, return midi pitches as generator of (pitch, ticks)""" - lastDur = "4" - for lyNote in lilypondString.split(" "): - try: - lyPitch, lyDur = re.split(r'(\d+)', lyNote)[0:2] - lastDur = lyDur - except ValueError: - lyPitch = re.split(r'(\d+)', lyNote)[0] - lyDur = lastDur - - yield (lyToMidi[lyPitch], lyToTicks[lyDur]) - - -def ly2cbox(lilypondString): - """Return (pbytes, durationInTicks) - a python byte data type with midi data for cbox""" - #cbox.Pattern.serialize_event(position, midibyte1 (noteon), midibyte2(pitch), midibyte3(velocity)) - pblob = bytes() - startTick = 0 - for midiPitch, durationInTicks in ly(lilypondString): - endTick = startTick + durationInTicks - 1 #-1 ticks to create a small logical gap. This is nothing compared to our tick value dimensions, but it is enough for the midi protocol to treat two notes as separate ones. Imporant to say that this does NOT affect the next note on. This will be mathematically correct anyway. - pblob += cbox.Pattern.serialize_event(startTick, 0x90, midiPitch, 100) # note on - pblob += cbox.Pattern.serialize_event(endTick , 0x80, midiPitch, 100) # note off - startTick = startTick + durationInTicks #no -1 for the next note - return pblob, startTick - - -cboxTracks = {} #trackName:(cboxTrack,cboxMidiOutUuid) -def cboxSetTrack(trackName, durationInTicks, pattern): - """Creates or resets calfbox tracks including jack connections - Keeps jack connections alive. - - pattern is most likely a single pattern created through cbox.Document.get_song().pattern_from_blob - But it can also be a list of such patterns. In this case all patterns must be the same duration - and the parameter durationInTicks is the length of ONE pattern. - """ - - if not trackName in cboxTracks: - cboxMidiOutUuid = cbox.JackIO.create_midi_output(trackName) - calfboxTrack = cbox.Document.get_song().add_track() - cboxTracks[trackName] = (calfboxTrack, cboxMidiOutUuid) - else: - calfboxTrack, cboxMidiOutUuid = cboxTracks[trackName] - calfboxTrack.delete() - - calfboxTrack = cbox.Document.get_song().add_track() - calfboxTrack.set_external_output(cboxMidiOutUuid) - cbox.JackIO.rename_midi_output(cboxMidiOutUuid, trackName) - calfboxTrack.set_name(trackName) - - if type(pattern) is cbox.DocPattern: - calfboxTrack.add_clip(0, 0, durationInTicks, pattern) #pos, offset, length(and not end-position, but is the same for the complete track), pattern - else: #iterable - assert iter(pattern) - #durationInTicks is the length of ONE pattern. - for i, pat in enumerate(pattern): - calfboxTrack.add_clip(i*durationInTicks, 0, durationInTicks, pat) #pos, offset, length, pattern. - calfboxTrack.add_clip(i*durationInTicks, 0, durationInTicks, pat) #pos, offset, length, pattern. - - return calfboxTrack - - -def ly2Track(trackName, lyString): - """Convert a simple string of lilypond notes to a cbox track and add that to the score""" - music = lyString - cboxBlob, durationInTicks = ly2cbox(music) - pattern = cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks) - cboxSetTrack(trackName, durationInTicks, pattern) - -def getLongestTrackDuationInTicks(): - return max( max(clip.pos + clip.length for clip in cboxTrack.track.status().clips) for cboxTrack in cbox.Document.get_song().status().tracks if cboxTrack.track.status().clips) - #for cboxTrack in cbox.Document.get_song().status().tracks: - # print (cboxTrack.track) - #TODO: These are different than the one above. It is more. Why? - #for cboxTrack, cboxMidiOutUuid in cboxTracks.values(): - # print (cboxTrack.status()) - - -def cboxLoop(eventLoop): - cbox.call_on_idle() - assert eventLoop.is_running() - - #it is not that simple. status = "[Running]" if cbox.Transport.status().playing else "[Stopped]" - if cbox.Transport.status().playing == 1: - status = "[Running]" - elif cbox.Transport.status().playing == 0: - status = "[Stopped]" - elif cbox.Transport.status().playing == 2: - status = "[Stopping]" - elif cbox.Transport.status().playing is None: - status = "[Uninitialized]" - else: - raise ValueError("Unknown playback status: {}".format(cbox.Transport.status().playing)) - - stdout.write(" \r") #it is a hack but it cleans the line from old artefacts - stdout.write('{}: {}\r'.format(status, cbox.Transport.status().pos_ppqn)) - stdout.flush() - eventLoop.call_later(0.1, cboxLoop, eventLoop) #100ms delay - -eventLoop = get_event_loop() -def initCbox(clientName, internalEventProcessor=True, commonMidiInput=True): - cbox.init_engine("") - cbox.Config.set("io", "client_name", clientName) - cbox.Config.set("io", "enable_common_midi_input", commonMidiInput) #the default "catch all" midi input - cbox.start_audio() - scene = cbox.Document.get_engine().new_scene() - scene.clear() - cbox.do_cmd("/master/set_ppqn_factor", None, [D4]) #quarter note has how many ticks? - - cbox.Transport.stop() - cbox.Transport.seek_ppqn(0) - cbox.Transport.set_tempo(120.0) #must be float - - if internalEventProcessor: - eventLoop.call_soon(cboxLoop, eventLoop) - return scene, cbox, eventLoop - - -def connectPhysicalKeyboards(port="midi"): - midiKeyboards = cbox.JackIO.get_ports(".*", cbox.JackIO.MIDI_TYPE, cbox.JackIO.PORT_IS_SOURCE | cbox.JackIO.PORT_IS_PHYSICAL) - ourMidiInPort = cbox.Config.get("io", "client_name",) + ":" + port - for keyboard in midiKeyboards: - cbox.JackIO.port_connect(keyboard, ourMidiInPort) - -def start(autoplay = False, userfunction = None, tempo = 120): - def ask_exit(): - print() - eventLoop.stop() - shutdownCbox() - - try: - dur = getLongestTrackDuationInTicks() - print("Starting with supplied music data. Setting sond duration to longest track") - assert dur > 0 - cbox.Document.get_song().set_loop(dur, dur) #set playback length for the entire score. - except ValueError: - print ("Starting without a track. Setting song duration to a high value to generate recording space") - cbox.Document.get_song().set_loop(MAXIMUM, MAXIMUM) #set playback length for the entire score. - - cbox.Transport.set_tempo(float(tempo)) - cbox.Document.get_song().update_playback() - - for signame in ('SIGINT', 'SIGTERM'): - eventLoop.add_signal_handler(getattr(signal, signame), ask_exit) - - if userfunction: - print ("Send SIGUSR1 with following command to trigger user function") - print ("kill -10 {}".format(os.getpid())) - print () - eventLoop.add_signal_handler(getattr(signal, "SIGUSR1"), userfunction) - - print ("Use jack transport to control playback") - print ("Press Ctrl+C to abort") - print("pid %s: send SIGINT or SIGTERM to exit." % os.getpid()) - - try: - eventLoop.run_forever() - finally: - eventLoop.close() - - -def shutdownCbox(): - cbox.Transport.stop() - cbox.Transport.seek_ppqn(0) - cbox.stop_audio() - cbox.shutdown_engine() diff --git a/template/calfbox/experiments/playPatternsAsMeasures.py b/template/calfbox/experiments/playPatternsAsMeasures.py deleted file mode 100755 index 8f3657f..0000000 --- a/template/calfbox/experiments/playPatternsAsMeasures.py +++ /dev/null @@ -1,51 +0,0 @@ -#! /usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) - -This code is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" - -from meta import ly2cbox, cboxSetTrack, initCbox, start, D4 - -scene, cbox, eventLoop = initCbox("test02") - -#Generate Music -music = "c'4 d' e' f'" -cboxBlob, durationInTicks = ly2cbox(music) -#cboxSetTrack("someInstrument", durationInTicks, pattern) - -oneMeasureInTicks = D4 * 4 -patternList = [ - cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks), - cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks), - cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks), - cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks), - cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks), - cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks), - cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks), - cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks), -] - -cboxSetTrack("metronome", oneMeasureInTicks, patternList) - -def userfunction(): - D4 = 210 * 2**8 - MEASURE = 4 * D4 - cbox.Transport.stop() - cbox.Document.get_song().update_playback() - cbox.Transport.seek_ppqn(4 * MEASURE + 2 * D4 ) #4th measure in the middle - cbox.Transport.play() - -start(userfunction=userfunction) diff --git a/template/calfbox/experiments/printAllMidiEvents.py b/template/calfbox/experiments/printAllMidiEvents.py deleted file mode 100755 index 394663d..0000000 --- a/template/calfbox/experiments/printAllMidiEvents.py +++ /dev/null @@ -1,32 +0,0 @@ -#! /usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) - -This code is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" - -from meta import initCbox, start, connectPhysicalKeyboards - -def processMidiIn(eventLoop): - eventList = cbox.get_new_events() - if eventList: - for (address, uninterestingStuff, event) in eventList: #we are only interested in the event, which is a list again. - print (address, "event:", event, "playback:", cbox.Transport.status().playing) - eventLoop.call_later(0.1, processMidiIn, eventLoop) - -scene, cbox, eventLoop = initCbox("test01", internalEventProcessor=False) -eventLoop.call_later(0.1, processMidiIn, eventLoop) #100ms -connectPhysicalKeyboards() -start() diff --git a/template/calfbox/experiments/printAllMidiEventsSpecificPort.py b/template/calfbox/experiments/printAllMidiEventsSpecificPort.py deleted file mode 100755 index 0f9c218..0000000 --- a/template/calfbox/experiments/printAllMidiEventsSpecificPort.py +++ /dev/null @@ -1,35 +0,0 @@ -#! /usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) - -This code is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" - -from meta import initCbox, start, connectPhysicalKeyboards - -def processMidiIn(eventLoop): - eventList = cbox.JackIO.get_new_events(cboxMidiPortUid) - if eventList: - for (address, uninterestingStuff, event) in eventList: #we are only interested in the event, which is a list again. - print (address, "event:", event, "playback:", cbox.Transport.status().playing) - eventLoop.call_later(0.1, processMidiIn, eventLoop) - -scene, cbox, eventLoop = initCbox("test01", internalEventProcessor=False, commonMidiInput=False) -cboxMidiPortUid = cbox.JackIO.create_midi_input("customInput") -cbox.JackIO.set_appsink_for_midi_input(cboxMidiPortUid, True) #This sounds like a program wide sink, but it is needed for every port. -cbox.JackIO.route_midi_input(cboxMidiPortUid, scene.uuid) -eventLoop.call_later(0.1, processMidiIn, eventLoop) #100ms -connectPhysicalKeyboards(port="customInput") -start() diff --git a/template/calfbox/experiments/printNotesOnlyDuringPlayback.py b/template/calfbox/experiments/printNotesOnlyDuringPlayback.py deleted file mode 100755 index 29a5715..0000000 --- a/template/calfbox/experiments/printNotesOnlyDuringPlayback.py +++ /dev/null @@ -1,79 +0,0 @@ -#! /usr/bin/env python3 -# -*- coding: utf-8 -*- -from meta import ly2Track, initCbox, start, connectPhysicalKeyboards - -def processMidiIn(eventLoop): - """cbox event: ("event address", None, [firstByte, pitch, velocit]) - - Cbox event come in pairs - - first is the normal midi-event + channel - channel = first & 0x0F - mode = first & 0xF0 - - Of course it doesn't need to be pitch and velocity. - But this is easier to document than "data2, data3" - - Either call cbox.call_on_idle or cbox.get_new_events. - Both will clean the event queue but only the latter will give us - the results as python data. - """ - eventList = cbox.get_new_events() - lenList = len(eventList) - if lenList >= 2 and lenList % 2 == 0: - for (address, uninterestingStuff, event) in eventList: #we are only interested in the event, which is a list again. - #print (address, "event:", event, "playback:", cbox.Transport.status().playing) - if address in ("/io/midi/event_time_samples", "/io/midi/event_time_ppqn", ): - assert len(event) == 1 - #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. - if processMidiIn.lastTimestamp: - raise NotImplementedError("the previous event didn't eat up the timestamp") - else: - if address == "/io/midi/event_time_ppqn": - assert cbox.Transport.status().playing == 1 - during_recording = True - else: - assert not cbox.Transport.status().playing == 1 - during_recording = False - processMidiIn.lastTimestamp = event[0] - - elif address == "/io/midi/simple_event" and len(event) == 3: #we can only unpack the event after knowing its length. - assert processMidiIn.lastTimestamp # Midi events are always preceded by timestamps - first, second, third = event - channel = first & 0x0F - mode = first & 0xF0 #0x90 note on, 0x80 note off and so on. - - if mode == 0x90: #Note On. 144 in decimal - midipitch = second - velocity = third - if during_recording: - print("ON: {}, Channel: {}, Pitch: {}, Velocity: {}".format(processMidiIn.lastTimestamp, channel, midipitch, velocity)) - #else: - # print("ON Time-Samples: {}, Channel: {}, Pitch: {}, Velocity: {}".format(processMidiIn.lastTimestamp, channel, midipitch, velocity)) - - elif mode == 0x80: #Note Off. 128 in decimal - midipitch = second - velocity = third - if during_recording: - print("OFF: {}, Channel: {}, Pitch: {}, Velocity: {}".format(processMidiIn.lastTimestamp, channel, midipitch, velocity)) - - #elif mode == 0xB0: #CC - # ccNumber = second - # ccValue = third - #else: - #discard the events - - processMidiIn.lastTimestamp = None - - else: - raise NotImplementedError("Address type {} unknown".format(address)) - - - eventLoop.call_later(0.1, processMidiIn, eventLoop) -processMidiIn.lastTimestamp = None - -scene, cbox, eventLoop = initCbox("test01", internalEventProcessor=False) -#ly2Track(trackName="doWeNeedThis", lyString="c8") -eventLoop.call_later(0.1, processMidiIn, eventLoop) #100ms -connectPhysicalKeyboards() -start() diff --git a/template/calfbox/experiments/simplePlayback.py b/template/calfbox/experiments/simplePlayback.py deleted file mode 100755 index 6312894..0000000 --- a/template/calfbox/experiments/simplePlayback.py +++ /dev/null @@ -1,30 +0,0 @@ -#! /usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) - -This code is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" - -from meta import ly2cbox, cboxSetTrack, initCbox, start - -scene, cbox, eventLoop = initCbox("test01") - -#Generate Music -music = "c'4 d' e' f'2 g' c''" -cboxBlob, durationInTicks = ly2cbox(music) -pattern = cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks) -cboxSetTrack("someInstrument", durationInTicks, pattern) - -start() diff --git a/template/calfbox/experiments/simplerPlayback.py b/template/calfbox/experiments/simplerPlayback.py deleted file mode 100755 index 43e8c7d..0000000 --- a/template/calfbox/experiments/simplerPlayback.py +++ /dev/null @@ -1,25 +0,0 @@ -#! /usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) - -This code is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" - -from meta import ly2Track, initCbox, start - -scene, cbox, eventLoop = initCbox("test01") - -ly2Track(trackName="someInstrument", lyString="c'4 d' e' f'2 g' c''") -start() diff --git a/template/calfbox/experiments/testmetadata.py b/template/calfbox/experiments/testmetadata.py deleted file mode 100755 index 7f50180..0000000 --- a/template/calfbox/experiments/testmetadata.py +++ /dev/null @@ -1,82 +0,0 @@ -#! /usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -This is a minimal calfbox python example. It is meant as a starting -point to find bugs and test performance. - -Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) - -This code is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" - -import atexit -from pprint import pprint -from calfbox import cbox - - -cbox.init_engine("") -cbox.Config.set("io", "outputs", 8) -NAME = "Cbox Interactive" -cbox.Config.set("io", "client_name", NAME) - -cbox.start_audio() -scene = cbox.Document.get_engine().new_scene() -scene.clear() - -print("Setting nonsense meta data to our first two ports and midi port") -cbox.JackIO.Metadata.set_property("Cbox Interactive:out_1", "foo", "bar") -cbox.JackIO.Metadata.set_property("Cbox Interactive:out_1", "faz", "baz") -cbox.JackIO.Metadata.set_property("Cbox Interactive:out_2", "rolf", "hello") -cbox.JackIO.Metadata.set_property("Cbox Interactive:out_2", "rolf", "hello") -cbox.JackIO.Metadata.set_property("Cbox Interactive:midi", "wolf", "world", "stryng") -cbox.JackIO.Metadata.set_property("Cbox Interactive:midi", "asd", "qwe", "") - - -print ("Setting port order for all 8 ports") - -portOrderDict = { - "Cbox Interactive:out_1": 50, - "Cbox Interactive:out_2": 40, - "Cbox Interactive:out_3": 3, - "Cbox Interactive:out_4": 5, - "Cbox Interactive:out_5": 7, - "Cbox Interactive:out_6": 999, - "Cbox Interactive:out_7": 4, - "Cbox Interactive:out_8": 4, - } - -try: - cbox.JackIO.Metadata.set_all_port_order(portOrderDict) - print ("Test to catch non-unique indices failed!. Quitting") - quit() -except ValueError as e: - print ("Caught expected ValueError for double index entry.\nAdjusting value and try again to set all ports.") - -portOrderDict["Cbox Interactive:out_8"] = 0 -cbox.JackIO.Metadata.set_all_port_order(portOrderDict) - -print("List of all metadata follows") -pprint (cbox.JackIO.Metadata.get_all_properties()) - -print() -print ("Now check your port order in QJackCtl or similar. Press [Return] to quit") -input() #wait for key to confirm order visually in qjackctl -quit() - -def exit_handler(): - #Restore initial state and stop the engine - cbox.Transport.stop() - cbox.stop_audio() - cbox.shutdown_engine() -atexit.register(exit_handler) diff --git a/template/calfbox/fbr.c b/template/calfbox/fbr.c deleted file mode 100644 index f4bd803..0000000 --- a/template/calfbox/fbr.c +++ /dev/null @@ -1,373 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "biquad-float.h" -#include "config.h" -#include "config-api.h" -#include "dspmath.h" -#include "eq.h" -#include "module.h" -#include "rt.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#define MODULE_PARAMS feedback_reducer_params - -#define MAX_FBR_BANDS 16 - -#define ANALYSIS_BUFFER_SIZE 8192 -#define ANALYSIS_BUFFER_BITS 13 - -// Sine table -static complex float euler_table[ANALYSIS_BUFFER_SIZE]; - -// Bit reversal table -static int map_table[ANALYSIS_BUFFER_SIZE]; - -// Bit-reversed von Hann window -static float von_hann_window_transposed[ANALYSIS_BUFFER_SIZE]; - -struct feedback_reducer_params -{ - struct eq_band bands[MAX_FBR_BANDS]; -}; - -struct feedback_reducer_module -{ - struct cbox_module module; - - struct feedback_reducer_params *params, *old_params; - - struct cbox_biquadf_coeffs coeffs[MAX_FBR_BANDS]; - struct cbox_biquadf_state state[MAX_FBR_BANDS][2]; - - float analysis_buffer[ANALYSIS_BUFFER_SIZE]; - float *wrptr; - int analysed; - - complex float fft_buffers[2][ANALYSIS_BUFFER_SIZE]; -}; - -// Trivial implementation of Cooley-Tukey (+ my own mistakes) + von Hann window -static int do_fft(struct feedback_reducer_module *m) -{ - // Copy + bit reversal addressing - for (int i = 0; i < ANALYSIS_BUFFER_SIZE; i++) - { - m->fft_buffers[0][i] = von_hann_window_transposed[i] * m->analysis_buffer[map_table[i]] * (2.0 / ANALYSIS_BUFFER_SIZE); - } - - for (int i = 0; i < ANALYSIS_BUFFER_BITS; i++) - { - complex float *src = m->fft_buffers[i & 1]; - complex float *dst = m->fft_buffers[(~i) & 1]; - int invi = ANALYSIS_BUFFER_BITS - i - 1; - int disp = 1 << i; - int mask = disp - 1; - - for (int j = 0; j < ANALYSIS_BUFFER_SIZE / 2; j++) - { - int jj1 = (j & mask) + ((j & ~mask) << 1); // insert 0 at i'th bit to get the left arm of the butterfly - int jj2 = jj1 + disp; // insert 1 at i'th bit to get the right arm - - // e^iw - complex float eiw1 = euler_table[(jj1 << invi) & (ANALYSIS_BUFFER_SIZE - 1)]; - complex float eiw2 = euler_table[(jj2 << invi) & (ANALYSIS_BUFFER_SIZE - 1)]; - - // printf("%d -> %d, %d\n", j, jj, jj + disp); - butterfly(&dst[jj1], &dst[jj2], src[jj1], src[jj2], eiw1, eiw2); - } - } - return ANALYSIS_BUFFER_BITS & 1; -} - -#define PEAK_REGION_RADIUS 3 - -struct potential_peak_info -{ - int bin; - float avg; - float centre; - float peak; - float dist; - float points; -}; - -static int peak_compare(const void *peak1, const void *peak2) -{ - const struct potential_peak_info *pi1 = peak1; - const struct potential_peak_info *pi2 = peak2; - - if (pi1->points < pi2->points) - return +1; - if (pi1->points > pi2->points) - return -1; - return 0; -} - -static int find_peaks(complex float *spectrum, float srate, float peak_freqs[16]) -{ - struct potential_peak_info pki[ANALYSIS_BUFFER_SIZE / 2 + 1]; - for (int i = 0; i <= ANALYSIS_BUFFER_SIZE / 2; i++) - { - pki[i].bin = i; - pki[i].points = 0.f; - } - float gmax = 0; - for (int i = PEAK_REGION_RADIUS; i <= ANALYSIS_BUFFER_SIZE / 2 - PEAK_REGION_RADIUS; i++) - { - struct potential_peak_info *pi = &pki[i]; - float sum = 0; - float sumf = 0; - float peak = 0; - for (int j = -PEAK_REGION_RADIUS; j <= PEAK_REGION_RADIUS; j++) - { - float f = (i + j); - float bin = cabs(spectrum[i + j]); - if (bin > peak) - peak = bin; - sum += bin; - sumf += f * bin; - } - pi->avg = sum / (2 * PEAK_REGION_RADIUS + 1); - pi->peak = peak; - pi->centre = sumf / sum; - pi->dist = (sumf / sum - i); - if (peak > gmax) - gmax = peak; - // printf("Bin %d sumf/sum %f avg %f peak %f p/a %f dist %f val %f\n", i, sumf / sum, pki[i].avg, peak, peak / pki[i].avg, sumf/sum - i, cabs(spectrum[i])); - } - for (int i = PEAK_REGION_RADIUS; i <= ANALYSIS_BUFFER_SIZE / 2 - PEAK_REGION_RADIUS; i++) - { - struct potential_peak_info *tpi = &pki[i]; - // ignore peaks below -40dB of the max bin - if (pki[(int)tpi->centre].peak < gmax * 0.01) - continue; - pki[(int)tpi->centre].points += 1; - } - #if 0 - for (int i = 0; i <= ANALYSIS_BUFFER_SIZE / 2; i++) - { - float freq = i * srate / ANALYSIS_BUFFER_SIZE; - printf("Bin %d freq %f points %f\n", i, freq, pki[i].points); - } - #endif - qsort(pki, ANALYSIS_BUFFER_SIZE / 2 + 1, sizeof(struct potential_peak_info), peak_compare); - - float peaks[16]; - int peak_count = 0; - for (int i = 0; i <= ANALYSIS_BUFFER_SIZE / 2; i++) - { - if (pki[i].points <= 1) - break; - if (pki[i].peak <= 0.0001) - break; - gboolean dupe = FALSE; - for (int j = 0; j < peak_count; j++) - { - if (fabs(peaks[j] - pki[i].centre) < PEAK_REGION_RADIUS) - { - dupe = TRUE; - break; - } - } - if (dupe) - continue; - peak_freqs[peak_count] = pki[i].centre * srate / ANALYSIS_BUFFER_SIZE; - peaks[peak_count++] = pki[i].centre; - printf("Mul %f freq %f points %f peak %f\n", pki[i].centre, pki[i].centre * srate / ANALYSIS_BUFFER_SIZE, pki[i].points, pki[i].peak); - if (peak_count == 4) - break; - } - return peak_count; -} - -static void redo_filters(struct feedback_reducer_module *m) -{ - for (int i = 0; i < MAX_FBR_BANDS; i++) - { - struct eq_band *band = &m->params->bands[i]; - if (band->active) - { - cbox_biquadf_set_peakeq_rbj(&m->coeffs[i], band->center, band->q, band->gain, m->module.srate); - } - } - m->old_params = m->params; -} - -gboolean feedback_reducer_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct feedback_reducer_module *m = (struct feedback_reducer_module *)ct->user_data; - - EFFECT_PARAM_ARRAY("/active", "i", bands, active, int, , 0, 1) else - EFFECT_PARAM_ARRAY("/center", "f", bands, center, double, , 10, 20000) else - EFFECT_PARAM_ARRAY("/q", "f", bands, q, double, , 0.01, 100) else - EFFECT_PARAM_ARRAY("/gain", "f", bands, gain, double, dB2gain_simple, -100, 100) else - if (!strcmp(cmd->command, "/start") && !strcmp(cmd->arg_types, "")) - { - m->analysed = 0; - cbox_rt_swap_pointers(m->module.rt, (void **)&m->wrptr, m->analysis_buffer); - } - else if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - if (m->wrptr == m->analysis_buffer + ANALYSIS_BUFFER_SIZE && m->analysed == 0) - { - float freqs[16]; - int count = find_peaks(m->fft_buffers[do_fft(m)], m->module.srate, freqs); - struct feedback_reducer_params *p = malloc(sizeof(struct feedback_reducer_params)); - memcpy(p->bands + count, &m->params->bands[0], sizeof(struct eq_band) * (MAX_FBR_BANDS - count)); - for (int i = 0; i < count; i++) - { - p->bands[i].active = TRUE; - p->bands[i].center = freqs[i]; - p->bands[i].q = freqs[i] / 50; // each band ~100 Hz (not really sure about filter Q vs bandwidth) - p->bands[i].gain = 0.125; - } - free(cbox_rt_swap_pointers(m->module.rt, (void **)&m->params, p)); \ - m->analysed = 1; - if (!cbox_execute_on(fb, NULL, "/refresh", "i", error, 1)) - return FALSE; - } - if (!cbox_execute_on(fb, NULL, "/finished", "i", error, m->analysed)) - return FALSE; - for (int i = 0; i < MAX_FBR_BANDS; i++) - { - if (!cbox_execute_on(fb, NULL, "/active", "ii", error, i, (int)m->params->bands[i].active)) - return FALSE; - if (!cbox_execute_on(fb, NULL, "/center", "if", error, i, m->params->bands[i].center)) - return FALSE; - if (!cbox_execute_on(fb, NULL, "/q", "if", error, i, m->params->bands[i].q)) - return FALSE; - if (!cbox_execute_on(fb, NULL, "/gain", "if", error, i, gain2dB_simple(m->params->bands[i].gain))) - return FALSE; - } - // return cbox_execute_on(fb, NULL, "/wet_dry", "f", error, m->params->wet_dry); - return CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error); - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); - return TRUE; -} - -void feedback_reducer_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) -{ - // struct feedback_reducer_module *m = module->user_data; -} - -void feedback_reducer_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) -{ - struct feedback_reducer_module *m = module->user_data; - - if (m->params != m->old_params) - redo_filters(m); - - if (m->wrptr && m->wrptr != m->analysis_buffer + ANALYSIS_BUFFER_SIZE) - { - for (int i = 0; i < CBOX_BLOCK_SIZE; i++) - { - if (m->wrptr == m->analysis_buffer + ANALYSIS_BUFFER_SIZE) - break; - *m->wrptr++ = inputs[0][i] + inputs[1][i]; - } - } - for (int c = 0; c < 2; c++) - { - gboolean first = TRUE; - for (int i = 0; i < MAX_FBR_BANDS; i++) - { - if (!m->params->bands[i].active) - continue; - if (first) - { - cbox_biquadf_process_to(&m->state[i][c], &m->coeffs[i], inputs[c], outputs[c]); - first = FALSE; - } - else - { - cbox_biquadf_process(&m->state[i][c], &m->coeffs[i], outputs[c]); - } - } - if (first) - memcpy(outputs[c], inputs[c], sizeof(float) * CBOX_BLOCK_SIZE); - } -} - -MODULE_SIMPLE_DESTROY_FUNCTION(feedback_reducer) - -MODULE_CREATE_FUNCTION(feedback_reducer) -{ - static int inited = 0; - if (!inited) - { - for (int i = 0; i < ANALYSIS_BUFFER_SIZE; i++) - { - euler_table[i] = cos(i * 2 * M_PI / ANALYSIS_BUFFER_SIZE) + I * sin(i * 2 * M_PI / ANALYSIS_BUFFER_SIZE); - int ni = 0; - for (int j = 0; j < ANALYSIS_BUFFER_BITS; j++) - { - if (i & (1 << (ANALYSIS_BUFFER_BITS - 1 - j))) - ni = ni | (1 << j); - } - map_table[i] = ni; - von_hann_window_transposed[i] = 0.5 * (1 - cos (ni * 2 * M_PI / (ANALYSIS_BUFFER_SIZE - 1))); - } - - inited = 1; - } - - struct feedback_reducer_module *m = malloc(sizeof(struct feedback_reducer_module)); - CALL_MODULE_INIT(m, 2, 2, feedback_reducer); - m->module.process_event = feedback_reducer_process_event; - m->module.process_block = feedback_reducer_process_block; - struct feedback_reducer_params *p = malloc(sizeof(struct feedback_reducer_params)); - m->params = p; - m->old_params = NULL; - m->analysed = 0; - m->wrptr = NULL; - - for (int b = 0; b < MAX_FBR_BANDS; b++) - { - p->bands[b].active = cbox_eq_get_band_param(cfg_section, b, "active", 0) > 0; - p->bands[b].center = cbox_eq_get_band_param(cfg_section, b, "center", 50 * pow(2.0, b / 2.0)); - p->bands[b].q = cbox_eq_get_band_param(cfg_section, b, "q", 0.707 * 2); - p->bands[b].gain = cbox_eq_get_band_param_db(cfg_section, b, "gain", 0); - } - redo_filters(m); - cbox_eq_reset_bands(m->state, MAX_FBR_BANDS); - - return &m->module; -} - - -struct cbox_module_keyrange_metadata feedback_reducer_keyranges[] = { -}; - -struct cbox_module_livecontroller_metadata feedback_reducer_controllers[] = { -}; - -DEFINE_MODULE(feedback_reducer, 2, 2) - diff --git a/template/calfbox/fifo.c b/template/calfbox/fifo.c deleted file mode 100644 index 5c9852b..0000000 --- a/template/calfbox/fifo.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "fifo.h" -#include - -struct cbox_fifo *cbox_fifo_new(uint32_t size) -{ - struct cbox_fifo *fifo = calloc(1, sizeof(struct cbox_fifo) + size); - if (!fifo) - return NULL; - fifo->data = (uint8_t *)(fifo + 1); - fifo->size = size; - fifo->write_count = 0; - fifo->write_offset= 0; - fifo->read_count = 0; - fifo->read_offset = 0; - return fifo; -} - -void cbox_fifo_destroy(struct cbox_fifo *fifo) -{ - free(fifo); -} - diff --git a/template/calfbox/fifo.h b/template/calfbox/fifo.h deleted file mode 100644 index d9446cd..0000000 --- a/template/calfbox/fifo.h +++ /dev/null @@ -1,136 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_FIFO_H -#define CBOX_FIFO_H - -#include -#include -#include -#include - -struct cbox_fifo -{ - uint8_t *data; - uint32_t size; - uint64_t pad; // ensure the write-related and read-related structs are on 64 bit boundary - uint32_t write_count; - uint32_t write_offset; - uint32_t read_count; - uint32_t read_offset; -}; - -extern struct cbox_fifo *cbox_fifo_new(uint32_t size); - -static inline uint32_t cbox_fifo_readsize(struct cbox_fifo *fifo); -static inline uint32_t cbox_fifo_writespace(struct cbox_fifo *fifo); -static inline gboolean cbox_fifo_read_atomic(struct cbox_fifo *fifo, void *dest, uint32_t bytes); -static inline gboolean cbox_fifo_write_atomic(struct cbox_fifo *fifo, const void *src, uint32_t bytes); -static inline gboolean cbox_fifo_peek(struct cbox_fifo *fifo, void *dest, uint32_t bytes); -static inline gboolean cbox_fifo_consume(struct cbox_fifo *fifo, uint32_t bytes); - -extern void cbox_fifo_destroy(struct cbox_fifo *fifo); - - -static inline uint32_t cbox_fifo_readsize(struct cbox_fifo *fifo) -{ - return fifo->write_count - fifo->read_count; -} - -static inline uint32_t cbox_fifo_writespace(struct cbox_fifo *fifo) -{ - return fifo->size - (fifo->write_count - fifo->read_count); -} - -static inline gboolean cbox_fifo_read_impl(struct cbox_fifo *fifo, void *dest, uint32_t bytes, gboolean advance) -{ - __sync_synchronize(); - if (fifo->write_count - fifo->read_count < bytes) - return FALSE; - - if (dest) - { - uint32_t ofs = fifo->read_count - fifo->read_offset; - assert(ofs >= 0 && ofs < fifo->size); - if (ofs + bytes > fifo->size) - { - uint8_t *dstb = dest; - uint32_t firstpart = fifo->size - ofs; - memcpy(dstb, fifo->data + ofs, firstpart); - memcpy(dstb + firstpart, fifo->data, bytes - firstpart); - } - else - memcpy(dest, fifo->data + ofs, bytes); - } - - if (advance) - { - __sync_synchronize(); - // Make sure data are copied before signalling that they can be overwritten - fifo->read_count += bytes; - if (fifo->read_count - fifo->read_offset >= fifo->size) - fifo->read_offset += fifo->size; - } - __sync_synchronize(); - - return TRUE; -} - -static inline gboolean cbox_fifo_read_atomic(struct cbox_fifo *fifo, void *dest, uint32_t bytes) -{ - return cbox_fifo_read_impl(fifo, dest, bytes, TRUE); -} - -static inline gboolean cbox_fifo_peek(struct cbox_fifo *fifo, void *dest, uint32_t bytes) -{ - return cbox_fifo_read_impl(fifo, dest, bytes, FALSE); -} - -static inline gboolean cbox_fifo_consume(struct cbox_fifo *fifo, uint32_t bytes) -{ - return cbox_fifo_read_impl(fifo, NULL, bytes, TRUE); -} - -static inline gboolean cbox_fifo_write_atomic(struct cbox_fifo *fifo, const void *src, uint32_t bytes) -{ - if (fifo->size - (fifo->write_count - fifo->read_count) < bytes) - return FALSE; - - uint32_t ofs = fifo->write_count - fifo->write_offset; - assert(ofs >= 0 && ofs < fifo->size); - if (ofs + bytes > fifo->size) - { - const uint8_t *srcb = src; - uint32_t firstpart = fifo->size - ofs; - memcpy(fifo->data + ofs, srcb, firstpart); - memcpy(fifo->data, srcb + firstpart, bytes - firstpart); - } - else - memcpy(fifo->data + ofs, src, bytes); - - // Make sure data are in the buffer before announcing the availability - __sync_synchronize(); - fifo->write_count += bytes; - if (fifo->write_count - fifo->write_offset >= fifo->size) - fifo->write_offset += fifo->size; - - return TRUE; -} - - -#endif diff --git a/template/calfbox/fluid.c b/template/calfbox/fluid.c deleted file mode 100644 index c06e0fa..0000000 --- a/template/calfbox/fluid.c +++ /dev/null @@ -1,444 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config.h" - -#if USE_FLUIDSYNTH - -#include "config-api.h" -#include "dspmath.h" -#include "module.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if FLUIDSYNTH_VERSION_MAJOR < 2 - -typedef unsigned int cbox_fluidsynth_id_t; -#define fluid_sfont_iteration_setup() fluid_preset_t sfont_iterator; -#define fluid_sfont_iteration_start(sfont) (sfont)->iteration_start(sfont) -#define fluid_sfont_iteration_next(sfont) (sfont)->iteration_next(sfont, &sfont_iterator) ? &sfont_iterator : NULL -#define fluid_preset_get_num(preset) (preset)->get_num(preset) -#define fluid_preset_get_banknum(preset) (preset)->get_banknum(preset) -#define fluid_preset_get_name(preset) (preset)->get_name(preset) - -#else - -typedef int cbox_fluidsynth_id_t; -#define fluid_sfont_iteration_setup() - -#endif - -#define CBOX_FLUIDSYNTH_ERROR cbox_fluidsynth_error_quark() - -enum CboxFluidsynthError -{ - CBOX_FLUIDSYNTH_ERROR_FAILED, -}; - -GQuark cbox_fluidsynth_error_quark(void) -{ - return g_quark_from_string("cbox-fluidsynth-error-quark"); -} - -static void fluidsynth_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs); -static void fluidsynth_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len); -static gboolean fluidsynth_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error); -static void fluidsynth_destroyfunc(struct cbox_module *module); - -struct fluidsynth_module -{ - struct cbox_module module; - - fluid_settings_t *settings; - fluid_synth_t *synth; - char *bank_name; - int sfid; - int output_pairs; - int is_multi; -#if OLD_FLUIDSYNTH - float **left_outputs, **right_outputs; -#endif - GString *error_log; -}; - -static gboolean select_patch_by_name(struct fluidsynth_module *m, int channel, const gchar *preset, GError **error) -{ - fluid_sfont_t* sfont = fluid_synth_get_sfont(m->synth, 0); - fluid_preset_t* tmp; - fluid_sfont_iteration_setup(); - - fluid_sfont_iteration_start(sfont); - while((tmp = fluid_sfont_iteration_next(sfont)) != NULL) - { - // trailing spaces are common in some SF2s - const char *pname = fluid_preset_get_name(tmp); - int len = strlen(pname); - while (len > 0 && pname[len - 1] == ' ') - len--; - - if (!strncmp(pname, preset, len) && preset[len] == '\0') - { - fluid_synth_program_select(m->synth, channel, m->sfid, fluid_preset_get_banknum(tmp), fluid_preset_get_num(tmp)); - return TRUE; - } - } - - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Preset not found: %s", preset); - return FALSE; -} - -static void cbox_fluidsynth_log_write(int level, const char *message, void *data) -{ - struct fluidsynth_module *m = data; - if (!m->error_log) - m->error_log = g_string_new(message); - else - g_string_append(m->error_log, message); - g_string_append_c(m->error_log, '\n'); -} - -static gboolean load_soundfont(struct fluidsynth_module *m, const char *bank_name, GError **error) -{ - g_message("Loading soundfont %s", bank_name); - int result = fluid_synth_sfload(m->synth, bank_name, 1); - if (result == FLUID_FAILED) - { - char *error_msg = g_string_free(m->error_log, FALSE); - m->error_log = NULL; - g_set_error(error, CBOX_FLUIDSYNTH_ERROR, CBOX_FLUIDSYNTH_ERROR_FAILED, "Failed to load the SF2 bank %s: %s", bank_name, error_msg); - g_free(error_msg); - return FALSE; - } - m->bank_name = g_strdup(bank_name); - m->sfid = result; - return TRUE; -} - -MODULE_CREATE_FUNCTION(fluidsynth) -{ - int i; - const char *bankname = cbox_config_get_string(cfg_section, "sf2"); - static int inited = 0; - if (!inited) - { - inited = 1; - } - - struct fluidsynth_module *m = malloc(sizeof(struct fluidsynth_module)); - int pairs = cbox_config_get_int(cfg_section, "output_pairs", 0); - m->output_pairs = pairs ? pairs : 1; - m->is_multi = pairs > 0; - if (m->output_pairs < 1 || m->output_pairs > 16) - { - free(m); - g_set_error(error, CBOX_FLUIDSYNTH_ERROR, CBOX_FLUIDSYNTH_ERROR_FAILED, "Invalid number of output pairs (found %d, supported range 1-16)", m->output_pairs); - return NULL; - } - if (pairs == 0) - { - CALL_MODULE_INIT(m, 0, 2 * m->output_pairs, fluidsynth); -#if OLD_FLUIDSYNTH - m->left_outputs = NULL; - m->right_outputs = NULL; -#endif - } - else - { - g_message("Multichannel mode enabled, %d output pairs, 2 effects", m->output_pairs); - CALL_MODULE_INIT(m, 0, 2 * m->output_pairs + 4, fluidsynth); -#if OLD_FLUIDSYNTH - m->left_outputs = malloc(sizeof(float *) * (m->output_pairs + 2)); - m->right_outputs = malloc(sizeof(float *) * (m->output_pairs + 2)); -#endif - } - m->module.process_event = fluidsynth_process_event; - m->module.process_block = fluidsynth_process_block; - m->module.aux_offset = 2 * m->output_pairs; - const char *audio_drivers[] = { NULL }; - fluid_audio_driver_register(audio_drivers); - m->settings = new_fluid_settings(); - fluid_settings_setnum(m->settings, "synth.sample-rate", m->module.srate); - fluid_settings_setint(m->settings, "synth.audio-channels", m->output_pairs); - fluid_settings_setint(m->settings, "synth.audio-groups", m->output_pairs); - fluid_settings_setint(m->settings, "synth.reverb.active", cbox_config_get_int(cfg_section, "reverb", 1)); - fluid_settings_setint(m->settings, "synth.chorus.active", cbox_config_get_int(cfg_section, "chorus", 1)); - m->synth = new_fluid_synth(m->settings); - fluid_set_log_function(FLUID_PANIC, cbox_fluidsynth_log_write, m); - fluid_set_log_function(FLUID_ERR, cbox_fluidsynth_log_write, m); - fluid_set_log_function(FLUID_WARN, cbox_fluidsynth_log_write, m); - //fluid_synth_add_sfloader(m->synth, new_fluid_defsfloader(m->settings)); - m->error_log = NULL; - m->bank_name = NULL; - m->sfid = -1; - if (bankname) - { - if (!load_soundfont(m, bankname, error)) - { - CBOX_DELETE(&m->module); - return NULL; - } - g_message("Soundfont %s loaded", bankname); - } - if (bankname) - { - for (i = 0; i < 16; i++) - { - gchar *key = g_strdup_printf("channel%d", i + 1); - gchar *preset = cbox_config_get_string(cfg_section, key); - fluid_synth_sfont_select(m->synth, i, m->sfid); - if (preset) - { - if (!select_patch_by_name(m, i, preset, error)) - { - CBOX_DELETE(&m->module); - return NULL; - } - } - g_free(key); - } - } - - return &m->module; -} - -void fluidsynth_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) -{ - struct fluidsynth_module *m = (struct fluidsynth_module *)module; - if (!m->is_multi) - fluid_synth_write_float(m->synth, CBOX_BLOCK_SIZE, outputs[0], 0, 1, outputs[1], 0, 1); - else - { -#if OLD_FLUIDSYNTH - for (int i = 0; i < 2 + m->output_pairs; i++) - { - m->left_outputs[i] = outputs[2 * i]; - m->right_outputs[i] = outputs[2 * i + 1]; - } - fluid_synth_nwrite_float(m->synth, CBOX_BLOCK_SIZE, m->left_outputs, m->right_outputs, m->left_outputs + m->output_pairs, m->right_outputs + m->output_pairs); -#else - float *fx_outputs[4]; - for (int i = 0; i < 4; i++) - fx_outputs[i] = outputs[2 * m->output_pairs + i]; - for (int i = 0; i < 2 * (2 + m->output_pairs); i++) - zerobf(outputs[i]); - fluid_synth_process(m->synth, CBOX_BLOCK_SIZE, 4, fx_outputs, 2 * m->output_pairs, outputs); -#endif - } -} - -void fluidsynth_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) -{ - struct fluidsynth_module *m = (struct fluidsynth_module *)module; - if (len > 0) - { - int cmd = data[0] >> 4; - int chn = data[0] & 15; - switch(cmd) - { - case 8: - fluid_synth_noteoff(m->synth, chn, data[1]); - break; - - case 9: - fluid_synth_noteon(m->synth, chn, data[1], data[2]); - break; - - case 10: - // polyphonic pressure not handled - break; - - case 11: - fluid_synth_cc(m->synth, chn, data[1], data[2]); - break; - - case 12: - fluid_synth_program_change(m->synth, chn, data[1]); - break; - - case 13: - fluid_synth_channel_pressure(m->synth, chn, data[1]); - break; - - case 14: - fluid_synth_pitch_bend(m->synth, chn, data[1] + 128 * data[2]); - break; - - } - } -} - -gboolean fluidsynth_process_load_patch(struct fluidsynth_module *m, const char *bank_name, GError **error) -{ - if (bank_name && !*bank_name) - bank_name = NULL; - int old_sfid = m->sfid; - char *old_bank_name = m->bank_name; - if (bank_name) - { - if (!load_soundfont(m, bank_name, error)) - return FALSE; - g_message("Soundfont %s loaded at ID %d", bank_name, m->sfid); - } - else - m->sfid = -1; - if (old_sfid != -1) - { - g_free(old_bank_name); - fluid_synth_sfunload(m->synth, old_sfid, 1); - } - if (m->sfid != -1) - { - for (int i = 0; i < 16; i++) - fluid_synth_sfont_select(m->synth, i, m->sfid); - } - m->bank_name = bank_name ? g_strdup(bank_name) : NULL; - return TRUE; -} - -gboolean fluidsynth_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct fluidsynth_module *m = (struct fluidsynth_module *)ct->user_data; - - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - if (!cbox_execute_on(fb, NULL, "/polyphony", "i", error, fluid_synth_get_polyphony(m->synth))) - return FALSE; - if (!cbox_execute_on(fb, NULL, "/soundfont", "s", error, m->bank_name ? m->bank_name : "")) - return FALSE; - for (int i = 0; i < 16; i++) - { - cbox_fluidsynth_id_t sfont_id, bank_num, preset_num; - fluid_synth_get_program(m->synth, i, &sfont_id, &bank_num, &preset_num); - fluid_preset_t *preset = fluid_synth_get_channel_preset(m->synth, i); - if (!cbox_execute_on(fb, NULL, "/patch", "iis", error, 1 + i, preset_num + 128 * bank_num, preset ? fluid_preset_get_name(preset) : "(unknown)")) - return FALSE; - } - return CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error); - } - else if (!strcmp(cmd->command, "/patches") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - if (m->sfid == -1) - return TRUE; - fluid_sfont_t* sfont = fluid_synth_get_sfont(m->synth, 0); - fluid_preset_t *tmp; - - fluid_sfont_iteration_setup(); - fluid_sfont_iteration_start(sfont); - while((tmp = fluid_sfont_iteration_next(sfont)) != NULL) - { - const char *pname = fluid_preset_get_name(tmp); - if (!cbox_execute_on(fb, NULL, "/patch", "is", error, (int)(fluid_preset_get_num(tmp) + 128 * fluid_preset_get_banknum(tmp)), pname)) - return FALSE; - } - return TRUE; - } - else if (!strcmp(cmd->command, "/set_patch") && !strcmp(cmd->arg_types, "ii")) - { - if (m->sfid == -1) - { - g_set_error(error, CBOX_FLUIDSYNTH_ERROR, CBOX_FLUIDSYNTH_ERROR_FAILED, "No soundfont loaded"); - return FALSE; - } - int channel = CBOX_ARG_I(cmd, 0); - if (channel < 1 || channel > 16) - { - g_set_error(error, CBOX_FLUIDSYNTH_ERROR, CBOX_FLUIDSYNTH_ERROR_FAILED, "Invalid channel %d", channel); - return FALSE; - } - int value = CBOX_ARG_I(cmd, 1); - return fluid_synth_program_select(m->synth, channel - 1, m->sfid, value >> 7, value & 127) == FLUID_OK; - } - else if (!strcmp(cmd->command, "/polyphony") && !strcmp(cmd->arg_types, "i")) - { - int polyphony = CBOX_ARG_I(cmd, 0); - if (polyphony < 2 || polyphony > 256) - { - g_set_error(error, CBOX_FLUIDSYNTH_ERROR, CBOX_FLUIDSYNTH_ERROR_FAILED, "Invalid polyphony %d (must be between 2 and 256)", polyphony); - return FALSE; - } - return fluid_synth_set_polyphony(m->synth, polyphony) == FLUID_OK; - } - else if (!strcmp(cmd->command, "/load_soundfont") && !strcmp(cmd->arg_types, "s")) - { - return fluidsynth_process_load_patch(m, CBOX_ARG_S(cmd, 0), error); - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); - return TRUE; -} - -void fluidsynth_destroyfunc(struct cbox_module *module) -{ - struct fluidsynth_module *m = (struct fluidsynth_module *)module; - -#if OLD_FLUIDSYNTH - if (m->output_pairs) - { - free(m->left_outputs); - free(m->right_outputs); - } -#endif - free(m->bank_name); - - delete_fluid_synth(m->synth); - delete_fluid_settings(m->settings); -} - -struct cbox_module_livecontroller_metadata fluidsynth_controllers[] = { - { -1, cmlc_continuouscc, 1, "Modulation", NULL}, - { -1, cmlc_continuouscc, 7, "Volume", NULL}, - { -1, cmlc_continuouscc, 10, "Pan", NULL}, - { -1, cmlc_continuouscc, 91, "Reverb", NULL}, - { -1, cmlc_continuouscc, 93, "Chorus", NULL}, - { -1, cmlc_onoffcc, 64, "Hold", NULL}, - { -1, cmlc_onoffcc, 66, "Sostenuto", NULL}, -}; - -struct cbox_module_keyrange_metadata fluidsynth_keyranges[] = { - { 1, 0, 127, "Channel 1" }, - { 2, 0, 127, "Channel 2" }, - { 3, 0, 127, "Channel 3" }, - { 4, 0, 127, "Channel 4" }, - { 5, 0, 127, "Channel 5" }, - { 6, 0, 127, "Channel 6" }, - { 7, 0, 127, "Channel 7" }, - { 8, 0, 127, "Channel 8" }, - { 9, 0, 127, "Channel 9" }, - { 10, 0, 127, "Channel 10" }, - { 11, 0, 127, "Channel 11" }, - { 12, 0, 127, "Channel 12" }, - { 13, 0, 127, "Channel 13" }, - { 14, 0, 127, "Channel 14" }, - { 15, 0, 127, "Channel 15" }, - { 16, 0, 127, "Channel 16" }, -}; - -DEFINE_MODULE(fluidsynth, 0, 2) - -#endif diff --git a/template/calfbox/fuzz.c b/template/calfbox/fuzz.c deleted file mode 100644 index 677d3d0..0000000 --- a/template/calfbox/fuzz.c +++ /dev/null @@ -1,173 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "biquad-float.h" -#include "config.h" -#include "config-api.h" -#include "dspmath.h" -#include "module.h" -#include -#include -#include -#include -#include -#include -#include - -#define MODULE_PARAMS fuzz_params - -struct fuzz_params -{ - float drive; - float wet_dry; - float rectify; - float band; - float bandwidth; - float band2; - float bandwidth2; -}; - -struct fuzz_module -{ - struct cbox_module module; - - struct fuzz_params *params, *old_params; - - struct cbox_biquadf_coeffs split_coeffs; - struct cbox_biquadf_coeffs post_coeffs; - struct cbox_biquadf_state split_state[2]; - struct cbox_biquadf_state post_state[2]; -}; - -gboolean fuzz_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct fuzz_module *m = (struct fuzz_module *)ct->user_data; - - EFFECT_PARAM("/drive", "f", drive, double, dB2gain_simple, -36, 36) else - EFFECT_PARAM("/wet_dry", "f", wet_dry, double, , 0, 1) else - EFFECT_PARAM("/rectify", "f", rectify, double, , 0, 1) else - EFFECT_PARAM("/band", "f", band, double, , 100, 5000) else - EFFECT_PARAM("/bandwidth", "f", bandwidth, double, , 0.25, 4) else - EFFECT_PARAM("/band2", "f", band2, double, , 100, 5000) else - EFFECT_PARAM("/bandwidth2", "f", bandwidth2, double, , 0.25, 4) else - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - return cbox_execute_on(fb, NULL, "/drive", "f", error, gain2dB_simple(m->params->drive)) - && cbox_execute_on(fb, NULL, "/wet_dry", "f", error, m->params->wet_dry) - && cbox_execute_on(fb, NULL, "/rectify", "f", error, m->params->rectify) - && cbox_execute_on(fb, NULL, "/band", "f", error, m->params->band) - && cbox_execute_on(fb, NULL, "/bandwidth", "f", error, m->params->bandwidth) - && cbox_execute_on(fb, NULL, "/band2", "f", error, m->params->band2) - && cbox_execute_on(fb, NULL, "/bandwidth2", "f", error, m->params->bandwidth2) - && CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error) - ; - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); - return TRUE; -} - -void fuzz_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) -{ - // struct fuzz_module *m = module->user_data; -} - -void fuzz_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) -{ - struct fuzz_module *m = module->user_data; - - if (m->params != m->old_params) - { - // update calculated values - } - - cbox_biquadf_set_bp_rbj(&m->split_coeffs, m->params->band, 0.7 / m->params->bandwidth, m->module.srate); - cbox_biquadf_set_bp_rbj(&m->post_coeffs, m->params->band2, 0.7 / m->params->bandwidth2, m->module.srate); - - float splitbuf[2][CBOX_BLOCK_SIZE]; - float drive = m->params->drive; - float sdrive = pow(drive, -0.7); - for (int c = 0; c < 2; c++) - { - cbox_biquadf_process_to(&m->split_state[c], &m->split_coeffs, inputs[c], splitbuf[c]); - for (int i = 0; i < CBOX_BLOCK_SIZE; i++) - { - float in = inputs[c][i]; - - float val = splitbuf[c][i]; - - val *= drive; - - val += m->params->rectify; - if (fabs(val) > 1.0) - val = (val > 0) ? 1 : -1; - else - val = val * (3 - val * val) * 0.5; - - val *= sdrive; - - val = cbox_biquadf_process_sample(&m->post_state[c], &m->post_coeffs, val); - - outputs[c][i] = in + (val - in) * m->params->wet_dry; - } - } -} - -MODULE_SIMPLE_DESTROY_FUNCTION(fuzz) - -MODULE_CREATE_FUNCTION(fuzz) -{ - static int inited = 0; - if (!inited) - { - inited = 1; - } - - struct fuzz_module *m = malloc(sizeof(struct fuzz_module)); - CALL_MODULE_INIT(m, 2, 2, fuzz); - m->module.process_event = fuzz_process_event; - m->module.process_block = fuzz_process_block; - struct fuzz_params *p = malloc(sizeof(struct fuzz_params)); - p->drive = cbox_config_get_gain_db(cfg_section, "drive", 0.f); - p->wet_dry = cbox_config_get_float(cfg_section, "wet_dry", 0.5f); - p->rectify = cbox_config_get_float(cfg_section, "rectify", 0.5f); - p->band = cbox_config_get_float(cfg_section, "band", 1000.f); - p->bandwidth = cbox_config_get_float(cfg_section, "bandwidth", 1); - p->band2 = cbox_config_get_float(cfg_section, "band2", 2000.f); - p->bandwidth2 = cbox_config_get_float(cfg_section, "bandwidth2", 1); - m->params = p; - m->old_params = NULL; - cbox_biquadf_reset(&m->split_state[0]); - cbox_biquadf_reset(&m->split_state[1]); - cbox_biquadf_reset(&m->post_state[0]); - cbox_biquadf_reset(&m->post_state[1]); - - return &m->module; -} - - -struct cbox_module_keyrange_metadata fuzz_keyranges[] = { -}; - -struct cbox_module_livecontroller_metadata fuzz_controllers[] = { -}; - -DEFINE_MODULE(fuzz, 2, 2) - diff --git a/template/calfbox/py/fx_gui.py b/template/calfbox/fx_gui.py similarity index 100% rename from template/calfbox/py/fx_gui.py rename to template/calfbox/fx_gui.py diff --git a/template/calfbox/fxchain.c b/template/calfbox/fxchain.c deleted file mode 100644 index 5cd123d..0000000 --- a/template/calfbox/fxchain.c +++ /dev/null @@ -1,231 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config.h" -#include "config-api.h" -#include "dspmath.h" -#include "module.h" -#include "rt.h" -#include -#include -#include -#include -#include -#include -#include - -struct fxchain_module -{ - struct cbox_module module; - - struct cbox_module **modules; - uint32_t module_count; -}; - -void fxchain_move(struct fxchain_module *m, unsigned int oldpos, unsigned int newpos) -{ - if (oldpos == newpos) - return; - struct cbox_module **modules = malloc(sizeof(struct cbox_module *) * m->module_count); - for (uint32_t i = 0; i < m->module_count; i++) - { - int s; - if (i == newpos) - s = oldpos; - else - { - if (oldpos < newpos) - s = (i < oldpos || i > newpos) ? i : i + 1; - else - s = (i < newpos || i > oldpos) ? i : i - 1; - } - modules[i] = m->modules[s]; - } - free(cbox_rt_swap_pointers(m->module.rt, (void **)&m->modules, modules)); -} - -gboolean fxchain_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct fxchain_module *m = (struct fxchain_module *)ct->user_data; - const char *subcommand = NULL; - int index = 0; - - //EFFECT_PARAM("/module_count", "i", stages, int, , 1, 12) else - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - for (uint32_t i = 0; i < m->module_count; i++) - { - gboolean res = FALSE; - if (m->modules[i]) - res = cbox_execute_on(fb, NULL, "/module", "ss", error, m->modules[i]->engine_name, m->modules[i]->instance_name); - else - res = cbox_execute_on(fb, NULL, "/module", "ss", error, "", ""); - if (!res) - return FALSE; - res = cbox_execute_on(fb, NULL, "/bypass", "ii", error, i + 1, m->modules[i] ? m->modules[i]->bypass : 0); - } - return CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error); - } - else if (cbox_parse_path_part_int(cmd, "/module/", &subcommand, &index, 1, m->module_count, error)) - { - if (!subcommand) - return FALSE; - return cbox_module_slot_process_cmd(&m->modules[index - 1], fb, cmd, subcommand, CBOX_GET_DOCUMENT(&m->module), m->module.rt, m->module.engine, error); - } - else if (!strcmp(cmd->command, "/insert") && !strcmp(cmd->arg_types, "i")) - { - int pos = CBOX_ARG_I(cmd, 0) - 1; - struct cbox_module **new_modules = malloc((m->module_count + 1) * sizeof(struct cbox_module *)); - memcpy(new_modules, m->modules, pos * sizeof(struct cbox_module *)); - new_modules[pos] = NULL; - memcpy(new_modules + pos + 1, m->modules + pos, (m->module_count - pos) * sizeof(struct cbox_module *)); - void *old_modules = cbox_rt_swap_pointers_and_update_count(m->module.rt, (void **)&m->modules, new_modules, &m->module_count, m->module_count + 1); - free(old_modules); - return TRUE; - } - else if (!strcmp(cmd->command, "/delete") && !strcmp(cmd->arg_types, "i")) - { - int pos = CBOX_ARG_I(cmd, 0) - 1; - struct cbox_module **new_modules = malloc((m->module_count + 1) * sizeof(struct cbox_module *)); - memcpy(new_modules, m->modules, pos * sizeof(struct cbox_module *)); - memcpy(new_modules + pos, m->modules + pos + 1, (m->module_count - pos - 1) * sizeof(struct cbox_module *)); - struct cbox_module *deleted_module = m->modules[pos]; - void *old_modules = cbox_rt_swap_pointers_and_update_count(m->module.rt, (void **)&m->modules, new_modules, &m->module_count, m->module_count - 1); - free(old_modules); - if (deleted_module) - CBOX_DELETE(deleted_module); - return TRUE; - } - else if (!strcmp(cmd->command, "/move") && !strcmp(cmd->arg_types, "ii")) - { - int oldpos = CBOX_ARG_I(cmd, 0) - 1; - int newpos = CBOX_ARG_I(cmd, 1) - 1; - fxchain_move(m, oldpos, newpos); - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); - return TRUE; -} - -void fxchain_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) -{ - // struct fxchain_module *m = module->user_data; -} - -void fxchain_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) -{ - struct fxchain_module *m = module->user_data; - - float bufs[2][2][CBOX_BLOCK_SIZE]; - - for (uint32_t i = 0; i < m->module_count; i++) - { - float *input_bufs[2], *output_bufs[2]; - for (int c = 0; c < 2; c++) - { - input_bufs[c] = i == 0 ? inputs[c] : bufs[i & 1][c]; - output_bufs[c] = i == m->module_count - 1 ? outputs[c] : bufs[(i + 1) & 1][c]; - } - if (m->modules[i] && !m->modules[i]->bypass) - m->modules[i]->process_block(m->modules[i]->user_data, input_bufs, output_bufs); - else - { - // this is not eficient at all, but empty modules aren't likely to be used except - // when setting up a chain. - for (int c = 0; c < 2; c++) - memcpy(output_bufs[c], input_bufs[c], CBOX_BLOCK_SIZE * sizeof(float)); - } - } - -} - -static void fxchain_destroyfunc(struct cbox_module *module) -{ - struct fxchain_module *m = module->user_data; - for (uint32_t i = 0; i < m->module_count; i++) - { - CBOX_DELETE(m->modules[i]); - m->modules[i] = NULL; - } - free(m->modules); -} - -MODULE_CREATE_FUNCTION(fxchain) -{ - static int inited = 0; - if (!inited) - { - inited = 1; - } - - int i, fx_count = 0; - for (i = 0; ; i++) - { - gchar *name = g_strdup_printf("effect%d", i + 1); - const char *fx_name = cbox_config_get_string(cfg_section, name); - g_free(name); - if (!fx_name) - break; - } - fx_count = i; - if (cfg_section && !fx_count) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "No effects defined"); - return NULL; - } - - struct fxchain_module *m = malloc(sizeof(struct fxchain_module)); - CALL_MODULE_INIT(m, 2, 2, fxchain); - m->module.process_event = fxchain_process_event; - m->module.process_block = fxchain_process_block; - m->modules = malloc(sizeof(struct cbox_module *) * fx_count); - m->module_count = fx_count; - - for (i = 0; i < fx_count; i++) - m->modules[i] = NULL; - - for (i = 0; i < fx_count; i++) - { - gchar *name = g_strdup_printf("effect%d", i + 1); - const char *fx_preset_name = cbox_config_get_string(cfg_section, name); - g_free(name); - m->modules[i] = cbox_module_new_from_fx_preset(fx_preset_name, doc, rt, engine, error); - if (!m->modules[i]) - goto failed; - } - fx_count = i; - - return &m->module; - -failed: - m->module_count = i; - CBOX_DELETE(&m->module); - return NULL; -} - - -struct cbox_module_keyrange_metadata fxchain_keyranges[] = { -}; - -struct cbox_module_livecontroller_metadata fxchain_controllers[] = { -}; - -DEFINE_MODULE(fxchain, 0, 2) - diff --git a/template/calfbox/gate.c b/template/calfbox/gate.c deleted file mode 100644 index fac233f..0000000 --- a/template/calfbox/gate.c +++ /dev/null @@ -1,181 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2012 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config.h" -#include "config-api.h" -#include "dspmath.h" -#include "module.h" -#include "onepole-float.h" -#include -#include -#include -#include -#include -#include -#include - -#define MODULE_PARAMS gate_params - -struct gate_params -{ - float threshold; - float ratio; - float attack; - float hold; - float release; -}; - -struct gate_module -{ - struct cbox_module module; - - struct gate_params *params, *old_params; - struct cbox_onepolef_coeffs attack_lp, release_lp, shifter_lp; - struct cbox_onepolef_state shifter1, shifter2; - struct cbox_onepolef_state tracker; - int hold_time, hold_threshold; -}; - -gboolean gate_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct gate_module *m = (struct gate_module *)ct->user_data; - - EFFECT_PARAM("/threshold", "f", threshold, double, dB2gain_simple, -100, 100) else - EFFECT_PARAM("/ratio", "f", ratio, double, , 1, 100) else - EFFECT_PARAM("/attack", "f", attack, double, , 1, 1000) else - EFFECT_PARAM("/hold", "f", hold, double, , 1, 1000) else - EFFECT_PARAM("/release", "f", release, double, , 1, 1000) else - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - return cbox_execute_on(fb, NULL, "/threshold", "f", error, gain2dB_simple(m->params->threshold)) - && cbox_execute_on(fb, NULL, "/ratio", "f", error, m->params->ratio) - && cbox_execute_on(fb, NULL, "/attack", "f", error, m->params->attack) - && cbox_execute_on(fb, NULL, "/hold", "f", error, m->params->hold) - && cbox_execute_on(fb, NULL, "/release", "f", error, m->params->release) - && CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error) - ; - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); - return TRUE; -} - -void gate_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) -{ - // struct gate_module *m = module->user_data; -} - -void gate_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) -{ - struct gate_module *m = module->user_data; - - if (m->params != m->old_params) - { - float scale = M_PI * 1000 / m->module.srate; - cbox_onepolef_set_lowpass(&m->attack_lp, scale / m->params->attack); - cbox_onepolef_set_lowpass(&m->release_lp, scale / m->params->release); - cbox_onepolef_set_allpass(&m->shifter_lp, M_PI * 100 / m->module.srate); - m->hold_threshold = (int)(m->module.srate * m->params->hold * 0.001); - m->old_params = m->params; - } - - float threshold = m->params->threshold; - float threshold2 = threshold * threshold * 1.73; - for (int i = 0; i < CBOX_BLOCK_SIZE; i++) - { - float left = inputs[0][i], right = inputs[1][i]; - float sig = fabs(left) > fabs(right) ? fabs(left) : fabs(right); - - // Primitive envelope detector - may not work so well with more interesting stereo signals - float shf1 = cbox_onepolef_process_sample(&m->shifter1, &m->shifter_lp, 0.5 * (left + right)); - float shf2 = cbox_onepolef_process_sample(&m->shifter2, &m->shifter_lp, shf1); - sig = sig*sig + shf1*shf1 + shf2 * shf2; - - // attack - hold - release logic based on signal envelope - int release = 1; - float gain = 1.0; - if (sig < threshold2) - { - // hold vs release - if (m->hold_time >= m->hold_threshold) - { - gain = powf(sig / threshold2, 0.5 * (m->params->ratio - 1)); - // gain = powf(sqrt(sig) / threshold, (m->params->ratio - 1)); - } - else - m->hold_time++; - } - else - { - // attack - going to 1 using attack rate - m->hold_time = 0; - gain = 1.0; - release = 0; - } - - gain = cbox_onepolef_process_sample(&m->tracker, release ? &m->release_lp : &m->attack_lp, gain); - - outputs[0][i] = left * gain; - outputs[1][i] = right * gain; - } -} - -MODULE_SIMPLE_DESTROY_FUNCTION(gate) - -MODULE_CREATE_FUNCTION(gate) -{ - static int inited = 0; - if (!inited) - { - inited = 1; - } - - struct gate_module *m = malloc(sizeof(struct gate_module)); - CALL_MODULE_INIT(m, 2, 2, gate); - m->module.process_event = gate_process_event; - m->module.process_block = gate_process_block; - m->hold_time = 0; - m->hold_threshold = 0; - - struct gate_params *p = malloc(sizeof(struct gate_params)); - p->threshold = cbox_config_get_gain_db(cfg_section, "threshold", -28.0); - p->ratio = cbox_config_get_float(cfg_section, "ratio", 3.0); - p->attack = cbox_config_get_float(cfg_section, "attack", 3.0); - p->hold = cbox_config_get_float(cfg_section, "hold", 100.0); - p->release = cbox_config_get_float(cfg_section, "release", 100.0); - m->params = p; - m->old_params = NULL; - - cbox_onepolef_reset(&m->tracker); - cbox_onepolef_reset(&m->shifter1); - cbox_onepolef_reset(&m->shifter2); - - return &m->module; -} - - -struct cbox_module_keyrange_metadata gate_keyranges[] = { -}; - -struct cbox_module_livecontroller_metadata gate_controllers[] = { -}; - -DEFINE_MODULE(gate, 2, 2) - diff --git a/template/calfbox/py/gui_tools.py b/template/calfbox/gui_tools.py similarity index 100% rename from template/calfbox/py/gui_tools.py rename to template/calfbox/gui_tools.py diff --git a/template/calfbox/hwcfg.c b/template/calfbox/hwcfg.c deleted file mode 100644 index b86cb7b..0000000 --- a/template/calfbox/hwcfg.c +++ /dev/null @@ -1,172 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config.h" - -#if USE_JACK - -#include "config-api.h" -#include "io.h" - -#include -#include -#include -#include - -static char *cfg(const char *section, const char *name, char *defvalue) -{ - char *value = cbox_config_get_string(section, name); - if (value) - return value; - return cbox_config_get_string_with_default("autojack", name, defvalue); -} - -static void generate_jack_config(const char *section, const char *id) -{ - char *rcfile = cbox_config_get_string("autojack", "jackdrc"); - FILE *f; - if (!rcfile) - { - rcfile = g_strdup_printf("%s/.jackdrc", getenv("HOME")); - g_message("Generating JACK config: %s\n", rcfile); - f = fopen(rcfile, "w"); - if (!f) - { - g_error("Cannot open file %s", rcfile); - return; - } - g_free(rcfile); - } - else - { - g_message("Generating JACK config: %s\n", rcfile); - f = fopen(rcfile, "w"); - if (!f) - { - g_error("Cannot open file %s", rcfile); - return; - } - } - - fprintf(f, "%s %s -d alsa -d hw:%s -r 44100 %s\n", - cfg(section, "jackd", "/usr/bin/jackd"), - cfg(section, "jack_options", "-R -T"), - id, - cfg(section, "alsa_options", "")); - fclose(f); -} - -static int try_soundcard(const char *name) -{ - gchar *id; - if (!cbox_config_has_section(name)) - return 0; - - g_message("Trying section %s", name); - - id = cbox_config_get_string(name, "device"); - if (id != NULL) - { - struct stat s; - int result; - gchar *fn = g_strdup_printf("/proc/asound/%s", id); - result = stat(fn, &s); - if (!result) - generate_jack_config(name, id); - g_free(fn); - return !result; - } - - id = cbox_config_get_string(name, "usbid"); - if (id != NULL) - { - int vid, pid; - if (sscanf(id, "%x:%x\n", &vid, &pid) !=2) - { - g_error("Invalid VID:PID value: %s", id); - return 0; - } - for (int i = 0; ; i++) - { - struct stat s; - int result; - FILE *f = NULL; - int tvid, tpid; - - // check if it's not beyond the last soundcard index - gchar *fn = g_strdup_printf("/proc/asound/card%d", i); - result = stat(fn, &s); - g_free(fn); - if (result) - break; - - // check if it has a USB ID - fn = g_strdup_printf("/proc/asound/card%d/usbid", i); - f = fopen(fn, "r"); - g_free(fn); - - if (!f) - continue; - - if (fscanf(f, "%x:%x", &tvid, &tpid) == 2) - { - if (vid == tvid && pid == tpid) - { - gchar *fn = g_strdup_printf("%d", i); - generate_jack_config(name, fn); - g_free(fn); - fclose(f); - return 1; - } - } - fclose(f); - } - return 0; - } - - return 0; -} - -int cbox_hwcfg_setup_jack(void) -{ - int i; - if (!cbox_config_has_section("autojack")) - return 0; - - for (i = 0; ; i++) - { - int result; - gchar *cardnum = g_strdup_printf("soundcard%d", i); - char *secname = cbox_config_get_string("autojack", cardnum); - g_free(cardnum); - - if (!secname) - break; - - secname = g_strdup_printf("soundcard:%s", secname); - result = try_soundcard(secname); - g_free(secname); - - if (result) - return 1; - } - - return 0; -} - -#endif diff --git a/template/calfbox/hwcfg.h b/template/calfbox/hwcfg.h deleted file mode 100644 index a5061b2..0000000 --- a/template/calfbox/hwcfg.h +++ /dev/null @@ -1,28 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_HWCFG_H -#define CBOX_HWCFG_H - -/** - * Autodetect JACK config based on cbox configuration vs ALSA devices present. - * @retval 1 if OK - */ -extern int cbox_hwcfg_setup_jack(void); - -#endif diff --git a/template/calfbox/instr.c b/template/calfbox/instr.c deleted file mode 100644 index d030c7c..0000000 --- a/template/calfbox/instr.c +++ /dev/null @@ -1,251 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "auxbus.h" -#include "config-api.h" -#include "instr.h" -#include "module.h" -#include "io.h" -#include "rt.h" -#include "scene.h" -#include -#include - -CBOX_CLASS_DEFINITION_ROOT(cbox_instrument) - -static gboolean cbox_instrument_output_process_cmd(struct cbox_instrument *instr, struct cbox_instrument_output *output, struct cbox_command_target *fb, struct cbox_osc_command *cmd, const char *subcmd, GError **error) -{ - if (!strcmp(subcmd, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!(cbox_execute_on(fb, NULL, "/gain_linear", "f", error, output->gain_obj.lin_gain) && - cbox_execute_on(fb, NULL, "/gain", "f", error, output->gain_obj.db_gain) && - cbox_execute_on(fb, NULL, "/output", "i", error, output->output_bus + 1))) - return FALSE; - return cbox_module_slot_process_cmd(&output->insert, fb, cmd, subcmd, CBOX_GET_DOCUMENT(instr->scene), instr->scene->rt, instr->scene->engine, error); - } - if (!strcmp(subcmd, "/gain") && !strcmp(cmd->arg_types, "f")) - { - // XXXKF this needs proper handling of concurrency/race conditions, might - // be built into the gain class in future. - cbox_gain_set_db(&output->gain_obj, CBOX_ARG_F(cmd, 0)); - return TRUE; - } - if (!strcmp(subcmd, "/output") && !strcmp(cmd->arg_types, "i")) - { - int obus = CBOX_ARG_I(cmd, 0); - int max_outputs = instr->scene->rt->io ? instr->scene->rt->io->io_env.output_count : 2; - int max_obus = 1 + (max_outputs - 1) / 2; - if (obus < 0 || obus > max_obus) { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid output %d (must be between 1 and %d, or 0 for none)", obus, max_obus); - return FALSE; - } - output->output_bus = obus - 1; - return TRUE; - } - if (!strncmp(subcmd, "/rec_dry/", 9)) - return cbox_execute_sub(&output->rec_dry.cmd_target, fb, cmd, subcmd + 8, error); - if (!strncmp(subcmd, "/rec_wet/", 9)) - return cbox_execute_sub(&output->rec_wet.cmd_target, fb, cmd, subcmd + 8, error); - return cbox_module_slot_process_cmd(&output->insert, fb, cmd, subcmd, CBOX_GET_DOCUMENT(instr->scene), instr->scene->rt, instr->scene->engine, error); -} - -static gboolean cbox_instrument_aux_process_cmd(struct cbox_instrument *instr, struct cbox_instrument_output *output, int id, struct cbox_command_target *fb, struct cbox_osc_command *cmd, const char *subcmd, GError **error) -{ - if (!strcmp(subcmd, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - if (!(cbox_execute_on(fb, NULL, "/gain_linear", "f", error, output->gain_obj.lin_gain) && - cbox_execute_on(fb, NULL, "/gain", "f", error, output->gain_obj.db_gain) && - cbox_execute_on(fb, NULL, "/bus", "s", error, instr->aux_output_names[id] ? instr->aux_output_names[id] : ""))) - return FALSE; - return cbox_module_slot_process_cmd(&output->insert, fb, cmd, subcmd, CBOX_GET_DOCUMENT(instr->scene), instr->scene->rt, instr->scene->engine, error); - } - else if (!strcmp(subcmd, "/bus") && !strcmp(cmd->arg_types, "s")) - { - struct cbox_scene *scene = instr->scene; - if (!CBOX_ARG_S(cmd, 0)) - { - struct cbox_aux_bus *old_bus = cbox_rt_swap_pointers(instr->module->rt, (void **)&instr->aux_outputs[id], NULL); - if (old_bus) - cbox_aux_bus_unref(old_bus); - return TRUE; - } - for (uint32_t i = 0; i < scene->aux_bus_count; i++) - { - if (!scene->aux_buses[i]) - continue; - if (!strcmp(scene->aux_buses[i]->name, CBOX_ARG_S(cmd, 0))) - { - g_free(instr->aux_output_names[id]); - instr->aux_output_names[id] = g_strdup(scene->aux_buses[i]->name); - cbox_aux_bus_ref(scene->aux_buses[i]); - struct cbox_aux_bus *old_bus = cbox_rt_swap_pointers(instr->module->rt, (void **)&instr->aux_outputs[id], scene->aux_buses[i]); - if (old_bus) - cbox_aux_bus_unref(old_bus); - return TRUE; - } - } - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown aux bus: %s", CBOX_ARG_S(cmd, 0)); - return FALSE; - } - else if (!strcmp(subcmd, "/output") && !strcmp(cmd->arg_types, "i")) // not supported - { - cbox_set_command_error(error, cmd); - return FALSE; - } - else // otherwise, treat just like an command on normal (non-aux) output - return cbox_instrument_output_process_cmd(instr, output, fb, cmd, subcmd, error); -} - -gboolean cbox_instrument_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct cbox_instrument *instr = ct->user_data; - const char *subcommand = NULL; - int index = 0; - int aux_offset = instr->module->aux_offset / 2; - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - if (!cbox_execute_on(fb, NULL, "/engine", "s", error, instr->module->engine_name)) - return FALSE; - if (!cbox_execute_on(fb, NULL, "/aux_offset", "i", error, instr->module->aux_offset / 2 + 1)) - return FALSE; - if (!cbox_execute_on(fb, NULL, "/outputs", "i", error, instr->module->outputs / 2)) - return FALSE; - return CBOX_OBJECT_DEFAULT_STATUS(instr, fb, error); - } - else if (cbox_parse_path_part_int(cmd, "/output/", &subcommand, &index, 1, aux_offset, error)) - { - if (!subcommand) - return FALSE; - if (index < 1 || index > (int)(1 + instr->module->aux_offset)) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid position %d (valid are 1..%d)", index, instr->module->aux_offset); - return FALSE; - } - return cbox_instrument_output_process_cmd(instr, &instr->outputs[index - 1], fb, cmd, subcommand, error); - } - else if (cbox_parse_path_part_int(cmd, "/aux/", &subcommand, &index, 1, instr->aux_output_count, error)) - { - if (!subcommand) - return FALSE; - int acount = 1 + instr->module->outputs - instr->module->aux_offset; - if (index < 1 || index > acount) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid position %d (valid are 1..%d)", index, acount); - return FALSE; - } - return cbox_instrument_aux_process_cmd(instr, &instr->outputs[aux_offset + index - 1], index - 1, fb, cmd, subcommand, error); - } - else - if (!strncmp(cmd->command, "/engine/",8)) - { - if (!instr->module->cmd_target.process_cmd) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "The engine %s has no command target defined", instr->module->engine_name); - return FALSE; - } - return cbox_execute_sub(&instr->module->cmd_target, fb, cmd, cmd->command + 7, error); - } - else if (!strcmp(cmd->command, "/move_to") && !strcmp(cmd->arg_types, "si")) - { - struct cbox_scene *new_scene = (struct cbox_scene *)CBOX_ARG_O(cmd, 0, instr->scene, cbox_scene, error); - if (!new_scene) - return FALSE; - int dstpos = CBOX_ARG_I(cmd, 1) - 1; - - if (dstpos < 0 || (uint32_t)dstpos > new_scene->layer_count) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid position %d (valid are 1..%d or 0 for append)", dstpos + 1, 1 + new_scene->layer_count); - return FALSE; - } - - return cbox_scene_move_instrument_to(instr->scene, instr, new_scene, dstpos, error); - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); -} - -void cbox_instrument_destroy_if_unused(struct cbox_instrument *instrument) -{ - if (instrument->refcount == 0) - CBOX_DELETE(instrument); -} - -void cbox_instrument_destroyfunc(struct cbox_objhdr *objhdr) -{ - struct cbox_instrument *instrument = CBOX_H2O(objhdr); - assert(instrument->refcount == 0); - for (uint32_t i = 0; i < (uint32_t)instrument->module->outputs >> 1; i ++) - { - cbox_instrument_output_uninit(&instrument->outputs[i]); - } - free(instrument->outputs); - for (uint32_t i = 0; i < instrument->aux_output_count; i++) - { - g_free(instrument->aux_output_names[i]); - } - free(instrument->aux_output_names); - free(instrument->aux_outputs); - CBOX_DELETE(instrument->module); - free(instrument); -} - -void cbox_instrument_unref_aux_buses(struct cbox_instrument *instrument) -{ - for (uint32_t j = 0; j < instrument->aux_output_count; j++) - { - if (instrument->aux_outputs[j]) - cbox_aux_bus_unref(instrument->aux_outputs[j]); - } -} - -void cbox_instrument_disconnect_aux_bus(struct cbox_instrument *instrument, struct cbox_aux_bus *bus) -{ - for (uint32_t j = 0; j < instrument->aux_output_count; j++) - { - if (instrument->aux_outputs[j] == bus) - { - cbox_aux_bus_unref(instrument->aux_outputs[j]); - instrument->aux_outputs[j] = NULL; - } - } -} - -void cbox_instrument_output_init(struct cbox_instrument_output *output, struct cbox_scene *scene, uint32_t max_numsamples) -{ - cbox_recording_source_init(&output->rec_dry, scene, max_numsamples, 2); - cbox_recording_source_init(&output->rec_wet, scene, max_numsamples, 2); - output->insert = NULL; - output->output_bus = 0; - cbox_gain_init(&output->gain_obj); -} - - -void cbox_instrument_output_uninit(struct cbox_instrument_output *output) -{ - cbox_recording_source_uninit(&output->rec_dry); - cbox_recording_source_uninit(&output->rec_wet); - if (output->insert) - { - CBOX_DELETE(output->insert); - output->insert = NULL; - } -} diff --git a/template/calfbox/instr.h b/template/calfbox/instr.h deleted file mode 100644 index a8b4eb8..0000000 --- a/template/calfbox/instr.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_INSTR_H -#define CBOX_INSTR_H - -#include "dspmath.h" -#include "recsrc.h" - -CBOX_EXTERN_CLASS(cbox_instrument) - -struct cbox_module; -struct cbox_rt; -struct cbox_scene; -struct cbox_instruments; - -struct cbox_instrument_output -{ - struct cbox_module *insert; - int output_bus; - struct cbox_gain gain_obj; - struct cbox_recording_source rec_dry, rec_wet; -}; - -struct cbox_instrument -{ - CBOX_OBJECT_HEADER() - struct cbox_command_target cmd_target; - struct cbox_module *module; - struct cbox_instrument_output *outputs; - struct cbox_scene *scene; - int refcount; - gchar **aux_output_names; - struct cbox_aux_bus **aux_outputs; - uint32_t aux_output_count; -}; - -extern void cbox_instrument_unref_aux_buses(struct cbox_instrument *instrument); -extern void cbox_instrument_disconnect_aux_bus(struct cbox_instrument *instrument, struct cbox_aux_bus *bus); -extern void cbox_instrument_destroy_if_unused(struct cbox_instrument *instrument); -extern gboolean cbox_instrument_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error); - -extern void cbox_instrument_output_init(struct cbox_instrument_output *output, struct cbox_scene *scene, uint32_t max_numsamples); -extern void cbox_instrument_output_uninit(struct cbox_instrument_output *output); - -#endif diff --git a/template/calfbox/py/instr_gui.py b/template/calfbox/instr_gui.py similarity index 100% rename from template/calfbox/py/instr_gui.py rename to template/calfbox/instr_gui.py diff --git a/template/calfbox/io.c b/template/calfbox/io.c deleted file mode 100644 index 9ffa7df..0000000 --- a/template/calfbox/io.c +++ /dev/null @@ -1,625 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "app.h" -#include "config.h" -#include "config-api.h" -#include "engine.h" -#include "errors.h" -#include "hwcfg.h" -#include "io.h" -#include "meter.h" -#include "midi.h" -#include "mididest.h" -#include "recsrc.h" -#include "seq.h" - -#include -#include -#include -#include - -const char *cbox_io_section = "io"; - -gboolean cbox_io_init(struct cbox_io *io, struct cbox_open_params *const params, struct cbox_command_target *fb, GError **error) -{ -#if USE_JACK -#if USE_LIBUSB - if (cbox_config_get_int(cbox_io_section, "use_usb", 0)) - return cbox_io_init_usb(io, params, fb, error); -#endif - return cbox_io_init_jack(io, params, fb, error); -#else -#if USE_LIBUSB - return cbox_io_init_usb(io, params, fb, error); -#endif -#endif -} - -int cbox_io_get_sample_rate(struct cbox_io *io) -{ - return io->impl->getsampleratefunc(io->impl); -} - -void cbox_io_poll_ports(struct cbox_io *io, struct cbox_command_target *fb) -{ - io->impl->pollfunc(io->impl, fb); -} - -int cbox_io_get_midi_data(struct cbox_io *io, struct cbox_midi_buffer *destination) -{ - return io->impl->getmidifunc(io->impl, destination); -} - -int cbox_io_start(struct cbox_io *io, struct cbox_io_callbacks *cb, struct cbox_command_target *fb) -{ - io->cb = cb; - return io->impl->startfunc(io->impl, fb, NULL); -} - -gboolean cbox_io_get_disconnect_status(struct cbox_io *io, GError **error) -{ - return io->impl->getstatusfunc(io->impl, error); -} - -gboolean cbox_io_cycle(struct cbox_io *io, struct cbox_command_target *fb, GError **error) -{ - return io->impl->cyclefunc(io->impl, fb, error); -} - -int cbox_io_stop(struct cbox_io *io) -{ - int result = io->impl->stopfunc(io->impl, NULL); - if (io->cb && io->cb->on_stopped) - io->cb->on_stopped(io->cb->user_data); - io->cb = NULL; - return result; -} - -struct cbox_midi_output *cbox_io_get_midi_output(struct cbox_io *io, const char *name, const struct cbox_uuid *uuid) -{ - if (uuid) - { - for (GSList *p = io->midi_outputs; p; p = g_slist_next(p)) - { - struct cbox_midi_output *midiout = p->data; - if (!midiout->removing && cbox_uuid_equal(&midiout->uuid, uuid)) - return midiout; - } - } - if (name) - { - for (GSList *p = io->midi_outputs; p; p = g_slist_next(p)) - { - struct cbox_midi_output *midiout = p->data; - if (!midiout->removing && !strcmp(midiout->name, name)) - return midiout; - } - } - return NULL; -} - -struct cbox_midi_input *cbox_io_get_midi_input(struct cbox_io *io, const char *name, const struct cbox_uuid *uuid) -{ - if (uuid) - { - for (GSList *p = io->midi_inputs; p; p = g_slist_next(p)) - { - struct cbox_midi_input *midiin = p->data; - if (!midiin->removing && cbox_uuid_equal(&midiin->uuid, uuid)) - return midiin; - } - } - if (name) - { - for (GSList *p = io->midi_inputs; p; p = g_slist_next(p)) - { - struct cbox_midi_input *midiin = p->data; - if (!midiin->removing && !strcmp(midiin->name, name)) - return midiin; - } - } - return NULL; -} - -struct cbox_audio_output *cbox_io_get_audio_output(struct cbox_io *io, const char *name, const struct cbox_uuid *uuid) -{ - if (uuid) - { - for (GSList *p = io->audio_outputs; p; p = g_slist_next(p)) - { - struct cbox_audio_output *audioout = p->data; - if (!audioout->removing && cbox_uuid_equal(&audioout->uuid, uuid)) - return audioout; - } - } - if (name) - { - for (GSList *p = io->audio_outputs; p; p = g_slist_next(p)) - { - struct cbox_audio_output *audioout = p->data; - if (!audioout->removing && !strcmp(audioout->name, name)) - return audioout; - } - } - return NULL; -} - -struct cbox_midi_output *cbox_io_create_midi_output(struct cbox_io *io, const char *name, GError **error) -{ - struct cbox_midi_output *midiout = cbox_io_get_midi_output(io, name, NULL); - if (midiout) - return midiout; - - midiout = io->impl->createmidioutfunc(io->impl, name, error); - if (!midiout) - return NULL; - - io->midi_outputs = g_slist_prepend(io->midi_outputs, midiout); - - // Notify client code to connect to new outputs if needed - if (io->cb->on_midi_outputs_changed) - io->cb->on_midi_outputs_changed(io->cb->user_data); - return midiout; -} - -void cbox_io_destroy_midi_output(struct cbox_io *io, struct cbox_midi_output *midiout) -{ - midiout->removing = TRUE; - - // This is not a very efficient way to do it. However, in this case, - // the list will rarely contain more than 10 elements, so simplicity - // and correctness may be more important. - GSList *copy = g_slist_copy(io->midi_outputs); - copy = g_slist_remove(copy, midiout); - - GSList *old = io->midi_outputs; - io->midi_outputs = copy; - - cbox_midi_merger_close(&midiout->merger, app.rt); - assert(!midiout->merger.inputs); - - // Notify client code to disconnect the output and to make sure the RT code - // is not using the old list anymore - if (io->cb->on_midi_outputs_changed) - io->cb->on_midi_outputs_changed(io->cb->user_data); - - assert(!midiout->merger.inputs); - - g_slist_free(old); - io->impl->destroymidioutfunc(io->impl, midiout); -} - -struct cbox_midi_input *cbox_io_create_midi_input(struct cbox_io *io, const char *name, GError **error) -{ - struct cbox_midi_input *midiin = cbox_io_get_midi_input(io, name, NULL); - if (midiin) - return midiin; - - midiin = io->impl->createmidiinfunc(io->impl, name, error); - if (!midiin) - return NULL; - - io->midi_inputs = g_slist_prepend(io->midi_inputs, midiin); - - // Notify client code to connect to new inputs if needed - if (io->cb->on_midi_inputs_changed) - io->cb->on_midi_inputs_changed(io->cb->user_data); - return midiin; -} - -void cbox_io_destroy_midi_input(struct cbox_io *io, struct cbox_midi_input *midiin) -{ - midiin->removing = TRUE; - - // This is not a very efficient way to do it. However, in this case, - // the list will rarely contain more than 10 elements, so simplicity - // and correctness may be more important. - GSList *copy = g_slist_copy(io->midi_inputs); - copy = g_slist_remove(copy, midiin); - - GSList *old = io->midi_inputs; - io->midi_inputs = copy; - - // Notify client code to disconnect the input and to make sure the RT code - // is not using the old list anymore - if (io->cb->on_midi_inputs_changed) - io->cb->on_midi_inputs_changed(io->cb->user_data); - - g_slist_free(old); - io->impl->destroymidiinfunc(io->impl, midiin); -} - -void cbox_io_destroy_all_midi_ports(struct cbox_io *io) -{ - for (GSList *p = io->midi_outputs; p; p = g_slist_next(p)) - { - struct cbox_midi_output *midiout = p->data; - midiout->removing = TRUE; - } - for (GSList *p = io->midi_inputs; p; p = g_slist_next(p)) - { - struct cbox_midi_output *midiin = p->data; - midiin->removing = TRUE; - } - - GSList *old_i = io->midi_inputs, *old_o = io->midi_outputs; - io->midi_outputs = NULL; - io->midi_inputs = NULL; - // Notify client code to disconnect the output and to make sure the RT code - // is not using the old list anymore - if (io->cb && io->cb->on_midi_outputs_changed) - io->cb->on_midi_outputs_changed(io->cb->user_data); - if (io->cb && io->cb->on_midi_inputs_changed) - io->cb->on_midi_inputs_changed(io->cb->user_data); - - while(old_o) - { - struct cbox_midi_output *midiout = old_o->data; - cbox_midi_merger_close(&midiout->merger, app.rt); - assert(!midiout->merger.inputs); - io->impl->destroymidioutfunc(io->impl, midiout); - old_o = g_slist_remove(old_o, midiout); - } - g_slist_free(old_o); - - while(old_i) - { - struct cbox_midi_input *midiin = old_i->data; - io->impl->destroymidiinfunc(io->impl, midiin); - old_i = g_slist_remove(old_i, midiin); - } - g_slist_free(old_i); -} - -static void cbox_audio_output_router_record_block(struct cbox_recorder *handler, const float **buffers, uint32_t offset, uint32_t numsamples) -{ - struct cbox_audio_output_router *router = handler->user_data; - if (router->left->removing || router->right->removing) - return; - cbox_gain_add_stereo(&router->gain, &router->left->buffer[offset], buffers[0], &router->right->buffer[offset], buffers[1], numsamples); -} - -static gboolean cbox_audio_output_router_attach(struct cbox_recorder *handler, struct cbox_recording_source *src, GError **error) -{ - struct cbox_audio_output_router *router = handler->user_data; - if (router->attached) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Router already attached"); - return FALSE; - } - router->source = src; - router->attached++; - return TRUE; -} - -static gboolean cbox_audio_output_router_detach(struct cbox_recorder *handler, GError **error) -{ - struct cbox_audio_output_router *router = handler->user_data; - if (router->attached != 1) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Router not yet attached"); - return FALSE; - } - assert (router->source); - --router->attached; - assert (router->attached == 0); - router->source = NULL; - return TRUE; -} - -static void cbox_audio_output_router_destroy(struct cbox_recorder *handler) -{ - struct cbox_audio_output_router *router = handler->user_data; - if (router->attached) - cbox_recording_source_detach(router->source, &router->recorder, NULL); - assert(!router->attached); - router->left->users--; - router->right->users--; -} - -static gboolean cbox_audio_output_router_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct cbox_audio_output_router *router = ct->user_data; - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - if (!cbox_execute_on(fb, NULL, "/gain", "f", error, router->gain.db_gain)) - return FALSE; - return CBOX_OBJECT_DEFAULT_STATUS(&router->recorder, fb, error); - } - if (!strcmp(cmd->command, "/gain") && !strcmp(cmd->arg_types, "f")) - { - cbox_gain_set_db(&router->gain, CBOX_ARG_F(cmd, 0)); - return TRUE; - } - return cbox_object_default_process_cmd(ct, fb, cmd, error); -} - -struct cbox_audio_output_router *cbox_io_create_audio_output_router(struct cbox_io *io, struct cbox_engine *engine, struct cbox_audio_output *left, struct cbox_audio_output *right) -{ - struct cbox_audio_output_router *router = calloc(1, sizeof(struct cbox_audio_output_router)); - CBOX_OBJECT_HEADER_INIT(&router->recorder, cbox_recorder, CBOX_GET_DOCUMENT(engine)); - cbox_command_target_init(&router->recorder.cmd_target, cbox_audio_output_router_process_cmd, &router->recorder); - router->recorder.user_data = router; - router->recorder.attach = cbox_audio_output_router_attach; - router->recorder.record_block = cbox_audio_output_router_record_block; - router->recorder.detach = cbox_audio_output_router_detach; - router->recorder.destroy = cbox_audio_output_router_destroy; - router->source = NULL; - router->left = left; - router->right = right; - router->attached = 0; - cbox_gain_init(&router->gain); - cbox_gain_set_db(&router->gain, 12.0); - left->users++; - right->users++; - CBOX_OBJECT_REGISTER(&router->recorder); - return router; -} - -struct cbox_audio_output *cbox_io_create_audio_output(struct cbox_io *io, const char *name, GError **error) -{ - struct cbox_audio_output *audioout = cbox_io_get_audio_output(io, name, NULL); - if (audioout) - return audioout; - - audioout = io->impl->createaudiooutfunc(io->impl, name, error); - if (!audioout) - return NULL; - - io->audio_outputs = g_slist_prepend(io->audio_outputs, audioout); - - // Notify client code to connect to new outputs if needed - if (io->cb->on_audio_outputs_changed) - io->cb->on_audio_outputs_changed(io->cb->user_data); - return audioout; -} - -struct cbox_audio_output *cbox_io_get_audio_output_by_uuid_string(struct cbox_io *io, const char *uuidstr, GError **error) -{ - struct cbox_uuid uuid; - if (!cbox_uuid_fromstring(&uuid, uuidstr, error)) - return NULL; - struct cbox_audio_output *audioout = cbox_io_get_audio_output(io, NULL, &uuid); - if (!audioout) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr); - return NULL; - } - return audioout; -} - -gboolean cbox_io_destroy_audio_output(struct cbox_io *io, struct cbox_audio_output *audioout, GError **error) -{ - if (audioout->users) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' is in use", audioout->name); - return FALSE; - } - audioout->removing = TRUE; - - // This is not a very efficient way to do it. However, in this case, - // the list will rarely contain more than 10 elements, so simplicity - // and correctness may be more important. - GSList *copy = g_slist_copy(io->audio_outputs); - copy = g_slist_remove(copy, audioout); - - GSList *old = io->audio_outputs; - io->audio_outputs = copy; - - // Notify client code to disconnect the output and to make sure the RT code - // is not using the old list anymore - if (io->cb->on_audio_outputs_changed) - io->cb->on_audio_outputs_changed(io->cb->user_data); - - g_slist_free(old); - io->impl->destroyaudiooutfunc(io->impl, audioout); - return TRUE; -} - -gboolean cbox_io_process_cmd(struct cbox_io *io, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error, gboolean *cmd_handled) -{ - *cmd_handled = FALSE; - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - *cmd_handled = TRUE; - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - for (GSList *p = io->midi_inputs; p; p = g_slist_next(p)) - { - struct cbox_midi_input *midiin = p->data; - if (!midiin->removing) - { - if (!cbox_execute_on(fb, NULL, "/midi_input", "su", error, midiin->name, &midiin->uuid)) - return FALSE; - } - } - for (GSList *p = io->midi_outputs; p; p = g_slist_next(p)) - { - struct cbox_midi_output *midiout = p->data; - if (!midiout->removing) - { - if (!cbox_execute_on(fb, NULL, "/midi_output", "su", error, midiout->name, &midiout->uuid)) - return FALSE; - } - } - return cbox_execute_on(fb, NULL, "/audio_inputs", "i", error, io->io_env.input_count) && - cbox_execute_on(fb, NULL, "/audio_outputs", "i", error, io->io_env.output_count) && - cbox_execute_on(fb, NULL, "/sample_rate", "i", error, io->io_env.srate) && - cbox_execute_on(fb, NULL, "/buffer_size", "i", error, io->io_env.buffer_size); - } - else if (io->impl->createmidiinfunc && !strcmp(cmd->command, "/create_midi_input") && !strcmp(cmd->arg_types, "s")) - { - *cmd_handled = TRUE; - struct cbox_midi_input *midiin; - midiin = cbox_io_create_midi_input(io, CBOX_ARG_S(cmd, 0), error); - if (!midiin) - return FALSE; - cbox_midi_appsink_init(&midiin->appsink, app.rt, &app.engine->stmap->tmap); - return cbox_uuid_report(&midiin->uuid, fb, error); - } - else if (!strcmp(cmd->command, "/route_midi_input") && !strcmp(cmd->arg_types, "ss")) - { - *cmd_handled = TRUE; - const char *uuidstr = CBOX_ARG_S(cmd, 0); - struct cbox_uuid uuid; - if (!cbox_uuid_fromstring(&uuid, uuidstr, error)) - return FALSE; - struct cbox_midi_input *midiin = cbox_io_get_midi_input(io, NULL, &uuid); - if (!midiin) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr); - return FALSE; - } - if (*CBOX_ARG_S(cmd, 1)) - { - if (cbox_uuid_fromstring(&midiin->output, CBOX_ARG_S(cmd, 1), error)) - midiin->output_set = TRUE; - } - else - midiin->output_set = FALSE; - if (io->impl->updatemidiinroutingfunc) - io->impl->updatemidiinroutingfunc(io->impl); - if (io->cb->on_midi_inputs_changed) - io->cb->on_midi_inputs_changed(io->cb->user_data); - return TRUE; - } - else if (!strcmp(cmd->command, "/set_appsink_for_midi_input") && !strcmp(cmd->arg_types, "si")) - { - *cmd_handled = TRUE; - const char *uuidstr = CBOX_ARG_S(cmd, 0); - struct cbox_uuid uuid; - if (!cbox_uuid_fromstring(&uuid, uuidstr, error)) - return FALSE; - struct cbox_midi_input *midiin = cbox_io_get_midi_input(io, NULL, &uuid); - if (!midiin) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr); - return FALSE; - } - midiin->enable_appsink = CBOX_ARG_I(cmd, 1); - return TRUE; - } - else if (!strcmp(cmd->command, "/get_new_events") && !strcmp(cmd->arg_types, "s")) - { - *cmd_handled = TRUE; - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - const char *uuidstr = CBOX_ARG_S(cmd, 0); - struct cbox_uuid uuid; - if (!cbox_uuid_fromstring(&uuid, uuidstr, error)) - return FALSE; - struct cbox_midi_input *midiin = cbox_io_get_midi_input(io, NULL, &uuid); - if (!midiin) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr); - return FALSE; - } - if (!midiin->enable_appsink) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "App sink not enabled for port '%s'", uuidstr); - return FALSE; - } - return cbox_midi_appsink_send_to(&midiin->appsink, fb, error); - } - else if (io->impl->createmidioutfunc && !strcmp(cmd->command, "/create_midi_output") && !strcmp(cmd->arg_types, "s")) - { - *cmd_handled = TRUE; - struct cbox_midi_output *midiout; - midiout = cbox_io_create_midi_output(io, CBOX_ARG_S(cmd, 0), error); - if (!midiout) - return FALSE; - return cbox_uuid_report(&midiout->uuid, fb, error); - } - else if (io->impl->destroymidiinfunc && !strcmp(cmd->command, "/delete_midi_input") && !strcmp(cmd->arg_types, "s")) - { - *cmd_handled = TRUE; - const char *uuidstr = CBOX_ARG_S(cmd, 0); - struct cbox_uuid uuid; - if (!cbox_uuid_fromstring(&uuid, uuidstr, error)) - return FALSE; - struct cbox_midi_input *midiin = cbox_io_get_midi_input(io, NULL, &uuid); - if (!midiin) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr); - return FALSE; - } - cbox_io_destroy_midi_input(io, midiin); - return TRUE; - } - else if (io->impl->destroymidioutfunc && !strcmp(cmd->command, "/delete_midi_output") && !strcmp(cmd->arg_types, "s")) - { - *cmd_handled = TRUE; - const char *uuidstr = CBOX_ARG_S(cmd, 0); - struct cbox_uuid uuid; - if (!cbox_uuid_fromstring(&uuid, uuidstr, error)) - return FALSE; - struct cbox_midi_output *midiout = cbox_io_get_midi_output(io, NULL, &uuid); - if (!midiout) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr); - return FALSE; - } - cbox_io_destroy_midi_output(io, midiout); - return TRUE; - } - else if (io->impl->createaudiooutfunc && !strcmp(cmd->command, "/create_audio_output") && !strcmp(cmd->arg_types, "s")) - { - *cmd_handled = TRUE; - struct cbox_audio_output *audioout; - audioout = cbox_io_create_audio_output(io, CBOX_ARG_S(cmd, 0), error); - if (!audioout) - return FALSE; - return cbox_uuid_report(&audioout->uuid, fb, error); - } - else if (io->impl->destroyaudiooutfunc && !strcmp(cmd->command, "/delete_audio_output") && !strcmp(cmd->arg_types, "s")) - { - *cmd_handled = TRUE; - const char *uuidstr = CBOX_ARG_S(cmd, 0); - struct cbox_audio_output *audioout = cbox_io_get_audio_output_by_uuid_string(io, uuidstr, error); - if (!audioout) - return FALSE; - return cbox_io_destroy_audio_output(io, audioout, error); - } - else if (io->impl->createmidioutfunc && !strcmp(cmd->command, "/create_audio_output_router") && !strcmp(cmd->arg_types, "ss")) - { - *cmd_handled = TRUE; - const char *uuidstr = CBOX_ARG_S(cmd, 0); - struct cbox_audio_output *left = cbox_io_get_audio_output_by_uuid_string(io, uuidstr, error); - if (!left) - return FALSE; - uuidstr = CBOX_ARG_S(cmd, 1); - struct cbox_audio_output *right = cbox_io_get_audio_output_by_uuid_string(io, uuidstr, error); - if (!right) - return FALSE; - // XXXKF hack alert - struct cbox_audio_output_router *router = cbox_io_create_audio_output_router(io, app.engine, left, right); - return cbox_uuid_report(&router->recorder._obj_hdr.instance_uuid, fb, error); - } - return FALSE; -} - -void cbox_io_close(struct cbox_io *io) -{ - io->impl->destroyfunc(io->impl); - io->impl = NULL; -} - diff --git a/template/calfbox/io.h b/template/calfbox/io.h deleted file mode 100644 index f200c63..0000000 --- a/template/calfbox/io.h +++ /dev/null @@ -1,194 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_IO_H -#define CBOX_IO_H - -#include -#include "config.h" -#if USE_JACK -#include -#endif -#include "dspmath.h" -#include "dom.h" -#include "ioenv.h" -#include "master.h" -#include "mididest.h" -#include "recsrc.h" - -struct cbox_io; -struct cbox_io_callbacks; -struct cbox_recording_source; -struct cbox_meter; -struct cbox_midi_buffer; -struct cbox_scene; - -struct cbox_open_params -{ -}; - -struct cbox_io_impl -{ - struct cbox_io *pio; - - int (*getsampleratefunc)(struct cbox_io_impl *ioi); - gboolean (*startfunc)(struct cbox_io_impl *ioi, struct cbox_command_target *fb, GError **error); - gboolean (*stopfunc)(struct cbox_io_impl *ioi, GError **error); - gboolean (*cyclefunc)(struct cbox_io_impl *ioi, struct cbox_command_target *fb, GError **error); - gboolean (*getstatusfunc)(struct cbox_io_impl *ioi, GError **error); - void (*pollfunc)(struct cbox_io_impl *ioi, struct cbox_command_target *fb); - int (*getmidifunc)(struct cbox_io_impl *ioi, struct cbox_midi_buffer *destination); - struct cbox_midi_output *(*createmidioutfunc)(struct cbox_io_impl *ioi, const char *name, GError **error); - void (*destroymidioutfunc)(struct cbox_io_impl *ioi, struct cbox_midi_output *midiout); - struct cbox_midi_input *(*createmidiinfunc)(struct cbox_io_impl *ioi, const char *name, GError **error); - void (*destroymidiinfunc)(struct cbox_io_impl *ioi, struct cbox_midi_input *midiout); - void (*updatemidiinroutingfunc)(struct cbox_io_impl *ioi); - struct cbox_audio_output *(*createaudiooutfunc)(struct cbox_io_impl *ioi, const char *name, GError **error); - void (*destroyaudiooutfunc)(struct cbox_io_impl *ioi, struct cbox_audio_output *audioout); - void (*controltransportfunc)(struct cbox_io_impl *ioi, gboolean roll, uint32_t pos); // (uint32_t)-1 if no change - gboolean (*getsynccompletedfunc)(struct cbox_io_impl *ioi); - void (*destroyfunc)(struct cbox_io_impl *ioi); -}; - -struct cbox_io -{ - struct cbox_io_impl *impl; - struct cbox_command_target cmd_target; - - float **input_buffers; // only valid inside jack_rt_process - float **output_buffers; // only valid inside jack_rt_process - struct cbox_io_env io_env; - - struct cbox_io_callbacks *cb; - GSList *midi_inputs; - GSList *midi_outputs; - GSList *audio_outputs; - uint32_t free_running_frame_counter; -}; - -enum cbox_transport_state -{ - ts_stopping, - ts_stopped, - ts_starting, - ts_rolling, -}; - -struct cbox_transport_position -{ - uint32_t bar; - uint32_t beat; - uint32_t tick; - uint32_t offset; - double tempo; - double ticks_per_beat; - double bar_start_tick; - uint32_t timesig_num; - uint32_t timesig_denom; -}; - -struct cbox_io_callbacks -{ - void *user_data; - - void (*process)(void *user_data, struct cbox_io *io, uint32_t nframes); - void (*on_started)(void *user_data); - void (*on_stopped)(void *user_data); - void (*on_disconnected)(void *user_data); - void (*on_reconnected)(void *user_data); - void (*on_midi_inputs_changed)(void *user_data); - void (*on_midi_outputs_changed)(void *user_data); - void (*on_audio_outputs_changed)(void *user_data); - gboolean (*on_transport_sync)(void *user_data, enum cbox_transport_state state, uint32_t frame); - void (*get_transport_data)(void *user_data, gboolean explicit_pos, uint32_t time_samples, struct cbox_transport_position *tp); - gboolean (*on_tempo_sync)(void *user_data, double beats_per_minute); -}; - -struct cbox_midi_input -{ - gchar *name; - struct cbox_uuid uuid; - struct cbox_midi_buffer buffer; - gboolean removing; - gboolean output_set; - struct cbox_uuid output; - gboolean enable_appsink; - struct cbox_midi_appsink appsink; -}; - -struct cbox_midi_output -{ - gchar *name; - struct cbox_uuid uuid; - struct cbox_midi_buffer buffer; - struct cbox_midi_merger merger; - // This is set if the output is in process of being removed and should not - // be used for output. - gboolean removing; -}; - -struct cbox_audio_output -{ - gchar *name; - struct cbox_uuid uuid; - // This is set if the output is in process of being removed and should not - // be used for output. - gboolean removing; - float *buffer; - uint32_t users; -}; - -struct cbox_audio_output_router -{ - struct cbox_recorder recorder; - struct cbox_recording_source *source; - struct cbox_audio_output *left, *right; - struct cbox_gain gain; - int attached; -}; - -extern gboolean cbox_io_init(struct cbox_io *io, struct cbox_open_params *const params, struct cbox_command_target *fb, GError **error); -#if USE_JACK -extern gboolean cbox_io_init_jack(struct cbox_io *io, struct cbox_open_params *const params, struct cbox_command_target *fb, GError **error); -#endif -extern gboolean cbox_io_init_usb(struct cbox_io *io, struct cbox_open_params *const params, struct cbox_command_target *fb, GError **error); - -extern int cbox_io_start(struct cbox_io *io, struct cbox_io_callbacks *cb, struct cbox_command_target *fb); -extern int cbox_io_stop(struct cbox_io *io); -extern int cbox_io_get_sample_rate(struct cbox_io *io); -extern int cbox_io_get_midi_data(struct cbox_io *io, struct cbox_midi_buffer *destination); -extern gboolean cbox_io_get_disconnect_status(struct cbox_io *io, GError **error); -extern gboolean cbox_io_cycle(struct cbox_io *io, struct cbox_command_target *fb, GError **error); -extern void cbox_io_poll_ports(struct cbox_io *io, struct cbox_command_target *fb); -extern struct cbox_midi_input *cbox_io_get_midi_input(struct cbox_io *io, const char *name, const struct cbox_uuid *uuid); -extern struct cbox_midi_output *cbox_io_get_midi_output(struct cbox_io *io, const char *name, const struct cbox_uuid *uuid); -extern struct cbox_audio_output *cbox_io_get_audio_output(struct cbox_io *io, const char *name, const struct cbox_uuid *uuid); -extern struct cbox_audio_output *cbox_io_get_audio_output_by_uuid_string(struct cbox_io *io, const char *uuidstr, GError **error); -extern struct cbox_midi_output *cbox_io_create_midi_output(struct cbox_io *io, const char *name, GError **error); -extern void cbox_io_destroy_midi_output(struct cbox_io *io, struct cbox_midi_output *midiout); -extern struct cbox_audio_output *cbox_io_create_audio_output(struct cbox_io *io, const char *name, GError **error); -extern gboolean cbox_io_destroy_audio_output(struct cbox_io *io, struct cbox_audio_output *audioout, GError **error); -extern struct cbox_midi_input *cbox_io_create_midi_input(struct cbox_io *io, const char *name, GError **error); -extern void cbox_io_destroy_midi_input(struct cbox_io *io, struct cbox_midi_input *midiin); -extern void cbox_io_destroy_all_midi_ports(struct cbox_io *io); -extern gboolean cbox_io_process_cmd(struct cbox_io *io, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error, gboolean *cmd_handled); -extern void cbox_io_close(struct cbox_io *io); - -extern const char *cbox_io_section; - -#endif diff --git a/template/calfbox/ioenv.h b/template/calfbox/ioenv.h deleted file mode 100644 index 9b0181f..0000000 --- a/template/calfbox/ioenv.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_IOENV_H -#define CBOX_IOENV_H - -struct cbox_io_env -{ - int srate; - uint32_t buffer_size; - uint32_t input_count, output_count; -}; - -static inline void cbox_io_env_clear(struct cbox_io_env *env) -{ - env->srate = 0; - env->buffer_size = 0; - env->input_count = 0; - env->output_count = 0; -} - -static inline void cbox_io_env_copy(struct cbox_io_env *dest, const struct cbox_io_env *src) -{ - dest->srate = src->srate; - dest->buffer_size = src->buffer_size; - dest->input_count = src->input_count; - dest->output_count = src->output_count; -} - -#endif - diff --git a/template/calfbox/jack_api_example.py b/template/calfbox/jack_api_example.py deleted file mode 100644 index be40d58..0000000 --- a/template/calfbox/jack_api_example.py +++ /dev/null @@ -1,177 +0,0 @@ -from calfbox import cbox -import time - -def cmd_dumper(cmd, fb, args): - print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) - -cbox.init_engine() - -cbox.Config.add_section("drumpattern:pat1", """ -title=Straight - Verse -beats=4 -track1=bd -track2=sd -track3=hh -track4=ho -bd_note=c1 -sd_note=d1 -hh_note=f#1 -ho_note=a#1 -bd_trigger=9... .... 9.6. .... -sd_trigger=.... 9..5 .2.. 9... -hh_trigger=9353 7353 7353 73.3 -ho_trigger=.... .... .... ..3. -""") - -cbox.Config.set("io", "use_usb", 0) -cbox.start_audio(cmd_dumper) - -global Document -Document = cbox.Document - -status = cbox.JackIO.status() -client_name = status.client_name -print ("Client name: %s" % client_name) -print ("Audio inputs: %d, outputs: %d" % (status.audio_inputs, status.audio_outputs)) -print ("Sample rate: %d frames/sec" % (status.sample_rate)) -print ("JACK period: %d frames" % (status.buffer_size)) -uuid_bad = cbox.JackIO.create_midi_output('bad') -uuid_bad2 = cbox.JackIO.create_midi_input('bad2') -cbox.JackIO.autoconnect_midi_output(uuid_bad, '%s:bad2' % client_name) -print (cbox.JackIO.get_connected_ports('%s:bad' % client_name)) -try: - cbox.JackIO.disconnect_midi_input(uuid_bad) - assert False -except: - pass -try: - cbox.JackIO.disconnect_midi_output(uuid_bad2) - assert False -except: - pass -assert len(cbox.JackIO.get_connected_ports('%s:bad' % client_name)) == 1 -assert cbox.JackIO.get_connected_ports('%s:bad' % client_name) == cbox.JackIO.get_connected_ports(uuid_bad) -assert cbox.JackIO.get_connected_ports('%s:bad2' % client_name) == cbox.JackIO.get_connected_ports(uuid_bad2) -cbox.JackIO.disconnect_midi_output(uuid_bad) -assert cbox.JackIO.get_connected_ports('%s:bad' % client_name) == [] -assert cbox.JackIO.get_connected_ports('%s:bad2' % client_name) == [] -assert cbox.JackIO.get_connected_ports(uuid_bad) == [] -assert cbox.JackIO.get_connected_ports(uuid_bad2) == [] - -cbox.JackIO.autoconnect_midi_output(uuid_bad2, '%s:bad' % client_name) -assert len(cbox.JackIO.get_connected_ports('%s:bad' % client_name)) == 1 -assert cbox.JackIO.get_connected_ports('%s:bad' % client_name) == cbox.JackIO.get_connected_ports(uuid_bad) -assert cbox.JackIO.get_connected_ports('%s:bad2' % client_name) == cbox.JackIO.get_connected_ports(uuid_bad2) -cbox.JackIO.disconnect_midi_input(uuid_bad2) -assert cbox.JackIO.get_connected_ports('%s:bad' % client_name) == [] -assert cbox.JackIO.get_connected_ports('%s:bad2' % client_name) == [] -assert cbox.JackIO.get_connected_ports(uuid_bad) == [] -assert cbox.JackIO.get_connected_ports(uuid_bad2) == [] - -cbox.JackIO.autoconnect_midi_output(uuid_bad2, '%s:bad' % client_name) -assert len(cbox.JackIO.get_connected_ports('%s:bad' % client_name)) == 1 -assert cbox.JackIO.get_connected_ports('%s:bad' % client_name) == cbox.JackIO.get_connected_ports(uuid_bad) -assert cbox.JackIO.get_connected_ports('%s:bad2' % client_name) == cbox.JackIO.get_connected_ports(uuid_bad2) -cbox.JackIO.disconnect_midi_port(uuid_bad) -assert cbox.JackIO.get_connected_ports('%s:bad' % client_name) == [] -assert cbox.JackIO.get_connected_ports('%s:bad2' % client_name) == [] -assert cbox.JackIO.get_connected_ports(uuid_bad) == [] -assert cbox.JackIO.get_connected_ports(uuid_bad2) == [] - -cbox.JackIO.autoconnect_midi_output(uuid_bad2, '%s:bad' % client_name) -assert len(cbox.JackIO.get_connected_ports('%s:bad' % client_name)) == 1 -assert cbox.JackIO.get_connected_ports('%s:bad' % client_name) == cbox.JackIO.get_connected_ports(uuid_bad) -assert cbox.JackIO.get_connected_ports('%s:bad2' % client_name) == cbox.JackIO.get_connected_ports(uuid_bad2) -cbox.JackIO.disconnect_midi_port(uuid_bad2) -assert cbox.JackIO.get_connected_ports('%s:bad' % client_name) == [] -assert cbox.JackIO.get_connected_ports('%s:bad2' % client_name) == [] -assert cbox.JackIO.get_connected_ports(uuid_bad) == [] -assert cbox.JackIO.get_connected_ports(uuid_bad2) == [] - -cbox.JackIO.delete_midi_output(uuid_bad) -cbox.JackIO.delete_midi_input(uuid_bad2) - -uuid = cbox.JackIO.create_midi_output('drums') -cbox.JackIO.autoconnect_midi_output(uuid, '*alsa_pcm:.*') -cbox.JackIO.rename_midi_output(uuid, 'kettles') - -uuid_in = cbox.JackIO.create_midi_input('extra') -cbox.JackIO.autoconnect_midi_input(uuid_in, '*alsa_pcm:.*') -cbox.JackIO.rename_midi_input(uuid_in, 'extra_port') - -uuid2 = cbox.JackIO.create_midi_output('violins') - -print (cbox.JackIO.jack_transport_position()) -status = cbox.JackIO.status() -print ("Before deleting, MIDI outputs: %s" % status.midi_output) - -cbox.JackIO.delete_midi_output(uuid2) -status = cbox.JackIO.status() -print ("After deleting, MIDI outputs: %s" % status.midi_output) - -print ("Physical audio inputs: %s" % (",".join(cbox.JackIO.get_ports(".*", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SOURCE | cbox.JackIO.PORT_IS_PHYSICAL)))) -print ("Physical audio outputs: %s" % (",".join(cbox.JackIO.get_ports(".*", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SINK | cbox.JackIO.PORT_IS_PHYSICAL)))) -print ("Physical MIDI inputs: %s" % (",".join(cbox.JackIO.get_ports(".*", cbox.JackIO.MIDI_TYPE, cbox.JackIO.PORT_IS_SOURCE | cbox.JackIO.PORT_IS_PHYSICAL)))) -print ("Physical MIDI outputs: %s" % (",".join(cbox.JackIO.get_ports(".*", cbox.JackIO.MIDI_TYPE, cbox.JackIO.PORT_IS_SINK | cbox.JackIO.PORT_IS_PHYSICAL)))) - -inputs = cbox.JackIO.get_ports(".*", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SOURCE | cbox.JackIO.PORT_IS_PHYSICAL) -outputs = cbox.JackIO.get_ports(".*", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SINK | cbox.JackIO.PORT_IS_PHYSICAL) -cbox.JackIO.port_connect(inputs[0], outputs[0]) -cbox.JackIO.port_connect(inputs[1], outputs[1]) -#assert "cbox:in_3" in cbox.JackIO.get_connected_ports(inputs[0]) -cbox.JackIO.port_disconnect(inputs[0], outputs[0]) -cbox.JackIO.port_disconnect(inputs[1], outputs[1]) - -scene = Document.get_scene() -scene.clear() -instrument = scene.add_new_instrument_layer("test_sampler", "sampler").get_instrument() -pgm_no = instrument.engine.get_unused_program() -pgm = instrument.engine.load_patch_from_file(pgm_no, 'synthbass.sfz', 'SynthBass') -instrument.engine.set_patch(1, pgm_no) -instrument.engine.set_patch(10, pgm_no) - -song = Document.get_song() -track = song.add_track() -track.set_external_output(uuid) -print ("Track outputs to: %s:%s" % (client_name, track.status().external_output)) -pattern = song.load_drum_pattern("pat1") -track.add_clip(0, 0, pattern.status().loop_end, pattern) -song.set_loop(0, pattern.status().loop_end) -song.update_playback() -cbox.Transport.play() -cbox.JackIO.transport_mode(True, False) -while not cbox.JackIO.jack_transport_position().is_master: - print ("Waiting to become the master") - time.sleep(0.01) -cbox.JackIO.transport_mode(False) -while cbox.JackIO.jack_transport_position().is_master: - print ("Waiting to stop being the master") - time.sleep(0.01) -cbox.JackIO.external_tempo(False) -assert cbox.JackIO.status().external_tempo == False -cbox.JackIO.external_tempo(True) -assert cbox.JackIO.status().external_tempo == True - -uuid3 = cbox.JackIO.create_audio_output('noises') -assert "cbox:noises" in cbox.JackIO.get_ports(".*:noises", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SOURCE) -cbox.JackIO.rename_audio_output(uuid3, "silence") -router = cbox.JackIO.create_audio_output_router(uuid3, uuid3) -assert type(router) is cbox.DocRecorder -try: - cbox.JackIO.delete_audio_output(uuid3) - assert False -except Exception as e: - assert 'is in use' in str(e) -router.delete() -assert "cbox:noises" not in cbox.JackIO.get_ports(".*:noises", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SOURCE) -assert "cbox:silence" in cbox.JackIO.get_ports(".*:silence", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SOURCE) -cbox.JackIO.delete_audio_output(uuid3) -assert "cbox:silence" not in cbox.JackIO.get_ports(".*:silence", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SOURCE) - -print("Ready!") - -while True: - events = cbox.get_new_events() - if events: - print (events) - time.sleep(0.05) diff --git a/template/calfbox/jack_audio_routing.py b/template/calfbox/jack_audio_routing.py deleted file mode 100644 index 17ebb94..0000000 --- a/template/calfbox/jack_audio_routing.py +++ /dev/null @@ -1,65 +0,0 @@ -from calfbox import cbox -import time - -def cmd_dumper(cmd, fb, args): - print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) - -cbox.init_engine() -cbox.Config.set("io", "use_usb", 0) -cbox.start_audio(cmd_dumper) - -global Document -Document = cbox.Document - -status = cbox.JackIO.status() -client_name = status.client_name -print ("Client name: %s" % client_name) -print ("Audio inputs: %d, outputs: %d" % (status.audio_inputs, status.audio_outputs)) -print ("Sample rate: %d frames/sec" % (status.sample_rate)) -print ("JACK period: %d frames" % (status.buffer_size)) - -inputs = cbox.JackIO.get_ports(".*", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SOURCE | cbox.JackIO.PORT_IS_PHYSICAL) -outputs = cbox.JackIO.get_ports(".*", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SINK | cbox.JackIO.PORT_IS_PHYSICAL) - -scene = Document.get_scene() -scene.clear() -instrument = scene.add_new_instrument_layer("test_sampler", "sampler").get_instrument() -pgm_no = instrument.engine.get_unused_program() -pgm = instrument.engine.load_patch_from_file(pgm_no, 'synthbass.sfz', 'SynthBass') -instrument.engine.set_patch(1, pgm_no) -instrument.engine.set_patch(10, pgm_no) - -print ("Connecting") - -uuid = cbox.JackIO.create_audio_output('noises') -router = cbox.JackIO.create_audio_output_router(uuid, uuid) -assert type(router) is cbox.DocRecorder -router2 = cbox.JackIO.create_audio_output_router(uuid, uuid) -assert type(router2) is cbox.DocRecorder -instrument.get_output_slot(0).rec_wet.attach(router) -instrument.get_output_slot(0).rec_wet.attach(router2) - -exc = None -try: - instrument.get_output_slot(0).rec_wet.attach(router2) -except Exception as e: - exc = e -assert "Router already attached" in str(exc) - -instrument.get_output_slot(0).rec_wet.detach(router2) - -try: - instrument.get_output_slot(0).rec_wet.detach(router2) -except Exception as e: - exc = e -assert "Recorder is not attached" in str(exc) - -router.delete() - -print("Ready!") - -while True: - events = cbox.get_new_events() - if events: - print (events) - time.sleep(0.05) diff --git a/template/calfbox/jack_output_routing.py b/template/calfbox/jack_output_routing.py deleted file mode 100644 index ba69bc7..0000000 --- a/template/calfbox/jack_output_routing.py +++ /dev/null @@ -1,50 +0,0 @@ -from calfbox import cbox -import time - -def cmd_dumper(cmd, fb, args): - print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) - -cbox.init_engine() - -cbox.Config.set("io", "use_usb", 0) -cbox.Config.set("io", "midi", "*.*") - -cbox.start_audio(cmd_dumper) - -global Document -Document = cbox.Document - -status = cbox.JackIO.status() -client_name = status.client_name -print ("Client name: %s" % client_name) -print ("Audio inputs: %d, outputs: %d" % (status.audio_inputs, status.audio_outputs)) -print ("Sample rate: %d frames/sec" % (status.sample_rate)) -print ("JACK period: %d frames" % (status.buffer_size)) - -left_dry = cbox.JackIO.create_audio_output('left_dry') -right_dry = cbox.JackIO.create_audio_output('right_dry') -left_wet = cbox.JackIO.create_audio_output('left_wet', '#1') -right_wet = cbox.JackIO.create_audio_output('right_wet', '#2') -router_dry = cbox.JackIO.create_audio_output_router(left_dry, right_dry) -assert type(router_dry) is cbox.DocRecorder -router_wet = cbox.JackIO.create_audio_output_router(left_wet, right_wet) -assert type(router_wet) is cbox.DocRecorder - -scene = Document.get_scene() -scene.clear() -instrument = scene.add_new_instrument_layer("test_sampler", "tonewheel_organ").get_instrument() -instrument.get_output_slot(0).set_insert_engine("delay") -instrument.get_output_slot(0).rec_dry.attach(router_dry) -instrument.get_output_slot(0).rec_wet.attach(router_wet) -assert router_dry.uuid == instrument.get_output_slot(0).rec_dry.status().handler[0].uuid -assert router_wet.uuid == instrument.get_output_slot(0).rec_wet.status().handler[0].uuid -router_wet.set_gain(-3.0) -assert router_wet.status().gain == -3 - -print("Ready!") - -while True: - events = cbox.get_new_events() - if events: - print (events) - time.sleep(0.05) diff --git a/template/calfbox/jack_scene_routing.py b/template/calfbox/jack_scene_routing.py deleted file mode 100644 index 0ff0b8d..0000000 --- a/template/calfbox/jack_scene_routing.py +++ /dev/null @@ -1,36 +0,0 @@ -from calfbox import cbox -import time - -def cmd_dumper(cmd, fb, args): - print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) - -cbox.init_engine() - -cbox.Config.set("io", "use_usb", 0) -cbox.start_audio(cmd_dumper) - -global Document -Document = cbox.Document - -status = cbox.JackIO.status() -client_name = status.client_name -print ("Client name: %s" % client_name) -print ("Audio inputs: %d, outputs: %d" % (status.audio_inputs, status.audio_outputs)) -print ("Sample rate: %d frames/sec" % (status.sample_rate)) -print ("JACK period: %d frames" % (status.buffer_size)) - -uuid_ext1 = cbox.JackIO.create_midi_output('ext1') -uuid_ext2 = cbox.JackIO.create_midi_output('ext2') - -scene = Document.get_scene() -scene.clear() -layer = scene.add_new_midi_layer(uuid_ext2) -#layer.set_external_output(uuid_ext1) - -print("Ready!") - -while True: - events = cbox.get_new_events() - if events: - print (events) - time.sleep(0.05) diff --git a/template/calfbox/jackinput.c b/template/calfbox/jackinput.c deleted file mode 100644 index 8db3b9c..0000000 --- a/template/calfbox/jackinput.c +++ /dev/null @@ -1,153 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "app.h" -#include "config.h" -#include "config-api.h" -#include "dspmath.h" -#include "module.h" -#include -#include -#include -#include -#include -#include -#include - -#if USE_JACK - -struct jack_input_module -{ - struct cbox_module module; - - int inputs[2]; - int offset; -}; - -void jack_input_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) -{ - // struct jack_input_module *m = module->user_data; -} - -void jack_input_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) -{ - struct jack_input_module *m = module->user_data; - - for (int i = 0; i < 2; i++) - { - if (m->inputs[i] < 0) - { - for (int j = 0; j < CBOX_BLOCK_SIZE; j++) - outputs[i][j] = 0; - } - else - { - float *src = module->rt->io->input_buffers[m->inputs[i]] + m->offset; - for (int j = 0; j < CBOX_BLOCK_SIZE; j++) - outputs[i][j] = src[j]; - } - } - m->offset = (m->offset + CBOX_BLOCK_SIZE) % app.io.io_env.buffer_size; -} - -static gboolean validate_input_index(int input, const char *cfg_section, const char *type, GError **error) -{ - if ((input < 1 || input > (int)app.io.io_env.input_count) && input != -1) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_OUT_OF_RANGE, "%s: invalid value for %s (%d), allowed values are 1..%d or -1 for unconnected", cfg_section, type, input, app.io.io_env.input_count); - return FALSE; - } - return TRUE; -} - -static void jack_input_destroyfunc(struct cbox_module *module) -{ -} - -static int to_base1(int val) -{ - if (val < 0) - return val; - return 1 + val; -} - -gboolean jack_input_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct jack_input_module *m = ct->user_data; - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - if (!cbox_execute_on(fb, NULL, "/inputs", "ii", error, to_base1(m->inputs[0]), to_base1(m->inputs[1]))) - return FALSE; - return CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error); - } - else if (!strcmp(cmd->command, "/inputs") && !strcmp(cmd->arg_types, "ii")) - { - int left_input = CBOX_ARG_I(cmd, 0); - int right_input = CBOX_ARG_I(cmd, 1); - if (!validate_input_index(left_input, "script", "left input", error)) - return FALSE; - if (!validate_input_index(right_input, "script", "right input", error)) - return FALSE; - m->inputs[0] = left_input < 0 ? -1 : left_input - 1; - m->inputs[1] = right_input < 0 ? -1 : right_input - 1; - return TRUE; - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); - return TRUE; -} - -MODULE_CREATE_FUNCTION(jack_input) -{ - static int inited = 0; - if (!inited) - { - inited = 1; - } - - int left_input = cbox_config_get_int(cfg_section, "left_input", 1); - int right_input = cbox_config_get_int(cfg_section, "right_input", 2); - if (!validate_input_index(left_input, cfg_section, "left_input", error)) - return NULL; - if (!validate_input_index(right_input, cfg_section, "right_input", error)) - return NULL; - - struct jack_input_module *m = malloc(sizeof(struct jack_input_module)); - CALL_MODULE_INIT(m, 0, 2, jack_input); - m->module.process_event = jack_input_process_event; - m->module.process_block = jack_input_process_block; - - m->inputs[0] = left_input - 1; - m->inputs[1] = right_input - 1; - m->offset = 0; - - return &m->module; -} - - -struct cbox_module_keyrange_metadata jack_input_keyranges[] = { -}; - -struct cbox_module_livecontroller_metadata jack_input_controllers[] = { -}; - -DEFINE_MODULE(jack_input, 0, 2) - -#endif diff --git a/template/calfbox/jackio.c b/template/calfbox/jackio.c deleted file mode 100644 index 9cc938e..0000000 --- a/template/calfbox/jackio.c +++ /dev/null @@ -1,1429 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2012 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config.h" - -#if USE_JACK - -#include "app.h" -#include "config-api.h" -#include "errors.h" -#include "hwcfg.h" -#include "io.h" -#include "meter.h" -#include "midi.h" -#include "mididest.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct cbox_jack_io_impl -{ - struct cbox_io_impl ioi; - - jack_client_t *client; - jack_port_t **inputs; - jack_port_t **outputs; - jack_port_t *midi; - char *error_str; // set to non-NULL if client has been booted out by JACK - char *client_name; - gboolean enable_common_midi_input; - jack_transport_state_t last_transport_state; - gboolean debug_transport; - gboolean external_tempo; - uint32_t master_detect_bits; - - jack_ringbuffer_t *rb_autoconnect; -}; - -/////////////////////////////////////////////////////////////////////////////// - -struct cbox_jack_midi_input -{ - struct cbox_midi_input hdr; - gchar *autoconnect_spec; - jack_port_t *port; - struct cbox_jack_io_impl *jii; -}; - -struct cbox_jack_midi_output -{ - struct cbox_midi_output hdr; - gchar *autoconnect_spec; - jack_port_t *port; - struct cbox_jack_io_impl *jii; -}; - -struct cbox_jack_audio_output -{ - struct cbox_audio_output hdr; - gchar *autoconnect_spec; - jack_port_t *port; - struct cbox_jack_io_impl *jii; -}; - -static struct cbox_midi_input *cbox_jackio_create_midi_in(struct cbox_io_impl *impl, const char *name, GError **error); -static struct cbox_midi_output *cbox_jackio_create_midi_out(struct cbox_io_impl *impl, const char *name, GError **error); -static struct cbox_audio_output *cbox_jackio_create_audio_out(struct cbox_io_impl *impl, const char *name, GError **error); -static void cbox_jackio_destroy_midi_in(struct cbox_io_impl *ioi, struct cbox_midi_input *midiin); -static void cbox_jackio_destroy_midi_out(struct cbox_io_impl *ioi, struct cbox_midi_output *midiout); -static void cbox_jackio_destroy_audio_out(struct cbox_io_impl *ioi, struct cbox_audio_output *audioout); -static void cbox_jack_midi_output_set_autoconnect(struct cbox_jack_midi_output *jmo, const gchar *autoconnect_spec); -static void cbox_jack_audio_output_set_autoconnect(struct cbox_jack_audio_output *jao, const gchar *autoconnect_spec); - -static const char *transport_state_names[] = {"Stopped", "Rolling", "Looping?", "Starting", "Unknown/invalid#4", "Unknown/invalid#5", "Unknown/invalid#6" }; - -void cbox_jack_midi_input_destroy(struct cbox_jack_midi_input *jmi) -{ - if (jmi->port) - { - jack_port_unregister(jmi->jii->client, jmi->port); - jmi->port = NULL; - } - g_free(jmi->hdr.name); - g_free(jmi->autoconnect_spec); - free(jmi); -} - -void cbox_jack_midi_output_destroy(struct cbox_jack_midi_output *jmo) -{ - if (jmo->port) - { - jack_port_unregister(jmo->jii->client, jmo->port); - jmo->port = NULL; - } - g_free(jmo->hdr.name); - g_free(jmo->autoconnect_spec); - assert(!jmo->hdr.merger.inputs); - free(jmo); -} - -void cbox_jack_audio_output_destroy(struct cbox_jack_audio_output *jao) -{ - if (jao->port) - { - jack_port_unregister(jao->jii->client, jao->port); - jao->port = NULL; - } - g_free(jao->hdr.name); - g_free(jao->autoconnect_spec); - free(jao); -} - -/////////////////////////////////////////////////////////////////////////////// - -static int copy_midi_data_to_buffer(jack_port_t *port, int buffer_size, struct cbox_midi_buffer *destination) -{ - void *midi = jack_port_get_buffer(port, buffer_size); - if (!midi) - return 0; - uint32_t event_count = jack_midi_get_event_count(midi); - - cbox_midi_buffer_clear(destination); - for (uint32_t i = 0; i < event_count; i++) - { - jack_midi_event_t event; - - if (!jack_midi_event_get(&event, midi, i)) - { - if (!cbox_midi_buffer_write_event(destination, event.time, event.buffer, event.size)) - return -i; - } - else - return -i; - } - - return event_count; -} - -/////////////////////////////////////////////////////////////////////////////// - -static int process_cb(jack_nframes_t nframes, void *arg) -{ - struct cbox_jack_io_impl *jii = arg; - struct cbox_io *io = jii->ioi.pio; - struct cbox_io_callbacks *cb = io->cb; - - io->io_env.buffer_size = nframes; - for (uint32_t i = 0; i < io->io_env.input_count; i++) - io->input_buffers[i] = jack_port_get_buffer(jii->inputs[i], nframes); - for (uint32_t i = 0; i < io->io_env.output_count; i++) - { - io->output_buffers[i] = jack_port_get_buffer(jii->outputs[i], nframes); - if (!io->output_buffers[i]) - continue; - for (uint32_t j = 0; j < nframes; j ++) - io->output_buffers[i][j] = 0.f; - } - if (cb->on_transport_sync || (jii->external_tempo && cb->on_tempo_sync)) { - jack_position_t pos; - memset(&pos, 0, sizeof(pos)); - jack_transport_state_t state = jack_transport_query(jii->client, &pos); - if (jii->external_tempo && cb->on_tempo_sync && (pos.valid & JackPositionBBT) && pos.beats_per_minute > 0) { - cb->on_tempo_sync(cb->user_data, pos.beats_per_minute); - } - if (cb->on_transport_sync) - { - if (state != jii->last_transport_state) - { - jack_position_t pos; - jack_transport_query(jii->client, &pos); - if (jii->debug_transport) - g_message("JACK transport: incoming state change, state = %s, last state = %s, pos = %d\n", transport_state_names[state], transport_state_names[(int)jii->last_transport_state], (int)pos.frame); - if (state == JackTransportStopped) - { - if (cb->on_transport_sync(cb->user_data, ts_stopping, pos.frame)) - jii->last_transport_state = state; - } - else - if (state == JackTransportRolling && jii->last_transport_state == JackTransportStarting) - { - if (cb->on_transport_sync(cb->user_data, ts_rolling, pos.frame)) - jii->last_transport_state = state; - } - else - jii->last_transport_state = state; - } - } - } - for (GSList *p = io->midi_inputs; p; p = p->next) - { - struct cbox_jack_midi_input *input = p->data; - if (input->hdr.output_set || input->hdr.enable_appsink) - { - copy_midi_data_to_buffer(input->port, io->io_env.buffer_size, &input->hdr.buffer); - if (input->hdr.enable_appsink) - cbox_midi_appsink_supply(&input->hdr.appsink, &input->hdr.buffer, io->free_running_frame_counter); - } - else - cbox_midi_buffer_clear(&input->hdr.buffer); - } - for (GSList *p = io->audio_outputs; p; p = p->next) - { - struct cbox_jack_audio_output *output = p->data; - float *buffer = jack_port_get_buffer(output->port, nframes); - output->hdr.buffer = buffer; - for (uint32_t j = 0; j < nframes; j ++) - buffer[j] = 0.f; - } - cb->process(cb->user_data, io, nframes); - for (uint32_t i = 0; i < io->io_env.input_count; i++) - io->input_buffers[i] = NULL; - for (uint32_t i = 0; i < io->io_env.output_count; i++) - io->output_buffers[i] = NULL; - for (GSList *p = io->midi_outputs; p; p = g_slist_next(p)) - { - struct cbox_jack_midi_output *midiout = p->data; - - void *pbuf = jack_port_get_buffer(midiout->port, nframes); - jack_midi_clear_buffer(pbuf); - - cbox_midi_merger_render(&midiout->hdr.merger); - if (midiout->hdr.buffer.count) - { - for (uint32_t i = 0; i < midiout->hdr.buffer.count; i++) - { - const struct cbox_midi_event *event = cbox_midi_buffer_get_event(&midiout->hdr.buffer, i); - const uint8_t *pdata = cbox_midi_event_get_data(event); - if (jack_midi_event_write(pbuf, event->time, pdata, event->size)) - { - g_warning("MIDI buffer overflow on JACK output port '%s'", midiout->hdr.name); - break; - } - } - } - } - io->free_running_frame_counter += nframes; - jii->master_detect_bits <<= 1; - return 0; -} - -static void autoconnect_port(jack_client_t *client, const char *port, const char *use_name, int is_cbox_input, const jack_port_t *only_connect_port, struct cbox_command_target *fb) -{ - int res; - if (only_connect_port) - { - jack_port_t *right; - right = jack_port_by_name(client, use_name); - if (only_connect_port != right) - return; - } - - const char *pfrom = is_cbox_input ? use_name : port; - const char *pto = !is_cbox_input ? use_name : port; - - res = jack_connect(client, pfrom, pto); - if (res == EEXIST) - res = 0; - gboolean suppressed = FALSE; - if (fb) - { - if (!res) - suppressed = cbox_execute_on(fb, NULL, "/io/jack/connected", "ss", NULL, pfrom, pto); - else - suppressed = cbox_execute_on(fb, NULL, "/io/jack/connect_failed", "sss", NULL, pfrom, pto, (res == EEXIST ? "already connected" : "failed")); - } - if (!suppressed) - g_message("Connect: %s %s %s (%s)", port, is_cbox_input ? "<-" : "->", use_name, res == 0 ? "success" : (res == EEXIST ? "already connected" : "failed")); -} - -static void autoconnect_by_spec(jack_client_t *client, const char *port, const char *orig_spec, int is_cbox_input, int is_midi, const jack_port_t *only_connect_port, struct cbox_command_target *fb) -{ - char *name, *copy_spec, *dpos; - const char *use_name; - - copy_spec = g_strdup(orig_spec); - name = copy_spec; - do { - dpos = strchr(name, ';'); - if (dpos) - *dpos = '\0'; - - use_name = name; - if (use_name[0] == '#') - { - char *endptr = NULL; - long portidx = strtol(use_name + 1, &endptr, 10) - 1; - if (endptr == use_name + strlen(use_name)) - { - const char **names = jack_get_ports(client, ".*", is_midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE, is_cbox_input ? JackPortIsOutput : JackPortIsInput); - // Client killed by JACK - if (!names) { - g_free(copy_spec); - return; - } - int i; - for (i = 0; i < portidx && names[i]; i++) - ; - - if (names[i]) - autoconnect_port(client, port, names[i], is_cbox_input, only_connect_port, fb); - else - g_message("Connect: unmatched port index %d for autoconnecting %s", (int)portidx, port); - - jack_free(names); - } - } - else if (use_name[0] == '~' || use_name[0] == '*') - { - const char **names = jack_get_ports(client, use_name + 1, is_midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE, is_cbox_input ? JackPortIsOutput : JackPortIsInput); - // Client killed by JACK - if (!names) { - g_free(copy_spec); - return; - } - - if (names && names[0]) - { - if (use_name[0] == '*') - { - int i; - for (i = 0; names[i]; i++) - autoconnect_port(client, port, names[i], is_cbox_input, only_connect_port, fb); - } - else - autoconnect_port(client, port, names[0], is_cbox_input, only_connect_port, fb); - } - else - g_message("Connect: unmatched port regexp %s", use_name); - jack_free(names); - } - else - autoconnect_port(client, port, use_name, is_cbox_input, only_connect_port, fb); - - if (dpos) - name = dpos + 1; - } while(dpos); - g_free(copy_spec); -} - -static void autoconnect_by_var(jack_client_t *client, const char *port, const char *config_var, int is_cbox_input, int is_midi, const jack_port_t *only_connect_port, struct cbox_command_target *fb) -{ - const char *orig_spec = cbox_config_get_string(cbox_io_section, config_var); - if (orig_spec) - autoconnect_by_spec(client, port, orig_spec, is_cbox_input, is_midi, only_connect_port, fb); -} - -static void port_connect_cb(jack_port_id_t port, int registered, void *arg) -{ - struct cbox_jack_io_impl *jii = arg; - if (registered) - { - jack_port_t *portobj = jack_port_by_id(jii->client, port); - - jack_ringbuffer_write(jii->rb_autoconnect, (char *)&portobj, sizeof(portobj)); - } -} - -static void port_autoconnect(struct cbox_jack_io_impl *jii, jack_port_t *portobj, struct cbox_command_target *fb) -{ - struct cbox_io *io = jii->ioi.pio; - - for (uint32_t i = 0; i < io->io_env.output_count; i++) - { - gchar *cbox_port = g_strdup_printf("%s:out_%d", jii->client_name, 1 + i); - gchar *config_key = g_strdup_printf("out_%d", 1 + i); - autoconnect_by_var(jii->client, cbox_port, config_key, 0, 0, portobj, fb); - g_free(cbox_port); - g_free(config_key); - } - for (uint32_t i = 0; i < io->io_env.input_count; i++) - { - gchar *cbox_port = g_strdup_printf("%s:in_%d", jii->client_name, 1 + i); - gchar *config_key = g_strdup_printf("in_%d", 1 + i); - autoconnect_by_var(jii->client, cbox_port, config_key, 1, 0, portobj, fb); - g_free(cbox_port); - g_free(config_key); - } - for (GSList *p = io->midi_outputs; p; p = g_slist_next(p)) - { - struct cbox_jack_midi_output *midiout = p->data; - if (midiout->autoconnect_spec) - { - gchar *cbox_port = g_strdup_printf("%s:%s", jii->client_name, midiout->hdr.name); - autoconnect_by_spec(jii->client, cbox_port, midiout->autoconnect_spec, FALSE, TRUE, portobj, fb); - g_free(cbox_port); - } - } - for (GSList *p = io->midi_inputs; p; p = g_slist_next(p)) - { - struct cbox_jack_midi_input *midiin = p->data; - if (midiin->autoconnect_spec) - { - gchar *cbox_port = g_strdup_printf("%s:%s", jii->client_name, midiin->hdr.name); - autoconnect_by_spec(jii->client, cbox_port, midiin->autoconnect_spec, TRUE, TRUE, portobj, fb); - g_free(cbox_port); - } - } - for (GSList *p = io->audio_outputs; p; p = g_slist_next(p)) - { - struct cbox_jack_audio_output *audioout = p->data; - if (audioout->autoconnect_spec) - { - gchar *cbox_port = g_strdup_printf("%s:%s", jii->client_name, audioout->hdr.name); - autoconnect_by_spec(jii->client, cbox_port, audioout->autoconnect_spec, FALSE, FALSE, portobj, fb); - g_free(cbox_port); - } - } - gchar *cbox_port = g_strdup_printf("%s:midi", jii->client_name); - autoconnect_by_var(jii->client, cbox_port, "midi", 1, 1, portobj, fb); - g_free(cbox_port); -} - -int cbox_jackio_get_sample_rate(struct cbox_io_impl *impl) -{ - struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl; - - return jack_get_sample_rate(jii->client); -} - -gboolean cbox_jackio_get_status(struct cbox_io_impl *impl, GError **error) -{ - struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl; - if (!jii->error_str) - return TRUE; - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "%s", jii->error_str); - return FALSE; -} - -static void client_shutdown_cb(jack_status_t code, const char *reason, void *arg) -{ - struct cbox_jack_io_impl *jii = arg; - struct cbox_io *io = jii->ioi.pio; - jii->error_str = g_strdup(reason); - if (io->cb && io->cb->on_disconnected) - (io->cb->on_disconnected)(io->cb->user_data); -} - -static void timebase_cb(jack_transport_state_t state, jack_nframes_t nframes, - jack_position_t *pos, int new_pos, void *arg) -{ - struct cbox_jack_io_impl *jii = arg; - struct cbox_io *io = jii->ioi.pio; - - jii->master_detect_bits |= 1; - if (io->cb->get_transport_data) { - struct cbox_transport_position tpos; - - io->cb->get_transport_data(io->cb->user_data, new_pos, pos->frame, &tpos); - pos->valid = JackPositionBBT; - pos->bar = tpos.bar; - pos->beat = tpos.beat; - pos->tick = tpos.tick; - pos->bar_start_tick = tpos.bar_start_tick; - pos->ticks_per_beat = tpos.ticks_per_beat; - pos->beats_per_minute = tpos.tempo; - pos->beats_per_bar = tpos.timesig_num; - pos->beat_type = tpos.timesig_denom; - } -} - -static int sync_cb(jack_transport_state_t state, jack_position_t *pos, void *arg) -{ - struct cbox_jack_io_impl *jii = arg; - struct cbox_io *io = jii->ioi.pio; - gboolean result = TRUE; - int last_state = jii->last_transport_state; - switch(state) - { - case JackTransportStopped: - result = io->cb->on_transport_sync(io->cb->user_data, ts_stopped, pos->frame); - break; - case JackTransportStarting: - jii->last_transport_state = JackTransportStarting; - result = io->cb->on_transport_sync(io->cb->user_data, ts_starting, pos->frame); - break; - case JackTransportRolling: - result = io->cb->on_transport_sync(io->cb->user_data, ts_rolling, pos->frame); - break; - default: - // assume the client is ready - result = TRUE; - } - if (jii->debug_transport) - g_message("JACK transport: incoming sync callback, state = %s, last state = %s, pos = %d, result = %d\n", transport_state_names[state], transport_state_names[last_state], (int)pos->frame, result); - return result; -} - -gboolean cbox_jackio_set_master_mode(struct cbox_jack_io_impl *jii, int mode, GError **error) -{ - if (jii->ioi.pio->cb->get_transport_data) - { - if (mode) { - switch(jack_set_timebase_callback(jii->client, mode == 1, timebase_cb, jii)) - { - case 0: - return TRUE; - case EBUSY: - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Timebase master already set"); - return FALSE; - default: - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Timebase master could not be set"); - return FALSE; - - } - } else { - switch(jack_release_timebase(jii->client)) - { - case 0: - return TRUE; - case EINVAL: - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Not a current timebase master"); - return FALSE; - default: - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Timebase master could not be unset"); - return FALSE; - } - } - } - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Transport not supported by engine"); - return FALSE; -} - -gboolean cbox_jackio_start(struct cbox_io_impl *impl, struct cbox_command_target *fb, GError **error) -{ - struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl; - struct cbox_io *io = jii->ioi.pio; - - if (io->cb->on_transport_sync) - jack_set_sync_callback(jii->client, sync_cb, jii); - jack_set_process_callback(jii->client, process_cb, jii); - jack_set_port_registration_callback(jii->client, port_connect_cb, jii); - jack_on_info_shutdown(jii->client, client_shutdown_cb, jii); - - if (io->cb->on_started) - io->cb->on_started(io->cb->user_data); - - jack_activate(jii->client); - - if (cbox_config_has_section(cbox_io_section)) - port_autoconnect(jii, NULL, fb); - - return TRUE; -} - -gboolean cbox_jackio_stop(struct cbox_io_impl *impl, GError **error) -{ - struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl; - - if (jii->error_str) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "%s", jii->error_str); - return FALSE; - } - int result = jack_deactivate(jii->client); - if (result) - g_warning("jack_deactivate has failed, result = %d", result); - jack_release_timebase(jii->client); - jack_set_process_callback(jii->client, NULL, 0); - return TRUE; -} - -void cbox_jackio_poll_ports(struct cbox_io_impl *impl, struct cbox_command_target *fb) -{ - struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl; - - while (jack_ringbuffer_read_space(jii->rb_autoconnect) >= sizeof(jack_port_t *)) - { - jack_port_t *portobj; - jack_ringbuffer_read(jii->rb_autoconnect, (char *)&portobj, sizeof(portobj)); - port_autoconnect(jii, portobj, fb); - } -} - -int cbox_jackio_get_midi_data(struct cbox_io_impl *impl, struct cbox_midi_buffer *destination) -{ - struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl; - if (!jii->enable_common_midi_input) - { - cbox_midi_buffer_clear(destination); - return 1; - } - - return copy_midi_data_to_buffer(jii->midi, jii->ioi.pio->io_env.buffer_size, destination); -} - -void cbox_jackio_destroy(struct cbox_io_impl *impl) -{ - struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl; - struct cbox_io *io = impl->pio; - if (jii->client) - { - if (jii->error_str) - { - g_free(jii->error_str); - jii->error_str = NULL; - } - else - { - for (uint32_t i = 0; i < io->io_env.input_count; i++) - jack_port_unregister(jii->client, jii->inputs[i]); - free(jii->inputs); - for (uint32_t i = 0; i < io->io_env.output_count; i++) - jack_port_unregister(jii->client, jii->outputs[i]); - free(jii->outputs); - if (jii->midi) - jack_port_unregister(jii->client, jii->midi); - } - if (jii->client_name) - { - free(jii->client_name); - jii->client_name = NULL; - } - cbox_io_destroy_all_midi_ports(io); - - jack_ringbuffer_free(jii->rb_autoconnect); - jack_client_close(jii->client); - } - free(jii); -} - -gboolean cbox_jackio_cycle(struct cbox_io_impl *impl, struct cbox_command_target *fb, GError **error) -{ - struct cbox_io *io = impl->pio; - struct cbox_io_callbacks *cb = io->cb; - cbox_io_close(io); - - // XXXKF use params structure some day - if (!cbox_io_init_jack(io, NULL, fb, error)) - return FALSE; - - cbox_io_start(io, cb, fb); - if (cb->on_reconnected) - (cb->on_reconnected)(cb->user_data); - return TRUE; -} - - - -/////////////////////////////////////////////////////////////////////////////// - -struct cbox_midi_input *cbox_jackio_create_midi_in(struct cbox_io_impl *impl, const char *name, GError **error) -{ - struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl; - jack_port_t *port = jack_port_register(jii->client, name, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); - if (!port) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot create input MIDI port '%s'", name); - return FALSE; - } - struct cbox_jack_midi_input *input = calloc(1, sizeof(struct cbox_jack_midi_input)); - input->hdr.name = g_strdup(name); - input->hdr.removing = FALSE; - input->port = port; - input->jii = jii; - cbox_uuid_generate(&input->hdr.uuid); - cbox_midi_buffer_init(&input->hdr.buffer); - - return (struct cbox_midi_input *)input; -} - -struct cbox_midi_output *cbox_jackio_create_midi_out(struct cbox_io_impl *impl, const char *name, GError **error) -{ - struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl; - jack_port_t *port = jack_port_register(jii->client, name, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); - if (!port) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot create output MIDI port '%s'", name); - return FALSE; - } - struct cbox_jack_midi_output *output = calloc(1, sizeof(struct cbox_jack_midi_output)); - output->hdr.name = g_strdup(name); - output->hdr.removing = FALSE; - output->port = port; - output->jii = jii; - cbox_uuid_generate(&output->hdr.uuid); - cbox_midi_buffer_init(&output->hdr.buffer); - cbox_midi_merger_init(&output->hdr.merger, &output->hdr.buffer); - - return (struct cbox_midi_output *)output; -} - -struct cbox_audio_output *cbox_jackio_create_audio_out(struct cbox_io_impl *impl, const char *name, GError **error) -{ - struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl; - jack_port_t *port = jack_port_register(jii->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); - if (!port) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot create output audio port '%s'", name); - return FALSE; - } - struct cbox_jack_audio_output *output = calloc(1, sizeof(struct cbox_jack_audio_output)); - output->hdr.name = g_strdup(name); - output->hdr.removing = FALSE; - output->hdr.users = 0; - output->port = port; - output->jii = jii; - cbox_uuid_generate(&output->hdr.uuid); - - return (struct cbox_audio_output *)output; -} - -void cbox_jack_port_set_autoconnect(gchar **spec_ptr, const gchar *autoconnect_spec, struct cbox_jack_io_impl *jii, const gchar *port_name, gboolean is_cbox_input, gboolean is_midi) -{ - if (*spec_ptr) - g_free(*spec_ptr); - *spec_ptr = autoconnect_spec && *autoconnect_spec ? g_strdup(autoconnect_spec) : NULL; - if (*spec_ptr) - { - gchar *cbox_port = g_strdup_printf("%s:%s", jii->client_name, port_name); - autoconnect_by_spec(jii->client, cbox_port, *spec_ptr, is_cbox_input, is_midi, NULL, NULL); - g_free(cbox_port); - } -} - -void cbox_jack_midi_input_set_autoconnect(struct cbox_jack_midi_input *jmi, const gchar *autoconnect_spec) -{ - cbox_jack_port_set_autoconnect(&jmi->autoconnect_spec, autoconnect_spec, jmi->jii, jmi->hdr.name, TRUE, TRUE); -} - -void cbox_jack_midi_output_set_autoconnect(struct cbox_jack_midi_output *jmo, const gchar *autoconnect_spec) -{ - cbox_jack_port_set_autoconnect(&jmo->autoconnect_spec, autoconnect_spec, jmo->jii, jmo->hdr.name, FALSE, TRUE); -} - -void cbox_jack_audio_output_set_autoconnect(struct cbox_jack_audio_output *jao, const gchar *autoconnect_spec) -{ - cbox_jack_port_set_autoconnect(&jao->autoconnect_spec, autoconnect_spec, jao->jii, jao->hdr.name, FALSE, FALSE); -} - -void cbox_jackio_destroy_midi_in(struct cbox_io_impl *ioi, struct cbox_midi_input *midiin) -{ - cbox_jack_midi_input_destroy((struct cbox_jack_midi_input *)midiin); -} - -void cbox_jackio_destroy_midi_out(struct cbox_io_impl *ioi, struct cbox_midi_output *midiout) -{ - cbox_jack_midi_output_destroy((struct cbox_jack_midi_output *)midiout); -} - -void cbox_jackio_destroy_audio_out(struct cbox_io_impl *ioi, struct cbox_audio_output *audioout) -{ - cbox_jack_audio_output_destroy((struct cbox_jack_audio_output *)audioout); -} - -#if JACK_HAS_RENAME -#define jack_port_rename_fn jack_port_rename -#else -#define jack_port_rename_fn(client, handle, name) jack_port_set_name(handle, name) -#endif - - -static gboolean cbox_jack_io_get_jack_uuid_from_name(struct cbox_jack_io_impl *jii, const char *name, jack_uuid_t *uuid, GError **error) -{ - jack_port_t *port = NULL; - port = jack_port_by_name(jii->client, name); - if (!port) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", name); - return FALSE; - } - assert(uuid); - jack_uuid_t temp_uuid = jack_port_uuid(port); - if (!temp_uuid) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "JACK uuid for port '%s' not found", name); - return FALSE; - } - *uuid = temp_uuid; - return TRUE; -} - -static gboolean cbox_jack_io_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)ct->user_data; - struct cbox_io *io = jii->ioi.pio; - gboolean handled = FALSE; - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - return cbox_execute_on(fb, NULL, "/client_type", "s", error, "JACK") && - cbox_execute_on(fb, NULL, "/client_name", "s", error, jii->client_name) && - cbox_execute_on(fb, NULL, "/external_tempo", "i", error, jii->external_tempo) && - cbox_io_process_cmd(io, fb, cmd, error, &handled); - } - else if (!strcmp(cmd->command, "/jack_transport_position") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - jack_position_t pos; - memset(&pos, 0, sizeof(pos)); - jack_transport_state_t state = jack_transport_query(jii->client, &pos); - if (!(cbox_execute_on(fb, NULL, "/state", "i", error, (int)state) && - cbox_execute_on(fb, NULL, "/is_master", "i", error, jii->master_detect_bits & 2 ? 1 : 0) && - cbox_execute_on(fb, NULL, "/unique_lo", "i", error, (int)pos.unique_1) && - cbox_execute_on(fb, NULL, "/unique_hi", "i", error, (int)(pos.unique_1 >> 32)) && - cbox_execute_on(fb, NULL, "/usecs_lo", "i", error, (int)pos.usecs) && - cbox_execute_on(fb, NULL, "/usecs_hi", "i", error, (int)(pos.usecs >> 32)) && - cbox_execute_on(fb, NULL, "/frame_rate", "i", error, (int)(pos.frame_rate)) && - cbox_execute_on(fb, NULL, "/frame", "i", error, (int)(pos.frame)))) - return FALSE; - if ((pos.valid & JackPositionBBT) && !( - cbox_execute_on(fb, NULL, "/bar", "i", error, (int)pos.bar) && - cbox_execute_on(fb, NULL, "/beat", "i", error, (int)pos.beat) && - cbox_execute_on(fb, NULL, "/tick", "i", error, (int)pos.tick) && - cbox_execute_on(fb, NULL, "/bar_start_tick", "f", error, (int)pos.bar_start_tick) && - cbox_execute_on(fb, NULL, "/beats_per_bar", "f", error, (double)pos.beats_per_bar) && - cbox_execute_on(fb, NULL, "/beat_type", "f", error, (double)pos.beat_type) && - cbox_execute_on(fb, NULL, "/ticks_per_beat", "f", error, (double)pos.ticks_per_beat) && - cbox_execute_on(fb, NULL, "/beats_per_minute", "f", error, (double)pos.beats_per_minute))) - return FALSE; - if ((pos.valid & JackBBTFrameOffset) && !( - cbox_execute_on(fb, NULL, "/bbt_frame_offset", "i", error, (int)pos.bbt_offset))) - return FALSE; - return TRUE; - } - else if (!strcmp(cmd->command, "/transport_mode") && !strcmp(cmd->arg_types, "i")) - { - return cbox_jackio_set_master_mode(jii, CBOX_ARG_I(cmd, 0), error); - } - else if (!strcmp(cmd->command, "/jack_transport_locate") && !strcmp(cmd->arg_types, "i")) - { - jack_transport_locate(jii->client, (uint32_t)CBOX_ARG_I(cmd, 0)); - return TRUE; - } - else if (!strcmp(cmd->command, "/rename_midi_port") && !strcmp(cmd->arg_types, "ss")) - { - const char *uuidstr = CBOX_ARG_S(cmd, 0); - const char *new_name = CBOX_ARG_S(cmd, 1); - struct cbox_uuid uuid; - if (!cbox_uuid_fromstring(&uuid, uuidstr, error)) - return FALSE; - struct cbox_midi_input *midiin = cbox_io_get_midi_input(io, NULL, &uuid); - struct cbox_midi_output *midiout = cbox_io_get_midi_output(io, NULL, &uuid); - if (!midiout && !midiin) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr); - return FALSE; - } - jack_port_t *port = midiout ? ((struct cbox_jack_midi_output *)midiout)->port - : ((struct cbox_jack_midi_input *)midiin)->port; - char **pname = midiout ? &midiout->name : &midiin->name; - if (0 != jack_port_rename_fn(jii->client, port, new_name)) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot set port name to '%s'", new_name); - return FALSE; - } - g_free(*pname); - *pname = g_strdup(new_name); - return TRUE; - } - else if (!strcmp(cmd->command, "/rename_audio_port") && !strcmp(cmd->arg_types, "ss")) - { - const char *uuidstr = CBOX_ARG_S(cmd, 0); - const char *new_name = CBOX_ARG_S(cmd, 1); - struct cbox_uuid uuid; - if (!cbox_uuid_fromstring(&uuid, uuidstr, error)) - return FALSE; - struct cbox_audio_output *audioout = cbox_io_get_audio_output(io, NULL, &uuid); - if (!audioout) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr); - return FALSE; - } - if (0 != jack_port_rename_fn(jii->client, ((struct cbox_jack_audio_output *)audioout)->port, new_name)) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot set port name to '%s'", new_name); - return FALSE; - } - g_free(audioout->name); - audioout->name = g_strdup(new_name); - return TRUE; - } - else if (!strcmp(cmd->command, "/autoconnect") && !strcmp(cmd->arg_types, "ss")) - { - const char *uuidstr = CBOX_ARG_S(cmd, 0); - const char *spec = CBOX_ARG_S(cmd, 1); - struct cbox_uuid uuid; - if (!cbox_uuid_fromstring(&uuid, uuidstr, error)) - return FALSE; - struct cbox_midi_output *midiout = cbox_io_get_midi_output(io, NULL, &uuid); - if (midiout) - { - cbox_jack_midi_output_set_autoconnect((struct cbox_jack_midi_output *)midiout, spec); - return TRUE; - } - struct cbox_midi_input *midiin = cbox_io_get_midi_input(io, NULL, &uuid); - if (midiin) - { - cbox_jack_midi_input_set_autoconnect((struct cbox_jack_midi_input *)midiin, spec); - return TRUE; - } - struct cbox_audio_output *audioout = cbox_io_get_audio_output(io, NULL, &uuid); - if (audioout) - { - cbox_jack_audio_output_set_autoconnect((struct cbox_jack_audio_output *)audioout, spec); - return TRUE; - } - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr); - return FALSE; - } - else if (!strcmp(cmd->command, "/disconnect_audio_output") && !strcmp(cmd->arg_types, "s")) - { - const char *uuidstr = CBOX_ARG_S(cmd, 0); - struct cbox_uuid uuid; - if (!cbox_uuid_fromstring(&uuid, uuidstr, error)) - return FALSE; - struct cbox_audio_output *audioout = cbox_io_get_audio_output(io, NULL, &uuid); - if (!audioout) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr); - return FALSE; - } - jack_port_disconnect(jii->client, ((struct cbox_jack_audio_output *)audioout)->port); - return TRUE; - } - else if (!strncmp(cmd->command, "/disconnect_midi_", 17) && !strcmp(cmd->arg_types, "s")) - { - bool is_both = !strcmp(cmd->command + 17, "port"); - bool is_in = is_both || !strcmp(cmd->command + 17, "input"); - bool is_out = is_both || !strcmp(cmd->command + 17, "output"); - if (is_in || is_out) { - const char *uuidstr = CBOX_ARG_S(cmd, 0); - struct cbox_uuid uuid; - if (!cbox_uuid_fromstring(&uuid, uuidstr, error)) - return FALSE; - struct cbox_midi_input *midiin = is_in ? cbox_io_get_midi_input(io, NULL, &uuid) : NULL; - struct cbox_midi_output *midiout = is_out ? cbox_io_get_midi_output(io, NULL, &uuid) : NULL; - if (!midiout && !midiin) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr); - return FALSE; - } - jack_port_t *port = midiout ? ((struct cbox_jack_midi_output *)midiout)->port - : ((struct cbox_jack_midi_input *)midiin)->port; - jack_port_disconnect(jii->client, port); - return TRUE; - } - } - - if (!strcmp(cmd->command, "/port_connect") && !strcmp(cmd->arg_types, "ss")) - { - const char *port_from = CBOX_ARG_S(cmd, 0); - const char *port_to = CBOX_ARG_S(cmd, 1); - int res = jack_connect(jii->client, port_from, port_to); - if (res == EEXIST) - res = 0; - if (res) - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot connect port '%s' to '%s'", port_from, port_to); - return res == 0; - } - else if (!strcmp(cmd->command, "/port_disconnect") && !strcmp(cmd->arg_types, "ss")) - { - const char *port_from = CBOX_ARG_S(cmd, 0); - const char *port_to = CBOX_ARG_S(cmd, 1); - int res = jack_disconnect(jii->client, port_from, port_to); - if (res) - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot disconnect port '%s' from '%s'", port_from, port_to); - return res == 0; - } - else if (!strcmp(cmd->command, "/get_connected_ports") && !strcmp(cmd->arg_types, "s")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - const char *name = CBOX_ARG_S(cmd, 0); - jack_port_t *port = NULL; - if (!strchr(name, ':')) { - // try UUID - struct cbox_uuid uuid; - if (!cbox_uuid_fromstring(&uuid, name, error)) - return FALSE; - struct cbox_midi_output *midiout = cbox_io_get_midi_output(io, NULL, &uuid); - if (midiout) - port = ((struct cbox_jack_midi_output *)midiout)->port; - else { - struct cbox_midi_input *midiin = cbox_io_get_midi_input(io, NULL, &uuid); - if (midiin) - port = ((struct cbox_jack_midi_input *)midiin)->port; - } - } - else - port = jack_port_by_name(jii->client, name); - if (!port) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", name); - return FALSE; - } - const char** ports = jack_port_get_all_connections(jii->client, port); - for (int i = 0; ports && ports[i]; i++) - { - if (!cbox_execute_on(fb, NULL, "/port", "s", error, ports[i])) - return FALSE; - } - jack_free(ports); - return TRUE; - } - else if (!strcmp(cmd->command, "/get_ports") && !strcmp(cmd->arg_types, "ssi")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - const char *mask = CBOX_ARG_S(cmd, 0); - const char *type = CBOX_ARG_S(cmd, 1); - uint32_t flags = CBOX_ARG_I(cmd, 2); - const char** ports = jack_get_ports(jii->client, mask, type, flags); - for (int i = 0; ports && ports[i]; i++) - { - if (!cbox_execute_on(fb, NULL, "/port", "s", error, ports[i])) - return FALSE; - } - jack_free(ports); - return TRUE; - } - else if (!strcmp(cmd->command, "/external_tempo") && !strcmp(cmd->arg_types, "i")) - { - jii->external_tempo = CBOX_ARG_I(cmd, 0); - return TRUE; - } - - //Metadata - - else if (!strcmp(cmd->command, "/set_property") && !strcmp(cmd->arg_types, "ssss")) - //parameters: "client:port", key, value, type according to jack_property_t (empty or NULL for string) - { - const char *name = CBOX_ARG_S(cmd, 0); - const char *key = CBOX_ARG_S(cmd, 1); - const char *value = CBOX_ARG_S(cmd, 2); - const char *type = CBOX_ARG_S(cmd, 3); - - jack_uuid_t subject; - if (!cbox_jack_io_get_jack_uuid_from_name(jii, name, &subject, error)) //error message set inside - return FALSE; - - if (jack_set_property(jii->client, subject, key, value, type)) // 0 on success - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Set property key:'%s' value: '%s' to port '%s' was not successful", key, value, name); - return FALSE; - } - return TRUE; - } - else if (!strcmp(cmd->command, "/client_set_property") && !strcmp(cmd->arg_types, "sss")) - /*same as set_property above, but works directly on our own jack client. - parameters: key, value, type according to jack_property_t (empty or NULL for string) - */ - { - const char *key = CBOX_ARG_S(cmd, 0); - const char *value = CBOX_ARG_S(cmd, 1); - const char *type = CBOX_ARG_S(cmd, 2); - - char* subject; - subject = jack_get_uuid_for_client_name(jii->client, jii->client_name); //lookup our own client - if (!subject) { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot get string UUID for our jack client."); - return FALSE; - } - - jack_uuid_t j_client_uuid_t; - if (jack_uuid_parse(subject, &j_client_uuid_t)) { //from jack/uuid.h // 0 on success - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "jack_uuid_parse() couldn't parse our string client UUID %s as numerical jack_uuid_t %ld", subject, j_client_uuid_t); - return FALSE; - } - - if (jack_set_property(jii->client, j_client_uuid_t, key, value, type)) // 0 on success - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Set client property key:'%s' value: '%s' was not successful", key, value); - return FALSE; - } - return TRUE; - } - else if (!strcmp(cmd->command, "/get_property") && !strcmp(cmd->arg_types, "ss")) - //parameters: "client:port", key - //returns python key, value and type as strings - { - const char *name = CBOX_ARG_S(cmd, 0); - const char *key = CBOX_ARG_S(cmd, 1); - - jack_uuid_t subject; - if (!cbox_jack_io_get_jack_uuid_from_name(jii, name, &subject, error)) //error message set inside - return FALSE; - - char* value = NULL; - char* type = NULL; - - if (jack_get_property(subject, key, &value, &type)) // 0 on success, -1 if the subject has no key property. - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' does not have key '%s'", name, key); - return FALSE; - } - - char* returntype; //We need to call jack_free on type in any case so it can't be our own data. - if (type == NULL) - returntype = ""; - else - returntype = type; - - if (!cbox_execute_on(fb, NULL, "/value", "ss", error, value, returntype)) //send return values to Python. - return FALSE; - - jack_free(value); - jack_free(type); - return TRUE; - } - else if (!strcmp(cmd->command, "/get_properties") && !strcmp(cmd->arg_types, "s")) - //parameters: "client:port" - { - const char *name = CBOX_ARG_S(cmd, 0); - - jack_uuid_t subject; - if (!cbox_jack_io_get_jack_uuid_from_name(jii, name, &subject, error)) //error message set inside - return FALSE; - - jack_description_t desc; - if (!jack_get_properties(subject, &desc)) // 0 on success, -1 if no subject with any properties exists. - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' with uuid '%lli' does not have any properties", name, (long long)subject); - return FALSE; - } - - const char *returntype; - for (uint32_t i = 0; icommand, "/get_all_properties") && !strcmp(cmd->arg_types, "")) - { - jack_description_t *descs; - int counter; - counter = jack_get_all_properties(&descs); - const char *returntype; - for (int j = 0; j < counter; j++) - { - jack_description_t *one_desc = &descs[j]; - for (uint32_t i = 0; i < one_desc->property_cnt ; i++) - { - if (one_desc->properties[i].type == NULL) - returntype = ""; - else - returntype = one_desc->properties[i].type; - - /* - index = jack_uuid_to_index(one_desc->subject) - portid = jack_port_by_id(jii->client, index); - portname = jack_port_name(port); - */ - if (!cbox_execute_on(fb, NULL, "/all_properties", "ssss", - error, - jack_port_name(jack_port_by_id(jii->client, jack_uuid_to_index(one_desc->subject))), - one_desc->properties[i].key, - one_desc->properties[i].data, - returntype)) - return FALSE; - } - jack_free_description(one_desc, 0); //if non-zero one_desc will also be passed to free() - } - jack_free(descs); - return TRUE; - } - - - else if (!strcmp(cmd->command, "/remove_property") && !strcmp(cmd->arg_types, "ss")) - { - const char *name = CBOX_ARG_S(cmd, 0); - const char *key = CBOX_ARG_S(cmd, 1); - - jack_uuid_t subject; - if (!cbox_jack_io_get_jack_uuid_from_name(jii, name, &subject, error)) //error message set inside - return FALSE; - - if (jack_remove_property(jii->client, subject, key)) // 0 on success, -1 otherwise - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Could not remove port '%s' key '%s'", name, key); - return FALSE; - } - return TRUE; - } - - else if (!strcmp(cmd->command, "/remove_properties") && !strcmp(cmd->arg_types, "s")) - { - const char *name = CBOX_ARG_S(cmd, 0); - - jack_uuid_t subject; - if (!cbox_jack_io_get_jack_uuid_from_name(jii, name, &subject, error)) //error message set inside - return FALSE; - - if (jack_remove_properties(jii->client, subject) == -1) // number of removed properties returned, -1 on error - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Could not remove properties of port '%s'", name); - return FALSE; - } - return TRUE; - } - - - else if (!strcmp(cmd->command, "/remove_all_properties") && !strcmp(cmd->arg_types, "")) - { - if (jack_remove_all_properties(jii->client)) // 0 on success, -1 otherwise - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Remove all JACK properties was not successful"); - return FALSE; - } - return TRUE; - } - - else - { - gboolean result = cbox_io_process_cmd(io, fb, cmd, error, &handled); - if (!handled) - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown combination of target path and argument: '%s', '%s'", cmd->command, cmd->arg_types); - return result; - } -} - -/////////////////////////////////////////////////////////////////////////////// - -static void cbox_jackio_control_transport(struct cbox_io_impl *impl, gboolean roll, uint32_t pos) -{ - struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl; - - if (jii->debug_transport) - g_message("JACK transport: control(op=%s, pos=%d)\n", roll ? "roll" : "stop", (int)pos); - - jack_transport_state_t state = jack_transport_query(jii->client, NULL); - if (roll && pos != (uint32_t)-1) - jack_transport_locate(jii->client, pos); - if (roll && state == JackTransportStopped) - jack_transport_start(jii->client); - if (!roll && state != JackTransportStopped) - jack_transport_stop(jii->client); - if (!roll && pos != (uint32_t)-1) - jack_transport_locate(jii->client, pos); -} - -static gboolean cbox_jackio_get_sync_completed(struct cbox_io_impl *impl) -{ - struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl; - return jack_transport_query(jii->client, NULL) != JackTransportStarting; -} - -/////////////////////////////////////////////////////////////////////////////// - -static void cbox_jackio_update_midi_in_routing(struct cbox_io_impl *impl) -{ - // XXXKF slow and wasteful, but that's okay for now - for (GSList *p = impl->pio->midi_inputs; p; p = g_slist_next(p)) - { - struct cbox_midi_input *midiin = p->data; - for (GSList *q = impl->pio->midi_outputs; q; q = g_slist_next(q)) - { - struct cbox_midi_output *midiout = q->data; - - bool add = midiin->output_set && !midiout->removing && cbox_uuid_equal(&midiout->uuid, &midiin->output); - if (add) - cbox_midi_merger_connect(&midiout->merger, &midiin->buffer, app.rt, NULL); - else - cbox_midi_merger_disconnect(&midiout->merger, &midiin->buffer, app.rt); - } - } -} - -/////////////////////////////////////////////////////////////////////////////// - -gboolean cbox_io_init_jack(struct cbox_io *io, struct cbox_open_params *const params, struct cbox_command_target *fb, GError **error) -{ - const char *client_name = cbox_config_get_string_with_default("io", "client_name", "cbox"); - - jack_client_t *client = NULL; - jack_status_t status = 0; - client = jack_client_open(client_name, JackNoStartServer, &status); - if (client == NULL) - { - if (!cbox_hwcfg_setup_jack()) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot set up JACK server configuration based on current hardware"); - return FALSE; - } - - status = 0; - client = jack_client_open(client_name, 0, &status); - } - if (client == NULL) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot create JACK instance"); - return FALSE; - } - - // XXXKF would use a callback instead - io->io_env.buffer_size = jack_get_buffer_size(client); - io->cb = NULL; - io->io_env.input_count = cbox_config_get_int("io", "inputs", 0); - io->input_buffers = malloc(sizeof(float *) * io->io_env.input_count); - io->io_env.output_count = cbox_config_get_int("io", "outputs", 2); - io->output_buffers = malloc(sizeof(float *) * io->io_env.output_count); - - struct cbox_jack_io_impl *jii = malloc(sizeof(struct cbox_jack_io_impl)); - io->impl = &jii->ioi; - jii->enable_common_midi_input = cbox_config_get_int("io", "enable_common_midi_input", 1); - jii->debug_transport = cbox_config_get_int("debug", "jack_transport", 0); - jii->last_transport_state = JackTransportStopped; - jii->external_tempo = FALSE; - - cbox_command_target_init(&io->cmd_target, cbox_jack_io_process_cmd, jii); - jii->ioi.pio = io; - jii->ioi.getsampleratefunc = cbox_jackio_get_sample_rate; - jii->ioi.startfunc = cbox_jackio_start; - jii->ioi.stopfunc = cbox_jackio_stop; - jii->ioi.getstatusfunc = cbox_jackio_get_status; - jii->ioi.pollfunc = cbox_jackio_poll_ports; - jii->ioi.cyclefunc = cbox_jackio_cycle; - jii->ioi.getmidifunc = cbox_jackio_get_midi_data; - jii->ioi.createmidiinfunc = cbox_jackio_create_midi_in; - jii->ioi.destroymidiinfunc = cbox_jackio_destroy_midi_in; - jii->ioi.createmidioutfunc = cbox_jackio_create_midi_out; - jii->ioi.destroymidioutfunc = cbox_jackio_destroy_midi_out; - jii->ioi.updatemidiinroutingfunc = cbox_jackio_update_midi_in_routing; - jii->ioi.createaudiooutfunc = cbox_jackio_create_audio_out; - jii->ioi.destroyaudiooutfunc = cbox_jackio_destroy_audio_out; - jii->ioi.controltransportfunc = cbox_jackio_control_transport; - jii->ioi.getsynccompletedfunc = cbox_jackio_get_sync_completed; - jii->ioi.destroyfunc = cbox_jackio_destroy; - - jii->client_name = g_strdup(jack_get_client_name(client)); - jii->client = client; - jii->rb_autoconnect = jack_ringbuffer_create(sizeof(jack_port_t *) * 128); - jii->error_str = NULL; - io->io_env.srate = jack_get_sample_rate(client); - - jii->inputs = malloc(sizeof(jack_port_t *) * io->io_env.input_count); - jii->outputs = malloc(sizeof(jack_port_t *) * io->io_env.output_count); - for (uint32_t i = 0; i < io->io_env.input_count; i++) - jii->inputs[i] = NULL; - for (uint32_t i = 0; i < io->io_env.output_count; i++) - jii->outputs[i] = NULL; - for (uint32_t i = 0; i < io->io_env.input_count; i++) - { - gchar *name = g_strdup_printf("in_%d", 1 + i); - jii->inputs[i] = jack_port_register(jii->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); - if (!jii->inputs[i]) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot create input port %d (%s)", i, name); - g_free(name); - goto cleanup; - } - g_free(name); - } - for (uint32_t i = 0; i < io->io_env.output_count; i++) - { - gchar *name = g_strdup_printf("out_%d", 1 + i); - jii->outputs[i] = jack_port_register(jii->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); - if (!jii->outputs[i]) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot create output port %d (%s)", i, name); - g_free(name); - goto cleanup; - } - g_free(name); - } - if (jii->enable_common_midi_input) - { - jii->midi = jack_port_register(jii->client, "midi", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); - if (!jii->midi) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot create MIDI port"); - return FALSE; - } - } - else - jii->midi = NULL; - - if (fb) - cbox_execute_on(fb, NULL, "/io/jack_client_name", "s", NULL, jii->client_name); - - cbox_io_poll_ports(io, fb); - - return TRUE; - -cleanup: - if (jii->inputs) - { - for (uint32_t i = 0; i < io->io_env.input_count; i++) - free(jii->inputs[i]); - free(jii->inputs); - } - if (jii->outputs) - { - for (uint32_t i = 0; i < io->io_env.output_count; i++) - free(jii->outputs[i]); - free(jii->outputs); - } - cbox_io_destroy_all_midi_ports(io); - if (jii->client_name) - free(jii->client_name); - jack_client_close(jii->client); - free(jii); - io->impl = NULL; - return FALSE; -}; - -#endif diff --git a/template/calfbox/layer.c b/template/calfbox/layer.c deleted file mode 100644 index 304e4d6..0000000 --- a/template/calfbox/layer.c +++ /dev/null @@ -1,293 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config-api.h" -#include "errors.h" -#include "instr.h" -#include "layer.h" -#include "midi.h" -#include "module.h" -#include "rt.h" -#include "scene.h" -#include - -gboolean cbox_layer_load(struct cbox_layer *layer, const char *name, GError **error) -{ - const char *cv = NULL; - struct cbox_instrument *instr = NULL; - gchar *section = g_strdup_printf("layer:%s", name); - - if (!cbox_config_has_section(section)) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Missing section for layer %s", name); - goto error; - } - - cv = cbox_config_get_string(section, "instrument"); - if (cv) - { - instr = cbox_scene_get_instrument_by_name(layer->scene, cv, TRUE, error); - if (!instr) - { - cbox_force_error(error); - g_prefix_error(error, "Cannot get instrument %s for layer %s: ", cv, name); - goto error; - } - } - - layer->enabled = cbox_config_get_int(section, "enabled", TRUE); - layer->low_note = 0; - layer->high_note = 127; - - cv = cbox_config_get_string(section, "low_note"); - if (cv) - layer->low_note = note_from_string(cv); - - cv = cbox_config_get_string(section, "high_note"); - if (cv) - layer->high_note = note_from_string(cv); - - layer->transpose = cbox_config_get_int(section, "transpose", 0); - layer->fixed_note = cbox_config_get_int(section, "fixed_note", -1); - layer->in_channel = cbox_config_get_int(section, "in_channel", 0) - 1; - layer->out_channel = cbox_config_get_int(section, "out_channel", 0) - 1; - layer->disable_aftertouch = !cbox_config_get_int(section, "aftertouch", TRUE); - layer->invert_sustain = cbox_config_get_int(section, "invert_sustain", FALSE); - layer->consume = cbox_config_get_int(section, "consume", FALSE); - layer->ignore_scene_transpose = cbox_config_get_int(section, "ignore_scene_transpose", FALSE); - layer->ignore_program_changes = cbox_config_get_int(section, "ignore_program_changes", FALSE); - layer->external_output_set = FALSE; - g_free(section); - - cbox_layer_set_instrument(layer, instr); - - return 1; - -error: - if (instr) - cbox_instrument_destroy_if_unused(instr); - - g_free(section); - return 0; -} - -void cbox_layer_set_instrument(struct cbox_layer *layer, struct cbox_instrument *instrument) -{ - if (layer->instrument) - { - layer->instrument->refcount--; - cbox_instrument_destroy_if_unused(layer->instrument); - layer->instrument = NULL; - } - layer->instrument = instrument; - if (layer->instrument) - layer->instrument->refcount++; -} - -static gboolean cbox_layer_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error); - -static void cbox_layer_destroyfunc(struct cbox_objhdr *objhdr) -{ - struct cbox_layer *layer = CBOX_H2O(objhdr); - if (layer->instrument && !--(layer->instrument->refcount)) - { - if (layer->instrument->scene) - cbox_scene_remove_instrument(layer->instrument->scene, layer->instrument); - - cbox_instrument_destroy_if_unused(layer->instrument); - } - if (layer->external_merger) { - cbox_midi_merger_disconnect(layer->external_merger, &layer->output_buffer, layer->scene->rt); - } - free(layer); -} - -gboolean cbox_layer_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct cbox_layer *layer = ct->user_data; - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - if (!(cbox_execute_on(fb, NULL, "/enable", "i", error, (int)layer->enabled) && - (layer->instrument - ? cbox_execute_on(fb, NULL, "/instrument_name", "s", error, layer->instrument->module->instance_name) && - cbox_execute_on(fb, NULL, "/instrument_uuid", "o", error, layer->instrument) - : (layer->external_output_set - ? cbox_uuid_report_as(&layer->external_output, "/external_output", fb, error) - : TRUE)) && - cbox_execute_on(fb, NULL, "/consume", "i", error, (int)layer->consume) && - cbox_execute_on(fb, NULL, "/ignore_scene_transpose", "i", error, (int)layer->ignore_scene_transpose) && - cbox_execute_on(fb, NULL, "/ignore_program_changes", "i", error, (int)layer->ignore_program_changes) && - cbox_execute_on(fb, NULL, "/disable_aftertouch", "i", error, (int)layer->disable_aftertouch) && - cbox_execute_on(fb, NULL, "/transpose", "i", error, (int)layer->transpose) && - cbox_execute_on(fb, NULL, "/fixed_note", "i", error, (int)layer->fixed_note) && - cbox_execute_on(fb, NULL, "/low_note", "i", error, (int)layer->low_note) && - cbox_execute_on(fb, NULL, "/high_note", "i", error, (int)layer->high_note) && - cbox_execute_on(fb, NULL, "/in_channel", "i", error, layer->in_channel + 1) && - cbox_execute_on(fb, NULL, "/out_channel", "i", error, layer->out_channel + 1) && - CBOX_OBJECT_DEFAULT_STATUS(layer, fb, error))) - return FALSE; - return TRUE; - } - else if (!strcmp(cmd->command, "/enable") && !strcmp(cmd->arg_types, "i")) - { - layer->enabled = 0 != CBOX_ARG_I(cmd, 0); - return TRUE; - } - else if (!strcmp(cmd->command, "/consume") && !strcmp(cmd->arg_types, "i")) - { - layer->consume = 0 != CBOX_ARG_I(cmd, 0); - return TRUE; - } - else if (!strcmp(cmd->command, "/ignore_scene_transpose") && !strcmp(cmd->arg_types, "i")) - { - layer->ignore_scene_transpose = 0 != CBOX_ARG_I(cmd, 0); - return TRUE; - } - else if (!strcmp(cmd->command, "/ignore_program_changes") && !strcmp(cmd->arg_types, "i")) - { - layer->ignore_program_changes = 0 != CBOX_ARG_I(cmd, 0); - return TRUE; - } - else if (!strcmp(cmd->command, "/disable_aftertouch") && !strcmp(cmd->arg_types, "i")) - { - layer->disable_aftertouch = 0 != CBOX_ARG_I(cmd, 0); - return TRUE; - } - else if (!strcmp(cmd->command, "/transpose") && !strcmp(cmd->arg_types, "i")) - { - layer->transpose = CBOX_ARG_I(cmd, 0); - return TRUE; - } - else if (!strcmp(cmd->command, "/fixed_note") && !strcmp(cmd->arg_types, "i")) - { - layer->fixed_note = CBOX_ARG_I(cmd, 0); - return TRUE; - } - else if (!strcmp(cmd->command, "/low_note") && !strcmp(cmd->arg_types, "i")) - { - layer->low_note = CBOX_ARG_I(cmd, 0); - return TRUE; - } - else if (!strcmp(cmd->command, "/high_note") && !strcmp(cmd->arg_types, "i")) - { - layer->high_note = CBOX_ARG_I(cmd, 0); - return TRUE; - } - else if (!strcmp(cmd->command, "/in_channel") && !strcmp(cmd->arg_types, "i")) - { - layer->in_channel = CBOX_ARG_I(cmd, 0) - 1; - return TRUE; - } - else if (!strcmp(cmd->command, "/out_channel") && !strcmp(cmd->arg_types, "i")) - { - layer->out_channel = CBOX_ARG_I(cmd, 0) - 1; - return TRUE; - } - else if (!strcmp(cmd->command, "/external_output") && !strcmp(cmd->arg_types, "s")) - { - if (*CBOX_ARG_S(cmd, 0)) - { - if (cbox_uuid_fromstring(&layer->external_output, CBOX_ARG_S(cmd, 0), error)) { - layer->external_output_set = TRUE; - } - } - else { - layer->external_output_set = FALSE; - } - cbox_scene_update_connected_outputs(layer->scene); - return TRUE; - } - else // otherwise, treat just like an command on normal (non-aux) output - return cbox_object_default_process_cmd(ct, fb, cmd, error); -} - -CBOX_CLASS_DEFINITION_ROOT(cbox_layer) - -struct cbox_layer *cbox_layer_new(struct cbox_scene *scene) -{ - struct cbox_document *doc = CBOX_GET_DOCUMENT(scene); - struct cbox_layer *l = malloc(sizeof(struct cbox_layer)); - - CBOX_OBJECT_HEADER_INIT(l, cbox_layer, doc); - cbox_command_target_init(&l->cmd_target, cbox_layer_process_cmd, l); - l->enabled = TRUE; - l->instrument = NULL; - l->low_note = 0; - l->high_note = 127; - - l->transpose = 0; - l->fixed_note = -1; - l->in_channel = -1; - l->out_channel = -1; - l->disable_aftertouch = FALSE; - l->invert_sustain = FALSE; - l->consume = FALSE; - l->ignore_scene_transpose = FALSE; - l->ignore_program_changes = FALSE; - l->scene = scene; - cbox_uuid_clear(&l->external_output); - l->external_output_set = FALSE; - l->external_merger = NULL; - CBOX_OBJECT_REGISTER(l); - return l; -} - -struct cbox_layer *cbox_layer_new_with_instrument(struct cbox_scene *scene, const char *instrument_name, GError **error) -{ - struct cbox_layer *layer = cbox_layer_new(scene); - struct cbox_instrument *instr = NULL; - if (!layer) goto error; - instr = cbox_scene_get_instrument_by_name(scene, instrument_name, TRUE, error); - if (!instr) - { - cbox_force_error(error); - g_prefix_error(error, "Cannot get instrument %s for new layer: ", instrument_name); - CBOX_DELETE(layer); - return NULL; - } - cbox_layer_set_instrument(layer, instr); - return layer; - -error: - CBOX_DELETE(layer); - if (instr) - cbox_instrument_destroy_if_unused(instr); - return NULL; -} - -struct cbox_layer *cbox_layer_new_from_config(struct cbox_scene *scene, const char *layer_name, GError **error) -{ - struct cbox_layer *layer = cbox_layer_new(scene); - if (!layer) - goto error; - - layer->scene = scene; - if (!cbox_layer_load(layer, layer_name, error)) - goto error; - - return layer; - -error: - CBOX_DELETE(layer); - return NULL; -} - - diff --git a/template/calfbox/layer.h b/template/calfbox/layer.h deleted file mode 100644 index 3f4c9e8..0000000 --- a/template/calfbox/layer.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_LAYER_H -#define CBOX_LAYER_H - -#include "dom.h" -#include "midi.h" -#include -#include - -struct cbox_module; -struct cbox_rt; - -CBOX_EXTERN_CLASS(cbox_layer) - -struct cbox_layer -{ - CBOX_OBJECT_HEADER() - struct cbox_scene *scene; - struct cbox_instrument *instrument; - struct cbox_command_target cmd_target; - gboolean enabled; - int8_t in_channel; // -1 for Omni - int8_t out_channel; // -1 for Omni - uint8_t low_note; - uint8_t high_note; - int8_t transpose; - int8_t fixed_note; - gboolean disable_aftertouch; - gboolean invert_sustain; - gboolean consume; - gboolean ignore_scene_transpose; - gboolean ignore_program_changes; - gboolean external_output_set; - struct cbox_uuid external_output; - struct cbox_midi_buffer output_buffer; - struct cbox_midi_merger *external_merger; -}; - -extern struct cbox_layer *cbox_layer_new(struct cbox_scene *scene); -extern struct cbox_layer *cbox_layer_new_with_instrument(struct cbox_scene *scene, const char *instrument_name, GError **error); -extern struct cbox_layer *cbox_layer_new_from_config(struct cbox_scene *scene, const char *instrument_name, GError **error); -extern gboolean cbox_layer_load(struct cbox_layer *layer, const char *name, GError **error); -extern void cbox_layer_set_instrument(struct cbox_layer *layer, struct cbox_instrument *instrument); -extern void cbox_layer_destroy(struct cbox_layer *layer); - -#endif diff --git a/template/calfbox/limiter.c b/template/calfbox/limiter.c deleted file mode 100644 index 401dfc5..0000000 --- a/template/calfbox/limiter.c +++ /dev/null @@ -1,154 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config.h" -#include "config-api.h" -#include "dspmath.h" -#include "module.h" -#include "onepole-float.h" -#include -#include -#include -#include -#include -#include -#include - -#define MODULE_PARAMS limiter_params - -struct limiter_params -{ - float threshold; - float attack; - float release; -}; - -struct limiter_module -{ - struct cbox_module module; - - struct limiter_params *params, *old_params; - - double cur_gain; - double atk_coeff, rel_coeff; -}; - -gboolean limiter_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct limiter_module *m = (struct limiter_module *)ct->user_data; - - EFFECT_PARAM("/threshold", "f", threshold, double, , -100, 12) else - EFFECT_PARAM("/attack", "f", attack, double, , 1, 1000) else - EFFECT_PARAM("/release", "f", release, double, , 1, 5000) else - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - return - cbox_execute_on(fb, NULL, "/threshold", "f", error, m->params->threshold) && - cbox_execute_on(fb, NULL, "/attack", "f", error, m->params->attack) && - cbox_execute_on(fb, NULL, "/release", "f", error, m->params->release) && - CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error); - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); - return TRUE; -} - -void limiter_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) -{ - // struct limiter_module *m = module->user_data; -} - -void limiter_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) -{ - struct limiter_module *m = module->user_data; - struct limiter_params *mp = m->params; - - if (m->params != m->old_params) - { - m->atk_coeff = 1 - exp(-1000.0 / (mp->attack * m->module.srate)); - m->rel_coeff = 1 - exp(-1000.0 / (mp->release * m->module.srate)); - // update calculated values - } - const double minval = pow(2.0, -110.0); - for (int i = 0; i < CBOX_BLOCK_SIZE; ++i) - { - float left = inputs[0][i], right = inputs[1][i]; - - float level = fabs(left); - if (fabs(right) > level) - level = fabs(right); - if (level < minval) - level = minval; - level = log(level); - - float gain = 0.0; - - if (level > mp->threshold * 0.11552) - gain = mp->threshold * 0.11552 - level; - - // instantaneous attack + slow release - if (gain >= m->cur_gain) - m->cur_gain += m->rel_coeff * (gain - m->cur_gain); - else - m->cur_gain += m->atk_coeff * (gain - m->cur_gain); - - gain = exp(m->cur_gain); - //if (gain < 1) - // printf("level = %f gain = %f\n", m->cur_level, gain); - - outputs[0][i] = left * gain; - outputs[1][i] = right * gain; - } -} - -MODULE_SIMPLE_DESTROY_FUNCTION(limiter) - -MODULE_CREATE_FUNCTION(limiter) -{ - static int inited = 0; - if (!inited) - { - inited = 1; - } - - struct limiter_module *m = malloc(sizeof(struct limiter_module)); - CALL_MODULE_INIT(m, 2, 2, limiter); - m->module.process_event = limiter_process_event; - m->module.process_block = limiter_process_block; - struct limiter_params *p = malloc(sizeof(struct limiter_params)); - p->threshold = -1; - p->attack = 10.f; - p->release = 2000.f; - m->params = p; - m->old_params = NULL; - m->cur_gain = 0.f; - - return &m->module; -} - - -struct cbox_module_keyrange_metadata limiter_keyranges[] = { -}; - -struct cbox_module_livecontroller_metadata limiter_controllers[] = { -}; - -DEFINE_MODULE(limiter, 0, 2) - diff --git a/template/calfbox/main.c b/template/calfbox/main.c deleted file mode 100644 index ed9ad48..0000000 --- a/template/calfbox/main.c +++ /dev/null @@ -1,450 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "app.h" -#include "config.h" -#include "config-api.h" -#include "dom.h" -#include "engine.h" -#include "instr.h" -#include "io.h" -#include "layer.h" -#include "menu.h" -#include "menuitem.h" -#include "midi.h" -#include "module.h" -#include "pattern.h" -#include "rt.h" -#include "scene.h" -#include "scripting.h" -#include "song.h" -#include "tarfile.h" -#include "ui.h" -#include "wavebank.h" - -#include -#include -#include -#include -#include -#include -#if USE_NCURSES -#include -#endif -#include -#include -#include -#include - -static const char *short_options = "i:c:" -#if USE_PYTHON -"r:" -#endif -"e:s:t:b:d:D:N:o:nmhpP"; - -static struct option long_options[] = { - {"help", 0, 0, 'h'}, - {"no-ui", 0, 0, 'n'}, - {"play", 0, 0, 'p'}, - {"no-io", 1, 0, 'N'}, - {"instrument", 1, 0, 'i'}, - {"scene", 1, 0, 's'}, - {"effect", 1, 0, 'e'}, - {"config", 1, 0, 'c'}, - {"metronome", 0, 0, 'm'}, - {"tempo", 1, 0, 't'}, - {"beats", 1, 0, 'b'}, - {"drum-pattern", 1, 0, 'd'}, - {"drum-track", 1, 0, 'D'}, -#if USE_PYTHON - {"run-script", 1, 0, 'r'}, -#endif - {"output", 1, 0, 'o'}, - {0,0,0,0}, -}; - -void print_help(char *progname) -{ - printf("Usage: %s [options]\n" - "\n" - "Options:\n" - " -h | --help Show this help text\n" - " -m | --metronome Create a simple metronome pattern\n" -#if USE_NCURSES - " -n | --no-ui Do not start the user interface\n" -#endif - " -p | --play Start pattern playback (default for -d/-D)\n" - " -P | --no-play Don't start pattern playback\n" - " -N | --no-io Use off-line processing instead of JACK I/O\n" - " -d | --drum-pattern

Load drum pattern with a given name\n" - " -D | --drum-track Load drum track with a given name\n" - " -t | --tempo Use given tempo (specified in beats/min)\n" - " -b | --beats Use given beats/bar\n" - " -e | --effect Override master effect with preset \n" - " -i | --instrument Load instrument as a single-instrument scene\n" - " -s | --scene Load a scene \n" - " -c | --config Use specified config file instead of default\n" -#if USE_PYTHON - " -r | --run-script Run a Python script from a given file\n" -#endif - " -o | --output Write the first stereo output to a WAV file\n" - "\n", - progname); - exit(0); -} - -#if USE_PYTHON - -// This is a workaround for what I consider a defect in pyconfig.h -#undef _XOPEN_SOURCE -#undef _POSIX_C_SOURCE - -#include - -static gboolean set_error_from_python(GError **error) -{ - PyObject *ptype = NULL, *pvalue = NULL, *ptraceback = NULL; - PyErr_Fetch(&ptype, &pvalue, &ptraceback); - PyObject *ptypestr = PyObject_Str(ptype); - PyObject *pvaluestr = PyObject_Str(pvalue); - PyObject *ptypestr_unicode = PyUnicode_AsUTF8String(ptypestr); - PyObject *pvaluestr_unicode = PyUnicode_AsUTF8String(pvaluestr); - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "%s: %s", PyBytes_AsString(ptypestr_unicode), PyBytes_AsString(pvaluestr_unicode)); - Py_DECREF(pvaluestr_unicode); - Py_DECREF(ptypestr_unicode); - //g_error("%s:%s", PyString_AsString(ptypestr), PyString_AsString(pvaluestr)); - Py_DECREF(ptypestr); - Py_DECREF(pvaluestr); - Py_DECREF(ptype); - Py_XDECREF(pvalue); - Py_XDECREF(ptraceback); - return FALSE; -} - -void cbox_script_run(const char *name) -{ - FILE *fp = fopen(name, "rb"); - if (!fp) - { - g_warning("Cannot open script file '%s': %s", name, strerror(errno)); - return; - } - // (_cbox module is discontinued, use _cbox2) - // PyImport_AppendInittab("_cbox", &PyInit_cbox); - Py_Initialize(); -#if 0 - if (PyType_Ready(&CboxCallbackType) < 0) - { - g_warning("Cannot install the C callback type"); - return; - } - Py_INCREF(&CboxCallbackType); - engine_initialised = TRUE; -#endif - if (PyRun_SimpleFile(fp, name) == 1) - { - GError *error = NULL; - set_error_from_python(&error); - cbox_print_error(error); - } - Py_Finalize(); -} - -#endif - -#if USE_NCURSES - -static int (*old_menu_on_idle)(struct cbox_ui_page *page); - -static int on_idle_with_ui_poll(struct cbox_ui_page *page) -{ - cbox_app_on_idle(NULL, NULL); - - if (old_menu_on_idle) - return old_menu_on_idle(page); - else - return 0; -} - -void run_ui() -{ - struct cbox_menu_state *st = NULL; - struct cbox_menu_page *page = cbox_menu_page_new(); - cbox_ui_start(); - old_menu_on_idle = page->page.on_idle; - page->page.on_idle = on_idle_with_ui_poll; - - struct cbox_menu *main_menu = create_main_menu(); - - st = cbox_menu_state_new(page, main_menu, stdscr, NULL); - page->state = st; - - cbox_ui_run(&page->page); - cbox_ui_stop(); - cbox_menu_state_destroy(st); - cbox_menu_page_destroy(page); - cbox_menu_destroy(main_menu); -} - -#endif - -void run_no_ui() -{ - printf("Ready. Press ENTER to exit.\n"); - fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) | O_NONBLOCK); - do { - int ch = getchar(); - if (ch == 10 || (ch == -1 && errno != EWOULDBLOCK)) - break; - usleep(100000); - cbox_app_on_idle(NULL, NULL); - } while(1); - fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) &~ O_NONBLOCK); -} - -int main(int argc, char *argv[]) -{ - struct cbox_open_params params; - struct cbox_layer *layer; - const char *config_name = NULL; - const char *instrument_name = NULL; - const char *scene_name = NULL; - const char *effect_preset_name = NULL; - const char *drum_pattern_name = NULL; - const char *drum_track_name = NULL; -#if USE_PYTHON - const char *script_name = NULL; -#endif - const char *output_name = NULL; - char *instr_section = NULL; - struct cbox_scene *scene = NULL; - int metronome = 0; - int bpb = 0; - float tempo = 0; - GError *error = NULL; -#if USE_NCURSES - gboolean no_ui = FALSE; -#endif - gboolean no_io = FALSE; - int play_immediately = 0; - int no_io_srate = 0; - - cbox_dom_init(); - - while(1) - { - int option_index; - int c = getopt_long(argc, argv, short_options, long_options, &option_index); - if (c == -1) - break; - switch (c) - { - case 'c': - config_name = optarg; - break; - case 'i': - instrument_name = optarg; - break; - case 'o': - output_name = optarg; - break; - case 's': - scene_name = optarg; - break; - case 'e': - effect_preset_name = optarg; - break; - case 'd': - drum_pattern_name = optarg; - break; - case 'D': - drum_track_name = optarg; - break; -#if USE_PYTHON - case 'r': - script_name = optarg; - break; -#endif - case 'm': - metronome = 1; - break; - case 'n': -#if USE_NCURSES - no_ui = TRUE; -#endif - break; - case 'N': - no_io = TRUE; - no_io_srate = atoi(optarg); - break; - case 'p': - play_immediately = 1; - break; - case 'P': - play_immediately = -1; - break; - case 'b': - bpb = atoi(optarg); - break; - case 't': - tempo = atof(optarg); - break; - case 'h': - case '?': - print_help(argv[0]); - return 0; - } - } - - app.tarpool = cbox_tarpool_new(); - app.document = cbox_document_new(); - app.rt = cbox_rt_new(app.document); - app.engine = cbox_engine_new(app.document, app.rt); - app.rt->engine = app.engine; - - cbox_config_init(config_name); - if (tempo < 1) - tempo = cbox_config_get_float("master", "tempo", 120); - if (bpb < 1) - bpb = cbox_config_get_int("master", "beats_per_bar", 4); - - if (no_io) - { - cbox_rt_set_offline(app.rt, no_io_srate, 1024); - } - else - { - GError *error = NULL; - if (!cbox_io_init(&app.io, ¶ms, NULL, &error)) - { - fprintf(stderr, "Cannot initialise sound I/O: %s\n", (error && error->message) ? error->message : "Unknown error"); - return 1; - } - cbox_rt_set_io(app.rt, &app.io); - } - cbox_wavebank_init(); - - if (!scene_name && !instrument_name) - { - scene_name = cbox_config_get_string("init", "scene"); - instrument_name = cbox_config_get_string("init", "instrument"); - if (!scene_name && !instrument_name) - { - if (cbox_config_has_section("scene:default")) - scene_name = "default"; - else - if (cbox_config_has_section("instrument:default")) - instrument_name = "default"; - } - } - - scene = cbox_scene_new(app.document, app.engine); - if (!scene) - goto fail; - if (scene_name) - { - app.current_scene_name = g_strdup_printf("scene:%s", scene_name); - if (!cbox_scene_load(scene, scene_name, &error)) - goto fail; - } - else - if (instrument_name) - { - app.current_scene_name = g_strdup_printf("instrument:%s", instrument_name); - layer = cbox_layer_new_with_instrument(scene, instrument_name, &error); - if (!layer) - goto fail; - - if (!cbox_scene_add_layer(scene, layer, &error)) - goto fail; - } - - if (!effect_preset_name) - effect_preset_name = cbox_config_get_string("master", "effect"); - - if (effect_preset_name && *effect_preset_name) - { - app.engine->effect = cbox_module_new_from_fx_preset(effect_preset_name, app.document, app.rt, app.engine, &error); - if (!app.engine->effect) - goto fail; - } - cbox_master_set_tempo(app.engine->master, tempo); - cbox_master_set_timesig(app.engine->master, bpb, 4); - - if (output_name && scene) - { - GError *error = NULL; - if (!cbox_recording_source_attach(&scene->rec_stereo_outputs[0], cbox_recorder_new_stream(app.engine, app.rt, output_name), &error)) - cbox_print_error(error); - } - cbox_rt_start(app.rt, NULL); - if (drum_pattern_name) - cbox_song_use_looped_pattern(app.engine->master->song, cbox_midi_pattern_load(app.engine->master->song, drum_pattern_name, 1, app.engine->master->ppqn_factor)); - else if (drum_track_name) - cbox_song_use_looped_pattern(app.engine->master->song, cbox_midi_pattern_load_track(app.engine->master->song, drum_track_name, 1, app.engine->master->ppqn_factor)); - else if (metronome) - cbox_song_use_looped_pattern(app.engine->master->song, cbox_midi_pattern_new_metronome(app.engine->master->song, app.engine->master->timesig_num, app.engine->master->ppqn_factor)); - - gboolean has_song = drum_pattern_name || drum_track_name || metronome; - if (play_immediately == 1 || (play_immediately != -1 && has_song)) - cbox_master_play(app.engine->master); -#if USE_PYTHON - if (script_name) - cbox_script_run(script_name); - else -#endif -#if USE_NCURSES - if (!no_ui) - run_ui(); - else - run_no_ui(); -#else - run_no_ui(); -#endif - cbox_rt_stop(app.rt); - if (!no_io) - cbox_io_close(&app.io); - goto ok; - -fail: - fprintf(stderr, "Cannot start: %s\n", error ? error->message : "unknown error"); -ok: - if (error) - g_error_free(error); - if (app.engine->effect) - { - CBOX_DELETE(app.engine->effect); - app.engine->effect = NULL; - } - CBOX_DELETE(app.engine); - CBOX_DELETE(app.rt); - cbox_tarpool_destroy(app.tarpool); - - if (cbox_wavebank_get_maxbytes() > 0) - g_message("Max waveform usage: %f MB", (float)(cbox_wavebank_get_maxbytes() / 1048576.0)); - - cbox_document_destroy(app.document); - cbox_wavebank_close(); - cbox_config_close(); - - g_free(instr_section); - - cbox_dom_close(); - - return 0; -} diff --git a/template/calfbox/master.c b/template/calfbox/master.c deleted file mode 100644 index ee379f7..0000000 --- a/template/calfbox/master.c +++ /dev/null @@ -1,389 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "engine.h" -#include "errors.h" -#include "master.h" -#include "seq.h" -#include "rt.h" -#include "song.h" -#include - -static gboolean master_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct cbox_master *m = ct->user_data; - if (!strcmp(cmd->command, "/status") && !*cmd->arg_types) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - if (!cbox_execute_on(fb, NULL, "/sample_rate", "i", error, m->srate)) - return FALSE; - if (!m->spb) - return TRUE; - return cbox_execute_on(fb, NULL, "/tempo", "f", error, m->tempo) && - cbox_execute_on(fb, NULL, "/timesig", "ii", error, m->timesig_num, m->timesig_denom) && - cbox_execute_on(fb, NULL, "/playing", "i", error, (int)m->state) && - cbox_execute_on(fb, NULL, "/pos", "i", error, m->spb->song_pos_samples) && - cbox_execute_on(fb, NULL, "/pos_ppqn", "i", error, m->spb->song_pos_ppqn) && - cbox_execute_on(fb, NULL, "/ppqn_factor", "i", error, (int)m->ppqn_factor); - } - else - if (!strcmp(cmd->command, "/tell") && !*cmd->arg_types) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - if (!m->spb) - return TRUE; - return cbox_execute_on(fb, NULL, "/playing", "i", error, (int)m->state) && - cbox_execute_on(fb, NULL, "/pos", "i", error, m->spb->song_pos_samples) && - cbox_execute_on(fb, NULL, "/pos_ppqn", "i", error, m->spb->song_pos_ppqn); - } - else - if (!strcmp(cmd->command, "/set_tempo") && !strcmp(cmd->arg_types, "f")) - { - cbox_master_set_tempo(m, CBOX_ARG_F(cmd, 0)); - return TRUE; - } - else - if (!strcmp(cmd->command, "/set_timesig") && !strcmp(cmd->arg_types, "ii")) - { - cbox_master_set_timesig(m, CBOX_ARG_I(cmd, 0), CBOX_ARG_I(cmd, 1)); - return TRUE; - } - else - if (!strcmp(cmd->command, "/set_ppqn_factor") && !strcmp(cmd->arg_types, "i")) - { - m->ppqn_factor = CBOX_ARG_I(cmd, 0); - return TRUE; - } - else - if (!strcmp(cmd->command, "/play") && !strcmp(cmd->arg_types, "")) - { - cbox_master_play(m); - return TRUE; - } - else - if (!strcmp(cmd->command, "/stop") && !strcmp(cmd->arg_types, "")) - { - cbox_master_stop(m); - return TRUE; - } - else - if (!strcmp(cmd->command, "/panic") && !strcmp(cmd->arg_types, "")) - { - cbox_master_panic(m); - return TRUE; - } - else - if (!strcmp(cmd->command, "/seek_samples") && !strcmp(cmd->arg_types, "i")) - { - cbox_master_seek_samples(m, CBOX_ARG_I(cmd, 0)); - return TRUE; - } - else - if (!strcmp(cmd->command, "/seek_ppqn") && !strcmp(cmd->arg_types, "i")) - { - cbox_master_seek_ppqn(m, CBOX_ARG_I(cmd, 0)); - return TRUE; - } - else - if ((!strcmp(cmd->command, "/samples_to_ppqn") || !strcmp(cmd->command, "/ppqn_to_samples")) && - !strcmp(cmd->arg_types, "i")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - if (m->spb) - { - if (cmd->command[1] == 's') - return cbox_execute_on(fb, NULL, "/value", "i", error, cbox_master_samples_to_ppqn(m, CBOX_ARG_I(cmd, 0))); - else - return cbox_execute_on(fb, NULL, "/value", "i", error, cbox_master_ppqn_to_samples(m, CBOX_ARG_I(cmd, 0))); - } - else - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Song playback not initialised."); - return FALSE; - } - } - else - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown combination of target path and argument: '%s', '%s'", cmd->command, cmd->arg_types); - return FALSE; - } -} - -static void cbox_master_init(struct cbox_master *master, struct cbox_engine *engine) -{ - master->srate = engine->io_env.srate; - master->tempo = 120.0; - master->new_tempo = 120.0; - master->timesig_num = 4; - master->timesig_denom = 4; - master->state = CMTS_STOP; - master->engine = engine; - master->song = NULL; - master->spb = NULL; - master->ppqn_factor = 48; - cbox_command_target_init(&master->cmd_target, master_process_cmd, master); -} - -struct cbox_master *cbox_master_new(struct cbox_engine *engine) -{ - struct cbox_master *master = malloc(sizeof(struct cbox_master)); - cbox_master_init(master, engine); - return master; -} - - -void cbox_master_set_sample_rate(struct cbox_master *master, int srate) -{ - master->srate = srate; -} - -void cbox_master_set_tempo(struct cbox_master *master, float tempo) -{ - // XXXKF not realtime-safe; won't crash, but may lose tempo - // changes when used multiple times in rapid succession - master->new_tempo = tempo; -} - -void cbox_master_set_timesig(struct cbox_master *master, int beats, int unit) -{ - if (beats > 0) - master->timesig_num = beats; - if (unit > 0) - master->timesig_denom = unit; -} - -#define cbox_master_play_args(ARG) - -DEFINE_RT_VOID_FUNC(cbox_master, master, cbox_master_play) -{ - struct cbox_rt *rt = master->engine->rt; - if (rt && rt->io && rt->io->impl->controltransportfunc) - { - rt->io->impl->controltransportfunc(rt->io->impl, FALSE, master->spb ? master->spb->song_pos_samples : (uint32_t)-1); - if (!rt->io->impl->getsynccompletedfunc(rt->io->impl)) - RT_CALL_AGAIN_LATER(); - rt->io->impl->controltransportfunc(rt->io->impl, TRUE, master->spb ? master->spb->song_pos_samples : (uint32_t)-1); - return; - } - // wait for the notes to be released - if (master->state == CMTS_STOPPING) - { - RT_CALL_AGAIN_LATER(); - return; - } - - master->state = CMTS_ROLLING; -} - -#define cbox_master_stop_args(ARG) - -DEFINE_RT_VOID_FUNC(cbox_master, master, cbox_master_stop) -{ - struct cbox_rt *rt = master->engine->rt; - if (rt && rt->io && rt->io->impl->controltransportfunc) - { - rt->io->impl->controltransportfunc(rt->io->impl, FALSE, -1); - return; - } - if (master->state == CMTS_ROLLING) - master->state = CMTS_STOPPING; - - if (master->state != CMTS_STOP) - RT_CALL_AGAIN_LATER(); -} - -struct seek_command_arg -{ - struct cbox_master *master; - gboolean is_ppqn; - uint32_t target_pos; - gboolean was_rolling; - gboolean status_known; - gboolean seek_in_progress; -}; - -static int seek_transport_execute(void *arg_) -{ - struct seek_command_arg *arg = arg_; - - struct cbox_rt *rt = arg->master->engine->rt; - if (rt && rt->io && rt->io->impl->controltransportfunc) - { - if (!arg->seek_in_progress) - { - arg->seek_in_progress = TRUE; - uint32_t pos = arg->target_pos; - if (arg->is_ppqn) - arg->target_pos = pos = cbox_master_ppqn_to_samples(arg->master, pos); - - rt->io->impl->controltransportfunc(rt->io->impl, arg->master->state == CMTS_ROLLING, pos); - // JACK slow-sync won't be performed if unless transport is rolling - if (!arg->was_rolling) - { - if (arg->master->spb) - cbox_song_playback_seek_samples(arg->master->spb, arg->target_pos); - return 5; - } - } - if (rt->io->impl->getsynccompletedfunc(rt->io->impl)) - { - if (arg->master->spb) - cbox_song_playback_seek_samples(arg->master->spb, arg->target_pos); - return 5; - } - return 0; - } - - // On first pass, check if transport is rolling from the DSP thread - if (!arg->status_known) - { - arg->status_known = TRUE; - arg->was_rolling = arg->master->state == CMTS_ROLLING; - } - // If transport was rolling, stop, release notes, seek, then restart - if (arg->master->state == CMTS_ROLLING) - arg->master->state = CMTS_STOPPING; - - // wait until transport stopped - if (arg->master->state != CMTS_STOP) - return 0; - - if (arg->master->spb) - { - if (arg->is_ppqn) - cbox_song_playback_seek_ppqn(arg->master->spb, arg->target_pos, FALSE); - else - cbox_song_playback_seek_samples(arg->master->spb, arg->target_pos); - } - if (arg->was_rolling) - arg->master->state = CMTS_ROLLING; - return 1; -} - -void cbox_master_seek_ppqn(struct cbox_master *master, uint32_t pos_ppqn) -{ - static struct cbox_rt_cmd_definition cmd = { NULL, seek_transport_execute, NULL }; - struct seek_command_arg arg = { master, TRUE, pos_ppqn, FALSE, FALSE, FALSE }; - cbox_rt_execute_cmd_sync(master->engine->rt, &cmd, &arg); -} - -void cbox_master_seek_samples(struct cbox_master *master, uint32_t pos_samples) -{ - static struct cbox_rt_cmd_definition cmd = { NULL, seek_transport_execute, NULL }; - struct seek_command_arg arg = { master, FALSE, pos_samples, FALSE, FALSE, FALSE }; - cbox_rt_execute_cmd_sync(master->engine->rt, &cmd, &arg); -} - -void cbox_master_panic(struct cbox_master *master) -{ - cbox_master_stop(master); - struct cbox_midi_buffer buf; - cbox_midi_buffer_init(&buf); - for (int ch = 0; ch < 16; ch++) - { - cbox_midi_buffer_write_inline(&buf, ch, 0xB0 + ch, 120, 0); - cbox_midi_buffer_write_inline(&buf, ch, 0xB0 + ch, 123, 0); - cbox_midi_buffer_write_inline(&buf, ch, 0xB0 + ch, 121, 0); - } - // Send to all outputs - cbox_engine_send_events_to(master->engine, NULL, &buf); -} - -uint32_t cbox_master_ppqn_to_samples(struct cbox_master *master, uint32_t time_ppqn) -{ - double tempo = master->tempo; - int offset = 0; - if (master->spb) - { - int idx = cbox_song_playback_tmi_from_ppqn(master->spb, time_ppqn); - if (idx != -1) - { - const struct cbox_tempo_map_item *tmi = &master->spb->tempo_map_items[idx]; - tempo = tmi->tempo; - time_ppqn -= tmi->time_ppqn; - offset = tmi->time_samples; - } - } - return offset + (int)(master->srate * 60.0 * time_ppqn / (tempo * master->ppqn_factor)); -} - -uint32_t cbox_master_samples_to_ppqn(struct cbox_master *master, uint32_t time_samples) -{ - double tempo = master->tempo; - uint32_t offset = 0; - if (master->spb) - { - int idx = cbox_song_playback_tmi_from_samples(master->spb, time_samples); - if (idx != -1 && idx < master->spb->tempo_map_item_count) - { - const struct cbox_tempo_map_item *tmi = &master->spb->tempo_map_items[idx]; - tempo = tmi->tempo; - time_samples -= tmi->time_samples; - offset = tmi->time_ppqn; - } - } - return offset + (uint32_t)(tempo * master->ppqn_factor * time_samples / (master->srate * 60.0)); -} - -void cbox_master_ppqn_to_bbt(const struct cbox_master *master, struct cbox_bbt *bbt, int time_ppqn, struct cbox_master_track_item *mti) -{ - bbt->bar = 0; - bbt->beat = 0; - bbt->tick = 0; - bbt->offset_samples = 0; - - uint32_t rel_ppqn = time_ppqn; - int idx = -1; - if (master->spb) - idx = cbox_song_playback_tmi_from_ppqn(master->spb, time_ppqn); - if (idx != -1 && idx < master->spb->tempo_map_item_count) - { - struct cbox_tempo_map_item *tmi = &master->spb->tempo_map_items[idx]; - rel_ppqn = time_ppqn - tmi->time_ppqn; - cbox_bbt_add(bbt, rel_ppqn, master->ppqn_factor, tmi->timesig_num, tmi->timesig_denom); - if (mti) - { - mti->tempo = tmi->tempo; - mti->timesig_num = tmi->timesig_num; - mti->timesig_denom = tmi->timesig_denom; - } - } - else - { - cbox_bbt_add(bbt, rel_ppqn, master->ppqn_factor, master->timesig_num, master->timesig_denom); - if (mti) - { - mti->tempo = master->tempo; - mti->timesig_num = master->timesig_num; - mti->timesig_denom = master->timesig_denom; - } - } -} - -void cbox_master_destroy(struct cbox_master *master) -{ - if (master->spb) - { - cbox_song_playback_destroy(master->spb); - master->spb = NULL; - } - free(master); -} diff --git a/template/calfbox/master.h b/template/calfbox/master.h deleted file mode 100644 index 69438fe..0000000 --- a/template/calfbox/master.h +++ /dev/null @@ -1,100 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_MASTER_H -#define CBOX_MASTER_H - -#include -#include "cmd.h" - -extern uint64_t PPQN; - -struct cbox_song; -struct cbox_rt; - -#define GET_RT_FROM_cbox_master(ptr) ((ptr)->engine->rt) - -enum cbox_master_transport_state -{ - CMTS_STOP, - CMTS_ROLLING, - CMTS_STOPPING, -}; - -struct cbox_master -{ - int srate; - float tempo, new_tempo; - int timesig_num; - int timesig_denom; // must be 4 for now - uint64_t ppqn_factor; - enum cbox_master_transport_state state; - struct cbox_engine *engine; - struct cbox_song *song; - struct cbox_song_playback *spb; - struct cbox_command_target cmd_target; -}; - -struct cbox_bbt -{ - uint32_t bar; - uint32_t beat; - uint32_t tick; - uint32_t offset_samples; -}; - -struct cbox_master_track_item; - -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) -{ - uint32_t ticks_per_beat = ppqn_factor * 4 / timesig_denom; - uint32_t beats_per_bar = timesig_num; - - accum->tick += ticks % ticks_per_beat; - if (accum->tick >= ticks_per_beat) - { - accum->tick -= ticks_per_beat; - accum->beat++; - } - uint32_t inc_beats = ticks / ticks_per_beat; - accum->beat += inc_beats % beats_per_bar; - if (accum->beat >= beats_per_bar) - { - accum->beat -= beats_per_bar; - accum->bar++; - } - accum->bar += inc_beats / beats_per_bar; -} - -extern struct cbox_master *cbox_master_new(struct cbox_engine *engine); -extern void cbox_master_set_sample_rate(struct cbox_master *master, int srate); -extern void cbox_master_set_tempo(struct cbox_master *master, float tempo); -extern void cbox_master_set_timesig(struct cbox_master *master, int beats, int unit); -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); -//extern uint32_t cbox_master_song_pos_from_bbt(struct cbox_master *master, const struct cbox_bbt *bbt); -extern void cbox_master_play(struct cbox_master *master); -extern void cbox_master_stop(struct cbox_master *master); -extern void cbox_master_panic(struct cbox_master *master); -extern void cbox_master_seek_ppqn(struct cbox_master *master, uint32_t pos_ppqn); -extern void cbox_master_seek_samples(struct cbox_master *master, uint32_t pos_samples); -extern void cbox_master_destroy(struct cbox_master *master); - -uint32_t cbox_master_ppqn_to_samples(struct cbox_master *master, uint32_t time_ppqn); -uint32_t cbox_master_samples_to_ppqn(struct cbox_master *master, uint32_t time_samples); - -#endif diff --git a/template/calfbox/menu.c b/template/calfbox/menu.c deleted file mode 100644 index e80ca0c..0000000 --- a/template/calfbox/menu.c +++ /dev/null @@ -1,288 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "menu.h" -#include "menuitem.h" -#include "ui.h" - -#if USE_NCURSES - -#include -#include -#include -#include -#include - -struct cbox_menu -{ - GPtrArray *items; - GStringChunk *strings; -}; - -static int cbox_menu_page_on_key(struct cbox_ui_page *p, int ch); -static int cbox_menu_page_on_idle(struct cbox_ui_page *p); - -struct cbox_menu *cbox_menu_new() -{ - struct cbox_menu *menu = malloc(sizeof(struct cbox_menu)); - - menu->items = g_ptr_array_new(); - menu->strings = g_string_chunk_new(100); - return menu; -} - -struct cbox_menu_item *cbox_menu_add_item(struct cbox_menu *menu, struct cbox_menu_item *item) -{ - g_ptr_array_add(menu->items, item); - return item; -} - -void cbox_menu_destroy(struct cbox_menu *menu) -{ - guint i; - - for (i = 0; i < menu->items->len; i++) - cbox_menu_item_destroy(g_ptr_array_index(menu->items, i)); - - g_ptr_array_free(menu->items, TRUE); - g_string_chunk_free(menu->strings); - free(menu); -} - - -/* -gchar *cbox_menu_item_value_format(const struct cbox_menu_item *item, void *context) -{ - switch(item->type) - { - case menu_item_static: - if (item->extras) - return ((struct cbox_menu_item_extras_static *)(item->extras))->format_value(item, context); - else - return g_strdup_printf(""); - case menu_item_submenu: - return g_strdup_printf("..."); - case menu_item_command: - return g_strdup_printf(""); - default: - if (!item->value) - return g_strdup_printf("(null)"); - switch(item->type) - { - case menu_item_value_int: - return g_strdup_printf(((struct cbox_menu_item_extras_int *)(item->extras))->fmt, *(int *)item->value); - case menu_item_value_double: - return g_strdup_printf(((struct cbox_menu_item_extras_double *)(item->extras))->fmt, *(double *)item->value); - case menu_item_value_enum: - return g_strdup_printf("%d", *(int *)item->value); - default: - return g_strdup_printf(""); - } - } - assert(0); - return NULL; -} -*/ - -void cbox_menu_state_size(struct cbox_menu_state *menu_state) -{ - struct cbox_menu *menu = menu_state->menu; - guint i; - menu_state->size.label_width = 0; - menu_state->size.value_width = 0; - menu_state->size.height = 0; - menu_state->yspace = getmaxy(menu_state->window) - 2; - - for (i = 0; i < menu->items->len; i++) - { - struct cbox_menu_item *item = g_ptr_array_index(menu->items, i); - - item->x = 1; - item->y = 1 + menu_state->size.height; - item->item_class->measure(item, menu_state); - } -} - -void cbox_menu_state_draw(struct cbox_menu_state *menu_state) -{ - struct cbox_menu *menu = menu_state->menu; - guint i; - - werase(menu_state->window); - box(menu_state->window, 0, 0); - for (i = 0; i < menu->items->len; i++) - { - struct cbox_menu_item *item = g_ptr_array_index(menu->items, i); - gchar *str = item->item_class->format_value(item, menu_state); - item->item_class->draw(item, menu_state, str, menu_state->cursor == i); - g_free(str); - } - wrefresh(menu_state->window); -} - -static void cbox_menu_page_draw(struct cbox_ui_page *p) -{ - struct cbox_menu_page *mp = p->user_data; - struct cbox_menu_state *st = mp->state; - cbox_menu_state_size(st); - cbox_menu_state_draw(st); -} - -static int cbox_menu_is_item_enabled(struct cbox_menu *menu, unsigned int item) -{ - assert(item < menu->items->len); - - return ((struct cbox_menu_item *)g_ptr_array_index(menu->items, item))->item_class->on_key != NULL; -} - -struct cbox_menu_state *cbox_menu_state_new(struct cbox_menu_page *page, struct cbox_menu *menu, WINDOW *window, void *context) -{ - struct cbox_menu_state *st = malloc(sizeof(struct cbox_menu_state)); - st->page = page; - st->menu = menu; - st->cursor = 0; - st->yoffset = 0; - st->window = window; - st->context = context; - st->caller = NULL; - st->menu_is_temporary = 0; - - while(st->cursor < menu->items->len - 1 && !cbox_menu_is_item_enabled(menu, st->cursor)) - st->cursor++; - - return st; -} - -int cbox_menu_page_on_idle(struct cbox_ui_page *p) -{ - struct cbox_menu_page *mp = p->user_data; - struct cbox_menu_state *st = mp->state; - cbox_menu_state_size(st); - cbox_menu_state_draw(st); - return 0; -} - -int cbox_menu_page_on_key(struct cbox_ui_page *p, int ch) -{ - struct cbox_menu_page *mp = p->user_data; - struct cbox_menu_state *st = mp->state; - struct cbox_menu *menu = st->menu; - struct cbox_menu_item *item = NULL; - int pos = st->cursor; - int res = 0; - if (st->cursor >= 0 && st->cursor < menu->items->len) - item = g_ptr_array_index(menu->items, st->cursor); - - if (ch == 27) - return ch; - - if (item->item_class->on_key) - { - res = item->item_class->on_key(item, st, ch); - st = mp->state; - if (res < 0) - { - cbox_menu_state_size(st); - cbox_menu_state_draw(st); - return 0; - } - } - - if (res > 0) - return res; - - switch(ch) - { - case 12: - wclear(st->window); - return 0; - case 27: - return ch; - case KEY_UP: - case KEY_END: - pos = ch == KEY_END ? menu->items->len - 1 : st->cursor - 1; - while(pos >= 0 && !cbox_menu_is_item_enabled(menu, pos)) - pos--; - if (pos >= 0) - st->cursor = pos; - if (ch == KEY_END) - { - st->yoffset = st->size.height - st->yspace; - if (st->yoffset < 0) - st->yoffset = 0; - } - else - if (pos >= 0 && (guint)pos < menu->items->len) - { - int npos = st->cursor; - int count = 0; - // show up to 2 disabled items above - while(npos >= 1 && !cbox_menu_is_item_enabled(menu, npos - 1) && count < 2) - { - npos--; - count++; - } - item = g_ptr_array_index(menu->items, npos); - if (item->y < 1 + st->yoffset) - st->yoffset = item->y - 1; - } - cbox_menu_state_draw(st); - return 0; - case KEY_HOME: - case KEY_DOWN: - pos = ch == KEY_HOME ? 0 : st->cursor + 1; - while(pos < (int)menu->items->len && !cbox_menu_is_item_enabled(menu, pos)) - pos++; - if (pos < (int)menu->items->len) - st->cursor = pos; - if (ch == KEY_HOME) - st->yoffset = 0; - else if (pos >= 0 && pos < (int)menu->items->len) - { - item = g_ptr_array_index(menu->items, st->cursor); - if (item->y - 1 - st->yoffset >= st->yspace) - st->yoffset = item->y - st->yspace; - } - cbox_menu_state_draw(st); - return 0; - } - return 0; -} - -void cbox_menu_state_destroy(struct cbox_menu_state *st) -{ - free(st); -} - -struct cbox_menu_page *cbox_menu_page_new() -{ - struct cbox_menu_page *page = malloc(sizeof(struct cbox_menu_page)); - page->state = NULL; - page->page.user_data = page; - page->page.draw = cbox_menu_page_draw; - page->page.on_key = cbox_menu_page_on_key; - page->page.on_idle = cbox_menu_page_on_idle; - return page; -} - -void cbox_menu_page_destroy(struct cbox_menu_page *p) -{ - free(p); -} - -#endif diff --git a/template/calfbox/menu.h b/template/calfbox/menu.h deleted file mode 100644 index bfe35e6..0000000 --- a/template/calfbox/menu.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_MENU_H -#define CBOX_MENU_H - -#include "config.h" - -#if USE_NCURSES - -#include -#include - -#include "menuitem.h" -#include "ui.h" - -struct cbox_menu; -struct cbox_menu_item; -struct cbox_menu_page; - -struct cbox_menu_state -{ - struct cbox_menu_page *page; - struct cbox_menu *menu; - guint cursor; - int yoffset, yspace; - struct cbox_menu_measure size; - WINDOW *window; - void *context; - struct cbox_menu_state *caller; - int menu_is_temporary; -}; - -extern struct cbox_menu *cbox_menu_new(void); -extern struct cbox_menu_item *cbox_menu_add_item(struct cbox_menu *menu, struct cbox_menu_item *item); -extern void cbox_menu_destroy(struct cbox_menu *menu); - -extern struct cbox_menu_state *cbox_menu_state_new(struct cbox_menu_page *page, struct cbox_menu *menu, WINDOW *window, void *context); -extern void cbox_menu_state_destroy(struct cbox_menu_state *st); - -struct cbox_menu_page -{ - struct cbox_ui_page page; - struct cbox_menu_state *state; -}; - -extern struct cbox_menu_page *cbox_menu_page_new(void); -extern void cbox_menu_page_destroy(struct cbox_menu_page *st); - -#endif - -#endif diff --git a/template/calfbox/menuitem.c b/template/calfbox/menuitem.c deleted file mode 100644 index 97274c0..0000000 --- a/template/calfbox/menuitem.c +++ /dev/null @@ -1,303 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "menu.h" -#if USE_NCURSES -#include "menuitem.h" - -#include -#include - -/*******************************************************************/ - -static void item_measure(struct cbox_menu_item *item, struct cbox_menu_state *state) -{ - struct cbox_menu_measure *m = &state->size; - gchar *value = item->item_class->format_value(item, state); - - int len = strlen(item->label); - int len2 = value ? strlen(value) : 0; - if (len > m->label_width) - m->label_width = len; - if (len2 > m->value_width) - m->value_width = len2; - m->height++; - - g_free(value); -} - -static void item_destroy(struct cbox_menu_item *item) -{ - if (item->flags & mif_free_label) - g_free(item->label); - if (item->flags & mif_free_context) - free(item->item_context); - if (item->flags & mif_context_is_struct) - { - struct cbox_menu_item_context *ctx = item->item_context; - ctx->destroy_func(ctx); - } - g_free(item); -} - -static void item_draw(struct cbox_menu_item *item, struct cbox_menu_state *state, gchar *value, int hilited) -{ - int y = item->y - state->yoffset; - if (y < 1 || y > state->yspace) - return; - if (hilited) - wattron(state->window, A_REVERSE); - mvwprintw(state->window, item->y - state->yoffset, item->x, "%-*s %*s", state->size.label_width, item->label, state->size.value_width, value); - wattroff(state->window, A_REVERSE); -} - -/*******************************************************************/ - -static gchar *command_format(const struct cbox_menu_item *item, struct cbox_menu_state *state) -{ - return g_strdup("*"); -} - -static int command_on_key(struct cbox_menu_item *item, struct cbox_menu_state *state, int key) -{ - if (key == 10) - { - struct cbox_menu_item_command *citem = (struct cbox_menu_item_command *)item; - return citem->execute(citem, state->context); - } - return 0; -} - -struct cbox_menu_item_class menu_item_class_command = { - .measure = item_measure, - .draw = item_draw, - .format_value = command_format, - .on_idle = NULL, - .on_key = command_on_key, - .destroy = item_destroy -}; - -/*******************************************************************/ - -void static_draw(struct cbox_menu_item *item, struct cbox_menu_state *state, gchar *value, int hilited) -{ - int y = item->y - state->yoffset; - if (y < 1 || y > state->yspace) - return; - if (!value) - { - wattron(state->window, A_BOLD); - mvwprintw(state->window, y, item->x, "%-*s", state->size.label_width + state->size.value_width, item->label); - wattroff(state->window, A_BOLD); - } - else - mvwprintw(state->window, y, item->x, "%-*s %*s", state->size.label_width, item->label, state->size.value_width, value); -} - -static gchar *static_format(const struct cbox_menu_item *item, struct cbox_menu_state *state) -{ - struct cbox_menu_item_static *sitem = (struct cbox_menu_item_static *)item; - if (!sitem->format_value) - return NULL; - return sitem->format_value(sitem, state->context); -} - -struct cbox_menu_item_class menu_item_class_static = { - .measure = item_measure, - .draw = static_draw, - .format_value = static_format, - .on_idle = NULL, - .on_key = NULL, - .destroy = item_destroy -}; - -/*******************************************************************/ - -static gchar *intvalue_format(const struct cbox_menu_item *item, struct cbox_menu_state *state) -{ - struct cbox_menu_item_int *iitem = (struct cbox_menu_item_int *)item; - - return g_strdup_printf("%d", *iitem->value); -} - -static int intvalue_on_key(struct cbox_menu_item *item, struct cbox_menu_state *state, int key) -{ - struct cbox_menu_item_int *iitem = (struct cbox_menu_item_int *)item; - int *pv; - - switch(key) - { - case KEY_LEFT: - pv = iitem->value; - if (*pv > iitem->vmin) - { - (*pv)--; - if (iitem->on_change) - iitem->on_change(iitem, state->context); - return -1; - } - return 0; - case KEY_RIGHT: - pv = iitem->value; - if (*pv < iitem->vmax) - { - (*pv)++; - if (iitem->on_change) - iitem->on_change(iitem, state->context); - return -1; - } - return 0; - } - return 0; -} - -struct cbox_menu_item_class menu_item_class_int = { - .measure = item_measure, - .draw = item_draw, - .format_value = intvalue_format, - .on_idle = NULL, - .on_key = intvalue_on_key, - .destroy = item_destroy -}; - - -/*******************************************************************/ - -static gchar *menu_format(const struct cbox_menu_item *item, struct cbox_menu_state *state) -{ - struct cbox_menu_item_menu *mitem = (struct cbox_menu_item_menu *)item; - - return g_strdup((mitem->menu || mitem->create_menu) ? "->" : "<-"); -} - -static int menu_on_key(struct cbox_menu_item *item, struct cbox_menu_state *state, int key) -{ - struct cbox_menu_page *page = state->page; - if (key == 10) - { - struct cbox_menu_item_menu *mitem = (struct cbox_menu_item_menu *)item; - if (mitem->create_menu) - { - struct cbox_menu_state *new_state = cbox_menu_state_new(page, mitem->create_menu(mitem, state->context), state->window, state->context); - new_state->caller = state; - new_state->menu_is_temporary = 1; - - page->state = new_state; - } - else - if (mitem->menu) - { - struct cbox_menu_state *new_state = cbox_menu_state_new(page, mitem->menu, state->window, state->context); - new_state->caller = state; - - page->state = new_state; - } - else - { - struct cbox_menu_state *caller_state = state->caller; - if (state->menu_is_temporary) - cbox_menu_destroy(state->menu); - cbox_menu_state_destroy(state); - - page->state = caller_state; - return -1; - } - return 0; - } - return 0; -} - -struct cbox_menu_item_class menu_item_class_menu = { - .measure = item_measure, - .draw = item_draw, - .format_value = menu_format, - .on_idle = NULL, - .on_key = menu_on_key, - .destroy = item_destroy -}; - -/*******************************************************************/ - -#define TREAT_LABEL(label) ((flags & mif_dup_label) == mif_dup_label ? g_strdup(label) : (char *)(label)) - -struct cbox_menu_item *cbox_menu_item_new_command(const char *label, cbox_menu_item_execute_func exec, void *item_context, uint32_t flags) -{ - struct cbox_menu_item_command *item = calloc(1, sizeof(struct cbox_menu_item_command)); - item->item.label = TREAT_LABEL(label); - item->item.flags = flags; - item->item.item_class = &menu_item_class_command; - item->item.item_context = item_context; - item->execute = exec; - return &item->item; -} - -struct cbox_menu_item *cbox_menu_item_new_static(const char *label, cbox_menu_item_format_value fmt, void *item_context, uint32_t flags) -{ - struct cbox_menu_item_static *item = calloc(1, sizeof(struct cbox_menu_item_static)); - item->item.label = TREAT_LABEL(label); - item->item.flags = flags; - item->item.item_class = &menu_item_class_static; - item->item.item_context = item_context; - item->format_value = fmt; - return &item->item; -} - -struct cbox_menu_item *cbox_menu_item_new_int(const char *label, int *value, int vmin, int vmax, void *item_context, uint32_t flags) -{ - struct cbox_menu_item_int *item = calloc(1, sizeof(struct cbox_menu_item_int)); - item->item.label = TREAT_LABEL(label); - item->item.flags = flags; - item->item.item_class = &menu_item_class_int; - item->item.item_context = item_context; - item->value = value; - item->vmin = vmin; - item->vmax = vmax; - item->on_change = NULL; - return &item->item; -} - -struct cbox_menu_item *cbox_menu_item_new_menu(const char *label, struct cbox_menu *menu, void *item_context, uint32_t flags) -{ - struct cbox_menu_item_menu *item = calloc(1, sizeof(struct cbox_menu_item_menu)); - item->item.label = TREAT_LABEL(label); - item->item.flags = flags; - item->item.item_class = &menu_item_class_menu; - item->item.item_context = item_context; - item->menu = menu; - item->create_menu = NULL; - return &item->item; -} - -struct cbox_menu_item *cbox_menu_item_new_dynamic_menu(const char *label, create_menu_func func, void *item_context, uint32_t flags) -{ - struct cbox_menu_item_menu *item = calloc(1, sizeof(struct cbox_menu_item_menu)); - item->item.label = TREAT_LABEL(label); - item->item.flags = flags; - item->item.item_class = &menu_item_class_menu; - item->item.item_context = item_context; - item->menu = NULL; - item->create_menu = func; - return &item->item; -} - -void cbox_menu_item_destroy(struct cbox_menu_item *item) -{ - item->item_class->destroy(item); -} - -#endif diff --git a/template/calfbox/menuitem.h b/template/calfbox/menuitem.h deleted file mode 100644 index e209745..0000000 --- a/template/calfbox/menuitem.h +++ /dev/null @@ -1,133 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_MENUITEM_H -#define CBOX_MENUITEM_H - -#if USE_NCURSES - -#include -#include -#include - -struct cbox_menu; -struct cbox_menu_item; -struct cbox_menu_item_command; -struct cbox_menu_item_static; -struct cbox_menu_item_menu; -struct cbox_menu_state; - -struct cbox_menu_measure -{ - int label_width; - int value_width; - int height; -}; - -struct cbox_menu_item_class -{ - void (*measure)(struct cbox_menu_item *item, struct cbox_menu_state *state); - void (*draw)(struct cbox_menu_item *item, struct cbox_menu_state *state, gchar *value, int hilited); - gchar *(*format_value)(const struct cbox_menu_item *item, struct cbox_menu_state *state); - int (*on_key)(struct cbox_menu_item *item, struct cbox_menu_state *state, int key); - int (*on_idle)(struct cbox_menu_item *item, struct cbox_menu_state *state); - void (*destroy)(struct cbox_menu_item *item); - -}; - -typedef int (*cbox_menu_item_execute_func)(struct cbox_menu_item_command *item, void *context); -typedef char *(*cbox_menu_item_format_value)(const struct cbox_menu_item_static *item, void *context); - -struct cbox_menu_item -{ - gchar *label; - struct cbox_menu_item_class *item_class; - void *item_context; - uint32_t flags; - int x, y; - /* TODO: is_active? */ -}; - -struct cbox_menu_item_command -{ - struct cbox_menu_item item; - int (*execute)(struct cbox_menu_item_command *item, void *menu_context); -}; - -struct cbox_menu_item_int -{ - struct cbox_menu_item item; - int *value; - int vmin, vmax; - const char *fmt; - int (*on_change)(struct cbox_menu_item_int *item, void *menu_context); -}; - -struct cbox_menu_item_double -{ - struct cbox_menu_item item; - double *value; - double vmin, vmax; - const char *fmt; - double step_arg; - double (*step)(struct cbox_menu_item_double *item, int where); - int (*on_change)(struct cbox_menu_item_double *item, void *menu_context); -}; - -struct cbox_menu_item_static -{ - struct cbox_menu_item item; - char *(*format_value)(const struct cbox_menu_item_static *item, void *menu_context); -}; - -typedef struct cbox_menu *(*create_menu_func)(struct cbox_menu_item_menu *item, void *menu_context); - -struct cbox_menu_item_context -{ - void (*destroy_func)(void *menu_context); -}; - -struct cbox_menu_item_menu -{ - struct cbox_menu_item item; - struct cbox_menu *menu; - create_menu_func create_menu; -}; - -enum { - mif_free_label = 1, // release the label on destroy - mif_free_context = 2, // release the context on destroy - mif_dup_label = 4 | mif_free_label, // clone the label, release the clone on destroy - 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) -}; - -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); -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); -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); -extern struct cbox_menu_item *cbox_menu_item_new_menu(const char *label, struct cbox_menu *menu, void *item_context, uint32_t flags); -extern struct cbox_menu_item *cbox_menu_item_new_dynamic_menu(const char *label, create_menu_func func, void *item_context, uint32_t flags); -extern void cbox_menu_item_destroy(struct cbox_menu_item *); - -static inline struct cbox_menu_item *cbox_menu_item_new_ok(void) -{ - return cbox_menu_item_new_menu("OK", NULL, NULL, 0); -} - -#endif - -#endif diff --git a/template/calfbox/py/metadata.py b/template/calfbox/metadata.py similarity index 98% rename from template/calfbox/py/metadata.py rename to template/calfbox/metadata.py index 03e31db..2c42ddc 100644 --- a/template/calfbox/py/metadata.py +++ b/template/calfbox/metadata.py @@ -10,7 +10,10 @@ import base64 # for icons import os.path #get_thing -from calfbox._cbox2 import do_cmd +try: + from _cbox2 import do_cmd #local file _cbox2.py +except ModuleNotFoundError: + from ._cbox2 import do_cmd def get_thing(): pass #overwritten by cbox.py after import diff --git a/template/calfbox/meter.c b/template/calfbox/meter.c deleted file mode 100644 index 688aa42..0000000 --- a/template/calfbox/meter.c +++ /dev/null @@ -1,137 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "dspmath.h" -#include "errors.h" -#include "meter.h" -#include -#include -#include - -static void clear_meter(struct cbox_meter *m) -{ - for (int i = 0; i < 2; i++) - { - m->volume[i] = 0.f; - m->peak[i] = 0.f; - m->last_peak[i] = 0.f; - } - m->smpcounter = 0; -} - -gboolean cbox_meter_attach(struct cbox_recorder *handler, struct cbox_recording_source *src, GError **error) -{ - struct cbox_meter *m = handler->user_data; - m->channels = src->channels; - clear_meter(m); - return TRUE; -} - -void cbox_meter_record_block(struct cbox_recorder *handler, const float **buffers, uint32_t offset, uint32_t numsamples) -{ - struct cbox_meter *m = handler->user_data; - for (int c = 0; c < m->channels; c++) - { - float peak = m->peak[c]; - float volume = m->volume[c]; - for (uint32_t i = 0; i < numsamples; i++) - { - float s = buffers[c][i]; - if (fabs(s) > peak) - peak = fabs(s); - volume += (s * s - volume) * 0.01; // XXXKF this is too simplistic, needs sample rate and proper time constant - } - m->peak[c] = peak; - m->volume[c] = sanef(volume); - } - m->smpcounter += numsamples; - if (m->smpcounter > m->srate) - { - for (int c = 0; c < m->channels; c++) - { - m->last_peak[c] = m->peak[c]; - m->peak[c] = 0; - } - m->smpcounter = 0; - } -} - -gboolean cbox_meter_detach(struct cbox_recorder *handler, GError **error) -{ - struct cbox_meter *m = handler->user_data; - m->channels = 0; - clear_meter(m); - return TRUE; -} - -void cbox_meter_destroy(struct cbox_recorder *handler) -{ - struct cbox_meter *m = handler->user_data; - free(m); -} - -static gboolean cbox_meter_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct cbox_meter *m = ct->user_data; - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - return CBOX_OBJECT_DEFAULT_STATUS(&m->recorder, fb, error); - } - if (!strcmp(cmd->command, "/get_peak") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - float peak[2]; - for (int c = 0; c < 2; c++) - { - float v = m->peak[c], w = m->last_peak[c]; - if (v < w) - v = w; - peak[c] = v; - } - - return cbox_execute_on(fb, NULL, "/peak", "ff", error, peak[0], peak[1]); - } - else - if (!strcmp(cmd->command, "/get_rms") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - return cbox_execute_on(fb, NULL, "/rms", "ff", error, sqrt(m->volume[0]), sqrt(m->volume[1])); - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); -} - -struct cbox_meter *cbox_meter_new(struct cbox_document *document, int srate) -{ - struct cbox_meter *m = malloc(sizeof(struct cbox_meter)); - CBOX_OBJECT_HEADER_INIT(&m->recorder, cbox_recorder, document); - m->recorder.user_data = m; - cbox_command_target_init(&m->recorder.cmd_target, cbox_meter_process_cmd, m); - m->recorder.attach = cbox_meter_attach; - m->recorder.detach = cbox_meter_detach; - m->recorder.record_block = cbox_meter_record_block; - m->recorder.destroy = cbox_meter_destroy; - m->srate = srate; - clear_meter(m); - CBOX_OBJECT_REGISTER(&m->recorder); - return m; -} \ No newline at end of file diff --git a/template/calfbox/meter.h b/template/calfbox/meter.h deleted file mode 100644 index c2bdf3d..0000000 --- a/template/calfbox/meter.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_METER_H -#define CBOX_METER_H - -#include "recsrc.h" - -struct cbox_meter -{ - struct cbox_recorder recorder; - - float volume[2]; // lowpassed squared - float peak[2]; - float last_peak[2]; - int srate; - int channels; - int smpcounter; -}; - -extern struct cbox_meter *cbox_meter_new(struct cbox_document *document, int srate); - -#endif diff --git a/template/calfbox/midi.c b/template/calfbox/midi.c deleted file mode 100644 index 6e5371e..0000000 --- a/template/calfbox/midi.c +++ /dev/null @@ -1,113 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config-api.h" -#include "midi.h" -#include - -int cbox_midi_buffer_write_inline(struct cbox_midi_buffer *buffer, uint32_t time, ...) -{ - uint8_t buf[4]; - va_list va; - va_start(va, time); - buf[0] = va_arg(va, int); - int size = midi_cmd_size(buf[0]); - for (int i = 1; i < size; i++) - buf[i] = va_arg(va, int); - return cbox_midi_buffer_write_event(buffer, time, buf, size); -} - -int cbox_midi_buffer_write_event(struct cbox_midi_buffer *buffer, uint32_t time, uint8_t *data, uint32_t size) -{ - struct cbox_midi_event *evt; - - if (buffer->count >= CBOX_MIDI_MAX_EVENTS) - return 0; - if (size > 4 && size > CBOX_MIDI_MAX_LONG_DATA - buffer->long_data_size) - return 0; - evt = &buffer->events[buffer->count++]; - evt->time = time; - evt->size = size; - if (size <= 4) - { - memcpy(evt->data_inline, data, size); - } - else - { - evt->data_ext = buffer->long_data + buffer->long_data_size; - memcpy(evt->data_ext, data, size); - buffer->long_data_size += size; - } - return 1; -} - -int cbox_midi_buffer_copy_event(struct cbox_midi_buffer *buffer, const struct cbox_midi_event *event, int new_time) -{ - struct cbox_midi_event *evt; - - if (buffer->count >= CBOX_MIDI_MAX_EVENTS) - return 0; - if (event->size > 4 && event->size > CBOX_MIDI_MAX_LONG_DATA - buffer->long_data_size) - return 0; - evt = &buffer->events[buffer->count++]; - evt->time = new_time; - evt->size = event->size; - if (event->size <= 4) - { - memcpy(evt->data_inline, event->data_inline, event->size); - } - else - { - evt->data_ext = buffer->long_data + buffer->long_data_size; - memcpy(evt->data_ext, event->data_ext, event->size); - buffer->long_data_size += event->size; - } - return 1; -} - -int note_from_string(const char *note) -{ - static const int semis[] = {9, 11, 0, 2, 4, 5, 7}; - int pos; - int nn = tolower(note[0]); - int nv; - if (nn >= '0' && nn <= '9') - return atoi(note); - if (nn < 'a' && nn > 'g') - return -1; - nv = semis[nn - 'a']; - - for (pos = 1; note[pos] == 'b' || note[pos] == '#'; pos++) - nv += (note[pos] == 'b') ? -1 : +1; - - 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')) - { - return nv + 12 * (2 + atoi(note + pos)); - } - - return -1; -} - -int cbox_config_get_note(const char *cfg_section, const char *key, int def_value) -{ - const char *cv = cbox_config_get_string(cfg_section, key); - if (cv) - return note_from_string(cv); - return def_value; -} - diff --git a/template/calfbox/midi.h b/template/calfbox/midi.h deleted file mode 100644 index ec4ef8c..0000000 --- a/template/calfbox/midi.h +++ /dev/null @@ -1,125 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_MIDI_H -#define CBOX_MIDI_H - -#include -#include -#include -#include - -struct cbox_midi_event -{ - uint32_t time; - uint32_t size; - union { - uint8_t data_inline[4]; /* up to 4 bytes */ - uint8_t *data_ext; /* if larger than 4 bytes */ - }; -}; - -#define CBOX_MIDI_MAX_EVENTS 256 -#define CBOX_MIDI_MAX_LONG_DATA 256 - -struct cbox_midi_buffer -{ - uint32_t count; - uint32_t long_data_size; - struct cbox_midi_event events[CBOX_MIDI_MAX_EVENTS]; - uint8_t long_data[CBOX_MIDI_MAX_LONG_DATA]; -}; - -static inline void cbox_midi_buffer_init(struct cbox_midi_buffer *buffer) -{ - buffer->count = 0; - buffer->long_data_size = 0; -} - -static inline void cbox_midi_buffer_clear(struct cbox_midi_buffer *buffer) -{ - buffer->count = 0; - buffer->long_data_size = 0; -} - -static inline void cbox_midi_buffer_copy(struct cbox_midi_buffer *dst, const struct cbox_midi_buffer *src) -{ - dst->count = src->count; - dst->long_data_size = src->long_data_size; - memcpy(dst->events, src->events, src->count * sizeof(struct cbox_midi_event)); - memcpy(dst->long_data, src->long_data, src->long_data_size); - // for any long events, update data pointers - for (uint32_t i = 0; i < src->count; i++) - { - if (dst->events[i].size > 4) - dst->events[i].data_ext += &dst->long_data[0] - &src->long_data[0]; - } -} - -static inline uint32_t cbox_midi_buffer_get_count(struct cbox_midi_buffer *buffer) -{ - return buffer->count; -} - -static inline uint32_t cbox_midi_buffer_get_last_event_time(struct cbox_midi_buffer *buffer) -{ - if (!buffer->count) - return 0; - return buffer->events[buffer->count - 1].time; -} - -static inline int cbox_midi_buffer_can_store_msg(struct cbox_midi_buffer *buffer, int size) -{ - if (buffer->count >= CBOX_MIDI_MAX_EVENTS) - return 0; - if (size < 4) - return 1; - return buffer->long_data_size + size <= CBOX_MIDI_MAX_LONG_DATA; -} - -static inline const struct cbox_midi_event *cbox_midi_buffer_get_event(const struct cbox_midi_buffer *buffer, uint32_t pos) -{ - if (pos >= buffer->count) - return NULL; - return &buffer->events[pos]; -} - -static inline const uint8_t *cbox_midi_event_get_data(const struct cbox_midi_event *evt) -{ - return evt->size > 4 ? evt->data_ext : evt->data_inline; -} - -static inline int midi_cmd_size(uint8_t cmd) -{ - static const int sizes[] = { 3, 3, 3, 3, 2, 2, 3, 1 }; - if (cmd < 128) - return 0; - return sizes[(cmd >> 4) - 8]; -} - -extern int cbox_midi_buffer_write_event(struct cbox_midi_buffer *buffer, uint32_t time, uint8_t *data, uint32_t size); - -extern int cbox_midi_buffer_write_inline(struct cbox_midi_buffer *buffer, uint32_t time, ...); - -extern int cbox_midi_buffer_copy_event(struct cbox_midi_buffer *buffer, const struct cbox_midi_event *event, int new_time); - -extern int note_from_string(const char *note); - -extern int cbox_config_get_note(const char *cfg_section, const char *key, int def_value); - -#endif diff --git a/template/calfbox/mididest.c b/template/calfbox/mididest.c deleted file mode 100644 index f9b6a6b..0000000 --- a/template/calfbox/mididest.c +++ /dev/null @@ -1,254 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "blob.h" -#include "mididest.h" -#include "rt.h" -#include "stm.h" - -void cbox_midi_merger_init(struct cbox_midi_merger *dest, struct cbox_midi_buffer *output) -{ - dest->inputs = NULL; - dest->output = output; - if (dest->output) - cbox_midi_buffer_clear(dest->output); -} - -// void cbox_midi_buffer_merge(struct cbox_midi_buffer *output, struct cbox_midi_buffer **inputs, int count, int *positions) -void cbox_midi_merger_render_to(struct cbox_midi_merger *dest, struct cbox_midi_buffer *output) -{ - if (!output) - return; - cbox_midi_buffer_clear(output); - for (struct cbox_midi_source *p = dest->inputs; p; p = p->next) - { - if (p->streaming) - p->bpos = 0; - } - - struct cbox_midi_source *first = dest->inputs; - struct cbox_midi_source *first_not = NULL; - while(first) - { - struct cbox_midi_source *earliest_source = NULL; - uint32_t earliest_time = (uint32_t)-1; - - for (struct cbox_midi_source *p = first; p != first_not; p = p->next) - { - struct cbox_midi_buffer *data = p->data; - if (p->bpos < data->count) - { - const struct cbox_midi_event *event = cbox_midi_buffer_get_event(data, p->bpos); - if (event->time < earliest_time) - { - earliest_source = p; - earliest_time = event->time; - } - } - else - { - // Narrow down the range from top and bottom - if (p == first) - first = p->next; - if (p->next == first_not) - { - first_not = p; - break; - } - } - } - if (earliest_source) - { - cbox_midi_buffer_copy_event(output, cbox_midi_buffer_get_event(earliest_source->data, earliest_source->bpos), earliest_time); - earliest_source->bpos++; - } - else - break; - } -} - -struct cbox_midi_source **cbox_midi_merger_find_source(struct cbox_midi_merger *dest, struct cbox_midi_buffer *buffer) -{ - for (struct cbox_midi_source **pp = &dest->inputs; *pp; pp = &((*pp)->next)) - if ((*pp)->data == buffer) - return pp; - return NULL; -} - -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) -{ - if (cbox_midi_merger_find_source(dest, buffer) != NULL) - return; - - struct cbox_midi_source *src = calloc(1, sizeof(struct cbox_midi_source)); - src->data = buffer; - src->bpos = 0; - src->streaming = TRUE; - src->next = NULL; // will be updated by the swap - src->merger_ptr = dest_ptr; - if (src->merger_ptr) - *src->merger_ptr = dest; - cbox_rt_swap_pointers_into(rt, (void **)&dest->inputs, src, (void **)&src->next); -} - -void cbox_midi_merger_disconnect(struct cbox_midi_merger *dest, struct cbox_midi_buffer *buffer, struct cbox_rt *rt) -{ - // Make sure there are no old commands that could modify the chain - // between find_source and swap_pointers. - cbox_rt_handle_cmd_queue(rt); - - struct cbox_midi_source **pp = cbox_midi_merger_find_source(dest, buffer); - if (!pp) - return; - - struct cbox_midi_source *ms = *pp; - void *old_ptr = cbox_rt_swap_pointers(rt, (void **)pp, ms->next); - assert(old_ptr == ms); - if (ms->merger_ptr) - *ms->merger_ptr = NULL; - free(ms); -} - -void cbox_midi_merger_push(struct cbox_midi_merger *dest, struct cbox_midi_buffer *buffer, struct cbox_rt *rt) -{ - if (!buffer->count) - return; - assert(!cbox_midi_merger_find_source(dest, buffer)); - struct cbox_midi_source src; - src.data = buffer; - src.bpos = 0; - src.streaming = FALSE; - src.next = dest->inputs; - src.merger_ptr = NULL; - cbox_rt_swap_pointers_into(rt, (void **)&dest->inputs, &src, (void **)&src.next); - while(src.bpos < buffer->count) - cbox_rt_handle_cmd_queue(rt); - - struct cbox_midi_source **pp = cbox_midi_merger_find_source(dest, buffer); - if (!pp) - return; - assert(*pp == &src); - void *old_ptr = cbox_rt_swap_pointers(rt, (void **)pp, src.next); - assert(old_ptr == &src); -} - -void cbox_midi_merger_close(struct cbox_midi_merger *dest, struct cbox_rt *rt) -{ - struct cbox_midi_source *ms = cbox_rt_swap_pointers(rt, (void **)&dest->inputs, NULL); - while(ms) - { - struct cbox_midi_source *p = ms; - ms = p->next; - if (p->merger_ptr) - *p->merger_ptr = NULL; - free(p); - } -} - -//////////////////////////////////////////////////////////////////////////////////////// - -void cbox_midi_appsink_init(struct cbox_midi_appsink *appsink, struct cbox_rt *rt, struct cbox_time_mapper *tmap) -{ - appsink->rt = rt; - appsink->tmap = tmap; - cbox_midi_buffer_init(&appsink->midibufs[0]); - cbox_midi_buffer_init(&appsink->midibufs[1]); - appsink->current_buffer = 0; -} - -void cbox_midi_appsink_supply(struct cbox_midi_appsink *appsink, struct cbox_midi_buffer *buffer, uint32_t time_offset) -{ - struct cbox_midi_buffer *sinkbuf = &appsink->midibufs[appsink->current_buffer]; - for (uint32_t i = 0; i < buffer->count; i++) - { - const struct cbox_midi_event *event = cbox_midi_buffer_get_event(buffer, i); - if (event) - { - if (!cbox_midi_buffer_can_store_msg(sinkbuf, event->size)) - break; - uint32_t abs_time_samples = time_offset + event->time; - uint32_t etime = abs_time_samples; - if (appsink->tmap) - etime = appsink->tmap->map_time(appsink->tmap, etime); - cbox_midi_buffer_copy_event(sinkbuf, event, etime); - } - } -} - -#define cbox_midi_appsink_get_input_midi_data__args(ARG) - -DEFINE_RT_FUNC(const struct cbox_midi_buffer *, cbox_midi_appsink, appsink, cbox_midi_appsink_get_input_midi_data_) -{ - const struct cbox_midi_buffer *ret = NULL; - if (appsink->midibufs[appsink->current_buffer].count) - { - // return the current buffer, switch to the new, empty one - ret = &appsink->midibufs[appsink->current_buffer]; - appsink->current_buffer = 1 - appsink->current_buffer; - cbox_midi_buffer_clear(&appsink->midibufs[appsink->current_buffer]); - } - - return ret; -} - -const struct cbox_midi_buffer *cbox_midi_appsink_get_input_midi_data(struct cbox_midi_appsink *appsink) -{ - // This checks the counter from the 'wrong' thread, but that's OK, it's - // just to avoid doing any RT work when input buffer is completely empty. - // Any further access/manipulation is done via RT cmd. - if (!appsink->midibufs[appsink->current_buffer].count) - return NULL; - return cbox_midi_appsink_get_input_midi_data_(appsink); -} - -gboolean cbox_midi_appsink_send_to(struct cbox_midi_appsink *appsink, struct cbox_command_target *fb, GError **error) -{ - const struct cbox_midi_buffer *midi_in = cbox_midi_appsink_get_input_midi_data(appsink); - // If no feedback, the input events are lost - probably better than if - // they filled up the input buffer needlessly. - if (fb && midi_in) - { - for (uint32_t i = 0; i < midi_in->count; i++) - { - const struct cbox_midi_event *event = cbox_midi_buffer_get_event(midi_in, i); - const uint8_t *data = cbox_midi_event_get_data(event); - uint32_t time = event->time & 0x7FFFFFFF; - uint32_t time_type = event->time >> 31; - if (time_type == 0 && !cbox_execute_on(fb, NULL, "/io/midi/event_time_samples", "i", error, time)) - return FALSE; - if (time_type == 1 && !cbox_execute_on(fb, NULL, "/io/midi/event_time_ppqn", "i", error, time)) - return FALSE; - // XXXKF doesn't handle SysEx properly yet, only 3-byte values - if (event->size <= 3) - { - if (!cbox_execute_on(fb, NULL, "/io/midi/simple_event", "iii" + (3 - event->size), error, data[0], data[1], data[2])) - return FALSE; - } - else - { - struct cbox_blob blob; - blob.data = (uint8_t *)data; - blob.size = event->size; - if (!cbox_execute_on(fb, NULL, "/io/midi/long_event", "b", error, &blob)) - return FALSE; - } - } - } - return TRUE; -} - diff --git a/template/calfbox/mididest.h b/template/calfbox/mididest.h deleted file mode 100644 index 079a43e..0000000 --- a/template/calfbox/mididest.h +++ /dev/null @@ -1,76 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_MIDIDEST_H -#define CBOX_MIDIDEST_H - -#include "midi.h" -#include - -struct cbox_command_target; -struct cbox_rt; - -struct cbox_midi_source -{ - struct cbox_midi_source *next; - struct cbox_midi_buffer *data; - uint32_t bpos; - gboolean streaming; - struct cbox_midi_merger **merger_ptr; -}; - -struct cbox_midi_merger -{ - struct cbox_midi_source *inputs; - struct cbox_midi_buffer *output; -}; - -void cbox_midi_merger_init(struct cbox_midi_merger *dest, struct cbox_midi_buffer *output); -void cbox_midi_merger_render_to(struct cbox_midi_merger *dest, struct cbox_midi_buffer *output); -static inline void cbox_midi_merger_render(struct cbox_midi_merger *dest) -{ - if (dest->output) - cbox_midi_merger_render_to(dest, dest->output); -} -struct cbox_midi_source **cbox_midi_merger_find_source(struct cbox_midi_merger *dest, struct cbox_midi_buffer *buffer); -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); -void cbox_midi_merger_disconnect(struct cbox_midi_merger *dest, struct cbox_midi_buffer *buffer, struct cbox_rt *rt); -void cbox_midi_merger_push(struct cbox_midi_merger *dest, struct cbox_midi_buffer *buffer, struct cbox_rt *rt); -void cbox_midi_merger_close(struct cbox_midi_merger *dest, struct cbox_rt *rt); - -struct cbox_time_mapper -{ - uint32_t (*map_time)(struct cbox_time_mapper *, uint32_t free_running_counter); -}; - -#define GET_RT_FROM_cbox_midi_appsink(appsink) ((appsink)->rt) - -struct cbox_midi_appsink -{ - struct cbox_rt *rt; - struct cbox_time_mapper *tmap; - struct cbox_midi_buffer midibufs[2]; - int current_buffer; -}; - -extern void cbox_midi_appsink_init(struct cbox_midi_appsink *appsink, struct cbox_rt *rt, struct cbox_time_mapper *tmap); -extern void cbox_midi_appsink_supply(struct cbox_midi_appsink *appsink, struct cbox_midi_buffer *buffer, uint32_t time_offset); -extern const struct cbox_midi_buffer *cbox_midi_appsink_get_input_midi_data(struct cbox_midi_appsink *appsink); -extern gboolean cbox_midi_appsink_send_to(struct cbox_midi_appsink *appsink, struct cbox_command_target *fb, GError **error); - -#endif diff --git a/template/calfbox/module.c b/template/calfbox/module.c deleted file mode 100644 index 5c5986c..0000000 --- a/template/calfbox/module.c +++ /dev/null @@ -1,270 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "cmd.h" -#include "config-api.h" -#include "engine.h" -#include "module.h" -#include "rt.h" - -#include -#include -#include -#include -#include -#include - -extern struct cbox_module_manifest sampler_module; -extern struct cbox_module_manifest fluidsynth_module; -extern struct cbox_module_manifest tonewheel_organ_module; -extern struct cbox_module_manifest stream_player_module; -extern struct cbox_module_manifest tone_control_module; -extern struct cbox_module_manifest delay_module; -extern struct cbox_module_manifest reverb_module; -extern struct cbox_module_manifest parametric_eq_module; -extern struct cbox_module_manifest phaser_module; -extern struct cbox_module_manifest chorus_module; -extern struct cbox_module_manifest fxchain_module; -extern struct cbox_module_manifest jack_input_module; -extern struct cbox_module_manifest feedback_reducer_module; -extern struct cbox_module_manifest compressor_module; -extern struct cbox_module_manifest gate_module; -extern struct cbox_module_manifest distortion_module; -extern struct cbox_module_manifest fuzz_module; -extern struct cbox_module_manifest limiter_module; - -struct cbox_module_manifest *cbox_module_list[] = { - &tonewheel_organ_module, -#if USE_FLUIDSYNTH - &fluidsynth_module, -#endif - &stream_player_module, - &tone_control_module, - &delay_module, - &reverb_module, - ¶metric_eq_module, - &phaser_module, - &chorus_module, - &sampler_module, - &fxchain_module, -#if USE_JACK - &jack_input_module, -#endif - &feedback_reducer_module, - &compressor_module, - &gate_module, - &distortion_module, - &fuzz_module, - &limiter_module, - NULL -}; - -CBOX_CLASS_DEFINITION_ROOT(cbox_module) - -void cbox_module_manifest_dump(struct cbox_module_manifest *manifest) -{ - static const char *ctl_classes[] = { "Switch CC#", "Continuous CC#", "Cont. Param", "Discrete Param", "Enum" }; - int i = 0; - printf("Module: %s\n", manifest->name); - printf("Audio I/O: min %d inputs, min %d outputs\n", manifest->min_inputs, manifest->min_outputs); - - printf("Live controllers:\n"); - printf("Ch# Type Number Name \n"); - printf("---- --------------- ------ ------------------------------\n"); - for (i = 0; i < manifest->num_live_controllers; i++) - { - struct cbox_module_livecontroller_metadata *lc = &manifest->live_controllers[i]; - if (lc->channel == 255) - printf("ALL "); - else - if (!lc->channel) - printf("ANY "); - else - printf("%-4d ", lc->channel); - printf("%15s %-6d %-30s\n", ctl_classes[lc->controller_class], lc->controller, lc->name); - } -} - -struct cbox_module_manifest *cbox_module_manifest_get_by_name(const char *name) -{ - struct cbox_module_manifest **mptr; - - for (mptr = cbox_module_list; *mptr; mptr++) - { - if (!strcmp((*mptr)->name, name)) - return *mptr; - } - return NULL; -} - -struct cbox_module *cbox_module_manifest_create_module(struct cbox_module_manifest *manifest, const char *cfg_section, struct cbox_document *doc, struct cbox_rt *rt, struct cbox_engine *engine, const char *instance_name, GError **error) -{ - g_clear_error(error); - struct cbox_module *module = manifest->create(manifest->user_data, cfg_section, doc, rt, engine, error); - if (!module) - return NULL; - - module->instance_name = g_strdup(instance_name); - module->input_samples = malloc(sizeof(float) * CBOX_BLOCK_SIZE * module->inputs); - module->output_samples = malloc(sizeof(float) * CBOX_BLOCK_SIZE * module->outputs); - module->engine_name = manifest->name; - cbox_midi_buffer_init(&module->midi_input); - - return module; -} - -void cbox_module_init(struct cbox_module *module, struct cbox_document *doc, struct cbox_rt *rt, struct cbox_engine *engine, void *user_data, int inputs, int outputs, cbox_process_cmd cmd_handler, void (*destroy)(struct cbox_module *module)) -{ - CBOX_OBJECT_HEADER_INIT(module, cbox_module, doc); - module->user_data = user_data; - module->rt = rt; - module->engine = engine; - module->instance_name = NULL; - module->input_samples = NULL; - module->output_samples = NULL; - module->inputs = inputs; - module->outputs = outputs; - module->aux_offset = outputs; - module->bypass = 0; - module->srate = engine->io_env.srate; - module->srate_inv = 1.0 / module->srate; - - cbox_command_target_init(&module->cmd_target, cmd_handler, module); - module->process_event = NULL; - module->process_block = NULL; - module->destroy = destroy; - CBOX_OBJECT_REGISTER(module); -} - -struct cbox_module *cbox_module_new_from_fx_preset(const char *name, struct cbox_document *doc, struct cbox_rt *rt, struct cbox_engine *engine, GError **error) -{ - gchar *section = g_strdup_printf("fxpreset:%s", name); - const char *engine_name; - struct cbox_module_manifest *mptr; - struct cbox_module *effect; - - if (!cbox_config_has_section(section)) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "No FX preset called '%s'", name); - goto fxpreset_error; - } - engine_name = cbox_config_get_string(section, "engine"); - if (!engine_name) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "FX engine not specified for preset '%s'", name); - goto fxpreset_error; - } - mptr = cbox_module_manifest_get_by_name(engine_name); - if (!mptr) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "FX preset '%s' refers to non-existing engine '%s'", name, engine_name); - goto fxpreset_error; - } - effect = cbox_module_manifest_create_module(mptr, section, doc, rt, engine, name, error); - if (!effect) - { - cbox_force_error(error); - g_prefix_error(error, "Could not instantiate FX preset '%s': ", name); - goto fxpreset_error; - } - g_free(section); - return effect; - -fxpreset_error: - g_free(section); - return NULL; -} - -gboolean cbox_module_slot_process_cmd(struct cbox_module **psm, - struct cbox_command_target *fb, struct cbox_osc_command *cmd, const char *subcmd, - struct cbox_document *doc, struct cbox_rt *rt, struct cbox_engine *engine, GError **error) -{ - struct cbox_module *sm = *psm; - if (!strcmp(subcmd, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - if (!(cbox_execute_on(fb, NULL, "/insert_engine", "s", error, sm ? sm->engine_name : "") && - cbox_execute_on(fb, NULL, "/insert_preset", "s", error, sm ? sm->instance_name : "") && - cbox_execute_on(fb, NULL, "/bypass", "i", error, sm ? sm->bypass : 0))) - return FALSE; - return TRUE; - } - if (!strcmp(subcmd, "/insert_preset") && !strcmp(cmd->arg_types, "s")) - { - struct cbox_module *effect = cbox_module_new_from_fx_preset(CBOX_ARG_S(cmd, 0), doc, rt, engine, error); - if (!effect) - return FALSE; - cbox_rt_swap_pointers(rt, (void **)psm, effect); - return TRUE; - } - if (!strcmp(subcmd, "/insert_engine") && !strcmp(cmd->arg_types, "s")) - { - struct cbox_module *effect = NULL; - if (*CBOX_ARG_S(cmd, 0)) - { - struct cbox_module_manifest *manifest = cbox_module_manifest_get_by_name(CBOX_ARG_S(cmd, 0)); - if (!manifest) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "No effect engine '%s'", CBOX_ARG_S(cmd, 0)); - return FALSE; - } - effect = cbox_module_manifest_create_module(manifest, NULL, doc, rt, engine, "unnamed", error); - if (!effect) - return FALSE; - } - cbox_rt_swap_pointers(rt, (void **)psm, effect); - return TRUE; - } - if (!sm) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "No engine on module in path '%s'", cmd->command); - return FALSE; - } - if (!strncmp(subcmd, "/engine/", 8)) - { - if (!sm->cmd_target.process_cmd) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "The engine %s has no command target defined", sm->engine_name); - return FALSE; - } - return cbox_execute_sub(&sm->cmd_target, fb, cmd, subcmd + 7, error); - } - if (!strcmp(subcmd, "/set_bypass") && !strcmp(cmd->arg_types, "i")) - { - sm->bypass = CBOX_ARG_I(cmd, 0); - return TRUE; - } - return cbox_object_default_process_cmd(&sm->cmd_target, fb, cmd, error); -} - -void cbox_module_swap_pointers_and_free(struct cbox_module *sm, void **pptr, void *value) -{ - free(cbox_rt_swap_pointers(sm->rt, pptr, value)); -} - -void cbox_module_destroyfunc(struct cbox_objhdr *hdr) -{ - struct cbox_module *module = CBOX_H2O(hdr); - g_free(module->instance_name); - free(module->input_samples); - free(module->output_samples); - if (module->destroy) - module->destroy(module); - free(module); -} diff --git a/template/calfbox/module.h b/template/calfbox/module.h deleted file mode 100644 index 37b8134..0000000 --- a/template/calfbox/module.h +++ /dev/null @@ -1,181 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_MODULE_H -#define CBOX_MODULE_H - -#include "dom.h" -#include "dspmath.h" -#include "errors.h" -#include "midi.h" - -#include - -CBOX_EXTERN_CLASS(cbox_module) - -#define CBOX_MAX_AUDIO_PORTS 36 - -struct cbox_engine; -struct cbox_rt; - -struct cbox_module_keyrange_metadata -{ - uint8_t channel; // 0 = omni - uint8_t low_key; - uint8_t high_key; - const char *name; -}; - -enum cbox_module_livecontroller_class -{ - cmlc_onoffcc, - cmlc_continuouscc, - cmlc_continuous, - cmlc_discrete, - cmlc_enum -}; - -struct cbox_module_livecontroller_metadata -{ - uint8_t channel; - enum cbox_module_livecontroller_class controller_class:8; - uint16_t controller; - const char *name; - void *extra_info; -}; - -struct cbox_module_voicingparam_metadata -{ -}; - -struct cbox_module -{ - CBOX_OBJECT_HEADER() - void *user_data; - struct cbox_rt *rt; - struct cbox_engine *engine; - const char *engine_name; - gchar *instance_name; - cbox_sample_t *input_samples; - cbox_sample_t *output_samples; - struct cbox_midi_buffer midi_input; - uint32_t inputs, outputs, aux_offset; - int bypass; - int srate; - double srate_inv; - - struct cbox_command_target cmd_target; - - void (*process_event)(struct cbox_module *module, const uint8_t *data, uint32_t len); - void (*process_block)(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs); - void (*destroy)(struct cbox_module *module); -}; - -struct cbox_module_manifest -{ - void *user_data; - const char *name; - int min_inputs; - int min_outputs; - - struct cbox_module_keyrange_metadata *keyranges; - int num_keyranges; - - struct cbox_module_livecontroller_metadata *live_controllers; - int num_live_controllers; - - struct cbox_module_voicingparam_metadata *voicing_params; - int num_voicing_params; - - struct cbox_module *(*create)(void *user_data, const char *cfg_section, struct cbox_document *doc, struct cbox_rt *rt, struct cbox_engine *engine, GError **error); -}; - -#define DEFINE_MODULE(modname, ninputs, noutputs) \ - struct cbox_module_manifest modname##_module = { \ - NULL, \ - .name = #modname, \ - .min_inputs = ninputs, \ - .min_outputs = noutputs, \ - .keyranges = modname##_keyranges, \ - .num_keyranges = sizeof(modname##_keyranges)/sizeof(modname##_keyranges[0]), \ - .live_controllers = modname##_controllers, \ - .num_live_controllers = sizeof(modname##_controllers)/sizeof(modname##_controllers[0]), \ - .create = modname##_create \ - }; - -extern struct cbox_module_manifest *cbox_module_list[]; - -extern void cbox_module_manifest_dump(struct cbox_module_manifest *manifest); -extern struct cbox_module_manifest *cbox_module_manifest_get_by_name(const char *name); -extern struct cbox_module *cbox_module_manifest_create_module(struct cbox_module_manifest *manifest, const char *cfg_section, struct cbox_document *doc, struct cbox_rt *rt, struct cbox_engine *engine, const char *instance_name, GError **error); - -extern struct cbox_module *cbox_module_new_from_fx_preset(const char *name, struct cbox_document *doc, struct cbox_rt *rt, struct cbox_engine *engine, GError **error); - -extern void cbox_module_init(struct cbox_module *module, struct cbox_document *doc, struct cbox_rt *rt, struct cbox_engine *engine, void *user_data, int inputs, int outputs, cbox_process_cmd cmd_handler, void (*destroy)(struct cbox_module *module)); -extern void cbox_module_swap_pointers_and_free(struct cbox_module *sm, void **pptr, void *value); - -extern gboolean cbox_module_slot_process_cmd(struct cbox_module **psm, struct cbox_command_target *fb, struct cbox_osc_command *cmd, const char *subcmd, struct cbox_document *doc, struct cbox_rt *rt, struct cbox_engine *engine, GError **error); - -#define EFFECT_PARAM_CLONE(res) \ - struct MODULE_PARAMS *res = malloc(sizeof(struct MODULE_PARAMS)); \ - memcpy(res, m->params, sizeof(struct MODULE_PARAMS)); \ - -#define EFFECT_PARAM(path, type, field, ctype, expr, minv, maxv) \ - if (!strcmp(cmd->command, path) && !strcmp(cmd->arg_types, type)) \ - { \ - ctype value = *(ctype *)cmd->arg_values[0]; \ - if (value < minv || value > maxv) \ - return cbox_set_range_error(error, path, minv, maxv);\ - EFFECT_PARAM_CLONE(pp); \ - pp->field = expr(value); \ - cbox_module_swap_pointers_and_free(&m->module, (void **)&m->params, pp); \ - } \ - -#define EFFECT_PARAM_ARRAY(path, type, array, field, ctype, expr, minv, maxv) \ - if (!strcmp(cmd->command, path) && !strcmp(cmd->arg_types, "i" type)) \ - { \ - int pos = *(int *)cmd->arg_values[0]; \ - ctype value = *(ctype *)cmd->arg_values[1]; \ - if (value < minv || value > maxv) \ - return cbox_set_range_error(error, path, minv, maxv);\ - EFFECT_PARAM_CLONE(pp); \ - pp->array[pos].field = expr(value); \ - cbox_module_swap_pointers_and_free(&m->module, (void **)&m->params, pp); \ - } \ - -#define MODULE_CREATE_FUNCTION(module) \ - struct cbox_module *module##_create(void *user_data, const char *cfg_section, struct cbox_document *doc, struct cbox_rt *rt, struct cbox_engine *engine, GError **error) - -#define MODULE_PROCESSCMD_FUNCTION(module) \ - gboolean module##_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) - -#define MODULE_SIMPLE_DESTROY_FUNCTION(module) \ - static void module##_destroyfunc(struct cbox_module *module_) \ - { \ - struct module##_module *m = (struct module##_module *)module_; \ - free(m->params); \ - } - -#define CALL_MODULE_INIT(m, inputs, outputs, name) \ - cbox_module_init(&(m)->module, doc, rt, engine, (m), inputs, outputs, name##_process_cmd, name##_destroyfunc); - -#define CALL_MODULE_INIT_SIMPLE(m, inputs, outputs) \ - cbox_module_init(&(m)->module, doc, rt, engine, (m), inputs, outputs, NULL, NULL); - - -#endif diff --git a/template/calfbox/py/nocturn.py b/template/calfbox/nocturn.py similarity index 100% rename from template/calfbox/py/nocturn.py rename to template/calfbox/nocturn.py diff --git a/template/calfbox/novabox.py b/template/calfbox/novabox.py deleted file mode 100644 index e0e23e3..0000000 --- a/template/calfbox/novabox.py +++ /dev/null @@ -1,107 +0,0 @@ -# A primitive drum machine interface for Novation Nocturn. -# -# Usage: -# - make sure that Novation Nocturn is connected *and* that the USB device -# that corresponds to it can be opened by the current user -# - create an ini file containing a scene with a single instrument using -# a sampler or fluidsynth engine + some drum mappings in SFZ or SF2 -# - ensure that the ini file contains 7 drum patterns, pat1..pat7, these -# can be copied from cboxrc-example -# - user buttons 1..7 start drum patterns -# - user button 8 stops the playback -# - encoder knob 1 adjusts the volume -# - mixer button exits the application - -import math -import sys - -sys.path = ["./py"] + sys.path - -import nocturn -import cbox - -quit = False - -instr_name = cbox.Document.get_scene().status().instruments.keys()[0] - -def clamp(val, min, max): - if val < min: - val = min - elif val > max: - val = max - return val - -class NovaBox: - def __init__(self): - self.nocturn = nocturn.Nocturn() - self.cur_pattern = None - self.handlers = {} - self.handlers[83] = self.on_xfade_touch - for i in range(7): - self.handlers[112 + i] = lambda cmd, val: self.on_buttonN_press(cmd - 112) if val > 0 else None - self.handlers[112 + 7] = lambda cmd, val: self.on_button8_press() if val > 0 else None - self.handlers[64] = self.on_knob1_change - self.handlers[65] = self.on_knob2_change - self.handlers[127] = lambda cmd, val: self.on_mixer_press() if val > 0 else None - - def on_knob1_change(self, cmd, val): - scene = cbox.Document.get_scene() - instr = scene.status().instruments[instr_name][1] - gain = instr.get_things('/output/1/status', ['gain']).gain - if val > 63: - val = -128 + val - instr.cmd('/output/1/gain', None, gain + val * 0.5) - - def on_knob2_change(self, cmd, val): - tempo = cbox.GetThings("/master/status", ['tempo'], []).tempo - if val > 63: - val = -128 + val - tempo = clamp(tempo + val * 0.5, 30, 300) - cbox.do_cmd('/master/set_tempo', None, [tempo]) - - def on_buttonN_press(self, button): - cbox.do_cmd("/master/stop", None, []) - song = cbox.Document.get_song() - song.loop_single_pattern(lambda: song.load_drum_pattern('pat%d' % (button + 1))) - cbox.do_cmd("/master/seek_ppqn", None, [0]) - cbox.do_cmd("/master/play", None, []) - self.cur_pattern = button - - def on_button8_press(self): - self.cur_pattern = None - cbox.do_cmd("/master/stop", None, []) - - def on_mixer_press(self): - global quit - quit = True - - def on_xfade_touch(self, cmd, val): - if val > 0: - print "Do not touch" - - def handler(self, cmd, val): - if cmd in self.handlers: - self.handlers[cmd](cmd, val) - return - - def poll(self): - self.nocturn.poll(self.handler) - - def update(self): - scene = cbox.Document.get_scene() - cmds = nocturn.NocturnCommands() - master = cbox.GetThings("/master/status", ['playing'], []) - for i in range(7): - cmds.setModeButtonLight(i, self.cur_pattern == i) - gain = scene.status().instruments[instr_name][1].get_things('/output/1/status', ['gain']).gain - cmds.setEncoderMode(0, 0) - cmds.setEncoderValue(0, clamp(int(gain * 2 + 64), 0, 127)) - - cmds.setModeButtonLight(7, self.cur_pattern is None) - self.nocturn.execute(cmds) - -nb = NovaBox() -while not quit: - nb.poll() - nb.update() -nb.nocturn.reset() diff --git a/template/calfbox/py/nullbox.py b/template/calfbox/nullbox.py similarity index 86% rename from template/calfbox/py/nullbox.py rename to template/calfbox/nullbox.py index f389766..38302c4 100644 --- a/template/calfbox/py/nullbox.py +++ b/template/calfbox/nullbox.py @@ -3,7 +3,9 @@ class NullCalfbox(str): #iterable Use this for testing and development. At the start of your program, first file, insert: - import calfbox.nullbox + import nullbox + or + from SOMETHING import nullbox All further from calfbox import cbox @@ -53,8 +55,13 @@ class NullCalfbox(str): #iterable import sys -import calfbox.nullbox -sys.modules["calfbox"] = sys.modules["calfbox.nullbox"] +import nullbox +#Hack 'from calfbox import cbox' +sys.modules["calfbox"] = sys.modules["nullbox"] import calfbox cbox = NullCalfbox("fake cbox null client") + +#Hack direct call 'import cbox' +sys.modules["cbox"] = cbox +import cbox diff --git a/template/calfbox/onepole-float.h b/template/calfbox/onepole-float.h deleted file mode 100644 index 291ce72..0000000 --- a/template/calfbox/onepole-float.h +++ /dev/null @@ -1,205 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_ONEPOLE_FLOAT_H -#define CBOX_ONEPOLE_FLOAT_H - -#include "dspmath.h" - -struct cbox_onepolef_state -{ - float x1; - float y1; -}; - -struct cbox_onepolef_coeffs -{ - float a0; - float a1; - float b1; -}; - -static inline void cbox_onepolef_reset(struct cbox_onepolef_state *state) -{ - state->x1 = state->y1 = 0.f; -} - -static inline void cbox_onepolef_set_lowpass(struct cbox_onepolef_coeffs *coeffs, float w) -{ - float x = tan (w * 0.5f); - float q = 1 / (1 + x); - float a01 = x*q; - float b1 = a01 - q; - - coeffs->a0 = a01; - coeffs->a1 = a01; - coeffs->b1 = b1; -} - -static inline void cbox_onepolef_set_highpass(struct cbox_onepolef_coeffs *coeffs, float w) -{ - float x = tan (w * 0.5f); - float q = 1 / (1 + x); - float a01 = x*q; - float b1 = a01 - q; - - coeffs->a0 = q; - coeffs->a1 = -q; - coeffs->b1 = b1; -} - -static inline void cbox_onepolef_set_highshelf_tonectl(struct cbox_onepolef_coeffs *coeffs, float w, float g0) -{ - float x = tan (w * 0.5f); - float q = 1 / (1 + x); - float b1 = x * q - q; - - coeffs->a0 = 0.5 * (1 + b1 + g0 - b1 * g0); - coeffs->a1 = 0.5 * (1 + b1 - g0 + b1 * g0); - coeffs->b1 = b1; -} - -static inline void cbox_onepolef_set_highshelf_setgain(struct cbox_onepolef_coeffs *coeffs, float g0) -{ - coeffs->a0 = 0.5 * (1 + coeffs->b1 + g0 - coeffs->b1 * g0); - coeffs->a1 = 0.5 * (1 + coeffs->b1 - g0 + coeffs->b1 * g0); -} - -static inline void cbox_onepolef_set_allpass(struct cbox_onepolef_coeffs *coeffs, float w) -{ - float x = tan (w * 0.5f); - float q = 1 / (1 + x); - float a01 = x*q; - float b1 = a01 - q; - - coeffs->a0 = b1; - coeffs->a1 = 1; - coeffs->b1 = b1; -} - -static inline float cbox_onepolef_process_sample(struct cbox_onepolef_state *state, struct cbox_onepolef_coeffs *coeffs, float in) -{ - float out = sanef(coeffs->a0 * in + coeffs->a1 * state->x1 - coeffs->b1 * state->y1); - - state->x1 = in; - state->y1 = out; - return out; -} - -#if USE_NEON_NOTREALLYFASTER - -#include - -static inline void cbox_onepolef_process(struct cbox_onepolef_state *state, struct cbox_onepolef_coeffs *coeffs, float *buffer) -{ - int i; - float a0 = coeffs->a0; - float a1 = coeffs->a1; - float b1 = coeffs->b1; - float32x2_t a00 = {1, a0}; - float32x2_t ab1 = {a1, -b1}; - float32x2_t xy = {state->x1, state->y1}; - float32x2_t zero = {0, 0}; - - for (i = 0; i < CBOX_BLOCK_SIZE; i++) - { - float32x2_t inin = vdup_n_f32(buffer[i]); // {in, in} - float32x2_t xymul = vmul_f32(ab1, xy); // {x1 * a1, y1 * b1} - xymul = vpadd_f32(zero, xymul); // {0, x1 * a1 + y1 * b1} - xy = vmla_f32(xymul, inin, a00); // {in, a0 * in + a1 * x1 + b1 * y1} - - buffer[i] = xy[1]; - } - state->x1 = xy[0]; - state->y1 = sanef(xy[1]); -} - -#else -static inline void cbox_onepolef_process(struct cbox_onepolef_state *state, struct cbox_onepolef_coeffs *coeffs, float *buffer) -{ - int i; - float a0 = coeffs->a0; - float a1 = coeffs->a1; - float b1 = coeffs->b1; - float x1 = state->x1; - float y1 = state->y1; - - for (i = 0; i < CBOX_BLOCK_SIZE; i++) - { - float in = buffer[i]; - double out = a0 * in + a1 * x1 - b1 * y1; - - buffer[i] = out; - x1 = in; - y1 = out; - } - state->x1 = x1; - state->y1 = sanef(y1); -} -#endif - -static inline void cbox_onepolef_process_stereo(struct cbox_onepolef_state *lstate, struct cbox_onepolef_state *rstate, struct cbox_onepolef_coeffs *coeffs, float *buffer) -{ - int i; - float a0 = coeffs->a0; - float a1 = coeffs->a1; - float b1 = coeffs->b1; - float lx1 = lstate->x1; - float ly1 = lstate->y1; - float rx1 = rstate->x1; - float ry1 = rstate->y1; - - for (i = 0; i < 2 * CBOX_BLOCK_SIZE; i += 2) - { - float inl = buffer[i], inr = buffer[i + 1]; - double outl = a0 * inl + a1 * lx1 - b1 * ly1; - double outr = a0 * inr + a1 * rx1 - b1 * ry1; - - buffer[i] = outl; - buffer[i + 1] = outr; - lx1 = inl; - ly1 = outl; - rx1 = inr; - ry1 = outr; - } - lstate->x1 = lx1; - lstate->y1 = sanef(ly1); - rstate->x1 = rx1; - rstate->y1 = sanef(ry1); -} - -static inline void cbox_onepolef_process_to(struct cbox_onepolef_state *state, struct cbox_onepolef_coeffs *coeffs, float *buffer_in, float *buffer_out) -{ - int i; - float a0 = coeffs->a0; - float a1 = coeffs->a1; - float b1 = coeffs->b1; - - for (i = 0; i < CBOX_BLOCK_SIZE; i++) - { - float in = buffer_in[i]; - double out = a0 * in + a1 * state->x1 - b1 * state->y1; - - buffer_out[i] = out; - state->x1 = in; - state->y1 = out; - } - state->y1 = sanef(state->y1); -} - -#endif diff --git a/template/calfbox/onepole-int.h b/template/calfbox/onepole-int.h deleted file mode 100644 index 41ebf27..0000000 --- a/template/calfbox/onepole-int.h +++ /dev/null @@ -1,120 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_ONEPOLE_INT_H -#define CBOX_ONEPOLE_INT_H - -#include "dspmath.h" - -struct cbox_onepole_state -{ - int32_t x1; - int32_t y1; -}; - -struct cbox_onepole_coeffs -{ - int32_t a0; - int32_t a1; - int32_t b1; - int shift; -}; - -static inline void cbox_onepole_reset(struct cbox_onepole_state *state) -{ - state->x1 = state->y1 = 0; -} - -static inline void cbox_onepole_set_lowpass(struct cbox_onepole_coeffs *coeffs, float w) -{ - float x = tan (w); - float q = 1 / (1 + x); - float a01 = x*q; - float b1 = a01 - q; - int shift = 28; - float scaler = (1 << shift); - - coeffs->a1 = coeffs->a0 = (int32_t)(a01 * scaler); - coeffs->b1 = (int32_t)(b1 * scaler); - coeffs->shift = shift; -} - -static inline void cbox_onepole_set_highpass(struct cbox_onepole_coeffs *coeffs, float w) -{ - float x = tan (w); - float q = 1 / (1 + x); - float a01 = x*q; - float b1 = a01 - q; - int shift = 28; - float scaler = (1 << shift)-1; - - coeffs->a0 = (int32_t)(a01 * scaler); - coeffs->a1 = -coeffs->a0; - coeffs->b1 = (int32_t)(b1 * scaler); - coeffs->shift = shift; -} - -static inline void cbox_onepole_process(struct cbox_onepole_state *state, struct cbox_onepole_coeffs *coeffs, int32_t *buffer) -{ - int i; - int64_t a0 = coeffs->a0; - int64_t a1 = coeffs->a1; - int64_t b1 = coeffs->b1; - int shift = coeffs->shift; - int64_t maxint = ((int64_t)0x7FFFFFF) << shift; - int32_t round = 1 << (shift - 1); - - for (i = 0; i < CBOX_BLOCK_SIZE; i++) - { - int32_t in = buffer[i]; - int64_t v = a0 * in + a1 * state->x1 - b1 * state->y1 + round; - int32_t out = (llabs(v) >= maxint) ? (v > 0 ? 0x7FFFFFFF : -0x7FFFFFFF) : (v >> shift); - - buffer[i] = out; - state->x1 = in; - state->y1 = out; - } - if (state->y1 > 0 && state->y1 < round) - state->y1--; - if (state->y1 < 0 && state->y1 > -round) - state->y1++; -} - -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) -{ - int i; - int64_t a0 = coeffs->a0; - int64_t a1 = coeffs->a1; - int64_t b1 = coeffs->b1; - int shift = coeffs->shift; - int64_t maxint = ((int64_t)0x7FFFFFF) << shift; - int64_t round = 1 << (shift - 1); - - for (i = 0; i < CBOX_BLOCK_SIZE; i++) - { - int32_t in = buffer_in[i]; - int64_t v = a0 * in + a1 * state->x1 - b1 * state->y1 + round; - int32_t out = (llabs(v) >= maxint) ? (v > 0 ? 0x7FFFFFFF : -0x7FFFFFFF) : (v >> shift); - - buffer_out[i] = out; - state->x1 = in; - state->y1 = out; - } -} - -#endif diff --git a/template/calfbox/pattern-maker.c b/template/calfbox/pattern-maker.c deleted file mode 100644 index 3b884c6..0000000 --- a/template/calfbox/pattern-maker.c +++ /dev/null @@ -1,183 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config.h" -#include "errors.h" -#include "pattern.h" -#include "pattern-maker.h" -#include "song.h" -#include -#if USE_LIBSMF -#include -#endif - -struct event_entry -{ - uint32_t time; - uint8_t data[4]; -}; - -static gint event_entry_compare(gconstpointer a, gconstpointer b, gpointer unused) -{ - const struct event_entry *ea = a, *eb = b; - // Event ordering - it's to ensure bank changes are emitted before program - // changes and program changes are emitted before notes. - static const char event_class[8] = { - 8, // Note Off - 9, // Note On - 20, // Poly Pressure - 4, // Control Change - 6, // Program Change - 16, // Mono Pressure - 18, // Pitch Wheel - 0, // SysEx/Realtime - }; - - if (ea->time < eb->time) - return -1; - if (ea->time == eb->time && event_class[(ea->data[0] >> 4) & 7] < event_class[(eb->data[0] >> 4) & 7]) - return -1; - if (ea->time == eb->time && (ea->data[0] & 15) < (eb->data[0] & 15)) - return -1; - if (ea->time == eb->time && ea->data[0] == eb->data[0] && ea->data[1] < eb->data[1]) - return -1; - if (ea->time == eb->time && ea->data[0] == eb->data[0] && ea->data[1] == eb->data[1]) - return 0; - return +1; -} - -static void event_entry_destroy(gpointer p) -{ - struct event_entry *e = p; - free(e); -} - -struct cbox_midi_pattern_maker -{ - CBOX_OBJECT_HEADER() - GTree *events; - uint64_t ppqn_factor; -}; - -struct cbox_midi_pattern_maker *cbox_midi_pattern_maker_new(uint64_t ppqn_factor) -{ - struct cbox_midi_pattern_maker *maker = malloc(sizeof(struct cbox_midi_pattern_maker)); - maker->events = g_tree_new_full(event_entry_compare, NULL, event_entry_destroy, NULL); - maker->ppqn_factor = ppqn_factor; - return maker; -} - - -void cbox_midi_pattern_maker_add(struct cbox_midi_pattern_maker *maker, uint32_t time, uint8_t cmd, uint8_t val1, uint8_t val2) -{ - struct event_entry *e = malloc(sizeof(struct event_entry)); - e->time = time; - e->data[0] = cmd; - e->data[1] = val1; - e->data[2] = val2; - - g_tree_insert(maker->events, e, NULL); -} - -void cbox_midi_pattern_maker_add_mem(struct cbox_midi_pattern_maker *maker, uint32_t time, const uint8_t *src, uint32_t len) -{ - if (len > 3) - { - g_warning("Event size %d not supported yet, ignoring", (int)len); - return; - } - struct event_entry *e = malloc(sizeof(struct event_entry)); - e->time = time; - memcpy(e->data, src, len); - - g_tree_insert(maker->events, e, NULL); -} - -struct traverse_state -{ - struct cbox_midi_event *events; - int pos; -}; - -static gboolean traverse_func(gpointer key, gpointer value, gpointer pstate) -{ - struct traverse_state *state = pstate; - struct event_entry *e = key; - struct cbox_midi_event *event = &state->events[state->pos++]; - event->time = e->time; - event->size = midi_cmd_size(e->data[0]); - memcpy(event->data_inline, &e->data[0], 3); - return FALSE; -} - -extern void cbox_song_add_pattern(struct cbox_song *song, struct cbox_midi_pattern *pattern); - -struct cbox_midi_pattern *cbox_midi_pattern_maker_create_pattern(struct cbox_midi_pattern_maker *maker, struct cbox_song *song, gchar *name) -{ - struct cbox_midi_pattern *p = malloc(sizeof(struct cbox_midi_pattern)); - CBOX_OBJECT_HEADER_INIT(p, cbox_midi_pattern, CBOX_GET_DOCUMENT(song)); - cbox_command_target_init(&p->cmd_target, cbox_midi_pattern_process_cmd, p); - p->owner = NULL; - p->name = name; - p->event_count = g_tree_nnodes(maker->events); - p->events = malloc(sizeof(struct cbox_midi_event[1]) * p->event_count); - - struct traverse_state st = { p->events, 0 }; - - g_tree_foreach(maker->events, traverse_func, &st); - - CBOX_OBJECT_REGISTER(p); - - cbox_song_add_pattern(song, p); - - return p; -} - -#if USE_LIBSMF -gboolean cbox_midi_pattern_maker_load_smf(struct cbox_midi_pattern_maker *maker, const char *filename, int *length, GError **error) -{ - smf_t *smf = smf_load(filename); - if (!smf) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot load SMF file '%s'", filename); - return FALSE; - } - - int ppqn = smf->ppqn; - smf_event_t *event = NULL; - while ((event = smf_get_next_event(smf)) != NULL) { - if (smf_event_is_metadata(event)) - continue; - - cbox_midi_pattern_maker_add_mem(maker, event->time_pulses * 1.0 * maker->ppqn_factor / ppqn, event->midi_buffer, event->midi_buffer_length); - } - if (length) - *length = smf_get_length_pulses(smf) * 1.0 * maker->ppqn_factor / ppqn; - smf_delete(smf); - - return TRUE; -} -#endif - -void cbox_midi_pattern_maker_destroy(struct cbox_midi_pattern_maker *maker) -{ - g_tree_destroy(maker->events); - free(maker); -} - - diff --git a/template/calfbox/pattern-maker.h b/template/calfbox/pattern-maker.h deleted file mode 100644 index 64bd2f8..0000000 --- a/template/calfbox/pattern-maker.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_PATTERN_MAKER_H -#define CBOX_PATTERN_MAKER_H - -#include - -CBOX_EXTERN_CLASS(cbox_midi_pattern_maker) - -struct cbox_midi_pattern; -struct cbox_midi_pattern_maker; -struct cbox_song; - -extern struct cbox_midi_pattern_maker *cbox_midi_pattern_maker_new(uint64_t ppqn_factor); -extern void cbox_midi_pattern_maker_destroy(struct cbox_midi_pattern_maker *maker); - -extern gboolean cbox_midi_pattern_maker_load_smf(struct cbox_midi_pattern_maker *maker, const char *filename, int *length, GError **error); - -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); -extern void cbox_midi_pattern_maker_add_mem(struct cbox_midi_pattern_maker *maker, uint32_t time, const uint8_t *src, uint32_t len); - -extern struct cbox_midi_pattern *cbox_midi_pattern_maker_create_pattern(struct cbox_midi_pattern_maker *maker, struct cbox_song *owner, gchar *name); - -#endif diff --git a/template/calfbox/pattern.c b/template/calfbox/pattern.c deleted file mode 100644 index a8ad1af..0000000 --- a/template/calfbox/pattern.c +++ /dev/null @@ -1,461 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "blob.h" -#include "config.h" -#include "config-api.h" -#include "pattern.h" -#include "pattern-maker.h" -#include "song.h" - -#include - -extern void cbox_song_remove_pattern(struct cbox_song *song, struct cbox_midi_pattern *pattern); - -CBOX_CLASS_DEFINITION_ROOT(cbox_midi_pattern) - -struct cbox_midi_pattern *cbox_midi_pattern_new_metronome(struct cbox_song *song, int ts, uint64_t ppqn_factor) -{ - struct cbox_midi_pattern_maker *m = cbox_midi_pattern_maker_new(ppqn_factor); - - int length = (int)ppqn_factor; - int channel = cbox_config_get_int("metronome", "channel", 10); - int accnote = cbox_config_get_note("metronome", "note_accent", 37); - int note = cbox_config_get_note("metronome", "note", 37); - - for (int i = 0; i < ts; i++) - { - int accent = !i && ts != 1; - cbox_midi_pattern_maker_add(m, length * i, 0x90 + channel - 1, accent ? accnote : note, accent ? 127 : 100); - cbox_midi_pattern_maker_add(m, length * i + 1, 0x80 + channel - 1, accent ? accnote : note, 0); - } - - struct cbox_midi_pattern *p = cbox_midi_pattern_maker_create_pattern(m, song, g_strdup_printf("click-%d", ts)); - p->loop_end = length * ts; - - cbox_midi_pattern_maker_destroy(m); - - return p; -} - -void cbox_midi_pattern_destroyfunc(struct cbox_objhdr *objhdr) -{ - struct cbox_midi_pattern *pattern = CBOX_H2O(objhdr); - if (pattern->owner) - cbox_song_remove_pattern(pattern->owner, pattern); - g_free(pattern->name); - if (pattern->events != NULL) - free(pattern->events); - free(pattern); -} - -#if USE_LIBSMF -static int cbox_midi_pattern_load_smf_into(struct cbox_midi_pattern_maker *m, const char *smf) -{ - int length = 0; - if (!cbox_midi_pattern_maker_load_smf(m, smf, &length, NULL)) - { - g_error("Cannot load SMF file %s", smf); - return -1; - } - return length; -} -#endif - -static int cbox_midi_pattern_load_melodic_into(struct cbox_midi_pattern_maker *m, const char *name, int start_pos, int transpose, int transpose_to_note, uint64_t ppqn_factor) -{ - gchar *cfg_section = g_strdup_printf("pattern:%s", name); - - if (!cbox_config_has_section(cfg_section)) - { - g_error("Melodic pattern '%s' not found", name); - g_free(cfg_section); - return -1; - } - - gchar *smf = cbox_config_get_string(cfg_section, "smf"); -#if USE_LIBSMF - if (smf) - return cbox_midi_pattern_load_smf_into(m, smf); -#else - if (smf) - g_warning("libsmf disabled at build time, MIDI import functionality not available."); -#endif - - int length = ppqn_factor * cbox_config_get_int(cfg_section, "beats", 4); - int gchannel = cbox_config_get_int(cfg_section, "channel", 1); - int gswing = cbox_config_get_int(cfg_section, "swing", 0); - int gres = cbox_config_get_int(cfg_section, "resolution", 4); - int orignote = cbox_config_get_note(cfg_section, "base_note", 24); - if (transpose_to_note != -1) - transpose += transpose_to_note - orignote; - - for (int t = 1; ; t++) - { - gchar *tname = g_strdup_printf("track%d", t); - char *trkname = cbox_config_get_string(cfg_section, tname); - g_free(tname); - if (trkname) - { - tname = g_strdup_printf("%s_vel", trkname); - int vel = cbox_config_get_note(cfg_section, tname, 100); - g_free(tname); - tname = g_strdup_printf("%s_res", trkname); - int res = cbox_config_get_note(cfg_section, tname, gres); - g_free(tname); - tname = g_strdup_printf("%s_channel", trkname); - int channel = cbox_config_get_note(cfg_section, tname, gchannel); - g_free(tname); - tname = g_strdup_printf("%s_swing", trkname); - int swing = cbox_config_get_int(cfg_section, tname, gswing); - g_free(tname); - tname = g_strdup_printf("%s_notes", trkname); - const char *notes = cbox_config_get_string(cfg_section, tname); - g_free(tname); - if (!notes) - { - g_error("Invalid track %s", trkname); - } - const char *s = notes; - int t = 0; - while(1) - { - if (!*s) - break; - - gchar *note; - const char *comma = strchr(s, ','); - if (comma) - { - note = g_strndup(s, comma - s); - s = comma + 1; - } - else - { - note = g_strdup(s); - s += strlen(s); - } - - if (*note) - { - int pitch = note_from_string(note); - - int pos = t * ppqn_factor / res + start_pos; - if (t & 1) - pos += ppqn_factor * swing / (res * 24); - - int pos2 = (t + 1) * ppqn_factor / res + start_pos; - if (t & 1) - pos2 += ppqn_factor * swing / (res * 24); - - pitch += transpose; - - cbox_midi_pattern_maker_add(m, pos, 0x90 + channel - 1, pitch, vel); - cbox_midi_pattern_maker_add(m, pos2 - 1, 0x80 + channel - 1, pitch, 0); - } - t++; - } - } - else - break; - } - - g_free(cfg_section); - - return length; -} - -static int cbox_midi_pattern_load_drum_into(struct cbox_midi_pattern_maker *m, const char *name, int start_pos, uint64_t ppqn_factor) -{ - gchar *cfg_section = g_strdup_printf("drumpattern:%s", name); - - if (!cbox_config_has_section(cfg_section)) - { - g_error("Drum pattern '%s' not found", name); - g_free(cfg_section); - return -1; - } - - gchar *smf = cbox_config_get_string(cfg_section, "smf"); -#if USE_LIBSMF - if (smf) - return cbox_midi_pattern_load_smf_into(m, smf); -#else - if (smf) - g_warning("libsmf disabled at build time, MIDI import functionality not available."); -#endif - - int length = ppqn_factor * cbox_config_get_int(cfg_section, "beats", 4); - int channel = cbox_config_get_int(cfg_section, "channel", 10); - int gswing = cbox_config_get_int(cfg_section, "swing", 0); - int gres = cbox_config_get_int(cfg_section, "resolution", 4); - - for (int t = 1; ; t++) - { - gchar *tname = g_strdup_printf("track%d", t); - char *trkname = cbox_config_get_string(cfg_section, tname); - g_free(tname); - if (trkname) - { - tname = g_strdup_printf("%s_note", trkname); - int note = cbox_config_get_note(cfg_section, tname, -1); - g_free(tname); - tname = g_strdup_printf("%s_res", trkname); - int res = cbox_config_get_note(cfg_section, tname, gres); - g_free(tname); - tname = g_strdup_printf("%s_swing", trkname); - int swing = cbox_config_get_int(cfg_section, tname, gswing); - g_free(tname); - tname = g_strdup_printf("%s_trigger", trkname); - const char *trigger = cbox_config_get_string(cfg_section, tname); - g_free(tname); - if (!trigger || note == -1) - { - g_error("Invalid track %s", trkname); - } - int t = 0; - for (int i = 0; trigger[i]; i++) - { - int pos = t * ppqn_factor / res + start_pos; - if (t & 1) - pos += ppqn_factor * swing / (res * 24); - if (trigger[i] >= '1' && trigger[i] <= '9') - { - int amt = (trigger[i] - '0') * 127 / 9; - cbox_midi_pattern_maker_add(m, pos, 0x90 + channel - 1, note, amt); - cbox_midi_pattern_maker_add(m, pos + 1, 0x80 + channel - 1, note, 0); - t++; - } - if (trigger[i] == 'F') // flam - { - int dflam = ppqn_factor / 4; - int rnd = rand() & 7; - dflam += rnd / 2; - cbox_midi_pattern_maker_add(m, pos - dflam, 0x90 + channel - 1, note, 90+rnd); - cbox_midi_pattern_maker_add(m, pos - dflam + 1, 0x80 + channel - 1, note, 0); - cbox_midi_pattern_maker_add(m, pos , 0x90 + channel - 1, note, 120 + rnd); - cbox_midi_pattern_maker_add(m, pos + 1, 0x80 + channel - 1, note, 0); - t++; - } - if (trigger[i] == 'D') // drag - { - pos = (t + 1) * ppqn_factor / res + start_pos; - //if (!(t & 1)) - // pos += ppqn_factor * swing / (res * 24); - float dflam = ppqn_factor/8.0; - int rnd = rand() & 7; - cbox_midi_pattern_maker_add(m, pos - dflam*2, 0x90 + channel - 1, note, 70+rnd); - cbox_midi_pattern_maker_add(m, pos - dflam*2 + 1, 0x80 + channel - 1, note, 0); - cbox_midi_pattern_maker_add(m, pos - dflam, 0x90 + channel - 1, note, 60+rnd); - cbox_midi_pattern_maker_add(m, pos - dflam + 1, 0x80 + channel - 1, note, 0); - t++; - } - else if (trigger[i] == '.') - t++; - } - } - else - break; - } - - g_free(cfg_section); - - return length; -} - -struct cbox_midi_pattern *cbox_midi_pattern_load(struct cbox_song *song, const char *name, int is_drum, uint64_t ppqn_factor) -{ - struct cbox_midi_pattern_maker *m = cbox_midi_pattern_maker_new(ppqn_factor); - - int length = 0; - if (is_drum) - length = cbox_midi_pattern_load_drum_into(m, name, 0, ppqn_factor); - else - length = cbox_midi_pattern_load_melodic_into(m, name, 0, 0, -1, ppqn_factor); - struct cbox_midi_pattern *p = cbox_midi_pattern_maker_create_pattern(m, song, g_strdup(name)); - p->loop_end = length; - - cbox_midi_pattern_maker_destroy(m); - - return p; -} - -struct cbox_midi_pattern *cbox_midi_pattern_load_track(struct cbox_song *song, const char *name, int is_drum, uint64_t ppqn_factor) -{ - int length = 0; - struct cbox_midi_pattern_maker *m = cbox_midi_pattern_maker_new(ppqn_factor); - - gchar *cfg_section = g_strdup_printf(is_drum ? "drumtrack:%s" : "track:%s", name); - - if (!cbox_config_has_section(cfg_section)) - { - g_error("Drum track '%s' not found", name); - g_free(cfg_section); - return NULL; - } - - for (int p = 1; ; p++) - { - gchar *pname = g_strdup_printf("pos%d", p); - char *patname = cbox_config_get_string(cfg_section, pname); - g_free(pname); - if (patname) - { - int tplen = 0; - char *comma = strchr(patname, ','); - while(*patname) - { - char *v = comma ? g_strndup(patname, comma - patname) : g_strdup(patname); - patname = comma ? comma + 1 : patname + strlen(patname); - - int xpval = 0, xpnote = -1; - if (!is_drum) - { - char *xp = strchr(v, '+'); - if (xp) - { - *xp = '\0'; - xpval = atoi(xp + 1); - } - else - { - xp = strchr(v, '='); - if (xp) - { - *xp = '\0'; - xpnote = note_from_string(xp + 1); - } - } - } - int plen = 0; - int is_drum_pat = is_drum; - int nofs = 0; - if (*v == '@') - { - nofs = 1; - is_drum_pat = !is_drum_pat; - } - if (is_drum_pat) - plen = cbox_midi_pattern_load_drum_into(m, v + nofs, length, ppqn_factor); - else - plen = cbox_midi_pattern_load_melodic_into(m, v + nofs, length, xpval, xpnote, ppqn_factor); - g_free(v); - if (plen < 0) - { - cbox_midi_pattern_maker_destroy(m); - return NULL; - } - if (plen > tplen) - tplen = plen; - if (*patname) - comma = strchr(patname, ','); - } - length += tplen; - } - else - break; - } - - g_free(cfg_section); - - struct cbox_midi_pattern *p = cbox_midi_pattern_maker_create_pattern(m, song, g_strdup(name)); - p->loop_end = length; - - cbox_midi_pattern_maker_destroy(m); - - return p; -} - -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) -{ - struct cbox_midi_pattern_maker *m = cbox_midi_pattern_maker_new(ppqn_factor); - - struct cbox_blob_serialized_event event; - for (size_t i = 0; i < blob->size; i += sizeof(event)) - { - // not sure about alignment guarantees of Python buffers - memcpy(&event, ((uint8_t *)blob->data) + i, sizeof(event)); - cbox_midi_pattern_maker_add(m, event.time, event.cmd, event.byte1, event.byte2); - } - - struct cbox_midi_pattern *p = cbox_midi_pattern_maker_create_pattern(m, song, g_strdup("unnamed-blob")); - p->loop_end = length; - - cbox_midi_pattern_maker_destroy(m); - - return p; -} - -struct cbox_blob *cbox_midi_pattern_to_blob(struct cbox_midi_pattern *pat, int *length) -{ - if (length) - *length = pat->loop_end; - - struct cbox_blob_serialized_event event; - int size = 0; - for (uint32_t i = 0; i < pat->event_count; i++) - { - // currently sysex events and the like are not supported - if (pat->events[i].size < 4) - size += sizeof(event); - } - - struct cbox_blob *blob = cbox_blob_new(size); - - size = 0; - uint8_t *data = blob->data; - for (uint32_t i = 0; i < pat->event_count; i++) - { - // currently sysex events and the like are not supported - const struct cbox_midi_event *src = &pat->events[i]; - if (src->size < 4) - { - event.time = src->time; - event.len = src->size; - memcpy(&event.cmd, &src->data_inline[0], event.len); - memcpy(data + size, &event, sizeof(event)); - size += sizeof(event); - } - } - return blob; -} - -gboolean cbox_midi_pattern_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct cbox_midi_pattern *p = ct->user_data; - - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - return cbox_execute_on(fb, NULL, "/event_count", "i", error, (int)p->event_count) && - cbox_execute_on(fb, NULL, "/loop_end", "i", error, (int)p->loop_end) && - cbox_execute_on(fb, NULL, "/name", "s", error, p->name) && - CBOX_OBJECT_DEFAULT_STATUS(p, fb, error) - ; - } - else if (!strcmp(cmd->command, "/name") && !strcmp(cmd->arg_types, "s")) - { - char *old_name = p->name; - p->name = g_strdup(CBOX_ARG_S(cmd, 0)); - g_free(old_name); - return TRUE; - } - return cbox_object_default_process_cmd(ct, fb, cmd, error); -} diff --git a/template/calfbox/pattern.h b/template/calfbox/pattern.h deleted file mode 100644 index 7cd1982..0000000 --- a/template/calfbox/pattern.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_PATTERN_H -#define CBOX_PATTERN_H - -#include "dom.h" -#include "master.h" -#include "midi.h" - -CBOX_EXTERN_CLASS(cbox_midi_pattern) - -struct cbox_blob; -struct cbox_song; - -struct cbox_midi_pattern -{ - CBOX_OBJECT_HEADER() - struct cbox_command_target cmd_target; - struct cbox_song *owner; - gchar *name; - struct cbox_midi_event *events; - uint32_t event_count; - int loop_end; -}; - -struct cbox_blob_serialized_event -{ - int32_t time; - unsigned char len, cmd, byte1, byte2; -}; - -extern struct cbox_midi_pattern *cbox_midi_pattern_new_metronome(struct cbox_song *song, int ts, uint64_t ppqn_factor); -extern struct cbox_midi_pattern *cbox_midi_pattern_load(struct cbox_song *song, const char *name, int is_drum, uint64_t ppqn_factor); -extern struct cbox_midi_pattern *cbox_midi_pattern_load_track(struct cbox_song *song, const char *name, int is_drum, uint64_t ppqn_factor); -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); - -extern struct cbox_blob *cbox_midi_pattern_to_blob(struct cbox_midi_pattern *pat, int *length); - -extern gboolean cbox_midi_pattern_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error); - -#endif diff --git a/template/calfbox/phaser.c b/template/calfbox/phaser.c deleted file mode 100644 index 4d1196f..0000000 --- a/template/calfbox/phaser.c +++ /dev/null @@ -1,251 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config.h" -#include "config-api.h" -#include "dspmath.h" -#include "module.h" -#include "onepole-float.h" -#include -#include -#include -#include -#include -#include -#include - -#define NO_STAGES 12 - -#define MODULE_PARAMS phaser_params - -struct phaser_params -{ - float center; - float mdepth; - float fb_amt; - float lfo_freq; - float sphase; - float wet_dry; - int stages; -}; - -#if USE_NEON -#include - -struct cbox_onepolef_stereo_state_neon { - float32x2_t x1; - float32x2_t y1; -}; -#endif - -struct phaser_module -{ - struct cbox_module module; - -#if USE_NEON - struct cbox_onepolef_stereo_state_neon state[NO_STAGES]; -#else - struct cbox_onepolef_state state[NO_STAGES][2]; -#endif - struct cbox_onepolef_coeffs coeffs[2]; - float fb[2]; - float tpdsr; - struct phaser_params *params; - - float phase; -}; - -gboolean phaser_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct phaser_module *m = (struct phaser_module *)ct->user_data; - - EFFECT_PARAM("/center_freq", "f", center, double, , 10, 20000) else - EFFECT_PARAM("/mod_depth", "f", mdepth, double, , 0, 10000) else - EFFECT_PARAM("/fb_amt", "f", fb_amt, double, , -1, 1) else - EFFECT_PARAM("/lfo_freq", "f", lfo_freq, double, , 0, 20) else - EFFECT_PARAM("/stereo_phase", "f", sphase, double, deg2rad, 0, 360) else - EFFECT_PARAM("/wet_dry", "f", wet_dry, double, , 0, 1) else - EFFECT_PARAM("/stages", "i", stages, int, , 1, 12) else - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - return cbox_execute_on(fb, NULL, "/center_freq", "f", error, m->params->center) && - cbox_execute_on(fb, NULL, "/mod_depth", "f", error, m->params->mdepth) && - cbox_execute_on(fb, NULL, "/fb_amt", "f", error, m->params->fb_amt) && - cbox_execute_on(fb, NULL, "/lfo_freq", "f", error, m->params->lfo_freq) && - cbox_execute_on(fb, NULL, "/stereo_phase", "f", error, rad2deg(m->params->sphase)) && - cbox_execute_on(fb, NULL, "/wet_dry", "f", error, m->params->wet_dry) && - cbox_execute_on(fb, NULL, "/stages", "i", error, m->params->stages) && - CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error); - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); - return TRUE; -} - -void phaser_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) -{ - // struct phaser_module *m = (struct phaser_module *)module; -} - -static inline float clip_w(float w) -{ - if (w > 0.9 * M_PI) - return 0.9 * M_PI; - return w; -} - -void phaser_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) -{ - struct phaser_module *m = (struct phaser_module *)module; - struct phaser_params *p = m->params; - int s, i; - int stages = p->stages; - float fb_amt = p->fb_amt; - if (stages < 0 || stages > NO_STAGES) - stages = 0; - - if (p->mdepth) - { - cbox_onepolef_set_allpass(&m->coeffs[0], clip_w(m->tpdsr * p->center * cent2factor(p->mdepth * sin(m->phase)))); - cbox_onepolef_set_allpass(&m->coeffs[1], clip_w(m->tpdsr * p->center * cent2factor(p->mdepth * sin(m->phase + p->sphase)))); - } - else - { - cbox_onepolef_set_allpass(&m->coeffs[0], m->tpdsr * p->center); - cbox_onepolef_set_allpass(&m->coeffs[1], m->tpdsr * p->center); - } - m->phase += p->lfo_freq * CBOX_BLOCK_SIZE * m->tpdsr; - -#if USE_NEON - float *__restrict input1 = inputs[0]; - float *__restrict input2 = inputs[1]; - float *__restrict output1 = outputs[0]; - float *__restrict output2 = outputs[1]; - float32x2_t wetdry = {p->wet_dry, p->wet_dry}; - float32x2_t fb_amt2 = {fb_amt, fb_amt}; - float32x2_t fb = {m->fb[0], m->fb[1]}; - float32x2_t a0 = {m->coeffs[0].a0, m->coeffs[1].a0}; - float32x2_t a1 = {m->coeffs[0].a1, m->coeffs[1].a1}; - float32x2_t b1 = {m->coeffs[0].b1, m->coeffs[1].b1}; - float32x2_t zero = {0.f, 0.f}; - const float32x2_t thresh = {(1.0 / (65536.0 * 65536.0)), (1.0 / (65536.0 * 65536.0))}; - for (i = 0; i < CBOX_BLOCK_SIZE; i++) - { - float32x2_t dry = {input1[i], input2[i]}; - float32x2_t wet = vsub_f32(dry, vmul_f32(fb, fb_amt2)); - for (s = 0; s < (stages & ~1); s += 2) - { - // wet = sanef(coeffs->a0 * wet + coeffs->a1 * state->x1 - coeffs->b1 * state->y1); - float32x2_t pre = wet; - wet = vmla_f32(vmls_f32(vmul_f32(a1, m->state[s].x1), b1, m->state[s].y1), a0, wet); - m->state[s].x1 = pre; - m->state[s].y1 = wet; - pre = wet; - wet = vmla_f32(vmls_f32(vmul_f32(a1, m->state[s + 1].x1), b1, m->state[s + 1].y1), a0, wet); - m->state[s + 1].x1 = pre; - m->state[s + 1].y1 = wet; - } - for (; s < stages; s++) - { - // wet = sanef(coeffs->a0 * wet + coeffs->a1 * state->x1 - coeffs->b1 * state->y1); - float32x2_t pre = wet; - wet = vmla_f32(vmls_f32(vmul_f32(a1, m->state[s].x1), b1, m->state[s].y1), a0, wet); - m->state[s].x1 = pre; - m->state[s].y1 = wet; - } - fb = wet; - wet = vadd_f32(dry, vmul_f32(vsub_f32(wet, dry), wetdry)); - output1[i] = wet[0]; - output2[i] = wet[1]; - } - // set values < threshold to zero - for (s = 0; s < stages; s++) - m->state[s].y1 = vbsl_f32(vcage_f32(m->state[s].y1, thresh), m->state[s].y1, zero); - m->fb[0] = fb[0]; - m->fb[1] = fb[1]; -#else - for (int c = 0; c < 2; c++) - { - for (i = 0; i < CBOX_BLOCK_SIZE; i++) - { - float dry = inputs[c][i]; - float wet = dry - m->fb[c] * fb_amt; - for (s = 0; s < stages; s++) - wet = cbox_onepolef_process_sample(&m->state[s][c], &m->coeffs[c], wet); - m->fb[c] = wet; - outputs[c][i] = dry + (wet - dry) * p->wet_dry; - } - } -#endif -} - -MODULE_SIMPLE_DESTROY_FUNCTION(phaser) - -MODULE_CREATE_FUNCTION(phaser) -{ - int b; - - static int inited = 0; - if (!inited) - { - inited = 1; - } - - struct phaser_module *m = malloc(sizeof(struct phaser_module)); - CALL_MODULE_INIT(m, 2, 2, phaser); - m->module.process_event = phaser_process_event; - m->module.process_block = phaser_process_block; - m->tpdsr = 2.0 * M_PI / m->module.srate; - m->phase = 0; - struct phaser_params *p = malloc(sizeof(struct phaser_params)); - m->params = p; - p->sphase = deg2rad(cbox_config_get_float(cfg_section, "stereo_phase", 90.f)); - p->lfo_freq = cbox_config_get_float(cfg_section, "lfo_freq", 1.f); - p->center = cbox_config_get_float(cfg_section, "center_freq", 1500.f); - p->mdepth = cbox_config_get_float(cfg_section, "mod_depth", 1200.f); - p->fb_amt = cbox_config_get_float(cfg_section, "feedback", 0.f); - p->wet_dry = cbox_config_get_float(cfg_section, "wet_dry", 0.5f); - p->stages = cbox_config_get_int(cfg_section, "stages", NO_STAGES); - -#if USE_NEON - for (b = 0; b < NO_STAGES; b++) - { - float32x2_t zero = {0.f, 0.f}; - m->state[b].x1 = zero; - m->state[b].y1 = zero; - } -#else - for (b = 0; b < NO_STAGES; b++) - for (int c = 0; c < 2; c++) - cbox_onepolef_reset(&m->state[b][c]); -#endif - - return &m->module; -} - - -struct cbox_module_keyrange_metadata phaser_keyranges[] = { -}; - -struct cbox_module_livecontroller_metadata phaser_controllers[] = { -}; - -DEFINE_MODULE(phaser, 2, 2) - diff --git a/template/calfbox/prefetch_pipe.c b/template/calfbox/prefetch_pipe.c deleted file mode 100644 index 13e397a..0000000 --- a/template/calfbox/prefetch_pipe.c +++ /dev/null @@ -1,309 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "prefetch_pipe.h" -#include "tarfile.h" -#include "wavebank.h" -#include -#include -#include -#include -#include - -// Don't bother fetching less than 4 (mono) or 8 KB (stereo) - -void cbox_prefetch_pipe_init(struct cbox_prefetch_pipe *pipe, uint32_t buffer_size, uint32_t min_buffer_frames) -{ - pipe->data = malloc(buffer_size); - pipe->buffer_size = buffer_size; - pipe->min_buffer_frames = min_buffer_frames; - pipe->sndfile = NULL; - pipe->state = pps_free; -} - -gboolean cbox_prefetch_pipe_openfile(struct cbox_prefetch_pipe *pipe) -{ - if (pipe->waveform->taritem) - pipe->sndfile = cbox_tarfile_opensndfile(pipe->waveform->tarfile, pipe->waveform->taritem, &pipe->sndstream, &pipe->info); - else - pipe->sndfile = sf_open(pipe->waveform->canonical_name, SFM_READ, &pipe->info); - if (!pipe->sndfile) - return FALSE; - pipe->file_pos_frame = sf_seek(pipe->sndfile, pipe->waveform->preloaded_frames, SEEK_SET); - if (pipe->file_loop_end > pipe->info.frames) - pipe->file_loop_end = pipe->info.frames; - pipe->buffer_loop_end = pipe->buffer_size / (sizeof(int16_t) * pipe->info.channels); - pipe->produced = pipe->file_pos_frame; - pipe->write_ptr = 0; - pipe->state = pps_active; - - return TRUE; -} - -void cbox_prefetch_pipe_consumed(struct cbox_prefetch_pipe *pipe, uint32_t frames) -{ - pipe->consumed += frames; -} - -void cbox_prefetch_pipe_fetch(struct cbox_prefetch_pipe *pipe) -{ - gboolean retry; - do { - retry = FALSE; - // XXXKF take consumption rate into account - - // How many frames left to consume - int32_t supply = pipe->produced - pipe->consumed; - if (supply < 0) - { - // Overrun already occurred. Cut the losses by skipping already missed - // part. - uint32_t overrun = -supply; - - // XXXKF This may or may not be stupid. I didn't put much thought into it. - pipe->produced += overrun; - pipe->file_pos_frame = sf_seek(pipe->sndfile, overrun, SEEK_CUR); - pipe->write_ptr += overrun; - if (pipe->write_ptr >= pipe->buffer_loop_end) - pipe->write_ptr %= pipe->buffer_loop_end; - } - // - if ((uint32_t)supply >= pipe->buffer_loop_end) - return; - - // How many frames to read to fill the full prefetch size - int32_t readsize = pipe->buffer_loop_end - supply; - if (readsize < (int32_t)pipe->min_buffer_frames) - return; - - if (pipe->write_ptr == pipe->buffer_loop_end) - pipe->write_ptr = 0; - - // If reading across buffer boundary, only read the part up to buffer - // end, and then retry from start of the buffer. - if (pipe->write_ptr + readsize > pipe->buffer_loop_end) - { - readsize = pipe->buffer_loop_end - pipe->write_ptr; - retry = TRUE; - } - // If past the file loop end, restart at file loop start - if (pipe->file_pos_frame >= pipe->file_loop_end) - { - if (pipe->file_loop_start == (uint32_t)-1 || (pipe->loop_count && pipe->play_count >= pipe->loop_count - 1)) - { - pipe->finished = TRUE; - for (int i = 0; i < readsize * pipe->info.channels; i++) - pipe->data[pipe->write_ptr * pipe->info.channels + i] = rand(); - break; - } - else - { - pipe->play_count++; - pipe->file_pos_frame = pipe->file_loop_start; - sf_seek(pipe->sndfile, pipe->file_loop_start, SEEK_SET); - } - } - // If reading across file loop boundary, read up to loop end and - // retry to restart - if (pipe->file_pos_frame + readsize > pipe->file_loop_end) - { - readsize = pipe->file_loop_end - pipe->file_pos_frame; - retry = TRUE; - } - - int32_t actread = sf_readf_short(pipe->sndfile, pipe->data + pipe->write_ptr * pipe->info.channels, readsize); - pipe->produced += actread; - pipe->file_pos_frame += actread; - pipe->write_ptr += actread; - } while(retry); -} - -void cbox_prefetch_pipe_closefile(struct cbox_prefetch_pipe *pipe) -{ - assert(pipe->state == pps_closing); - assert(pipe->sndfile); - sf_close(pipe->sndfile); - pipe->sndfile = NULL; - pipe->state = pps_free; -} - -void cbox_prefetch_pipe_close(struct cbox_prefetch_pipe *pipe) -{ - if (pipe->sndfile) - cbox_prefetch_pipe_closefile(pipe); - if (pipe->data) - { - free(pipe->data); - pipe->data = NULL; - } -} - -static void *prefetch_thread(void *user_data) -{ - struct cbox_prefetch_stack *stack = user_data; - - while(!stack->finished) - { - usleep(1000); - for (int i = 0; i < stack->pipe_count; i++) - { - struct cbox_prefetch_pipe *pipe = &stack->pipes[i]; - switch(pipe->state) - { - case pps_free: - case pps_finished: - case pps_error: - break; - case pps_opening: - if (!cbox_prefetch_pipe_openfile(pipe)) - pipe->state = pps_error; - assert(pipe->state != pps_opening); - break; - case pps_active: - if (pipe->returned) - pipe->state = pps_closing; - else - cbox_prefetch_pipe_fetch(pipe); - break; - case pps_closing: - cbox_prefetch_pipe_closefile(pipe); - break; - default: - break; - } - } - } - return 0; -} - -struct cbox_prefetch_stack *cbox_prefetch_stack_new(int npipes, uint32_t buffer_size, uint32_t min_buffer_frames) -{ - struct cbox_prefetch_stack *stack = calloc(1, sizeof(struct cbox_prefetch_stack)); - stack->pipes = calloc(npipes, sizeof(struct cbox_prefetch_pipe)); - stack->next_free_pipe = calloc(npipes, sizeof(int)); - - for (int i = 0; i < npipes; i++) - { - cbox_prefetch_pipe_init(&stack->pipes[i], buffer_size, min_buffer_frames); - stack->next_free_pipe[i] = i - 1; - } - stack->pipe_count = npipes; - stack->last_free_pipe = npipes - 1; - stack->finished = FALSE; - - if (pthread_create(&stack->thr_prefetch, NULL, prefetch_thread, stack)) - { - // XXXKF set thread priority - g_warning("Cannot create a prefetch thread. Exiting.\n"); - return NULL; - } - - return stack; -} - -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) -{ - // The stack may include some pipes that are already returned but not yet - // fully prepared for opening a new file - int *ppos = &stack->last_free_pipe; - while(*ppos != -1 && stack->pipes[*ppos].state != pps_free) - ppos = &stack->next_free_pipe[*ppos]; - if (*ppos == -1) { - for (int i = 0; i < stack->pipe_count; ++i) { - printf("Pipe %d state %d next-free %d\n", i, stack->pipes[i].state, stack->next_free_pipe[i]); - } - printf("last_free_pipe %d\n", stack->last_free_pipe); - return NULL; - } - - int pos = *ppos; - struct cbox_prefetch_pipe *pipe = &stack->pipes[pos]; - - *ppos = stack->next_free_pipe[pos]; - stack->next_free_pipe[pos] = -1; - - pipe->waveform = waveform; - if (file_loop_start == (uint32_t)-1 && loop_count) - file_loop_start = 0; - pipe->file_loop_start = file_loop_start; - pipe->file_loop_end = file_loop_end; - pipe->buffer_loop_end = 0; - pipe->finished = FALSE; - pipe->returned = FALSE; - pipe->produced = waveform->preloaded_frames; - pipe->consumed = 0; - pipe->play_count = 0; - pipe->loop_count = loop_count; - - __sync_synchronize(); - pipe->state = pps_opening; - return pipe; -} - -void cbox_prefetch_stack_push(struct cbox_prefetch_stack *stack, struct cbox_prefetch_pipe *pipe) -{ - switch(pipe->state) - { - case pps_free: - assert(0); - break; - case pps_error: - case pps_closed: - pipe->state = pps_free; - break; - case pps_opening: - // Close the file as soon as open operation completes - pipe->returned = TRUE; - break; - default: - pipe->state = pps_closing; - break; - } - - __sync_synchronize(); - - int pos = pipe - stack->pipes; - assert(stack->next_free_pipe[pos] == -1); - stack->next_free_pipe[pos] = stack->last_free_pipe; - stack->last_free_pipe = pos; - - __sync_synchronize(); -} - -int cbox_prefetch_stack_get_active_pipe_count(struct cbox_prefetch_stack *stack) -{ - int count = 0; - for (int i = 0; i < stack->pipe_count; i++) - { - if (stack->pipes[i].state != pps_free) - count++; - } - return count; -} - -void cbox_prefetch_stack_destroy(struct cbox_prefetch_stack *stack) -{ - void *result = NULL; - stack->finished = TRUE; - pthread_join(stack->thr_prefetch, &result); - for (int i = 0; i < stack->pipe_count; i++) - cbox_prefetch_pipe_close(&stack->pipes[i]); - free(stack->next_free_pipe); - free(stack->pipes); - free(stack); -} diff --git a/template/calfbox/prefetch_pipe.h b/template/calfbox/prefetch_pipe.h deleted file mode 100644 index 8eef6f1..0000000 --- a/template/calfbox/prefetch_pipe.h +++ /dev/null @@ -1,95 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_PREFETCH_PIPE_H -#define CBOX_PREFETCH_PIPE_H - -#include -#include -#include -#include -#include -#include - -#define PIPE_MIN_PREFETCH_SIZE_FRAMES 2048 - -struct cbox_waveform; - -enum cbox_prefetch_pipe_state -{ - pps_free, - pps_opening, - pps_active, - pps_finished, - pps_error, - pps_closing, - pps_closed, -}; - -struct cbox_prefetch_pipe -{ - union { - volatile enum cbox_prefetch_pipe_state state; - uint64_t atomic1; - }; - struct cbox_waveform *waveform; - struct cbox_tarfile_sndstream sndstream; - int16_t *data; - uint32_t buffer_size; - uint32_t min_buffer_frames; - SF_INFO info; - SNDFILE *sndfile; - uint32_t file_pos_frame; - uint32_t file_loop_start; - uint32_t file_loop_end; - uint32_t buffer_loop_end; - uint32_t play_count, loop_count; - size_t write_ptr; - size_t produced; - size_t consumed; - gboolean finished; - gboolean returned; -}; - -extern void cbox_prefetch_pipe_init(struct cbox_prefetch_pipe *pipe, uint32_t buffer_size, uint32_t min_buffer_frames); -extern void cbox_prefetch_pipe_consumed(struct cbox_prefetch_pipe *pipe, uint32_t frames); -extern void cbox_prefetch_pipe_close(struct cbox_prefetch_pipe *pipe); - -static inline uint32_t cbox_prefetch_pipe_get_remaining(struct cbox_prefetch_pipe *pipe) -{ - assert(pipe->consumed <= pipe->produced); - return pipe->produced - pipe->consumed; -} - -struct cbox_prefetch_stack -{ - struct cbox_prefetch_pipe *pipes; - int *next_free_pipe; - int pipe_count; - pthread_t thr_prefetch; - int last_free_pipe; - gboolean finished; -}; - -extern struct cbox_prefetch_stack *cbox_prefetch_stack_new(int npipes, uint32_t buffer_size, uint32_t min_buffer_frames); -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); -extern void cbox_prefetch_stack_push(struct cbox_prefetch_stack *stack, struct cbox_prefetch_pipe *pipe); -extern int cbox_prefetch_stack_get_active_pipe_count(struct cbox_prefetch_stack *stack); -extern void cbox_prefetch_stack_destroy(struct cbox_prefetch_stack *stack); - -#endif diff --git a/template/calfbox/recsrc.c b/template/calfbox/recsrc.c deleted file mode 100644 index 55a3317..0000000 --- a/template/calfbox/recsrc.c +++ /dev/null @@ -1,130 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "app.h" -#include "errors.h" -#include "recsrc.h" -#include "rt.h" -#include "scene.h" -#include "stm.h" - -static void cbox_recorder_destroyfunc(struct cbox_objhdr *objhdr) -{ - struct cbox_recorder *rec = CBOX_H2O(objhdr); - if (rec->destroy) - rec->destroy(rec); - free(rec); -} - -CBOX_CLASS_DEFINITION_ROOT(cbox_recorder) - -static gboolean cbox_recording_source_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error); - -void cbox_recording_source_init(struct cbox_recording_source *src, struct cbox_scene *scene, uint32_t max_numsamples, int channels) -{ - src->scene = scene; - src->handlers = NULL; - src->handler_count = 0; - src->max_numsamples = max_numsamples; - src->channels = channels; - cbox_command_target_init(&src->cmd_target, cbox_recording_source_process_cmd, src); -} - -gboolean cbox_recording_source_attach(struct cbox_recording_source *src, struct cbox_recorder *rec, GError **error) -{ - if (rec->attach && !rec->attach(rec, src, error)) - return FALSE; - cbox_rt_array_insert(app.rt, (void ***)&src->handlers, &src->handler_count, 0, rec); - return TRUE; -} - -int cbox_recording_source_detach(struct cbox_recording_source *src, struct cbox_recorder *rec, GError **error) -{ - int index = -1; - for (uint32_t i = 0; i < src->handler_count; i++) - { - if (src->handlers[i] == rec) - { - index = i; - break; - } - } - if (index == -1) - { - if (error) - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Recorder is not attached to this source"); - return 0; - } - - cbox_rt_array_remove(app.rt, (void ***)&src->handlers, &src->handler_count, index); - // XXXKF: when converting to async API, the array_remove must be done synchronously or - // detach needs to be called in the cleanup part of the remove command, otherwise detach - // may be called on 'live' recorder, which may cause unpredictable results. - return rec->detach ? rec->detach(rec, error) : 1; -} - -void cbox_recording_source_push(struct cbox_recording_source *src, const float **buffers, uint32_t offset, uint32_t numsamples) -{ - for (uint32_t i = 0; i < src->handler_count; i++) - src->handlers[i]->record_block(src->handlers[i], buffers, offset, numsamples); -} - -void cbox_recording_source_uninit(struct cbox_recording_source *src) -{ - STM_ARRAY_FREE_OBJS(src->handlers, src->handler_count); - src->handlers = NULL; - src->handler_count = 0; -} - -gboolean cbox_recording_source_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct cbox_recording_source *src = ct->user_data; - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - for (uint32_t i = 0; i < src->handler_count; i++) - { - if (!cbox_execute_on(fb, NULL, "/handler", "o", error, src->handlers[i])) - return FALSE; - } - return TRUE; - } - else - if (!strcmp(cmd->command, "/attach") && !strcmp(cmd->arg_types, "s")) - { - struct cbox_objhdr *objhdr = CBOX_ARG_O(cmd, 0, src->scene, cbox_recorder, error); - if (!objhdr) - return FALSE; - struct cbox_recorder *rec = CBOX_H2O(objhdr); - return cbox_recording_source_attach(src, rec, error); - } - else - if (!strcmp(cmd->command, "/detach") && !strcmp(cmd->arg_types, "s")) - { - struct cbox_objhdr *objhdr = CBOX_ARG_O(cmd, 0, src->scene, cbox_recorder, error); - if (!objhdr) - return FALSE; - struct cbox_recorder *rec = CBOX_H2O(objhdr); - return cbox_recording_source_detach(src, rec, error); - } - else - return cbox_set_command_error(error, cmd); -} - diff --git a/template/calfbox/recsrc.h b/template/calfbox/recsrc.h deleted file mode 100644 index 23cf498..0000000 --- a/template/calfbox/recsrc.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_RECSRC_H -#define CBOX_RECSRC_H - -#include "cmd.h" -#include "dom.h" - -struct cbox_recording_source; -struct cbox_rt; -struct cbox_engine; - -CBOX_EXTERN_CLASS(cbox_recorder) - -struct cbox_recorder -{ - CBOX_OBJECT_HEADER() - void *user_data; - struct cbox_command_target cmd_target; - - gboolean (*attach)(struct cbox_recorder *handler, struct cbox_recording_source *src, GError **error); - void (*record_block)(struct cbox_recorder *handler, const float **buffers, uint32_t offset, uint32_t numsamples); - gboolean (*detach)(struct cbox_recorder *handler, GError **error); - void (*destroy)(struct cbox_recorder *handler); -}; - -struct cbox_recording_source -{ - struct cbox_command_target cmd_target; - struct cbox_scene *scene; - - struct cbox_recorder **handlers; - uint32_t handler_count; - uint32_t max_numsamples; - int channels; -}; - -#define IS_RECORDING_SOURCE_CONNECTED(src) ((src).handler_count != 0) - -extern void cbox_recording_source_init(struct cbox_recording_source *src, struct cbox_scene *scene, uint32_t max_numsamples, int channels); -extern gboolean cbox_recording_source_attach(struct cbox_recording_source *src, struct cbox_recorder *rec, GError **error); -extern int cbox_recording_source_detach(struct cbox_recording_source *src, struct cbox_recorder *rec, GError **error); -extern void cbox_recording_source_push(struct cbox_recording_source *src, const float **buffers, uint32_t offset, uint32_t numsamples); -extern void cbox_recording_source_uninit(struct cbox_recording_source *src); - -extern struct cbox_recorder *cbox_recorder_new_stream(struct cbox_engine *engine, struct cbox_rt *rt, const char *filename); - -#endif diff --git a/template/calfbox/reverb.c b/template/calfbox/reverb.c deleted file mode 100644 index d490d3c..0000000 --- a/template/calfbox/reverb.c +++ /dev/null @@ -1,355 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config.h" -#include "config-api.h" -#include "dspmath.h" -#include "module.h" -#include "onepole-float.h" -#include -#include -#include -#include -#include -#include -#include - -// The reverb structure is based on this article: -// http://www.spinsemi.com/knowledge_base/effects.html#Reverberation - -#define DELAY_BUFFER 1024 -#define ALLPASS_BUFFER 2048 - -struct allpass_param -{ - int delay; - float diffusion; -}; - -struct cbox_reverb_leg_params -{ - int delay_length; - int allpass_units; - struct allpass_param allpasses[0]; -}; - -struct cbox_reverb_leg -{ - struct cbox_reverb_leg_params *params; - float (*allpass_storage)[ALLPASS_BUFFER]; - float delay_storage[DELAY_BUFFER]; - struct cbox_onepolef_state filter_state; - float buffer[CBOX_BLOCK_SIZE]; -}; - -static struct cbox_reverb_leg_params *leg_params_new(int delay_length, int allpasses, const struct allpass_param *allpass_params) -{ - struct cbox_reverb_leg_params *p = malloc(sizeof(struct cbox_reverb_leg_params) + sizeof(struct allpass_param) * allpasses); - p->delay_length = delay_length; - p->allpass_units = allpasses; - - if (allpass_params) - { - for (int i = 0; i < allpasses; i++) - memcpy(&p->allpasses[i], &allpass_params[i], sizeof(struct allpass_param)); - } - - return p; -} - -static void cbox_reverb_leg_reset(struct cbox_reverb_leg *leg) -{ - cbox_onepolef_reset(&leg->filter_state); - int i; - for (int a = 0; a < leg->params->allpass_units; a++) - for (i = 0; i < ALLPASS_BUFFER; i++) - leg->allpass_storage[a][i] = 0.f; - for (i = 0; i < DELAY_BUFFER; i++) - leg->delay_storage[i] = 0.f; -} - -static void cbox_reverb_leg_init(struct cbox_reverb_leg *leg) -{ - leg->allpass_storage = malloc(leg->params->allpass_units * ALLPASS_BUFFER * sizeof(float)); - cbox_reverb_leg_reset(leg); -} - -static void cbox_reverb_leg_cleanup(struct cbox_reverb_leg *leg) -{ - free(leg->params); - free(leg->allpass_storage); -} - -#define MODULE_PARAMS reverb_params - -struct reverb_params -{ - float decay_time; - float wetamt; - float dryamt; - float lowpass, highpass; -}; - -struct reverb_state -{ - struct cbox_reverb_leg *legs; - int leg_count; - int total_time; -}; - -struct reverb_module -{ - struct cbox_module module; - - struct cbox_onepolef_coeffs filter_coeffs[2]; - struct reverb_params *params, *old_params; - struct reverb_state *state; - float gain; - int pos; -}; - -gboolean reverb_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct reverb_module *m = (struct reverb_module *)ct->user_data; - - EFFECT_PARAM("/wet_amt", "f", wetamt, double, dB2gain_simple, -100, 100) else - EFFECT_PARAM("/dry_amt", "f", dryamt, double, dB2gain_simple, -100, 100) else - EFFECT_PARAM("/decay_time", "f", decay_time, double, , 500, 5000) else - EFFECT_PARAM("/lowpass", "f", lowpass, double, , 30, 20000) else - EFFECT_PARAM("/highpass", "f", highpass, double, , 30, 20000) else - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - return cbox_execute_on(fb, NULL, "/wet_amt", "f", error, gain2dB_simple(m->params->wetamt)) && - cbox_execute_on(fb, NULL, "/dry_amt", "f", error, gain2dB_simple(m->params->dryamt)) && - cbox_execute_on(fb, NULL, "/decay_time", "f", error, m->params->decay_time) && - cbox_execute_on(fb, NULL, "/lowpass", "f", error, m->params->lowpass) && - cbox_execute_on(fb, NULL, "/highpass", "f", error, m->params->highpass) && - CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error); - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); - return TRUE; -} - -void reverb_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) -{ - // struct reverb_module *m = (struct reverb_module *)module; -} - -static void cbox_reverb_process_leg(struct reverb_module *m, int u) -{ - int pos; - int dv; - float *storage; - - struct reverb_state *state = m->state; - struct cbox_reverb_leg *b = &state->legs[u]; - int uprev = u ? (u - 1) : (state->leg_count - 1); - struct cbox_reverb_leg *bprev = &state->legs[uprev]; - - float gain = m->gain; - pos = m->pos; - storage = bprev->delay_storage; - float *buf = b->buffer; - for (int i = 0; i < CBOX_BLOCK_SIZE; i++) - { - buf[i] += cbox_onepolef_process_sample(&b->filter_state, &m->filter_coeffs[u&1], storage[pos & (DELAY_BUFFER - 1)] * gain); - pos++; - } - - int units = b->params->allpass_units; - for (int a = 0; a < units; a++) - { - pos = m->pos; - storage = b->allpass_storage[a]; - dv = b->params->allpasses[a].delay; - float w = b->params->allpasses[a].diffusion; - for (int i = 0; i < CBOX_BLOCK_SIZE; i++) - { - float dry = buf[i]; - float out = dry; - - float delayed = storage[pos & (ALLPASS_BUFFER - 1)]; - - float feedback = sanef(out - w * delayed); - - buf[i] = sanef(feedback * w + delayed); - - storage[(pos + dv) & (ALLPASS_BUFFER - 1)] = feedback; - pos++; - } - } - pos = m->pos; - storage = b->delay_storage; - dv = b->params->delay_length - (u == 0 ? CBOX_BLOCK_SIZE : 0); - for (int i = 0; i < CBOX_BLOCK_SIZE; i++) - { - storage[(pos + dv) & (DELAY_BUFFER - 1)] = buf[i]; - pos++; - } -} - -void reverb_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) -{ - struct reverb_module *m = (struct reverb_module *)module; - struct reverb_params *p = m->params; - - float dryamt = p->dryamt; - float wetamt = p->wetamt; - struct reverb_state *s = m->state; - - if (p != m->old_params) - { - float tpdsr = 2.f * M_PI * m->module.srate_inv; - cbox_onepolef_set_lowpass(&m->filter_coeffs[0], p->lowpass * tpdsr); - cbox_onepolef_set_highpass(&m->filter_coeffs[1], p->highpass * tpdsr); - float rv = p->decay_time * m->module.srate / 1000; - m->gain = pow(0.001, s->total_time / (rv * s->leg_count / 2)); - m->old_params = p; - } - - int mid = s->leg_count >> 1; - memcpy(s->legs[0].buffer, inputs[0], CBOX_BLOCK_SIZE * sizeof(float)); - memcpy(s->legs[mid].buffer, inputs[1], CBOX_BLOCK_SIZE * sizeof(float)); - for (int u = 1; u < mid; u++) - { - for (int i = 0; i < CBOX_BLOCK_SIZE; i++) - s->legs[u].buffer[i] = 0.f; - for (int i = 0; i < CBOX_BLOCK_SIZE; i++) - s->legs[u + mid].buffer[i] = 0.f; - } - - for (int u = 0; u < s->leg_count; u++) - cbox_reverb_process_leg(m, u); - - for (int i = 0; i < CBOX_BLOCK_SIZE; i++) - outputs[0][i] = inputs[0][i] * dryamt + s->legs[mid - 1].buffer[i] * wetamt; - for (int i = 0; i < CBOX_BLOCK_SIZE; i++) - outputs[1][i] = inputs[1][i] * dryamt + s->legs[s->leg_count - 1].buffer[i] * wetamt; - m->pos += CBOX_BLOCK_SIZE; -} - -static void reverb_destroyfunc(struct cbox_module *module_) -{ - struct reverb_module *m = (struct reverb_module *)module_; - free(m->params); - for (int i = 0; i < m->state->leg_count; i++) - cbox_reverb_leg_cleanup(&m->state->legs[i]); - free(m->state->legs); - free(m->state); -} - -static struct reverb_state *create_reverb_state(int leg_count, ...) -{ - struct reverb_state *state = malloc(sizeof(struct reverb_state)); - state->leg_count = leg_count; - state->legs = malloc(state->leg_count * sizeof(struct cbox_reverb_leg)); - va_list va; - va_start(va, leg_count); - state->total_time = 0; - for (int u = 0; u < state->leg_count; u++) - { - int delay_length = va_arg(va, int); - int allpasses = va_arg(va, int); - state->total_time += delay_length; - state->legs[u].params = leg_params_new(delay_length, allpasses, NULL); - for (int i = 0; i < allpasses; i++) - { - struct allpass_param *ap = &state->legs[u].params->allpasses[i]; - ap->delay = va_arg(va, int); - ap->diffusion = va_arg(va, double); - state->total_time += ap->delay * ap->diffusion; // very rough approximation - } - } - va_end(va); - for (int u = 0; u < state->leg_count; u++) - cbox_reverb_leg_init(&state->legs[u]); - return state; -} - -MODULE_CREATE_FUNCTION(reverb) -{ - static int inited = 0; - if (!inited) - { - inited = 1; - } - - struct reverb_module *m = malloc(sizeof(struct reverb_module)); - CALL_MODULE_INIT(m, 2, 2, reverb); - m->module.process_event = reverb_process_event; - m->module.process_block = reverb_process_block; - m->pos = 0; - m->old_params = NULL; - m->params = malloc(sizeof(struct reverb_params)); - m->params->decay_time = cbox_config_get_float(cfg_section, "decay_time", 1000); - m->params->dryamt = cbox_config_get_gain_db(cfg_section, "dry_gain", 0.f); - m->params->wetamt = cbox_config_get_gain_db(cfg_section, "wet_gain", -6.f); - - m->state = create_reverb_state(4, - 133, 3, - 731, 0.45, - 873, 0.5, - 1215, 0.55, - 461, 3, - 1054, 0.5, - 1519, 0.5, - 973, 0.5, - 251, 3, - 617, 0.45, - 941, 0.5, - 1277, 0.55, - 379, 3, - 1119, 0.5, - 1477, 0.5, - 933, 0.5); - -#if 0 - m->state = create_reverb_state(2, - 133, 6, - 1573, 0.35, - 587, 0.35, - 921, 0.45, - 605, 0.5, - 1051, 0.45, - 397, 0.5, - 251, 6, - 1561, 0.35, - 594, 0.35, - 927, 0.55, - 611, 0.5, - 1147, 0.55, - 393, 0.5); -#endif - - m->params->lowpass = cbox_config_get_float(cfg_section, "lowpass", 8000.f); - m->params->highpass = cbox_config_get_float(cfg_section, "highpass", 35.f); - - return &m->module; -} - -struct cbox_module_keyrange_metadata reverb_keyranges[] = { -}; - -struct cbox_module_livecontroller_metadata reverb_controllers[] = { -}; - -DEFINE_MODULE(reverb, 2, 2) - diff --git a/template/calfbox/rt.c b/template/calfbox/rt.c deleted file mode 100644 index f8d658e..0000000 --- a/template/calfbox/rt.c +++ /dev/null @@ -1,506 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "dom.h" -#include "engine.h" -#include "io.h" -#include "midi.h" -#include "module.h" -#include "rt.h" -#include "stm.h" -#include "seq.h" -#include "song.h" -#include -#include -#include - -CBOX_CLASS_DEFINITION_ROOT(cbox_rt) - -static void cbox_rt_process(void *user_data, struct cbox_io *io, uint32_t nframes); - -struct cbox_rt_cmd_instance -{ - struct cbox_rt_cmd_definition *definition; - void *user_data; - int *completed_ptr; // for synchronous commands only -}; - -static gboolean cbox_rt_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct cbox_rt *rt = ct->user_data; - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - if (rt->io) - { - GError *cerror = NULL; - if (cbox_io_get_disconnect_status(rt->io, &cerror)) - { - return cbox_execute_on(fb, NULL, "/audio_channels", "ii", error, rt->io->io_env.input_count, rt->io->io_env.output_count) && - cbox_execute_on(fb, NULL, "/state", "is", error, 1, "OK") && - CBOX_OBJECT_DEFAULT_STATUS(rt, fb, error); - } - else - { - return cbox_execute_on(fb, NULL, "/audio_channels", "ii", error, rt->io->io_env.input_count, rt->io->io_env.output_count) && - cbox_execute_on(fb, NULL, "/state", "is", error, -1, cerror ? cerror->message : "Unknown error") && - CBOX_OBJECT_DEFAULT_STATUS(rt, fb, error); - } - } - else - return cbox_execute_on(fb, NULL, "/audio_channels", "ii", error, 0, 2) && - cbox_execute_on(fb, NULL, "/state", "is", error, 0, "Offline") && - CBOX_OBJECT_DEFAULT_STATUS(rt, fb, error); - } - else if (!strcmp(cmd->command, "/cycle") && !strcmp(cmd->arg_types, "")) - { - if (rt->io && !cbox_io_get_disconnect_status(rt->io, NULL)) - { - return cbox_io_cycle(rt->io, fb, error); - } - else - { - if (rt->io) - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Already connected"); - else - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot cycle connection in off-line mode"); - return FALSE; - } - } - else if (!strcmp(cmd->command, "/flush") && !cmd->arg_types[0]) { - cbox_rt_handle_cmd_queue(rt); - return TRUE; - } - else if (!strncmp(cmd->command, "/engine/", 8)) - return cbox_execute_sub(&rt->engine->cmd_target, fb, cmd, cmd->command + 7, error); - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); -} - -struct cbox_rt *cbox_rt_new(struct cbox_document *doc) -{ - struct cbox_rt *rt = malloc(sizeof(struct cbox_rt)); - CBOX_OBJECT_HEADER_INIT(rt, cbox_rt, doc); - rt->rb_execute = cbox_fifo_new(sizeof(struct cbox_rt_cmd_instance) * RT_CMD_QUEUE_ITEMS); - rt->rb_cleanup = cbox_fifo_new(sizeof(struct cbox_rt_cmd_instance) * RT_CMD_QUEUE_ITEMS * 2); - rt->io = NULL; - rt->engine = NULL; - rt->started = FALSE; - rt->disconnected = FALSE; - rt->io_env.srate = 0; - rt->io_env.buffer_size = 0; - rt->io_env.input_count = 0; - rt->io_env.output_count = 0; - - cbox_command_target_init(&rt->cmd_target, cbox_rt_process_cmd, rt); - CBOX_OBJECT_REGISTER(rt); - cbox_document_set_service(doc, "rt", &rt->_obj_hdr); - - return rt; -} - -struct cbox_objhdr *cbox_rt_newfunc(struct cbox_class *class_ptr, struct cbox_document *doc) -{ - return NULL; -} - -void cbox_rt_destroyfunc(struct cbox_objhdr *obj_ptr) -{ - struct cbox_rt *rt = (void *)obj_ptr; - cbox_fifo_destroy(rt->rb_execute); - cbox_fifo_destroy(rt->rb_cleanup); - - free(rt); -} - -static void cbox_rt_on_disconnected(void *user_data) -{ - struct cbox_rt *rt = user_data; - rt->disconnected = TRUE; -} - -static void cbox_rt_on_reconnected(void *user_data) -{ - struct cbox_rt *rt = user_data; - rt->disconnected = FALSE; -} - -static void cbox_rt_on_midi_outputs_changed(void *user_data) -{ - struct cbox_rt *rt = user_data; - if (rt->engine) - { - cbox_engine_update_song_playback(rt->engine); - cbox_engine_update_output_connections(rt->engine); - } -} - -static void cbox_rt_on_midi_inputs_changed(void *user_data) -{ - struct cbox_rt *rt = user_data; - if (rt->engine) - cbox_engine_update_input_connections(rt->engine); -} - -static void cbox_rt_get_transport_data(void *user_data, gboolean explicit_pos, uint32_t time_samples, struct cbox_transport_position *tp) -{ - struct cbox_rt *rt = user_data; - if (rt->engine) - { - struct cbox_master *master = rt->engine->master; - uint32_t time_ppqn; - if (explicit_pos) - time_ppqn = cbox_master_samples_to_ppqn(master, time_samples); - else - time_ppqn = master->spb ? master->spb->song_pos_ppqn : 0; - struct cbox_bbt bbt; - struct cbox_master_track_item mti; - cbox_master_ppqn_to_bbt(master, &bbt, time_ppqn, &mti); - tp->bar = bbt.bar; - tp->beat = bbt.beat; - tp->tick = bbt.tick; - tp->ticks_per_beat = master->ppqn_factor * 4 / mti.timesig_denom; - tp->bar_start_tick = 0; - tp->timesig_num = mti.timesig_num; - tp->timesig_denom = mti.timesig_denom; - tp->tempo = mti.tempo; - } -} - -void cbox_rt_on_update_io_env(struct cbox_rt *rt) -{ - if (rt->engine) - { - cbox_io_env_copy(&rt->engine->io_env, &rt->io_env); - cbox_master_set_sample_rate(rt->engine->master, rt->io_env.srate); - } -} - -void cbox_rt_set_io(struct cbox_rt *rt, struct cbox_io *io) -{ - assert(!rt->started); - rt->io = io; - if (io) - { - cbox_io_env_copy(&rt->io_env, &io->io_env); - cbox_rt_on_update_io_env(rt); - } - else - { - cbox_io_env_clear(&rt->io_env); - } -} - -void cbox_rt_set_offline(struct cbox_rt *rt, int sample_rate, int buffer_size) -{ - assert(!rt->started); - rt->io = NULL; - rt->io_env.srate = sample_rate; - rt->io_env.buffer_size = buffer_size; - rt->io_env.input_count = 0; - rt->io_env.output_count = 2; - cbox_rt_on_update_io_env(rt); -} - -void cbox_rt_on_started(void *user_data) -{ - struct cbox_rt *rt = user_data; - rt->started = 1; -} - -void cbox_rt_on_stopped(void *user_data) -{ - struct cbox_rt *rt = user_data; - rt->started = 0; -} - -gboolean cbox_rt_on_transport_sync(void *user_data, enum cbox_transport_state state, uint32_t frame) -{ - struct cbox_rt *rt = user_data; - if (!rt->engine) - return TRUE; - return cbox_engine_on_transport_sync(rt->engine, state, frame); -} - -gboolean cbox_rt_on_tempo_sync(void *user_data, double tempo) -{ - struct cbox_rt *rt = user_data; - if (rt->engine) - cbox_engine_on_tempo_sync(rt->engine, tempo); - return TRUE; -} - -void cbox_rt_start(struct cbox_rt *rt, struct cbox_command_target *fb) -{ - if (rt->io) - { - rt->cbs = calloc(1, sizeof(struct cbox_io_callbacks)); - rt->cbs->user_data = rt; - rt->cbs->process = cbox_rt_process; - rt->cbs->on_started = cbox_rt_on_started; - rt->cbs->on_stopped = cbox_rt_on_stopped; - rt->cbs->on_disconnected = cbox_rt_on_disconnected; - rt->cbs->on_reconnected = cbox_rt_on_reconnected; - rt->cbs->on_midi_inputs_changed = cbox_rt_on_midi_inputs_changed; - rt->cbs->on_midi_outputs_changed = cbox_rt_on_midi_outputs_changed; - rt->cbs->on_transport_sync = cbox_rt_on_transport_sync; - rt->cbs->on_tempo_sync = cbox_rt_on_tempo_sync; - rt->cbs->get_transport_data = cbox_rt_get_transport_data; - - assert(!rt->started); - cbox_io_start(rt->io, rt->cbs, fb); - assert(rt->started); - } -} - -void cbox_rt_stop(struct cbox_rt *rt) -{ - if (rt->io) - { - assert(rt->started); - cbox_io_stop(rt->io); - free(rt->cbs); - rt->cbs = NULL; - assert(!rt->started); - } -} - -void cbox_rt_handle_cmd_queue(struct cbox_rt *rt) -{ - struct cbox_rt_cmd_instance cmd; - - while(cbox_fifo_read_atomic(rt->rb_cleanup, &cmd, sizeof(cmd))) - { - assert(!cmd.completed_ptr); - cmd.definition->cleanup(cmd.user_data); - } -} - -static void wait_write_space(struct cbox_fifo *rb) -{ - int t = 0; - while (cbox_fifo_writespace(rb) < sizeof(struct cbox_rt_cmd_instance)) - { - // wait until some space frees up in the execute queue - usleep(1000); - t++; - if (t >= 1000) - { - fprintf(stderr, "Execute queue full, waiting...\n"); - t = 0; - } - } -} - -void cbox_rt_execute_cmd_sync(struct cbox_rt *rt, struct cbox_rt_cmd_definition *def, void *user_data) -{ - struct cbox_rt_cmd_instance cmd; - - if (def->prepare) - if (def->prepare(user_data)) - return; - - // No realtime thread - do it all in the main thread - if (!rt || !rt->started || rt->disconnected) - { - while (!def->execute(user_data)) - ; - if (def->cleanup) - def->cleanup(user_data); - return; - } - - int completed = 0; - memset(&cmd, 0, sizeof(cmd)); - cmd.definition = def; - cmd.user_data = user_data; - cmd.completed_ptr = &completed; - - wait_write_space(rt->rb_execute); - cbox_fifo_write_atomic(rt->rb_execute, &cmd, sizeof(cmd)); - do - { - if (completed) - { - if (def->cleanup) - def->cleanup(user_data); - break; - } - struct cbox_rt_cmd_instance cmd2; - - if (!cbox_fifo_read_atomic(rt->rb_cleanup, &cmd2, sizeof(cmd2))) - { - // still no result in cleanup queue - wait - usleep(10000); - continue; - } - // async command or something from outer layer - clean it up - cmd2.definition->cleanup(cmd2.user_data); - } while(1); -} - -int cbox_rt_execute_cmd_async(struct cbox_rt *rt, struct cbox_rt_cmd_definition *def, void *user_data) -{ - struct cbox_rt_cmd_instance cmd = { def, user_data, NULL }; - - if (def->prepare) - { - int error = def->prepare(user_data); - if (error) - return error; - } - // No realtime thread - do it all in the main thread - if (!rt || !rt->started || rt->disconnected) - { - while (!def->execute(user_data)) - ; - if (def->cleanup) - def->cleanup(user_data); - } else { - wait_write_space(rt->rb_execute); - cbox_fifo_write_atomic(rt->rb_execute, &cmd, sizeof(cmd)); - // will be cleaned up by next sync call or by cbox_rt_cmd_handle_queue - } - - return 0; -} - -//////////////////////////////////////////////////////////////////////////////////////// - -void cbox_rt_process(void *user_data, struct cbox_io *io, uint32_t nframes) -{ - struct cbox_rt *rt = user_data; - if (rt->engine) - cbox_engine_process(rt->engine, io, nframes, io->output_buffers, io->io_env.output_count); - else - cbox_rt_handle_rt_commands(rt); -} - -//////////////////////////////////////////////////////////////////////////////////////// - -void cbox_rt_handle_rt_commands(struct cbox_rt *rt) -{ - struct cbox_rt_cmd_instance cmd; - - // Process command queue, needs engine's MIDI aux buf to be initialised to work - int cost = 0; - while(cost < RT_MAX_COST_PER_CALL && cbox_fifo_peek(rt->rb_execute, &cmd, sizeof(cmd))) - { - int result = (cmd.definition->execute)(cmd.user_data); - if (!result) - break; - cost += result; - cbox_fifo_consume(rt->rb_execute, sizeof(cmd)); - if (cmd.completed_ptr) - *cmd.completed_ptr = 1; - else if (cmd.definition->cleanup) - { - gboolean success = cbox_fifo_write_atomic(rt->rb_cleanup, (const char *)&cmd, sizeof(cmd)); - if (!success) - g_error("Clean-up FIFO full. Main thread deadlock?"); - } - } -} - -//////////////////////////////////////////////////////////////////////////////////////// - -#define cbox_rt_swap_pointers_args(ARG) ARG(void **, ptr) ARG(void *, new_value) - -DEFINE_RT_FUNC(void *, cbox_rt, rt, cbox_rt_swap_pointers) -{ - void *old_value = *ptr; - *ptr = new_value; - return old_value; -} - -#define cbox_rt_swap_pointers_into_args(ARG) ARG(void **, ptr) ARG(void *, new_value) ARG(void **, old_value_p) - -DEFINE_RT_VOID_FUNC(cbox_rt, rt, cbox_rt_swap_pointers_into) -{ - *old_value_p = *ptr; - *ptr = new_value; -} - -#define cbox_rt_swap_pointers_and_update_count_args(ARG) ARG(void **, ptr) ARG(void *, new_value) ARG(uint32_t *, pcount) ARG(uint32_t, new_count) - -DEFINE_RT_FUNC(void *, cbox_rt, rt, cbox_rt_swap_pointers_and_update_count) -{ - void *old_value = *ptr; - *ptr = new_value; - if (pcount) - *pcount = new_count; - return old_value; -} - -//////////////////////////////////////////////////////////////////////////////////////// - -void cbox_rt_array_insert(struct cbox_rt *rt, void ***ptr, uint32_t *pcount, int index, void *new_value) -{ - assert(index >= -1); - assert(index == -1 || (uint32_t)index <= *pcount); - assert(*pcount < (1U << 31)); - void **new_array = stm_array_clone_insert(*ptr, *pcount, index, new_value); - free(cbox_rt_swap_pointers_and_update_count(rt, (void **)ptr, new_array, pcount, *pcount + 1)); -} - -void *cbox_rt_array_remove(struct cbox_rt *rt, void ***ptr, uint32_t *pcount, int index) -{ - if (index == -1) - index = *pcount - 1; - assert(index >= 0); - assert((uint32_t)index < *pcount); - assert(*pcount < (1U << 31)); - void *p = (*ptr)[index]; - void **new_array = stm_array_clone_remove(*ptr, *pcount, index); - free(cbox_rt_swap_pointers_and_update_count(rt, (void **)ptr, new_array, pcount, *pcount - 1)); - return p; -} - -gboolean cbox_rt_array_remove_by_value(struct cbox_rt *rt, void ***ptr, uint32_t *pcount, void *value_to_remove) -{ - for (uint32_t i = 0; i < *pcount; i++) - { - if ((*ptr)[i] == value_to_remove) - { - cbox_rt_array_remove(rt, ptr, pcount, i); - return TRUE; - } - } - return FALSE; -} - -//////////////////////////////////////////////////////////////////////////////////////// - -struct cbox_midi_merger *cbox_rt_get_midi_output(struct cbox_rt *rt, struct cbox_uuid *uuid) -{ - if (rt->engine) - { - struct cbox_midi_merger *merger = cbox_engine_get_midi_output(rt->engine, uuid); - if (merger) - return merger; - } - if (!rt->io) - return NULL; - - struct cbox_midi_output *midiout = cbox_io_get_midi_output(rt->io, NULL, uuid); - return midiout ? &midiout->merger : NULL; -} - -//////////////////////////////////////////////////////////////////////////////////////// - diff --git a/template/calfbox/rt.h b/template/calfbox/rt.h deleted file mode 100644 index 11a9c7c..0000000 --- a/template/calfbox/rt.h +++ /dev/null @@ -1,208 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_RT_H -#define CBOX_RT_H - -#include - -#include "cmd.h" -#include "dom.h" -#include "fifo.h" -#include "ioenv.h" -#include "midi.h" -#include "mididest.h" - -#define RT_CMD_QUEUE_ITEMS 1024 -#define RT_MAX_COST_PER_CALL 100 - -struct cbox_instruments; -struct cbox_scene; -struct cbox_io; -struct cbox_midi_pattern; -struct cbox_song; -struct cbox_rt_cmd_instance; - -struct cbox_rt_cmd_definition -{ - int (*prepare)(void *user_data); // non-zero to skip the whole thing - int (*execute)(void *user_data); // returns cost - void (*cleanup)(void *user_data); -}; - -CBOX_EXTERN_CLASS(cbox_rt) - -struct cbox_rt -{ - CBOX_OBJECT_HEADER() - - struct cbox_io *io; - struct cbox_io_callbacks *cbs; - - struct cbox_fifo *rb_execute, *rb_cleanup; - - struct cbox_command_target cmd_target; - int started, disconnected; - struct cbox_io_env io_env; - struct cbox_engine *engine; -}; - -extern struct cbox_rt *cbox_rt_new(struct cbox_document *doc); - -extern void cbox_rt_set_io(struct cbox_rt *rt, struct cbox_io *io); -extern void cbox_rt_set_offline(struct cbox_rt *rt, int sample_rate, int buffer_size); -extern void cbox_rt_start(struct cbox_rt *rt, struct cbox_command_target *fb); -extern void cbox_rt_handle_cmd_queue(struct cbox_rt *rt); -// This one should be called from RT thread process function to execute the queued RT commands -extern void cbox_rt_handle_rt_commands(struct cbox_rt *rt); -extern void cbox_rt_stop(struct cbox_rt *rt); - -// Those are for calling from the main thread. I will add a RT-thread version later. -extern void cbox_rt_execute_cmd_sync(struct cbox_rt *rt, struct cbox_rt_cmd_definition *cmd, void *user_data); -extern int cbox_rt_execute_cmd_async(struct cbox_rt *rt, struct cbox_rt_cmd_definition *cmd, void *user_data); -extern void *cbox_rt_swap_pointers(struct cbox_rt *rt, void **ptr, void *new_value); -extern void cbox_rt_swap_pointers_into(struct cbox_rt *rt, void **ptr, void *new_value, void **old_value_ptr); -extern void *cbox_rt_swap_pointers_and_update_count(struct cbox_rt *rt, void **ptr, void *new_value, uint32_t *pcount, uint32_t new_count); - -extern void cbox_rt_array_insert(struct cbox_rt *rt, void ***ptr, uint32_t *pcount, int index, void *new_value); -extern void *cbox_rt_array_remove(struct cbox_rt *rt, void ***ptr, uint32_t *pcount, int index); -extern gboolean cbox_rt_array_remove_by_value(struct cbox_rt *rt, void ***ptr, uint32_t *pcount, void *value_to_remove); -extern struct cbox_midi_merger *cbox_rt_get_midi_output(struct cbox_rt *rt, struct cbox_uuid *uuid); - -/////////////////////////////////////////////////////////////////////////////// - -#define GET_RT_FROM_cbox_rt(ptr) (ptr) -#define RT_FUNC_ARG_MEMBER(type, name) type name; -#define RT_FUNC_ARG_LIST(type, name) , type name -#define RT_FUNC_ARG_PASS(type, name) _args.name = name; -#define RT_FUNC_ARG_PASS2(type, name) , _args.name -#define RT_FUNC_ARG_PASS3(type, name) , _args->name -#define RT_FUNC_ARG_PASS4(type, name) , name -#define RT_FUNC_ARG_PASS5(type, name) _args->name = name; -#define DEFINE_RT_FUNC(restype, objtype, argname, name) \ - struct rt_function_args_##name { \ - struct objtype *_obj; \ - restype _result; \ - name##_args(RT_FUNC_ARG_MEMBER) \ - }; \ - static restype RT_IMPL_##name(struct objtype *_obj, int *_cost name##_args(RT_FUNC_ARG_LIST)); \ - int exec_##name(void *user_data) { \ - int cost = 1; \ - struct rt_function_args_##name *_args = user_data;\ - _args->_result = RT_IMPL_##name(_args->_obj, &cost name##_args(RT_FUNC_ARG_PASS3)); \ - return cost; \ - } \ - restype name(struct objtype *_obj name##_args(RT_FUNC_ARG_LIST)) \ - { \ - struct cbox_rt *rt = GET_RT_FROM_##objtype(_obj); \ - if (rt) { \ - struct rt_function_args_##name _args; \ - static struct cbox_rt_cmd_definition _cmd = { .prepare = NULL, .execute = exec_##name, .cleanup = NULL }; \ - _args._obj = _obj; \ - name##_args(RT_FUNC_ARG_PASS) \ - cbox_rt_execute_cmd_sync(rt, &_cmd, &_args); \ - return _args._result; \ - } else { \ - int cost; \ - restype _result; \ - do { \ - cost = 1; \ - _result = RT_IMPL_##name(_obj, &cost name##_args(RT_FUNC_ARG_PASS4)); \ - } while(!cost); \ - return _result; \ - } \ - } \ - restype RT_IMPL_##name(struct objtype *argname, int *_cost name##_args(RT_FUNC_ARG_LIST)) - -#define DEFINE_RT_VOID_FUNC(objtype, argname, name) \ - struct rt_function_args_##name { \ - struct objtype *_obj; \ - name##_args(RT_FUNC_ARG_MEMBER) \ - }; \ - static void RT_IMPL_##name(struct objtype *_obj, int *_cost name##_args(RT_FUNC_ARG_LIST)); \ - int exec_##name(void *user_data) { \ - int cost = 1; \ - struct rt_function_args_##name *_args = user_data;\ - RT_IMPL_##name(_args->_obj, &cost name##_args(RT_FUNC_ARG_PASS3)); \ - return cost; \ - } \ - void name(struct objtype *_obj name##_args(RT_FUNC_ARG_LIST)) \ - { \ - struct cbox_rt *rt = GET_RT_FROM_##objtype(_obj); \ - if (rt) { \ - struct rt_function_args_##name _args = { ._obj = _obj }; \ - static struct cbox_rt_cmd_definition _cmd = { .prepare = NULL, .execute = exec_##name, .cleanup = NULL }; \ - name##_args(RT_FUNC_ARG_PASS) \ - cbox_rt_execute_cmd_sync(rt, &_cmd, &_args); \ - } else { \ - int cost; \ - do { \ - cost = 1; \ - RT_IMPL_##name(_obj, &cost name##_args(RT_FUNC_ARG_PASS4)); \ - } while(!cost); \ - } \ - } \ - void RT_IMPL_##name(struct objtype *argname, int *_cost name##_args(RT_FUNC_ARG_LIST)) - -#define DEFINE_ASYNC_RT_FUNC(objtype, argname, name) \ - struct rt_function_args_##name { \ - struct objtype *_obj; \ - name##_args(RT_FUNC_ARG_MEMBER) \ - }; \ - static void RT_IMPL_##name(struct objtype *_obj, int *_cost name##_args(RT_FUNC_ARG_LIST)); \ - static int prepare_##name(void *user_data); \ - static void cleanup_##name(void *user_data); \ - static int exec_##name(void *user_data) { \ - int cost = 1; \ - struct rt_function_args_##name *_args = user_data;\ - RT_IMPL_##name(_args->_obj, &cost name##_args(RT_FUNC_ARG_PASS3)); \ - return cost; \ - } \ - void name(struct objtype *_obj name##_args(RT_FUNC_ARG_LIST)) \ - { \ - struct cbox_rt *rt = GET_RT_FROM_##objtype(_obj); \ - struct rt_function_args_##name *_args = malloc(sizeof(struct rt_function_args_##name)); \ - _args->_obj = _obj; \ - name##_args(RT_FUNC_ARG_PASS5) \ - static struct cbox_rt_cmd_definition _cmd = { .prepare = prepare_##name, .execute = exec_##name, .cleanup = cleanup_##name }; \ - if (cbox_rt_execute_cmd_async(rt, &_cmd, _args)) \ - free(_args); \ - } \ - void RT_IMPL_##name(struct objtype *argname, int *_cost name##_args(RT_FUNC_ARG_LIST)) - -#define ASYNC_PREPARE_FUNC(objtype, argname, name) \ - static int PREPARE_IMPL_##name(struct objtype *argname, struct rt_function_args_##name *args); \ - int prepare_##name(void *user_data) {\ - struct rt_function_args_##name *_args = user_data;\ - return PREPARE_IMPL_##name(_args->_obj, _args); \ - } \ - int PREPARE_IMPL_##name(struct objtype *argname, struct rt_function_args_##name *args) - -#define ASYNC_CLEANUP_FUNC(objtype, argname, name) \ - static void CLEANUP_IMPL_##name(struct objtype *argname, struct rt_function_args_##name *args); \ - void cleanup_##name(void *user_data) {\ - struct rt_function_args_##name *_args = user_data;\ - CLEANUP_IMPL_##name(_args->_obj, _args); \ - free(_args); \ - } \ - void CLEANUP_IMPL_##name(struct objtype *argname, struct rt_function_args_##name *args) - -#define RT_CALL_AGAIN_LATER() ((*_cost) = 0) -#define RT_SET_COST(n) ((*_cost) = (n)) - -#endif diff --git a/template/calfbox/sampler.c b/template/calfbox/sampler.c deleted file mode 100644 index deb94c7..0000000 --- a/template/calfbox/sampler.c +++ /dev/null @@ -1,781 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config-api.h" -#include "dspmath.h" -#include "engine.h" -#include "errors.h" -#include "midi.h" -#include "module.h" -#include "rt.h" -#include "sampler.h" -#include "sampler_impl.h" -#include "sfzloader.h" -#include "stm.h" -#include -#include -#include -#include -#include -#include -#include -#include - -float sampler_sine_wave[2049]; - -GQuark cbox_sampler_error_quark() -{ - return g_quark_from_string("cbox-sampler-error-quark"); -} - -static void sampler_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs); -static void sampler_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len); -static void sampler_destroyfunc(struct cbox_module *module); - -void sampler_steal_voice(struct sampler_module *m) -{ - int max_age = 0; - struct sampler_voice *voice_found = NULL; - for (int i = 0; i < 16; i++) - { - FOREACH_VOICE(m->channels[i].voices_running, v) - { - if (v->amp_env.cur_stage == 15) - continue; - int age = m->serial_no - v->serial_no; - if (v->gen.loop_start == (uint32_t)-1) - age += (int)((v->gen.bigpos >> 32) * 100.0 / v->gen.cur_sample_end); - else - if (v->released) - age += 10; - if (age > max_age) - { - max_age = age; - voice_found = v; - } - } - } - if (voice_found) - { - voice_found->released = 1; - cbox_envelope_go_to(&voice_found->amp_env, 15); - } -} - -static inline float clip01(float v) -{ - if (v < 0.f) - return 0; - if (v > 1.f) - return 1; - return v; -} - -void sampler_create_voice_from_prevoice(struct sampler_module *m, struct sampler_prevoice *pv) -{ - if (!m->voices_free) - return; - struct sampler_released_groups exgroups; - sampler_released_groups_init(&exgroups); - sampler_voice_start(m->voices_free, pv->channel, pv->layer_data, pv->note, pv->vel, &exgroups); - if (exgroups.low_groups || exgroups.group_count) - sampler_channel_release_groups(pv->channel, pv->note, &exgroups); -} - -void sampler_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) -{ - struct sampler_module *m = (struct sampler_module *)module; - - int active_prevoices[16]; - uint32_t active_prevoices_mask = 0; - FOREACH_PREVOICE(m->prevoices_running, pv) - { - uint32_t c = pv->channel - m->channels; - active_prevoices[c] = (active_prevoices_mask >> c) & 1 ? 1 + active_prevoices[c] : 1; - active_prevoices_mask |= 1 << c; - if (sampler_prevoice_process(pv, m)) - { - sampler_prevoice_unlink(&m->prevoices_running, pv); - sampler_create_voice_from_prevoice(m, pv); - sampler_prevoice_link(&m->prevoices_free, pv); - } - } - - for (int c = 0; c < m->output_pairs + m->aux_pairs; c++) - { - int oo = 2 * c; - for (int i = 0; i < CBOX_BLOCK_SIZE; i++) - outputs[oo][i] = outputs[oo + 1][i] = 0.f; - } - - int vcount = 0, vrel = 0, pvcount = 0; - for (int i = 0; i < 16; i++) - { - int cvcount = 0; - FOREACH_VOICE(m->channels[i].voices_running, v) - { - sampler_voice_process(v, m, outputs); - - if (v->amp_env.cur_stage == 15) - vrel++; - cvcount++; - } - int cpvcount = (active_prevoices_mask >> i) & 1 ? active_prevoices[i] : 0; - m->channels[i].active_voices = cvcount; - m->channels[i].active_prevoices = cpvcount; - vcount += cvcount; - pvcount += cpvcount; - } - m->active_voices = vcount; - m->active_prevoices = pvcount; - if(vcount - vrel > m->max_voices + 1) - sampler_steal_voice(m); - if(vcount - vrel > m->max_voices) - sampler_steal_voice(m); - m->serial_no++; - m->current_time += CBOX_BLOCK_SIZE; -} - -void sampler_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) -{ - struct sampler_module *m = (struct sampler_module *)module; - if (len > 0) - { - int cmd = data[0] >> 4; - int chn = data[0] & 15; - struct sampler_channel *c = &m->channels[chn]; - switch(cmd) - { - case 8: - sampler_channel_stop_note(c, data[1], data[2], FALSE); - break; - - case 9: - if (data[2] > 0) - sampler_channel_start_note(c, data[1], data[2], FALSE); - else - sampler_channel_stop_note(c, data[1], data[2], FALSE); - break; - - case 10: - c->last_polyaft = data[2]; - // Lazy clearing - if (!(c->poly_pressure_mask & (1 << (data[1] >> 2)))) - { - // Clear the group of 4 - memset(c->poly_pressure + (data[1] & ~3), 0, 4); - c->poly_pressure_mask |= 1 << (data[1] >> 2); - } - c->poly_pressure[data[1]] = data[2]; - // XXXKF add a new opcode for cymbal chokes via poly pressure - // if (data[2] == 127) - // sampler_channel_stop_note(c, data[1], data[2], TRUE); - break; - - case 11: - sampler_channel_process_cc(c, data[1], data[2]); - break; - - case 12: - sampler_channel_program_change(c, data[1]); - break; - - case 13: - c->last_chanaft = data[1]; - break; - - case 14: - c->pitchwheel = data[1] + 128 * data[2] - 8192; - break; - - } - } -} - -static int get_first_free_program_no(struct sampler_module *m) -{ - int prog_no = -1; - gboolean found; - - // XXXKF this has a N-squared complexity - but I'm not seeing - // this being used with more than 10 programs at the same time - // in the near future - do { - prog_no++; - found = FALSE; - for (uint32_t i = 0; i < m->program_count; i++) - { - if (m->programs[i]->prog_no == prog_no) - { - found = TRUE; - break; - } - } - } while(found); - - return prog_no; -} - -static int find_program(struct sampler_module *m, int prog_no) -{ - for (uint32_t i = 0; i < m->program_count; i++) - { - if (m->programs[i]->prog_no == prog_no) - return i; - } - return -1; -} - -struct release_program_voices_data -{ - struct sampler_module *module; - - struct sampler_program *old_pgm, *new_pgm; - uint16_t channels_to_wait_for; -}; - -static int release_program_voices_execute(void *data) -{ - struct release_program_voices_data *rpv = data; - struct sampler_module *m = rpv->module; - int finished = 1; - - FOREACH_PREVOICE(m->prevoices_running, pv) - { - if (pv->channel->program == rpv->old_pgm) - { - sampler_prevoice_unlink(&m->prevoices_running, pv); - sampler_prevoice_link(&m->prevoices_free, pv); - } - } - for (int i = 0; i < 16; i++) - { - uint16_t mask = 1 << i; - struct sampler_channel *c = &m->channels[i]; - if (c->program == rpv->old_pgm || c->program == NULL) - { - sampler_channel_set_program_RT(c, rpv->new_pgm); - rpv->channels_to_wait_for |= mask; - } - if (rpv->channels_to_wait_for & mask) - { - FOREACH_VOICE(c->voices_running, v) - { - if (m->deleting || !m->module.rt) - { - sampler_voice_inactivate(v, TRUE); - continue; - } - // This is a new voice, started after program change, so it - // should not be terminated and waited for. - if (v->program == rpv->new_pgm) - continue; - // The voice is still going, so repeat until it fades out - finished = 0; - // If not in final fadeout stage, force final fadeout. - if (v->amp_env.cur_stage != 15) - { - v->released = 1; - cbox_envelope_go_to(&v->amp_env, 15); - } - } - } - } - - return finished; -} - -static void swap_program(struct sampler_module *m, int index, struct sampler_program *pgm, gboolean delete_old) -{ - static struct cbox_rt_cmd_definition release_program_voices = { NULL, release_program_voices_execute, NULL }; - - struct sampler_program *old_program = NULL; - if (pgm) - old_program = cbox_rt_swap_pointers(m->module.rt, (void **)&m->programs[index], pgm); - else - old_program = cbox_rt_array_remove(m->module.rt, (void ***)&m->programs, &m->program_count, index); - - struct release_program_voices_data data = {m, old_program, pgm, 0}; - - cbox_rt_execute_cmd_sync(m->module.rt, &release_program_voices, &data); - - if (delete_old && old_program) - CBOX_DELETE(old_program); -} - -static void select_initial_program(struct sampler_module *m) -{ - static struct cbox_rt_cmd_definition release_program_voices = { NULL, release_program_voices_execute, NULL }; - struct release_program_voices_data data = {m, NULL, m->programs[0], 0}; - cbox_rt_execute_cmd_sync(m->module.rt, &release_program_voices, &data); -} - -void sampler_register_program(struct sampler_module *m, struct sampler_program *pgm) -{ - struct sampler_program **programs = malloc(sizeof(struct sampler_program *) * (m->program_count + 1)); - memcpy(programs, m->programs, sizeof(struct sampler_program *) * m->program_count); - programs[m->program_count] = pgm; - free(cbox_rt_swap_pointers_and_update_count(m->module.rt, (void **)&m->programs, programs, &m->program_count, m->program_count + 1)); - if (m->program_count == 1U) - select_initial_program(m); -} - -static gboolean load_program_at(struct sampler_module *m, const char *cfg_section, const char *name, int prog_no, struct sampler_program **ppgm, GError **error) -{ - struct sampler_program *pgm = NULL; - int index = find_program(m, prog_no); - pgm = sampler_program_new_from_cfg(m, cfg_section, name, prog_no, error); - if (!pgm) - return FALSE; - - if (index != -1) - swap_program(m, index, pgm, TRUE); - else - sampler_register_program(m, pgm); - - if (ppgm) - *ppgm = pgm; - return TRUE; -} - -void sampler_unselect_program(struct sampler_module *m, struct sampler_program *prg) -{ - // Ensure no new notes are played on that program - prg->deleting = TRUE; - // Remove from the list of available programs, so that it cannot be selected again - for (uint32_t i = 0; i < m->program_count; i++) - { - if (m->programs[i] == prg) - swap_program(m, i, NULL, FALSE); - } -} - -static gboolean load_from_string(struct sampler_module *m, const char *sample_dir, const char *sfz_data, const char *name, int prog_no, struct sampler_program **ppgm, GError **error) -{ - int index = find_program(m, prog_no); - struct sampler_program *pgm = sampler_program_new(m, prog_no, name, NULL, sample_dir, error); - if (!pgm) - return FALSE; - pgm->source_file = g_strdup("string"); - if (!sampler_module_load_program_sfz(m, pgm, sfz_data, TRUE, error)) - { - free(pgm); - return FALSE; - } - - if (index != -1) - { - swap_program(m, index, pgm, TRUE); - if (ppgm) - *ppgm = pgm; - return TRUE; - } - - struct sampler_program **programs = calloc((m->program_count + 1), sizeof(struct sampler_program *)); - memcpy(programs, m->programs, sizeof(struct sampler_program *) * m->program_count); - programs[m->program_count] = pgm; - if (ppgm) - *ppgm = pgm; - free(cbox_rt_swap_pointers_and_update_count(m->module.rt, (void **)&m->programs, programs, &m->program_count, m->program_count + 1)); - if (m->program_count == 1) - select_initial_program(m); - return TRUE; -} - -gboolean sampler_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct sampler_module *m = (struct sampler_module *)ct->user_data; - - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - for (int i = 0; i < 16; i++) - { - struct sampler_channel *channel = &m->channels[i]; - gboolean result; - if (channel->program) - result = cbox_execute_on(fb, NULL, "/patch", "iis", error, i + 1, channel->program->prog_no, channel->program->name); - else - result = cbox_execute_on(fb, NULL, "/patch", "iis", error, i + 1, -1, ""); - if (!result) - return FALSE; - if (!(cbox_execute_on(fb, NULL, "/channel_voices", "ii", error, i + 1, channel->active_voices) && - cbox_execute_on(fb, NULL, "/channel_prevoices", "ii", error, i + 1, channel->active_prevoices) && - cbox_execute_on(fb, NULL, "/output", "ii", error, i + 1, channel->output_shift) && - cbox_execute_on(fb, NULL, "/volume", "ii", error, i + 1, sampler_channel_addcc(channel, 7)) && - cbox_execute_on(fb, NULL, "/pan", "ii", error, i + 1, sampler_channel_addcc(channel, 10)))) - return FALSE; - } - - return cbox_execute_on(fb, NULL, "/active_voices", "i", error, m->active_voices) && - cbox_execute_on(fb, NULL, "/active_prevoices", "i", error, m->active_prevoices) && - cbox_execute_on(fb, NULL, "/active_pipes", "i", error, cbox_prefetch_stack_get_active_pipe_count(m->pipe_stack)) && - cbox_execute_on(fb, NULL, "/polyphony", "i", error, m->max_voices) && - CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error); - } - else - if (!strcmp(cmd->command, "/keyswitch_state") && !strcmp(cmd->arg_types, "ii")) - { - int channel = CBOX_ARG_I(cmd, 0); - if (channel < 1 || channel > 16) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid channel %d", channel); - return FALSE; - } - int group = CBOX_ARG_I(cmd, 1); - if (group < 0 || group >= MAX_KEYSWITCH_GROUPS) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid keyswitch group %d", group); - return FALSE; - } - if (!cbox_execute_on(fb, NULL, "/last_key", "i", error, m->channels[channel - 1].keyswitch_lastkey[group])) - return FALSE; - return TRUE; - } - else - if (!strcmp(cmd->command, "/patches") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - for (uint32_t i = 0; i < m->program_count; i++) - { - struct sampler_program *prog = m->programs[i]; - if (!cbox_execute_on(fb, NULL, "/patch", "isoi", error, prog->prog_no, prog->name, prog, prog->in_use)) - return FALSE; - } - return TRUE; - } - else if (!strcmp(cmd->command, "/polyphony") && !strcmp(cmd->arg_types, "i")) - { - int polyphony = CBOX_ARG_I(cmd, 0); - if (polyphony < 1 || polyphony > MAX_SAMPLER_VOICES) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid polyphony %d (must be between 1 and %d)", polyphony, (int)MAX_SAMPLER_VOICES); - return FALSE; - } - m->max_voices = polyphony; - return TRUE; - } - else if (!strcmp(cmd->command, "/set_patch") && !strcmp(cmd->arg_types, "ii")) - { - int channel = CBOX_ARG_I(cmd, 0); - if (channel < 1 || channel > 16) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid channel %d", channel); - return FALSE; - } - int value = CBOX_ARG_I(cmd, 1); - struct sampler_program *pgm = NULL; - for (uint32_t i = 0; i < m->program_count; i++) - { - if (m->programs[i]->prog_no == value) - { - pgm = m->programs[i]; - break; - } - } - sampler_channel_set_program(&m->channels[channel - 1], pgm); - return TRUE; - } - else if (!strcmp(cmd->command, "/set_output") && !strcmp(cmd->arg_types, "ii")) - { - int channel = CBOX_ARG_I(cmd, 0); - int output = CBOX_ARG_I(cmd, 1); - if (channel < 1 || channel > 16) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid channel %d", channel); - return FALSE; - } - if (output < 0 || output >= m->output_pairs) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid output %d", output); - return FALSE; - } - m->channels[channel - 1].output_shift = output; - return TRUE; - } - else if (!strcmp(cmd->command, "/load_patch") && !strcmp(cmd->arg_types, "iss")) - { - struct sampler_program *pgm = NULL; - if (!load_program_at(m, CBOX_ARG_S(cmd, 1), CBOX_ARG_S(cmd, 2), CBOX_ARG_I(cmd, 0), &pgm, error)) - return FALSE; - if (fb) - return cbox_execute_on(fb, NULL, "/uuid", "o", error, pgm); - return TRUE; - } - else if (!strcmp(cmd->command, "/load_patch_from_file") && !strcmp(cmd->arg_types, "iss")) - { - struct sampler_program *pgm = NULL; - char *cfg_section = g_strdup_printf("spgm:!%s", CBOX_ARG_S(cmd, 1)); - gboolean res = load_program_at(m, cfg_section, CBOX_ARG_S(cmd, 2), CBOX_ARG_I(cmd, 0), &pgm, error); - g_free(cfg_section); - if (res && pgm && fb) - return cbox_execute_on(fb, NULL, "/uuid", "o", error, pgm); - return res; - } - else if (!strcmp(cmd->command, "/load_patch_from_string") && !strcmp(cmd->arg_types, "isss")) - { - struct sampler_program *pgm = NULL; - if (!load_from_string(m, CBOX_ARG_S(cmd, 1), CBOX_ARG_S(cmd, 2), CBOX_ARG_S(cmd, 3), CBOX_ARG_I(cmd, 0), &pgm, error)) - return FALSE; - if (fb && pgm) - return cbox_execute_on(fb, NULL, "/uuid", "o", error, pgm); - return TRUE; - } - else if (!strcmp(cmd->command, "/get_unused_program") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - return cbox_execute_on(fb, NULL, "/program_no", "i", error, get_first_free_program_no(m)); - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); - return TRUE; -} - -gboolean sampler_select_program(struct sampler_module *m, int channel, const gchar *preset, GError **error) -{ - for (uint32_t i = 0; i < m->program_count; i++) - { - if (!strcmp(m->programs[i]->name, preset)) - { - sampler_channel_set_program(&m->channels[channel], m->programs[i]); - return TRUE; - } - } - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Preset not found: %s", preset); - return FALSE; -} - -double sampler_get_current_beat(struct sampler_module *m) -{ - uint32_t song_pos_samples = cbox_engine_current_pos_samples(m->module.engine); - double tempo_bpm = m->module.engine->master->tempo; - double song_pos_sec = song_pos_samples * 1.0 / m->module.srate; - return song_pos_sec * tempo_bpm / 60; -} - -MODULE_CREATE_FUNCTION(sampler) -{ - int i; - static int inited = 0; - if (!inited) - { - for (int i = 0; i < 2049; i++) - sampler_sine_wave[i] = sin(i * M_PI / 1024.0); - inited = 1; - } - - int max_voices = cbox_config_get_int(cfg_section, "polyphony", MAX_SAMPLER_VOICES); - if (max_voices < 1 || max_voices > MAX_SAMPLER_VOICES) - { - g_set_error(error, CBOX_SAMPLER_ERROR, CBOX_SAMPLER_ERROR_INVALID_LAYER, "%s: invalid polyphony value", cfg_section); - return NULL; - } - int output_pairs = cbox_config_get_int(cfg_section, "output_pairs", 1); - if (output_pairs < 1 || output_pairs > 16) - { - g_set_error(error, CBOX_SAMPLER_ERROR, CBOX_SAMPLER_ERROR_INVALID_LAYER, "%s: invalid output pairs value", cfg_section); - return NULL; - } - int aux_pairs = cbox_config_get_int(cfg_section, "aux_pairs", 0); - if (aux_pairs < 0 || aux_pairs > 4) - { - g_set_error(error, CBOX_SAMPLER_ERROR, CBOX_SAMPLER_ERROR_INVALID_LAYER, "%s: invalid aux pairs value", cfg_section); - return NULL; - } - - struct sampler_module *m = calloc(1, sizeof(struct sampler_module)); - CALL_MODULE_INIT(m, 0, (output_pairs + aux_pairs) * 2, sampler); - m->output_pairs = output_pairs; - m->aux_pairs = aux_pairs; - m->module.aux_offset = m->output_pairs * 2; - m->module.process_event = sampler_process_event; - m->module.process_block = sampler_process_block; - m->programs = NULL; - m->max_voices = max_voices; - m->serial_no = 0; - m->deleting = FALSE; - // XXXKF read defaults from some better place, like config - // XXXKF allow dynamic change of the number of the pipes - m->pipe_stack = cbox_prefetch_stack_new(MAX_SAMPLER_VOICES, cbox_config_get_int("streaming", "streambuf_size", 65536), cbox_config_get_int("streaming", "min_buf_frames", PIPE_MIN_PREFETCH_SIZE_FRAMES)); - m->disable_mixer_controls = cbox_config_get_int("sampler", "disable_mixer_controls", 0); - - float srate = m->module.srate; - for (i = 0; i < 12800; i++) - { - float freq = 440 * pow(2, (i - 5700) / 1200.0); - if (freq < 20.0) - freq = 20.0; - if (freq > srate * 0.45) - freq = srate * 0.45; - float omega=(float)(2*M_PI*freq/srate); - m->sincos[i].sine = sinf(omega); - m->sincos[i].cosine = cosf(omega); - m->sincos[i].prewarp = 2.0 * tan(hz2w(freq, srate) * 0.5f); - m->sincos[i].prewarp2 = 1.0 / (1.0 + m->sincos[i].prewarp); - - } - for (i = 0; ; i++) - { - gchar *s = g_strdup_printf("program%d", i); - char *p = cbox_config_get_string(cfg_section, s); - g_free(s); - - if (!p) - { - m->program_count = i; - break; - } - } - m->programs = calloc(m->program_count, sizeof(struct sampler_program *)); - int success = 1; - for (i = 0; i < (int)m->program_count; i++) - { - gchar *s = g_strdup_printf("program%d", i); - char *pgm_section = NULL; - int pgm_id = -1; - const char *pgm_name = cbox_config_get_string(cfg_section, s); - g_free(s); - char *at = strchr(pgm_name, '@'); - if (at) - { - pgm_id = atoi(at + 1); - s = g_strndup(pgm_name, at - pgm_name); - pgm_section = g_strdup_printf("spgm:%s", s); - g_free(s); - } - else - { - pgm_id = i; - pgm_section = g_strdup_printf("spgm:%s", pgm_name); - } - - m->programs[i] = sampler_program_new_from_cfg(m, pgm_section, pgm_section + 5, pgm_id, error); - g_free(pgm_section); - if (!m->programs[i]) - { - success = 0; - break; - } - } - if (!success) - { - // XXXKF free programs/layers, first ensuring that they're fully initialised - free(m); - return NULL; - } - m->voices_free = NULL; - memset(m->voices_all, 0, sizeof(m->voices_all)); - for (i = 0; i < MAX_SAMPLER_VOICES; i++) - { - struct sampler_voice *v = &m->voices_all[i]; - v->gen.mode = spt_inactive; - sampler_voice_link(&m->voices_free, v); - } - m->active_voices = 0; - m->active_prevoices = 0; - - m->prevoices_free = NULL; - memset(m->prevoices_all, 0, sizeof(m->prevoices_all)); - for (i = 0; i < MAX_SAMPLER_PREVOICES; i++) - { - struct sampler_prevoice *v = &m->prevoices_all[i]; - sampler_prevoice_link(&m->prevoices_free, v); - } - - for (i = 0; i < 16; i++) - sampler_channel_init(&m->channels[i], m); - - for (i = 0; i < 16; i++) - { - gchar *key = g_strdup_printf("channel%d", i + 1); - gchar *preset = cbox_config_get_string(cfg_section, key); - if (preset) - { - if (!sampler_select_program(m, i, preset, error)) - { - CBOX_DELETE(&m->module); - return NULL; - } - } - g_free(key); - key = g_strdup_printf("channel%d_output", i + 1); - m->channels[i].output_shift = cbox_config_get_int(cfg_section, key, 1) - 1; - g_free(key); - } - - - return &m->module; -} - -void sampler_destroyfunc(struct cbox_module *module) -{ - struct sampler_module *m = (struct sampler_module *)module; - uint32_t i; - m->deleting = TRUE; - - for (i = 0; i < m->program_count;) - { - if (m->programs[i]) - CBOX_DELETE(m->programs[i]); - else - i++; - } - for (i = 0; i < 16U; i++) - { - assert (m->channels[i].voices_running == NULL); - } - cbox_prefetch_stack_destroy(m->pipe_stack); - free(m->programs); -} - -#define MAKE_TO_STRING_CONTENT(name, v) \ - case v: return name; - -#define MAKE_FROM_STRING_CONTENT(n, v) \ - if (!strcmp(name, n)) { *value = v; return TRUE; } - -#define MAKE_FROM_TO_STRING(enumtype) \ - const char *enumtype##_to_string(enum enumtype value) \ - { \ - switch(value) { \ - ENUM_VALUES_##enumtype(MAKE_TO_STRING_CONTENT) \ - default: return NULL; \ - } \ - } \ - \ - gboolean enumtype##_from_string(const char *name, enum enumtype *value) \ - { \ - ENUM_VALUES_##enumtype(MAKE_FROM_STRING_CONTENT) \ - return FALSE; \ - } - -ENUM_LIST(MAKE_FROM_TO_STRING) - -////////////////////////////////////////////////////////////////////////// - -struct cbox_module_livecontroller_metadata sampler_controllers[] = { -}; - -struct cbox_module_keyrange_metadata sampler_keyranges[] = { -}; - -DEFINE_MODULE(sampler, 0, 2) - diff --git a/template/calfbox/sampler.h b/template/calfbox/sampler.h deleted file mode 100644 index e1bd0a0..0000000 --- a/template/calfbox/sampler.h +++ /dev/null @@ -1,395 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_SAMPLER_H -#define CBOX_SAMPLER_H - -#include "biquad-float.h" -#include "envelope.h" -#include "module.h" -#include "onepole-float.h" -#include "prefetch_pipe.h" -#include "sampler_layer.h" -#include "sampler_prg.h" -#include "wavebank.h" -#include - -#define MAX_SAMPLER_VOICES 128 -#define MAX_SAMPLER_PREVOICES 128 -#define SAMPLER_NO_LOOP ((uint32_t)-1) - -#define CBOX_SAMPLER_ERROR cbox_sampler_error_quark() - -enum CboxSamplerError -{ - CBOX_SAMPLER_ERROR_FAILED, - CBOX_SAMPLER_ERROR_INVALID_LAYER, - CBOX_SAMPLER_ERROR_INVALID_WAVEFORM, - CBOX_SAMPLER_ERROR_NO_PROGRAMS -}; - -struct sampler_noteinitfunc; -struct sampler_voice; -struct sampler_prevoice; - -#define GET_RT_FROM_sampler_channel(channel) ((channel)->module->module.rt) - -#define MAX_KEYSWITCH_GROUPS 16 - -struct sampler_channel -{ - struct sampler_module *module; - int pitchwheel; - uint32_t switchmask[4]; - uint32_t sustainmask[4]; - uint32_t sostenutomask[4]; - int previous_note, first_note_vel; - struct sampler_program *program; - struct sampler_voice *voices_running; - int active_voices, active_prevoices; - uint8_t prev_note_velocity[128]; - uint8_t poly_pressure[128]; - uint32_t prev_note_start_time[128]; - int channel_volume_cc, channel_pan_cc; - int output_shift; - uint32_t poly_pressure_mask; - uint8_t intcc[smsrc_perchan_count]; - float floatcc[smsrc_perchan_count]; - uint8_t last_polyaft, last_chanaft; - uint8_t keyswitch_state[MAX_KEYSWITCH_GROUPS]; - uint8_t keyswitch_lastkey[MAX_KEYSWITCH_GROUPS]; -}; - -struct sampler_lfo -{ - uint32_t phase, delta, xdelta; - uint32_t age, delay, fade; - int32_t wave; - float random_value; -}; - -struct sampler_gen -{ - enum sampler_player_type mode; - int16_t *sample_data; - int16_t *scratch; - - uint64_t bigpos, bigdelta; - uint64_t virtpos, virtdelta; - uint32_t loop_start, loop_end; - uint32_t cur_sample_end; - float lgain, rgain; - float last_lgain, last_rgain; - float fadein_counter; - uint64_t fadein_pos; - - // In-memory mode only - uint32_t loop_overlap; - float loop_overlap_step; - float stretching_jump; - float stretching_crossfade; - uint32_t play_count, loop_count; - int16_t scratch_bandlimited[2 * MAX_INTERPOLATION_ORDER * 2]; - - // Streaming mode only - int16_t *streaming_buffer; - uint32_t consumed, consumed_credit, streaming_buffer_frames; - gboolean prefetch_only_loop, in_streaming_buffer; -}; - -struct sampler_prevoice -{ - struct sampler_prevoice *prev, *next; - struct sampler_layer_data *layer_data; - struct sampler_channel *channel; - int note, vel; - int age; - double sync_trigger_time, sync_initial_time, sync_beats; - float delay_computed; -}; - -struct sampler_filter -{ - struct cbox_biquadf_coeffs filter_coeffs, filter_coeffs_extra; - struct cbox_biquadf_coeffs *second_filter; - struct cbox_biquadf_state filter_left[3], filter_right[3]; -}; - -struct sampler_voice -{ - struct sampler_voice *prev, *next; - struct sampler_layer_data *layer; - // Note: may be NULL when program is being deleted - struct sampler_program *program; - struct cbox_waveform *last_waveform; - struct sampler_gen gen; - struct cbox_prefetch_pipe *current_pipe; - int note; - int vel; - int released, released_with_sustain, released_with_sostenuto, captured_sostenuto; - int off_by; - int age; - float pitch_shift; - float cutoff_shift, cutoff2_shift; - float gain_shift, gain_fromvel; - struct sampler_filter filter, filter2; - struct cbox_onepolef_state onepole_left, onepole_right; - struct cbox_onepolef_coeffs onepole_coeffs; - struct sampler_channel *channel; - struct cbox_envelope amp_env, filter_env, pitch_env; - struct sampler_lfo amp_lfo, filter_lfo, pitch_lfo; - enum sampler_loop_mode loop_mode; - int output_pair_no; - int send1bus, send2bus; - float send1gain, send2gain; - int serial_no; - struct cbox_envelope_shape vel_envs[3], cc_envs[3]; // amp, filter, pitch - struct cbox_biquadf_state eq_left[3], eq_right[3]; - struct cbox_biquadf_coeffs eq_coeffs[3]; - gboolean layer_changed; - int last_level; - uint64_t last_level_min_rate; - uint32_t last_eq_bitmask; - float reloffset; - uint32_t offset; - int off_vel; -}; - -struct sampler_module -{ - struct cbox_module module; - - struct sampler_voice *voices_free, voices_all[MAX_SAMPLER_VOICES]; - struct sampler_prevoice *prevoices_free, prevoices_all[MAX_SAMPLER_PREVOICES], *prevoices_running; - struct sampler_channel channels[16]; - struct sampler_program **programs; - uint32_t program_count; - int active_voices, max_voices; - int active_prevoices; - int serial_no; - int output_pairs, aux_pairs; - uint32_t current_time; - gboolean deleting; - int disable_mixer_controls; - struct cbox_prefetch_stack *pipe_stack; - struct cbox_sincos sincos[12800]; -}; - -#define MAX_RELEASED_GROUPS 16 - -struct sampler_released_groups -{ - // Groups 1-32 use a bitmask - uint32_t low_groups; - int group_count; - int groups[MAX_RELEASED_GROUPS]; -}; - -static inline void sampler_released_groups_init(struct sampler_released_groups *groups) -{ - groups->low_groups = 0; - groups->group_count = 0; -} - -static inline gboolean sampler_released_groups_check(struct sampler_released_groups *groups, int group) -{ - if (group <= 32) - return (groups->low_groups >> (group - 1)) & 1; - for (int j = 0; j < groups->group_count; j++) - { - if (groups->groups[j] == group) - return TRUE; - } - return FALSE; -} - -static inline void sampler_released_groups_add(struct sampler_released_groups *groups, int group) -{ - if (group <= 32) - { - groups->low_groups |= (1 << (group - 1)); - return; - } - if (groups->group_count >= MAX_RELEASED_GROUPS) - return; - if (!sampler_released_groups_check(groups, group)) - groups->groups[groups->group_count++] = group; -} - -extern GQuark cbox_sampler_error_quark(void); - -extern void sampler_register_program(struct sampler_module *m, struct sampler_program *pgm); -extern gboolean sampler_select_program(struct sampler_module *m, int channel, const gchar *preset, GError **error); -extern void sampler_unselect_program(struct sampler_module *m, struct sampler_program *prg); -extern double sampler_get_current_beat(struct sampler_module *m); - -extern void sampler_channel_init(struct sampler_channel *c, struct sampler_module *m); -// This function may only be called from RT thread! -extern void sampler_channel_set_program_RT(struct sampler_channel *c, struct sampler_program *prg); -// ... and this one is RT-safe -extern void sampler_channel_set_program(struct sampler_channel *c, struct sampler_program *prg); -extern void sampler_channel_start_note(struct sampler_channel *c, int note, int vel, gboolean is_release_trigger); -extern void sampler_channel_stop_note(struct sampler_channel *c, int note, int vel, gboolean is_polyaft); -extern void sampler_channel_program_change(struct sampler_channel *c, int program); -extern void sampler_channel_stop_sustained(struct sampler_channel *c); -extern void sampler_channel_stop_sostenuto(struct sampler_channel *c); -extern void sampler_channel_capture_sostenuto(struct sampler_channel *c); -extern void sampler_channel_release_groups(struct sampler_channel *c, int note, struct sampler_released_groups *exgroups); -extern void sampler_channel_stop_all(struct sampler_channel *c); -extern void sampler_channel_process_cc(struct sampler_channel *c, int cc, int val); -extern void sampler_channel_reset_keyswitches(struct sampler_channel *c); - -extern void sampler_voice_start(struct sampler_voice *v, struct sampler_channel *c, struct sampler_layer_data *l, int note, int vel, struct sampler_released_groups *exgroups); -extern void sampler_voice_start_silent(struct sampler_layer_data *l, struct sampler_released_groups *exgroups); -extern void sampler_voice_release(struct sampler_voice *v, gboolean is_polyaft); -extern void sampler_voice_process(struct sampler_voice *v, struct sampler_module *m, cbox_sample_t **outputs); -extern void sampler_voice_link(struct sampler_voice **pv, struct sampler_voice *v); -extern void sampler_voice_unlink(struct sampler_voice **pv, struct sampler_voice *v); -extern void sampler_voice_inactivate(struct sampler_voice *v, gboolean expect_active); -extern void sampler_voice_update_params_from_layer(struct sampler_voice *v); -extern float sampler_channel_get_expensive_cc(struct sampler_channel *c, struct sampler_voice *v, struct sampler_prevoice *pv, int cc_no); - -extern void sampler_prevoice_start(struct sampler_prevoice *pv, struct sampler_channel *c, struct sampler_layer_data *l, int note, int vel); -extern int sampler_prevoice_process(struct sampler_prevoice *pv, struct sampler_module *m); -extern void sampler_prevoice_link(struct sampler_prevoice **pv, struct sampler_prevoice *v); -extern void sampler_prevoice_unlink(struct sampler_prevoice **pv, struct sampler_prevoice *v); - -extern float sampler_sine_wave[2049]; - -static inline int sampler_channel_addcc(struct sampler_channel *c, int cc_no) -{ - return (((int)c->intcc[cc_no]) << 7) + c->intcc[cc_no + 32]; -} - -static inline float sampler_channel_getcc(struct sampler_channel *c, struct sampler_voice *v, int cc_no) -{ - if (cc_no < 128) - return c->floatcc[cc_no]; - return sampler_channel_get_expensive_cc(c, v, NULL, cc_no); -} - -static inline float sampler_program_get_curve_value(struct sampler_program *program, uint32_t curve_id, float val) -{ - if (val < 0) - val = 0; - if (val > 1) - val = 1; - if (curve_id < MAX_MIDI_CURVES && program->interpolated_curves[curve_id]) - { - float *curve = program->interpolated_curves[curve_id]; - int vint = floorf(val * 127); - // Linear interpolation if within bounds - if (vint < 127) - { - float vfrac = val * 127 - vint; - val = curve[vint] + (curve[vint + 1] - curve[vint]) * vfrac; - } - else - val = curve[vint]; - } - else - { - // possibly wrong implementation of built in curves - switch(curve_id) - { - case 0: - break; - case 1: - case 3: - // slightly fake bipolar, so that both 63 and 64 are 'neutral' (needs to be somewhat symmetric) - if (val < 63.f/127.f) - val = (curve_id == 3 ? -1 : 1) * -(63.f - 127 * val) / 63.f; - else if (val <= 64.f/127.f) - val = 0; - else - val = (curve_id == 3 ? -1 : 1) * (127 * val - 64) / 63.f; - break; - case 2: - val = 1 - val; break; - case 4: - val = val * val; - break; // maybe, or maybe it's inverted? - case 5: val = sqrtf(val); - break; // maybe - case 6: val = sqrtf(1-val); - break; // maybe - } - } - return val; -} - -static inline float sampler_channel_getcc_mod(struct sampler_channel *c, struct sampler_voice *v, int cc_no, int curve_id, float step) -{ - float val = (cc_no < 128) ? c->floatcc[cc_no] : sampler_channel_get_expensive_cc(c, v, NULL, cc_no); - if (step) - val = floorf(0.9999f * val * (step + 1)) / step; - if (curve_id || c->program->interpolated_curves[0]) - val = sampler_program_get_curve_value(c->program, curve_id, val); - return val; -} - -static inline int sampler_channel_getintcc(struct sampler_channel *c, struct sampler_voice *v, int cc_no) -{ - if (cc_no < 128) - return c->intcc[cc_no]; - return (int)127 * (sampler_channel_get_expensive_cc(c, v, NULL, cc_no)); -} - -static inline float sampler_channel_getcc_prevoice(struct sampler_channel *c, struct sampler_prevoice *pv, int cc_no, int curve_id, float step) -{ - float val = (cc_no < 128) ? c->floatcc[cc_no] : sampler_channel_get_expensive_cc(c, NULL, pv, cc_no); - if (step) - val = floorf(0.9999f * val * (step + 1)) / step; - if (curve_id || c->program->interpolated_curves[0]) - val = sampler_program_get_curve_value(c->program, curve_id, val); - return val; -} - -static inline float sampler_channel_get_poly_pressure(struct sampler_channel *c, uint8_t note) -{ - note &= 0x7F; - return (c->poly_pressure_mask & (1 << (note >> 2))) ? c->poly_pressure[note] * (1.f / 127.f) : 0;; -} - -static inline gboolean sampler_cc_range_is_in(const struct sampler_cc_range *range, struct sampler_channel *c) -{ - while(range) - { - int ccval = sampler_channel_getintcc(c, NULL, range->key.cc_number); - if (ccval < range->value.locc || ccval > range->value.hicc) - return FALSE; - range = range->next; - } - return TRUE; -} - -#define FOREACH_VOICE(var, p) \ - for (struct sampler_voice *p = (var), *p##_next = NULL; p && (p##_next = p->next, TRUE); p = p##_next) -#define FOREACH_PREVOICE(var, p) \ - for (struct sampler_prevoice *p = (var), *p##_next = NULL; p && (p##_next = p->next, TRUE); p = p##_next) -#define CANCEL_PREVOICE(var, p) \ - {\ - struct sampler_prevoice *_tmp = (p); \ - if (_tmp->prev) \ - _tmp->prev->next = _tmp->next; \ - else \ - var = _tmp->next; \ - _tmp->prev = _tmp->next = NULL; \ - } - -#endif diff --git a/template/calfbox/sampler_api_example.py b/template/calfbox/sampler_api_example.py deleted file mode 100644 index 84bea11..0000000 --- a/template/calfbox/sampler_api_example.py +++ /dev/null @@ -1,59 +0,0 @@ -import os -import sys -import struct -import time -import unittest - -sys.path = ["./py"] + sys.path - -import cbox - -global Document -Document = cbox.Document - -scene = Document.get_scene() -scene.clear() -instrument = scene.add_new_instrument_layer("test_sampler", "sampler").get_instrument() - -npfs = instrument.engine.load_patch_from_string(0, '.', '', 'new_patch') -instrument.engine.set_patch(1, 0) - -mgrp = npfs.get_global().get_children()[0] -g1 = mgrp.new_child() -g1.set_param("cutoff", "100") -g1.set_param("resonance", "6") -g1.set_param("fil_type", "lpf_4p") -g1.set_param("fileg_start", "50") -g1.set_param("fileg_attack", "0.01") -g1.set_param("fileg_decay", "0.2") -g1.set_param("fileg_sustain", "20") -g1.set_param("fileg_depth", "5400") -g1.set_param("fileg_release", "10") -g1.set_param("ampeg_release", "0.1") -g1.set_param("amp_veltrack", "0") -g1.set_param("volume", "-12") -g1.set_param("fileg_depthcc14", "-5400") - -#g1.set_param("cutoff", "1000") -#g1.set_param("fillfo_freq", "4") -#g1.set_param("fillfo_depth", "2400") -#g1.set_param("fillfo_wave", "12") -#g1.set_param("fillfo_freqcc2", "4") - -r1 = g1.new_child() -r1.set_param("sample", "*saw") -r1.set_param("transpose", "0") -r1.set_param("tune", "5") -r1.set_param("gain_cc17", "12") - -r2 = g1.new_child() -r2.set_param("sample", "*sqr") -r2.set_param("transpose", "12") -r2.set_param("gain_cc17", "-12") - -print(instrument.engine.status()) - -print("Ready!") - -while True: - cbox.call_on_idle() diff --git a/template/calfbox/sampler_api_example2.py b/template/calfbox/sampler_api_example2.py deleted file mode 100644 index 8ea328e..0000000 --- a/template/calfbox/sampler_api_example2.py +++ /dev/null @@ -1,38 +0,0 @@ -from calfbox import cbox - -def cmd_dumper(cmd, fb, args): - print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) - -cbox.init_engine() -cbox.start_audio(cmd_dumper) - -global Document -Document = cbox.Document - -scene = Document.get_scene() -scene.clear() -instrument = scene.add_new_instrument_layer("test_sampler", "sampler").get_instrument() -pgm_no = instrument.engine.get_unused_program() -pgm = instrument.engine.load_patch_from_file(pgm_no, 'synthbass.sfz', 'SynthBass') - -# This is not needed, it's here to test for a bug where reloading a program at -# the same slot didn't return the newly loaded program object. -pgm = instrument.engine.load_patch_from_file(pgm_no, 'synthbass.sfz', 'SynthBass') -assert pgm - -instrument.engine.set_patch(1, pgm_no) -print (instrument.engine.get_patches()) -print (instrument.get_output_slot(0)) -print (instrument.get_output_slot(0).status()) -instrument.get_output_slot(0).set_insert_engine("reverb") -print (instrument.get_output_slot(0).status()) -instrument.get_output_slot(0).engine.cmd("/wet_amt", None, 1.0) -for i in pgm.get_global().get_children()[0].get_children(): - print ("", i.as_string()) - for j in i.get_children(): - print ("", j.as_string()) - -print("Ready!") - -while True: - cbox.call_on_idle(cmd_dumper) diff --git a/template/calfbox/sampler_api_example4.py b/template/calfbox/sampler_api_example4.py deleted file mode 100644 index 38cb8f3..0000000 --- a/template/calfbox/sampler_api_example4.py +++ /dev/null @@ -1,27 +0,0 @@ -from calfbox import cbox - -def cmd_dumper(cmd, fb, args): - print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) - -cbox.init_engine() -cbox.start_audio(cmd_dumper) - -global Document -Document = cbox.Document - -scene = Document.get_scene() -scene.clear() -instrument = scene.add_new_instrument_layer("test_sampler", "sampler").get_instrument() -pgm_no = instrument.engine.get_unused_program() -pgm = instrument.engine.load_patch_from_tar(pgm_no, 'sonatina.sbtar', 'Brass - Horn Solo.sfz', 'HornSolo') -pgm.add_control_init(7, 127) -pgm.add_control_init(10, 0) -print (pgm.load_file('Brass - Horn Solo.sfz').readlines()) -print (pgm.status()) -print (list(pgm.get_control_inits())) -instrument.engine.set_patch(1, pgm_no) - -print("Ready!") - -while True: - cbox.call_on_idle(cmd_dumper) diff --git a/template/calfbox/sampler_api_example5.py b/template/calfbox/sampler_api_example5.py deleted file mode 100644 index 43f8670..0000000 --- a/template/calfbox/sampler_api_example5.py +++ /dev/null @@ -1,186 +0,0 @@ -from calfbox import cbox -from pprint import pprint - -def cmd_dumper(cmd, fb, args): - print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) - -cbox.init_engine() -cbox.start_audio(cmd_dumper) - -global Document -Document = cbox.Document - -scene = Document.get_scene() -scene.clear() -instrument = scene.add_new_instrument_layer("test_sampler", "sampler").get_instrument() -pgm_no = instrument.engine.get_unused_program() - - - -#A pseudo sfz file that features all kind of labels -sfzString = """ - region_label=Region-2a //This is put into implicitMaster0 and implicitGroup0 - - //One global per file -global_label=Global1 - -//Prepare keyswitches to test labels later -sw_default=60 -sw_lokey=60 -sw_hikey=70 - -// //is not part of the hierarchy. control has no label. - -master_label=ImplicitMaster0 //Not parsed? -> Unlabeled -group_label=ImplicitGroup0 //Not parsed? -> Unlabeled - region_label=Region-1a //This is put into implicitMaster0 and implicitGroup0 - - group_label=Group0 //master_label=WrongLabel //This is a label in the wrong place. It will override the group_label - region_label=Region0a - - master_label=Master1 Label -sw_last=60 -sw_label=Keyswitch For Master 1 - - -group_label=ImplicitGroup1 //Not parsed? -> Unlabeled - - group_label=Group1 - region_label=Region1a sw_down=68 - region_label=Region1b sw_last=67 - - - master_label=Master2 -sw_last=61 -sw_label=Keyswitch For Master 2 - -group_label=ImplicitGroup2 //Not parsed? -> Unlabeled - - - group_label=Group2 - region_label=Region2a - wrongOpcode=hello world foo bar region_label=Region2b //a wrong opcode. Will throw a warning on load but is nevertheless available in our python data - - group_label=Group3 - sample=*saw pitch_oncc1=40 label_cc1=Detune second oscillator - key=72 label_key72=A very special key -""" - -pgm = instrument.engine.load_patch_from_string(pgm_no, '.', sfzString, 'test_sampler_hierarchy') - -print ("Program:", pgm, pgm.status()) # -> Program: SamplerProgram -print ("Control Inits:", pgm.get_control_inits()) #Empty . Is this ? No. -globalHierarchy = pgm.get_global() # -> Single SamplerLayer. Literally sfz . But not the global scope, e.g. no under any . -#If there is no tag in the .sfz this will still create a root SamplerLayer -print ("Global:", globalHierarchy) -print ("Groups:", pgm.get_keyswitch_groups()) - -print("\nShow all SamplerLayer in their global/master/group/region hierarchy through indentations.\n" + "=" * 80) -def recurse(item, level = 0): - status = item.status() - print (" " * level + str(status)) - for subitem in item.get_children(): - recurse(subitem, level + 1) - -recurse(globalHierarchy) - - -print("\nShow all non-engine-default sfz opcodes and values in a global/master/group/region hierarchy.\n" + "=" * 80) -def recurse2(item, level = 0): - status = item.status() - data = item.as_dict() - children = item.get_children() - if data or children: - print (" " * level + "<%s> %s" % (item.status().level, data)) - for subitem in children: - recurse2(subitem, level + 1) - -recurse2(globalHierarchy) - -print("\nAs an example get all Keyswitch Labels and their keys.\n" + "=" * 80) - -def findKeyswitches(program): - """Returns a tuple: dict, sw_lokey, sw_highkey - - dict with key=keystring e.g. c#4 - and value=(opcode,label). label can be empty. - - Only existing keyswitches are included, not every number from 0-127. - Two special keys "sw_lokey" and "sw_hikeys" are returned and show the total range of possible - keyswitches. - - This is just a function to find the keyswitches in our example, not every advanced scenario - with sw_previous for context-sensitive-regions (which isn't really a keyswitch), - nor sw_lokey, sw_hikey and multiple parallel switches per key. - - Specifically it just searches for sw_last, sw_down and sw_up and assumes that any level (master, - group, region) can only use one of the three. - - sw_down and sw_up are implementation-dependent. We must assume that there are instruments that - use these opcodes without specifying sw_lokey and sw_hikey. sw_last requires the range. - For these reasons we cannot do sanity-checking here. We just report all single-key keyswitches. - - Finally it assumes that there is one sw_lokey and one sw_hikey in the whole file, and it - must be in global. No actual keyswitches will be in global. - """ - def findKS(data, writeInResult): - if "sw_label" in data: - label = data["sw_label"] - else: - label = "" - - if "sw_last" in data: - writeInResult[data["sw_last"]] = "sw_last", label - elif "sw_down" in data: - writeInResult[data["sw_down"]] = "sw_down", label - elif "sw_up" in data: - writeInResult[data["sw_up"]] = "sw_up", label - - result = {} # int:tuple(string) - hierarchy = program.get_hierarchy() - for k,v in hierarchy.items(): #Global - globalData = k.as_dict() - swlokeyValue = globalData["sw_lokey"] if "sw_lokey" in globalData else "" - swhikeyValue = globalData["sw_hikey"] if "sw_hikey" in globalData else "" - for k1,v1 in v.items(): #Master - findKS(k1.as_dict(), result) - if v1: - for k2,v2 in v1.items(): #Group - findKS(k2.as_dict(), result) - if v2: - for k3,v3 in v2.items(): #Regions - findKS(k3.as_dict(), result) - - return (result, swlokeyValue, swhikeyValue) - - -keyswitches = findKeyswitches(pgm) -pprint(keyswitches) - -instrument.engine.set_patch(1, pgm_no) -print ("Group 0 lastkey before keyswitch:", instrument.engine.get_keyswitch_state(1, 0)) -scene.send_midi_event(0x90, 61 ,64) #Trigger Keyswitch c#4 . Default for this example is 60/c4 -print ("Group 0 lastkey after keyswitch:", instrument.engine.get_keyswitch_state(1, 0)) - - -#The following were just stages during development, now handled by recurse and recurse2() above -""" -hierarchy = pgm.get_hierarchy() #starts with global and dicts down with get_children(). First single entry layer is get_global() -print ("Complete Hierarchy") -pprint(hierarchy) - -for k,v in hierarchy.items(): #Global - print (k.as_string()) - for k1,v1 in v.items(): #Master - print (k1.as_string()) - if v1: - for k2,v2 in v1.items(): #Group - print (k2.as_string()) - if v2: - for k3,v3 in v2.items(): #Regions - print (k3.as_string()) -""" - -print("Ready!") -while True: - cbox.call_on_idle(cmd_dumper) diff --git a/template/calfbox/sampler_api_load_stress_test.py b/template/calfbox/sampler_api_load_stress_test.py deleted file mode 100755 index 01e4755..0000000 --- a/template/calfbox/sampler_api_load_stress_test.py +++ /dev/null @@ -1,110 +0,0 @@ -#! /usr/bin/env python3 -# -*- coding: utf-8 -*- - -NUMBER_OF_INSTRUMENTS = 240 -""" -2021-11-13 Benchmark: -NumberOfInstruments,StartInSeconds,QuitInSeconds -30, 5, 3 -60, 10, 6 -120, 21, 12 -240, 42, 25 - -Conclusion: Linear time. -""" - -from calfbox import cbox - -import atexit -from datetime import datetime - -#Capture Ctlr+C / SIGINT and let @atexit handle the rest. -import signal -import sys -def signal_handler(sig, frame): - sys.exit(0) #atexit will trigger -signal.signal(signal.SIGINT, signal_handler) - - -def cmd_dumper(cmd, fb, args): - #print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) - pass - -def stopSession(): - """This got registered with atexit in the nsm new or open callback above. - will handle all python exceptions, but not segfaults of C modules. """ - print() - print("Starting Quit through @atexit, stopSession") - starttime = datetime.now() - #Don't do that. We are just a client. - #cbox.Transport.stop() - #print("@atexit: Calfbox Transport stopped ") - cbox.stop_audio() - print("@atexit: Calfbox Audio stopped ") - cbox.shutdown_engine() - print("@atexit: Calfbox Engine shutdown ") - endtime = datetime.now() - starttime - print (f"Shutdown took {endtime.seconds} seconds for {NUMBER_OF_INSTRUMENTS} instruments") - -cbox.init_engine() -cbox.start_audio(cmd_dumper) -atexit.register(stopSession) #this will handle all python exceptions, but not segfaults of C modules. - -scenes = {} -jackAudioOutLefts = {} -jackAudioOutRights = {} -outputMergerRouters = {} -routerToGlobalSummingStereoMixers = {} -lmixUuid = cbox.JackIO.create_audio_output('left_mix', "#1") #add "#1" as second parameter for auto-connection to system out 1 -rmixUuid = cbox.JackIO.create_audio_output('right_mix', "#2") #add "#2" as second parameter for auto-connection to system out 2 -cboxMidiPortUids = {} -sfzSamplerLayers = {} -instrumentLayers = {}\ - - -print (f"Creating {NUMBER_OF_INSTRUMENTS} instruments") -starttime = datetime.now() - -for i in range(NUMBER_OF_INSTRUMENTS): - scenes[i] = cbox.Document.get_engine().new_scene() - scenes[i].clear() - - #instrumentLayer = scenes[i].status().layers[0].get_instrument() - sfzSamplerLayers[i] = scenes[i].add_new_instrument_layer(str(i), "sampler") #"sampler" is the cbox sfz engine - scenes[i].status().layers[0].get_instrument().engine.load_patch_from_string(0, "", "", "") #fill with null instruments - - jackAudioOutLefts[i] = cbox.JackIO.create_audio_output(str(i) +"_L") - jackAudioOutRights[i] = cbox.JackIO.create_audio_output(str(i) +"_R") - - outputMergerRouters[i] = cbox.JackIO.create_audio_output_router(jackAudioOutLefts[i], jackAudioOutRights[i]) - outputMergerRouters[i].set_gain(-3.0) - #instrumentLayer = sfzSamplerLayers[i].get_instrument() - instrumentLayers[i] = scenes[i].status().layers[0].get_instrument() - 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? - - routerToGlobalSummingStereoMixers[i] = cbox.JackIO.create_audio_output_router(lmixUuid, rmixUuid) - routerToGlobalSummingStereoMixers[i].set_gain(-3.0) - instrument = sfzSamplerLayers[i].get_instrument() - instrument.get_output_slot(0).rec_wet.attach(routerToGlobalSummingStereoMixers[i]) - - #Create Midi Input Port - cboxMidiPortUids[i] = cbox.JackIO.create_midi_input(str(i) + "midi_in") - cbox.JackIO.set_appsink_for_midi_input(cboxMidiPortUids[i], True) #This sounds like a program wide sink, but it is needed for every port. - 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. - - #Actually load the instrument - programNumber = 0 - #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 - program = instrumentLayers[i].engine.load_patch_from_string(programNumber, ".", "", " sample=*saw") #fill with null instruments - print (f"[{i}]", program.status()) - instrumentLayers[i].engine.set_patch(1, programNumber) #1 is the channel, counting from 1. #TODO: we want this to be on all channels. - -endtime = datetime.now() - starttime -print (f"Creation took {endtime.seconds} seconds for {NUMBER_OF_INSTRUMENTS} instruments") - -print() -print("Press Ctrl+C for a controlled shutdown.") -print() - -while True: - cbox.call_on_idle(cmd_dumper) diff --git a/template/calfbox/sampler_api_test.py b/template/calfbox/sampler_api_test.py deleted file mode 100644 index 8beca0c..0000000 --- a/template/calfbox/sampler_api_test.py +++ /dev/null @@ -1,267 +0,0 @@ -import os -import sys -import struct -import time -import unittest - -sys.path = ["./py"] + sys.path - -import cbox - -global Document -Document = cbox.Document - -def verify_region(region, has, has_not, full = False): - values = set() - values_dict = {} - rtext = region.as_string() if not full else region.as_string_full() - for pair in rtext.split(" "): - if "=" in pair: - key, value = pair.split("=") - values.add(pair) - values.add(key) - values_dict[key] = value - if 'genericmod' in rtext: - print (rtext, "Expected:", has) - assert False - for i in has: - if '_oncc' in i: - i = i.replace('_oncc', '_cc') - if i not in values: - print("Not found: %s, has: %s" % (i, rtext)) - assert False - for i in has_not: - if i in values: - print("Found unwanted string: %s=%s" % (i, values_dict[i])) - assert False - -scene = Document.get_scene() -scene.clear() -instrument = scene.add_new_instrument_layer("test_sampler", "sampler").get_instrument() - -npfs = instrument.engine.load_patch_from_string(0, '.', '', 'new_patch') -instrument.engine.set_patch(1, 0) - -glb = npfs.get_global() -assert glb -assert glb.get_children() -master = glb.get_children()[0] - -g1 = master.new_child() -g1.set_param("cutoff", "100") -g1.set_param("resonance", "6") -g1.set_param("fil_type", "lpf_4p") -g1.set_param("fileg_decay", "0.2") -g1.set_param("fileg_sustain", "10") -g1.set_param("fileg_depth", "5400") -g1.set_param("fileg_release", "10") -g1.set_param("ampeg_release", "0.1") -g1.set_param("amp_veltrack", "0") -g1.set_param("volume", "-12") -g1.set_param("fileg_depthcc14", "-5400") - -def check_exception(param, value, substr): - error = False - try: - g1.set_param(param, value) - except Exception as e: - error = True - assert substr in str(e), "Exception with substring '%s' expected when setting %s to %s, caught another one: %s" % (substr, param, value, str(e)) - assert error, "Exception with substring '%s' expected when setting %s to %s, none caught" % (substr, param, value) - -check_exception("cutoff", "bla", "correct numeric value") -check_exception("key", "bla", "valid note name") -check_exception("lochan", "bla", "correct integer value") -check_exception("lochan", "10.5", "correct integer value") -check_exception("offset", "bla", "correct unsigned integer value") -check_exception("offset", "10.5", "correct unsigned integer value") -check_exception("offset", "-1000", "correct unsigned integer value") - -# Make sure that multiple CC assignments work -g1.set_param("locc5", "100") -g1.set_param("locc8", "100") -g1.set_param("hicc8", "110") - -#g1.set_param("cutoff", "1000") -#g1.set_param("fillfo_freq", "4") -#g1.set_param("fillfo_depth", "2400") -#g1.set_param("fillfo_wave", "12") - -r1 = g1.new_child() -r1.set_param("sample", "*saw") -r1.set_param("transpose", "0") -r1.set_param("tune", "5") -r1.set_param("gain_cc17", "12") - -verify_region(g1, ["locc5=100", "locc8=100", "hicc8=110"], ['hicc5']) -verify_region(r1, ["locc5=100", "locc8=100", "hicc8=110"], [], full=True) - -r1.set_param("hicc5", "120") -r1.set_param("hicc8", "124") - -verify_region(g1, ["locc5=100", "locc8=100", "hicc8=110"], ['hicc5']) -verify_region(g1, ["locc5=100", "hicc5=127", "locc8=100", "hicc8=110"], [], full=True) -verify_region(r1, ["locc5=100", "hicc5=120", "locc8=100", "hicc8=124"], [], full=True) - -r2 = g1.new_child() -r2.set_param("sample", "*sqr") -r2.set_param("transpose", "12") -r2.set_param("gain_cc17", "-12") -verify_region(r2, ["sample=*sqr", "gain_cc17=-12", "transpose=12"], []) -r2.set_param("gain_cc17", "-24") -verify_region(r2, ["gain_cc17=-24"], ["gain_cc17=-12"]) -r2.unset_param("gain_cc17") -verify_region(r2, ["sample=*sqr"], ["gain_cc17"]) -r2.unset_param("transpose") -verify_region(r2, ["sample=*sqr"], ["transpose"]) -r2.unset_param("sample") -verify_region(r2, [], ["transpose", "sample"]) - -g1.unset_param("cutoff") -g1.unset_param("resonance") -g1.unset_param("fil_type") -g1.unset_param("fileg_sustain") -g1.unset_param("fileg_decay") -g1.unset_param("fileg_depth") -g1.unset_param("fileg_release") -g1.unset_param("ampeg_release") -g1.unset_param("amp_veltrack") -g1.unset_param("volume") -g1.unset_param("fileg_depthcc14") -g1.unset_param("locc5") -g1.unset_param("locc8") -g1.unset_param("hicc8") - -params_to_test = [ - 'lokey', 'hikey', 'lovel', 'hivel', 'key', - 'cutoff', 'pan', 'offset', 'tune', 'position', 'width', - 'amp_random', 'fil_random', 'pitch_random', 'delay_random', - 'pitch_veltrack', 'reloffset_veltrack', 'offset_veltrack', - 'delay_cc5', 'delay_cc10', 'reloffset_cc5', 'reloffset_cc10', 'offset_cc5', 'offset_cc10', - 'delay_curvecc8', 'reloffset_curvecc5', 'offset_curvecc10', - 'delay_stepcc8', 'reloffset_stepcc5', 'offset_stepcc10', - 'cutoff_cc1', "resonance_cc1", 'pitch_cc1', 'tonectl_cc1', 'gain_cc1', 'amplitude_cc1', - 'cutoff_oncc2', "resonance_oncc2", 'pitch_oncc2', 'tonectl_oncc2', 'gain_oncc2', 'amplitude_oncc2', - 'cutoff_curvecc1', 'resonance_curvecc5', 'pitch_curvecc10', 'amplitude_curvecc10', - 'cutoff_smoothcc1', 'resonance_smoothcc5', 'pitch_smoothcc10', 'amplitude_smoothcc10', - 'cutoff_stepcc1', 'resonance_stepcc5', 'pitch_stepcc10', 'amplitude_stepcc10', - 'loop_start', 'loop_end', - 'ampeg_attack', - 'amplfo_depth', 'fillfo_depth', - 'fileg_vel2depth', - 'fileg_depthcc5', 'fillfo_depthcc8','amplfo_depthcc5', - 'fileg_depth_curvecc5', 'fillfo_depth_curvecc8','amplfo_depth_curvecc5', - 'fileg_depth_smoothcc5', 'fillfo_depth_smoothcc8','amplfo_depth_smoothcc5', - 'fileg_depth_stepcc5', 'fillfo_depth_stepcc8','amplfo_depth_stepcc5', - 'amplfo_freqcc5', 'fillfo_freqcc10', 'pitchlfo_freqcc5', - 'cutoff_chanaft', 'resonance_chanaft', - 'cutoff_polyaft', 'resonance_polyaft', - 'amplfo_depthpolyaft', 'fillfo_depthpolyaft', 'pitchlfo_depthpolyaft', - 'amplfo_freqpolyaft', 'fillfo_freqpolyaft', 'pitchlfo_freqpolyaft', - 'eq1_freqcc1', 'eq2_gaincc2', 'eq3_bwcc3', - 'eq1_freq_curvecc1', 'eq2_gain_curvecc2', 'eq3_bw_curvecc3', - 'eq1_freq_smoothcc1', 'eq2_gain_smoothcc2', 'eq3_bw_smoothcc3', - 'eq1_freq_stepcc1', 'eq2_gain_stepcc2', 'eq3_bw_stepcc3', - 'fileg_vel2start', 'fileg_vel2delay', 'fileg_vel2attack', 'fileg_vel2hold', 'fileg_vel2decay', 'fileg_vel2sustain', 'fileg_vel2release', - 'fileg_startcc1', 'fileg_delaycc1', 'fileg_attackcc1', 'fileg_holdcc1', 'fileg_decaycc1', 'fileg_sustaincc1', 'fileg_releasecc1', - 'fileg_start_curvecc1', 'fileg_delay_curvecc1', 'fileg_attack_curvecc1', 'fileg_hold_curvecc1', 'fileg_decay_curvecc1', 'fileg_sustain_curvecc1', 'fileg_release_curvecc1', - 'fileg_start_smoothcc1', 'fileg_delay_smoothcc1', 'fileg_attack_smoothcc1', 'fileg_hold_smoothcc1', 'fileg_decay_smoothcc1', 'fileg_sustain_smoothcc1', 'fileg_release_smoothcc1', - 'fileg_start_stepcc1', 'fileg_delay_stepcc1', 'fileg_attack_stepcc1', 'fileg_hold_stepcc1', 'fileg_decay_stepcc1', 'fileg_sustain_stepcc1', 'fileg_release_stepcc1', - 'cutoff2', 'resonance2', 'fileg_depth2', 'fileg_depth2cc1', 'fileg_depth2_stepcc1', - 'fillfo_depth2', 'fillfo_depth2cc1', 'fillfo_depth2_stepcc1', - 'amp_velcurve_5', 'amp_velcurve_127', - 'locc5', 'hicc5', - 'on_locc8', 'on_hicc8', - 'lfo5_freq', 'lfo1_wave', 'lfo3_fade', 'lfo4_delay', - ] -for i in range(len(params_to_test)): - param = params_to_test[0] - print ("Trying %s" % param) - rest = params_to_test[1:] - value1, value2 = "100", "80" - if 'key' in param: - value1, value2 = "e1", "g1" - # Verify that a setting is reported back - r2.set_param(param, value1) - verify_region(r2, ["%s=%s" % (param, value1)], rest) - - # Verify that setting the same value in parent doesn't change the local 'has' flag - g1.set_param(param, value1) - verify_region(r2, ["%s=%s" % (param, value1)], rest) - - # Verify that setting a different local value doesn't get overridden by parent - r2.set_param(param, value2) - verify_region(r2, ["%s=%s" % (param, value2)], rest) - # Write the original value - r2.set_param(param, value1) - verify_region(r2, ["%s=%s" % (param, value1)], rest) - - # Delete the parent value, confirm the deletion doesn't propagate to child - g1.unset_param(param) - verify_region(r2, ["%s=%s" % (param, value1)], rest) - # Set a different value in the parent, confirm it doesn't affect the child - g1.set_param(param, value2) - verify_region(g1, ["%s=%s" % (param, value2)], rest) - verify_region(r2, ["%s=%s" % (param, value1)], rest) - - # Check that the newly created child inherits the setting from the parent - r3 = g1.new_child() - verify_region(r3, [], [param]) - verify_region(r3, ["%s=%s" % (param, value2)], [], full=True) - r3.delete() - - # Verify that the original child still has the original value - verify_region(r2, ["%s=%s" % (param, value1)], [], full=True) - # Delete the child value, make sure it disappears but the inherited - # value is still reported in the full listing - r2.unset_param(param) - verify_region(r2, [], params_to_test) - verify_region(r2, ["%s=%s" % (param, value2)], [], full=True) - # Delete the setting in the parent, make sure the inherited value in the - # child disappears too - g1.unset_param(param) - verify_region(r2, [], ["%s=%s" % (param, value2)], full=True) - - master.set_param(param, value1) - verify_region(master, ["%s=%s" % (param, value1)], []) - verify_region(g1, [], params_to_test) - verify_region(g1, ["%s=%s" % (param, value1)], [], full=True) - verify_region(r2, [], params_to_test) - verify_region(r2, ["%s=%s" % (param, value1)], [], full=True) - master.unset_param(param) - verify_region(master, [], params_to_test) - verify_region(g1, [], params_to_test) - verify_region(r2, [], params_to_test) - - params_to_test = params_to_test[1:] + params_to_test[0:1] - -old_names = [ - ("hilev", "hivel"), - ("lolev", "lovel"), - ("loopstart", "loop_start"), - ("loopend", "loop_end"), - ("loopmode", "loop_mode", "one_shot", "loop_continuous"), - ("bendup", "bend_up"), - ("benddown", "bend_down"), - ("offby", "off_by"), -] - -for t in old_names: - if len(t) == 2: - old, new = t - v1, v2 = "10", "20" - else: - old, new, v1, v2 = t - print ("Trying alias: %s" % old) - r1.set_param(old, v1) - verify_region(r1, ["%s=%s" % (new, v1)], [old]) - r1.set_param(old, v2) - verify_region(r1, ["%s=%s" % (new, v2)], [old]) - r1.unset_param(old) - verify_region(r1, [], [old, new]) - r1.set_param(new, v1) - verify_region(r1, ["%s=%s" % (new, v1)], [old]) - r1.set_param(new, v2) - verify_region(r1, ["%s=%s" % (new, v2)], [old]) - r1.unset_param(new) - verify_region(r1, [], [old, new]) diff --git a/template/calfbox/sampler_channel.c b/template/calfbox/sampler_channel.c deleted file mode 100644 index 93078cb..0000000 --- a/template/calfbox/sampler_channel.c +++ /dev/null @@ -1,545 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config-api.h" -#include "dspmath.h" -#include "engine.h" -#include "errors.h" -#include "midi.h" -#include "module.h" -#include "rt.h" -#include "sampler.h" -#include "sampler_impl.h" -#include "sfzloader.h" -#include "stm.h" -#include -#include -#include -#include -#include -#include -#include -#include - -static inline void set_cc_int(struct sampler_channel *c, uint32_t cc, uint8_t value) -{ - c->intcc[cc] = value; - c->floatcc[cc] = value * (1.0f / 127.f); -} - -static inline void set_cc_float(struct sampler_channel *c, uint32_t cc, float value) -{ - c->intcc[cc] = (uint8_t)(value * 127) & 127; - c->floatcc[cc] = value; -} - -void sampler_channel_reset_keyswitches(struct sampler_channel *c) -{ - if (c->program && c->program->rll) - { - memset(c->keyswitch_state, 255, sizeof(c->keyswitch_state)); - memset(c->keyswitch_lastkey, 255, sizeof(c->keyswitch_lastkey)); - for (uint32_t i = 0; i < c->program->rll->keyswitch_group_count; ++i) - { - const struct sampler_keyswitch_group *ksg = c->program->rll->keyswitch_groups[i]; - if (ksg->def_value == 255) - { - c->keyswitch_state[i] = ksg->key_offsets[0]; - c->keyswitch_lastkey[i] = ksg->lo; - } - else - { - c->keyswitch_state[i] = ksg->key_offsets[ksg->def_value]; - c->keyswitch_lastkey[i] = ksg->def_value + ksg->lo; - } - } - } -} - -void sampler_channel_init(struct sampler_channel *c, struct sampler_module *m) -{ - c->module = m; - c->voices_running = NULL; - c->active_voices = 0; - c->active_prevoices = 0; - c->pitchwheel = 0; - c->output_shift = 0; - for (int i = 0; i < smsrc_perchan_count; ++i) - { - c->intcc[i] = 0; - c->floatcc[i] = 0; - } - c->poly_pressure_mask = 0; - - // default to maximum and pan=centre if MIDI mixing disabled - if (m->disable_mixer_controls) - { - c->channel_volume_cc = 16383; - c->channel_pan_cc = 8192; - } - else - { - sampler_channel_process_cc(c, 7, 100); - sampler_channel_process_cc(c, 7 + 32, 0); - sampler_channel_process_cc(c, 10, 64); - sampler_channel_process_cc(c, 10 + 32, 0); - } - set_cc_int(c, 11, 127); - set_cc_int(c, 71, 64); - set_cc_int(c, 74, 64); - set_cc_float(c, smsrc_alternate, 0); - c->previous_note = -1; - c->last_polyaft = 0; - c->first_note_vel = 100; - c->program = NULL; - sampler_channel_set_program_RT(c, m->program_count ? m->programs[0] : NULL); - memset(c->switchmask, 0, sizeof(c->switchmask)); - memset(c->sustainmask, 0, sizeof(c->sustainmask)); - memset(c->sostenutomask, 0, sizeof(c->sostenutomask)); -} - -void sampler_channel_process_cc(struct sampler_channel *c, int cc, int val) -{ - struct sampler_module *m = c->module; - // Handle CC triggering. - if (c->program && c->program->rll && c->program->rll->layers_oncc) - { - struct sampler_rll *rll = c->program->rll; - if ((rll->cc_trigger_bitmask[cc >> 5] & (1 << (cc & 31)))) - { - int old_value = c->intcc[cc]; - for (GSList *p = rll->layers_oncc; p; p = p->next) - { - struct sampler_layer *layer = p->data; - assert(layer->runtime); - // Default (compatible) behaviour means the region will trigger - // on every CC that has value within the specified range. - // XXXKF add a switch for only triggering the region on - // transition between in-range and out-of-range. - gboolean compatible_oncc_behaviour = TRUE; - - struct sampler_cc_range *on_cc = layer->runtime->on_cc; - gboolean trigger = FALSE; - while(on_cc) - { - if (on_cc->key.cc_number == cc && - (val >= on_cc->value.locc && val <= on_cc->value.hicc) && - (compatible_oncc_behaviour || !(old_value >= on_cc->value.locc && old_value <= on_cc->value.hicc))) - { - trigger = TRUE; - break; - } - on_cc = on_cc->next; - } - if(trigger) - { - struct sampler_voice *v = m->voices_free; - if (!v) - break; - struct sampler_released_groups exgroups; - sampler_released_groups_init(&exgroups); - sampler_voice_start(v, c, layer->runtime, layer->runtime->pitch_keycenter, 127, &exgroups); - sampler_channel_release_groups(c, -1, &exgroups); - } - } - } - } - int was_enabled = c->intcc[cc] >= 64; - int enabled = val >= 64; - switch(cc) - { - case 10: - case 10 + 32: - set_cc_int(c, cc, val); - if (!c->module->disable_mixer_controls) - c->channel_pan_cc = sampler_channel_addcc(c, 10); - break; - case 7: - case 7 + 32: - set_cc_int(c, cc, val); - if (!c->module->disable_mixer_controls) - c->channel_volume_cc = sampler_channel_addcc(c, 7); - break; - case 64: - if (was_enabled && !enabled) - { - sampler_channel_stop_sustained(c); - } - break; - case 66: - if (was_enabled && !enabled) - sampler_channel_stop_sostenuto(c); - else if (!was_enabled && enabled) - sampler_channel_capture_sostenuto(c); - break; - - case 120: - case 123: - sampler_channel_stop_all(c); - break; - case 121: - // Recommended Practice (RP-015) Response to Reset All Controllers - // http://www.midi.org/techspecs/rp15.php - sampler_channel_process_cc(c, 64, 0); - sampler_channel_process_cc(c, 66, 0); - set_cc_int(c, 11, 127); - set_cc_int(c, 1, 0); - set_cc_float(c, smsrc_alternate, 0); - c->pitchwheel = 0; - c->last_chanaft = 0; - c->poly_pressure_mask = 0; - c->last_polyaft = 0; - sampler_channel_reset_keyswitches(c); - return; - } - if (cc < smsrc_perchan_count) - set_cc_int(c, cc, val); -} - -void sampler_channel_release_groups(struct sampler_channel *c, int note, struct sampler_released_groups *exgroups) -{ - if (exgroups->group_count || exgroups->low_groups) - { - FOREACH_VOICE(c->voices_running, v) - { - if (v->off_by && v->note != note) - { - if (sampler_released_groups_check(exgroups, v->off_by)) - { - v->released = 1; - if (v->layer->off_mode == som_fast) - cbox_envelope_go_to(&v->amp_env, 15); - } - } - } - } -} - -void sampler_channel_start_note(struct sampler_channel *c, int note, int vel, gboolean is_release_trigger) -{ - struct sampler_module *m = c->module; - float random = rand() * 1.0 / (RAND_MAX + 1.0); - - set_cc_float(c, smsrc_alternate, c->intcc[smsrc_alternate] ? 0.f : 1.f); - set_cc_float(c, smsrc_random_unipolar, random); // is that a per-voice or per-channel value? is it generated per region or per note? - - gboolean is_first = FALSE; - if (!is_release_trigger) - { - c->switchmask[note >> 5] |= 1 << (note & 31); - c->prev_note_velocity[note] = vel; - c->prev_note_start_time[note] = m->current_time; - is_first = TRUE; - FOREACH_VOICE(c->voices_running, v) - { - if (!v->released && v->layer->trigger != stm_release) - { - is_first = FALSE; - break; - } - } - } - struct sampler_program *prg = c->program; - if (!prg || !prg->rll || prg->deleting) - return; - - if (!is_release_trigger) - { - for (uint32_t i = 0; i < prg->rll->keyswitch_group_count; ++i) - { - const struct sampler_keyswitch_group *ks = prg->rll->keyswitch_groups[i]; - if (note >= ks->lo && note <= ks->hi) - { - c->keyswitch_lastkey[i] = note; - c->keyswitch_state[i] = ks->key_offsets[note - ks->lo]; - } - } - } - - struct sampler_rll_iterator iter; - sampler_rll_iterator_init(&iter, prg->rll, c, note, vel, random, is_first, is_release_trigger); - - struct sampler_layer *layer = sampler_rll_iterator_next(&iter); - if (!layer) - { - if (!is_release_trigger) - c->previous_note = note; - return; - } - struct sampler_layer_data *layers[MAX_SAMPLER_VOICES]; - struct sampler_layer_data *delayed_layers[MAX_SAMPLER_PREVOICES]; - int lcount = 0, dlcount = 0, slcount = 0; - struct sampler_voice *free_voice = m->voices_free; - struct sampler_prevoice *free_prevoice = m->prevoices_free; - int fvcount = 0, fpcount = 0; - - while(layer && lcount < MAX_SAMPLER_VOICES + MAX_SAMPLER_PREVOICES) - { - if (free_voice) - { - free_voice = free_voice->next; - fvcount++; - } - if (free_prevoice) - { - free_prevoice = free_prevoice->next; - fpcount++; - } - assert(layer->runtime); - if (layer->runtime->computed.eff_use_prevoice) - delayed_layers[dlcount++] = layer->runtime; - else - { - layers[lcount++] = layer->runtime; - if (layer->runtime->computed.eff_is_silent) - slcount++; - } - layer = sampler_rll_iterator_next(&iter); - } - - struct sampler_released_groups exgroups; - sampler_released_groups_init(&exgroups); - // If running out of polyphony, do not start the note if all the regions cannot be played - // (silent notes don't count into polyphony) - if (lcount <= slcount + fvcount && dlcount <= fpcount) - { - // this might perhaps be optimized by mapping the group identifiers to flat-array indexes - // but I'm not going to do that until someone gives me an SFZ worth doing that work ;) - - for (int i = 0; i < lcount; ++i) - { - struct sampler_layer_data *l = layers[i]; - if (l->computed.eff_is_silent) - sampler_voice_start_silent(l, &exgroups); - else - { - int velc = (!is_first && l->vel_mode == svm_previous) ? c->first_note_vel : vel; - sampler_voice_start(m->voices_free, c, l, note, velc, &exgroups); - } - } - for (int i = 0; i < dlcount; ++i) - { - struct sampler_layer_data *l = delayed_layers[i]; - int velc = (!is_first && l->vel_mode == svm_previous) ? c->first_note_vel : vel; - sampler_prevoice_start(m->prevoices_free, c, l, note, velc); - } - } - if (!is_release_trigger) - c->previous_note = note; - if (is_first) - c->first_note_vel = vel; - sampler_channel_release_groups(c, note, &exgroups); -} - -void sampler_channel_start_release_triggered_voices(struct sampler_channel *c, int note) -{ - if (c->program && c->program->rll && c->program->rll->has_release_layers) - { - if (c->prev_note_velocity[note]) - { - sampler_channel_start_note(c, note, c->prev_note_velocity[note], TRUE); - c->prev_note_velocity[note] = 0; - } - } -} - -void sampler_channel_stop_note(struct sampler_channel *c, int note, int vel, gboolean is_polyaft) -{ - c->switchmask[note >> 5] &= ~(1 << (note & 31)); - FOREACH_PREVOICE(c->module->prevoices_running, pv) - { - if (pv->note == note) - sampler_prevoice_unlink(&c->module->prevoices_running, pv); - } - FOREACH_VOICE(c->voices_running, v) - { - if (v->note == note && v->layer->trigger != stm_release) - { - v->off_vel = vel; - if (v->captured_sostenuto) - v->released_with_sostenuto = 1; - else if (c->intcc[64] >= 64) - v->released_with_sustain = 1; - else - sampler_voice_release(v, is_polyaft); - - } - } - if (c->intcc[64] < 64) - sampler_channel_start_release_triggered_voices(c, note); - else - c->sustainmask[note >> 5] |= (1 << (note & 31)); -} - -void sampler_channel_stop_sustained(struct sampler_channel *c) -{ - FOREACH_VOICE(c->voices_running, v) - { - if (v->channel == c && v->released_with_sustain && v->layer->trigger != stm_release) - { - sampler_voice_release(v, FALSE); - v->released_with_sustain = 0; - } - } - // Start release layers for the newly released keys - if (c->program && c->program->rll && c->program->rll->has_release_layers) - { - for (int i = 0; i < 128; i++) - { - if (c->sustainmask[i >> 5] & (1 << (i & 31))) - sampler_channel_start_release_triggered_voices(c, i); - } - } - memset(c->sustainmask, 0, sizeof(c->sustainmask)); -} - -void sampler_channel_stop_sostenuto(struct sampler_channel *c) -{ - FOREACH_VOICE(c->voices_running, v) - { - if (v->released_with_sostenuto && v->layer->trigger != stm_release) - { - sampler_channel_start_release_triggered_voices(c, v->note); - sampler_voice_release(v, FALSE); - v->released_with_sostenuto = 0; - // XXXKF unsure what to do with sustain - } - } - // Start release layers for the newly released keys - if (c->program && c->program->rll && c->program->rll->has_release_layers) - { - for (int i = 0; i < 128; i++) - { - if (c->sostenutomask[i >> 5] & (1 << (i & 31))) - sampler_channel_start_release_triggered_voices(c, i); - } - } - memset(c->sostenutomask, 0, sizeof(c->sostenutomask)); -} - -void sampler_channel_capture_sostenuto(struct sampler_channel *c) -{ - FOREACH_VOICE(c->voices_running, v) - { - if (!v->released && v->loop_mode != slm_one_shot && v->loop_mode != slm_one_shot_chokeable && !v->layer->count) - { - // XXXKF unsure what to do with sustain - v->captured_sostenuto = 1; - c->sostenutomask[v->note >> 5] |= (1 << (v->note & 31)); - } - } -} - -void sampler_channel_stop_all(struct sampler_channel *c) -{ - FOREACH_VOICE(c->voices_running, v) - { - sampler_voice_release(v, v->loop_mode == slm_one_shot_chokeable); - v->released_with_sustain = 0; - v->released_with_sostenuto = 0; - v->captured_sostenuto = 0; - } -} - -void sampler_channel_set_program_RT(struct sampler_channel *c, struct sampler_program *prg) -{ - FOREACH_PREVOICE(c->module->prevoices_running, pv) - { - if (pv->channel == c) - { - sampler_prevoice_unlink(&c->module->prevoices_running, pv); - sampler_prevoice_link(&c->module->prevoices_free, pv); - } - } - if (c->program) - c->program->in_use--; - c->program = prg; - if (prg) - { - assert(prg->rll); - sampler_channel_reset_keyswitches(c); - for(GSList *p = prg->ctrl_init_list; p; p = p->next) - { - union sampler_ctrlinit_union u; - u.ptr = p->data; - // printf("Setting controller %d -> %d\n", u.cinit.controller, u.cinit.value); - set_cc_int(c, u.cinit.controller, u.cinit.value); - } - c->program->in_use++; - } -} - -#define sampler_channel_set_program_args(ARG) ARG(struct sampler_program *, prg) - -DEFINE_RT_VOID_FUNC(sampler_channel, c, sampler_channel_set_program) -{ - sampler_channel_set_program_RT(c, prg); -} - -void sampler_channel_program_change(struct sampler_channel *c, int program) -{ - struct sampler_module *m = c->module; - // XXXKF replace with something more efficient - for (uint32_t i = 0; i < m->program_count; i++) - { - // XXXKF support banks - if (m->programs[i]->prog_no == program) - { - sampler_channel_set_program_RT(c, m->programs[i]); - return; - } - } - g_warning("Unknown program %d", program); - if (m->program_count) - sampler_channel_set_program_RT(c, m->programs[0]); -} - -float sampler_channel_get_expensive_cc(struct sampler_channel *c, struct sampler_voice *v, struct sampler_prevoice *pv, int cc_no) -{ - switch(cc_no) - { - case smsrc_pitchbend: - return c->pitchwheel / 8191.f; - case smsrc_lastpolyaft: // how this is defined? is it last or is it current voice's? - return sampler_channel_get_poly_pressure(c, v ? v->note : (pv ? pv->note : 0)); - case smsrc_noteonvel: - return v ? v->vel / 127.0 : (pv ? pv->vel / 127.0 : 0); - case smsrc_noteoffvel: - return v ? v->off_vel / 127.0 : 0; - case smsrc_keynotenum: - return v ? v->note / 127.0 : (pv ? pv->note / 127.0 : 0); - case smsrc_keynotegate: - return c->switchmask[0] || c->switchmask[1] || c->switchmask[2] || c->switchmask[3]; // XXXKF test interactions with sustain/sostenuto - case smsrc_chanaft_sfz2: - return c->last_chanaft / 127.0; - case smsrc_random_unipolar: - case smsrc_alternate: - return c->floatcc[cc_no]; - case smsrc_random_bipolar: - return -1 + 2 * c->floatcc[smsrc_random_unipolar]; - case smsrc_keydelta: // not supported yet - return 0; - case smsrc_keydelta_abs: - return 0; - case smsrc_tempo: - return c->module->module.engine->master->tempo; // XXXKF what scale??? - default: - assert(0); - return 0.f; - } -} diff --git a/template/calfbox/sampler_gen.c b/template/calfbox/sampler_gen.c deleted file mode 100644 index 79c99b7..0000000 --- a/template/calfbox/sampler_gen.c +++ /dev/null @@ -1,509 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config-api.h" -#include "dspmath.h" -#include "errors.h" -#include "midi.h" -#include "module.h" -#include "rt.h" -#include "sampler.h" -#include "sfzloader.h" -#include -#include -#include -#include -#include - -#define LOW_QUALITY_INTERPOLATION 0 - -struct resampler_state -{ - float *leftright; - int offset; - float lgain, rgain, lgain_delta, rgain_delta; -}; - -#if USE_NEON - -#include - -static inline void process_voice_mono_noloop(struct sampler_gen *v, struct resampler_state *rs, const int16_t *srcdata, int endpos) -{ - static const float32x2_t shift1a = {0.f, 1.f}, shift1b = {1.f, 1.f}; - static const float32x2_t shift2a = {-1.f, -1.f}, shift2b = {0.f, 0.f}; - static const float32x2_t shift3a = {-2.f, -2.f}, shift3b = {-2.f, -1.f}; - static const float32x2_t scalinga = {-1 / 6.0, 3 / 6.0}, scalingb = {-3 / 6.0, 1 / 6.0}; - uint64x1_t pos = v->bigpos, delta = v->bigdelta; - float32x2_t gains = {rs->lgain, rs->rgain}; - const float32x2_t gaindeltas = {rs->lgain_delta, rs->rgain_delta}; - for (uint32_t i = rs->offset; i < endpos; i++) - { - float32x2_t posposf = vcvt_n_f32_u32(vreinterpret_u32_u64(pos), 32); - - int32x4_t smp = vmovl_s16(vld1_s16(&srcdata[pos >> 32])); - pos = vadd_u64(pos, delta); - - float32x2_t t2 = vdup_n_f32(posposf[0]); - float32x2_t samplesa = vcvt_f32_s32(vget_low_s32(smp)), samplesb = vcvt_f32_s32(vget_high_s32(smp)); - - float32x2_t mula = vmul_f32(vmul_f32(vadd_f32(t2, shift1a), vadd_f32(t2, shift2a)), vmul_f32(vadd_f32(t2, shift3a), scalinga)); - float32x2_t mulb = vmul_f32(vmul_f32(vadd_f32(t2, shift1b), vadd_f32(t2, shift2b)), vmul_f32(vadd_f32(t2, shift3b), scalingb)); - float32x2_t v = vmla_f32(vmul_f32(samplesa, mula), samplesb, mulb); - float32x2_t result = vmul_f32(gains, vadd_f32(v, vrev64_f32(v))); - gains = vadd_f32(gains, gaindeltas); - - rs->leftright[2 * i] = result[0]; - rs->leftright[2 * i + 1] = result[1]; - } - rs->lgain = gains[0]; - rs->rgain = gains[1]; - v->bigpos = pos; - rs->offset = endpos; -} - -static inline void process_voice_stereo_noloop(struct sampler_gen *v, struct resampler_state *rs, const int16_t *srcdata, int endpos) -{ - static const float32x2_t shift1a = {0.f, 1.f}, shift1b = {1.f, 1.f}; - static const float32x2_t shift2a = {-1.f, -1.f}, shift2b = {0.f, 0.f}; - static const float32x2_t shift3a = {-2.f, -2.f}, shift3b = {-2.f, -1.f}; - static const float32x2_t scalinga = {-1 / 6.0, 3 / 6.0}, scalingb = {-3 / 6.0, 1 / 6.0}; - uint64x1_t pos = v->bigpos, delta = v->bigdelta; - float32x2_t gains = {rs->lgain, rs->rgain}; - const float32x2_t gaindeltas = {rs->lgain_delta, rs->rgain_delta}; - for (uint32_t i = rs->offset; i < endpos; i++) - { - float32x2_t posposf = vcvt_n_f32_u32(vreinterpret_u32_u64(pos), 32); - - int16x4x2_t pp = vld2_s16(&srcdata[(pos >> 31) &~ 1]); - pos = vadd_u64(pos, delta); - int32x4_t smp_left = vmovl_s16(pp.val[0]), smp_right = vmovl_s16(pp.val[1]); - - float32x2_t t2 = vdup_n_f32(posposf[0]); - float32x2_t samplesLa = vcvt_f32_s32(vget_low_s32(smp_left)), samplesLb = vcvt_f32_s32(vget_high_s32(smp_left)); - float32x2_t samplesRa = vcvt_f32_s32(vget_low_s32(smp_right)), samplesRb = vcvt_f32_s32(vget_high_s32(smp_right)); - - float32x2_t mula = vmul_f32(vmul_f32(vadd_f32(t2, shift1a), vadd_f32(t2, shift2a)), vmul_f32(vadd_f32(t2, shift3a), scalinga)); - float32x2_t mulb = vmul_f32(vmul_f32(vadd_f32(t2, shift1b), vadd_f32(t2, shift2b)), vmul_f32(vadd_f32(t2, shift3b), scalingb)); - float32x2_t vL = vmla_f32(vmul_f32(samplesLa, mula), samplesLb, mulb); - float32x2_t vR = vmla_f32(vmul_f32(samplesRa, mula), samplesRb, mulb); - float32x2x2_t transposed = vtrn_f32(vL, vR); - float32x2_t result = vmul_f32(gains, vadd_f32(transposed.val[0], transposed.val[1])); - gains = vadd_f32(gains, gaindeltas); - - rs->leftright[2 * i] = result[0]; - rs->leftright[2 * i + 1] = result[1]; - } - rs->lgain = gains[0]; - rs->rgain = gains[1]; - v->bigpos = pos; - rs->offset = endpos; -} - -#elif USE_SSE - -#include - -typedef __m128 V4SF; - -static const V4SF shift1 = {0, 1, 1, 1}; -static const V4SF shift2 = {-1, -1, 0, 0}; -static const V4SF shift3 = {-2, -2, -2, -1}; -static const V4SF scaling = {-1, 3, -3, 1}; -static const V4SF zero = {0, 0, 0, 0}; - - -static inline void process_voice_mono_noloop(struct sampler_gen *v, struct resampler_state *rs, const int16_t *srcdata, int endpos) -{ - uint64_t pos = v->bigpos; - const float ffrac = 1.0f / 6.0f; - const float _scaler = 1.f / (128.f * 16777216.f); - for (int i = rs->offset; i < endpos; i++) - { - //float t = ((pos >> 8) & 0x00FFFFFF) * scaler; - const int16_t *p = &srcdata[pos >> 32]; - - V4SF t2 = __builtin_ia32_cvtsi2ss(zero, (pos & 0xFFFFFFFF) >> 1) * _scaler; - pos += v->bigdelta; - - V4SF t4 = __builtin_ia32_shufps(t2, t2, 0); - V4SF v4mul = (t4 + shift1) * (t4 + shift2) * (t4 + shift3) * scaling; - V4SF samples = {p[0], p[1], p[2], p[3]}; - v4mul = __builtin_ia32_mulps(samples, v4mul); - - float c = (v4mul[0] + v4mul[1] + v4mul[2] + v4mul[3]) * ffrac; - - rs->leftright[2 * i] = rs->lgain * c; - rs->leftright[2 * i + 1] = rs->rgain * c; - rs->lgain += rs->lgain_delta; - rs->rgain += rs->rgain_delta; - } - v->bigpos = pos; - rs->offset = endpos; -} - -static inline void process_voice_stereo_noloop(struct sampler_gen *v, struct resampler_state *rs, const int16_t *srcdata, int endpos) -{ - uint64_t pos = v->bigpos; - const float ffrac = 1.0f / 6.0f; - const float _scaler = 1.f / (128.f * 16777216.f); - for (int i = rs->offset; i < endpos; i++) - { - //float t = ((pos >> 8) & 0x00FFFFFF) * scaler; - const int16_t *p = &srcdata[(pos >> 31) & ~1]; - - V4SF t2 = __builtin_ia32_cvtsi2ss(zero, (pos & 0xFFFFFFFF) >> 1) * _scaler; - pos += v->bigdelta; - - V4SF t4 = __builtin_ia32_shufps(t2, t2, 0); - V4SF v4mul = (t4 + shift1) * (t4 + shift2) * (t4 + shift3) * scaling; - V4SF samples_left = {p[0], p[2], p[4], p[6]}; - samples_left = __builtin_ia32_mulps(samples_left, v4mul); - V4SF samples_right = {p[1], p[3], p[5], p[7]}; - samples_right = __builtin_ia32_mulps(samples_right, v4mul); - - float cl = (samples_left[0] + samples_left[1] + samples_left[2] + samples_left[3]) * ffrac; - float cr = (samples_right[0] + samples_right[1] + samples_right[2] + samples_right[3]) * ffrac; - - rs->leftright[2 * i] = rs->lgain * cl; - rs->leftright[2 * i + 1] = rs->rgain * cr; - rs->lgain += rs->lgain_delta; - rs->rgain += rs->rgain_delta; - } - v->bigpos = pos; - rs->offset = endpos; -} - -#else - -static inline void process_voice_mono_noloop(struct sampler_gen *v, struct resampler_state *rs, const int16_t *srcdata, int endpos) -{ - const float ffrac = 1.0f / 6.0f; - const float scaler = 1.f / 16777216.f; - - for (int i = rs->offset; i < endpos; i++) - { - float t = ((v->bigpos >> 8) & 0x00FFFFFF) * scaler; - const int16_t *p = &srcdata[v->bigpos >> 32]; -#if LOW_QUALITY_INTERPOLATION - float c = (1.f - t) * p[1] + t * p[2]; -#else - float b0 = -t*(t-1.f)*(t-2.f); - float b1 = 3.f*(t+1.f)*(t-1.f)*(t-2.f); - float c = (b0 * p[0] + b1 * p[1] - 3.f*(t+1.f)*t*(t-2.f) * p[2] + (t+1.f)*t*(t-1.f) * p[3]) * ffrac; -#endif - rs->leftright[2 * i] = rs->lgain * c; - rs->leftright[2 * i + 1] = rs->rgain * c; - rs->lgain += rs->lgain_delta; - rs->rgain += rs->rgain_delta; - v->bigpos += v->bigdelta; - } - rs->offset = endpos; -} - -static inline void process_voice_stereo_noloop(struct sampler_gen *v, struct resampler_state *rs, const int16_t *srcdata, int endpos) -{ - const float ffrac = 1.0f / 6.0f; - const float scaler = 1.f / 16777216.f; - - for (int i = rs->offset; i < endpos; i++) - { - float t = ((v->bigpos >> 8) & 0x00FFFFFF) * scaler; - const int16_t *p = &srcdata[(v->bigpos >> 31) & ~1]; -#if LOW_QUALITY_INTERPOLATION - float c0 = (1.f - t) * p[2] + t * p[4]; - float c1 = (1.f - t) * p[3] + t * p[5]; -#else - float b0 = -t*(t-1.f)*(t-2.f); - float b1 = 3.f*(t+1.f)*(t-1.f)*(t-2.f); - float c0 = (b0 * p[0] + b1 * p[2] - 3.f*(t+1.f)*t*(t-2.f) * p[4] + (t+1.f)*t*(t-1.f) * p[6]) * ffrac; - float c1 = (b0 * p[1] + b1 * p[3] - 3.f*(t+1.f)*t*(t-2.f) * p[5] + (t+1.f)*t*(t-1.f) * p[7]) * ffrac; -#endif - rs->leftright[2 * i] = rs->lgain * c0; - rs->leftright[2 * i + 1] = rs->rgain * c1; - rs->lgain += rs->lgain_delta; - rs->rgain += rs->rgain_delta; - v->bigpos += v->bigdelta; - } - rs->offset = endpos; -} - -#endif - -static inline uint32_t process_voice_noloop(struct sampler_gen *v, struct resampler_state *rs, const int16_t *srcdata, uint32_t pos_offset, uint32_t usable_sample_end) -{ - uint32_t out_frames = CBOX_BLOCK_SIZE - rs->offset; - - uint64_t sample_end64 = ((uint64_t)usable_sample_end) << 32; - // Check how many frames can be written to output buffer without going - // past usable_sample_end. - if (__builtin_expect(v->bigpos + (out_frames - 1) * v->bigdelta >= sample_end64, 0)) - out_frames = (sample_end64 - v->bigpos) / v->bigdelta + 1; - - assert(out_frames > 0 && out_frames <= (uint32_t)(CBOX_BLOCK_SIZE - rs->offset)); - uint32_t oldpos = v->bigpos >> 32; - if (v->mode == spt_stereo16) - process_voice_stereo_noloop(v, rs, srcdata - (pos_offset << 1), rs->offset + out_frames); - else - process_voice_mono_noloop(v, rs, srcdata - pos_offset, rs->offset + out_frames); - return (v->bigpos >> 32) - oldpos; -} - -static void process_voice_withloop(struct sampler_gen *v, struct resampler_state *rs) -{ - // This is the first frame where interpolation will cross the loop boundary - uint32_t loop_end = v->loop_end; - uint32_t loop_edge = loop_end - MAX_INTERPOLATION_ORDER; - - while ( rs->offset < CBOX_BLOCK_SIZE ) { - uint64_t startframe = v->bigpos >> 32; - - int16_t *source_data = v->sample_data; - uint32_t source_offset = 0; - uint32_t usable_sample_end = loop_edge; - // if the first frame to play is already within 3 frames of loop end - // (we need consecutive 4 frames for cubic interpolation) then - // "straighten out" the area around the loop, and play that - if (__builtin_expect(startframe >= loop_edge, 0)) - { - // if fully past the loop end, then it's normal wraparound - // (or end of the sample if not looping) - if (startframe >= loop_end) - { - if (v->loop_start == (uint32_t)-1) - { - v->mode = spt_inactive; - return; - } - v->play_count++; - if (v->loop_count && v->play_count >= v->loop_count) - { - v->mode = spt_inactive; - return; - } - v->bigpos -= (uint64_t)(loop_end - v->loop_start) << 32; - continue; - } - - usable_sample_end = loop_end; - source_data = v->scratch; - source_offset = loop_edge; - } - - process_voice_noloop(v, rs, source_data, source_offset, usable_sample_end); - } -} - -static void process_voice_streaming(struct sampler_gen *v, struct resampler_state *rs, uint32_t limit) -{ - if (v->consumed_credit > 0) - { - if (v->consumed_credit >= limit) - { - v->consumed_credit -= limit; - return; - } - limit -= v->consumed_credit; - v->consumed_credit = 0; - } - // This is the first frame where interpolation will cross the loop boundary - int16_t scratch[2 * MAX_INTERPOLATION_ORDER * 2]; - - while ( limit && rs->offset < CBOX_BLOCK_SIZE ) { - uint64_t startframe = v->bigpos >> 32; - - int16_t *source_data = v->in_streaming_buffer ? v->streaming_buffer : v->sample_data; - uint32_t loop_start = v->in_streaming_buffer ? 0 : v->loop_start; - uint32_t loop_end = v->in_streaming_buffer ? v->streaming_buffer_frames : v->loop_end; - uint32_t loop_edge = loop_end - MAX_INTERPOLATION_ORDER; - uint32_t source_offset = 0; - uint32_t usable_sample_end = loop_edge; - // if the first frame to play is already within 3 frames of loop end - // (we need consecutive 4 frames for cubic interpolation) then - // "straighten out" the area around the loop, and play that - if (startframe >= loop_edge) - { - // if fully past the loop end, then it's normal wraparound - // (or end of the sample if not looping) - if (startframe >= loop_end) - { - if (v->loop_start == (uint32_t)-1) - { - v->mode = spt_inactive; - return; - } - v->bigpos -= (uint64_t)(loop_end - loop_start) << 32; - if (v->prefetch_only_loop) - v->consumed -= (loop_end - loop_start); - else - v->in_streaming_buffer = TRUE; - continue; - } - - int shift = (v->mode == spt_stereo16) ? 1 : 0; - - // 'linearize' the virtual circular buffer - write 3 (or N) frames before end of the loop - // and 3 (N) frames at the start of the loop, and play it; in rare cases this will need to be - // repeated twice if output write pointer is close to CBOX_BLOCK_SIZE or playback rate is very low, - // but that's OK. - uint32_t halfscratch = MAX_INTERPOLATION_ORDER << shift; - memcpy(&scratch[0], &source_data[loop_edge << shift], halfscratch * sizeof(int16_t) ); - if (v->loop_start == (uint32_t)-1) - memset(scratch + halfscratch, 0, halfscratch * sizeof(int16_t)); - else - memcpy(scratch + halfscratch, &v->streaming_buffer[v->loop_start << shift], halfscratch * sizeof(int16_t)); - - usable_sample_end = loop_end; - source_data = scratch; - source_offset = loop_edge; - } - if (limit != (uint32_t)-1 && usable_sample_end - startframe > limit) - usable_sample_end = startframe + limit; - - uint32_t consumed = process_voice_noloop(v, rs, source_data, source_offset, usable_sample_end); - if (consumed > limit) - { - // The number of frames 'consumed' may be greater than the amount - // available because of sample-skipping (at least that's the only - // *legitimate* reason). This should be accounted for in the, - // consumed sample counter (hence temporary storage of the - // 'buffer overconsumption' in the consumed_credit field), but is not - // actually causing any use of missing data, as the missing samples - // have been skipped. - assert(v->consumed_credit == 0); - v->consumed_credit = consumed - limit; - assert (v->consumed_credit <= 1 + (v->bigdelta >> 32)); - consumed = limit; - } - v->consumed += consumed; - if (consumed < limit) - limit -= consumed; - else - break; - } -} - -void sampler_gen_reset(struct sampler_gen *v) -{ - v->mode = spt_inactive; - v->bigpos = 0; - v->last_lgain = 0.f; - v->last_rgain = 0.f; - v->play_count = 0; - v->consumed = 0; - v->consumed_credit = 0; - v->streaming_buffer = NULL; - v->in_streaming_buffer = FALSE; - v->prefetch_only_loop = FALSE; - v->fadein_counter = -1.f; -} - -uint32_t sampler_gen_sample_playback(struct sampler_gen *v, float *leftright, uint32_t limit) -{ - struct resampler_state rs; - rs.leftright = leftright; - rs.offset = 0; - rs.lgain = v->last_lgain; - rs.rgain = v->last_rgain; - rs.lgain_delta = (v->lgain - v->last_lgain) * (1.f / CBOX_BLOCK_SIZE); - rs.rgain_delta = (v->rgain - v->last_rgain) * (1.f / CBOX_BLOCK_SIZE); - if (v->streaming_buffer) - process_voice_streaming(v, &rs, limit); - else - { - process_voice_withloop(v, &rs); - } - uint32_t written = rs.offset; - - if (!v->streaming_buffer) - { - v->virtpos += written * v->virtdelta; - if (v->virtpos != v->bigpos) - { - while ((v->virtpos >> 32) >= v->loop_end && v->loop_start != SAMPLER_NO_LOOP) - v->virtpos -= ((uint64_t)(v->loop_end - v->loop_start)) << 32; - } - // XXXKF looping - if (v->fadein_counter == -1 && fabs((v->bigpos - v->virtpos) / (65536.0 * 65536.0)) > v->stretching_jump) - { - int64_t jump = (int64_t)(v->stretching_jump * 65536.0 * 65536.0); - int64_t newpos = v->bigpos > v->virtpos ? v->bigpos - jump : v->bigpos + jump; - if (newpos < 0) - newpos = 0; - // XXXKF beware of extremely short loops - while ((newpos >> 32) >= v->loop_end && v->loop_start != SAMPLER_NO_LOOP) - newpos -= ((uint64_t)(v->loop_end - v->loop_start)) << 32; - if ((newpos >> 32) >= v->cur_sample_end - 4) - newpos = ((uint64_t)v->cur_sample_end - 4)<< 32; - v->fadein_pos = newpos; - v->fadein_counter = 0; - } - else if (v->fadein_counter != -1) - { - float leftright_fadein[2 * CBOX_BLOCK_SIZE]; - - rs.offset = 0; - rs.leftright = leftright_fadein; - rs.lgain = v->last_lgain; - rs.rgain = v->last_rgain; - - uint64_t oldpos = v->bigpos; - v->bigpos = v->fadein_pos; - process_voice_withloop(v, &rs); - v->fadein_pos = v->bigpos; - v->bigpos = oldpos; - - uint32_t written2 = rs.offset; - - // XXXKF not the best set of special cases - uint32_t i; - if (written2 > written) - { - for (i = 2 * written; i < 2 * written2; i += 2) - leftright[i] = leftright[i + 1] = 0.f; - written = written2; - } - if (written2 < written) - { - for (i = 2 * written2; i < 2 * written; i += 2) - leftright_fadein[i] = leftright_fadein[i + 1] = 0.f; - written2 = written; - } - float cnt = v->fadein_counter; - float scl = v->bigdelta / (v->stretching_crossfade * v->virtdelta); - for (i = 0; i < 2 * written2; i += 2) - { - leftright[i] += (leftright_fadein[i] - leftright[i]) * cnt; - leftright[i + 1] += (leftright_fadein[i + 1] - leftright[i + 1]) * cnt; - cnt += scl; - if (cnt > 1.f) - cnt = 1.f; - } - if (cnt >= 1.f) - { - cnt = -1.f; - v->bigpos = v->fadein_pos; - } - v->fadein_counter = cnt; - } - } - v->last_lgain = v->lgain; - v->last_rgain = v->rgain; - return written; -} - diff --git a/template/calfbox/sampler_impl.h b/template/calfbox/sampler_impl.h deleted file mode 100644 index 4e28748..0000000 --- a/template/calfbox/sampler_impl.h +++ /dev/null @@ -1,68 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_SAMPLER_IMPL_H -#define CBOX_SAMPLER_IMPL_H - -extern void sampler_gen_reset(struct sampler_gen *v); -extern uint32_t sampler_gen_sample_playback(struct sampler_gen *v, float *leftright, uint32_t limit); -extern void sampler_program_change_byidx(struct sampler_module *m, struct sampler_channel *c, int program_idx); -extern void sampler_program_change(struct sampler_module *m, struct sampler_channel *c, int program); - -static inline int sfz_note_from_string(const char *note) -{ - static const int semis[] = {9, 11, 0, 2, 4, 5, 7}; - int pos; - int nn = tolower(note[0]); - int nv; - if ((nn >= '0' && nn <= '9') || nn == '-') - { - int nv = atoi(note); - if (nv >= -1 && nv <= 127) - return nv; - return -2; - } - if (nn < 'a' || nn > 'g') - return -2; - nv = semis[nn - 'a']; - - for (pos = 1; tolower(note[pos]) == 'b' || note[pos] == '#'; pos++) - nv += (note[pos] != '#') ? -1 : +1; - - if ((note[pos] == '-' && note[pos + 1] == '1' && note[pos + 2] == '\0') || (note[pos] >= '0' && note[pos] <= '9' && note[pos + 1] == '\0')) - { - return nv + 12 * (1 + atoi(note + pos)); - } - - return -2; -} - -static inline gboolean atof_C_verify(const char *key, const char *value, double *result, GError **error) -{ - char *endptr = NULL; - double res = g_ascii_strtod(value, &endptr); - if (endptr && !*endptr && endptr != value) - { - *result = res; - return TRUE; - } - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "'%s' is not a correct numeric value for %s", value, key); - return FALSE; -} - -#endif diff --git a/template/calfbox/sampler_layer.c b/template/calfbox/sampler_layer.c deleted file mode 100644 index bea11ec..0000000 --- a/template/calfbox/sampler_layer.c +++ /dev/null @@ -1,2106 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config-api.h" -#include "dspmath.h" -#include "errors.h" -#include "midi.h" -#include "module.h" -#include "rt.h" -#include "sampler.h" -#include "sampler_impl.h" -#include "sfzloader.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static inline gboolean sampler_modulation_key_equal(const struct sampler_modulation_key *k1, const struct sampler_modulation_key *k2) -{ - return (k1->src == k2->src && k1->src2 == k2->src2 && k1->dest == k2->dest); -} - -static inline void sampler_modulation_dump_one(const struct sampler_modulation *sm) -{ - printf("%d x %d -> %d : %f : %d\n", sm->key.src, sm->key.src2, sm->key.dest, sm->value.amount, sm->value.curve_id); -} - -////////////////////////////////////////////////////////////////////////////////////////////////// - -static inline gboolean sampler_noteinitfunc_key_equal(const struct sampler_noteinitfunc_key *k1, const struct sampler_noteinitfunc_key *k2) -{ - return (k1->notefunc_voice == k2->notefunc_voice && k1->variant == k2->variant); -} - -static inline void sampler_noteinitfunc_dump_one(const struct sampler_noteinitfunc *nif) -{ - printf("%p(%d) = %f\n", nif->key.notefunc_voice, nif->key.variant, nif->value.value); -} - -////////////////////////////////////////////////////////////////////////////////////////////////// - -static inline gboolean sampler_cc_range_key_equal(const struct sampler_cc_range_key *k1, const struct sampler_cc_range_key *k2) -{ - return k1->cc_number == k2->cc_number; -} - -static inline void sampler_cc_range_dump_one(const struct sampler_cc_range *ccrange) -{ - printf("CC%d in [%c%d, %c%d]\n", (int)ccrange->key.cc_number, ccrange->value.has_locc ? '!' : '.', (int)ccrange->value.locc, ccrange->value.has_hicc ? '!' : '.', (int)ccrange->value.hicc); -} - -////////////////////////////////////////////////////////////////////////////////////////////////// - -static inline gboolean sampler_flex_lfo_key_equal(const struct sampler_flex_lfo_key *k1, const struct sampler_flex_lfo_key *k2) -{ - return k1->id == k2->id; -} - -static inline void sampler_flex_lfo_dump_one(const struct sampler_flex_lfo *lfo) -{ - printf("LFO%d (freq %s %f, delay %s %f, fade %s %f, wave %s %d)\n", - (int)lfo->key.id, - lfo->value.has_freq ? "(local)" : "(inherited)", lfo->value.freq, - lfo->value.has_delay ? "(local)" : "(inherited)", lfo->value.delay, - lfo->value.has_fade ? "(local)" : "(inherited)", lfo->value.fade, - lfo->value.has_wave ? "(local)" : "(inherited)", lfo->value.wave - ); -} - -////////////////////////////////////////////////////////////////////////////////////////////////// - -#define SAMPLER_COLL_FUNC_DUMP(sname) \ - void sname##_dump(const struct sname *p) \ - { \ - for(; p; p = p->next) \ - sname##_dump_one(p); \ - } - -SAMPLER_COLL_LIST(SAMPLER_COLL_FUNC_DUMP) - -#define SAMPLER_COLL_FUNC_FIND(sname) \ - static struct sname *sname##_find(struct sname *list, const struct sname##_key *key) \ - { \ - for(struct sname *p = list; p; p = p->next) \ - { \ - struct sname##_key *dkey = &p->key; \ - if (sname##_key_equal(dkey, key)) \ - return p; \ - } \ - return NULL; \ - } \ - static struct sname *sname##_find2(struct sname **list_ptr, const struct sname##_key *key, struct sname ***link_ptr) \ - { \ - for(struct sname **pp = list_ptr; *pp; pp = &(*pp)->next) \ - { \ - struct sname##_key *dkey = &(*pp)->key; \ - if (sname##_key_equal(dkey, key)) \ - { \ - if (link_ptr) \ - *link_ptr = pp; \ - return *pp; \ - } \ - } \ - return NULL; \ - } - -SAMPLER_COLL_LIST(SAMPLER_COLL_FUNC_FIND) - -#define SAMPLER_COLL_FIELD_INIT(name, has_name, type, init_value) \ - d->value.name = init_value; \ - d->value.has_name = FALSE; - -#define SAMPLER_COLL_FUNC_ADD(sname) \ -static struct sname *sname##_add(struct sname **list_ptr, const struct sname##_key *key) \ -{ \ - struct sname *d = sname##_find(*list_ptr, key); \ - if (d) \ - return d; \ - d = g_malloc0(sizeof(struct sname)); \ - d->key = *key; \ - SAMPLER_COLL_FIELD_LIST_##sname(SAMPLER_COLL_FIELD_INIT)\ - d->next = *list_ptr; \ - *list_ptr = d; \ - return d; \ -} - -SAMPLER_COLL_LIST(SAMPLER_COLL_FUNC_ADD) - -#define SAMPLER_COLL_FUNC_DESTROY(sname) \ -static void sname##s_destroy(struct sname *list_ptr) \ -{ \ - while(list_ptr) \ - { \ - struct sname *p = list_ptr->next; \ - g_free(list_ptr); \ - list_ptr = p; \ - } \ -} - -SAMPLER_COLL_LIST(SAMPLER_COLL_FUNC_DESTROY) - -#define SAMPLER_COLL_FIELD_ISNULL(name, has_name, type, init_value) \ - if (d->name != init_value || d->has_name) \ - return FALSE; - -#define SAMPLER_COLL_FUNC_ISNULL(sname) \ - static inline gboolean sname##_is_null(const struct sname##_value *d) \ - { \ - SAMPLER_COLL_FIELD_LIST_##sname(SAMPLER_COLL_FIELD_ISNULL) \ - return TRUE; \ - } - -SAMPLER_COLL_LIST(SAMPLER_COLL_FUNC_ISNULL) - -// sampler_modulation_set_amount etc. -#define SAMPLER_COLL_FIELD_SETTER(name, has_name, type, init_value, sname) \ - struct sname *sname##_set_##name##_by_offset(struct sampler_layer *l, uint32_t offset, const struct sname##_key *key, gboolean set_local_value, type value) \ - { \ - void *vl = &l->data; \ - struct sname **list_ptr = vl + offset; \ - struct sname *dstm = sname##_add(list_ptr, key); \ - if (!set_local_value && dstm->value.has_name) \ - return dstm; \ - dstm->value.has_name = set_local_value; \ - dstm->value.name = value; \ - return dstm; \ - } - -#define SAMPLER_COLL_FUNC_SETTERS(sname) \ - SAMPLER_COLL_FIELD_LIST_##sname(SAMPLER_COLL_FIELD_SETTER, sname) - -SAMPLER_COLL_LIST(SAMPLER_COLL_FUNC_SETTERS) - -#define SAMPLER_COLL_FIELD_UNSET(name, has_name, type, init_value, sname) \ - if ((unset_mask & (1 << sname##_value_field_##name)) && d->value.has_name == remove_local) \ - { \ - d->value.name = parent ? parent->value.name : init_value; \ - d->value.has_name = FALSE; \ - } \ - -#define SAMPLER_COLL_FIELD_KEY_ENUM_VALUE(name, has_name, type, init_value, sname) \ - sname##_value_field_##name, - -#define SAMPLER_COLL_FUNC_UNSET(sname) \ - enum { \ - SAMPLER_COLL_FIELD_LIST_##sname(SAMPLER_COLL_FIELD_KEY_ENUM_VALUE, sname) \ - }; \ - static gboolean sname##_unset_by_offset(struct sampler_layer *l, uint32_t offset, const struct sname##_key *key, gboolean remove_local, uint32_t unset_mask) \ - { \ - void *vl = &l->data, *vp = l->parent ? &l->parent->data : NULL; \ - struct sname **list_ptr = vl + offset; \ - struct sname **parent_list_ptr = vp ? vp + offset : NULL; \ - struct sname **link_ptr = NULL; \ - struct sname *d = sname##_find2(list_ptr, key, &link_ptr); \ - if (!d) \ - return FALSE; \ - struct sname *parent = remove_local && *parent_list_ptr != NULL ? sname##_find(*parent_list_ptr, key) : NULL; \ - SAMPLER_COLL_FIELD_LIST_##sname(SAMPLER_COLL_FIELD_UNSET, sname) \ - /* Delete if it's all default values and it's not overriding anything */ \ - if (sname##_is_null(&d->value)) {\ - *link_ptr = d->next; \ - g_free(d); \ - } \ - return TRUE; \ - } - -SAMPLER_COLL_LIST(SAMPLER_COLL_FUNC_UNSET) - -#define SAMPLER_COLL_FIELD_ZEROHASATTR(name, has_name, type, init_value) \ - dstv->value.has_name = FALSE; -#define SAMPLER_COLL_FUNC_CLONE(sname) \ - static struct sname *sname##_clone(struct sname *src, gboolean copy_hasattr) \ - { \ - struct sname *dst = NULL, **last = &dst;\ - for(const struct sname *srcv = src; srcv; srcv = srcv->next) \ - { \ - struct sname *dstv = g_malloc(sizeof(struct sname)); \ - memcpy(dstv, srcv, sizeof(struct sname)); \ - if (!copy_hasattr) \ - { \ - SAMPLER_COLL_FIELD_LIST_##sname(SAMPLER_COLL_FIELD_ZEROHASATTR) \ - } \ - *last = dstv; \ - dstv->next = NULL; \ - last = &dstv->next; \ - } \ - return dst; \ - } - -SAMPLER_COLL_LIST(SAMPLER_COLL_FUNC_CLONE) - -////////////////////////////////////////////////////////////////////////////////////////////////// - -enum sampler_layer_param_type -{ - slpt_invalid, - slpt_alias, - slpt_int, - slpt_uint32_t, - slpt_float, - slpt_dBamp, - slpt_midi_note_t, - slpt_enum, - slpt_string, - slpt_midicurve, - slpt_ccrange, - // modulation matrix - slpt_mod_amount, // src (or CC) * src2 (or CC) -> dest - slpt_mod_curveid, - slpt_mod_smooth, - slpt_mod_step, - slpt_generic_modulation, - // note init functions - slpt_voice_nif, - slpt_prevoice_nif, - slpt_flex_lfo, - slpt_nonfunctional, - slpt_reserved, -}; - -struct sampler_layer_param_entry -{ - const char *name; - size_t offset; - enum sampler_layer_param_type type; - double def_value; - uint64_t extra_int; - void *extra_ptr; - void (*set_has_value)(struct sampler_layer_data *, gboolean); - gboolean (*get_has_value)(struct sampler_layer_data *); -}; - -#define MODSRC_CC 0xFFF -#define smsrc_CC MODSRC_CC - -#define ENCODE_MOD(src, src2, dst) ((((uint32_t)(src) & 0xFFFU) | (((uint32_t)(src2) & 0xFFFU) << 12U) | ((uint32_t)(dst) << 24U))) - -#define PROC_SUBSTRUCT_FIELD_SETHASFUNC(name, index, def_value, parent) \ - static void sampler_layer_data_##parent##_set_has_##name(struct sampler_layer_data *l, gboolean value) { l->has_##parent.name = value; } \ - static gboolean sampler_layer_data_##parent##_get_has_##name(struct sampler_layer_data *l) { return l->has_##parent.name; } - -#define PROC_FIELD_SETHASFUNC(type, name, default_value) \ - static void sampler_layer_data_set_has_##name(struct sampler_layer_data *l, gboolean value) { l->has_##name = value; } \ - static gboolean sampler_layer_data_get_has_##name(struct sampler_layer_data *l) { return l->has_##name; } -#define PROC_FIELD_SETHASFUNC_string(name) \ - static void sampler_layer_data_set_has_##name(struct sampler_layer_data *l, gboolean value) { l->has_##name = value; } \ - static gboolean sampler_layer_data_get_has_##name(struct sampler_layer_data *l) { return l->has_##name; } -#define PROC_FIELD_SETHASFUNC_dBamp(type, name, default_value) \ - PROC_FIELD_SETHASFUNC(type, name, default_value) -#define PROC_FIELD_SETHASFUNC_enum(type, name, default_value) \ - PROC_FIELD_SETHASFUNC(type, name, default_value) -#define PROC_FIELD_SETHASFUNC_dahdsr(field, name, default_value) \ - DAHDSR_FIELDS(PROC_SUBSTRUCT_FIELD_SETHASFUNC, field) -#define PROC_FIELD_SETHASFUNC_lfo(field, name, default_value) \ - LFO_FIELDS(PROC_SUBSTRUCT_FIELD_SETHASFUNC, field) -#define PROC_FIELD_SETHASFUNC_eq(field, name, default_value) \ - EQ_FIELDS(PROC_SUBSTRUCT_FIELD_SETHASFUNC, field) -#define PROC_FIELD_SETHASFUNC_ccrange(name, parname) -#define PROC_FIELD_SETHASFUNC_midicurve(name) \ - static gboolean sampler_layer_data_set_get_has_##name(struct sampler_layer_data *l, uint32_t index, int value) \ - { \ - if (value != -1) \ - l->name.has_values[index] = value; \ - return l->name.has_values[index]; \ - } - -SAMPLER_FIXED_FIELDS(PROC_FIELD_SETHASFUNC) - -#define LOFS(field) offsetof(struct sampler_layer_data, field) - -#define FIELD_MOD(name, param, src, src2, dest) \ - { name, LOFS(modulations), slpt_mod_##param, 0, ENCODE_MOD(smsrc_##src, smsrc_##src2, smdest_##dest), NULL, NULL, NULL }, -#define FIELD_AMOUNT(name, src, dest) \ - FIELD_MOD(name, amount, src, none, dest) -#define FIELD_AMOUNT_CC(name, dest) \ - FIELD_ALIAS(name "cc#", name "_oncc#") \ - FIELD_MOD(name "_oncc#", amount, CC, none, dest) \ - FIELD_MOD(name "_curvecc#", curveid, CC, none, dest) \ - FIELD_MOD(name "_smoothcc#", smooth, CC, none, dest) \ - FIELD_MOD(name "_stepcc#", step, CC, none, dest) -#define FIELD_AMOUNT_CC_(name, dest) \ - FIELD_ALIAS(name "_cc#", name "_oncc#") \ - FIELD_MOD(name "_oncc#", amount, CC, none, dest) \ - FIELD_MOD(name "_curvecc#", curveid, CC, none, dest) \ - FIELD_MOD(name "_smoothcc#", smooth, CC, none, dest) \ - FIELD_MOD(name "_stepcc#", step, CC, none, dest) -#define FIELD_VOICE_NIF(name, nif, variant) \ - { name, LOFS(voice_nifs), slpt_voice_nif, 0, variant, nif, NULL, NULL }, -#define FIELD_PREVOICE_NIF(name, nif, variant) \ - { name, LOFS(prevoice_nifs), slpt_prevoice_nif, 0, variant, nif, NULL, NULL }, -#define FIELD_ALIAS(alias, name) \ - { alias, -1, slpt_alias, 0, 0, name, NULL, NULL }, -#define FIELD_NONFUNCTIONAL(name) \ - { name, -1, slpt_nonfunctional, 0, 0, NULL, NULL, NULL }, - -#define PROC_SUBSTRUCT_FIELD_DESCRIPTOR(name, index, def_value, parent, parent_name, parent_index, parent_struct) \ - { #parent_name "_" #name, offsetof(struct sampler_layer_data, parent) + offsetof(struct parent_struct, name), slpt_float, def_value, parent_index * 100 + index, NULL, sampler_layer_data_##parent##_set_has_##name, sampler_layer_data_##parent##_get_has_##name }, \ - -#define PROC_SUBSTRUCT_FIELD_DESCRIPTOR_DAHDSR(name, index, def_value, parent, parent_name, parent_index, parent_struct) \ - { #parent_name "_" #name, offsetof(struct sampler_layer_data, parent) + offsetof(struct parent_struct, name), slpt_float, def_value, parent_index * 100 + index, NULL, sampler_layer_data_##parent##_set_has_##name, sampler_layer_data_##parent##_get_has_##name }, \ - FIELD_VOICE_NIF(#parent_name "_vel2" #name, sampler_nif_vel2env, (parent_index << 4) + snif_env_##name) \ - FIELD_AMOUNT_CC(#parent_name "_" #name, ampeg_stage + (parent_index << 4) + snif_env_##name) \ - -#define PROC_FIELD_DESCRIPTOR(type, name, default_value) \ - { #name, LOFS(name), slpt_##type, default_value, 0, NULL, sampler_layer_data_set_has_##name, sampler_layer_data_get_has_##name }, -#define PROC_FIELD_DESCRIPTOR_dBamp(type, name, default_value) \ - { #name, LOFS(name), slpt_##type, default_value, 0, NULL, sampler_layer_data_set_has_##name, sampler_layer_data_get_has_##name }, -#define PROC_FIELD_DESCRIPTOR_string(name) \ - { #name, LOFS(name), slpt_string, 0, LOFS(name##_changed), NULL, sampler_layer_data_set_has_##name, sampler_layer_data_get_has_##name }, -#define PROC_FIELD_DESCRIPTOR_enum(enumtype, name, default_value) \ - { #name, LOFS(name), slpt_enum, (double)(enum enumtype)default_value, 0, enumtype##_from_string, sampler_layer_data_set_has_##name, sampler_layer_data_get_has_##name }, -#define PROC_FIELD_DESCRIPTOR_midicurve(name) \ - { #name "_#", LOFS(name), slpt_midicurve, 0, 0, (void *)sampler_layer_data_set_get_has_##name, NULL, NULL }, - -#define FIELD_DEPTH_SET(name, dest, attrib) \ - FIELD_ALIAS(#name attrib "cc#", #name attrib "_oncc#") \ - FIELD_MOD(#name attrib "_oncc#", amount, name, CC, dest) \ - FIELD_MOD(#name attrib "_curvecc#", curveid, name, CC, dest) \ - FIELD_MOD(#name attrib "_smoothcc#", smooth, name, CC, dest) \ - FIELD_MOD(#name attrib "_stepcc#", step, name, CC, dest) \ - FIELD_MOD(#name attrib "polyaft", amount, name, polyaft, dest) \ - FIELD_MOD(#name attrib "chanaft", amount, name, chanaft, dest) \ - FIELD_MOD(#name attrib, amount, name, none, dest) \ - -#define PROC_FIELD_DESCRIPTOR_dahdsr(field, name, index) \ - DAHDSR_FIELDS(PROC_SUBSTRUCT_FIELD_DESCRIPTOR_DAHDSR, field, name, index, cbox_dahdsr) \ - FIELD_DEPTH_SET(name, from_##name, "_depth") \ - FIELD_MOD(#name "_vel2depth", amount, name, vel, from_##name) - -#define PROC_FIELD_DESCRIPTOR_lfo(field, name, index) \ - LFO_FIELDS(PROC_SUBSTRUCT_FIELD_DESCRIPTOR, field, name, index, sampler_lfo_params) \ - FIELD_AMOUNT(#name "_freqpolyaft", polyaft, name##_freq) \ - FIELD_AMOUNT(#name "_freqchanaft", chanaft, name##_freq) \ - FIELD_AMOUNT_CC(#name "_freq", name##_freq) \ - FIELD_DEPTH_SET(name, from_##name, "_depth") - -#define PROC_FIELD_DESCRIPTOR_eq(field, name, index) \ - EQ_FIELDS(PROC_SUBSTRUCT_FIELD_DESCRIPTOR, field, name, index, sampler_eq_params) \ - FIELD_AMOUNT_CC(#name "_freq", name##_freq) \ - FIELD_AMOUNT_CC(#name "_bw", name##_bw) \ - FIELD_AMOUNT_CC(#name "_gain", name##_gain) - -#define PROC_FIELD_DESCRIPTOR_ccrange(field, parname) \ - { #parname "locc#", LOFS(field), slpt_ccrange, 0, 0, NULL, NULL, NULL }, \ - { #parname "hicc#", LOFS(field), slpt_ccrange, 127, 1, NULL, NULL, NULL }, - -#define FIELD_FLEX_LFO(name, field) \ - { name, LOFS(flex_lfos), slpt_flex_lfo, 0, sampler_flex_lfo_value_field_##field, NULL, NULL, NULL }, - -#define NIF_VARIANT_CC 0x01000000 -#define NIF_VARIANT_CURVECC 0x02000000 -#define NIF_VARIANT_STEPCC 0x03000000 -#define NIF_VARIANT_MASK 0xFF000000 - -struct sampler_layer_param_entry sampler_layer_params[] = { - SAMPLER_FIXED_FIELDS(PROC_FIELD_DESCRIPTOR) - - FIELD_AMOUNT("cutoff_chanaft", chanaft, cutoff) - FIELD_AMOUNT("resonance_chanaft", chanaft, resonance) - FIELD_AMOUNT("cutoff_polyaft", polyaft, cutoff) - FIELD_AMOUNT("resonance_polyaft", polyaft, resonance) - - FIELD_DEPTH_SET(fileg, cutoff2, "_depth2") - FIELD_MOD("fileg_vel2depth2", amount, fileg, vel, cutoff2) - - FIELD_DEPTH_SET(fillfo, cutoff2, "_depth2") - - FIELD_AMOUNT("cutoff2_chanaft", chanaft, cutoff2) - FIELD_AMOUNT("resonance2_chanaft", chanaft, resonance2) - FIELD_AMOUNT("cutoff2_polyaft", polyaft, cutoff2) - FIELD_AMOUNT("resonance2_polyaft", polyaft, resonance2) - - FIELD_AMOUNT_CC_("gain", gain) - FIELD_AMOUNT_CC_("cutoff", cutoff) - FIELD_AMOUNT_CC_("resonance", resonance) - FIELD_AMOUNT_CC_("cutoff2", cutoff2) - FIELD_AMOUNT_CC_("resonance2", resonance2) - FIELD_AMOUNT_CC_("pitch", pitch) - FIELD_AMOUNT_CC_("tune", pitch) - FIELD_AMOUNT_CC_("tonectl", tonectl) - FIELD_AMOUNT_CC_("pan", pan) - FIELD_AMOUNT_CC_("amplitude", amplitude) - - FIELD_VOICE_NIF("amp_random", sampler_nif_addrandom, 0) - FIELD_VOICE_NIF("fil_random", sampler_nif_addrandom, 1) - FIELD_VOICE_NIF("pitch_random", sampler_nif_addrandom, 2) - FIELD_VOICE_NIF("pitch_veltrack", sampler_nif_vel2pitch, 0) - FIELD_VOICE_NIF("offset_veltrack", sampler_nif_vel2offset, 0) - FIELD_VOICE_NIF("reloffset_veltrack", sampler_nif_vel2reloffset, 0) - FIELD_PREVOICE_NIF("delay_random", sampler_nif_addrandomdelay, 0) - FIELD_PREVOICE_NIF("sync_beats", sampler_nif_syncbeats, 0) - FIELD_PREVOICE_NIF("delay_cc#", sampler_nif_cc2delay, NIF_VARIANT_CC) - FIELD_PREVOICE_NIF("delay_curvecc#", sampler_nif_cc2delay, NIF_VARIANT_CURVECC) - FIELD_PREVOICE_NIF("delay_stepcc#", sampler_nif_cc2delay, NIF_VARIANT_STEPCC) - FIELD_VOICE_NIF("reloffset_cc#", sampler_nif_cc2reloffset, NIF_VARIANT_CC) - FIELD_VOICE_NIF("reloffset_curvecc#", sampler_nif_cc2reloffset, NIF_VARIANT_CURVECC) - FIELD_VOICE_NIF("reloffset_stepcc#", sampler_nif_cc2reloffset, NIF_VARIANT_STEPCC) - FIELD_VOICE_NIF("offset_cc#", sampler_nif_cc2offset, NIF_VARIANT_CC) - FIELD_VOICE_NIF("offset_curvecc#", sampler_nif_cc2offset, NIF_VARIANT_CURVECC) - FIELD_VOICE_NIF("offset_stepcc#", sampler_nif_cc2offset, NIF_VARIANT_STEPCC) - - FIELD_FLEX_LFO("lfo#_freq", freq) - FIELD_FLEX_LFO("lfo#_delay", delay) - FIELD_FLEX_LFO("lfo#_fade", fade) - FIELD_FLEX_LFO("lfo#_wave", wave) - - FIELD_ALIAS("hilev", "hivel") - FIELD_ALIAS("lolev", "lovel") - FIELD_ALIAS("loopstart", "loop_start") - FIELD_ALIAS("loopend", "loop_end") - FIELD_ALIAS("loopmode", "loop_mode") - FIELD_ALIAS("bendup", "bend_up") - FIELD_ALIAS("benddown", "bend_down") - FIELD_ALIAS("offby", "off_by") - FIELD_ALIAS("offset_oncc#", "offset_cc#") - FIELD_ALIAS("reloffset_oncc#", "reloffset_cc#") - FIELD_ALIAS("delay_oncc#", "delay_cc#") - - //NONFUNCTIONAL Opcodes can still be looked up and used as strings in a GUI - FIELD_NONFUNCTIONAL("region_label") //ARIA - FIELD_NONFUNCTIONAL("group_label") //ARIA - FIELD_NONFUNCTIONAL("master_label") //ARIA - FIELD_NONFUNCTIONAL("global_label") //ARIA - FIELD_NONFUNCTIONAL("sw_label") //Keyswitch. ARIA - //label_cc and label_key are in sfzloader.c because they are under - - { "genericmod_#_#_#_#", -1, slpt_generic_modulation, 0, 0, NULL, NULL, NULL }, -}; -#define NPARAMS (sizeof(sampler_layer_params) / sizeof(sampler_layer_params[0])) - -static void sampler_layer_apply_unknown(struct sampler_layer *l, const char *key, const char *value); - -static int compare_entries(const void *p1, const void *p2) -{ - const struct sampler_layer_param_entry *e1 = p1, *e2 = p2; - return strcmp(e1->name, e2->name); -} - -void sampler_layer_prepare_params(void) -{ - qsort(sampler_layer_params, NPARAMS, sizeof(struct sampler_layer_param_entry), compare_entries); - for (size_t i = 0; i < NPARAMS; ++i) - { - struct sampler_layer_param_entry *e = &sampler_layer_params[i]; - if (e->type == slpt_alias) - { - struct sampler_layer_param_entry prototype; - prototype.name = e->extra_ptr; - void *found = bsearch(&prototype, sampler_layer_params, NPARAMS, sizeof(sampler_layer_params[0]), compare_entries); - if (!found) - printf("Alias %s redirects to non-existent name (%s)\n", e->name, prototype.name); - assert(found); - e->extra_ptr = found; - } - if (i) - { - struct sampler_layer_param_entry *prev_e = &sampler_layer_params[i - 1]; - if (!strcmp(e->name, prev_e->name)) - { - printf("Duplicate parameter %s\n", e->name); - assert(FALSE); - } - } - } -} - -// This only works for setting. Unsetting is slightly different. -static gboolean override_logic(gboolean is_equal, gboolean has_value, gboolean set_local_value) -{ - if (!set_local_value && has_value) // Do not override locally set values - return FALSE; - // Override if a value or a inherited value is replaced with a local setting. - return (!is_equal) || (set_local_value != has_value); -} - -static inline void mod_key_decode(uint64_t extra_int, const uint32_t *args, struct sampler_modulation_key *mod_key) -{ - uint32_t modsrc = (extra_int & 0xFFF); - uint32_t modsrc2 = ((extra_int >> 12) & 0xFFF); - if (modsrc == MODSRC_CC) - modsrc = args[0]; - if (modsrc2 == MODSRC_CC) - modsrc2 = args[0]; - mod_key->src = modsrc; - mod_key->src2 = modsrc2; - mod_key->dest = ((extra_int >> 24) & 0xFF); -} - -static inline void nif_key_decode(uint64_t extra_int, void *extra_ptr, const uint32_t *args, struct sampler_noteinitfunc_key *nif_key) -{ - uint32_t variant = extra_int &~ NIF_VARIANT_MASK; - nif_key->notefunc_voice = extra_ptr; - if (extra_int & NIF_VARIANT_MASK) - { - int cc = args[0] & 255; - variant = cc + (variant << 8); - } - nif_key->variant = variant; -} - -static inline void flex_lfo_key_decode(const uint32_t *args, struct sampler_flex_lfo_key *flex_lfo_key) -{ - flex_lfo_key->id = args[0]; -} - -#define OVERRIDE_LOGIC(type) override_logic(!memcmp(p, data_ptr, sizeof(type)), e->get_has_value(&l->data), set_local_value) - -#define CAST_FLOAT_VALUE fvalue = *(double *)data_ptr - -gboolean sampler_layer_param_entry_set_from_ptr(const struct sampler_layer_param_entry *e, struct sampler_layer *l, gboolean set_local_value, const void *data_ptr, const uint32_t *args, GError **error) -{ - void *p = ((uint8_t *)&l->data) + e->offset; - uint32_t cc = 0; - double fvalue = 0; - struct sampler_modulation_key mod_key = {0, 0, 0}; - struct sampler_noteinitfunc_key nif_key = {{NULL}, 0}; - struct sampler_flex_lfo_key flex_lfo_key = {0}; - - switch(e->type) - { - case slpt_midi_note_t: - if (!OVERRIDE_LOGIC(midi_note_t)) - return TRUE; - memcpy(p, data_ptr, sizeof(midi_note_t)); - break; - case slpt_int: - if (!OVERRIDE_LOGIC(int)) - return TRUE; - memcpy(p, data_ptr, sizeof(int)); - break; - case slpt_enum: - case slpt_uint32_t: - if (!OVERRIDE_LOGIC(uint32_t)) - return TRUE; - memcpy(p, data_ptr, sizeof(uint32_t)); - break; - case slpt_string: - { - char **pc = p; - gboolean str_differs = (!*pc != !data_ptr) || strcmp(*pc, data_ptr); - if (!override_logic(!str_differs, e->get_has_value(&l->data), set_local_value)) - return TRUE; - if (str_differs) - { - free(*pc); - *pc = g_strdup(data_ptr); - gboolean *changed_ptr = (gboolean *)(((uint8_t *)&l->data) + e->extra_int); - *changed_ptr = 1; - } - } - break; - case slpt_float: - case slpt_dBamp: - fvalue = *(double *)data_ptr; - if (!override_logic((float)fvalue == *(float *)p, e->get_has_value(&l->data), set_local_value)) - return TRUE; - *(float *)p = fvalue; - break; - case slpt_midicurve: - CAST_FLOAT_VALUE; - if (args[0] >= 0 && args[0] <= 127) - { - gboolean (*setgethasfunc)(struct sampler_layer_data *, uint32_t, int) = e->extra_ptr; - float *dst = &((struct sampler_midi_curve *)p)->values[args[0]]; - if (!override_logic(*dst == fvalue, setgethasfunc(&l->data, args[0], -1), set_local_value)) - return TRUE; - *dst = fvalue; - setgethasfunc(&l->data, args[0], set_local_value); - } - else - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Curve entry index (%u) is out of range for %s", (unsigned)args[0], e->name); - return FALSE; - } - break; - case slpt_ccrange: - { - int number = *(int *)data_ptr; - cc = args[0]; - switch(e->extra_int) { - case 0: - sampler_cc_range_set_locc_by_offset(l, e->offset, &(struct sampler_cc_range_key){cc}, set_local_value, number); - break; - case 1: - sampler_cc_range_set_hicc_by_offset(l, e->offset, &(struct sampler_cc_range_key){cc}, set_local_value, number); - break; - default: assert(0); - } - break; - } - case slpt_mod_amount: - CAST_FLOAT_VALUE; - mod_key_decode(e->extra_int, args, &mod_key); - sampler_modulation_set_amount_by_offset(l, e->offset, &mod_key, set_local_value, fvalue); - break; - case slpt_mod_curveid: - CAST_FLOAT_VALUE; - mod_key_decode(e->extra_int, args, &mod_key); - sampler_modulation_set_curve_id_by_offset(l, e->offset, &mod_key, set_local_value, (int)fvalue); - break; - case slpt_mod_smooth: - CAST_FLOAT_VALUE; - mod_key_decode(e->extra_int, args, &mod_key); - sampler_modulation_set_smooth_by_offset(l, e->offset, &mod_key, set_local_value, fvalue); - break; - case slpt_mod_step: - CAST_FLOAT_VALUE; - mod_key_decode(e->extra_int, args, &mod_key); - sampler_modulation_set_step_by_offset(l, e->offset, &mod_key, set_local_value, fvalue); - break; - case slpt_generic_modulation: - CAST_FLOAT_VALUE; - sampler_modulation_set_amount_by_offset(l, e->offset, &(struct sampler_modulation_key){args[0], args[1], args[2]}, set_local_value, fvalue); - sampler_modulation_set_curve_id_by_offset(l, e->offset, &(struct sampler_modulation_key){args[0], args[1], args[2]}, set_local_value, (int)args[3]); - break; - case slpt_voice_nif: - case slpt_prevoice_nif: - CAST_FLOAT_VALUE; - nif_key_decode(e->extra_int, e->extra_ptr, args, &nif_key); - switch(e->extra_int & NIF_VARIANT_MASK) - { - case 0: - case NIF_VARIANT_CC: - sampler_noteinitfunc_set_value_by_offset(l, e->offset, &nif_key, set_local_value, fvalue); - break; - case NIF_VARIANT_CURVECC: - sampler_noteinitfunc_set_curve_id_by_offset(l, e->offset, &nif_key, set_local_value, (int)fvalue); - break; - case NIF_VARIANT_STEPCC: - sampler_noteinitfunc_set_step_by_offset(l, e->offset, &nif_key, set_local_value, fvalue); - break; - } - break; - case slpt_flex_lfo: - CAST_FLOAT_VALUE; - flex_lfo_key_decode(args, &flex_lfo_key); - switch(e->extra_int) - { - case sampler_flex_lfo_value_field_freq: - sampler_flex_lfo_set_freq_by_offset(l, e->offset, &flex_lfo_key, set_local_value, fvalue); - break; - case sampler_flex_lfo_value_field_delay: - sampler_flex_lfo_set_delay_by_offset(l, e->offset, &flex_lfo_key, set_local_value, fvalue); - break; - case sampler_flex_lfo_value_field_fade: - sampler_flex_lfo_set_fade_by_offset(l, e->offset, &flex_lfo_key, set_local_value, fvalue); - break; - case sampler_flex_lfo_value_field_wave: - sampler_flex_lfo_set_wave_by_offset(l, e->offset, &flex_lfo_key, set_local_value, (int)fvalue); - break; - } - break; - case slpt_reserved: - case slpt_invalid: - case slpt_alias: - case slpt_nonfunctional: - printf("Unhandled parameter type of parameter %s\n", e->name); - assert(0); - return FALSE; - } - if (e->set_has_value) - e->set_has_value(&l->data, set_local_value); - if (l->child_layers) { - /* Propagate to children */ - GHashTableIter iter; - g_hash_table_iter_init(&iter, l->child_layers); - gpointer key, value; - while(g_hash_table_iter_next(&iter, &key, &value)) - { - struct sampler_layer *child = value; - if (!sampler_layer_param_entry_set_from_ptr(e, child, FALSE, data_ptr, args, error)) - return FALSE; - } - } - return TRUE; -} - -#define VERIFY_FLOAT_VALUE do { if (!atof_C_verify(e->name, value, &fvalue, error)) return FALSE; } while(0) - -gboolean sampler_layer_param_entry_set_from_string(const struct sampler_layer_param_entry *e, struct sampler_layer *l, gboolean set_local_value, const char *value, const uint32_t *args, GError **error) -{ - double fvalue; - switch(e->type) - { - case slpt_midi_note_t: - { - midi_note_t note = sfz_note_from_string(value); - if (note < -1) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "'%s' is not a valid note name for %s", value, e->name); - return FALSE; - } - return sampler_layer_param_entry_set_from_ptr(e, l, set_local_value, ¬e, args, error); - } - case slpt_int: - { - char *endptr; - errno = 0; - int number = strtol(value, &endptr, 10); - if (errno || *endptr || endptr == value) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "'%s' is not a correct integer value for %s", value, e->name); - return FALSE; - } - return sampler_layer_param_entry_set_from_ptr(e, l, set_local_value, &number, args, error); - } - case slpt_enum: - { - gboolean (*func)(const char *, uint32_t *value); - func = e->extra_ptr; - uint32_t data = 0; - if (!func(value, &data)) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "'%s' is not a correct value for %s", value, e->name); - return FALSE; - } - return sampler_layer_param_entry_set_from_ptr(e, l, set_local_value, &data, args, error); - } - case slpt_uint32_t: - { - char *endptr; - errno = 0; - uint32_t number = (uint32_t)strtoul(value, &endptr, 10); - if (errno || *endptr || endptr == value || value[0] == '-') - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "'%s' is not a correct unsigned integer value for %s", value, e->name); - return FALSE; - } - return sampler_layer_param_entry_set_from_ptr(e, l, set_local_value, &number, args, error); - } - case slpt_string: - return sampler_layer_param_entry_set_from_ptr(e, l, set_local_value, value, args, error); - case slpt_ccrange: - { - char *endptr; - errno = 0; - int number = strtol(value, &endptr, 10); - if (errno || *endptr || endptr == value || number < 0 || number > 127) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "'%s' is not a correct control change value for %s", value, e->name); - return FALSE; - } - return sampler_layer_param_entry_set_from_ptr(e, l, set_local_value, &number, args, error); - } - case slpt_nonfunctional: - { - char *argptr = strchr(e->name, '#'); - if (argptr) - { - int rootlen = argptr - e->name; - char buf[128]; - strncpy(buf, e->name, rootlen); - sprintf(buf + rootlen, "%d", args[0]); - sampler_layer_apply_unknown(l, buf, value); - } - else - sampler_layer_apply_unknown(l, e->name, value); - return TRUE; - } - case slpt_float: - case slpt_dBamp: - default: - VERIFY_FLOAT_VALUE; - return sampler_layer_param_entry_set_from_ptr(e, l, set_local_value, &fvalue, args, error); - } -} - -#define COPY_NUM_FROM_PARENT(case_value, type) \ - case case_value: \ - if (!unset_local_value && e->get_has_value(&l->data)) \ - return TRUE; \ - *(type *)p = pp ? *(type *)pp : (type)e->def_value; \ - e->set_has_value(&l->data, 0); \ - break; -gboolean sampler_layer_param_entry_unset(const struct sampler_layer_param_entry *e, struct sampler_layer *l, gboolean unset_local_value, const uint32_t *args, GError **error) -{ - void *p = ((uint8_t *)&l->data) + e->offset; - void *pp = l->parent ? ((uint8_t *)&l->parent->data) + e->offset : NULL; - uint32_t cc; - struct sampler_modulation_key mod_key = {0, 0, 0}; - struct sampler_noteinitfunc_key nif_key = {{NULL}, 0}; - struct sampler_flex_lfo_key flex_lfo_key = {0}; - - switch(e->type) - { - COPY_NUM_FROM_PARENT(slpt_midi_note_t, midi_note_t) - COPY_NUM_FROM_PARENT(slpt_int, int) - COPY_NUM_FROM_PARENT(slpt_enum, uint32_t) // XXXKF that's a kludge, enums are not guaranteed to be uint32_t (but they should be on common platforms) - COPY_NUM_FROM_PARENT(slpt_uint32_t, uint32_t) - COPY_NUM_FROM_PARENT(slpt_float, float) - COPY_NUM_FROM_PARENT(slpt_dBamp, float) - case slpt_string: - { - if (!unset_local_value && e->get_has_value(&l->data)) - return TRUE; - char **pc = p; - free(*pc); - *pc = pp ? g_strdup(*(const char **)pp) : NULL; - e->set_has_value(&l->data, 0); - gboolean *changed_ptr = (gboolean *)(((uint8_t *)&l->data) + e->extra_int); - *changed_ptr = 1; - } - return TRUE; - case slpt_midicurve: - if (args[0] >= 0 && args[0] <= 127) - { - struct sampler_midi_curve *curve = p, *parent_curve = pp; - gboolean (*setgethasfunc)(struct sampler_layer_data *, uint32_t, gboolean value) = e->extra_ptr; - if (setgethasfunc(&l->data, args[0], -1) && !unset_local_value) - return TRUE; - curve->values[args[0]] = parent_curve ? parent_curve->values[args[0]] : SAMPLER_CURVE_GAP; - setgethasfunc(&l->data, args[0], 0); - break; - } - else - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Curve entry index (%u) is out of range for %s", (unsigned)args[0], e->name); - return FALSE; - } - case slpt_ccrange: - cc = args[0]; - if (!sampler_cc_range_unset_by_offset(l, e->offset, &(struct sampler_cc_range_key){cc}, unset_local_value, 1 << e->extra_int)) - { - if (!unset_local_value) - return TRUE; - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Controller number %d not used for %s", cc, e->name); - return FALSE; - } - break; - case slpt_mod_amount: - mod_key_decode(e->extra_int, args, &mod_key); - sampler_modulation_unset_by_offset(l, e->offset, &mod_key, unset_local_value, 1 << sampler_modulation_value_field_amount); - break; - case slpt_mod_curveid: - mod_key_decode(e->extra_int, args, &mod_key); - sampler_modulation_unset_by_offset(l, e->offset, &mod_key, unset_local_value, 1 << sampler_modulation_value_field_curve_id); - break; - case slpt_mod_smooth: - mod_key_decode(e->extra_int, args, &mod_key); - sampler_modulation_unset_by_offset(l, e->offset, &mod_key, unset_local_value, 1 << sampler_modulation_value_field_smooth); - break; - case slpt_mod_step: - mod_key_decode(e->extra_int, args, &mod_key); - sampler_modulation_unset_by_offset(l, e->offset, &mod_key, unset_local_value, 1 << sampler_modulation_value_field_step); - break; - case slpt_generic_modulation: - mod_key = (struct sampler_modulation_key){args[0], args[1], args[2]}; - sampler_modulation_unset_by_offset(l, e->offset, &mod_key, unset_local_value, (1 << sampler_modulation_value_field_amount) | (1 << sampler_modulation_value_field_curve_id)); - break; - case slpt_voice_nif: - case slpt_prevoice_nif: - { - nif_key_decode(e->extra_int, e->extra_ptr, args, &nif_key); - static const uint32_t value_fields[] = { - sampler_noteinitfunc_value_field_value, sampler_noteinitfunc_value_field_value, - sampler_noteinitfunc_value_field_curve_id, sampler_noteinitfunc_value_field_step, - }; - if (!sampler_noteinitfunc_unset_by_offset(l, e->offset, &nif_key, unset_local_value, 1 << value_fields[e->extra_int >> 24])) - { - if (!unset_local_value) - return TRUE; - if (e->extra_int & NIF_VARIANT_MASK) - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Controller number %d not used for %s", args[0], e->name); - else - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "%s not set", e->name); - return FALSE; - } - break; - } - case slpt_flex_lfo: - flex_lfo_key_decode(args, &flex_lfo_key); - switch(e->extra_int) - { - case sampler_flex_lfo_value_field_freq: - sampler_flex_lfo_unset_by_offset(l, e->offset, &flex_lfo_key, unset_local_value, 1 << sampler_flex_lfo_value_field_freq); - break; - case sampler_flex_lfo_value_field_delay: - sampler_flex_lfo_unset_by_offset(l, e->offset, &flex_lfo_key, unset_local_value, 1 << sampler_flex_lfo_value_field_delay); - break; - case sampler_flex_lfo_value_field_fade: - sampler_flex_lfo_unset_by_offset(l, e->offset, &flex_lfo_key, unset_local_value, 1 << sampler_flex_lfo_value_field_fade); - break; - case sampler_flex_lfo_value_field_wave: - sampler_flex_lfo_unset_by_offset(l, e->offset, &flex_lfo_key, unset_local_value, 1 << sampler_flex_lfo_value_field_wave); - break; - } - break; - case slpt_invalid: - case slpt_reserved: - case slpt_alias: - default: - printf("Unhandled parameter type of parameter %s\n", e->name); - assert(0); - return FALSE; - } - if (l->child_layers) { - /* Propagate to children */ - GHashTableIter iter; - g_hash_table_iter_init(&iter, l->child_layers); - gpointer key, value; - while(g_hash_table_iter_next(&iter, &key, &value)) - { - struct sampler_layer *child = value; - if (!sampler_layer_param_entry_unset(e, child, FALSE, args, error)) - return FALSE; - } - } - return TRUE; -} -#undef COPY_NUM_FROM_PARENT - -// Compare against a template that uses # to represent a number, extract -// any such numbers. -static int templcmp(const char *key, const char *templ, uint32_t *numbers) -{ - while(*key && *templ) - { - if (*templ == '#') - { - if (isdigit(*key)) { - uint32_t num = 0; - do { - num = num * 10 + (unsigned char)(*key - '0'); - key++; - } while (isdigit(*key)); - *numbers++ = num; - templ++; - continue; - } - } - else if (*key == *templ) - { - templ++, key++; - continue; - } - if (*key < *templ) - return -1; - else - return +1; - } - if (*key) - return +1; - if (*templ) - return -1; - return 0; -} - -const struct sampler_layer_param_entry *sampler_layer_param_find(const char *key, uint32_t *args) -{ - static int prepared = 0; - if (!prepared) - { - sampler_layer_prepare_params(); - prepared = 1; - } - int niter = 0; - uint32_t lo = 0, hi = NPARAMS; - while(lo < hi) { - ++niter; - uint32_t mid = (lo + hi) >> 1; - const struct sampler_layer_param_entry *e = &sampler_layer_params[mid]; - int res = templcmp(key, e->name, args); - if (res == 0) - { - // printf("%s found in %d iterations\n", key, niter); - if (e->type == slpt_alias) - return (const struct sampler_layer_param_entry *)e->extra_ptr; - return e; - } - if (res < 0) - hi = mid; - else - lo = mid + 1; - } - return NULL; -} - -int sampler_layer_apply_fixed_param(struct sampler_layer *l, const char *key, const char *value, GError **error) -{ - uint32_t args[10]; - const struct sampler_layer_param_entry *e = sampler_layer_param_find(key, args); - if (e) - return sampler_layer_param_entry_set_from_string(e, l, TRUE, value, args, error); - else - return -1; -} - -int sampler_layer_unapply_fixed_param(struct sampler_layer *l, const char *key, GError **error) -{ - uint32_t args[10]; - const struct sampler_layer_param_entry *e = sampler_layer_param_find(key, args); - if (e) - return sampler_layer_param_entry_unset(e, l, TRUE, args, error); - else - return -1; -} - -static int count_ancestors(struct sampler_layer *l) -{ - int ancestors = 0; - for (; l->parent; l = l->parent) - ++ancestors; - assert(ancestors < 4); - return ancestors; -} - -static gboolean sampler_layer_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct sampler_layer *layer = ct->user_data; - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - static const char *layer_types[] = { "global", "master", "group", "region" }; - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - if (!((!layer->parent_program || cbox_execute_on(fb, NULL, "/parent_program", "o", error, layer->parent_program)) && - (!layer->parent || cbox_execute_on(fb, NULL, "/parent", "o", error, layer->parent)) && - cbox_execute_on(fb, NULL, "/level", "s", error, layer_types[count_ancestors(layer)]) && - CBOX_OBJECT_DEFAULT_STATUS(layer, fb, error))) - return FALSE; - return TRUE; - } - if ((!strcmp(cmd->command, "/as_string") || !strcmp(cmd->command, "/as_string_full")) && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - gchar *res = sampler_layer_to_string(layer, !strcmp(cmd->command, "/as_string_full")); - gboolean result = cbox_execute_on(fb, NULL, "/value", "s", error, res[0] == ' ' ? res + 1 : res); - g_free(res); - return result; - } - if ((!strcmp(cmd->command, "/as_list") || !strcmp(cmd->command, "/as_list_full")) && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - // Temporary, kludgy implementation - gchar *res = sampler_layer_to_string(layer, !strcmp(cmd->command, "/as_list_full")); - const gchar *iter = res; - gboolean result = TRUE; - while(iter) { - while(isspace(*iter)) - ++iter; - const gchar *pkey = iter; - const gchar *eq = iter; - while(*eq && *eq != '=') - ++eq; - if (*eq == '=') - { - gchar *key = g_strndup(pkey, eq - pkey); - const gchar *pvalue = eq + 1; - while(*pvalue && isspace(*pvalue)) - ++pvalue; - const gchar *pend = pvalue; - while(*pend && *pend != '=') - ++pend; - if (*pend == '=' ) - { - while(pend > pvalue && (isalnum(pend[-1]) || pend[-1] == '_')) - pend--; - iter = pend; - } - else - iter = NULL; - while(pend > pvalue && isspace(pend[-1])) - pend--; - gchar *value = g_strndup(pvalue, pend - pvalue); - result = cbox_execute_on(fb, NULL, "/value", "ss", error, key, value); - g_free(value); - g_free(key); - if (!result) - break; - } - else - break; - } - g_free(res); - return result; - } - if (!strcmp(cmd->command, "/set_param") && !strcmp(cmd->arg_types, "ss")) - { - const char *key = CBOX_ARG_S(cmd, 0); - const char *value = CBOX_ARG_S(cmd, 1); - if (sampler_layer_apply_param(layer, key, value, error)) - { - sampler_layer_update(layer); - sampler_program_update_layers(layer->parent_program); - return TRUE; - } - return FALSE; - } - if (!strcmp(cmd->command, "/unset_param") && !strcmp(cmd->arg_types, "s")) - { - const char *key = CBOX_ARG_S(cmd, 0); - if (sampler_layer_unapply_param(layer, key, error)) - { - sampler_layer_update(layer); - sampler_program_update_layers(layer->parent_program); - return TRUE; - } - return FALSE; - } - if (!strcmp(cmd->command, "/new_child") && !strcmp(cmd->arg_types, "")) - { - // XXXKF needs a string argument perhaps - if (layer->parent && layer->parent->parent && layer->parent->parent->parent) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot create a region within a region"); - return FALSE; - } - struct sampler_layer *l = sampler_layer_new(layer->module, layer->parent_program, layer); - sampler_layer_data_finalize(&l->data, l->parent ? &l->parent->data : NULL, layer->parent_program); - sampler_layer_reset_switches(l, l->module); - sampler_layer_update(l); - - if (l->parent && l->parent->parent && l->parent->parent->parent) - { - sampler_program_add_layer(layer->parent_program, l); - sampler_program_update_layers(layer->parent_program); - } - - return cbox_execute_on(fb, NULL, "/uuid", "o", error, l); - } - if (!strcmp(cmd->command, "/get_children") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - GHashTableIter iter; - g_hash_table_iter_init(&iter, layer->child_layers); - gpointer key, value; - while(g_hash_table_iter_next(&iter, &key, &value)) - { - if (!cbox_execute_on(fb, NULL, "/child", "o", error, key)) - return FALSE; - } - return TRUE; - } - // otherwise, treat just like an command on normal (non-aux) output - return cbox_object_default_process_cmd(ct, fb, cmd, error); -} - - -#define PROC_FIELDS_INITIALISER(type, name, def_value) \ - ld->name = def_value; \ - ld->has_##name = 0; -#define PROC_FIELDS_INITIALISER_string(name) \ - ld->name = NULL; \ - ld->name##_changed = FALSE; \ - ld->has_##name = 0; -#define PROC_FIELDS_INITIALISER_midicurve(name) \ - sampler_midi_curve_init(&ld->name); -#define PROC_FIELDS_INITIALISER_enum(type, name, def_value) \ - PROC_FIELDS_INITIALISER(type, name, def_value) -#define PROC_FIELDS_INITIALISER_dBamp(type, name, def_value) \ - ld->name = def_value; \ - ld->name##_linearized = -1; \ - ld->has_##name = 0; -#define PROC_FIELDS_INITIALISER_dahdsr(name, parname, index) \ - DAHDSR_FIELDS(PROC_SUBSTRUCT_RESET_FIELD, name, ld); \ - DAHDSR_FIELDS(PROC_SUBSTRUCT_RESET_HAS_FIELD, name, ld) -#define PROC_FIELDS_INITIALISER_lfo(name, parname, index) \ - LFO_FIELDS(PROC_SUBSTRUCT_RESET_FIELD, name, ld); \ - LFO_FIELDS(PROC_SUBSTRUCT_RESET_HAS_FIELD, name, ld) -#define PROC_FIELDS_INITIALISER_eq(name, parname, index) \ - EQ_FIELDS(PROC_SUBSTRUCT_RESET_FIELD, name, ld); \ - EQ_FIELDS(PROC_SUBSTRUCT_RESET_HAS_FIELD, name, ld) -#define PROC_FIELDS_INITIALISER_ccrange(name, parname) \ - ld->name = NULL; - -CBOX_CLASS_DEFINITION_ROOT(sampler_layer) - -struct sampler_layer *sampler_layer_new(struct sampler_module *m, struct sampler_program *parent_program, struct sampler_layer *parent) -{ - struct sampler_layer *l = calloc(1, sizeof(struct sampler_layer)); - struct cbox_document *doc = CBOX_GET_DOCUMENT(parent_program); - memset(l, 0, sizeof(struct sampler_layer)); - CBOX_OBJECT_HEADER_INIT(l, sampler_layer, doc); - cbox_command_target_init(&l->cmd_target, sampler_layer_process_cmd, l); - - l->module = m; - l->child_layers = g_hash_table_new(NULL, NULL); - if (parent) - { - sampler_layer_data_clone(&l->data, &parent->data, FALSE); - l->parent_program = parent_program; - l->parent = parent; - g_hash_table_replace(parent->child_layers, l, l); - l->runtime = NULL; - CBOX_OBJECT_REGISTER(l); - return l; - } - l->parent_program = parent_program; - - struct sampler_layer_data *ld = &l->data; - SAMPLER_FIXED_FIELDS(PROC_FIELDS_INITIALISER) - - ld->computed.eff_waveform = NULL; - ld->computed.eff_freq = 44100; - ld->modulations = NULL; - ld->voice_nifs = NULL; - ld->prevoice_nifs = NULL; - - ld->computed.eff_use_keyswitch = 0; - if (!parent) - { - // Systemwide default instead? - uint32_t mod_offset = LOFS(modulations); - sampler_modulation_set_amount_by_offset(l, mod_offset, &(struct sampler_modulation_key){74, smsrc_none, smdest_cutoff}, TRUE, 9600); - sampler_modulation_set_curve_id_by_offset(l, mod_offset, &(struct sampler_modulation_key){74, smsrc_none, smdest_cutoff}, TRUE, 1); - sampler_modulation_set_amount_by_offset(l, mod_offset, &(struct sampler_modulation_key){71, smsrc_none, smdest_resonance}, TRUE, 12); - sampler_modulation_set_curve_id_by_offset(l, mod_offset, &(struct sampler_modulation_key){71, smsrc_none, smdest_resonance}, TRUE, 1); - sampler_modulation_set_amount_by_offset(l, mod_offset, &(struct sampler_modulation_key){smsrc_pitchlfo, 1, smdest_pitch}, TRUE, 100); - } - l->runtime = NULL; - l->unknown_keys = NULL; - CBOX_OBJECT_REGISTER(l); - return l; -} - -#define PROC_FIELDS_CLONE(type, name, def_value) \ - dst->name = src->name; \ - dst->has_##name = copy_hasattr ? src->has_##name : FALSE; -#define PROC_FIELDS_CLONE_string(name) \ - dst->name = src->name ? g_strdup(src->name) : NULL; \ - dst->name##_changed = src->name##_changed; \ - dst->has_##name = copy_hasattr ? src->has_##name : FALSE; -#define PROC_FIELDS_CLONE_midicurve(name) \ - memcpy(dst->name.values, src->name.values, sizeof(float) * 128); \ - if(copy_hasattr) \ - memcpy(dst->name.has_values, src->name.has_values, sizeof(src->name.has_values)); -#define PROC_FIELDS_CLONE_dBamp PROC_FIELDS_CLONE -#define PROC_FIELDS_CLONE_enum PROC_FIELDS_CLONE -#define PROC_FIELDS_CLONE_dahdsr(name, parname, index) \ - DAHDSR_FIELDS(PROC_SUBSTRUCT_CLONE, name, dst, src) \ - if (!copy_hasattr) \ - DAHDSR_FIELDS(PROC_SUBSTRUCT_RESET_HAS_FIELD, name, dst) -#define PROC_FIELDS_CLONE_lfo(name, parname, index) \ - LFO_FIELDS(PROC_SUBSTRUCT_CLONE, name, dst, src) \ - if (!copy_hasattr) \ - LFO_FIELDS(PROC_SUBSTRUCT_RESET_HAS_FIELD, name, dst) -#define PROC_FIELDS_CLONE_eq(name, parname, index) \ - EQ_FIELDS(PROC_SUBSTRUCT_CLONE, name, dst, src) \ - if (!copy_hasattr) \ - EQ_FIELDS(PROC_SUBSTRUCT_RESET_HAS_FIELD, name, dst) -#define PROC_FIELDS_CLONE_ccrange(name, parname) \ - dst->name = sampler_cc_range_clone(src->name, copy_hasattr); - -void sampler_layer_data_clone(struct sampler_layer_data *dst, const struct sampler_layer_data *src, gboolean copy_hasattr) -{ - SAMPLER_FIXED_FIELDS(PROC_FIELDS_CLONE) - dst->modulations = sampler_modulation_clone(src->modulations, copy_hasattr); - dst->voice_nifs = sampler_noteinitfunc_clone(src->voice_nifs, copy_hasattr); - dst->prevoice_nifs = sampler_noteinitfunc_clone(src->prevoice_nifs, copy_hasattr); - dst->flex_lfos = sampler_flex_lfo_clone(src->flex_lfos, copy_hasattr); - dst->computed.eff_waveform = src->computed.eff_waveform; - if (dst->computed.eff_waveform) - cbox_waveform_ref(dst->computed.eff_waveform); -} - -void sampler_midi_curve_init(struct sampler_midi_curve *curve) -{ - for (uint32_t i = 0; i < 128; ++i) - curve->values[i] = SAMPLER_CURVE_GAP; - memset(curve->has_values, 0, 128); -} - -void sampler_midi_curve_interpolate(const struct sampler_midi_curve *curve, float dest[128], float def_start, float def_end, gboolean is_quadratic) -{ - const float *src = curve->values; - int start = 0; - float sv = src[start]; - if (sv == SAMPLER_CURVE_GAP) - sv = def_start; - if (is_quadratic && sv >= 0) - sv = sqrtf(sv); - for (int i = 1; i < 128; i++) - { - float ev = src[i]; - if (ev == SAMPLER_CURVE_GAP) - { - if (i < 127) - continue; - else - ev = def_end; - } - if (is_quadratic && ev >= 0) - ev = sqrtf(ev); - if (is_quadratic) - { - for (int j = start; j <= i; j++) - dest[j] = powf(sv + (ev - sv) * (j - start) / (i - start), 2.f); - } - else - { - for (int j = start; j <= i; j++) - dest[j] = sv + (ev - sv) * (j - start) / (i - start); - } - start = i; - sv = ev; - } -} - -static inline int sampler_filter_num_stages(float cutoff, enum sampler_filter_type fil_type) -{ - if (cutoff == -1) - return 0; - if (fil_type == sft_lp24hybrid || fil_type == sft_lp24 || fil_type == sft_lp24nr || fil_type == sft_hp24 || fil_type == sft_hp24nr || fil_type == sft_bp12) - return 2; - if (fil_type == sft_lp36) - return 3; - return 1; -} - - -// If veltrack > 0, then the default range goes from -84dB to 0dB -// If veltrack == 0, then the default range is all 0dB -// If veltrack < 0, then the default range goes from 0dB to -84dB -#define START_VALUE_amp_velcurve (l->amp_veltrack > 0 ? dB2gain(-l->amp_veltrack * 84.0 / 100.0) : 1) -#define END_VALUE_amp_velcurve (l->amp_veltrack < 0 ? dB2gain(l->amp_veltrack * 84.0 / 100.0) : 1) -#define IS_QUADRATIC_amp_velcurve l->velcurve_quadratic - -#define PROC_FIELDS_FINALISER(type, name, def_value) -#define PROC_FIELDS_FINALISER_string(name) -#define PROC_FIELDS_FINALISER_midicurve(name) \ - sampler_midi_curve_interpolate(&l->name, l->computed.eff_##name, START_VALUE_##name, END_VALUE_##name, IS_QUADRATIC_##name); -#define PROC_FIELDS_FINALISER_enum(type, name, def_value) -#define PROC_FIELDS_FINALISER_dBamp(type, name, def_value) \ - l->name##_linearized = dB2gain(l->name); -#define PROC_FIELDS_FINALISER_dahdsr(name, parname, index) \ - cbox_envelope_init_dahdsr(&l->name##_shape, &l->name, m->module.srate / CBOX_BLOCK_SIZE, 100.f, &l->name##_shape == &l->amp_env_shape); -#define PROC_FIELDS_FINALISER_lfo(name, parname, index) /* no finaliser required */ -#define PROC_FIELDS_FINALISER_eq(name, parname, index) l->name.effective_freq = (l->name.freq ? l->name.freq : 5 * powf(10.f, 1 + (index))); -#define PROC_FIELDS_FINALISER_ccrange(name, parname) /* no finaliser required */ - -void sampler_layer_data_finalize(struct sampler_layer_data *l, struct sampler_layer_data *parent, struct sampler_program *p) -{ - struct sampler_module *m = p->module; - SAMPLER_FIXED_FIELDS(PROC_FIELDS_FINALISER) - - // Handle change of sample in the parent group without override on region level - if (parent && (l->sample_changed || parent->sample_changed)) - { - struct cbox_waveform *oldwf = l->computed.eff_waveform; - if (l->sample && *l->sample) - { - GError *error = NULL; - l->computed.eff_waveform = cbox_wavebank_get_waveform(p->name, p->tarfile, p->sample_dir, l->sample, &error); - if (!l->computed.eff_waveform) - { - g_warning("Cannot load waveform \"%s\" in sample_dir \"%s\" : \"%s\"", l->sample, p->sample_dir, error ? error->message : "unknown error"); - g_error_free(error); - } - } - else - l->computed.eff_waveform = NULL; - if (oldwf) - cbox_waveform_unref(oldwf); - l->computed.eff_is_silent = !l->sample || !strcmp(l->sample, "*silence"); - l->sample_changed = FALSE; - } - - l->computed.eff_use_keyswitch = ((l->sw_down != -1) || (l->sw_up != -1) || (l->sw_last != -1) || (l->sw_previous != -1)); - l->computed.eff_use_simple_trigger_logic = - (l->seq_length == 1 && l->seq_position == 1) && - (l->trigger != stm_first && l->trigger != stm_legato) && - (l->lochan == 1 && l->hichan == 16) && - (l->lorand == 0 && l->hirand == 1) && - (l->lobend == -8192 && l->hibend == 8192) && - (l->lochanaft == 0 && l->hichanaft == 127) && - (l->lopolyaft == 0 && l->hipolyaft == 127) && - (l->lobpm == 0 && l->hibpm == NO_HI_BPM_VALUE) && - !l->cc && !l->computed.eff_use_keyswitch; - l->computed.eff_use_xfcc = l->xfin_cc || l->xfout_cc; - l->computed.eff_use_channel_mixer = l->position != 0 || l->width != 100; - l->computed.eff_freq = (l->computed.eff_waveform && l->computed.eff_waveform->info.samplerate) ? l->computed.eff_waveform->info.samplerate : 44100; - l->computed.eff_loop_mode = l->loop_mode; - l->computed.eff_use_filter_mods = l->cutoff != -1 || l->cutoff2 != -1; - if (l->loop_mode == slm_unknown) - { - if (l->computed.eff_waveform && l->computed.eff_waveform->has_loop) - l->computed.eff_loop_mode = slm_loop_continuous; - else - if (l->computed.eff_waveform) - l->computed.eff_loop_mode = l->loop_end == 0 ? slm_no_loop : slm_loop_continuous; - } - - l->computed.eff_loop_start = l->loop_start; - l->computed.eff_loop_end = l->loop_end; - if (l->computed.eff_loop_mode == slm_one_shot || l->computed.eff_loop_mode == slm_no_loop || l->computed.eff_loop_mode == slm_one_shot_chokeable) - l->computed.eff_loop_start = SAMPLER_NO_LOOP; - if ((l->computed.eff_loop_mode == slm_loop_continuous || l->computed.eff_loop_mode == slm_loop_sustain) && l->computed.eff_loop_start == SAMPLER_NO_LOOP) - l->computed.eff_loop_start = 0; - if ((l->computed.eff_loop_mode == slm_loop_continuous || l->computed.eff_loop_mode == slm_loop_sustain) && l->computed.eff_loop_start == 0 && l->computed.eff_waveform && l->computed.eff_waveform->has_loop) - l->computed.eff_loop_start = l->computed.eff_waveform->loop_start; - if (l->loop_end == 0 && l->computed.eff_waveform != NULL) - l->computed.eff_loop_end = l->computed.eff_waveform->has_loop ? l->computed.eff_waveform->loop_end : l->computed.eff_waveform->info.frames; - - if (l->off_mode == som_unknown) - l->off_mode = l->off_by != 0 ? som_fast : som_normal; - - // XXXKF this is dodgy, needs to convert to use 'programmed vs effective' values pattern - if (l->key >= 0 && l->key <= 127) - l->lokey = l->hikey = l->pitch_keycenter = l->key; - - // 'linearize' the virtual circular buffer - write 3 (or N) frames before end of the loop - // and 3 (N) frames at the start of the loop, and play it; in rare cases this will need to be - // repeated twice if output write pointer is close to CBOX_BLOCK_SIZE or playback rate is very low, - // but that's OK. - if (l->computed.eff_waveform && l->computed.eff_waveform->preloaded_frames == (size_t)l->computed.eff_waveform->info.frames) - { - int shift = l->computed.eff_waveform->info.channels == 2 ? 1 : 0; - uint32_t halfscratch = MAX_INTERPOLATION_ORDER << shift; - memcpy(&l->computed.scratch_loop[0], &l->computed.eff_waveform->data[(l->computed.eff_loop_end - MAX_INTERPOLATION_ORDER) << shift], halfscratch * sizeof(int16_t) ); - memcpy(&l->computed.scratch_end[0], &l->computed.eff_waveform->data[(l->computed.eff_loop_end - MAX_INTERPOLATION_ORDER) << shift], halfscratch * sizeof(int16_t) ); - memset(l->computed.scratch_end + halfscratch, 0, halfscratch * sizeof(int16_t)); - if (l->computed.eff_loop_start != (uint32_t)-1) - memcpy(l->computed.scratch_loop + halfscratch, &l->computed.eff_waveform->data[l->computed.eff_loop_start << shift], halfscratch * sizeof(int16_t)); - else - memset(l->computed.scratch_loop + halfscratch, 0, halfscratch * sizeof(int16_t)); - } - if (l->cutoff < 20) - l->computed.logcutoff = -1; - else - l->computed.logcutoff = 1200.0 * log(l->cutoff / 440.0) / log(2) + 5700.0; - - if (l->cutoff2 < 20) - l->computed.logcutoff2 = -1; - else - l->computed.logcutoff2 = 1200.0 * log(l->cutoff2 / 440.0) / log(2) + 5700.0; - - l->computed.eq_bitmask = ((l->eq1.gain != 0 || l->eq1.vel2gain != 0) ? 1 : 0) - | ((l->eq2.gain != 0 || l->eq2.vel2gain != 0) ? 2 : 0) - | ((l->eq3.gain != 0 || l->eq3.vel2gain != 0) ? 4 : 0); - l->computed.mod_bitmask = 0; - for(struct sampler_modulation *mod = l->modulations; mod; mod = mod->next) - { - const struct sampler_modulation_key *mk = &mod->key; - if (mk->dest >= smdest_eg_stage_start && mk->dest <= smdest_eg_stage_end) - l->computed.mod_bitmask |= slmb_ampeg_cc << ((mk->dest >> 4) & 3); - } - - l->computed.eff_use_prevoice = (l->delay || l->prevoice_nifs); - l->computed.eff_num_stages = sampler_filter_num_stages(l->cutoff, l->fil_type); - l->computed.eff_num_stages2 = sampler_filter_num_stages(l->cutoff2, l->fil2_type); - - l->computed.resonance_scaled = pow(l->resonance_linearized, 1.f / l->computed.eff_num_stages); - l->computed.resonance2_scaled = pow(l->resonance2_linearized, 1.f / l->computed.eff_num_stages2); -} - -void sampler_layer_reset_switches(struct sampler_layer *l, struct sampler_module *m) -{ - l->current_seq_position = l->data.seq_position; -} - -struct layer_foreach_struct -{ - struct sampler_layer *layer; - const char *cfg_section; -}; - -static void layer_foreach_func(void *user_data, const char *key) -{ - if (!strcmp(key, "file")) - key = "sample"; - // import is handled in sampler_load_layer_overrides - if (!strcmp(key, "import")) - return; - // layer%d should be ignored, it's handled by sampler_program_new_from_cfg - if (!strncmp(key, "layer", 5) && isdigit(key[5])) - return; - struct layer_foreach_struct *lfs = user_data; - const char *value = cbox_config_get_string(lfs->cfg_section, key); - GError *error = NULL; - if (!sampler_layer_apply_param(lfs->layer, key, value, &error)) - { - if (error) - g_warning("Error '%s', context: %s in section %s", error->message, key, lfs->cfg_section); - else - g_warning("Unknown sample layer parameter: %s in section %s", key, lfs->cfg_section); - } -} - -void sampler_layer_load_overrides(struct sampler_layer *l, const char *cfg_section) -{ - char *imp = cbox_config_get_string(cfg_section, "import"); - if (imp) - sampler_layer_load_overrides(l, imp); - - struct layer_foreach_struct lfs = { - .layer = l, - .cfg_section = cfg_section - }; - cbox_config_foreach_key(layer_foreach_func, cfg_section, &lfs); -} - -struct sampler_layer *sampler_layer_new_from_section(struct sampler_module *m, struct sampler_program *parent_program, struct sampler_layer *parent, const char *cfg_section) -{ - struct sampler_layer *l = sampler_layer_new(m, parent_program, parent ? parent : parent_program->global->default_child->default_child); - sampler_layer_load_overrides(l, cfg_section); - sampler_layer_data_finalize(&l->data, l->parent ? &l->parent->data : NULL, parent_program); - sampler_layer_reset_switches(l, m); - return l; -} - -static void sampler_layer_apply_unknown(struct sampler_layer *l, const char *key, const char *value) -{ - if (!l->unknown_keys) - l->unknown_keys = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - - g_hash_table_insert(l->unknown_keys, g_strdup(key), g_strdup(value)); -} - -gboolean sampler_layer_apply_param(struct sampler_layer *l, const char *key, const char *value, GError **error) -{ - int res = sampler_layer_apply_fixed_param(l, key, value, error); - if (res >= 0) - return res; - sampler_layer_apply_unknown(l, key, value); - //g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown SFZ property key: '%s'", key); - //return FALSE; - g_warning("Unknown SFZ property key: '%s'", key); - return TRUE; -} - -gboolean sampler_layer_unapply_param(struct sampler_layer *layer, const char *key, GError **error) -{ - int res = sampler_layer_unapply_fixed_param(layer, key, error); - if (res >= 0) - return res; - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown SFZ property key: '%s'", key); - return FALSE; -} - -#define TYPE_PRINTF_uint32_t(name, def_value) \ - if (show_inherited || l->has_##name) \ - g_string_append_printf(outstr, " %s=%u", #name, (unsigned)(l->name)); -#define TYPE_PRINTF_int(name, def_value) \ - if (show_inherited || l->has_##name) \ - g_string_append_printf(outstr, " %s=%d", #name, (int)(l->name)); -#define TYPE_PRINTF_midi_note_t(name, def_value) \ - if (show_inherited || l->has_##name) { \ - int val = l->name; \ - if (val == -1) \ - g_string_append_printf(outstr, " %s=-1", #name); \ - else \ - g_string_append_printf(outstr, " %s=%c%s%d", #name, "ccddeffggaab"[val%12], "\000#\000#\000\000#\000#\000#\000#\000"+(val%12), (val/12-1)); \ - } else {} -#define TYPE_PRINTF_float(name, def_value) \ - if (show_inherited || l->has_##name) \ - g_string_append_printf(outstr, " %s=%s", #name, g_ascii_dtostr(floatbuf, floatbufsize, l->name)); - -#define PROC_FIELDS_TO_FILEPTR(type, name, def_value) \ - TYPE_PRINTF_##type(name, def_value) -#define PROC_FIELDS_TO_FILEPTR_string(name) \ - if (show_inherited || l->has_##name) \ - g_string_append_printf(outstr, " %s=%s", #name, l->name ? l->name : ""); -#define PROC_FIELDS_TO_FILEPTR_midicurve(name) \ - for (uint32_t i = 0; i < 128; ++i) { \ - if ((show_inherited || l->name.has_values[i]) && l->name.values[i] != SAMPLER_CURVE_GAP) \ - g_string_append_printf(outstr, " %s_%u=%s", #name, (unsigned)i, g_ascii_dtostr(floatbuf, floatbufsize, l->name.values[i])); \ - } -#define PROC_FIELDS_TO_FILEPTR_dBamp(type, name, def_value) \ - if (show_inherited || l->has_##name) \ - g_string_append_printf(outstr, " %s=%s", #name, g_ascii_dtostr(floatbuf, floatbufsize, l->name)); -#define PROC_FIELDS_TO_FILEPTR_enum(enumtype, name, def_value) \ - if ((show_inherited || l->has_##name) && (tmpstr = enumtype##_to_string(l->name)) != NULL) \ - g_string_append_printf(outstr, " %s=%s", #name, tmpstr); - -#define ENV_PARAM_OUTPUT(param, index, def_value, env, envfield, envname) \ - if (show_inherited || l->has_##envfield.param) \ - g_string_append_printf(outstr, " " #envname "_" #param "=%s", g_ascii_dtostr(floatbuf, floatbufsize, env.param)); - -#define PROC_FIELDS_TO_FILEPTR_dahdsr(name, parname, index) \ - DAHDSR_FIELDS(ENV_PARAM_OUTPUT, l->name, name, parname) -#define PROC_FIELDS_TO_FILEPTR_lfo(name, parname, index) \ - LFO_FIELDS(ENV_PARAM_OUTPUT, l->name, name, parname) -#define PROC_FIELDS_TO_FILEPTR_eq(name, parname, index) \ - EQ_FIELDS(ENV_PARAM_OUTPUT, l->name, name, parname) -#define PROC_FIELDS_TO_FILEPTR_ccrange(name, parname) \ - { \ - struct sampler_cc_range *range = l->name; \ - while (range) { \ - if (show_inherited || range->value.has_locc) \ - g_string_append_printf(outstr, " " #parname "locc%d=%d", range->key.cc_number, range->value.locc); \ - if (show_inherited || range->value.has_hicc) \ - g_string_append_printf(outstr, " " #parname "hicc%d=%d", range->key.cc_number, range->value.hicc); \ - range = range->next; \ - } \ - } - -static const char *addrandom_variants[] = { "amp", "fil", "pitch" }; -static const char *env_stages[] = { "delay", "attack", "hold", "decay", "sustain", "release", "start" }; -static const char *modsrc_names[] = { "vel", "chanaft", "polyaft", "pitch", "pitcheg", "fileg", "ampeg", "pitchlfo", "fillfo", "amplfo", "" }; -static const char *moddest_names[] = { "gain", "pitch", "cutoff", "resonance", "tonectl", "pan", "amplitude", "cutoff2", "resonance2", "pitchlfo_freq", "fillfo_freq", "amplfo_freq", - "eq1_freq", "eq1_bw", "eq1_gain", - "eq2_freq", "eq2_bw", "eq2_gain", - "eq3_freq", "eq3_bw", "eq3_gain", - }; - -static void mod_cc_attrib_to_string(GString *outstr, const char *attrib, const struct sampler_modulation_key *md, const char *floatbuf) -{ - if (md->dest >= smdest_eg_stage_start && md->dest <= smdest_eg_stage_end) - { - uint32_t param = md->dest - smdest_eg_stage_start; - g_string_append_printf(outstr, " %seg_%s%s%d=%s", addrandom_variants[(param >> 4) & 3], env_stages[param & 15], attrib, md->src, floatbuf); - } - else if (md->src < smsrc_perchan_count) - { - g_string_append_printf(outstr, " %s%s%d=%s", moddest_names[md->dest], attrib, md->src, floatbuf); - } - else if ((md->src == smsrc_amplfo && md->dest == smdest_gain) || - (md->src == smsrc_fillfo && md->dest == smdest_cutoff) || - (md->src == smsrc_pitchlfo && md->dest == smdest_pitch)) - { - if (md->src2 < EXT_CC_COUNT) - g_string_append_printf(outstr, " %s_depth%s%d=%s", modsrc_names[md->src - smsrc_perchan_count], attrib, md->src2, floatbuf); - } - else if ((md->src == smsrc_ampenv && md->dest == smdest_gain) || - (md->src == smsrc_filenv && md->dest == smdest_cutoff) || - (md->src == smsrc_pitchenv && md->dest == smdest_pitch)) - { - if (md->src2 < EXT_CC_COUNT) - g_string_append_printf(outstr, " %s_depth%s%d=%s", modsrc_names[md->src - smsrc_perchan_count], attrib, md->src2, floatbuf); - } - else if ((md->src == smsrc_filenv && md->dest == smdest_cutoff2) || - (md->src == smsrc_fillfo && md->dest == smdest_cutoff2)) - { - if (md->src2 < EXT_CC_COUNT) - g_string_append_printf(outstr, " %s_depth2%s%d=%s", modsrc_names[md->src - smsrc_perchan_count], attrib, md->src2, floatbuf); - } - else - assert(md->src2 >= EXT_CC_COUNT); -} - -static void nif_attrib_to_string(GString *outstr, const char *attrib, const struct sampler_noteinitfunc *nd, const char *floatbuf) -{ - int v = nd->key.variant; - if (nd->value.value) - g_string_append_printf(outstr, " %s_cc%d=%s", attrib, v, floatbuf); - if (nd->value.curve_id) - g_string_append_printf(outstr, " %s_curvecc%d=%d", attrib, v, nd->value.curve_id); - if (nd->value.step) - { - char floatbuf2[G_ASCII_DTOSTR_BUF_SIZE]; - int floatbufsize = G_ASCII_DTOSTR_BUF_SIZE; - g_ascii_dtostr(floatbuf2, floatbufsize, nd->value.step); - g_string_append_printf(outstr, " %s_stepcc%d=%s", attrib, v, floatbuf2); - } -} - -gchar *sampler_layer_to_string(struct sampler_layer *lr, gboolean show_inherited) -{ - struct sampler_layer_data *l = &lr->data; - GString *outstr = g_string_sized_new(200); - const char *tmpstr; - char floatbuf[G_ASCII_DTOSTR_BUF_SIZE]; - int floatbufsize = G_ASCII_DTOSTR_BUF_SIZE; - SAMPLER_FIXED_FIELDS(PROC_FIELDS_TO_FILEPTR) - - for(struct sampler_noteinitfunc *nd = l->voice_nifs; nd; nd = nd->next) - { - if (!nd->value.has_value && !nd->value.has_curve && !nd->value.has_step && !show_inherited) - continue; - #define PROC_ENVSTAGE_NAME(name, index, def_value) #name, - static const char *env_stages[] = { DAHDSR_FIELDS(PROC_ENVSTAGE_NAME) "start" }; - uint32_t v = nd->key.variant; - g_ascii_dtostr(floatbuf, floatbufsize, nd->value.value); - - if (nd->key.notefunc_voice == sampler_nif_addrandom && v >= 0 && v <= 2) - g_string_append_printf(outstr, " %s_random=%s", addrandom_variants[v], floatbuf); - else if (nd->key.notefunc_voice == sampler_nif_vel2pitch) - g_string_append_printf(outstr, " pitch_veltrack=%s", floatbuf); - else if (nd->key.notefunc_voice == sampler_nif_vel2reloffset) - g_string_append_printf(outstr, " reloffset_veltrack=%s", floatbuf); - else if (nd->key.notefunc_voice == sampler_nif_cc2reloffset) - nif_attrib_to_string(outstr, "reloffset", nd, floatbuf); - else if (nd->key.notefunc_voice == sampler_nif_vel2offset) - g_string_append_printf(outstr, " offset_veltrack=%s", floatbuf); - else if (nd->key.notefunc_voice == sampler_nif_cc2offset) - nif_attrib_to_string(outstr, "offset", nd, floatbuf); - else if (nd->key.notefunc_voice == sampler_nif_vel2env && (v & 15) >= snif_env_delay && (v & 15) <= snif_env_start && ((v >> 4) & 3) < 3) - g_string_append_printf(outstr, " %seg_vel2%s=%s", addrandom_variants[v >> 4], env_stages[1 + (v & 15)], floatbuf); - else - assert(0); // unknown NIF - } - for(struct sampler_noteinitfunc *nd = l->prevoice_nifs; nd; nd = nd->next) - { - if (!nd->value.has_value && !nd->value.has_curve && !nd->value.has_step && !show_inherited) - continue; - g_ascii_dtostr(floatbuf, floatbufsize, nd->value.value); - - if (nd->key.notefunc_prevoice == sampler_nif_cc2delay) - nif_attrib_to_string(outstr, "delay", nd, floatbuf); - else if (nd->key.notefunc_prevoice == sampler_nif_addrandomdelay) - g_string_append_printf(outstr, " delay_random=%s", floatbuf); - else - assert(0); // unknown NIF - } - for(struct sampler_flex_lfo *flfo = l->flex_lfos; flfo; flfo = flfo->next) - { - if (flfo->value.has_freq || show_inherited) - { - g_ascii_dtostr(floatbuf, floatbufsize, flfo->value.freq); - g_string_append_printf(outstr, " lfo%d_freq=%s", (int)flfo->key.id, floatbuf); - } - if (flfo->value.has_delay || show_inherited) - { - g_ascii_dtostr(floatbuf, floatbufsize, flfo->value.delay); - g_string_append_printf(outstr, " lfo%d_delay=%s", (int)flfo->key.id, floatbuf); - } - if (flfo->value.has_fade || show_inherited) - { - g_ascii_dtostr(floatbuf, floatbufsize, flfo->value.fade); - g_string_append_printf(outstr, " lfo%d_fade=%s", (int)flfo->key.id, floatbuf); - } - if (flfo->value.has_wave || show_inherited) - g_string_append_printf(outstr, " lfo%d_wave=%d", (int)flfo->key.id, flfo->value.wave); - } - for(struct sampler_modulation *md = l->modulations; md; md = md->next) - { - const struct sampler_modulation_key *mk = &md->key; - const struct sampler_modulation_value *mv = &md->value; - if (mv->has_curve || show_inherited) - { - g_ascii_dtostr(floatbuf, floatbufsize, mv->curve_id); - mod_cc_attrib_to_string(outstr, "_curvecc", mk, floatbuf); - } - if (mv->has_smooth || show_inherited) - { - g_ascii_dtostr(floatbuf, floatbufsize, mv->smooth); - mod_cc_attrib_to_string(outstr, "_smoothcc", mk, floatbuf); - } - if (mv->has_step || show_inherited) - { - g_ascii_dtostr(floatbuf, floatbufsize, mv->step); - mod_cc_attrib_to_string(outstr, "_stepcc", mk, floatbuf); - } - if (mv->has_amount || show_inherited) - { - gboolean is_egcc = mk->dest >= smdest_eg_stage_start && mk->dest <= smdest_eg_stage_end; - gboolean is_lfofreq = mk->dest >= smdest_pitchlfo_freq && mk->dest <= smdest_eq3_gain; - g_ascii_dtostr(floatbuf, floatbufsize, mv->amount); - - if (mk->src2 == smsrc_none) - { - if (is_egcc) - { - uint32_t param = mk->dest - smdest_eg_stage_start; - g_string_append_printf(outstr, " %seg_%scc%d=%s", addrandom_variants[(param >> 4) & 3], env_stages[param & 15], mk->src, floatbuf); - continue; - } - if (mk->src < smsrc_perchan_count) - { - // Inconsistency: cutoff_cc5 but amplfo_freqcc5 - if (is_lfofreq) - g_string_append_printf(outstr, " %scc%d=%s", moddest_names[mk->dest], mk->src, floatbuf); - else - g_string_append_printf(outstr, " %s_cc%d=%s", moddest_names[mk->dest], mk->src, floatbuf); - continue; - } - if (mk->src < smsrc_perchan_count + sizeof(modsrc_names) / sizeof(modsrc_names[0])) - { - if ((mk->src == smsrc_filenv && mk->dest == smdest_cutoff) || - (mk->src == smsrc_pitchenv && mk->dest == smdest_pitch) || - (mk->src == smsrc_amplfo && mk->dest == smdest_gain) || - (mk->src == smsrc_fillfo && mk->dest == smdest_cutoff) || - (mk->src == smsrc_pitchlfo && mk->dest == smdest_pitch)) - g_string_append_printf(outstr, " %s_depth=%s", modsrc_names[mk->src - smsrc_perchan_count], floatbuf); - else if ((mk->src == smsrc_filenv && mk->dest == smdest_cutoff2) || - (mk->src == smsrc_fillfo && mk->dest == smdest_cutoff2)) - g_string_append_printf(outstr, " %s_depth2=%s", modsrc_names[mk->src - smsrc_perchan_count], floatbuf); - else if (is_lfofreq) - g_string_append_printf(outstr, " %s%s=%s", moddest_names[mk->dest], modsrc_names[mk->src - smsrc_perchan_count], floatbuf); - else - g_string_append_printf(outstr, " %s_%s=%s", moddest_names[mk->dest], modsrc_names[mk->src - smsrc_perchan_count], floatbuf); - continue; - } - } - if ((mk->src == smsrc_amplfo && mk->dest == smdest_gain) || - (mk->src == smsrc_fillfo && mk->dest == smdest_cutoff) || - (mk->src == smsrc_pitchlfo && mk->dest == smdest_pitch)) - { - switch(mk->src2) - { - case smsrc_chanaft: - case smsrc_polyaft: - g_string_append_printf(outstr, " %s_depth%s=%s", modsrc_names[mk->src - smsrc_perchan_count], modsrc_names[mk->src2 - smsrc_perchan_count], floatbuf); - continue; - case smsrc_none: - g_string_append_printf(outstr, " %s_depth=%s", modsrc_names[mk->src - smsrc_perchan_count], floatbuf); - continue; - default: - if (mk->src2 < EXT_CC_COUNT) - { - g_string_append_printf(outstr, " %s_depthcc%d=%s", modsrc_names[mk->src - smsrc_perchan_count], mk->src2, floatbuf); - continue; - } - break; - } - } - if ((mk->src == smsrc_ampenv && mk->dest == smdest_gain) || - (mk->src == smsrc_filenv && mk->dest == smdest_cutoff) || - (mk->src == smsrc_pitchenv && mk->dest == smdest_pitch)) - { - if (mk->src2 == smsrc_vel) - { - g_string_append_printf(outstr, " %s_vel2depth=%s", modsrc_names[mk->src - smsrc_perchan_count], floatbuf); - continue; - } - if (mk->src2 == smsrc_none) - { - g_string_append_printf(outstr, " %s_depth=%s", modsrc_names[mk->src - smsrc_perchan_count], floatbuf); - continue; - } - if (mk->src2 < EXT_CC_COUNT) - { - g_string_append_printf(outstr, " %s_depthcc%d=%s", modsrc_names[mk->src - smsrc_perchan_count], mk->src2, floatbuf); - continue; - } - } - if (mk->src == smsrc_filenv && mk->dest == smdest_cutoff2) - { - if (mk->src2 == smsrc_vel) - { - g_string_append_printf(outstr, " %s_vel2depth2=%s", modsrc_names[mk->src - smsrc_perchan_count], floatbuf); - continue; - } - assert(mk->src2 != smsrc_none); - if (mk->src2 < EXT_CC_COUNT) - { - g_string_append_printf(outstr, " %s_depth2cc%d=%s", modsrc_names[mk->src - smsrc_perchan_count], mk->src2, floatbuf); - continue; - } - } - if (mk->src == smsrc_fillfo && mk->dest == smdest_cutoff2) - { - assert(mk->src2 != smsrc_none); - if (mk->src2 < EXT_CC_COUNT) - { - g_string_append_printf(outstr, " %s_depth2cc%d=%s", modsrc_names[mk->src - smsrc_perchan_count], mk->src2, floatbuf); - continue; - } - } - g_string_append_printf(outstr, " genericmod_%d_%d_%d_%d=%s", mk->src, mk->src2, mk->dest, mv->curve_id, floatbuf); - } - } - - if (lr->unknown_keys) - { - GHashTableIter hti; - gchar *key, *value; - g_hash_table_iter_init(&hti, lr->unknown_keys); - while(g_hash_table_iter_next(&hti, (gpointer *)&key, (gpointer *)&value)) - g_string_append_printf(outstr, " %s=%s", key, value); - } - - gchar *res = outstr->str; - g_string_free(outstr, FALSE); - return res; -} - -void sampler_layer_dump(struct sampler_layer *l, FILE *f) -{ - gchar *str = sampler_layer_to_string(l, FALSE); - fprintf(f, "%s\n", str); -} - -void sampler_layer_data_close(struct sampler_layer_data *l) -{ - sampler_flex_lfos_destroy(l->flex_lfos); - sampler_cc_ranges_destroy(l->cc); - sampler_cc_ranges_destroy(l->on_cc); - sampler_cc_ranges_destroy(l->xfin_cc); - sampler_cc_ranges_destroy(l->xfout_cc); - sampler_noteinitfuncs_destroy(l->voice_nifs); - sampler_noteinitfuncs_destroy(l->prevoice_nifs); - sampler_modulations_destroy(l->modulations); - if (l->computed.eff_waveform) - { - cbox_waveform_unref(l->computed.eff_waveform); - l->computed.eff_waveform = NULL; - } - g_free(l->sample); -} - -void sampler_layer_data_destroy(struct sampler_layer_data *l) -{ - sampler_layer_data_close(l); - free(l); -} - -struct sampler_layer *sampler_layer_new_clone(struct sampler_layer *layer, - struct sampler_module *m, struct sampler_program *parent_program, struct sampler_layer *parent) -{ - struct sampler_layer *l = sampler_layer_new(m, parent_program, parent); - sampler_layer_data_clone(&l->data, &layer->data, TRUE); - sampler_layer_reset_switches(l, m); - if (layer->unknown_keys) - { - GHashTableIter iter; - g_hash_table_iter_init(&iter, layer->unknown_keys); - gpointer key, value; - while(g_hash_table_iter_next(&iter, &key, &value)) - sampler_layer_apply_param(l, (gchar *)key, (gchar *)value, NULL); - } - - GHashTableIter iter; - g_hash_table_iter_init(&iter, layer->child_layers); - gpointer key, value; - gboolean is_child_a_region = layer->parent && layer->parent->parent; - while(g_hash_table_iter_next(&iter, &key, &value)) - { - struct sampler_layer *chl = sampler_layer_new_clone(key, m, parent_program, l); - g_hash_table_insert(l->child_layers, chl, NULL); - if (key == layer->default_child) - l->default_child = chl; - if (is_child_a_region) - sampler_program_add_layer(parent_program, chl); - } - - return l; -} - -void sampler_layer_destroyfunc(struct cbox_objhdr *objhdr) -{ - struct sampler_layer *l = CBOX_H2O(objhdr); - struct sampler_program *prg = l->parent_program; - assert(g_hash_table_size(l->child_layers) == 0); - - if (l->parent) - { - g_hash_table_remove(l->parent->child_layers, l); - if (prg && prg->rll) - { - sampler_program_delete_layer(prg, l); - sampler_program_update_layers(l->parent_program); - } - l->parent = NULL; - } - sampler_layer_data_close(&l->data); - if (l->runtime) - sampler_layer_data_destroy(l->runtime); - if (l->unknown_keys) - g_hash_table_destroy(l->unknown_keys); - if (l->child_layers) - g_hash_table_destroy(l->child_layers); - - free(l); -} - -////////////////////////////////////////////////////////////////////////// - -struct sampler_layer_update_cmd -{ - struct sampler_module *module; - struct sampler_layer *layer; - struct sampler_layer_data *new_data; - struct sampler_layer_data *old_data; -}; - -static int sampler_layer_update_cmd_prepare(void *data) -{ - struct sampler_layer_update_cmd *cmd = data; - cmd->old_data = cmd->layer->runtime; - cmd->new_data = calloc(1, sizeof(struct sampler_layer_data)); - - sampler_layer_data_clone(cmd->new_data, &cmd->layer->data, TRUE); - sampler_layer_data_finalize(cmd->new_data, cmd->layer->parent ? &cmd->layer->parent->data : NULL, cmd->layer->parent_program); - if (cmd->layer->runtime == NULL) - { - // initial update of the layer, so none of the voices need updating yet - // because the layer hasn't been allocated to any voice - cmd->layer->runtime = cmd->new_data; - free(cmd); - return 1; - } - return 0; -} - -static int sampler_layer_update_cmd_execute(void *data) -{ - struct sampler_layer_update_cmd *cmd = data; - - for (int i = 0; i < 16; i++) - { - FOREACH_VOICE(cmd->module->channels[i].voices_running, v) - { - if (v->layer == cmd->layer->runtime) - { - v->layer = cmd->new_data; - v->layer_changed = TRUE; - sampler_voice_update_params_from_layer(v); - } - } - } - FOREACH_PREVOICE(cmd->module->prevoices_running, pv) - { - if (pv->layer_data == cmd->layer->runtime) - { - pv->layer_data = cmd->new_data; - // XXXKF when need arises - // pv->layer_changed = TRUE; - // sampler_prevoice_update_params_from_layer(v); - } - } - cmd->old_data = cmd->layer->runtime; - cmd->layer->runtime = cmd->new_data; - return 10; -} - -static void sampler_layer_update_cmd_cleanup(void *data) -{ - struct sampler_layer_update_cmd *cmd = data; - - sampler_layer_data_destroy(cmd->old_data); - free(cmd); -} - -void sampler_layer_update(struct sampler_layer *l) -{ - // if changing a group, update all child regions instead - if (g_hash_table_size(l->child_layers)) - { - GHashTableIter iter; - g_hash_table_iter_init(&iter, l->child_layers); - gpointer key, value; - while(g_hash_table_iter_next(&iter, &key, &value)) - { - sampler_layer_data_finalize(&((struct sampler_layer *)key)->data, &l->data, l->parent_program); - sampler_layer_update((struct sampler_layer *)key); - } - return; - } - static struct cbox_rt_cmd_definition rtcmd = { - .prepare = sampler_layer_update_cmd_prepare, - .execute = sampler_layer_update_cmd_execute, - .cleanup = sampler_layer_update_cmd_cleanup, - }; - - struct sampler_layer_update_cmd *lcmd = malloc(sizeof(struct sampler_layer_update_cmd)); - lcmd->module = l->module; - lcmd->layer = l; - lcmd->new_data = NULL; - lcmd->old_data = NULL; - - cbox_rt_execute_cmd_async(l->module->module.rt, &rtcmd, lcmd); -} diff --git a/template/calfbox/sampler_layer.h b/template/calfbox/sampler_layer.h deleted file mode 100644 index 0a1b5bc..0000000 --- a/template/calfbox/sampler_layer.h +++ /dev/null @@ -1,718 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_SAMPLER_LAYER_H -#define CBOX_SAMPLER_LAYER_H - -#include "dom.h" -#include "wavebank.h" -#include -#include - -// arbitrary value that doesn't collide with a useful range -#define SAMPLER_CURVE_GAP -100000 -#define NO_HI_BPM_VALUE 10000 -#define CC_COUNT 128 -#define EXT_CC_COUNT 143 - -struct sampler_program; -struct sampler_voice; -struct sampler_prevoice; -struct sampler_noteinitfunc; -struct sampler_module; - -enum sampler_player_type -{ - spt_inactive, - spt_mono16, - spt_stereo16, - spt_finished -}; - -enum sampler_loop_mode -{ - slm_unknown, - slm_no_loop, - slm_one_shot, - slm_loop_continuous, - slm_loop_sustain, // unsupported - slm_one_shot_chokeable, - slmcount -}; - -#define ENUM_VALUES_sampler_loop_mode(MACRO) \ - MACRO("no_loop", slm_no_loop) \ - MACRO("one_shot", slm_one_shot) \ - MACRO("loop_continuous", slm_loop_continuous) \ - MACRO("loop_sustain", slm_loop_sustain) \ - MACRO("one_shot_chokeable", slm_one_shot_chokeable) - -enum sampler_off_mode -{ - som_unknown, - som_normal, - som_fast -}; - -#define ENUM_VALUES_sampler_off_mode(MACRO) \ - MACRO("normal", som_normal) \ - MACRO("fast", som_fast) - -enum sampler_vel_mode -{ - svm_unknown, - svm_current, - svm_previous -}; - -#define ENUM_VALUES_sampler_vel_mode(MACRO) \ - MACRO("current", svm_current) \ - MACRO("previous", svm_previous) - -enum sampler_trigger -{ - stm_attack, - stm_release, - stm_first, - stm_legato, -}; - -#define ENUM_VALUES_sampler_trigger(MACRO) \ - MACRO("attack", stm_attack) \ - MACRO("release", stm_release) \ - MACRO("first", stm_first) \ - MACRO("legato", stm_legato) - -enum sampler_filter_type -{ - sft_unknown, - sft_lp12, - sft_hp12, - sft_bp6, - sft_lp24, - sft_hp24, - sft_bp12, - sft_lp6, - sft_hp6, - sft_lp12nr, - sft_hp12nr, - sft_lp24nr, - sft_hp24nr, - sft_lp24hybrid, - sft_lp36, -}; - -#define ENUM_VALUES_sampler_filter_type(MACRO) \ - MACRO("lpf_2p", sft_lp12) \ - MACRO("hpf_2p", sft_hp12) \ - MACRO("bpf_2p", sft_bp6) \ - MACRO("lpf_4p", sft_lp24) \ - MACRO("hpf_4p", sft_hp24) \ - MACRO("bpf_4p", sft_bp12) \ - MACRO("lpf_1p", sft_lp6) \ - MACRO("hpf_1p", sft_hp6) \ - MACRO("lpf_2p_nores", sft_lp12nr) \ - MACRO("hpf_2p_nores", sft_hp12nr) \ - MACRO("lpf_4p_nores", sft_lp24nr) \ - MACRO("hpf_4p_nores", sft_hp24nr) \ - MACRO("lpf_4p_hybrid", sft_lp24hybrid) \ - MACRO("lpf_6p", sft_lp36) \ - -enum sampler_xf_curve -{ - stxc_power, - stxc_gain, -}; - -#define ENUM_VALUES_sampler_xf_curve(MACRO) \ - MACRO("power", stxc_power) \ - MACRO("gain", stxc_gain) - -#define ENUM_LIST(MACRO) \ - MACRO(sampler_loop_mode) \ - MACRO(sampler_off_mode) \ - MACRO(sampler_vel_mode) \ - MACRO(sampler_trigger) \ - MACRO(sampler_filter_type) \ - MACRO(sampler_xf_curve) \ - -#define MAKE_FROM_TO_STRING_EXTERN(enumtype) \ - extern const char *enumtype##_to_string(enum enumtype value); \ - extern gboolean enumtype##_from_string(const char *name, enum enumtype *value); - -ENUM_LIST(MAKE_FROM_TO_STRING_EXTERN) - -enum sampler_modsrc -{ - smsrc_cc0 = 0, - - smsrc_pitchbend = 128, - smsrc_chanaft_sfz2 = 129, - smsrc_lastpolyaft = 130, // ? - smsrc_noteonvel = 131, - smsrc_noteoffvel = 132, - smsrc_keynotenum = 133, - smsrc_keynotegate = 134, - smsrc_random_unipolar = 135, - smsrc_random_bipolar = 136, - smsrc_alternate = 137, - smsrc_keydelta = 140, - smsrc_keydelta_abs = 141, - smsrc_tempo = 142, - - // those are per-note, not per-channel - smsrc_vel, - smsrc_chanaft, - smsrc_polyaft, - smsrc_pitch, - smsrc_pitchenv, - smsrc_filenv, - smsrc_ampenv, - smsrc_pitchlfo, - smsrc_fillfo, - smsrc_amplfo, - smsrc_none, - - smsrccount, - smsrc_perchan_offset = 0, - smsrc_perchan_count = smsrc_vel, - smsrc_pernote_offset = smsrc_vel, - smsrc_pernote_count = smsrccount - smsrc_pernote_offset, - - smsrc_ampeg = smsrc_ampenv, - smsrc_fileg = smsrc_filenv, - smsrc_pitcheg = smsrc_pitchenv, -}; - -enum sampler_moddest -{ - smdest_gain, - smdest_pitch, - smdest_cutoff, - smdest_resonance, - smdest_tonectl, - smdest_pan, - smdest_amplitude, - smdest_cutoff2, - smdest_resonance2, - smdest_pitchlfo_freq, - smdest_fillfo_freq, - smdest_amplfo_freq, - - smdest_eq1_freq, - smdest_eq1_bw, - smdest_eq1_gain, - smdest_eq2_freq, - smdest_eq2_bw, - smdest_eq2_gain, - smdest_eq3_freq, - smdest_eq3_bw, - smdest_eq3_gain, - - smdestcount, - - smdest_from_amplfo = smdest_gain, - smdest_from_fillfo = smdest_cutoff, - smdest_from_pitchlfo = smdest_pitch, - smdest_from_ampeg = smdest_gain, - smdest_from_fileg = smdest_cutoff, - smdest_from_pitcheg = smdest_pitch, - - smdest_ampeg_stage = 0x80, - smdest_fileg_stage = 0x90, - smdest_pitcheg_stage = 0xA0, - - smdest_eg_stage_start = 0x80, - smdest_eg_stage_end = 0xAF, -}; - -struct sampler_modulation_key -{ - enum sampler_modsrc src; - enum sampler_modsrc src2; - enum sampler_moddest dest; -}; - -struct sampler_modulation_value -{ - float amount; - float smooth; - float step; - unsigned int curve_id:12; - unsigned int has_amount:1; - unsigned int has_curve:1; - unsigned int has_smooth:1; - unsigned int has_step:1; -}; - -#define SAMPLER_COLL_FIELD_LIST_sampler_modulation(MACRO, ...) \ - MACRO(amount, has_amount, float, 0, ## __VA_ARGS__) \ - MACRO(curve_id, has_curve, uint32_t, 0, ## __VA_ARGS__) \ - MACRO(smooth, has_smooth, float, 0, ## __VA_ARGS__) \ - MACRO(step, has_step, float, 0, ## __VA_ARGS__) - -#define SAMPLER_COLL_CHAIN_LIST_sampler_modulation(MACRO, ...) \ - MACRO(modulations, modulation, ## __VA_ARGS__) - -////////////////////////////////////////////////////////////////////////////////////////////////// - -typedef void (*SamplerNoteInitFunc)(struct sampler_noteinitfunc *nif, struct sampler_voice *voice); -typedef void (*SamplerNoteInitFunc2)(struct sampler_noteinitfunc *nif, struct sampler_prevoice *prevoice); - -struct sampler_noteinitfunc_key -{ - union { - SamplerNoteInitFunc notefunc_voice; - SamplerNoteInitFunc2 notefunc_prevoice; - }; - int variant; -}; - -struct sampler_noteinitfunc_value -{ - float value; - uint32_t curve_id; - float step; - unsigned int has_value:1; - unsigned int has_curve:1; - unsigned int has_step:1; -}; - -enum sampler_noteinitfunc_envelope_variant -{ - snif_env_delay = 0, - snif_env_attack = 1, - snif_env_hold = 2, - snif_env_decay = 3, - snif_env_sustain = 4, - snif_env_release = 5, - snif_env_start = 6, -}; - -#define SAMPLER_COLL_FIELD_LIST_sampler_noteinitfunc(MACRO, ...) \ - MACRO(value, has_value, float, 0, ## __VA_ARGS__) \ - MACRO(curve_id, has_curve, int, 0, ## __VA_ARGS__) \ - MACRO(step, has_step, float, 0, ## __VA_ARGS__) - -#define SAMPLER_COLL_CHAIN_LIST_sampler_noteinitfunc(MACRO, ...) \ - MACRO(voice_nifs, voice_nif, ## __VA_ARGS__) \ - MACRO(prevoice_nifs, prevoice_nif, ## __VA_ARGS__) - -////////////////////////////////////////////////////////////////////////////////////////////////// - -struct sampler_cc_range_key -{ - uint8_t cc_number; -}; - -struct sampler_cc_range_value -{ - uint8_t locc; - uint8_t hicc; - uint8_t has_locc:1; - uint8_t has_hicc:1; -}; - -#define SAMPLER_COLL_FIELD_LIST_sampler_cc_range(MACRO, ...) \ - MACRO(locc, has_locc, uint8_t, 0, ## __VA_ARGS__) \ - MACRO(hicc, has_hicc, uint8_t, 127, ## __VA_ARGS__) - -#define SAMPLER_COLL_CHAIN_LIST_sampler_cc_range(MACRO, ...) \ - MACRO(on_cc, on_cc, ## __VA_ARGS__) \ - MACRO(cc, cc, ## __VA_ARGS__) \ - MACRO(xfin_cc, xfin_cc, ## __VA_ARGS__) \ - MACRO(xfout_cc, xfout_cc, ## __VA_ARGS__) - -////////////////////////////////////////////////////////////////////////////////////////////////// - -struct sampler_flex_lfo_key -{ - uint32_t id; -}; - -struct sampler_flex_lfo_value -{ - // Note: layout/order must be identical to sampler_lfo_params - float freq; - float delay; - float fade; - int wave; - - uint8_t has_freq:1; - uint8_t has_delay:1; - uint8_t has_fade:1; - uint8_t has_wave:1; -}; - -#define SAMPLER_COLL_FIELD_LIST_sampler_flex_lfo(MACRO, ...) \ - MACRO(freq, has_freq, float, 0, ## __VA_ARGS__) \ - MACRO(delay, has_delay, float, 0, ## __VA_ARGS__) \ - MACRO(fade, has_fade, float, 0, ## __VA_ARGS__) \ - MACRO(wave, has_wave, int, 0, ## __VA_ARGS__) - -#define SAMPLER_COLL_CHAIN_LIST_sampler_flex_lfo(MACRO, ...) \ - MACRO(flex_lfos, flex_lfos, ## __VA_ARGS__) - -////////////////////////////////////////////////////////////////////////////////////////////////// - -#define SAMPLER_COLL_LIST(MACRO) \ - MACRO(sampler_modulation) \ - MACRO(sampler_noteinitfunc) \ - MACRO(sampler_cc_range) \ - MACRO(sampler_flex_lfo) - -#define SAMPLER_COLL_DEFINITION(sname) \ - struct sname \ - { \ - struct sname##_key key; \ - struct sname##_value value; \ - struct sname *next; \ - }; - -SAMPLER_COLL_LIST(SAMPLER_COLL_DEFINITION) - -////////////////////////////////////////////////////////////////////////////////////////////////// - -struct sampler_lfo_params -{ - float freq; - float delay; - float fade; - int wave; -}; - -struct sampler_eq_params -{ - float freq; - float bw; - float gain; - float effective_freq; - float vel2freq; - float vel2gain; -}; - -typedef int midi_note_t; - -/* - * Transforms: - * notransform - self-explanatory - * dBamp - amplitude/gain stored as dB - */ - -#define SAMPLER_FIXED_FIELDS(MACRO) \ - MACRO##_string(sample) \ - MACRO(uint32_t, offset, 0) \ - MACRO(uint32_t, offset_random, 0) \ - MACRO(uint32_t, loop_start, 0) \ - MACRO(uint32_t, loop_end, 0) \ - MACRO(uint32_t, end, 0) \ - MACRO(uint32_t, loop_overlap, (uint32_t)-1) \ - MACRO##_enum(sampler_loop_mode, loop_mode, slm_unknown) \ - MACRO##_enum(sampler_trigger, trigger, stm_attack) \ - MACRO##_dBamp(float, volume, 0) \ - MACRO(float, amplitude, 100) \ - MACRO(float, pan, 0) \ - MACRO(float, position, 0) \ - MACRO(float, width, 100) \ - MACRO(float, tune, 0) \ - MACRO(int, transpose, 0) \ - MACRO(int, lochan, 1) \ - MACRO(int, hichan, 16) \ - MACRO(float, lorand, 0) \ - MACRO(float, hirand, 1) \ - MACRO(midi_note_t, key, -1) \ - MACRO(midi_note_t, lokey, 0) \ - MACRO(midi_note_t, hikey, 127) \ - MACRO(midi_note_t, pitch_keycenter, 60) \ - MACRO(int, pitch_keytrack, 100) \ - MACRO(midi_note_t, fil_keycenter, 60) \ - MACRO(int, fil_keytrack, 0) \ - MACRO(midi_note_t, fil2_keycenter, 60) \ - MACRO(int, fil2_keytrack, 0) \ - MACRO(midi_note_t, amp_keycenter, 60) \ - MACRO(int, amp_keytrack, 0) \ - MACRO(int, fil_veltrack, 0) \ - MACRO(int, fil2_veltrack, 0) \ - MACRO(int, amp_veltrack, 100) \ - MACRO(int, lovel, 0) \ - MACRO(int, hivel, 127) \ - MACRO(int, lobend, -8192) \ - MACRO(int, hibend, 8192) \ - MACRO(int, lochanaft, 0) \ - MACRO(int, hichanaft, 127) \ - MACRO(int, lopolyaft, 0) \ - MACRO(int, hipolyaft, 127) \ - MACRO(float, lobpm, 0.0) \ - MACRO(float, hibpm, NO_HI_BPM_VALUE) \ - MACRO(int, velcurve_quadratic, 1) \ - MACRO##_enum(sampler_filter_type, fil_type, sft_lp12) \ - MACRO##_enum(sampler_filter_type, fil2_type, sft_lp12) \ - MACRO##_enum(sampler_off_mode, off_mode, som_unknown) \ - MACRO##_enum(sampler_vel_mode, vel_mode, svm_current) \ - MACRO(float, cutoff, -1) \ - MACRO##_dBamp(float, resonance, 0) \ - MACRO(float, cutoff2, -1) \ - MACRO##_dBamp(float, resonance2, 0) \ - MACRO(midi_note_t, sw_lokey, 0) \ - MACRO(midi_note_t, sw_hikey, 127) \ - MACRO(midi_note_t, sw_last, -1) \ - MACRO(midi_note_t, sw_down, -1) \ - MACRO(midi_note_t, sw_up, -1) \ - MACRO(midi_note_t, sw_previous, -1) \ - MACRO(midi_note_t, sw_default, -1) \ - MACRO(int, seq_position, 1) \ - MACRO(int, seq_length, 1) \ - MACRO(float, sync_offset, 0) \ - MACRO(int, effect1bus, 1) \ - MACRO(int, effect2bus, 2) \ - MACRO(float, effect1, 0) \ - MACRO(float, effect2, 0) \ - MACRO(float, delay, 0) \ - MACRO(int, output, 0) \ - MACRO(int, group, 0) \ - MACRO(int, off_by, 0) \ - MACRO(int, count, 0) \ - MACRO(int, bend_up, 200) \ - MACRO(int, bend_down, -200) \ - MACRO(int, bend_step, 1) \ - MACRO(int, timestretch, 0) \ - MACRO(float, timestretch_jump, 500) \ - MACRO(float, timestretch_crossfade, 100) \ - MACRO(float, rt_decay, 0) \ - MACRO(float, tonectl, 0) \ - MACRO(float, tonectl_freq, 0) \ - MACRO(float, reloffset, 0) \ - MACRO(float, xfin_lokey, 0) \ - MACRO(float, xfin_hikey, 0) \ - MACRO(float, xfout_lokey, 127) \ - MACRO(float, xfout_hikey, 127) \ - MACRO##_enum(sampler_xf_curve, xf_keycurve, stxc_power) \ - MACRO(float, xfin_lovel, 0) \ - MACRO(float, xfin_hivel, 0) \ - MACRO(float, xfout_lovel, 127) \ - MACRO(float, xfout_hivel, 127) \ - MACRO##_enum(sampler_xf_curve, xf_velcurve, stxc_power) \ - MACRO##_ccrange(xfin_cc, xfin_) \ - MACRO##_ccrange(xfout_cc, xfout_) \ - MACRO##_enum(sampler_xf_curve, xf_cccurve, stxc_power) \ - MACRO##_dahdsr(amp_env, ampeg, 0) \ - MACRO##_dahdsr(filter_env, fileg, 1) \ - MACRO##_dahdsr(pitch_env, pitcheg, 2) \ - MACRO##_lfo(amp_lfo, amplfo, 0) \ - MACRO##_lfo(filter_lfo, fillfo, 1) \ - MACRO##_lfo(pitch_lfo, pitchlfo, 2) \ - MACRO##_eq(eq1, eq1, 0) \ - MACRO##_eq(eq2, eq2, 1) \ - MACRO##_eq(eq3, eq3, 2) \ - MACRO##_ccrange(on_cc, on_) \ - MACRO##_ccrange(cc, ) \ - MACRO##_midicurve(amp_velcurve) \ - -// XXXKF: consider making send1gain the dBamp type... except it's -// a linear percentage value in SFZ spec - bit weird! - -#define DAHDSR_FIELDS(MACRO, ...) \ - MACRO(start, 0, 0, ## __VA_ARGS__) \ - MACRO(delay, 1, 0, ## __VA_ARGS__) \ - MACRO(attack, 2, 0, ## __VA_ARGS__) \ - MACRO(hold, 3, 0, ## __VA_ARGS__) \ - MACRO(decay, 4, 0, ## __VA_ARGS__) \ - MACRO(sustain, 5, 100, ## __VA_ARGS__) \ - MACRO(release, 6, 0.05, ## __VA_ARGS__) \ - -#define LFO_FIELDS(MACRO, ...) \ - MACRO(freq, 0, 0, ## __VA_ARGS__) \ - MACRO(delay, 1, 0, ## __VA_ARGS__) \ - MACRO(fade, 2, 0, ## __VA_ARGS__) \ - MACRO(wave, 3, 1, ## __VA_ARGS__) \ - -#define EQ_FIELDS(MACRO, ...) \ - MACRO(freq, 0, 0, ## __VA_ARGS__) \ - MACRO(bw, 1, 1, ## __VA_ARGS__) \ - MACRO(gain, 2, 0, ## __VA_ARGS__) \ - MACRO(vel2freq, 3, 0, ## __VA_ARGS__) \ - MACRO(vel2gain, 5, 0, ## __VA_ARGS__) \ - -#define PROC_SUBSTRUCT_HAS_FIELD(name, index, param, def_value) \ - unsigned int name:1; -#define PROC_SUBSTRUCT_RESET_FIELD(name, index, def_value, param, dst) \ - dst->param.name = def_value; -#define PROC_SUBSTRUCT_RESET_HAS_FIELD(name, index, def_value, param, dst) \ - dst->has_##param.name = 0; -#define PROC_SUBSTRUCT_CLONE(name, index, def_value, param, dst, src) \ - dst->param.name = src->param.name; \ - dst->has_##param.name = src->has_##param.name; - -struct sampler_dahdsr_has_fields -{ - DAHDSR_FIELDS(PROC_SUBSTRUCT_HAS_FIELD, name) -}; - -struct sampler_lfo_has_fields -{ - LFO_FIELDS(PROC_SUBSTRUCT_HAS_FIELD, name) -}; - -struct sampler_eq_has_fields -{ - EQ_FIELDS(PROC_SUBSTRUCT_HAS_FIELD, name) -}; - -struct sampler_midi_curve -{ - float values[128]; - uint8_t has_values[128]; -}; - -#define PROC_FIELDS_TO_STRUCT(type, name, def_value) \ - type name; -#define PROC_FIELDS_TO_STRUCT_string(name) \ - gchar *name; \ - gboolean name##_changed; -#define PROC_FIELDS_TO_STRUCT_dBamp(type, name, def_value) \ - type name; \ - type name##_linearized; -#define PROC_FIELDS_TO_STRUCT_enum(enumtype, name, def_value) \ - enum enumtype name; -#define PROC_FIELDS_TO_STRUCT_dahdsr(name, parname, index) \ - struct cbox_dahdsr name; \ - struct cbox_envelope_shape name##_shape; -#define PROC_FIELDS_TO_STRUCT_lfo(name, parname, index) \ - struct sampler_lfo_params name; -#define PROC_FIELDS_TO_STRUCT_eq(name, parname, index) \ - struct sampler_eq_params name; -#define PROC_FIELDS_TO_STRUCT_ccrange(name, parname) \ - struct sampler_cc_range *name; -#define PROC_FIELDS_TO_STRUCT_midicurve(name) \ - struct sampler_midi_curve name; - -#define PROC_HAS_FIELD(type, name, def_value) \ - unsigned int has_##name:1; -#define PROC_HAS_FIELD_string(name) \ - unsigned int has_##name:1; -#define PROC_HAS_FIELD_dBamp(type, name, def_value) \ - PROC_HAS_FIELD(type, name, def_value) -#define PROC_HAS_FIELD_enum(enumtype, name, def_value) \ - PROC_HAS_FIELD(type, name, def_value) - -#define PROC_HAS_FIELD_dahdsr(name, parname, index) \ - struct sampler_dahdsr_has_fields has_##name; -#define PROC_HAS_FIELD_lfo(name, parname, index) \ - struct sampler_lfo_has_fields has_##name; -#define PROC_HAS_FIELD_eq(name, parname, index) \ - struct sampler_eq_has_fields has_##name; -#define PROC_HAS_FIELD_ccrange(name, parname) -#define PROC_HAS_FIELD_midicurve(name) - -CBOX_EXTERN_CLASS(sampler_layer) - -enum sampler_layer_mod_bitmasks -{ - slmb_ampeg_cc = 0x01, - slmb_fileg_cc = 0x02, - slmb_pitcheg_cc = 0x04, -}; - -struct sampler_layer_computed -{ - // computed values: - struct cbox_waveform *eff_waveform; - enum sampler_loop_mode eff_loop_mode; - uint32_t eff_loop_start, eff_loop_end; - float eff_freq; - gboolean eff_use_keyswitch:1; - gboolean eff_use_simple_trigger_logic:1; - gboolean eff_use_xfcc:1; - gboolean eff_use_prevoice:1; - gboolean eff_use_channel_mixer:1; - gboolean eff_use_filter_mods:1; - gboolean eff_is_silent:1; - int16_t scratch_loop[2 * MAX_INTERPOLATION_ORDER * 2]; - int16_t scratch_end[2 * MAX_INTERPOLATION_ORDER * 2]; - float resonance_scaled, resonance2_scaled; - float logcutoff, logcutoff2; - uint32_t eq_bitmask, mod_bitmask; - int eff_num_stages, eff_num_stages2; - - float eff_amp_velcurve[128]; -}; - -struct sampler_layer_data -{ - SAMPLER_FIXED_FIELDS(PROC_FIELDS_TO_STRUCT) - SAMPLER_FIXED_FIELDS(PROC_HAS_FIELD) - - struct sampler_modulation *modulations; - struct sampler_noteinitfunc *voice_nifs, *prevoice_nifs; - struct sampler_flex_lfo *flex_lfos; - - struct sampler_layer_computed computed; -}; - -struct sampler_layer -{ - CBOX_OBJECT_HEADER() - struct cbox_command_target cmd_target; - - struct sampler_layer_data data, *runtime; - - struct sampler_module *module; - struct sampler_program *parent_program; - struct sampler_layer *parent, *default_child; - - int current_seq_position; - - GHashTable *unknown_keys; - GHashTable *child_layers; -}; - -extern struct sampler_layer *sampler_layer_new(struct sampler_module *m, struct sampler_program *parent_program, struct sampler_layer *parent_group); -extern struct sampler_layer *sampler_layer_new_from_section(struct sampler_module *m, struct sampler_program *parent_program, struct sampler_layer *parent_group, const char *cfg_section); -extern struct sampler_layer *sampler_layer_new_clone(struct sampler_layer *layer, struct sampler_module *m, struct sampler_program *parent_program, struct sampler_layer *parent_group); -extern void sampler_layer_set_modulation(struct sampler_layer *l, enum sampler_modsrc src, enum sampler_modsrc src2, enum sampler_moddest dest, float amount, int flags); -extern void sampler_layer_set_modulation1(struct sampler_layer *l, enum sampler_modsrc src, enum sampler_moddest dest, float amount, int flags); -extern void sampler_layer_add_nif(struct sampler_layer *l, SamplerNoteInitFunc notefunc_voice, SamplerNoteInitFunc2 notefunc_prevoice, int variant, float param); -extern void sampler_layer_load_overrides(struct sampler_layer *l, const char *cfg_section); -extern void sampler_layer_data_finalize(struct sampler_layer_data *l, struct sampler_layer_data *parent, struct sampler_program *p); -extern void sampler_layer_reset_switches(struct sampler_layer *l, struct sampler_module *m); -extern gboolean sampler_layer_apply_param(struct sampler_layer *l, const char *key, const char *value, GError **error); -extern gboolean sampler_layer_unapply_param(struct sampler_layer *l, const char *key, GError **error); -extern gchar *sampler_layer_to_string(struct sampler_layer *l, gboolean show_inherited); -extern void sampler_layer_dump(struct sampler_layer *l, FILE *f); -extern void sampler_layer_update(struct sampler_layer *l); - -extern void sampler_layer_data_clone(struct sampler_layer_data *dst, const struct sampler_layer_data *src, gboolean copy_hasattr); -extern void sampler_layer_data_close(struct sampler_layer_data *l); -extern void sampler_layer_data_destroy(struct sampler_layer_data *l); - -extern void sampler_nif_vel2pitch(struct sampler_noteinitfunc *nif, struct sampler_voice *v); -extern void sampler_nif_vel2offset(struct sampler_noteinitfunc *nif, struct sampler_voice *v); -extern void sampler_nif_vel2reloffset(struct sampler_noteinitfunc *nif, struct sampler_voice *v); -extern void sampler_nif_vel2env(struct sampler_noteinitfunc *nif, struct sampler_voice *v); -extern void sampler_nif_cc2offset(struct sampler_noteinitfunc *nif, struct sampler_voice *v); -extern void sampler_nif_cc2reloffset(struct sampler_noteinitfunc *nif, struct sampler_voice *v); -extern void sampler_nif_addrandom(struct sampler_noteinitfunc *nif, struct sampler_voice *v); - -extern void sampler_nif_cc2delay(struct sampler_noteinitfunc *nif, struct sampler_prevoice *pv); -extern void sampler_nif_addrandomdelay(struct sampler_noteinitfunc *nif, struct sampler_prevoice *pv); -extern void sampler_nif_syncbeats(struct sampler_noteinitfunc *nif, struct sampler_prevoice *pv); - -extern void sampler_midi_curve_init(struct sampler_midi_curve *curve); -extern void sampler_midi_curve_interpolate(const struct sampler_midi_curve *curve, float dest[128], float def_start, float def_end, gboolean is_quadratic); - -#endif diff --git a/template/calfbox/sampler_nif.c b/template/calfbox/sampler_nif.c deleted file mode 100644 index b8c06c2..0000000 --- a/template/calfbox/sampler_nif.c +++ /dev/null @@ -1,115 +0,0 @@ -#include "config-api.h" -#include "dspmath.h" -#include "errors.h" -#include "midi.h" -#include "module.h" -#include "rt.h" -#include "sampler.h" -#include "sampler_impl.h" - -////////////////////////////////////////////////////////////////////////// -// Note initialisation functions - -void sampler_nif_cc2delay(struct sampler_noteinitfunc *nif, struct sampler_prevoice *pv) -{ - float cc = sampler_channel_getcc_prevoice(pv->channel, pv, nif->key.variant, nif->value.curve_id, nif->value.step); - pv->delay_computed += nif->value.value * cc; -} - -void sampler_nif_addrandomdelay(struct sampler_noteinitfunc *nif, struct sampler_prevoice *pv) -{ - pv->delay_computed += nif->value.value * rand() * (1.0 / RAND_MAX); -} - -void sampler_nif_syncbeats(struct sampler_noteinitfunc *nif, struct sampler_prevoice *pv) -{ - if (nif->value.value > 0) - { - pv->sync_beats = nif->value.value; - double cur_beat = sampler_get_current_beat(pv->channel->module); - pv->sync_initial_time = cur_beat; - double cur_rel_beat = fmod(cur_beat, pv->sync_beats); - double bar_start = cur_beat - cur_rel_beat; - if (pv->layer_data->sync_offset <= cur_rel_beat) // trigger in next bar - pv->sync_trigger_time = bar_start + pv->sync_beats + pv->layer_data->sync_offset; - else // trigger in the same bar - pv->sync_trigger_time = bar_start + pv->layer_data->sync_offset; - // printf("cur_beat %f trigger %f offset %f\n", cur_beat, pv->sync_trigger_time, pv->layer_data->sync_offset); - } -} - -void sampler_nif_vel2pitch(struct sampler_noteinitfunc *nif, struct sampler_voice *v) -{ - v->pitch_shift += nif->value.value * v->vel * (1.0 / 127.0); -} - -void sampler_nif_vel2offset(struct sampler_noteinitfunc *nif, struct sampler_voice *v) -{ - v->offset += nif->value.value * v->vel * (1.0 / 127.0); -} - -void sampler_nif_cc2offset(struct sampler_noteinitfunc *nif, struct sampler_voice *v) -{ - v->offset += nif->value.value * sampler_channel_getcc_mod(v->channel, v, nif->key.variant, nif->value.curve_id, nif->value.step); -} - -void sampler_nif_vel2reloffset(struct sampler_noteinitfunc *nif, struct sampler_voice *v) -{ - v->reloffset += nif->value.value * v->vel * (1.0 / 127.0); -} - -void sampler_nif_cc2reloffset(struct sampler_noteinitfunc *nif, struct sampler_voice *v) -{ - v->reloffset += nif->value.value * sampler_channel_getcc_mod(v->channel, v, nif->key.variant, nif->value.curve_id, nif->value.step); -} - -void sampler_nif_addrandom(struct sampler_noteinitfunc *nif, struct sampler_voice *v) -{ - float rnd = rand() * 1.0 / RAND_MAX; - switch(nif->key.variant) - { - case 0: - v->gain_shift += rnd * nif->value.value; - break; - case 1: - v->cutoff_shift += rnd * nif->value.value; - break; - case 2: - v->pitch_shift += rnd * nif->value.value; // this is in cents - break; - } -} - -static void modify_env_stage_by_nif(struct sampler_noteinitfunc *nif, struct sampler_voice *v, uint32_t variant, float value) -{ - int env_type = variant >> 4; - struct cbox_envelope *env = NULL; - switch(env_type) - { - case 0: - env = &v->amp_env; - break; - case 1: - env = &v->filter_env; - break; - case 2: - env = &v->pitch_env; - break; - default: - assert(0); - } - if (env->shape != &v->vel_envs[env_type]) - { - memcpy(&v->vel_envs[env_type], env->shape, sizeof(struct cbox_envelope_shape)); - env->shape = &v->vel_envs[env_type]; - } - float param = nif->value.value * value; - if ((variant & 15) == snif_env_sustain || (variant & 15) == snif_env_start) - param *= 0.01; - cbox_envelope_modify_dahdsr(env->shape, variant & 15, param, v->channel->module->module.srate * (1.0 / CBOX_BLOCK_SIZE)); -} - -void sampler_nif_vel2env(struct sampler_noteinitfunc *nif, struct sampler_voice *v) -{ - modify_env_stage_by_nif(nif, v, nif->key.variant, v->vel * (1.0 / 127.0)); -} diff --git a/template/calfbox/sampler_prevoice.c b/template/calfbox/sampler_prevoice.c deleted file mode 100644 index 7089550..0000000 --- a/template/calfbox/sampler_prevoice.c +++ /dev/null @@ -1,81 +0,0 @@ -#include "config.h" -#include "config-api.h" -#include "dspmath.h" -#include "errors.h" -#include "midi.h" -#include "module.h" -#include "rt.h" -#include "sampler.h" -#include "sampler_impl.h" - -void sampler_prevoice_start(struct sampler_prevoice *pv, struct sampler_channel *channel, struct sampler_layer_data *l, int note, int vel) -{ - pv->channel = channel; - pv->layer_data = l; - pv->note = note; - pv->vel = vel; - pv->age = 0; - pv->delay_computed = 0.f; - pv->sync_beats = -1; - pv->sync_initial_time = -1; - pv->sync_trigger_time = -1; - - for(struct sampler_noteinitfunc *nif = pv->layer_data->prevoice_nifs; nif; nif = nif->next) - nif->key.notefunc_prevoice(nif, pv); - sampler_prevoice_unlink(&channel->module->prevoices_free, pv); - sampler_prevoice_link(&channel->module->prevoices_running, pv); -} - -void sampler_prevoice_link(struct sampler_prevoice **pv, struct sampler_prevoice *v) -{ - v->prev = NULL; - v->next = *pv; - if (*pv) - (*pv)->prev = v; - *pv = v; -} - -void sampler_prevoice_unlink(struct sampler_prevoice **pv, struct sampler_prevoice *v) -{ - if (*pv == v) - *pv = v->next; - if (v->prev) - v->prev->next = v->next; - if (v->next) - v->next->prev = v->prev; - v->prev = NULL; - v->next = NULL; -} - -int sampler_prevoice_process(struct sampler_prevoice *pv, struct sampler_module *m) -{ - struct sampler_layer_data *layer_data = pv->layer_data; - if (pv->sync_beats != -1) - { - double cur_beat = sampler_get_current_beat(m); - - if (cur_beat < pv->sync_initial_time - 0.001 || cur_beat >= pv->sync_trigger_time + 1) - { - gboolean backward_jump = cur_beat < pv->sync_initial_time; - // 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); - // Recalculate after seek/looping etc - pv->sync_initial_time = cur_beat; - double cur_rel_beat = fmod(cur_beat, pv->sync_beats); - double bar_start = cur_beat - cur_rel_beat; - if (pv->layer_data->sync_offset <= cur_rel_beat && !backward_jump) // trigger in next bar - pv->sync_trigger_time = bar_start + pv->sync_beats + pv->layer_data->sync_offset; - else // trigger in the same bar - pv->sync_trigger_time = bar_start + pv->layer_data->sync_offset; - } - if (cur_beat < pv->sync_trigger_time) - return 0; - // Let the other logic (note delay etc.) take over - pv->sync_beats = -1; - } - pv->age += CBOX_BLOCK_SIZE; - if (pv->age >= (layer_data->delay + pv->delay_computed) * m->module.srate) - return 1; - - return 0; -} - diff --git a/template/calfbox/sampler_prg.c b/template/calfbox/sampler_prg.c deleted file mode 100644 index abe22e4..0000000 --- a/template/calfbox/sampler_prg.c +++ /dev/null @@ -1,640 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "app.h" -#include "blob.h" -#include "engine.h" -#include "instr.h" -#include "rt.h" -#include "sampler.h" -#include "sampler_prg.h" -#include "sfzloader.h" -#include "tarfile.h" - -#include - -CBOX_CLASS_DEFINITION_ROOT(sampler_program) - -// GSList *sampler_channel_get_next_layer(struct sampler_channel *c, GSList *next_layer, int note, int vel, float random, gboolean is_first, gboolean is_release) - -struct sampler_layer *sampler_rll_iterator_next(struct sampler_rll_iterator *iter) -{ -retry: - while(iter->next_layer) - { - struct sampler_layer *lr = iter->next_layer->data; - struct sampler_layer_data *l = lr->runtime; - iter->next_layer = g_slist_next(iter->next_layer); - if (!l->computed.eff_waveform) - continue; - - if (l->computed.eff_use_simple_trigger_logic) - { - if (iter->note >= l->lokey && iter->note <= l->hikey && - iter->vel >= l->lovel && iter->vel <= l->hivel) - return lr; - else - continue; - } - - if ((l->trigger == stm_first && !iter->is_first) || - (l->trigger == stm_legato && iter->is_first) || - (l->trigger == stm_release && !iter->is_release)) // sw_last keyswitches are still added to the note-on list in RLL - continue; - struct sampler_channel *c = iter->channel; - struct sampler_module *m = c->module; - if (iter->note >= l->lokey && iter->note <= l->hikey && - iter->vel >= l->lovel && iter->vel <= l->hivel && - c >= &m->channels[l->lochan - 1] && c <= &m->channels[l->hichan - 1] && - iter->random >= l->lorand && iter->random < l->hirand && - c->pitchwheel >= l->lobend && c->pitchwheel < l->hibend && - c->last_chanaft >= l->lochanaft && c->last_chanaft <= l->hichanaft && - c->last_polyaft >= l->lopolyaft && c->last_polyaft <= l->hipolyaft && - c->module->module.engine->master->tempo >= l->lobpm && c->module->module.engine->master->tempo < l->hibpm && - sampler_cc_range_is_in(l->cc, c)) - { - if (!l->computed.eff_use_keyswitch || - ((l->sw_down == -1 || (c->switchmask[l->sw_down >> 5] & (1 << (l->sw_down & 31)))) && - (l->sw_up == -1 || !(c->switchmask[l->sw_up >> 5] & (1 << (l->sw_up & 31)))) && - (l->sw_previous == -1 || l->sw_previous == c->previous_note))) - { - gboolean play = lr->current_seq_position == 1; - lr->current_seq_position--; - if (lr->current_seq_position > l->seq_length) - lr->current_seq_position = 1; - else if (lr->current_seq_position < 1) - lr->current_seq_position = l->seq_length; - if (play) - return lr; - } - } - } - while(iter->next_keyswitch_index < iter->rll->keyswitch_group_count && - iter->next_keyswitch_index < MAX_KEYSWITCH_GROUPS) - { - struct sampler_rll *rll = iter->rll; - uint32_t ks_group = iter->next_keyswitch_index++; - - uint8_t ks_state = iter->channel->keyswitch_state[ks_group]; - if (ks_state == 255) // nothing defined for this switch state - continue; - uint8_t key_range = rll->ranges_by_key[iter->note]; - if (key_range != 255) - { - GSList **layers_by_range = iter->is_release ? rll->release_layers_by_range : rll->layers_by_range; - layers_by_range += (rll->keyswitch_groups[ks_group]->group_offset + ks_state) * rll->layers_by_range_count; - iter->next_layer = layers_by_range[key_range]; - if (iter->next_layer) - goto retry; - } - } - return NULL; -} - -void sampler_rll_iterator_init(struct sampler_rll_iterator *iter, struct sampler_rll *rll, struct sampler_channel *c, int note, int vel, float random, gboolean is_first, gboolean is_release) -{ - iter->channel = c; - iter->note = note; - iter->vel = vel; - iter->random = random; - iter->is_first = is_first; - iter->is_release = is_release; - iter->rll = rll; - iter->next_keyswitch_index = 0; - - if (note >= rll->lokey && note <= rll->hikey) - { - assert(note >= 0 && note <= 127); - GSList **layers_by_range = is_release ? rll->release_layers_by_range : rll->layers_by_range; - if (layers_by_range) - { - uint8_t key_range = rll->ranges_by_key[note]; - if (key_range != 255) - iter->next_layer = layers_by_range[key_range]; - } - else - iter->next_layer = NULL; - } - else - iter->next_layer = NULL; -} - -static gboolean return_layers(GSList *layers, const char *keyword, struct cbox_command_target *fb, GError **error) -{ - for (GSList *p = layers; p; p = g_slist_next(p)) - { - if (!cbox_execute_on(fb, NULL, keyword, "o", error, p->data)) - return FALSE; - } - return TRUE; -} - -static gboolean sampler_program_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct sampler_program *program = ct->user_data; - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - if (!((!program->name || cbox_execute_on(fb, NULL, "/name", "s", error, program->name)) && - cbox_execute_on(fb, NULL, "/sample_dir", "s", error, program->sample_dir) && - cbox_execute_on(fb, NULL, "/source_file", "s", error, program->source_file) && - cbox_execute_on(fb, NULL, "/program_no", "i", error, program->prog_no) && - cbox_execute_on(fb, NULL, "/in_use", "i", error, program->in_use) && - CBOX_OBJECT_DEFAULT_STATUS(program, fb, error))) - return FALSE; - return TRUE; - } - if (!strcmp(cmd->command, "/regions") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - return return_layers(program->all_layers, "/region", fb, error); - } - if (!strcmp(cmd->command, "/global") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - return cbox_execute_on(fb, NULL, "/uuid", "o", error, program->global); - } - if (!strcmp(cmd->command, "/control_inits") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - for (GSList *p = program->ctrl_init_list; p; p = p->next) - { - const struct sampler_ctrlinit *cin = (const struct sampler_ctrlinit *)&p->data; - if (!cbox_execute_on(fb, NULL, "/control_init", "ii", error, (int)cin->controller, (int)cin->value)) - return FALSE; - } - return TRUE; - } - if (!strcmp(cmd->command, "/control_labels") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - for (GSList *p = program->ctrl_label_list; p; p = p->next) - { - const struct sampler_ctrllabel *cin = (const struct sampler_ctrllabel *)p->data; - if (!cbox_execute_on(fb, NULL, "/control_label", "is", error, (int)cin->controller, cin->label)) - return FALSE; - } - return TRUE; - } - if (!strcmp(cmd->command, "/key_labels") && !strcmp(cmd->arg_types, "")) - //Because "key" is a programming keyword as well we use "pitch" instead internally - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - for (GSList *p = program->pitch_label_list; p; p = p->next) - { - const struct sampler_pitchlabel *cin = (const struct sampler_pitchlabel *)p->data; - if (!cbox_execute_on(fb, NULL, "/key_label", "is", error, (int)cin->pitch, cin->label)) - return FALSE; - } - return TRUE; - } - if (!strcmp(cmd->command, "/add_control_init") && !strcmp(cmd->arg_types, "ii")) - { - sampler_program_add_controller_init(program, CBOX_ARG_I(cmd, 0), CBOX_ARG_I(cmd, 1)); - return TRUE; - } - if (!strcmp(cmd->command, "/delete_control_init") && !strcmp(cmd->arg_types, "ii")) - { - sampler_program_remove_controller_init(program, CBOX_ARG_I(cmd, 0), CBOX_ARG_I(cmd, 1)); - return TRUE; - } - if (!strcmp(cmd->command, "/new_group") && !strcmp(cmd->arg_types, "")) - { - struct sampler_layer *l = sampler_layer_new(program->module, program, program->global->default_child); - return cbox_execute_on(fb, NULL, "/uuid", "o", error, l); - } - if (!strcmp(cmd->command, "/clone_to") && !strcmp(cmd->arg_types, "si")) - { - struct cbox_instrument *instrument = (struct cbox_instrument *)CBOX_ARG_O(cmd, 0, program, cbox_instrument, error); - if (!instrument) - return FALSE; - struct cbox_module *module = instrument->module; - if (strcmp(module->engine_name, "sampler")) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot copy sampler program to module '%s' of type '%s'", module->instance_name, module->engine_name); - return FALSE; - } - struct sampler_program *prg = sampler_program_clone(program, (struct sampler_module *)module, CBOX_ARG_I(cmd, 1), error); - if (!prg) - return FALSE; - sampler_register_program((struct sampler_module *)module, prg); - return cbox_execute_on(fb, NULL, "/uuid", "o", error, prg); - } - if (!strcmp(cmd->command, "/load_file") && !strcmp(cmd->arg_types, "si")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - struct cbox_blob *blob = cbox_blob_new_from_file(program->name, program->tarfile, program->sample_dir, CBOX_ARG_S(cmd, 0), CBOX_ARG_I(cmd, 1), error); - if (!blob) - return FALSE; - return cbox_execute_on(fb, NULL, "/data", "b", error, blob); - } - if (!strcmp(cmd->command, "/keyswitch_groups") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - for (uint32_t i = 0; i < program->rll->keyswitch_group_count; ++i) - { - struct sampler_keyswitch_group *ksg = program->rll->keyswitch_groups[i]; - if (!cbox_execute_on(fb, NULL, "/key_range", "ii", error, ksg->lo, ksg->hi)) - return FALSE; - } - return TRUE; - } - return cbox_object_default_process_cmd(ct, fb, cmd, error); -} - -struct sampler_program *sampler_program_new(struct sampler_module *m, int prog_no, const char *name, struct cbox_tarfile *tarfile, const char *sample_dir, GError **error) -{ - gchar *perm_sample_dir = g_strdup(sample_dir); - if (sample_dir && !perm_sample_dir) - return NULL; - - struct cbox_document *doc = CBOX_GET_DOCUMENT(&m->module); - struct sampler_program *prg = malloc(sizeof(struct sampler_program)); - if (!prg) - { - g_free(perm_sample_dir); - return NULL; - } - memset(prg, 0, sizeof(*prg)); - CBOX_OBJECT_HEADER_INIT(prg, sampler_program, doc); - cbox_command_target_init(&prg->cmd_target, sampler_program_process_cmd, prg); - - prg->module = m; - prg->prog_no = prog_no; - prg->name = g_strdup(name); - prg->tarfile = tarfile; - prg->source_file = NULL; - prg->sample_dir = perm_sample_dir; - prg->all_layers = NULL; - prg->rll = NULL; - prg->ctrl_init_list = NULL; - prg->ctrl_label_list = NULL; - prg->pitch_label_list = NULL; - prg->global = sampler_layer_new(m, prg, NULL); - prg->global->default_child = sampler_layer_new(m, prg, prg->global); - prg->global->default_child->default_child = sampler_layer_new(m, prg, prg->global->default_child); - prg->deleting = FALSE; - prg->in_use = 0; - for (int i = 0; i < MAX_MIDI_CURVES; ++i) - { - prg->curves[i] = NULL; - prg->interpolated_curves[i] = NULL; - } - CBOX_OBJECT_REGISTER(prg); - return prg; -} - -struct sampler_program *sampler_program_new_from_cfg(struct sampler_module *m, const char *cfg_section, const char *name, int pgm_id, GError **error) -{ - int i; - - char *name2 = NULL, *sfz_path = NULL, *spath = NULL, *tar_name = NULL; - const char *sfz = NULL; - struct cbox_tarfile *tarfile = NULL; - - g_clear_error(error); - tar_name = cbox_config_get_string(cfg_section, "tar"); - if (!strncmp(cfg_section, "spgm:!", 6)) - { - sfz = cfg_section + 6; - if (!strncmp(sfz, "sbtar:", 6)) - { - gchar *p = strchr(sfz + 6, ';'); - if (p) - { - char *tmp = g_strndup(sfz + 6, p - sfz - 6); - tarfile = cbox_tarpool_get_tarfile(app.tarpool, tmp, error); - g_free(tmp); - if (!tarfile) - return NULL; - sfz = strrchr(p + 1, '/'); - if (!sfz) - sfz = p + 1; - name2 = p + 1; - sfz_path = g_path_get_dirname(p + 1); - } - else - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot load sampler program '%s' from section '%s': missing name of a file inside a tar archive", name, cfg_section); - return NULL; - } - } - else - { - name2 = strrchr(name, '/'); - if (name2) - name2++; - } - } - else - { - if (tar_name) - { - tarfile = cbox_tarpool_get_tarfile(app.tarpool, tar_name, error); - if (!tarfile) - return NULL; - } - if (!sfz && !cbox_config_has_section(cfg_section)) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot load sampler program '%s' from section '%s': section not found", name, cfg_section); - return FALSE; - } - name2 = cbox_config_get_string(cfg_section, "name"); - - sfz_path = cbox_config_get_string(cfg_section, "sfz_path"); - spath = cbox_config_get_string(cfg_section, "sample_path"); - sfz = cbox_config_get_string(cfg_section, "sfz"); - if (tarfile && !sfz_path) - sfz_path = "."; - } - - if (sfz && !sfz_path && !spath && !tarfile) - { - char *lastslash = strrchr(sfz, '/'); - if (lastslash && !sfz_path && !spath) - { - char *tmp = g_strndup(sfz, lastslash - sfz); - sfz_path = cbox_config_permify(tmp); - g_free(tmp); - sfz = lastslash + 1; - } - } - - struct sampler_program *prg = sampler_program_new( - m, - pgm_id != -1 ? pgm_id : cbox_config_get_int(cfg_section, "program", 0), - name2 ? name2 : name, - tarfile, - spath ? spath : (sfz_path ? sfz_path : ""), - error - ); - if (!prg) - return NULL; - - if (sfz) - { - if (sfz_path) - prg->source_file = g_build_filename(sfz_path, sfz, NULL); - else - { - prg->source_file = g_strdup(sfz); - } - - if (sampler_module_load_program_sfz(m, prg, prg->source_file, FALSE, error)) - return prg; - CBOX_DELETE(prg); - return NULL; - } else { - prg->source_file = g_strdup_printf("config:%s", cfg_section); - } - - for (i = 0; ; i++) - { - gchar *s = g_strdup_printf("group%d", 1 + i); - const char *group_section = cbox_config_get_string(cfg_section, s); - g_free(s); - if (!group_section) - break; - - gchar *swhere = g_strdup_printf("sgroup:%s", group_section); - struct sampler_layer *g = sampler_layer_new_from_section(m, prg, prg->global->default_child, swhere); - if (!g) - g_warning("Sample layer '%s' cannot be created - skipping", group_section); - else - { - // XXXKF - // sampler_program_add_group(prg, g); - for (int j = 0; ; j++) - { - char *where = NULL; - gchar *s = g_strdup_printf("layer%d", 1 + j); - const char *layer_section = cbox_config_get_string(swhere, s); - g_free(s); - if (!layer_section) - break; - where = g_strdup_printf("slayer:%s", layer_section); - struct sampler_layer *l = sampler_layer_new_from_section(m, prg, g, where); - if (!l) - g_warning("Sample layer '%s' cannot be created - skipping", layer_section); - else - { - sampler_layer_update(l); - if (!l->data.computed.eff_waveform) - { - g_warning("Sample layer '%s' does not have a waveform - skipping", layer_section); - CBOX_DELETE((struct sampler_layer *)l); - } - else - sampler_program_add_layer(prg, l); - } - g_free(where); - } - } - g_free(swhere); - } - for (i = 0; ; i++) - { - char *where = NULL; - gchar *s = g_strdup_printf("layer%d", 1 + i); - const char *layer_section = cbox_config_get_string(cfg_section, s); - g_free(s); - if (!layer_section) - break; - where = g_strdup_printf("slayer:%s", layer_section); - - struct sampler_layer *l = sampler_layer_new_from_section(m, prg, NULL, where); - if (!l) - g_warning("Sample layer '%s' cannot be created - skipping", layer_section); - else - { - sampler_layer_update(l); - if (!l->data.computed.eff_waveform) - { - g_warning("Sample layer '%s' does not have a waveform - skipping", layer_section); - CBOX_DELETE((struct sampler_layer *)l); - } - else - sampler_program_add_layer(prg, l); - } - g_free(where); - } - prg->all_layers = g_slist_reverse(prg->all_layers); - sampler_program_update_layers(prg); - return prg; -} - -void sampler_program_add_layer(struct sampler_program *prg, struct sampler_layer *l) -{ - // Always call sampler_update_layer before sampler_program_add_layer. - assert(l->runtime); - assert(l->parent); - assert(l->parent->parent); - assert(l->parent->parent->parent); - assert(l->parent->parent->parent == prg->global); - prg->all_layers = g_slist_prepend(prg->all_layers, l); -} - -void sampler_program_delete_layer(struct sampler_program *prg, struct sampler_layer *l) -{ - prg->all_layers = g_slist_remove(prg->all_layers, l); -} - - -void sampler_program_add_controller_init(struct sampler_program *prg, uint16_t controller, uint8_t value) -{ - union sampler_ctrlinit_union u; - u.ptr = NULL; - u.cinit.controller = controller; - u.cinit.value = value; - prg->ctrl_init_list = g_slist_append(prg->ctrl_init_list, u.ptr); -} - -static void sampler_ctrl_label_destroy(gpointer value) -{ - struct sampler_ctrllabel *label = value; - free(label->label); - free(label); -} - -void sampler_program_add_pitch_label(struct sampler_program *prg, uint16_t pitch, gchar *text) -{ - struct sampler_pitchlabel *label = calloc(1, sizeof(struct sampler_pitchlabel)); - label->pitch = pitch; - label->label = text; - prg->pitch_label_list = g_slist_append(prg->pitch_label_list, label); -} - -static void sampler_pitch_label_destroy(gpointer value) -{ - struct sampler_pitchlabel *label = value; - free(label->label); - free(label); -} - -void sampler_program_add_controller_label(struct sampler_program *prg, uint16_t controller, gchar *text) -{ - struct sampler_ctrllabel *label = calloc(1, sizeof(struct sampler_ctrllabel)); - label->controller = controller; - label->label = text; - prg->ctrl_label_list = g_slist_append(prg->ctrl_label_list, label); -} - -void sampler_program_remove_controller_init(struct sampler_program *prg, uint16_t controller, int which) -{ - for (GSList **p = &prg->ctrl_init_list; *p; ) - { - const struct sampler_ctrlinit *cin = (const struct sampler_ctrlinit *)&(*p)->data; - if (cin->controller != controller) - { - p = &((*p)->next); - continue; - } - if (which > 0) - which--; - GSList *q = (GSList *)cbox_rt_swap_pointers(prg->module->module.rt, (void **)p, (*p)->next); - g_slist_free1(q); - if (which == 0) - break; - } -} - -static void delete_layers_recursively(struct sampler_layer *layer) -{ - GHashTableIter iter; - g_hash_table_iter_init(&iter, layer->child_layers); - GSList *dellist = NULL; - gpointer key, value; - while(g_hash_table_iter_next(&iter, &key, &value)) - dellist = g_slist_prepend(dellist, key); - GSList *liter = dellist; - while(liter) - { - struct sampler_layer *chl = liter->data; - delete_layers_recursively(chl); - liter = liter->next; - } - g_slist_free(dellist); - CBOX_DELETE(layer); -} - -void sampler_program_destroyfunc(struct cbox_objhdr *hdr_ptr) -{ - struct sampler_program *prg = CBOX_H2O(hdr_ptr); - sampler_unselect_program(prg->module, prg); - if (prg->rll) - { - sampler_rll_destroy(prg->rll); - prg->rll = NULL; - } - for (GSList *p = prg->all_layers; p; p = g_slist_next(p)) - CBOX_DELETE((struct sampler_layer *)p->data); - delete_layers_recursively(prg->global); - - for (int i = 0; i < MAX_MIDI_CURVES; ++i) - { - g_free(prg->curves[i]); - g_free(prg->interpolated_curves[i]); - } - g_free(prg->name); - g_free(prg->sample_dir); - g_free(prg->source_file); - g_slist_free(prg->all_layers); - g_slist_free(prg->ctrl_init_list); - g_slist_free_full(prg->ctrl_label_list, sampler_ctrl_label_destroy); - g_slist_free_full(prg->pitch_label_list, sampler_pitch_label_destroy); - if (prg->tarfile) - cbox_tarpool_release_tarfile(app.tarpool, prg->tarfile); - free(prg); -} - -void sampler_program_update_layers(struct sampler_program *prg) -{ - struct sampler_module *m = prg->module; - struct sampler_rll *new_rll = sampler_rll_new_from_program(prg); - struct sampler_rll *old_rll = cbox_rt_swap_pointers(m->module.rt, (void **)&prg->rll, new_rll); - if (old_rll) - sampler_rll_destroy(old_rll); -} - -struct sampler_program *sampler_program_clone(struct sampler_program *prg, struct sampler_module *m, int prog_no, GError **error) -{ - struct sampler_program *newprg = sampler_program_new(m, prog_no, prg->name, prg->tarfile, prg->sample_dir, error); - if (!newprg) - return NULL; - if (prg->source_file) - newprg->source_file = g_strdup(prg->source_file); - // The values are stored as a union aliased with the data pointer, so no need to deep-copy - newprg->ctrl_init_list = g_slist_copy(prg->ctrl_init_list); - // XXXKF ctrl_label_list - newprg->rll = NULL; - newprg->global = sampler_layer_new_clone(prg->global, m, newprg, NULL); - sampler_program_update_layers(newprg); - if (newprg->tarfile) - newprg->tarfile->refs++; - - return newprg; -} diff --git a/template/calfbox/sampler_prg.h b/template/calfbox/sampler_prg.h deleted file mode 100644 index 630e447..0000000 --- a/template/calfbox/sampler_prg.h +++ /dev/null @@ -1,127 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_SAMPLER_PRG_H -#define CBOX_SAMPLER_PRG_H - -#include "cmd.h" -#include "dom.h" - -struct cbox_tarfile; -struct sampler_channel; - -CBOX_EXTERN_CLASS(sampler_program) - -#define MAX_MIDI_CURVES 32 - -struct sampler_keyswitch_group -{ - uint8_t lo, hi, num_used, def_value; - uint32_t group_offset; - uint8_t key_offsets[]; -}; - -// Runtime layer lists; in future, I might something more clever, like a tree -struct sampler_rll -{ - GSList *layers_oncc; - uint32_t cc_trigger_bitmask[4]; // one bit per CC - uint8_t lokey, hikey; - uint8_t ranges_by_key[128]; - uint32_t layers_by_range_count; - GSList **layers_by_range, **release_layers_by_range; - struct sampler_keyswitch_group **keyswitch_groups; - uint32_t keyswitch_group_count; - uint32_t keyswitch_key_count; - gboolean has_release_layers; -}; - -struct sampler_rll_iterator -{ - struct sampler_channel *channel; - int note, vel; - float random; - gboolean is_first, is_release; - GSList *next_layer; - struct sampler_rll *rll; - uint32_t next_keyswitch_index; -}; - -struct sampler_ctrlinit -{ - uint16_t controller; - uint8_t value; -}; - -union sampler_ctrlinit_union { - gpointer ptr; - struct sampler_ctrlinit cinit; -}; - -struct sampler_ctrllabel { - uint16_t controller; - gchar *label; -}; - -struct sampler_pitchlabel { - uint16_t pitch; - gchar *label; -}; - -struct sampler_program -{ - CBOX_OBJECT_HEADER() - struct cbox_command_target cmd_target; - - struct sampler_module *module; - gchar *name; - int prog_no; - struct sampler_layer *global; - GSList *all_layers; - GSList *ctrl_init_list; - GSList *ctrl_label_list; - GSList *pitch_label_list; - struct sampler_rll *rll; - gchar *sample_dir; // can be empty, cannot be NULL - gchar *source_file; // can be empty, cannot be NULL - int in_use; - struct cbox_tarfile *tarfile; - gboolean deleting; - struct sampler_midi_curve *curves[MAX_MIDI_CURVES]; - float *interpolated_curves[MAX_MIDI_CURVES]; -}; - -extern struct sampler_rll *sampler_rll_new_from_program(struct sampler_program *prg); -extern void sampler_rll_destroy(struct sampler_rll *rll); - -extern void sampler_rll_iterator_init(struct sampler_rll_iterator *iter, struct sampler_rll *rll, struct sampler_channel *c, int note, int vel, float random, gboolean is_first, gboolean is_release); -extern struct sampler_layer *sampler_rll_iterator_next(struct sampler_rll_iterator *iter); - -extern struct sampler_program *sampler_program_new(struct sampler_module *m, int prog_no, const char *name, struct cbox_tarfile *tarfile, const char *sample_dir, GError **error); -extern struct sampler_program *sampler_program_new_from_cfg(struct sampler_module *m, const char *cfg_section, const char *name, int pgm_id, GError **error); -extern void sampler_program_add_layer(struct sampler_program *prg, struct sampler_layer *l); -extern void sampler_program_delete_layer(struct sampler_program *prg, struct sampler_layer *l); -extern void sampler_program_add_group(struct sampler_program *prg, struct sampler_layer *l); -extern void sampler_program_add_controller_init(struct sampler_program *prg, uint16_t controller, uint8_t value); -extern void sampler_program_add_controller_label(struct sampler_program *prg, uint16_t controller, gchar *label); // keeps ownership -extern void sampler_program_add_pitch_label(struct sampler_program *prg, uint16_t pitch, gchar *label); // keeps ownership -extern void sampler_program_remove_controller_init(struct sampler_program *prg, uint16_t controller, int which); -extern void sampler_program_update_layers(struct sampler_program *prg); -extern struct sampler_program *sampler_program_clone(struct sampler_program *prg, struct sampler_module *m, int prog_no, GError **error); - -#endif diff --git a/template/calfbox/sampler_rll.c b/template/calfbox/sampler_rll.c deleted file mode 100644 index cafc7d2..0000000 --- a/template/calfbox/sampler_rll.c +++ /dev/null @@ -1,173 +0,0 @@ -#include "app.h" -#include "blob.h" -#include "instr.h" -#include "rt.h" -#include "sampler.h" -#include "sampler_prg.h" - -///////////////////////////////////////////////////////////////////////////////// - -static void add_layers(struct sampler_rll *rll, GSList **layers_by_range, struct sampler_layer *l, uint32_t lokey, uint32_t hikey) -{ - if (lokey >= 0 && lokey <= 127 && - hikey >= 0 && hikey <= 127 && lokey <= hikey) - { - int start = rll->ranges_by_key[lokey]; - int end = rll->ranges_by_key[hikey]; - assert(start != 255 && end != 255); - for (int i = start; i <= end; ++i) - { - if (!layers_by_range[i] || layers_by_range[i]->data != l) - layers_by_range[i] = g_slist_prepend(layers_by_range[i], l); - } - } -} - -struct sampler_rll *sampler_rll_new_from_program(struct sampler_program *prg) -{ - struct sampler_rll *rll = g_new(struct sampler_rll, 1); - rll->layers_oncc = NULL; - for (int i = 0; i < 4; i++) - rll->cc_trigger_bitmask[i] = 0; - - GHashTable *keyswitch_groups = g_hash_table_new(g_direct_hash, g_direct_equal); - uint32_t keyswitch_group_count = 0, keyswitch_key_count = 0; - GPtrArray *keyswitch_group_array = g_ptr_array_new(); - memset(rll->ranges_by_key, 255, sizeof(rll->ranges_by_key)); - rll->has_release_layers = FALSE; - for (GSList *p = prg->all_layers; p; p = g_slist_next(p)) - { - struct sampler_layer *l = p->data; - if (l->data.trigger == stm_release) - rll->has_release_layers = TRUE; - if (l->data.sw_last >= 0 && l->data.sw_last <= 127 && - l->data.sw_lokey >= 0 && l->data.sw_lokey <= 127 && - l->data.sw_hikey >= 0 && l->data.sw_hikey <= 127 && - l->data.sw_last >= l->data.sw_lokey && l->data.sw_last <= l->data.sw_hikey) - { - int width = l->data.sw_hikey - l->data.sw_lokey + 1; - gpointer key = GINT_TO_POINTER(l->data.sw_lokey + (l->data.sw_hikey << 8)); - uint8_t value = l->data.sw_last - l->data.sw_lokey; - struct sampler_keyswitch_group *ks = g_hash_table_lookup(keyswitch_groups, key); - if (!ks) - { - ks = g_malloc(sizeof(struct sampler_keyswitch_group) + width); - ks->lo = l->data.sw_lokey; - ks->hi = l->data.sw_hikey; - ks->num_used = 0; - ks->def_value = 255; - memset(ks->key_offsets, 255, width); - - g_hash_table_insert(keyswitch_groups, (gpointer)key, ks); - g_ptr_array_add(keyswitch_group_array, ks); - keyswitch_group_count++; - } - if (l->data.sw_default >= ks->lo && l->data.sw_default <= ks->hi && ks->def_value == 255) - ks->def_value = l->data.sw_default - ks->lo; - if (ks->key_offsets[value] == 255) - { - ks->key_offsets[value] = ks->num_used; - ks->num_used++; - keyswitch_key_count++; - assert(ks->num_used <= width); - } - } - } - rll->keyswitch_groups = (gpointer)g_ptr_array_free(keyswitch_group_array, FALSE); - rll->keyswitch_group_count = keyswitch_group_count; - rll->keyswitch_key_count = keyswitch_key_count; - uint32_t offset = 0; - for (uint32_t i = 0; i < keyswitch_group_count; ++i) - { - rll->keyswitch_groups[i]->group_offset = 1 + offset; - offset += rll->keyswitch_groups[i]->num_used; - } - assert(offset == keyswitch_key_count); - - uint16_t lo_count[129], hi_count[128], low = 127, high = 0; - for (int i = 0; i < 128; i++) - lo_count[i] = hi_count[i] = 0; - lo_count[128] = 0; - - // XXXKF handle 'key' field without relying on the existing ugly hack - for (GSList *p = prg->all_layers; p; p = g_slist_next(p)) - { - struct sampler_layer *l = p->data; - if (l->data.lokey >= 0 && l->data.lokey <= 127 && - l->data.hikey >= 0 && l->data.hikey <= 127 && - l->data.lokey <= l->data.hikey) - { - lo_count[l->data.lokey]++; - hi_count[l->data.hikey]++; - if (l->data.lokey < low) - low = l->data.lokey; - if (l->data.hikey > high) - high = l->data.hikey; - } - } - rll->lokey = low; - rll->hikey = high; - uint32_t range_count = 1; - for (int i = low + 1; i <= high; ++i) - { - rll->ranges_by_key[i - 1] = range_count - 1; - if (hi_count[i - 1] || lo_count[i]) - range_count++; - } - rll->ranges_by_key[high] = range_count - 1; - rll->layers_by_range = g_malloc0_n(range_count * (1 + keyswitch_key_count), sizeof(GSList *)); - rll->release_layers_by_range = rll->has_release_layers ? g_malloc0_n(range_count * (1 + keyswitch_key_count), sizeof(GSList *)) : NULL; - rll->layers_by_range_count = range_count; - for (GSList *p = prg->all_layers; p; p = g_slist_next(p)) - { - struct sampler_layer *l = p->data; - uint32_t ks_offset = 0; - if (l->data.sw_last >= 0 && l->data.sw_last <= 127 && - l->data.sw_lokey >= 0 && l->data.sw_lokey <= 127 && - l->data.sw_hikey >= 0 && l->data.sw_hikey <= 127 && - l->data.sw_last >= l->data.sw_lokey && l->data.sw_last <= l->data.sw_hikey) - { - gpointer key = GINT_TO_POINTER(l->data.sw_lokey + (l->data.sw_hikey << 8)); - struct sampler_keyswitch_group *ks = g_hash_table_lookup(keyswitch_groups, key); - assert(ks); - int rel_offset = ks->key_offsets[l->data.sw_last - l->data.sw_lokey]; - assert(rel_offset != -1); - ks_offset = ks->group_offset + rel_offset; - } - - struct sampler_cc_range *oncc = l->data.on_cc; - if (oncc) - { - rll->layers_oncc = g_slist_prepend(rll->layers_oncc, l); - while(oncc) - { - int cc = oncc->key.cc_number; - rll->cc_trigger_bitmask[cc >> 5] |= 1 << (cc & 31); - oncc = oncc->next; - } - } - if (l->data.trigger == stm_release) - add_layers(rll, rll->release_layers_by_range + ks_offset * range_count, l, l->data.lokey, l->data.hikey); - else - add_layers(rll, rll->layers_by_range + ks_offset * range_count, l, l->data.lokey, l->data.hikey); - } - g_hash_table_unref(keyswitch_groups); - return rll; -} - -void sampler_rll_destroy(struct sampler_rll *rll) -{ - g_slist_free(rll->layers_oncc); - for (uint32_t i = 0; i < rll->layers_by_range_count * (1 + rll->keyswitch_key_count); ++i) - { - if (rll->has_release_layers) - g_slist_free(rll->release_layers_by_range[i]); - g_slist_free(rll->layers_by_range[i]); - } - for (uint32_t i = 0; i < rll->keyswitch_group_count; ++i) - g_free(rll->keyswitch_groups[i]); - g_free(rll->keyswitch_groups); - g_free(rll->release_layers_by_range); - g_free(rll->layers_by_range); - g_free(rll); -} diff --git a/template/calfbox/sampler_voice.c b/template/calfbox/sampler_voice.c deleted file mode 100644 index b31f8a5..0000000 --- a/template/calfbox/sampler_voice.c +++ /dev/null @@ -1,995 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config.h" -#include "config-api.h" -#include "dspmath.h" -#include "errors.h" -#include "midi.h" -#include "module.h" -#include "rt.h" -#include "sampler.h" -#include "sampler_impl.h" -#include "sfzloader.h" -#include "stm.h" -#include -#include -#include -#include -#include -#include -#include -#include - - -static void lfo_update_freq(struct sampler_lfo *lfo, struct sampler_lfo_params *lfop, int srate, double srate_inv) -{ - lfo->delta = (uint32_t)(lfop->freq * 65536.0 * 65536.0 * CBOX_BLOCK_SIZE * srate_inv); - lfo->delay = (uint32_t)(lfop->delay * srate); - lfo->fade = (uint32_t)(lfop->fade * srate); - lfo->wave = (int32_t)(lfop->wave); -} - -static void lfo_init(struct sampler_lfo *lfo, struct sampler_lfo_params *lfop, int srate, double srate_inv) -{ - lfo->phase = 0; - lfo->xdelta = 0; - lfo->age = 0; - lfo->random_value = 0; // safe, less CPU intensive value - lfo_update_freq(lfo, lfop, srate, srate_inv); -} - -static inline float lfo_run(struct sampler_lfo *lfo) -{ - if (lfo->age < lfo->delay) - { - lfo->age += CBOX_BLOCK_SIZE; - return 0.f; - } - uint32_t delta = lfo->delta + lfo->xdelta; - - const int FRAC_BITS = 32 - 11; - - float v = 0; - switch(lfo->wave) { - case 0: // triangle - { - uint32_t ph = lfo->phase + 0x40000000; - uint32_t tri = (ph & 0x7FFFFFFF) ^ (((ph >> 31) - 1) & 0x7FFFFFFF); - v = -1.0 + tri * (2.0 / (1U << 31)); - break; - } - case 1: // sine (default) - default: - { - uint32_t iphase = lfo->phase >> FRAC_BITS; - float frac = (lfo->phase & ((1 << FRAC_BITS) - 1)) * (1.0 / (1 << FRAC_BITS)); - v = sampler_sine_wave[iphase] + (sampler_sine_wave[iphase + 1] - sampler_sine_wave[iphase]) * frac; - break; - } - case 2: - v = lfo->phase < 0xC0000000 ? 1 : -1; - break; - case 3: - v = lfo->phase < 0x80000000 ? 1 : -1; - break; - case 4: - v = lfo->phase < 0x40000000 ? 1 : -1; - break; - case 5: - v = lfo->phase < 0x20000000 ? 1 : -1; - break; - case 6: - v = -1 + lfo->phase * (1.0 / (1U << 31)); - break; - case 7: - v = 1 - lfo->phase * (1.0 / (1U << 31)); - break; - case 12: - if ((lfo->phase & 0x80000000) != ((lfo->phase + delta) & 0x80000000)) - lfo->random_value = -1 + 2 * rand() / (1.0 * RAND_MAX); - v = lfo->random_value; - break; - } - lfo->phase += delta; - if (lfo->fade && lfo->age < lfo->delay + lfo->fade) - { - v *= (lfo->age - lfo->delay) * 1.0 / lfo->fade; - lfo->age += CBOX_BLOCK_SIZE; - } - - return v; -} - -static gboolean is_tail_finished(struct sampler_voice *v) -{ - if (!v->layer->computed.eff_num_stages) - return TRUE; - double eps = 1.0 / 65536.0; - if (cbox_biquadf_is_audible(&v->filter.filter_left[0], eps)) - return FALSE; - if (cbox_biquadf_is_audible(&v->filter.filter_right[0], eps)) - return FALSE; - int num_stages = v->layer->computed.eff_num_stages; - if (num_stages > 1) - { - if (cbox_biquadf_is_audible(&v->filter.filter_left[num_stages - 1], eps)) - return FALSE; - if (cbox_biquadf_is_audible(&v->filter.filter_right[num_stages - 1], eps)) - return FALSE; - } - - return TRUE; -} - -#if USE_NEON - -#include - -static inline void mix_block_into_with_gain(cbox_sample_t **outputs, int oofs, float *src_leftright, float gain) -{ - float *dst_left = outputs[oofs]; - float *dst_right = outputs[oofs + 1]; - float32x2_t gain2 = {gain, gain}; - for (size_t i = 0; i < CBOX_BLOCK_SIZE; i += 2) - { - float32x2_t lr1 = vld1_f32(&src_leftright[2 * i]); - float32x2_t lr2 = vld1_f32(&src_leftright[2 * i + 2]); - float32x2x2_t lr12 = vtrn_f32(lr1, lr2); - float32x2_t dl1 = vld1_f32(&dst_left[i]); - float32x2_t dr1 = vld1_f32(&dst_right[i]); - - float32x2_t l1 = vmla_f32(dl1, lr12.val[0], gain2); - vst1_f32(&dst_left[i], l1); - float32x2_t r1 = vmla_f32(dr1, lr12.val[1], gain2); - vst1_f32(&dst_right[i], r1); - } -} - -static inline void mix_block_into(cbox_sample_t **outputs, int oofs, float *src_leftright) -{ - float *dst_left = outputs[oofs]; - float *dst_right = outputs[oofs + 1]; - for (size_t i = 0; i < CBOX_BLOCK_SIZE; i += 2) - { - float32x2_t lr1 = vld1_f32(&src_leftright[2 * i]); - float32x2_t lr2 = vld1_f32(&src_leftright[2 * i + 2]); - float32x2x2_t lr12 = vtrn_f32(lr1, lr2); - float32x2_t dl1 = vld1_f32(&dst_left[i]); - float32x2_t dr1 = vld1_f32(&dst_right[i]); - - float32x2_t l1 = vadd_f32(dl1, lr12.val[0]); - vst1_f32(&dst_left[i], l1); - float32x2_t r1 = vadd_f32(dr1, lr12.val[1]); - vst1_f32(&dst_right[i], r1); - } -} - -#else - -static inline void mix_block_into_with_gain(cbox_sample_t **outputs, int oofs, float *src_leftright, float gain) -{ - cbox_sample_t *dst_left = outputs[oofs]; - cbox_sample_t *dst_right = outputs[oofs + 1]; - for (size_t i = 0; i < CBOX_BLOCK_SIZE; i++) - { - dst_left[i] += gain * src_leftright[2 * i]; - dst_right[i] += gain * src_leftright[2 * i + 1]; - } -} - -static inline void mix_block_into(cbox_sample_t **outputs, int oofs, float *src_leftright) -{ - cbox_sample_t *dst_left = outputs[oofs]; - cbox_sample_t *dst_right = outputs[oofs + 1]; - for (size_t i = 0; i < CBOX_BLOCK_SIZE; i++) - { - dst_left[i] += src_leftright[2 * i]; - dst_right[i] += src_leftright[2 * i + 1]; - } -} - -#endif - -//////////////////////////////////////////////////////////////////////////////// - -static float sfz_crossfade(float param, float xfin_lo, float xfin_hi, float xfout_lo, float xfout_hi, enum sampler_xf_curve xfc) -{ - if (param >= xfin_hi && param <= xfout_lo) - return 1.f; - if (param < xfin_lo || param > xfout_hi) - return 0.f; - float for0 = (param < xfout_lo) ? xfin_lo : xfout_hi; - float for1 = (param < xfout_lo) ? xfin_hi : xfout_lo; - if (for0 == for1) - return 1.f; - if (xfc == stxc_gain) - return (param - for0) / (for1 - for0); - else - { - float v = (param - for0) / (for1 - for0); - return sqrtf(v); - } -} - -// One-half version for CC-based crossfades -static inline float sfz_crossfade2(float param, float xflo, float xfhi, float left, float right, enum sampler_xf_curve xfc) -{ - if (xflo > xfhi) - return sfz_crossfade2(param, xfhi, xflo, right, left, xfc); - if (param <= xflo) - return left; - if (param >= xfhi) - return right; - float res; - if (xflo == xfhi) - res = 0.5f * (left + right); - else - res = left + (right - left) * (param - xflo) / (xfhi - xflo); - if (xfc == stxc_gain) - return res; - else - return sqrtf(res); -} - -//////////////////////////////////////////////////////////////////////////////// - -void sampler_voice_activate(struct sampler_voice *v, enum sampler_player_type mode) -{ - assert(v->gen.mode == spt_inactive); - sampler_voice_unlink(&v->program->module->voices_free, v); - assert(mode != spt_inactive); - assert(v->channel); - v->gen.mode = mode; - sampler_voice_link(&v->channel->voices_running, v); -} - -void sampler_voice_start_silent(struct sampler_layer_data *l, struct sampler_released_groups *exgroupdata) -{ - if (l->group >= 1) - sampler_released_groups_add(exgroupdata, l->group); -} - -void sampler_voice_start(struct sampler_voice *v, struct sampler_channel *c, struct sampler_layer_data *l, int note, int vel, struct sampler_released_groups *exgroupdata) -{ - struct sampler_module *m = c->module; - sampler_gen_reset(&v->gen); - - v->age = 0; - if (l->trigger == stm_release) - { - // time since last 'note on' for that note - v->age = m->current_time - c->prev_note_start_time[note]; - double age = v->age * m->module.srate_inv; - // if attenuation is more than 84dB, ignore the release trigger - if (age * l->rt_decay > 84) - return; - } - uint32_t end = l->computed.eff_waveform->info.frames; - if (l->end != 0) - end = (l->end == SAMPLER_NO_LOOP) ? 0 : l->end; - v->last_waveform = l->computed.eff_waveform; - v->gen.cur_sample_end = end; - if (end > l->computed.eff_waveform->info.frames) - end = l->computed.eff_waveform->info.frames; - - assert(!v->current_pipe); - if (end > l->computed.eff_waveform->preloaded_frames) - { - if (l->computed.eff_loop_mode == slm_loop_continuous && l->computed.eff_loop_end < l->computed.eff_waveform->preloaded_frames) - { - // Everything fits in prefetch, because loop ends in prefetch and post-loop part is not being played - } - else - { - uint32_t loop_start = -1, loop_end = end; - // If in loop mode, set the loop over the looped part... unless we're doing sustain-only loop on prefetch area only. Then - // streaming will only cover the release part, and it shouldn't be looped. - if (l->computed.eff_loop_mode == slm_loop_continuous || (l->computed.eff_loop_mode == slm_loop_sustain && l->computed.eff_loop_end >= l->computed.eff_waveform->preloaded_frames)) - { - loop_start = l->computed.eff_loop_start; - loop_end = l->computed.eff_loop_end; - } - // Those are initial values only, they will be adjusted in process function - v->current_pipe = cbox_prefetch_stack_pop(m->pipe_stack, l->computed.eff_waveform, loop_start, loop_end, l->count); - if (!v->current_pipe) - { - g_warning("Prefetch pipe pool exhausted, no streaming playback will be possible"); - end = l->computed.eff_waveform->preloaded_frames; - v->gen.cur_sample_end = end; - } - } - } - - v->output_pair_no = (l->output + c->output_shift) % m->output_pairs; - v->serial_no = m->serial_no; - - v->gen.loop_overlap = l->loop_overlap; - v->gen.loop_overlap_step = l->loop_overlap > 0 ? 1.0 / l->loop_overlap : 0; - v->gain_fromvel = l->computed.eff_amp_velcurve[vel]; - v->gain_shift = (note - l->amp_keycenter) * l->amp_keytrack; - - v->gain_fromvel *= sfz_crossfade(note, l->xfin_lokey, l->xfin_hikey, l->xfout_lokey, l->xfout_hikey, l->xf_keycurve); - v->gain_fromvel *= sfz_crossfade(vel, l->xfin_lovel, l->xfin_hivel, l->xfout_lovel, l->xfout_hivel, l->xf_velcurve); - - v->note = note; - v->vel = vel; - v->off_vel = 0; - v->pitch_shift = 0; - v->released = 0; - v->released_with_sustain = 0; - v->released_with_sostenuto = 0; - v->captured_sostenuto = 0; - v->channel = c; - v->layer = l; - v->program = c->program; - v->amp_env.shape = &l->amp_env_shape; - v->filter_env.shape = &l->filter_env_shape; - v->pitch_env.shape = &l->pitch_env_shape; - - v->cutoff_shift = vel * l->fil_veltrack / 127.0 + (note - l->fil_keycenter) * l->fil_keytrack; - v->cutoff2_shift = vel * l->fil2_veltrack / 127.0 + (note - l->fil2_keycenter) * l->fil2_keytrack; - v->loop_mode = l->computed.eff_loop_mode; - v->off_by = l->off_by; - v->reloffset = l->reloffset; - int auxes = (m->module.outputs - m->module.aux_offset) / 2; - if (l->effect1bus >= 1 && l->effect1bus < 1 + auxes) - v->send1bus = l->effect1bus; - else - v->send1bus = 0; - if (l->effect2bus >= 1 && l->effect2bus < 1 + auxes) - v->send2bus = l->effect2bus; - else - v->send2bus = 0; - v->send1gain = l->effect1 * 0.01; - v->send2gain = l->effect2 * 0.01; - if (l->group >= 1) - { - sampler_released_groups_add(exgroupdata, l->group); - } - lfo_init(&v->amp_lfo, &l->amp_lfo, m->module.srate, m->module.srate_inv); - lfo_init(&v->filter_lfo, &l->filter_lfo, m->module.srate, m->module.srate_inv); - lfo_init(&v->pitch_lfo, &l->pitch_lfo, m->module.srate, m->module.srate_inv); - - for (int i = 0; i < 3; ++i) - { - cbox_biquadf_reset(&v->filter.filter_left[i]); - cbox_biquadf_reset(&v->filter.filter_right[i]); - cbox_biquadf_reset(&v->filter2.filter_left[i]); - cbox_biquadf_reset(&v->filter2.filter_right[i]); - } - cbox_onepolef_reset(&v->onepole_left); - cbox_onepolef_reset(&v->onepole_right); - // set gain later (it's a less expensive operation) - if (l->tonectl_freq != 0) - cbox_onepolef_set_highshelf_tonectl(&v->onepole_coeffs, l->tonectl_freq * M_PI * m->module.srate_inv, 1.0); - - v->offset = l->offset; - for(struct sampler_noteinitfunc *nif = v->layer->voice_nifs; nif; nif = nif->next) - nif->key.notefunc_voice(nif, v); - if (v->gain_shift) - v->gain_fromvel *= dB2gain(v->gain_shift); - - if (v->offset + v->reloffset != 0) - { - // For streamed samples, allow only half the preload period worth of offset to avoid gaps - // (maybe we can allow up to preload period minus one buffer size here?) - uint32_t maxend = v->current_pipe ? (l->computed.eff_waveform->preloaded_frames >> 1) : l->computed.eff_waveform->preloaded_frames; - int32_t pos = v->offset + v->reloffset * maxend * 0.01; - if (pos < 0) - pos = 0; - if ((uint32_t)pos > maxend) - pos = (int32_t)maxend; - v->offset = pos; - } - - cbox_envelope_reset(&v->amp_env); - cbox_envelope_reset(&v->filter_env); - cbox_envelope_reset(&v->pitch_env); - - v->last_eq_bitmask = 0; - - sampler_voice_activate(v, l->computed.eff_waveform->info.channels == 2 ? spt_stereo16 : spt_mono16); - - uint32_t pos = v->offset; - if (l->offset_random) - pos += ((uint32_t)(rand() + (rand() << 16))) % l->offset_random; - if (pos >= end) - pos = end; - v->gen.bigpos = ((uint64_t)pos) << 32; - v->gen.virtpos = ((uint64_t)pos) << 32; - - if (v->current_pipe && v->gen.bigpos) - cbox_prefetch_pipe_consumed(v->current_pipe, v->gen.bigpos >> 32); - v->layer_changed = TRUE; -} - -void sampler_voice_link(struct sampler_voice **pv, struct sampler_voice *v) -{ - v->prev = NULL; - v->next = *pv; - if (*pv) - (*pv)->prev = v; - *pv = v; -} - -void sampler_voice_unlink(struct sampler_voice **pv, struct sampler_voice *v) -{ - if (*pv == v) - *pv = v->next; - if (v->prev) - v->prev->next = v->next; - if (v->next) - v->next->prev = v->prev; - v->prev = NULL; - v->next = NULL; -} - -void sampler_voice_inactivate(struct sampler_voice *v, gboolean expect_active) -{ - assert((v->gen.mode != spt_inactive) == expect_active); - sampler_voice_unlink(&v->channel->voices_running, v); - v->gen.mode = spt_inactive; - if (v->current_pipe) - { - cbox_prefetch_stack_push(v->program->module->pipe_stack, v->current_pipe); - v->current_pipe = NULL; - } - v->channel = NULL; - sampler_voice_link(&v->program->module->voices_free, v); -} - -void sampler_voice_release(struct sampler_voice *v, gboolean is_polyaft) -{ - if ((v->loop_mode == slm_one_shot_chokeable) != is_polyaft) - return; - if (v->loop_mode != slm_one_shot && !v->layer->count) - { - v->released = 1; - if (v->loop_mode == slm_loop_sustain && v->current_pipe) - { - // Break the loop - v->current_pipe->file_loop_end = v->gen.cur_sample_end; - v->current_pipe->file_loop_start = -1; - } - } -} - -void sampler_voice_update_params_from_layer(struct sampler_voice *v) -{ - struct sampler_layer_data *l = v->layer; - struct sampler_module *m = v->program->module; - lfo_update_freq(&v->amp_lfo, &l->amp_lfo, m->module.srate, m->module.srate_inv); - lfo_update_freq(&v->filter_lfo, &l->filter_lfo, m->module.srate, m->module.srate_inv); - lfo_update_freq(&v->pitch_lfo, &l->pitch_lfo, m->module.srate, m->module.srate_inv); - cbox_envelope_update_shape(&v->amp_env, &l->amp_env_shape); - cbox_envelope_update_shape(&v->filter_env, &l->filter_env_shape); - cbox_envelope_update_shape(&v->pitch_env, &l->pitch_env_shape); -} - -static inline void lfo_update_xdelta(struct sampler_module *m, struct sampler_lfo *lfo, uint32_t modmask, uint32_t dest, const float *moddests) -{ - if (!(modmask & (1 << dest))) - lfo->xdelta = 0; - else - lfo->xdelta = (uint32_t)(moddests[dest] * 65536.0 * 65536.0 * CBOX_BLOCK_SIZE * m->module.srate_inv); -} - -static inline void sampler_filter_process_control(struct sampler_filter *f, enum sampler_filter_type fil_type, float logcutoff, float resonance_linearized, const struct cbox_sincos *sincos_base) -{ - f->second_filter = &f->filter_coeffs; - - if (logcutoff < 0) - logcutoff = 0; - if (logcutoff > 12798) - logcutoff = 12798; - //float resonance = v->resonance*pow(32.0,c->cc[71]/maxv); - float resonance = resonance_linearized; - if (resonance < 0.7f) - resonance = 0.7f; - if (resonance > 32.f) - resonance = 32.f; - const struct cbox_sincos *sincos = &sincos_base[(int)logcutoff]; - switch(fil_type) - { - case sft_lp24hybrid: - cbox_biquadf_set_lp_rbj_lookup(&f->filter_coeffs, sincos, resonance * resonance); - cbox_biquadf_set_1plp_lookup(&f->filter_coeffs_extra, sincos, 1); - f->second_filter = &f->filter_coeffs_extra; - break; - case sft_lp12: - case sft_lp24: - case sft_lp36: - cbox_biquadf_set_lp_rbj_lookup(&f->filter_coeffs, sincos, resonance); - break; - case sft_hp12: - case sft_hp24: - cbox_biquadf_set_hp_rbj_lookup(&f->filter_coeffs, sincos, resonance); - break; - case sft_bp6: - case sft_bp12: - cbox_biquadf_set_bp_rbj_lookup(&f->filter_coeffs, sincos, resonance); - break; - case sft_lp6: - case sft_lp12nr: - case sft_lp24nr: - cbox_biquadf_set_1plp_lookup(&f->filter_coeffs, sincos, fil_type != sft_lp6); - break; - case sft_hp6: - case sft_hp12nr: - case sft_hp24nr: - cbox_biquadf_set_1php_lookup(&f->filter_coeffs, sincos, fil_type != sft_hp6); - break; - default: - assert(0); - } -} - -static inline void sampler_filter_process_audio(struct sampler_filter *f, int num_stages, float *leftright) -{ - for (int i = 0; i < num_stages; ++i) - cbox_biquadf_process_stereo(&f->filter_left[i], &f->filter_right[i], i ? f->second_filter : &f->filter_coeffs, leftright); -} - -static inline void do_channel_mixing(float *leftright, uint32_t numsamples, float position, float width) -{ - float crossmix = (100.0 - width) * 0.005f; - float amtleft = position > 0 ? 1 - 0.01 * position : 1; - float amtright = position < 0 ? 1 - 0.01 * -position : 1; - if (amtleft < 0) - amtleft = 0; - if (amtright < 0) - amtright = 0; - for (uint32_t i = 0; i < 2 * numsamples; i += 2) { - float left = leftright[i], right = leftright[i + 1]; - float newleft = left + crossmix * (right - left); - float newright = right + crossmix * (left - right); - leftright[i] = newleft * amtleft; - leftright[i + 1] = newright * amtright; - } -} - -static inline uint32_t sampler_gen_sample_playback_with_pipe(struct sampler_gen *gen, float *leftright, struct cbox_prefetch_pipe *current_pipe) -{ - if (!current_pipe) - return sampler_gen_sample_playback(gen, leftright, (uint32_t)-1); - - uint32_t limit = cbox_prefetch_pipe_get_remaining(current_pipe); - if (limit <= 4) - { - gen->mode = spt_inactive; - return 0; - } - uint32_t samples = sampler_gen_sample_playback(gen, leftright, limit - 4); - cbox_prefetch_pipe_consumed(current_pipe, gen->consumed); - gen->consumed = 0; - return samples; -} - -static const float gain_for_num_stages[] = { 1, 1, 0.5, 0.33f }; - -void sampler_voice_process(struct sampler_voice *v, struct sampler_module *m, cbox_sample_t **outputs) -{ - struct sampler_layer_data *l = v->layer; - assert(v->gen.mode != spt_inactive); - - struct sampler_channel *c = v->channel; - v->age += CBOX_BLOCK_SIZE; - - const float velscl = v->vel * (1.f / 127.f); - - struct cbox_envelope_shape *pitcheg_shape = v->pitch_env.shape, *fileg_shape = v->filter_env.shape, *ampeg_shape = v->amp_env.shape; - if (__builtin_expect(l->computed.mod_bitmask, 0)) - { - #define COPY_ORIG_SHAPE(envtype, envtype2, index) \ - if (l->computed.mod_bitmask & slmb_##envtype2##eg_cc) { \ - memcpy(&v->cc_envs[index], envtype2##eg_shape, sizeof(struct cbox_envelope_shape)); \ - envtype2##eg_shape = &v->cc_envs[index]; \ - } - COPY_ORIG_SHAPE(amp, amp, 0) - COPY_ORIG_SHAPE(filter, fil, 1) - COPY_ORIG_SHAPE(pitch, pitch, 2) - - for(struct sampler_modulation *sm = l->modulations; sm; sm = sm->next) - { - // Simplified modulations for EG stages (CCs only) - if (sm->key.dest >= smdest_eg_stage_start && sm->key.dest <= smdest_eg_stage_end) - { - float value = 0.f; - if (sm->key.src < smsrc_pernote_offset) - value = sampler_channel_getcc_mod(c, v, sm->key.src, sm->value.curve_id, sm->value.step); - uint32_t param = sm->key.dest - smdest_eg_stage_start; - if (value * sm->value.amount != 0) - cbox_envelope_modify_dahdsr(&v->cc_envs[(param >> 4)], param & 0x0F, value * sm->value.amount, m->module.srate * 1.0 / CBOX_BLOCK_SIZE); - } - } - #define UPDATE_ENV_POSITION(envtype, envtype2) \ - if (l->computed.mod_bitmask & slmb_##envtype##eg_cc) \ - cbox_envelope_update_shape_after_modify(&v->envtype2##_env, envtype##eg_shape, m->module.srate * 1.0 / CBOX_BLOCK_SIZE); - UPDATE_ENV_POSITION(amp, amp) - UPDATE_ENV_POSITION(fil, filter) - UPDATE_ENV_POSITION(pitch, pitch) - } - - // if it's a DAHD envelope without sustain, consider the note finished - if (__builtin_expect(v->amp_env.cur_stage == 4 && ampeg_shape->stages[3].end_value <= 0.f, 0)) - cbox_envelope_go_to(&v->amp_env, 15); - - #define RECALC_EQ_MASK_EQ1 (7 << smdest_eq1_freq) - #define RECALC_EQ_MASK_EQ2 (7 << smdest_eq2_freq) - #define RECALC_EQ_MASK_EQ3 (7 << smdest_eq3_freq) - #define RECALC_EQ_MASK_ALL (RECALC_EQ_MASK_EQ1 | RECALC_EQ_MASK_EQ2 | RECALC_EQ_MASK_EQ3) - uint32_t recalc_eq_mask = 0; - - if (__builtin_expect(v->layer_changed, 0)) - { - v->last_level = -1; - if (v->last_waveform != v->layer->computed.eff_waveform) - { - v->last_waveform = v->layer->computed.eff_waveform; - if (v->layer->computed.eff_waveform) - { - v->gen.mode = v->layer->computed.eff_waveform->info.channels == 2 ? spt_stereo16 : spt_mono16; - v->gen.cur_sample_end = v->layer->computed.eff_waveform->info.frames; - } - else - { - sampler_voice_inactivate(v, TRUE); - return; - } - } - if (l->computed.eq_bitmask & (1 << 0)) recalc_eq_mask |= RECALC_EQ_MASK_EQ1; - if (l->computed.eq_bitmask & (1 << 1)) recalc_eq_mask |= RECALC_EQ_MASK_EQ2; - if (l->computed.eq_bitmask & (1 << 2)) recalc_eq_mask |= RECALC_EQ_MASK_EQ3; - v->last_eq_bitmask = l->computed.eq_bitmask; - v->layer_changed = FALSE; - } - - float pitch = (v->note - l->pitch_keycenter) * l->pitch_keytrack + l->tune + l->transpose * 100 + v->pitch_shift; - float modsrcs[smsrc_pernote_count]; - modsrcs[smsrc_vel - smsrc_pernote_offset] = v->vel * velscl; - modsrcs[smsrc_pitch - smsrc_pernote_offset] = pitch * (1.f / 100.f); - modsrcs[smsrc_chanaft - smsrc_pernote_offset] = c->last_chanaft * (1.f / 127.f); - modsrcs[smsrc_polyaft - smsrc_pernote_offset] = sampler_channel_get_poly_pressure(c, v->note); - modsrcs[smsrc_pitchenv - smsrc_pernote_offset] = cbox_envelope_get_value(&v->pitch_env, pitcheg_shape) * 0.01f; - modsrcs[smsrc_filenv - smsrc_pernote_offset] = l->computed.eff_use_filter_mods ? cbox_envelope_get_value(&v->filter_env, fileg_shape) * 0.01f : 0; - modsrcs[smsrc_ampenv - smsrc_pernote_offset] = cbox_envelope_get_value(&v->amp_env, ampeg_shape) * 0.01f; - - modsrcs[smsrc_amplfo - smsrc_pernote_offset] = lfo_run(&v->amp_lfo); - modsrcs[smsrc_fillfo - smsrc_pernote_offset] = l->computed.eff_use_filter_mods ? lfo_run(&v->filter_lfo) : 0; - modsrcs[smsrc_pitchlfo - smsrc_pernote_offset] = lfo_run(&v->pitch_lfo); - - float moddests[smdestcount]; - moddests[smdest_pitch] = pitch; - moddests[smdest_cutoff] = v->cutoff_shift; - moddests[smdest_cutoff2] = v->cutoff2_shift; - // These are always set - uint32_t modmask = (1 << smdest_pitch) | (1 << smdest_cutoff) | (1 << smdest_cutoff2); -#if 0 - // Those are lazy-initialized using modmask. - moddests[smdest_gain] = 0; - moddests[smdest_resonance] = 0; - moddests[smdest_tonectl] = 0; - moddests[smdest_pitchlfo_freq] = 0; - moddests[smdest_fillfo_freq] = 0; - moddests[smdest_amplfo_freq] = 0; -#endif - if (__builtin_expect(l->trigger == stm_release, 0)) - { - moddests[smdest_gain] = -v->age * l->rt_decay * m->module.srate_inv; - modmask |= (1 << smdest_gain); - } - - if (c->pitchwheel) - { - int pw = c->pitchwheel * (c->pitchwheel > 0 ? l->bend_up : -l->bend_down); - // approximate dividing by 8191 - if (pw < 0) - pw >>= 13; - else - pw = (pw + 4096) >> 13; - if (l->bend_step > 1) - pw = (pw / l->bend_step) * l->bend_step; - moddests[smdest_pitch] += pw; - } - - for (struct sampler_modulation *sm = l->modulations; sm; sm = sm->next) - { - enum sampler_modsrc src = sm->key.src; - enum sampler_modsrc src2 = sm->key.src2; - enum sampler_moddest dest = sm->key.dest; - float value = 0.f, value2 = 1.f; - if (src < smsrc_pernote_offset) - value = sampler_channel_getcc_mod(c, v, src, sm->value.curve_id, sm->value.step); - else - value = modsrcs[src - smsrc_pernote_offset]; - - if (src2 != smsrc_none) - { - if (src2 < smsrc_pernote_offset) - value2 = sampler_channel_getcc_mod(c, v, src2, sm->value.curve_id, sm->value.step); - else - value2 = modsrcs[src2 - smsrc_pernote_offset]; - - value *= value2; - } - if (dest < 32) - { - if (dest == smdest_amplitude) - { - if (!(modmask & (1 << dest))) // first value - { - moddests[dest] = value * sm->value.amount; - modmask |= (1 << dest); - } - else - moddests[dest] *= value * sm->value.amount; - } - else if (!(modmask & (1 << dest))) // first value - { - moddests[dest] = value * sm->value.amount; - modmask |= (1 << dest); - } - else - moddests[dest] += value * sm->value.amount; - } - } - lfo_update_xdelta(m, &v->pitch_lfo, modmask, smdest_pitchlfo_freq, moddests); - if (l->computed.eff_use_filter_mods) - lfo_update_xdelta(m, &v->filter_lfo, modmask, smdest_fillfo_freq, moddests); - lfo_update_xdelta(m, &v->amp_lfo, modmask, smdest_amplfo_freq, moddests); - recalc_eq_mask |= modmask; - - #define RECALC_EQ_IF(index) \ - if (recalc_eq_mask & RECALC_EQ_MASK_EQ##index) \ - { \ - float dfreq = velscl * l->eq##index.vel2freq + ((modmask & (1 << smdest_eq##index##_freq)) ? moddests[smdest_eq##index##_freq] : 0);\ - float fbw = (modmask & (1 << smdest_eq##index##_bw)) ? pow(0.5, moddests[smdest_eq##index##_bw]) : 1;\ - float dgain = velscl * l->eq##index.vel2gain + ((modmask & (1 << smdest_eq##index##_gain)) ? moddests[smdest_eq##index##_gain] : 0);\ - cbox_biquadf_set_peakeq_rbj_scaled(&v->eq_coeffs[index - 1], l->eq##index.effective_freq + dfreq, fbw / l->eq##index.bw, dB2gain(0.5 * (l->eq##index.gain + dgain)), m->module.srate); \ - if (!(v->last_eq_bitmask & (1 << (index - 1)))) \ - { \ - cbox_biquadf_reset(&v->eq_left[index-1]); \ - cbox_biquadf_reset(&v->eq_right[index-1]); \ - } \ - } - if (__builtin_expect(recalc_eq_mask, 0)) - { - RECALC_EQ_IF(1) - RECALC_EQ_IF(2) - RECALC_EQ_IF(3) - } - cbox_envelope_advance(&v->pitch_env, v->released, pitcheg_shape); - if (l->computed.eff_use_filter_mods) - cbox_envelope_advance(&v->filter_env, v->released, fileg_shape); - cbox_envelope_advance(&v->amp_env, v->released, ampeg_shape); - if (__builtin_expect(v->amp_env.cur_stage < 0, 0)) - { - if (__builtin_expect(is_tail_finished(v), 0)) - { - sampler_voice_inactivate(v, TRUE); - return; - } - } - - double maxv = 127 << 7; - double freq = l->computed.eff_freq * cent2factor(moddests[smdest_pitch]) ; - uint64_t freq64 = (uint64_t)(freq * 65536.0 * 65536.0 * m->module.srate_inv); - - gboolean playing_sustain_loop = !v->released && v->loop_mode == slm_loop_sustain; - uint32_t loop_start, loop_end; - gboolean bandlimited = FALSE; - - if (!v->current_pipe) - { - v->gen.sample_data = v->last_waveform->data; - if (v->last_waveform->levels) - { - gboolean use_cached = v->last_level > 0 && v->last_level < v->last_waveform->level_count - && freq64 > v->last_level_min_rate && freq64 <= v->last_waveform->levels[v->last_level].max_rate; - if (__builtin_expect(use_cached, 1)) - { - v->gen.sample_data = v->last_waveform->levels[v->last_level].data; - bandlimited = TRUE; - } - else - { - for (int i = 0; i < v->last_waveform->level_count; i++) - { - if (freq64 <= v->last_waveform->levels[i].max_rate) - { - v->last_level = i; - v->gen.sample_data = v->last_waveform->levels[i].data; - bandlimited = TRUE; - - break; - } - v->last_level_min_rate = v->last_waveform->levels[i].max_rate; - } - } - } - } - - // XXXKF or maybe check for on-cc being in the on-cc range instead? - gboolean play_loop = v->layer->computed.eff_loop_end && (v->loop_mode == slm_loop_continuous || playing_sustain_loop) && !v->layer->on_cc; - loop_start = play_loop ? v->layer->computed.eff_loop_start : (v->layer->count ? 0 : (uint32_t)-1); - loop_end = play_loop ? v->layer->computed.eff_loop_end : v->gen.cur_sample_end; - - if (v->current_pipe) - { - v->gen.sample_data = v->gen.loop_count ? v->current_pipe->data : v->last_waveform->data; - v->gen.streaming_buffer = v->current_pipe->data; - - v->gen.prefetch_only_loop = (loop_end < v->last_waveform->preloaded_frames); - v->gen.loop_overlap = 0; - if (v->gen.prefetch_only_loop) - { - assert(!v->gen.in_streaming_buffer); // XXXKF this won't hold true when loops are edited while sound is being played (but that's not supported yet anyway) - v->gen.loop_start = loop_start; - v->gen.loop_end = loop_end; - v->gen.streaming_buffer_frames = 0; - } - else - { - v->gen.loop_start = 0; - v->gen.loop_end = v->last_waveform->preloaded_frames; - v->gen.streaming_buffer_frames = v->current_pipe->buffer_loop_end; - } - } - else - { - v->gen.loop_count = v->layer->count; - v->gen.loop_start = loop_start; - v->gen.loop_end = loop_end; - - if (!bandlimited) - { - // Use pre-calculated join - v->gen.scratch = loop_start == (uint32_t)-1 ? v->layer->computed.scratch_end : v->layer->computed.scratch_loop; - } - else - { - // The standard waveforms have extra MAX_INTERPOLATION_ORDER of samples from the loop start added past loop_end, - // to avoid wasting time generating the joins in all the practical cases. The slow path covers custom loops - // (i.e. partial loop or no loop) over bandlimited versions of the standard waveforms, and those are probably - // not very useful anyway, as changing the loop removes the guarantee of the waveform being bandlimited and - // may cause looping artifacts or introduce DC offset (e.g. if only a positive part of a sine wave is looped). - if (loop_start == 0 && loop_end == l->computed.eff_waveform->info.frames) - v->gen.scratch = v->gen.sample_data + l->computed.eff_waveform->info.frames - MAX_INTERPOLATION_ORDER; - else - { - // Generate the join for the current wave level - // XXXKF this could be optimised further, by checking if waveform and loops are the same as the last - // time. However, this code is not likely to be used... ever, so optimising it is not the priority. - int shift = l->computed.eff_waveform->info.channels == 2 ? 1 : 0; - uint32_t halfscratch = MAX_INTERPOLATION_ORDER << shift; - - v->gen.scratch = v->gen.scratch_bandlimited; - memcpy(&v->gen.scratch_bandlimited[0], &v->gen.sample_data[(loop_end - MAX_INTERPOLATION_ORDER) << shift], halfscratch * sizeof(int16_t) ); - if (loop_start != (uint32_t)-1) - memcpy(v->gen.scratch_bandlimited + halfscratch, &v->gen.sample_data[loop_start << shift], halfscratch * sizeof(int16_t)); - else - memset(v->gen.scratch_bandlimited + halfscratch, 0, halfscratch * sizeof(int16_t)); - } - } - } - - if (l->timestretch) - { - v->gen.bigdelta = freq64; - v->gen.virtdelta = (uint64_t)(l->computed.eff_freq * 65536.0 * 65536.0 * m->module.srate_inv); - v->gen.stretching_jump = l->timestretch_jump; - v->gen.stretching_crossfade = l->timestretch_crossfade; - } - else - { - v->gen.bigdelta = freq64; - v->gen.virtdelta = freq64; - } - float gain = modsrcs[smsrc_ampenv - smsrc_pernote_offset] * l->volume_linearized * v->gain_fromvel * c->channel_volume_cc * sampler_channel_addcc(c, 11) / (maxv * maxv); - if (l->computed.eff_use_xfcc) { - for(struct sampler_cc_range *p = l->xfin_cc; p; p = p->next) - gain *= sfz_crossfade2(c->intcc[p->key.cc_number], p->value.locc, p->value.hicc, 0, 1, l->xf_cccurve); - for(struct sampler_cc_range *p = l->xfout_cc; p; p = p->next) - gain *= sfz_crossfade2(c->intcc[p->key.cc_number], p->value.locc, p->value.hicc, 1, 0, l->xf_cccurve); - } - if ((modmask & (1 << smdest_gain)) && moddests[smdest_gain] != 0.f) - gain *= dB2gain(moddests[smdest_gain]); - - float amplitude = l->amplitude; - if ((modmask & (1 << smdest_amplitude))) - amplitude *= moddests[smdest_amplitude]; - - gain *= amplitude * (1.0 / 100.0); - // http://drealm.info/sfz/plj-sfz.xhtml#amp "The overall gain must remain in the range -144 to 6 decibels." - if (gain > 2.f) - gain = 2.f; - float pan = (l->pan + ((modmask & (1 << smdest_pan) ? moddests[smdest_pan] : 0)) + 100.f) * (1.f / 200.f) + (c->channel_pan_cc * 1.f / maxv - 0.5f) * 2.f; - if (pan < 0.f) - pan = 0.f; - if (pan > 1.f) - pan = 1.f; - v->gen.lgain = gain * (1.f - pan) / 32768.f; - v->gen.rgain = gain * pan / 32768.f; - - if (l->cutoff != -1) - { - float mod_resonance = (modmask & (1 << smdest_resonance)) ? dB2gain(gain_for_num_stages[l->computed.eff_num_stages] * moddests[smdest_resonance]) : 1; - sampler_filter_process_control(&v->filter, l->fil_type, l->computed.logcutoff + moddests[smdest_cutoff], l->computed.resonance_scaled * mod_resonance, m->sincos); - } - if (l->cutoff2 != -1) - { - float mod_resonance = (modmask & (1 << smdest_resonance2)) ? dB2gain(gain_for_num_stages[l->computed.eff_num_stages2] * moddests[smdest_resonance2]) : 1; - sampler_filter_process_control(&v->filter2, l->fil2_type, l->computed.logcutoff2 + moddests[smdest_cutoff2], l->computed.resonance2_scaled * mod_resonance, m->sincos); - } - - if (__builtin_expect(l->tonectl_freq != 0, 0)) - { - float ctl = l->tonectl + (modmask & (1 << smdest_tonectl) ? moddests[smdest_tonectl] : 0); - if (fabs(ctl) > 0.0001f) - cbox_onepolef_set_highshelf_setgain(&v->onepole_coeffs, dB2gain(ctl)); - else - cbox_onepolef_set_highshelf_setgain(&v->onepole_coeffs, 1.0); - } - - // Audio processing starts here - float leftright[2 * CBOX_BLOCK_SIZE]; - - uint32_t samples = sampler_gen_sample_playback_with_pipe(&v->gen, leftright, v->current_pipe); - if (l->computed.eff_use_channel_mixer) - do_channel_mixing(leftright, samples, l->position, l->width); - for (int i = 2 * samples; i < 2 * CBOX_BLOCK_SIZE; i++) - leftright[i] = 0.f; - - if (l->cutoff != -1) - sampler_filter_process_audio(&v->filter, l->computed.eff_num_stages, leftright); - if (l->cutoff2 != -1) - sampler_filter_process_audio(&v->filter2, l->computed.eff_num_stages2, leftright); - - if (__builtin_expect(l->tonectl_freq != 0, 0)) - cbox_onepolef_process_stereo(&v->onepole_left, &v->onepole_right, &v->onepole_coeffs, leftright); - - if (__builtin_expect(l->computed.eq_bitmask, 0)) - { - for (int eq = 0; eq < 3; eq++) - { - if (l->computed.eq_bitmask & (1 << eq)) - { - cbox_biquadf_process_stereo(&v->eq_left[eq], &v->eq_right[eq], &v->eq_coeffs[eq], leftright); - } - } - } - - mix_block_into(outputs, v->output_pair_no * 2, leftright); - if (__builtin_expect((v->send1bus > 0 && v->send1gain != 0) || (v->send2bus > 0 && v->send2gain != 0), 0)) - { - if (v->send1bus > 0 && v->send1gain != 0) - { - int oofs = m->module.aux_offset + (v->send1bus - 1) * 2; - mix_block_into_with_gain(outputs, oofs, leftright, v->send1gain); - } - if (v->send2bus > 0 && v->send2gain != 0) - { - int oofs = m->module.aux_offset + (v->send2bus - 1) * 2; - mix_block_into_with_gain(outputs, oofs, leftright, v->send2gain); - } - } - if (v->gen.mode == spt_inactive) - sampler_voice_inactivate(v, FALSE); -} - diff --git a/template/calfbox/scene.c b/template/calfbox/scene.c deleted file mode 100644 index f29ed0e..0000000 --- a/template/calfbox/scene.c +++ /dev/null @@ -1,1238 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "auxbus.h" -#include "config-api.h" -#include "engine.h" -#include "errors.h" -#include "instr.h" -#include "io.h" -#include "layer.h" -#include "master.h" -#include "midi.h" -#include "module.h" -#include "pattern.h" -#include "rt.h" -#include "scene.h" -#include "seq.h" -#include -#include - -CBOX_CLASS_DEFINITION_ROOT(cbox_scene) - -static gboolean cbox_scene_addlayercmd(struct cbox_scene *s, struct cbox_command_target *fb, struct cbox_osc_command *cmd, int cmd_type, GError **error) -{ - int pos = CBOX_ARG_I(cmd, 0); - if (pos < 0 || pos > (int)(1 + s->layer_count)) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid position %d (valid are 1..%d or 0 for append)", pos, 1 + s->layer_count); - return FALSE; - } - if (pos == 0) - pos = s->layer_count; - else - pos--; - struct cbox_layer *layer = NULL; - - switch(cmd_type) - { - case 1: - layer = cbox_layer_new_from_config(s, CBOX_ARG_S(cmd, 1), error); - break; - case 2: - layer = cbox_layer_new_with_instrument(s, CBOX_ARG_S(cmd, 1), error); - break; - case 3: - { - struct cbox_instrument *instr = cbox_scene_create_instrument(s, CBOX_ARG_S(cmd, 1), CBOX_ARG_S(cmd, 2), error); - if (!instr) - return FALSE; - layer = cbox_layer_new_with_instrument(s, CBOX_ARG_S(cmd, 1), error); - break; - } - case 4: - layer = cbox_layer_new(s); - if (cbox_uuid_fromstring(&layer->external_output, CBOX_ARG_S(cmd, 1), error)) - { - layer->external_output_set = TRUE; - } - else - { - CBOX_DELETE(layer); - return FALSE; - } - break; - default: - assert(0); - break; - } - if (!layer) - return FALSE; - if (!cbox_scene_insert_layer(s, layer, pos, error)) - { - CBOX_DELETE(layer); - return FALSE; - } - if (fb) - { - if (!cbox_execute_on(fb, NULL, "/uuid", "o", error, layer)) - return FALSE; - } - return TRUE; -} - -static gboolean cbox_scene_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct cbox_scene *s = ct->user_data; - const char *subcommand = NULL; - char *subobj = NULL; - int index = 0; - - if (!strcmp(cmd->command, "/transpose") && !strcmp(cmd->arg_types, "i")) - { - s->transpose = CBOX_ARG_I(cmd, 0); - return TRUE; - } - else if (!strcmp(cmd->command, "/load") && !strcmp(cmd->arg_types, "s")) - { - if (!cbox_scene_load(s, CBOX_ARG_S(cmd, 0), error)) - return FALSE; - return TRUE; - } - else if (!strcmp(cmd->command, "/clear") && !strcmp(cmd->arg_types, "")) - { - cbox_scene_clear(s); - return TRUE; - } - else if (!strcmp(cmd->command, "/add_layer") && !strcmp(cmd->arg_types, "is")) - { - return cbox_scene_addlayercmd(s, fb, cmd, 1, error); - } - else if (!strcmp(cmd->command, "/add_instrument_layer") && !strcmp(cmd->arg_types, "is")) - { - return cbox_scene_addlayercmd(s, fb, cmd, 2, error); - } - else if (!strcmp(cmd->command, "/add_new_instrument_layer") && !strcmp(cmd->arg_types, "iss")) - { - return cbox_scene_addlayercmd(s, fb, cmd, 3, error); - } - else if (!strcmp(cmd->command, "/add_midi_layer") && !strcmp(cmd->arg_types, "is")) - { - return cbox_scene_addlayercmd(s, fb, cmd, 4, error); - } - else if (!strcmp(cmd->command, "/delete_layer") && !strcmp(cmd->arg_types, "i")) - { - int pos = CBOX_ARG_I(cmd, 0); - if (pos < 0 || pos > (int)s->layer_count) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid position %d (valid are 1..%d or 0 for last)", pos, s->layer_count); - return FALSE; - } - if (pos == 0) - pos = s->layer_count - 1; - else - pos--; - struct cbox_layer *layer = cbox_scene_remove_layer(s, pos); - CBOX_DELETE(layer); - return TRUE; - } - else if (!strcmp(cmd->command, "/move_layer") && !strcmp(cmd->arg_types, "ii")) - { - int oldpos = CBOX_ARG_I(cmd, 0); - if (oldpos < 1 || oldpos > (int)s->layer_count) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid position %d (valid are 1..%d)", oldpos, s->layer_count); - return FALSE; - } - int newpos = CBOX_ARG_I(cmd, 1); - if (newpos < 1 || newpos > (int)s->layer_count) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid position %d (valid are 1..%d)", newpos, s->layer_count); - return FALSE; - } - cbox_scene_move_layer(s, oldpos - 1, newpos - 1); - return TRUE; - } - else if (cbox_parse_path_part_int(cmd, "/layer/", &subcommand, &index, 1, s->layer_count, error)) - { - if (!subcommand) - return FALSE; - return cbox_execute_sub(&s->layers[index - 1]->cmd_target, fb, cmd, subcommand, error); - } - else if (cbox_parse_path_part_str(cmd, "/aux/", &subcommand, &subobj, error)) - { - if (!subcommand) - return FALSE; - struct cbox_aux_bus *aux = cbox_scene_get_aux_bus(s, subobj, FALSE, error); - g_free(subobj); - if (!aux) - return FALSE; - return cbox_execute_sub(&aux->cmd_target, fb, cmd, subcommand, error); - } - else if (!strncmp(cmd->command, "/instr/", 7)) - { - const char *obj = &cmd->command[1]; - const char *pos = strchr(obj, '/'); - obj = &pos[1]; - pos = strchr(obj, '/'); - if (!pos) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid instrument path '%s'", cmd->command); - return FALSE; - } - int len = pos - obj; - - gchar *name = g_strndup(obj, len); - struct cbox_instrument *instr = cbox_scene_get_instrument_by_name(s, name, FALSE, error); - if (instr) - { - g_free(name); - - return cbox_execute_sub(&instr->cmd_target, fb, cmd, pos, error); - } - else - { - cbox_force_error(error); - g_prefix_error(error, "Cannot access instrument '%s': ", name); - g_free(name); - return FALSE; - } - return TRUE; - } - else if (!strcmp(cmd->command, "/load_aux") && !strcmp(cmd->arg_types, "s")) - { - struct cbox_aux_bus *bus = cbox_scene_get_aux_bus(s, CBOX_ARG_S(cmd, 0), TRUE, error); - if (!bus) - return FALSE; - if (fb) - { - if (!cbox_execute_on(fb, NULL, "/uuid", "o", error, bus)) - return FALSE; - } - return TRUE; - } - else if (!strcmp(cmd->command, "/delete_aux") && !strcmp(cmd->arg_types, "s")) - { - const char *name = CBOX_ARG_S(cmd, 0); - struct cbox_aux_bus *aux = cbox_scene_get_aux_bus(s, name, FALSE, error); - if (!aux) - return FALSE; - CBOX_DELETE(aux); - return TRUE; - } - else if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - if (!cbox_execute_on(fb, NULL, "/name", "s", error, s->name) || - !cbox_execute_on(fb, NULL, "/title", "s", error, s->title) || - !cbox_execute_on(fb, NULL, "/transpose", "i", error, s->transpose) || - !cbox_execute_on(fb, NULL, "/enable_default_song_input", "i", error, s->enable_default_song_input) || - !cbox_execute_on(fb, NULL, "/enable_default_external_input", "i", error, s->enable_default_external_input) || - !CBOX_OBJECT_DEFAULT_STATUS(s, fb, error)) - return FALSE; - - for (uint32_t i = 0; i < s->layer_count; i++) - { - if (!cbox_execute_on(fb, NULL, "/layer", "o", error, s->layers[i])) - return FALSE; - } - for (uint32_t i = 0; i < s->instrument_count; i++) - { - if (!cbox_execute_on(fb, NULL, "/instrument", "sso", error, s->instruments[i]->module->instance_name, s->instruments[i]->module->engine_name, s->instruments[i])) - return FALSE; - } - for (uint32_t i = 0; i < s->aux_bus_count; i++) - { - if (!cbox_execute_on(fb, NULL, "/aux", "so", error, s->aux_buses[i]->name, s->aux_buses[i])) - return FALSE; - } - return TRUE; - } - else - if (!strcmp(cmd->command, "/send_event") && (!strcmp(cmd->arg_types, "iii") || !strcmp(cmd->arg_types, "ii") || !strcmp(cmd->arg_types, "i"))) - { - int mcmd = CBOX_ARG_I(cmd, 0); - int arg1 = 0, arg2 = 0; - if (cmd->arg_types[1] == 'i') - { - arg1 = CBOX_ARG_I(cmd, 1); - if (cmd->arg_types[2] == 'i') - arg2 = CBOX_ARG_I(cmd, 2); - } - struct cbox_midi_buffer buf; - cbox_midi_buffer_init(&buf); - cbox_midi_buffer_write_inline(&buf, 0, mcmd, arg1, arg2); - cbox_midi_merger_push(&s->scene_input_merger, &buf, s->rt); - return TRUE; - } - else - if (!strcmp(cmd->command, "/play_note") && !strcmp(cmd->arg_types, "iii")) - { - int channel = CBOX_ARG_I(cmd, 0); - int note = CBOX_ARG_I(cmd, 1); - int velocity = CBOX_ARG_I(cmd, 2); - struct cbox_midi_buffer buf; - cbox_midi_buffer_init(&buf); - cbox_midi_buffer_write_inline(&buf, 0, 0x90 + ((channel - 1) & 15), note & 127, velocity & 127); - cbox_midi_buffer_write_inline(&buf, 1, 0x80 + ((channel - 1) & 15), note & 127, velocity & 127); - cbox_midi_merger_push(&s->scene_input_merger, &buf, s->rt); - return TRUE; - } - else - if (!strcmp(cmd->command, "/play_pattern") && !strcmp(cmd->arg_types, "sfi")) - { - struct cbox_midi_pattern *pattern = (struct cbox_midi_pattern *)CBOX_ARG_O(cmd, 0, s, cbox_midi_pattern, error); - if (!pattern) - return FALSE; - - struct cbox_adhoc_pattern *ap = cbox_adhoc_pattern_new(s->engine, CBOX_ARG_I(cmd, 2), pattern); - ap->master->tempo = ap->master->new_tempo = CBOX_ARG_F(cmd, 1); - cbox_scene_play_adhoc_pattern(s, ap); - return TRUE; - } - else - if (!strcmp(cmd->command, "/enable_default_song_input") && !strcmp(cmd->arg_types, "i")) - { - s->enable_default_song_input = CBOX_ARG_I(cmd, 0); - cbox_scene_update_connected_inputs(s); - return TRUE; - } - else - if (!strcmp(cmd->command, "/enable_default_external_input") && !strcmp(cmd->arg_types, "i")) - { - s->enable_default_external_input = CBOX_ARG_I(cmd, 0); - cbox_scene_update_connected_inputs(s); - return TRUE; - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); -} - -gboolean cbox_scene_load(struct cbox_scene *s, const char *name, GError **error) -{ - const char *cv = NULL; - int i; - gchar *section = g_strdup_printf("scene:%s", name); - - if (!cbox_config_has_section(section)) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "No config section for scene '%s'", name); - goto error; - } - - cbox_scene_clear(s); - - assert(s->layers == NULL); - assert(s->instruments == NULL); - assert(s->aux_buses == NULL); - assert(s->layer_count == 0); - assert(s->instrument_count == 0); - assert(s->aux_bus_count == 0); - - for (i = 1; ; i++) - { - struct cbox_layer *l = NULL; - - gchar *sn = g_strdup_printf("layer%d", i); - cv = cbox_config_get_string(section, sn); - g_free(sn); - - if (!cv) - break; - - l = cbox_layer_new_from_config(s, cv, error); - if (!l) - goto error; - - if (!cbox_scene_add_layer(s, l, error)) - goto error; - } - - s->transpose = cbox_config_get_int(section, "transpose", 0); - s->title = g_strdup(cbox_config_get_string_with_default(section, "title", "")); - g_free(section); - cbox_command_target_init(&s->cmd_target, cbox_scene_process_cmd, s); - s->name = g_strdup(name); - return TRUE; - -error: - g_free(section); - return FALSE; -} - -gboolean cbox_scene_insert_layer(struct cbox_scene *scene, struct cbox_layer *layer, int pos, GError **error) -{ - uint32_t i; - - struct cbox_instrument *instrument = layer->instrument; - - if (instrument) { - for (i = 0; i < instrument->aux_output_count; i++) - { - assert(!instrument->aux_outputs[i]); - if (instrument->aux_output_names[i]) - { - instrument->aux_outputs[i] = cbox_scene_get_aux_bus(scene, instrument->aux_output_names[i], TRUE, error); - if (!instrument->aux_outputs[i]) - return FALSE; - cbox_aux_bus_ref(instrument->aux_outputs[i]); - } - } - for (i = 0; i < scene->layer_count; i++) - { - if (scene->layers[i]->instrument == layer->instrument) - break; - } - if (i == scene->layer_count) - { - layer->instrument->scene = scene; - cbox_rt_array_insert(scene->rt, (void ***)&scene->instruments, &scene->instrument_count, -1, layer->instrument); - } - } - cbox_rt_array_insert(scene->rt, (void ***)&scene->layers, &scene->layer_count, pos, layer); - if (layer->external_output_set && scene->rt) - cbox_scene_update_connected_outputs(scene); - - return TRUE; -} - -gboolean cbox_scene_add_layer(struct cbox_scene *scene, struct cbox_layer *layer, GError **error) -{ - return cbox_scene_insert_layer(scene, layer, scene->layer_count, error); -} - -struct cbox_layer *cbox_scene_remove_layer(struct cbox_scene *scene, int pos) -{ - struct cbox_layer *removed = scene->layers[pos]; - cbox_rt_array_remove(scene->rt, (void ***)&scene->layers, &scene->layer_count, pos); - if (removed->instrument) - cbox_instrument_unref_aux_buses(removed->instrument); - if (removed->external_output_set) - cbox_scene_update_connected_outputs(scene); - - return removed; -} - -void cbox_scene_move_layer(struct cbox_scene *scene, unsigned int oldpos, unsigned int newpos) -{ - if (oldpos == newpos) - return; - struct cbox_layer **layers = malloc(sizeof(struct cbox_layer *) * scene->layer_count); - for (uint32_t i = 0; i < scene->layer_count; i++) - { - int s; - if (i == newpos) - s = oldpos; - else - { - if (oldpos < newpos) - s = (i < oldpos || i > newpos) ? i : i + 1; - else - s = (i < newpos || i > oldpos) ? i : i - 1; - } - layers[i] = scene->layers[s]; - } - free(cbox_rt_swap_pointers(scene->rt, (void **)&scene->layers, layers)); -} - -gboolean cbox_scene_remove_instrument(struct cbox_scene *scene, struct cbox_instrument *instrument) -{ - assert(instrument->scene == scene); - for (uint32_t pos = 0; pos < scene->instrument_count; pos++) - { - if (scene->instruments[pos] == instrument) - { - cbox_rt_array_remove(scene->rt, (void ***)&scene->instruments, &scene->instrument_count, pos); - g_hash_table_remove(scene->instrument_hash, instrument->module->instance_name); - instrument->scene = NULL; - return TRUE; - } - } - return FALSE; -} - -gboolean cbox_scene_insert_aux_bus(struct cbox_scene *scene, struct cbox_aux_bus *aux_bus) -{ - struct cbox_aux_bus **aux_buses = malloc(sizeof(struct cbox_aux_bus *) * (scene->aux_bus_count + 1)); - memcpy(aux_buses, scene->aux_buses, sizeof(struct cbox_aux_bus *) * (scene->aux_bus_count)); - aux_buses[scene->aux_bus_count] = aux_bus; - free(cbox_rt_swap_pointers_and_update_count(scene->rt, (void **)&scene->aux_buses, aux_buses, &scene->aux_bus_count, scene->aux_bus_count + 1)); - return TRUE; -} - -void cbox_scene_remove_aux_bus(struct cbox_scene *scene, struct cbox_aux_bus *removed) -{ - int pos = -1; - for (uint32_t i = 0; i < scene->aux_bus_count; i++) - { - if (scene->aux_buses[i] == removed) - { - pos = i; - break; - } - } - assert(pos != -1); - for (uint32_t i = 0; i < scene->instrument_count; i++) - cbox_instrument_disconnect_aux_bus(scene->instruments[i], removed); - - struct cbox_aux_bus **aux_buses = malloc(sizeof(struct cbox_aux_bus *) * (scene->aux_bus_count - 1)); - memcpy(aux_buses, scene->aux_buses, sizeof(struct cbox_aux_bus *) * pos); - memcpy(aux_buses + pos, scene->aux_buses + pos + 1, sizeof(struct cbox_aux_bus *) * (scene->aux_bus_count - pos - 1)); - free(cbox_rt_swap_pointers_and_update_count(scene->rt, (void **)&scene->aux_buses, aux_buses, &scene->aux_bus_count, scene->aux_bus_count - 1)); -} - -struct cbox_aux_bus *cbox_scene_get_aux_bus(struct cbox_scene *scene, const char *name, int allow_load, GError **error) -{ - for (uint32_t i = 0; i < scene->aux_bus_count; i++) - { - if (!strcmp(scene->aux_buses[i]->name, name)) - { - return scene->aux_buses[i]; - } - } - if (!allow_load) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Aux bus not found: %s", name); - return FALSE; - } - struct cbox_aux_bus *bus = cbox_aux_bus_load(scene, name, scene->rt, error); - if (!bus) - return NULL; - return bus; -} - -static int write_events_to_instrument_ports(struct cbox_scene *scene, struct cbox_midi_buffer *source) -{ - uint32_t i; - - for (i = 0; i < scene->instrument_count; i++) - cbox_midi_buffer_clear(&scene->instruments[i]->module->midi_input); - for (uint32_t l = 0; l < scene->layer_count; l++) - { - struct cbox_layer *lp = scene->layers[l]; - if (lp->external_output_set) - cbox_midi_buffer_clear(&lp->output_buffer); - } - - if (!source) - return 0; - - uint32_t event_count = cbox_midi_buffer_get_count(source); - for (i = 0; i < event_count; i++) - { - const struct cbox_midi_event *event = cbox_midi_buffer_get_event(source, i); - - // XXXKF ignore sysex for now - if (event->size >= 4) - continue; - - for (uint32_t l = 0; l < scene->layer_count; l++) - { - struct cbox_layer *lp = scene->layers[l]; - if (!lp->enabled) - continue; - if (!lp->external_output_set && !lp->instrument) - continue; - uint8_t data[4] = {0, 0, 0, 0}; - memcpy(data, event->data_inline, event->size); - if (data[0] < 0xF0) // per-channel messages - { - int cmd = data[0] >> 4; - // filter on MIDI channel - if (lp->in_channel >= 0 && lp->in_channel != (data[0] & 0x0F)) - continue; - // force output channel - if (lp->out_channel >= 0) - data[0] = (data[0] & 0xF0) + (lp->out_channel & 0x0F); - if (cmd >= 8 && cmd <= 10) - { - if (cmd == 10 && lp->disable_aftertouch) - continue; - // note filter - if (data[1] < lp->low_note || data[1] > lp->high_note) - continue; - // transpose - int transpose = lp->transpose + (lp->ignore_scene_transpose ? 0 : scene->transpose); - if (transpose) - { - int note = data[1] + transpose; - if (note < 0 || note > 127) - continue; - data[1] = (uint8_t)note; - } - // fixed note - if (lp->fixed_note != -1) - { - data[1] = (uint8_t)lp->fixed_note; - } - } - else if (cmd == 11 && data[1] == 64 && lp->invert_sustain) - { - data[2] = 127 - data[2]; - } - else if (lp->ignore_program_changes && cmd == 11 && (data[1] == 0 || data[1] == 32)) - continue; - else if (cmd == 13 && lp->disable_aftertouch) - continue; - else if (cmd == 12 && lp->ignore_program_changes) - continue; - } - struct cbox_midi_buffer *output = lp->instrument ? &lp->instrument->module->midi_input : &lp->output_buffer; - if (!cbox_midi_buffer_write_event(output, event->time, data, event->size)) - return -i; - if (lp->consume) - break; - } - } - - return event_count; -} - -void cbox_scene_update_connected_outputs(struct cbox_scene *scene) -{ - for (uint32_t l = 0; l < scene->layer_count; l++) - { - struct cbox_layer *lp = scene->layers[l]; - struct cbox_midi_merger *merger = NULL; - if (lp->external_output_set) - merger = cbox_rt_get_midi_output(scene->engine->rt, &lp->external_output); - if (merger != lp->external_merger) - { - if (lp->external_merger) - cbox_midi_merger_disconnect(lp->external_merger, &lp->output_buffer, scene->rt); - if (merger) - cbox_midi_merger_connect(merger, &lp->output_buffer, scene->rt, &lp->external_merger); - } - } -} - -void cbox_scene_render(struct cbox_scene *scene, uint32_t nframes, float *output_buffers[], uint32_t output_channels) -{ - uint32_t i, n; - - if (scene->rt && scene->rt->io) - { - struct cbox_io *io = scene->rt->io; - for (i = 0; i < io->io_env.input_count; i++) - { - if (IS_RECORDING_SOURCE_CONNECTED(scene->rec_mono_inputs[i])) - cbox_recording_source_push(&scene->rec_mono_inputs[i], (const float **)&io->input_buffers[i], 0, nframes); - } - for (i = 0; i < io->io_env.input_count / 2; i++) - { - if (IS_RECORDING_SOURCE_CONNECTED(scene->rec_stereo_inputs[i])) - { - const float *buf[2] = { io->input_buffers[i * 2], io->input_buffers[i * 2 + 1] }; - cbox_recording_source_push(&scene->rec_stereo_inputs[i], buf, 0, nframes); - } - } - } - - for(struct cbox_adhoc_pattern **ppat = &scene->adhoc_patterns; *ppat; ) - { - cbox_midi_buffer_clear(&(*ppat)->output_buffer); - if ((*ppat)->completed) - { - struct cbox_adhoc_pattern *retired = *ppat; - *ppat = retired->next; - retired->next = scene->retired_adhoc_patterns; - scene->retired_adhoc_patterns = retired; - } - else - { - cbox_adhoc_pattern_render((*ppat), 0, nframes); - ppat = &((*ppat)->next); - } - } - - cbox_midi_buffer_clear(&scene->midibuf_total); - cbox_midi_merger_render(&scene->scene_input_merger); - - write_events_to_instrument_ports(scene, &scene->midibuf_total); - - for (n = 0; n < scene->aux_bus_count; n++) - { - for (i = 0; i < nframes; i ++) - { - scene->aux_buses[n]->input_bufs[0][i] = 0.f; - scene->aux_buses[n]->input_bufs[1][i] = 0.f; - } - } - - for (n = 0; n < scene->instrument_count; n++) - { - struct cbox_instrument *instr = scene->instruments[n]; - struct cbox_module *module = instr->module; - int event_count = instr->module->midi_input.count; - int cur_event = 0; - uint32_t highwatermark = 0; - cbox_sample_t channels[CBOX_MAX_AUDIO_PORTS][CBOX_BLOCK_SIZE]; - cbox_sample_t *outputs[CBOX_MAX_AUDIO_PORTS]; - for (i= 0; i < module->outputs; i++) - outputs[i] = channels[i]; - - for (i = 0; i < nframes; i += CBOX_BLOCK_SIZE) - { - scene->engine->song_pos_offset = i; - if (i >= highwatermark) - { - while(cur_event < event_count) - { - const struct cbox_midi_event *event = cbox_midi_buffer_get_event(&module->midi_input, cur_event); - if (event) - { - if (event->time <= i) - (*module->process_event)(module, cbox_midi_event_get_data(event), event->size); - else - { - highwatermark = event->time; - break; - } - } - else - break; - - cur_event++; - } - } - (*module->process_block)(module, NULL, outputs); - for (uint32_t o = 0; o < module->outputs / 2; o++) - { - struct cbox_instrument_output *oobj = &instr->outputs[o]; - struct cbox_module *insert = oobj->insert; - struct cbox_gain *gain_obj = &oobj->gain_obj; - if (IS_RECORDING_SOURCE_CONNECTED(oobj->rec_dry)) - cbox_recording_source_push(&oobj->rec_dry, (const float **)(outputs + 2 * o), i, CBOX_BLOCK_SIZE); - if (insert && !insert->bypass) - (*insert->process_block)(insert, outputs + 2 * o, outputs + 2 * o); - if (IS_RECORDING_SOURCE_CONNECTED(oobj->rec_wet)) - cbox_recording_source_push(&oobj->rec_wet, (const float **)(outputs + 2 * o), i, CBOX_BLOCK_SIZE); - float *leftbuf, *rightbuf; - if (o < module->aux_offset / 2) - { - if (oobj->output_bus < 0) - continue; - uint32_t leftch = oobj->output_bus * 2; - if (leftch >= output_channels) - continue; - leftbuf = output_buffers[leftch]; - uint32_t rightch = leftch + 1; - rightbuf = rightch >= output_channels ? NULL : output_buffers[rightch]; - } - else - { - int bus = o - module->aux_offset / 2; - struct cbox_aux_bus *busobj = instr->aux_outputs[bus]; - if (busobj == NULL) - continue; - leftbuf = busobj->input_bufs[0]; - rightbuf = busobj->input_bufs[1]; - } - if (leftbuf && rightbuf) - { - cbox_gain_add_stereo(gain_obj, &leftbuf[i], channels[2 * o], &rightbuf[i], channels[2 * o + 1], CBOX_BLOCK_SIZE); - } - else - { - if (leftbuf) - cbox_gain_add_mono(gain_obj, &leftbuf[i], channels[2 * o], CBOX_BLOCK_SIZE); - if (rightbuf) - cbox_gain_add_mono(gain_obj, &rightbuf[i], channels[2 * o + 1], CBOX_BLOCK_SIZE); - } - } - } - while(cur_event < event_count) - { - const struct cbox_midi_event *event = cbox_midi_buffer_get_event(&module->midi_input, cur_event); - if (event) - { - (*module->process_event)(module, cbox_midi_event_get_data(event), event->size); - } - else - break; - - cur_event++; - } - } - - for (n = 0; n < scene->aux_bus_count; n++) - { - struct cbox_aux_bus *bus = scene->aux_buses[n]; - float left[CBOX_BLOCK_SIZE], right[CBOX_BLOCK_SIZE]; - float *outputs[2] = {left, right}; - for (i = 0; i < nframes; i += CBOX_BLOCK_SIZE) - { - float *inputs[2]; - inputs[0] = &bus->input_bufs[0][i]; - inputs[1] = &bus->input_bufs[1][i]; - bus->module->process_block(bus->module, inputs, outputs); - for (int j = 0; j < CBOX_BLOCK_SIZE; j++) - { - output_buffers[0][i + j] += left[j]; - output_buffers[1][i + j] += right[j]; - } - } - } - - uint32_t output_count = scene->engine->io_env.output_count; - // XXXKF this assumes that the buffers are zeroed on start - which isn't true if there are multiple scenes - for (i = 0; i < output_count; i++) - { - if (IS_RECORDING_SOURCE_CONNECTED(scene->rec_mono_outputs[i])) - cbox_recording_source_push(&scene->rec_mono_outputs[i], (const float **)&output_buffers[i], 0, nframes); - } - for (i = 0; i < output_count / 2; i++) - { - if (IS_RECORDING_SOURCE_CONNECTED(scene->rec_stereo_outputs[i])) - { - const float *buf[2] = { output_buffers[i * 2], output_buffers[i * 2 + 1] }; - cbox_recording_source_push(&scene->rec_stereo_outputs[i], buf, 0, nframes); - } - } -} - -void cbox_scene_clear(struct cbox_scene *scene) -{ - g_free(scene->name); - g_free(scene->title); - scene->name = g_strdup(""); - scene->title = g_strdup(""); - while(scene->layer_count > 0) - { - struct cbox_layer *layer = cbox_scene_remove_layer(scene, 0); - CBOX_DELETE(layer); - } - - while(scene->aux_bus_count > 0) - CBOX_DELETE(scene->aux_buses[scene->aux_bus_count - 1]); -} - -static struct cbox_instrument *create_instrument(struct cbox_scene *scene, struct cbox_module *module) -{ - int auxes = (module->outputs - module->aux_offset) / 2; - - struct cbox_instrument *instr = malloc(sizeof(struct cbox_instrument)); - CBOX_OBJECT_HEADER_INIT(instr, cbox_instrument, CBOX_GET_DOCUMENT(scene)); - instr->scene = scene; - instr->module = module; - instr->outputs = calloc(module->outputs / 2, sizeof(struct cbox_instrument_output)); - instr->refcount = 0; - instr->aux_outputs = calloc(auxes, sizeof(struct cbox_aux_bus *)); - instr->aux_output_names = calloc(auxes, sizeof(char *)); - instr->aux_output_count = auxes; - - for (uint32_t i = 0; i < module->outputs / 2; i ++) - cbox_instrument_output_init(&instr->outputs[i], scene, module->engine->io_env.buffer_size); - - return instr; -} - -struct cbox_instrument *cbox_scene_create_instrument(struct cbox_scene *scene, const char *instrument_name, const char *engine_name, GError **error) -{ - gpointer value = g_hash_table_lookup(scene->instrument_hash, instrument_name); - if (value) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Instrument already exists: '%s'", instrument_name); - return NULL; - } - - struct cbox_document *doc = CBOX_GET_DOCUMENT(scene); - struct cbox_module_manifest *mptr = NULL; - struct cbox_instrument *instr = NULL; - struct cbox_module *module = NULL; - - mptr = cbox_module_manifest_get_by_name(engine_name); - if (!mptr) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "No engine called '%s'", engine_name); - return NULL; - } - - module = cbox_module_manifest_create_module(mptr, NULL, doc, scene->rt, scene->engine, instrument_name, error); - if (!module) - { - cbox_force_error(error); - g_prefix_error(error, "Cannot create engine '%s' for instrument '%s': ", engine_name, instrument_name); - return NULL; - } - - instr = create_instrument(scene, module); - - cbox_command_target_init(&instr->cmd_target, cbox_instrument_process_cmd, instr); - g_hash_table_insert(scene->instrument_hash, g_strdup(instrument_name), instr); - CBOX_OBJECT_REGISTER(instr); - - return instr; -} - -struct cbox_instrument *cbox_scene_get_instrument_by_name(struct cbox_scene *scene, const char *name, gboolean load, GError **error) -{ - struct cbox_module_manifest *mptr = NULL; - struct cbox_instrument *instr = NULL; - struct cbox_module *module = NULL; - gchar *instr_section = NULL; - gpointer value = g_hash_table_lookup(scene->instrument_hash, name); - const char *cv, *instr_engine; - struct cbox_document *doc = CBOX_GET_DOCUMENT(scene); - assert(scene); - - if (value) - return value; - if (!load) - return NULL; - - instr_section = g_strdup_printf("instrument:%s", name); - - if (!cbox_config_has_section(instr_section)) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "No config section for instrument '%s'", name); - goto error; - } - - instr_engine = cbox_config_get_string(instr_section, "engine"); - if (!instr_engine) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Engine not specified in instrument '%s'", name); - goto error; - } - - mptr = cbox_module_manifest_get_by_name(instr_engine); - if (!mptr) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "No engine called '%s'", instr_engine); - goto error; - } - - // cbox_module_manifest_dump(mptr); - - module = cbox_module_manifest_create_module(mptr, instr_section, doc, scene->rt, scene->engine, name, error); - if (!module) - { - cbox_force_error(error); - g_prefix_error(error, "Cannot create engine '%s' for instrument '%s': ", instr_engine, name); - goto error; - } - - instr = create_instrument(scene, module); - - for (uint32_t i = 0; i < module->outputs / 2; i ++) - { - struct cbox_instrument_output *oobj = instr->outputs + i; - - gchar *key = i == 0 ? g_strdup("output_bus") : g_strdup_printf("output%d_bus", 1 + i); - oobj->output_bus = cbox_config_get_int(instr_section, key, 1) - 1; - g_free(key); - key = i == 0 ? g_strdup("gain") : g_strdup_printf("gain%d", 1 + i); - cbox_gain_set_db(&oobj->gain_obj, cbox_config_get_float(instr_section, key, 0)); - g_free(key); - - key = i == 0 ? g_strdup("insert") : g_strdup_printf("insert%d", 1 + i); - cv = cbox_config_get_string(instr_section, key); - g_free(key); - - if (cv) - { - oobj->insert = cbox_module_new_from_fx_preset(cv, CBOX_GET_DOCUMENT(scene), module->rt, scene->engine, error); - if (!oobj->insert) - { - cbox_force_error(error); - g_prefix_error(error, "Cannot instantiate effect preset '%s' for instrument '%s': ", cv, name); - } - } - } - - for (uint32_t i = 0; i < instr->aux_output_count; i++) - { - instr->aux_outputs[i] = NULL; - - gchar *key = g_strdup_printf("aux%d", 1 + i); - gchar *value = cbox_config_get_string(instr_section, key); - instr->aux_output_names[i] = value ? g_strdup(value) : NULL; - g_free(key); - - } - cbox_command_target_init(&instr->cmd_target, cbox_instrument_process_cmd, instr); - - free(instr_section); - - g_hash_table_insert(scene->instrument_hash, g_strdup(name), instr); - CBOX_OBJECT_REGISTER(instr); - - // cbox_recording_source_attach(&instr->outputs[0].rec_dry, cbox_recorder_new_stream("output.wav")); - - return instr; - -error: - free(instr_section); - return NULL; -} - -static struct cbox_recording_source *create_rec_sources(struct cbox_scene *scene, int buffer_size, int count, int channels) -{ - struct cbox_recording_source *s = malloc(sizeof(struct cbox_recording_source) * count); - for (int i = 0; i < count; i++) - cbox_recording_source_init(&s[i], scene, buffer_size, channels); - return s; -} - -static void destroy_rec_sources(struct cbox_recording_source *s, int count) -{ - for (int i = 0; i < count; i++) - cbox_recording_source_uninit(&s[i]); - free(s); -} - -struct cbox_scene *cbox_scene_new(struct cbox_document *document, struct cbox_engine *engine) -{ - if (!engine->io_env.buffer_size) - return NULL; - - struct cbox_scene *s = malloc(sizeof(struct cbox_scene)); - if (!s) - return NULL; - - CBOX_OBJECT_HEADER_INIT(s, cbox_scene, document); - s->engine = engine; - s->rt = engine ? engine->rt : NULL; - s->instrument_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); - s->name = g_strdup(""); - s->title = g_strdup(""); - s->layers = NULL; - s->aux_buses = NULL; - s->instruments = NULL; - s->layer_count = 0; - s->instrument_count = 0; - s->aux_bus_count = 0; - cbox_command_target_init(&s->cmd_target, cbox_scene_process_cmd, s); - s->transpose = 0; - s->connected_inputs = NULL; - s->connected_input_count = 0; - s->enable_default_song_input = TRUE; - s->enable_default_external_input = TRUE; - - cbox_midi_buffer_init(&s->midibuf_total); - cbox_midi_merger_init(&s->scene_input_merger, &s->midibuf_total); - - int buffer_size = engine->io_env.buffer_size; - s->rec_mono_inputs = create_rec_sources(s, buffer_size, engine->io_env.input_count, 1); - s->rec_stereo_inputs = create_rec_sources(s, buffer_size, engine->io_env.input_count / 2, 2); - s->rec_mono_outputs = create_rec_sources(s, buffer_size, engine->io_env.output_count, 1); - s->rec_stereo_outputs = create_rec_sources(s, buffer_size, engine->io_env.output_count / 2, 2); - s->adhoc_patterns = NULL; - s->retired_adhoc_patterns = NULL; - - CBOX_OBJECT_REGISTER(s); - - cbox_engine_add_scene(s->engine, s); - cbox_scene_update_connected_inputs(s); - return s; -} - -void cbox_scene_update_connected_inputs(struct cbox_scene *scene) -{ - if (!scene->rt || !scene->rt->io) - return; - - // This is called when a MIDI Input port has been created, connected/disconnected - // or is about to be removed (and then the removing flag will be set) - for (uint32_t i = 0; i < scene->connected_input_count; ) - { - struct cbox_midi_input *input = scene->connected_inputs[i]; - if (input->removing || !cbox_uuid_equal(&input->output, &scene->_obj_hdr.instance_uuid)) - { - cbox_midi_merger_disconnect(&scene->scene_input_merger, &input->buffer, scene->rt); - cbox_rt_array_remove(scene->rt, (void ***)&scene->connected_inputs, &scene->connected_input_count, i); - } - else - i++; - } - for (GSList *p = scene->rt->io->midi_inputs; p; p = p->next) - { - struct cbox_midi_input *input = p->data; - if (cbox_uuid_equal(&input->output, &scene->_obj_hdr.instance_uuid)) - { - gboolean found = FALSE; - for (uint32_t i = 0; i < scene->connected_input_count; i++) - { - if (scene->connected_inputs[i] == input) - { - found = TRUE; - break; - } - } - if (!found) - { - cbox_midi_merger_connect(&scene->scene_input_merger, &input->buffer, scene->rt, NULL); - cbox_rt_array_insert(scene->rt, (void ***)&scene->connected_inputs, &scene->connected_input_count, -1, input); - } - } - } - if (scene->enable_default_song_input) - { - cbox_midi_merger_connect(&scene->scene_input_merger, &scene->engine->midibuf_aux, scene->rt, NULL); - cbox_midi_merger_connect(&scene->scene_input_merger, &scene->engine->midibuf_song, scene->rt, NULL); - } - else - { - cbox_midi_merger_disconnect(&scene->scene_input_merger, &scene->engine->midibuf_aux, scene->rt); - cbox_midi_merger_disconnect(&scene->scene_input_merger, &scene->engine->midibuf_song, scene->rt); - } - - if (scene->enable_default_external_input) - cbox_midi_merger_connect(&scene->scene_input_merger, &scene->engine->midibuf_jack, scene->rt, NULL); - else - cbox_midi_merger_disconnect(&scene->scene_input_merger, &scene->engine->midibuf_jack, scene->rt); - -} - -static void free_adhoc_pattern_list(struct cbox_scene *scene, struct cbox_adhoc_pattern *ap) -{ - while(ap) - { - struct cbox_adhoc_pattern *next = ap->next; - ap->next = NULL; - cbox_midi_merger_disconnect(&scene->scene_input_merger, &ap->output_buffer, scene->rt); - cbox_adhoc_pattern_destroy(ap); - ap = next; - } -} - -struct play_adhoc_pattern_arg -{ - struct cbox_scene *scene; - struct cbox_adhoc_pattern *ap; - struct cbox_adhoc_pattern *retired; -}; - -static int play_adhoc_pattern_execute(void *arg_) -{ - struct play_adhoc_pattern_arg *arg = arg_; - - if (arg->ap) - { - struct cbox_adhoc_pattern *ap = arg->scene->adhoc_patterns; - - // If there is already an adhoc pattern with a given non-zero id, stop it - // and release all the pending notes. Retry until all the notes are - // released. - if (arg->ap->id) - { - while(ap && ap->id != arg->ap->id) - ap = ap->next; - if (ap) - { - ap->completed = TRUE; - if (ap->active_notes.channels_active) - return 0; - } - } - - arg->ap->next = arg->scene->adhoc_patterns; - arg->scene->adhoc_patterns = arg->ap; - } - arg->retired = arg->scene->retired_adhoc_patterns; - arg->scene->retired_adhoc_patterns = NULL; - // XXXKF should convert pattern length into sample position instead of assuming 0x7FFFFFFF (though it likely doesn't matter) - cbox_midi_clip_playback_set_pattern(&arg->ap->playback, arg->ap->pattern_playback, 0, 0x7FFFFFFF, 0, 0); - return 1; -} - -void cbox_scene_play_adhoc_pattern(struct cbox_scene *scene, struct cbox_adhoc_pattern *ap) -{ - static struct cbox_rt_cmd_definition cmd = { NULL, play_adhoc_pattern_execute, NULL }; - struct play_adhoc_pattern_arg arg = { scene, ap, NULL }; - cbox_midi_merger_connect(&scene->scene_input_merger, &ap->output_buffer, scene->rt, NULL); - cbox_rt_execute_cmd_sync(scene->rt, &cmd, &arg); - if (arg.retired) - free_adhoc_pattern_list(scene, arg.retired); -} - -gboolean cbox_scene_move_instrument_to(struct cbox_scene *scene, struct cbox_instrument *instrument, struct cbox_scene *new_scene, int dstpos, GError **error) -{ - int lcount = 0; - if (dstpos == -1) - dstpos = new_scene->layer_count; - for (uint32_t i = 0; i < scene->layer_count; i++) - { - if (scene->layers[i]->instrument == instrument) - lcount++; - } - if (!lcount) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Instrument '%s' not found in source scene", instrument->module->instance_name); - return FALSE; - } - if (cbox_scene_get_instrument_by_name(new_scene, instrument->module->instance_name, FALSE, NULL)) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Instrument '%s' already exists in target scene", instrument->module->instance_name); - return FALSE; - } - - struct cbox_layer **new_src_layers = malloc(sizeof(struct cbox_layer *) * (scene->layer_count - lcount)); - struct cbox_layer **new_dst_layers = malloc(sizeof(struct cbox_layer *) * (new_scene->layer_count + lcount)); - int srcidx = 0, dstidx = 0; - memcpy(&new_dst_layers[dstidx], new_scene->layers, dstpos * sizeof(struct cbox_layer **)); - dstidx = dstpos; - for (uint32_t i = 0; i < scene->layer_count; i++) - { - if (scene->layers[i]->instrument != instrument) - new_src_layers[srcidx++] = scene->layers[i]; - else - new_dst_layers[dstidx++] = scene->layers[i]; - } - memcpy(&new_dst_layers[dstidx], new_scene->layers, (new_scene->layer_count - dstpos) * sizeof(struct cbox_layer **)); - dstidx += new_scene->layer_count; - - free(cbox_rt_swap_pointers_and_update_count(scene->rt, (void **)&scene->layers, new_src_layers, &scene->layer_count, srcidx)); - cbox_rt_array_remove_by_value(scene->rt, (void ***)&scene->instruments, &scene->instrument_count, instrument); - - cbox_rt_array_insert(scene->rt, (void ***)&new_scene->instruments, &new_scene->instrument_count, -1, instrument); - free(cbox_rt_swap_pointers_and_update_count(new_scene->rt, (void **)&new_scene->layers, new_dst_layers, &new_scene->layer_count, dstidx)); - - return TRUE; -} - -static void cbox_scene_destroyfunc(struct cbox_objhdr *objhdr) -{ - struct cbox_scene *scene = CBOX_H2O(objhdr); - cbox_midi_merger_disconnect(&scene->scene_input_merger, &scene->engine->midibuf_aux, scene->rt); - cbox_midi_merger_disconnect(&scene->scene_input_merger, &scene->engine->midibuf_jack, scene->rt); - cbox_midi_merger_disconnect(&scene->scene_input_merger, &scene->engine->midibuf_song, scene->rt); - cbox_engine_remove_scene(scene->engine, scene); - cbox_scene_clear(scene); - g_free(scene->name); - g_free(scene->title); - assert(scene->instrument_count == 0); - free(scene->layers); - free(scene->aux_buses); - free(scene->instruments); - g_hash_table_destroy(scene->instrument_hash); - free(scene->connected_inputs); - - destroy_rec_sources(scene->rec_mono_inputs, scene->engine->io_env.input_count); - destroy_rec_sources(scene->rec_stereo_inputs, scene->engine->io_env.input_count / 2); - destroy_rec_sources(scene->rec_mono_outputs, scene->engine->io_env.output_count); - destroy_rec_sources(scene->rec_stereo_outputs, scene->engine->io_env.output_count / 2); - - free_adhoc_pattern_list(scene, scene->retired_adhoc_patterns); - free_adhoc_pattern_list(scene, scene->adhoc_patterns); - cbox_midi_merger_close(&scene->scene_input_merger, scene->engine->rt); - free(scene); -} diff --git a/template/calfbox/scene.h b/template/calfbox/scene.h deleted file mode 100644 index 5b9174c..0000000 --- a/template/calfbox/scene.h +++ /dev/null @@ -1,82 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_SCENE_H -#define CBOX_SCENE_H - -#include "cmd.h" -#include "dom.h" -#include "mididest.h" - -CBOX_EXTERN_CLASS(cbox_scene) - -struct cbox_aux_bus; -struct cbox_instrument; -struct cbox_midi_buffer; -struct cbox_recording_source; -struct cbox_song_playback; - -struct cbox_scene -{ - CBOX_OBJECT_HEADER() - struct cbox_command_target cmd_target; - gchar *name; - gchar *title; - - GHashTable *instrument_hash; - struct cbox_rt *rt; - struct cbox_layer **layers; - uint32_t layer_count; - struct cbox_instrument **instruments; - uint32_t instrument_count; - struct cbox_aux_bus **aux_buses; - uint32_t aux_bus_count; - int transpose; - struct cbox_engine *engine; - struct cbox_midi_merger scene_input_merger; - struct cbox_midi_buffer midibuf_total; - - struct cbox_midi_input **connected_inputs; - uint32_t connected_input_count; - - gboolean enable_default_song_input, enable_default_external_input; - - struct cbox_recording_source *rec_mono_inputs, *rec_mono_outputs; - struct cbox_recording_source *rec_stereo_inputs, *rec_stereo_outputs; - - struct cbox_adhoc_pattern *adhoc_patterns, *retired_adhoc_patterns; -}; - -extern struct cbox_scene *cbox_scene_new(struct cbox_document *document, struct cbox_engine *engine); -extern gboolean cbox_scene_add_layer(struct cbox_scene *scene, struct cbox_layer *layer, GError **error); -extern gboolean cbox_scene_insert_layer(struct cbox_scene *scene, struct cbox_layer *layer, int pos, GError **error); -extern struct cbox_layer *cbox_scene_remove_layer(struct cbox_scene *scene, int pos); -extern void cbox_scene_move_layer(struct cbox_scene *scene, unsigned int oldpos, unsigned int newpos); -extern gboolean cbox_scene_load(struct cbox_scene *scene, const char *section, GError **error); -extern gboolean cbox_scene_remove_instrument(struct cbox_scene *scene, struct cbox_instrument *instrument); -extern struct cbox_aux_bus *cbox_scene_get_aux_bus(struct cbox_scene *scene, const char *name, int allow_load, GError **error); -extern void cbox_scene_render(struct cbox_scene *scene, uint32_t nframes, float *output_buffers[], uint32_t output_channels); -extern void cbox_scene_clear(struct cbox_scene *scene); -extern void cbox_scene_update_connected_inputs(struct cbox_scene *scene); -extern void cbox_scene_update_connected_outputs(struct cbox_scene *scene); -extern gboolean cbox_scene_move_instrument_to(struct cbox_scene *scene, struct cbox_instrument *instrument, struct cbox_scene *new_scene, int dstpos, GError **error); -extern struct cbox_instrument *cbox_scene_get_instrument_by_name(struct cbox_scene *scene, const char *name, gboolean load, GError **error); -extern struct cbox_instrument *cbox_scene_create_instrument(struct cbox_scene *scene, const char *instrument_name, const char *engine_name, GError **error); -extern void cbox_scene_play_adhoc_pattern(struct cbox_scene *scene, struct cbox_adhoc_pattern *ap); - -#endif diff --git a/template/calfbox/scripting.c b/template/calfbox/scripting.c deleted file mode 100644 index d4f273d..0000000 --- a/template/calfbox/scripting.c +++ /dev/null @@ -1,579 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config.h" - -#include "app.h" -#include "blob.h" -#include "engine.h" -#include "errors.h" -#include "module.h" -#include "scripting.h" -#include -#include - -#include "config-api.h" -#include "tarfile.h" -#include "wavebank.h" -#include "scene.h" - -static gboolean audio_running = FALSE; -static gboolean engine_initialised = FALSE; - -gboolean cbox_embed_init_engine(const char *config_file, GError **error) -{ - if (engine_initialised) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Engine already initialized"); - return FALSE; - } - - cbox_dom_init(); - app.tarpool = cbox_tarpool_new(); - app.document = cbox_document_new(); - app.rt = cbox_rt_new(app.document); - app.engine = cbox_engine_new(app.document, app.rt); - app.rt->engine = app.engine; - cbox_config_init(config_file); - cbox_wavebank_init(); - engine_initialised = 1; - - return TRUE; -} - -gboolean cbox_embed_shutdown_engine(GError **error) -{ - if (!engine_initialised) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Engine not initialized"); - return FALSE; - } - - CBOX_DELETE(app.engine); - CBOX_DELETE(app.rt); - cbox_tarpool_destroy(app.tarpool); - cbox_document_destroy(app.document); - cbox_wavebank_close(); - cbox_config_close(); - cbox_dom_close(); - engine_initialised = FALSE; - - return TRUE; -} - -gboolean cbox_embed_start_audio(struct cbox_command_target *target, GError **error) -{ - if (!engine_initialised) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Engine not initialized"); - return FALSE; - } - if (audio_running) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Audio already started"); - return FALSE; - } - - struct cbox_open_params params; - - GError *error2 = NULL; - if (!cbox_io_init(&app.io, ¶ms, target, &error2)) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot initialise sound I/O: %s", (error2 && error2->message ? error2->message : "Unknown error")); - g_error_free(error2); - return FALSE; - } - - const char *effect_preset_name = cbox_config_get_string("master", "effect"); - - cbox_rt_set_io(app.rt, &app.io); - cbox_scene_new(app.document, app.engine); - cbox_rt_start(app.rt, target); - if (effect_preset_name && *effect_preset_name) - { - app.engine->effect = cbox_module_new_from_fx_preset(effect_preset_name, app.document, app.rt, app.engine, error); - if (!app.engine->effect) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot load master effect preset %s: %s", effect_preset_name, (error2 && error2->message ? error2->message : "Unknown error")); - g_error_free(error2); - return FALSE; - } - } - audio_running = TRUE; - return TRUE; -} - -gboolean cbox_embed_stop_audio(GError **error) -{ - if (!engine_initialised) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Engine not initialised"); - return FALSE; - } - if (!audio_running) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Audio not running"); - return FALSE; - } - - while(app.engine->scene_count > 0) - CBOX_DELETE(app.engine->scenes[0]); - cbox_rt_stop(app.rt); - cbox_io_close(&app.io); - audio_running = FALSE; - return TRUE; -} - -struct cbox_command_target *cbox_embed_get_cmd_root() -{ - return &app.cmd_target; -} - -#if USE_PYTHON - -// This is a workaround for what I consider a defect in pyconfig.h -#undef _XOPEN_SOURCE -#undef _POSIX_C_SOURCE - -#include - -struct PyCboxCallback -{ - PyObject_HEAD - struct cbox_command_target *target; -}; - -static PyObject * -PyCboxCallback_New(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - struct PyCboxCallback *self; - - self = (struct PyCboxCallback *)type->tp_alloc(type, 0); - if (self != NULL) { - self->target = NULL; - } - - return (PyObject *)self; -} - -static int -PyCboxCallback_Init(struct PyCboxCallback *self, PyObject *args, PyObject *kwds) -{ - PyObject *cobj = NULL; - if (!PyArg_ParseTuple(args, "O!:init", &PyCapsule_Type, &cobj)) - return -1; - - self->target = PyCapsule_GetPointer(cobj, NULL); - return 0; -} - -static PyObject *cbox_python_do_cmd_on(struct cbox_command_target *ct, PyObject *self, PyObject *args); - -static PyObject * -PyCboxCallback_Call(PyObject *_self, PyObject *args, PyObject *kwds) -{ - struct PyCboxCallback *self = (struct PyCboxCallback *)_self; - - return cbox_python_do_cmd_on(self->target, _self, args); -} - -PyTypeObject CboxCallbackType = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "_cbox.Callback", - .tp_basicsize = sizeof(struct PyCboxCallback), - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = "Callback for feedback channel to Cbox C code", - .tp_init = (initproc)PyCboxCallback_Init, - .tp_new = PyCboxCallback_New, - .tp_call = PyCboxCallback_Call -}; - -static gboolean set_error_from_python(GError **error) -{ - PyObject *ptype = NULL, *pvalue = NULL, *ptraceback = NULL; - PyErr_Fetch(&ptype, &pvalue, &ptraceback); - PyObject *ptypestr = PyObject_Str(ptype); - PyObject *pvaluestr = PyObject_Str(pvalue); - PyObject *ptypestr_unicode = PyUnicode_AsUTF8String(ptypestr); - PyObject *pvaluestr_unicode = PyUnicode_AsUTF8String(pvaluestr); - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "%s: %s", PyBytes_AsString(ptypestr_unicode), PyBytes_AsString(pvaluestr_unicode)); - Py_DECREF(pvaluestr_unicode); - Py_DECREF(ptypestr_unicode); - //g_error("%s:%s", PyString_AsString(ptypestr), PyString_AsString(pvaluestr)); - Py_DECREF(ptypestr); - Py_DECREF(pvaluestr); - Py_DECREF(ptype); - Py_XDECREF(pvalue); - Py_XDECREF(ptraceback); - return FALSE; -} - -static gboolean bridge_to_python_callback(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - PyObject *callback = ct->user_data; - - int argc = strlen(cmd->arg_types); - PyObject *arg_values = PyList_New(argc); - for (int i = 0; i < argc; i++) - { - if (cmd->arg_types[i] == 's') - { - PyList_SetItem(arg_values, i, PyUnicode_FromString(cmd->arg_values[i])); - } - else - if (cmd->arg_types[i] == 'o') - { - struct cbox_objhdr *oh = cmd->arg_values[i]; - char buf[40]; - cbox_uuid_tostring(&oh->instance_uuid, buf); - PyList_SetItem(arg_values, i, PyUnicode_FromString(buf)); - } - else - if (cmd->arg_types[i] == 'u') - { - struct cbox_uuid *uuid = cmd->arg_values[i]; - char buf[40]; - cbox_uuid_tostring(uuid, buf); - PyList_SetItem(arg_values, i, PyUnicode_FromString(buf)); - } - else - if (cmd->arg_types[i] == 'i') - { - PyList_SetItem(arg_values, i, PyLong_FromLong(*(int *)cmd->arg_values[i])); - } - else - if (cmd->arg_types[i] == 'f') - { - PyList_SetItem(arg_values, i, PyFloat_FromDouble(*(double *)cmd->arg_values[i])); - } - else - if (cmd->arg_types[i] == 'b') - { - struct cbox_blob *blob = cmd->arg_values[i]; - PyList_SetItem(arg_values, i, PyByteArray_FromStringAndSize(blob->data, blob->size)); - } - else - { - PyList_SetItem(arg_values, i, Py_None); - Py_INCREF(Py_None); - } - } - struct PyCboxCallback *fbcb = NULL; - - PyObject *args = PyTuple_New(3); - PyTuple_SetItem(args, 0, PyUnicode_FromString(cmd->command)); - PyObject *pyfb = NULL; - if (fb) - { - struct PyCboxCallback *fbcb = PyObject_New(struct PyCboxCallback, &CboxCallbackType); - fbcb->target = fb; - pyfb = (PyObject *)fbcb; - } - else - { - pyfb = Py_None; - Py_INCREF(Py_None); - } - PyTuple_SetItem(args, 1, pyfb); - PyTuple_SetItem(args, 2, arg_values); - - PyObject *result = PyObject_Call(callback, args, NULL); - Py_DECREF(args); - - if (fbcb) - fbcb->target = NULL; - - if (result) - { - Py_DECREF(result); - return TRUE; - } - - return set_error_from_python(error); -} - -static PyObject *cbox_python_do_cmd_on(struct cbox_command_target *ct, PyObject *self, PyObject *args) -{ - const char *command = NULL; - PyObject *callback = NULL; - PyObject *list = NULL; - if (!PyArg_ParseTuple(args, "sOO!:do_cmd", &command, &callback, &PyList_Type, &list)) - return NULL; - - int len = PyList_Size(list); - void *extra = malloc(len * sizeof(double)); - struct cbox_osc_command cmd; - GError *error = NULL; - char *arg_types = malloc(len + 1); - void **arg_values = malloc(2 * len * sizeof(void *)); - void **arg_extra = &arg_values[len]; - cmd.command = command; - cmd.arg_types = arg_types; - cmd.arg_values = arg_values; - double *arg_space = extra; - gboolean free_blobs = FALSE; - for (int i = 0; i < len; i++) - { - cmd.arg_values[i] = &arg_space[i]; - PyObject *value = PyList_GetItem(list, i); - - if (PyLong_Check(value)) - { - arg_types[i] = 'i'; - *(int *)arg_values[i] = PyLong_AsLong(value); - } - else - if (PyFloat_Check(value)) - { - arg_types[i] = 'f'; - *(double *)arg_values[i] = PyFloat_AsDouble(value); - } - else - if (PyUnicode_Check(value)) - { - PyObject *utf8str = PyUnicode_AsUTF8String(value); - arg_types[i] = 's'; - arg_extra[i] = utf8str; - arg_values[i] = PyBytes_AsString(utf8str); - free_blobs = TRUE; - } - else - if (PyByteArray_Check(value)) - { - const void *buf = PyByteArray_AsString(value); - ssize_t len = PyByteArray_Size(value); - - if (buf) - { - // note: this is not really acquired, the blob is freed using free and not cbox_blob_destroy - struct cbox_blob *blob = cbox_blob_new_acquire_data((void *)buf, len); - arg_types[i] = 'b'; - arg_values[i] = blob; - free_blobs = TRUE; - } - else - arg_types[i] = 'N'; - } - else - { - PyObject *ob_type = (PyObject *)value->ob_type; - PyObject *typename_unicode = PyObject_Str(ob_type); - PyObject *typename_bytes = PyUnicode_AsUTF8String(typename_unicode); - PyObject *exc = PyErr_Format(PyExc_ValueError, "Cannot decode Python type '%s' to execute '%s'", PyBytes_AsString(typename_bytes), command); - Py_DECREF(typename_bytes); - Py_DECREF(typename_unicode); - - return exc; - } - } - arg_types[len] = '\0'; - - struct cbox_command_target target; - cbox_command_target_init(&target, bridge_to_python_callback, callback); - - // cbox_osc_command_dump(&cmd); - Py_INCREF(callback); - gboolean result = ct->process_cmd(ct, callback != Py_None ? &target : NULL, &cmd, &error); - Py_DECREF(callback); - - if (free_blobs) - { - for (int i = 0; i < len; i++) - { - if (arg_types[i] == 'b') - free(arg_values[i]); - if (arg_types[i] == 's') - Py_DECREF((PyObject *)arg_extra[i]); - } - } - free(arg_space); - free(arg_values); - free(arg_types); - - if (!result) - return PyErr_Format(PyExc_Exception, "%s", error ? error->message : "Unknown error"); - - Py_RETURN_NONE; -} - -static PyObject *cbox_python_do_cmd(PyObject *self, PyObject *args) -{ - if (!engine_initialised) - return PyErr_Format(PyExc_Exception, "Engine not initialised"); - return cbox_python_do_cmd_on(&app.cmd_target, self, args); -} - -#if CALFBOX_AS_MODULE - -#include "config-api.h" -#include "tarfile.h" -#include "wavebank.h" -#include "scene.h" - -static PyObject *pyerror_from_gerror(PyObject *exception, GError *error) -{ - PyObject *pyerr = PyErr_Format(exception, "%s", error ? error->message : "Unknown error"); - g_error_free(error); - return pyerr; -} - -static PyObject *cbox_python_init_engine(PyObject *self, PyObject *args) -{ - const char *config_file = NULL; - if (!PyArg_ParseTuple(args, "|z:init_engine", &config_file)) - return NULL; - - GError *error = NULL; - if (!cbox_embed_init_engine(config_file, &error)) - return pyerror_from_gerror(PyExc_Exception, error); - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject *cbox_python_shutdown_engine(PyObject *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":shutdown_engine")) - return NULL; - - GError *error = NULL; - if (!cbox_embed_shutdown_engine(&error)) - return pyerror_from_gerror(PyExc_Exception, error); - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject *cbox_python_start_audio(PyObject *self, PyObject *args) -{ - PyObject *callback = NULL; - if (!PyArg_ParseTuple(args, "|O:start_audio", &callback)) - return NULL; - - struct cbox_command_target target; - gboolean has_target = callback && callback != Py_None; - if (has_target) - cbox_command_target_init(&target, bridge_to_python_callback, callback); - - GError *error = NULL; - if (!cbox_embed_start_audio(has_target ? &target : NULL, &error)) - return pyerror_from_gerror(PyExc_Exception, error); - - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject *cbox_python_start_noaudio(PyObject *self, PyObject *args) -{ - PyObject *callback = NULL; - int sample_rate = 0; - if (!PyArg_ParseTuple(args, "i|O:start_noaudio", &sample_rate, &callback)) - return NULL; - if (!engine_initialised) - return PyErr_Format(PyExc_Exception, "Engine not initialised"); - if (audio_running) - return PyErr_Format(PyExc_Exception, "Audio already started"); - - struct cbox_command_target target; - if (callback && callback != Py_None) - cbox_command_target_init(&target, bridge_to_python_callback, callback); - - cbox_rt_set_offline(app.rt, sample_rate, 1024); - cbox_scene_new(app.document, app.engine); - cbox_rt_start(app.rt, (callback && callback != Py_None) ? &target : NULL); - audio_running = TRUE; - - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject *cbox_python_stop_audio(PyObject *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":stop_audio")) - return NULL; - - GError *error = NULL; - if (!cbox_embed_stop_audio(&error)) - return pyerror_from_gerror(PyExc_Exception, error); - Py_INCREF(Py_None); - return Py_None; -} - -#endif - -static PyMethodDef CboxMethods[] = { - {"do_cmd", cbox_python_do_cmd, METH_VARARGS, "Execute a CalfBox command using a global path."}, -#if CALFBOX_AS_MODULE - {"init_engine", cbox_python_init_engine, METH_VARARGS, "Initialise the CalfBox engine using optional config file."}, - {"shutdown_engine", cbox_python_shutdown_engine, METH_VARARGS, "Shutdown the CalfBox engine."}, - {"start_audio", cbox_python_start_audio, METH_VARARGS, "Start real-time audio processing using I/O settings from the current config."}, - {"start_noaudio", cbox_python_start_noaudio, METH_VARARGS, "Start dummy audio processing using sample rate specified as argument."}, - {"stop_audio", cbox_python_stop_audio, METH_VARARGS, "Stop real-time audio processing."}, -#endif - {NULL, NULL, 0, NULL} -}; - -static PyModuleDef CboxModule = { - PyModuleDef_HEAD_INIT, "_cbox", NULL, -1, CboxMethods, - NULL, NULL, NULL, NULL -}; - -#if CALFBOX_AS_MODULE - -static void cbox_python_atexit() -{ - if (audio_running) { - cbox_rt_stop(app.rt); - cbox_io_close(&app.io); - audio_running = FALSE; - } - if (engine_initialised) { - cbox_tarpool_destroy(app.tarpool); - cbox_document_destroy(app.document); - cbox_wavebank_close(); - cbox_config_close(); - cbox_dom_close(); - engine_initialised = FALSE; - } -} - -PyMODINIT_FUNC -PyInit__cbox(void) -{ - PyObject *m = PyModule_Create(&CboxModule); - if (!m) - return NULL; - Py_INCREF(&CboxCallbackType); - if (PyType_Ready(&CboxCallbackType) < 0) - return NULL; - PyModule_AddObject(m, "Callback", (PyObject *)&CboxCallbackType); - atexit(cbox_python_atexit); - - return m; -} - -#else - -PyObject* -PyInit_cbox(void) -{ - PyObject *m = PyModule_Create(&CboxModule); - PyModule_AddObject(m, "Callback", (PyObject *)&CboxCallbackType); - return m; -} - -#endif -#endif diff --git a/template/calfbox/scripting.h b/template/calfbox/scripting.h deleted file mode 100644 index ff93271..0000000 --- a/template/calfbox/scripting.h +++ /dev/null @@ -1,19 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -extern void cbox_script_run(const char *name); diff --git a/template/calfbox/send_pattern_to_midi_out_example.py b/template/calfbox/send_pattern_to_midi_out_example.py deleted file mode 100644 index d6412f0..0000000 --- a/template/calfbox/send_pattern_to_midi_out_example.py +++ /dev/null @@ -1,39 +0,0 @@ -#! /usr/bin/env python3 -# -*- coding: utf-8 -*- - -from calfbox import cbox - -def cmd_dumper(cmd, fb, args): - print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) - -cbox.init_engine() -cbox.start_audio(cmd_dumper) - -outportname = "CboxSendPattern" -cboxMidiOutUuid = cbox.JackIO.create_midi_output(outportname) #Add a named midi out port -cbox.JackIO.rename_midi_output(cboxMidiOutUuid, outportname) #For good measure. -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. -outputScene.clear() #For good measure. -outputScene.add_new_midi_layer(cboxMidiOutUuid) #Connect the scene to our midi output port. Without this there will be no midi out. - - -# Send 8 pitches 0x90 with velocity 0 -# Create a binary blob that contains the MIDI events -pblob = bytes() -for pitch in range(0,8): - # note on - pblob += cbox.Pattern.serialize_event(1, 0x90, pitch, 0) #tick in pattern, midi, pitch, velocity -# Create a new pattern object using events from the blob -allNoteOnZeroPattern = cbox.Document.get_song().pattern_from_blob(pblob, 0) #0 ticks. - - -print ("\nThis example sends midi events from a pattern without any tracks. Rolling transport or not doesn't matter.") -print("Ready!") -counter = 0 #To add delay -while True: - cbox.call_on_idle(cmd_dumper) - if counter > 10**5 * 4 : - print ("Send pattern") - outputScene.play_pattern(allNoteOnZeroPattern, 150.0) #150 tempo - counter = 0 - counter += 1 diff --git a/template/calfbox/seq-adhoc.c b/template/calfbox/seq-adhoc.c deleted file mode 100644 index 73aab57..0000000 --- a/template/calfbox/seq-adhoc.c +++ /dev/null @@ -1,56 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "master.h" -#include "seq.h" - -struct cbox_adhoc_pattern *cbox_adhoc_pattern_new(struct cbox_engine *engine, int id, struct cbox_midi_pattern *pattern) -{ - struct cbox_adhoc_pattern *ap = calloc(1, sizeof(struct cbox_adhoc_pattern)); - ap->next = NULL; - ap->pattern = pattern; - ap->pattern_playback = cbox_midi_pattern_playback_new(pattern); - ap->master = cbox_master_new(engine); - cbox_midi_playback_active_notes_init(&ap->active_notes); - cbox_midi_clip_playback_init(&ap->playback, &ap->active_notes, ap->master); - cbox_midi_buffer_init(&ap->output_buffer); - ap->id = id; - ap->completed = FALSE; - - return ap; -} - -void cbox_adhoc_pattern_render(struct cbox_adhoc_pattern *ap, uint32_t offset, uint32_t nsamples) -{ - if (ap->completed) - { - cbox_midi_playback_active_notes_release(&ap->active_notes, &ap->output_buffer, NULL); - return; - } - if (ap->playback.pos >= ap->playback.pattern->event_count) - ap->completed = TRUE; - cbox_midi_clip_playback_render(&ap->playback, &ap->output_buffer, offset, nsamples, FALSE); -} - -void cbox_adhoc_pattern_destroy(struct cbox_adhoc_pattern *ap) -{ - // XXXKF decide on pattern ownership and general object lifetime issues - cbox_midi_pattern_playback_destroy(ap->playback.pattern); - cbox_master_destroy(ap->master); - free(ap); -} diff --git a/template/calfbox/seq.c b/template/calfbox/seq.c deleted file mode 100644 index 98e1d4b..0000000 --- a/template/calfbox/seq.c +++ /dev/null @@ -1,930 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "engine.h" -#include "pattern.h" -#include "rt.h" -#include "seq.h" -#include "song.h" -#include "track.h" -#include - -static inline void accumulate_event(struct cbox_midi_playback_active_notes *notes, const struct cbox_midi_event *event) -{ - if (event->size != 3) - return; - // this ignores poly aftertouch - which, I supposed, is OK for now - if (event->data_inline[0] < 0x90 || event->data_inline[0] > 0x9F) - return; - if (event->data_inline[2] > 0) - { - int ch = event->data_inline[0] & 0x0F; - int note = event->data_inline[1] & 0x7F; - if (!(notes->channels_active & (1 << ch))) - { - for (int i = 0; i < 4; i++) - notes->notes[ch][i] = 0; - notes->channels_active |= 1 << ch; - } - notes->notes[ch][note >> 5] |= 1 << (note & 0x1F); - } -} - -// this releases a note on note off (accumulate_event is 'sticky') -static inline void accumulate_event2(struct cbox_midi_playback_active_notes *notes, const struct cbox_midi_event *event) -{ - if (event->size != 3) - return; - // this ignores poly aftertouch - which, I supposed, is OK for now - if (event->data_inline[0] < 0x80 || event->data_inline[0] > 0x9F) - return; - int ch = event->data_inline[0] & 0x0F; - int note = event->data_inline[1] & 0x7F; - uint32_t mask = 1 << (note & 0x1F); - if (event->data_inline[0] >= 0x90 && event->data_inline[2] > 0) - { - if (!(notes->channels_active & (1 << ch))) - { - for (int i = 0; i < 4; i++) - notes->notes[ch][i] = 0; - notes->channels_active |= 1 << ch; - } - notes->notes[ch][note >> 5] |= mask; - } else { - if (notes->notes[ch][note >> 5] & mask) { - notes->notes[ch][note >> 5] &= ~mask; - if (!notes->notes[ch][0] && !notes->notes[ch][1] && !notes->notes[ch][2] && !notes->notes[ch][3]) { - notes->channels_active &= ~(1 << ch); - } - } - } -} - -struct cbox_track_playback *cbox_track_playback_new_from_track(struct cbox_track *track, struct cbox_master *master, struct cbox_song_playback *spb, struct cbox_track_playback *old_state) -{ - struct cbox_track_playback *pb = malloc(sizeof(struct cbox_track_playback)); - cbox_uuid_copy(&pb->track_uuid, &CBOX_O2H(track)->instance_uuid); - pb->old_state = old_state; - pb->generation = track->generation; - pb->ref_count = 1; - pb->master = master; - int len = g_list_length(track->items); - pb->items = calloc(len, sizeof(struct cbox_track_playback_item)); - pb->external_merger = NULL; - pb->spb = spb; - pb->state_copied = FALSE; - pb->mute = track->mute; - - GList *it = track->items; - struct cbox_track_playback_item *p = pb->items; - uint32_t safe = 0; - while(it != NULL) - { - struct cbox_track_item *item = it->data; - struct cbox_midi_pattern_playback *mppb = cbox_song_playback_get_pattern(spb, item->pattern); - - // if items overlap, the first one takes precedence - if (item->time < safe) - { - // fully contained in previous item? skip all of it - // not fully contained - insert the fragment - if (item->time + item->length >= safe) - { - int cut = safe - item->time; - p->time = safe; - p->pattern = mppb; - p->offset = item->offset + cut; - p->length = item->length - cut; - p++; - } - } - else - { - p->time = item->time; - p->pattern = mppb; - p->offset = item->offset; - p->length = item->length; - safe = item->time + item->length; - p++; - } - - it = g_list_next(it); - } - // in case of full overlap, some items might have been skipped - pb->items_count = p - pb->items; - pb->pos = 0; - cbox_midi_clip_playback_init(&pb->playback, &pb->active_notes, master); - cbox_midi_playback_active_notes_init(&pb->active_notes); - cbox_midi_buffer_init(&pb->output_buffer); - cbox_track_playback_start_item(pb, 0, FALSE, 0); - - if (track->external_output_set) - { - struct cbox_midi_merger *merger = cbox_rt_get_midi_output(spb->engine->rt, &track->external_output); - if (merger) - cbox_midi_merger_connect(merger, &pb->output_buffer, spb->engine->rt, &pb->external_merger); - } - - return pb; -} - -void cbox_track_confirm_stuck_notes(struct cbox_track_playback *pb, struct cbox_midi_playback_active_notes *stuck_notes, uint32_t new_pos_ppqn) -{ - // Check if no notes are stuck - if (!stuck_notes->channels_active) - return; - uint32_t pos = 0; - while(pos < pb->items_count && pb->items[pos].time + pb->items[pos].length < new_pos_ppqn) - pos++; - if (pos >= pb->items_count) // past the end of the track - all notes are stuck - return; - const struct cbox_track_playback_item *tpi = &pb->items[pos]; - uint32_t rel_time_ppqn = new_pos_ppqn - tpi->time; - if (rel_time_ppqn < tpi->length) - { - // inside the clip - rel_time_ppqn += tpi->offset; - - for (unsigned c = 0; c < 16; c++) - { - if (!(stuck_notes->channels_active & (1 << c))) - continue; - - gboolean any_left = FALSE; - for (unsigned g = 0; g < 4; g++) - { - uint32_t group = stuck_notes->notes[c][g]; - if (!group) - continue; - for (unsigned i = 0; i < 32; i++) - { - if (!(group & (1 << i))) - continue; - uint8_t n = i + g * 32; - if (cbox_midi_pattern_playback_is_note_active_at(tpi->pattern, rel_time_ppqn, c, n)) - { - // That note is not stuck - group &= ~(1 << i); - } else { - // It is stuck, so keep the channel as containing stuck notes - any_left = TRUE; - } - } - stuck_notes->notes[c][g] = group; - } - if (!any_left) { - stuck_notes->channels_active &= ~(1 << c); - } - } - return; - } -} - -void cbox_track_playback_seek_ppqn(struct cbox_track_playback *pb, uint32_t time_ppqn, uint32_t min_time_ppqn) -{ - pb->pos = 0; - while(pb->pos < pb->items_count && pb->items[pb->pos].time + pb->items[pb->pos].length < time_ppqn) - pb->pos++; - cbox_track_playback_start_item(pb, time_ppqn, TRUE, min_time_ppqn); -} - -void cbox_track_playback_seek_samples(struct cbox_track_playback *pb, uint32_t time_samples) -{ - pb->pos = 0; - while(pb->pos < pb->items_count && cbox_master_ppqn_to_samples(pb->master, pb->items[pb->pos].time + pb->items[pb->pos].length) < time_samples) - pb->pos++; - if (pb->pos < pb->items_count) - { - int min_time_ppqn = cbox_master_samples_to_ppqn(pb->master, time_samples); - cbox_track_playback_start_item(pb, time_samples, FALSE, min_time_ppqn); - } -} - -void cbox_track_playback_start_item(struct cbox_track_playback *pb, int time, int is_ppqn, int min_time_ppqn) -{ - if (pb->pos >= pb->items_count) - { - return; - } - struct cbox_track_playback_item *cur = &pb->items[pb->pos]; - int time_samples, time_ppqn; - - if (is_ppqn) - { - time_ppqn = time; - time_samples = cbox_master_ppqn_to_samples(pb->master, time_ppqn); - } - else - { - time_samples = time; - time_ppqn = cbox_master_samples_to_ppqn(pb->master, time_samples); - } - int start_time_ppqn = cur->time, end_time_ppqn = cur->time + cur->length; - int start_time_samples = cbox_master_ppqn_to_samples(pb->master, start_time_ppqn); - int end_time_samples = cbox_master_ppqn_to_samples(pb->master, end_time_ppqn); - cbox_midi_clip_playback_set_pattern(&pb->playback, cur->pattern, start_time_samples, end_time_samples, cur->time, cur->offset); - - if (is_ppqn) - { - if (time_ppqn < start_time_ppqn) - cbox_midi_clip_playback_seek_ppqn(&pb->playback, 0, min_time_ppqn); - else - cbox_midi_clip_playback_seek_ppqn(&pb->playback, time_ppqn - start_time_ppqn, min_time_ppqn); - } - else - { - if (time_ppqn < start_time_ppqn) - cbox_midi_clip_playback_seek_samples(&pb->playback, 0, min_time_ppqn); - else - cbox_midi_clip_playback_seek_samples(&pb->playback, time_samples - start_time_samples, min_time_ppqn); - } -} - -void cbox_track_playback_render(struct cbox_track_playback *pb, uint32_t offset, uint32_t nsamples) -{ - struct cbox_song_playback *spb = pb->master->spb; - if (pb->mute) { - cbox_midi_playback_active_notes_release(&pb->active_notes, &pb->output_buffer, NULL); - } - uint32_t rpos = 0; - while(rpos < nsamples && pb->pos < pb->items_count) - { - uint32_t rend = nsamples; - struct cbox_track_playback_item *cur = &pb->items[pb->pos]; - // a gap before the current item - if (spb->song_pos_samples + rpos < pb->playback.start_time_samples) - { - uint32_t space_samples = pb->playback.start_time_samples - (spb->song_pos_samples + rpos); - if (space_samples >= rend - rpos) - return; - rpos += space_samples; - offset += space_samples; - } - // check if item finished - int cur_segment_end_samples = cbox_master_ppqn_to_samples(pb->master, cur->time + cur->length); - int render_end_samples = spb->song_pos_samples + rend; - if (render_end_samples > cur_segment_end_samples) - { - rend = cur_segment_end_samples - spb->song_pos_samples; - cbox_midi_clip_playback_render(&pb->playback, &pb->output_buffer, offset, rend - rpos, pb->mute); - pb->pos++; - cbox_track_playback_start_item(pb, cur_segment_end_samples, FALSE, FALSE); - } - else - cbox_midi_clip_playback_render(&pb->playback, &pb->output_buffer, offset, rend - rpos, pb->mute); - offset += rend - rpos; - rpos = rend; - } -} - -void cbox_track_playback_ref(struct cbox_track_playback *pb) -{ - ++pb->ref_count; -} - -void cbox_track_playback_destroy(struct cbox_track_playback *pb) -{ - if (pb->external_merger) - cbox_midi_merger_disconnect(pb->external_merger, &pb->output_buffer, pb->spb->engine->rt); - - for (uint32_t i = 0; i < pb->items_count; ++i) - cbox_midi_pattern_playback_unref(pb->items[i].pattern); - free(pb->items); - free(pb); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////// - -static gint note_compare_fn(const void *p1, const void *p2, void *user_data) -{ - const struct cbox_midi_event *e1 = p1, *e2 = p2; - int cn1 = ((e1->data_inline[0] & 0x0F) << 8) | e1->data_inline[1]; - int cn2 = ((e2->data_inline[0] & 0x0F) << 8) | e2->data_inline[1]; - if (cn1 < cn2) - return -1; - if (cn2 < cn1) - return +1; - if (e1->time < e2->time) - return -1; - if (e1->time > e2->time) - return +1; - if (p1 < p2) - return -1; - if (p1 > p2) - return +1; - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////// - -struct cbox_midi_pattern_playback *cbox_midi_pattern_playback_new(struct cbox_midi_pattern *pattern) -{ - struct cbox_midi_pattern_playback *mppb = calloc(1, sizeof(struct cbox_midi_pattern_playback)); - mppb->events = malloc(sizeof(struct cbox_midi_event) * pattern->event_count); - memcpy(mppb->events, pattern->events, sizeof(struct cbox_midi_event) * pattern->event_count); - mppb->event_count = pattern->event_count; - mppb->ref_count = 1; - cbox_midi_playback_active_notes_init(&mppb->note_bitmask); - mppb->note_lookup = g_sequence_new(NULL); - for (uint32_t i = 0; i < mppb->event_count; ++i) { - struct cbox_midi_event *event = &mppb->events[i]; - if (event->size == 3 && (event->data_inline[0] & 0xE0) == 0x80) { - g_sequence_insert_sorted(mppb->note_lookup, event, note_compare_fn, NULL); - if (event->data_inline[0] >= 0x90) - accumulate_event(&mppb->note_bitmask, event); - } - } - - return mppb; -} - -void cbox_midi_pattern_playback_unref(struct cbox_midi_pattern_playback *mppb) -{ - if (!(--mppb->ref_count)) - cbox_midi_pattern_playback_destroy(mppb); -} - -void cbox_midi_pattern_playback_ref(struct cbox_midi_pattern_playback *mppb) -{ - ++mppb->ref_count; -} - -void cbox_midi_pattern_playback_destroy(struct cbox_midi_pattern_playback *mppb) -{ - g_sequence_free(mppb->note_lookup); - free(mppb->events); - free(mppb); -} - -gboolean cbox_midi_pattern_playback_is_note_active_at(struct cbox_midi_pattern_playback *mppb, uint32_t time_ppqn, uint32_t channel, uint32_t note) -{ - struct cbox_midi_event event; - event.time = time_ppqn; - event.size = 3; - event.data_inline[0] = 0x90 | channel; - event.data_inline[1] = note; - event.data_inline[2] = 127; - // printf("checking stuck note ch %d note %d at %d\n", channel, note, time_ppqn); - GSequenceIter *i = g_sequence_search(mppb->note_lookup, &event, note_compare_fn, NULL); - if (g_sequence_iter_is_begin(i)) // before first note - { - // printf("before first note\n"); - return FALSE; - } - i = g_sequence_iter_prev(i); - // A preceding note with the same channel and note number - struct cbox_midi_event *pevent = g_sequence_get(i); - // If it's an event for a different note, channel or not a note on event, then the note hasn't been active at the time - // XXXKF what about notes that start before clip offset? - if (pevent->size != 3 || pevent->data_inline[0] != event.data_inline[0] || pevent->data_inline[1] != event.data_inline[1] || !pevent->data_inline[2]) { - // printf("pevent wrong %d %d %d %d\n", pevent->time, pevent->data_inline[0], pevent->data_inline[1], pevent->data_inline[2]); - return FALSE; - } - - // printf("confirmed note ch %d note %d\n", channel, note); - return TRUE; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////// - -void cbox_midi_clip_playback_init(struct cbox_midi_clip_playback *pb, struct cbox_midi_playback_active_notes *active_notes, struct cbox_master *master) -{ - pb->pattern = NULL; - pb->master = master; - pb->pos = 0; - pb->rel_time_samples = 0; - pb->start_time_samples = 0; - pb->end_time_samples = 0; - pb->active_notes = active_notes; - pb->min_time_ppqn = 0; - // cbox_midi_playback_active_notes_init(active_notes); -} - -void cbox_midi_clip_playback_set_pattern(struct cbox_midi_clip_playback *pb, struct cbox_midi_pattern_playback *pattern, int start_time_samples, int end_time_samples, int item_start_ppqn, int offset_ppqn) -{ - pb->pattern = pattern; - pb->pos = 0; - pb->rel_time_samples = 0; - pb->start_time_samples = start_time_samples; - pb->end_time_samples = end_time_samples; - pb->item_start_ppqn = item_start_ppqn; - pb->offset_ppqn = offset_ppqn; - pb->min_time_ppqn = offset_ppqn; -} - -void cbox_midi_clip_playback_render(struct cbox_midi_clip_playback *pb, struct cbox_midi_buffer *buf, uint32_t offset, uint32_t nsamples, gboolean mute) -{ - uint32_t end_time_samples = pb->end_time_samples; - uint32_t cur_time_samples = pb->start_time_samples + pb->rel_time_samples; - - if (end_time_samples > cur_time_samples + nsamples) - end_time_samples = cur_time_samples + nsamples; - - while(pb->pos < pb->pattern->event_count) - { - const struct cbox_midi_event *src = &pb->pattern->events[pb->pos]; - - if (src->time - pb->offset_ppqn + pb->item_start_ppqn >= pb->min_time_ppqn) - { - uint32_t event_time_samples = cbox_master_ppqn_to_samples(pb->master, src->time - pb->offset_ppqn + pb->item_start_ppqn); - - if (event_time_samples >= end_time_samples) - break; - int32_t time = 0; - if (event_time_samples >= cur_time_samples) // convert negative relative time to 0 time - time = event_time_samples - cur_time_samples; - - if (!mute) { - cbox_midi_buffer_copy_event(buf, src, offset + time); - if (pb->active_notes) - accumulate_event2(pb->active_notes, src); - } - } - pb->pos++; - } - pb->rel_time_samples += nsamples; -} - -void cbox_midi_clip_playback_seek_ppqn(struct cbox_midi_clip_playback *pb, uint32_t time_ppqn, uint32_t min_time_ppqn) -{ - uint32_t patrel_time_ppqn = time_ppqn + pb->offset_ppqn; - uint32_t L = 0, U = pb->pattern->event_count; - - if (patrel_time_ppqn > 0) { - while (U > L + 2) { - uint32_t M = (L >> 1) + (U >> 1) + (L & U & 1); - uint32_t time = pb->pattern->events[M].time; - if (time < patrel_time_ppqn) - L = M + 1; - else if (time >= patrel_time_ppqn) - U = M + 1; // this might still be the event we're looking for - } - } - - uint32_t pos = L; - while (pos < U && pb->pattern->events[pos].time < patrel_time_ppqn) - pos++; - pb->rel_time_samples = cbox_master_ppqn_to_samples(pb->master, pb->item_start_ppqn + time_ppqn) - pb->start_time_samples; - pb->min_time_ppqn = min_time_ppqn; - pb->pos = pos; -} - -void cbox_midi_clip_playback_seek_samples(struct cbox_midi_clip_playback *pb, uint32_t time_samples, uint32_t min_time_ppqn) -{ - uint32_t pos = 0; - while (pos < pb->pattern->event_count && time_samples > cbox_master_ppqn_to_samples(pb->master, pb->item_start_ppqn + pb->pattern->events[pos].time - pb->offset_ppqn)) - pos++; - pb->rel_time_samples = time_samples; - pb->min_time_ppqn = min_time_ppqn; - pb->pos = pos; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////// - -void cbox_midi_playback_active_notes_init(struct cbox_midi_playback_active_notes *notes) -{ - notes->channels_active = 0; -} - -void cbox_midi_playback_active_notes_copy(struct cbox_midi_playback_active_notes *dest, const struct cbox_midi_playback_active_notes *src) -{ - dest->channels_active = src->channels_active; - memcpy(dest->notes, src->notes, sizeof(dest->notes)); -} - -int cbox_midi_playback_active_notes_release(struct cbox_midi_playback_active_notes *notes, struct cbox_midi_buffer *buf, struct cbox_midi_playback_active_notes *leftover_notes) -{ - if (!notes->channels_active) - return 0; - int note_offs = 0; - for (int c = 0; c < 16; c++) - { - if (!(notes->channels_active & (1 << c))) - continue; - - for (int g = 0; g < 4; g++) - { - uint32_t group = notes->notes[c][g]; - if (!group) - continue; - for (int i = 0; i < 32; i++) - { - int n = i + g * 32; - if (!(group & (1 << i))) - continue; - if (!cbox_midi_buffer_can_store_msg(buf, 3)) - return -1; - cbox_midi_buffer_write_inline(buf, cbox_midi_buffer_get_last_event_time(buf), 0x80 + c, n, 0); - group &= ~(1 << i); - notes->notes[c][g] = group; - if (leftover_notes) - leftover_notes->notes[c][g] &= ~(1 << i); - note_offs++; - } - } - // all Note Offs emitted without buffer overflow - channel is no longer active - notes->channels_active &= ~(1 << c); - } - return note_offs; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////// - -struct cbox_song_playback *cbox_song_playback_new(struct cbox_song *song, struct cbox_master *master, struct cbox_engine *engine, struct cbox_song_playback *old_state) -{ - struct cbox_song_playback *spb = calloc(1, sizeof(struct cbox_song_playback)); - if (old_state && old_state->song != song) - old_state = NULL; - spb->song = song; - spb->engine = engine; - spb->pattern_map = g_hash_table_new(NULL, NULL); - spb->master = master; - spb->track_count = g_list_length(song->tracks); - spb->tracks = malloc(spb->track_count * sizeof(struct cbox_track_playback *)); - spb->song_pos_samples = 0; - spb->song_pos_ppqn = 0; - spb->min_time_ppqn = 0; - spb->loop_start_ppqn = song->loop_start_ppqn; - spb->loop_end_ppqn = song->loop_end_ppqn; - cbox_midi_merger_init(&spb->track_merger, NULL); - int pos = 0; - for (GList *p = song->tracks; p != NULL; p = g_list_next(p)) - { - struct cbox_track *trk = p->data; - struct cbox_track_playback *old_trk = NULL; - if (old_state && old_state->track_count) - { - for (uint32_t i = 0; i < old_state->track_count; i++) - { - if (cbox_uuid_equal(&old_state->tracks[i]->track_uuid, &CBOX_O2H(trk)->instance_uuid)) - { - old_trk = old_state->tracks[i]; - break; - } - } - } - if (old_trk && trk->generation == old_trk->generation) { - old_trk->state_copied = TRUE; - cbox_track_playback_ref(old_trk); - spb->tracks[pos++] = old_trk; - } - else { - if (old_trk) - old_trk->state_copied = FALSE; - spb->tracks[pos++] = cbox_track_playback_new_from_track(trk, spb->master, spb, old_trk); - } - if (!trk->external_output_set) - cbox_midi_merger_connect(&spb->track_merger, &spb->tracks[pos - 1]->output_buffer, NULL, NULL); - } - - spb->tempo_map_item_count = g_list_length(song->master_track_items); - spb->tempo_map_items = malloc(spb->tempo_map_item_count * sizeof(struct cbox_tempo_map_item)); - pos = 0; - int pos_ppqn = 0; - int pos_samples = 0; - double tempo = master->tempo; - int timesig_num = master->timesig_num; - int timesig_denom = master->timesig_denom; - struct cbox_bbt cur_bbt = {0, 0, 0, 0}; - for (GList *p = song->master_track_items; p != NULL; p = g_list_next(p)) - { - struct cbox_master_track_item *mti = p->data; - if (mti->tempo == 0 && mti->timesig_num == 0 && - mti->timesig_denom == 0 && p == song->master_track_items) { - spb->tempo_map_item_count--; - continue; - } - if (mti->tempo > 0) - tempo = mti->tempo; - if (mti->timesig_num > 0) - timesig_num = mti->timesig_num; - if (mti->timesig_denom > 0) - timesig_denom = mti->timesig_denom; - struct cbox_tempo_map_item *tmi = &spb->tempo_map_items[pos]; - tmi->time_ppqn = pos_ppqn; - tmi->time_samples = pos_samples; - tmi->tempo = tempo; - tmi->timesig_num = timesig_num; - tmi->timesig_denom = timesig_denom; - memcpy(&tmi->bbt, &cur_bbt, sizeof(cur_bbt)); - - cbox_bbt_add(&cur_bbt, mti->duration_ppqn, master->ppqn_factor, timesig_num, timesig_denom); - pos_ppqn += mti->duration_ppqn; - pos_samples += master->srate * 60.0 * mti->duration_ppqn / (tempo * master->ppqn_factor); - pos++; - } - return spb; -} - -void cbox_song_playback_apply_old_state(struct cbox_song_playback *spb) -{ - for (uint32_t i = 0; i < spb->track_count; i++) - { - struct cbox_track_playback *tpb = spb->tracks[i]; - tpb->spb = spb; - if (tpb->old_state) - { - cbox_midi_playback_active_notes_copy(&tpb->active_notes, &tpb->old_state->active_notes); - tpb->old_state->state_copied = TRUE; - tpb->old_state = NULL; - } - } -} - - -static void cbox_song_playback_set_tempo(struct cbox_song_playback *spb, double tempo) -{ - int ppos = spb->song_pos_ppqn; - int pos1 = cbox_master_ppqn_to_samples(spb->master, ppos); - int pos2 = cbox_master_ppqn_to_samples(spb->master, ppos + 1); - double relpos = 0.0; - if (pos1 != pos2) - relpos = (spb->song_pos_samples - pos1) * 1.0 / (pos2 - pos1); - spb->master->tempo = tempo; - - // This seek loses the fractional value of the PPQN song position. - // This needs to be compensated for by shifting the playback - // position by the fractional part. - cbox_song_playback_seek_ppqn(spb, ppos, spb->min_time_ppqn); - if (relpos > 0) - { - pos2 = cbox_master_ppqn_to_samples(spb->master, ppos + 1); - cbox_song_playback_seek_samples(spb, spb->song_pos_samples + (pos2 - spb->song_pos_samples) * relpos + 0.5); - } -} - -int cbox_song_playback_get_next_tempo_change(struct cbox_song_playback *spb) -{ - double new_tempo = 0; - // Skip items at or already past the playback pointer - while (spb->tempo_map_pos + 1 < spb->tempo_map_item_count && - spb->song_pos_samples >= spb->tempo_map_items[spb->tempo_map_pos + 1].time_samples) - { - new_tempo = spb->tempo_map_items[spb->tempo_map_pos + 1].tempo; - spb->tempo_map_pos++; - } - if (new_tempo != 0.0 && new_tempo != spb->master->tempo) { - cbox_song_playback_set_tempo(spb, new_tempo); - } - - // No more items? - if (spb->tempo_map_pos + 1 >= spb->tempo_map_item_count) - return -1; - - return spb->tempo_map_items[spb->tempo_map_pos + 1].time_samples; -} - -void cbox_song_playback_prepare_render(struct cbox_song_playback *spb) -{ - for(uint32_t i = 0; i < spb->track_count; i++) - { - cbox_midi_buffer_clear(&spb->tracks[i]->output_buffer); - } -} - -void cbox_song_playback_render(struct cbox_song_playback *spb, struct cbox_midi_buffer *output, uint32_t nsamples) -{ - cbox_midi_buffer_clear(output); - - if (spb->master->new_tempo != 0) - { - if (spb->master->new_tempo != spb->master->tempo) - cbox_song_playback_set_tempo(spb, spb->master->new_tempo); - spb->master->new_tempo = 0; - } - if (spb->master->state == CMTS_STOPPING) - { - if (cbox_song_playback_active_notes_release(spb, NULL, 0, output) > 0) - spb->master->state = CMTS_STOP; - } - else - if (spb->master->state == CMTS_ROLLING) - { - uint32_t end_samples = cbox_master_ppqn_to_samples(spb->master, spb->loop_end_ppqn); - - uint32_t rpos = 0; - while (rpos < nsamples) - { - uint32_t rend = nsamples; - - // 1. Shorten the period so that it doesn't go past a tempo change - int tmpos = cbox_song_playback_get_next_tempo_change(spb); - if (tmpos != -1) - { - // Number of samples until the next tempo change - uint32_t stntc = tmpos - spb->song_pos_samples; - if (rend - rpos > stntc) - rend = rpos + stntc; - } - - // 2. Shorten the period so that it doesn't go past the song length - uint32_t end_pos = spb->song_pos_samples + (rend - rpos); - if (end_pos >= end_samples) - { - rend = end_samples - spb->song_pos_samples; - end_pos = end_samples; - } - - if (rend > rpos) - { - for (uint32_t i = 0; i < spb->track_count; i++) - cbox_track_playback_render(spb->tracks[i], rpos, rend - rpos); - } - - if (end_pos < end_samples) - { - spb->song_pos_samples += rend - rpos; - // XXXKF optimize - spb->min_time_ppqn = cbox_master_samples_to_ppqn(spb->master, spb->song_pos_samples - 1) + 1; - spb->song_pos_ppqn = cbox_master_samples_to_ppqn(spb->master, spb->song_pos_samples); - } - else - { - if (spb->loop_start_ppqn >= spb->loop_end_ppqn) - { - spb->song_pos_samples = end_samples; - spb->song_pos_ppqn = spb->loop_end_ppqn; - spb->master->state = CMTS_STOPPING; - break; - } - - cbox_song_playback_seek_ppqn(spb, spb->loop_start_ppqn, spb->loop_start_ppqn); - } - rpos = rend; - } - cbox_midi_merger_render_to(&spb->track_merger, output); - } -} - -int cbox_song_playback_active_notes_release(struct cbox_song_playback *spb, struct cbox_song_playback *new_spb, uint32_t new_pos, struct cbox_midi_buffer *buf) -{ - // Release notes from deleted tracks - for(uint32_t i = 0; i < spb->track_count; i++) - { - struct cbox_track_playback *trk = spb->tracks[i]; - if (new_spb && trk->state_copied) - continue; - struct cbox_midi_buffer *output = trk->external_merger ? &trk->output_buffer : buf; - if (cbox_midi_playback_active_notes_release(&trk->active_notes, output, NULL) < 0) - return 0; - } - // Release notes from removed/modified clips - if (new_spb) { - for(uint32_t i = 0; i < new_spb->track_count; i++) - { - struct cbox_track_playback *new_trk = new_spb->tracks[i]; - if (!new_trk->active_notes.channels_active) - continue; - // struct cbox_track_playback *old_trk = new_trk->old_state; - // if (!old_trk) - // continue; - struct cbox_midi_buffer *output = new_trk->external_merger ? &new_trk->output_buffer : buf; - struct cbox_midi_playback_active_notes stuck_notes; - cbox_midi_playback_active_notes_copy(&stuck_notes, &new_trk->active_notes); - cbox_track_confirm_stuck_notes(new_trk, &stuck_notes, new_pos); - if (cbox_midi_playback_active_notes_release(&stuck_notes, output, &new_trk->active_notes) < 0) - return 0; - } - } - return 1; -} - -void cbox_song_playback_seek_ppqn(struct cbox_song_playback *spb, int time_ppqn, int min_time_ppqn) -{ - for(uint32_t i = 0; i < spb->track_count; i++) - { - struct cbox_track_playback *trk = spb->tracks[i]; - cbox_track_playback_seek_ppqn(trk, time_ppqn, min_time_ppqn); - } - spb->song_pos_samples = cbox_master_ppqn_to_samples(spb->master, time_ppqn); - spb->song_pos_ppqn = time_ppqn; - spb->min_time_ppqn = min_time_ppqn; - spb->tempo_map_pos = cbox_song_playback_tmi_from_ppqn(spb, time_ppqn); -} - -void cbox_song_playback_seek_samples(struct cbox_song_playback *spb, uint32_t time_samples) -{ - for(uint32_t i = 0; i < spb->track_count; i++) - { - struct cbox_track_playback *trk = spb->tracks[i]; - cbox_track_playback_seek_samples(trk, time_samples); - } - spb->song_pos_samples = time_samples; - spb->song_pos_ppqn = cbox_master_samples_to_ppqn(spb->master, time_samples); - spb->min_time_ppqn = spb->song_pos_ppqn; - spb->tempo_map_pos = cbox_song_playback_tmi_from_samples(spb, time_samples); -} - -int cbox_song_playback_tmi_from_ppqn(struct cbox_song_playback *spb, uint32_t time_ppqn) -{ - if (!spb->tempo_map_item_count) - return -1; - assert(spb->tempo_map_items[0].time_samples == 0); - assert(spb->tempo_map_items[0].time_ppqn == 0); - // XXXKF should use binary search here really - for (int i = 1; i < spb->tempo_map_item_count; i++) - { - if (time_ppqn < spb->tempo_map_items[i].time_ppqn) - return i - 1; - } - return spb->tempo_map_item_count - 1; -} - -int cbox_song_playback_tmi_from_samples(struct cbox_song_playback *spb, uint32_t time_samples) -{ - if (!spb->tempo_map_item_count) - return -1; - assert(spb->tempo_map_items[0].time_samples == 0); - assert(spb->tempo_map_items[0].time_ppqn == 0); - // XXXKF should use binary search here really - for (int i = 1; i < spb->tempo_map_item_count; i++) - { - if (time_samples < spb->tempo_map_items[i].time_samples) - return i - 1; - } - return spb->tempo_map_item_count - 1; -} - -struct cbox_midi_pattern_playback *cbox_song_playback_get_pattern(struct cbox_song_playback *spb, struct cbox_midi_pattern *pattern) -{ - struct cbox_midi_pattern_playback *mppb = g_hash_table_lookup(spb->pattern_map, pattern); - if (mppb) { - cbox_midi_pattern_playback_ref(mppb); - return mppb; - } - - mppb = cbox_midi_pattern_playback_new(pattern); - g_hash_table_insert(spb->pattern_map, pattern, mppb); - - return mppb; -} - -uint32_t cbox_song_playback_correct_for_looping(struct cbox_song_playback *spb, uint32_t abs_samples) -{ - struct cbox_engine *engine = spb->engine; - // This is rather expensive, the start/end of the loop expressed in samples should be cached. - uint32_t loop_end_samples = cbox_master_ppqn_to_samples(engine->master, spb->loop_end_ppqn); - if (abs_samples >= loop_end_samples) { - // Correct for looping - uint32_t loop_start_samples = cbox_master_ppqn_to_samples(engine->master, spb->loop_start_ppqn); - if (loop_start_samples < loop_end_samples) { - uint32_t loop_length = loop_end_samples - loop_start_samples; - abs_samples = loop_start_samples + (abs_samples - loop_start_samples) % loop_length; - } - } - return abs_samples; -} - -void cbox_song_playback_destroy(struct cbox_song_playback *spb) -{ - cbox_midi_merger_close(&spb->track_merger, spb->engine->rt); - for (uint32_t i = 0; i < spb->track_count; i++) - { - if (!(--spb->tracks[i]->ref_count)) - cbox_track_playback_destroy(spb->tracks[i]); - } - free(spb->tempo_map_items); - free(spb->tracks); - g_hash_table_destroy(spb->pattern_map); - free(spb); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////// - -uint32_t cbox_song_time_mapper_map_time(struct cbox_time_mapper *tmap, uint32_t free_running_counter) -{ - struct cbox_song_time_mapper *stmap = (struct cbox_song_time_mapper *)tmap; - struct cbox_engine *engine = stmap->engine; - - if (!engine->spb || engine->master->state != CMTS_ROLLING) - return free_running_counter & 0x7FFFFFFF; - - int32_t rel_samples = free_running_counter - engine->rt->io->free_running_frame_counter; - if (rel_samples < 0 || rel_samples >= 1048576) - return (uint32_t)-1; - uint32_t abs_samples = engine->spb->song_pos_samples + rel_samples; - abs_samples = cbox_song_playback_correct_for_looping(engine->spb, abs_samples); - uint32_t abs_ppqn = cbox_master_samples_to_ppqn(engine->master, abs_samples); - return abs_ppqn | 0x80000000; -} - -void cbox_song_time_mapper_init(struct cbox_song_time_mapper *tmap, struct cbox_engine *engine) -{ - tmap->tmap.map_time = cbox_song_time_mapper_map_time; - tmap->engine = engine; -} diff --git a/template/calfbox/seq.h b/template/calfbox/seq.h deleted file mode 100644 index f8ca0de..0000000 --- a/template/calfbox/seq.h +++ /dev/null @@ -1,199 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_SEQ_H -#define CBOX_SEQ_H - -#include - -#include "dom.h" -#include "midi.h" -#include "mididest.h" - -struct cbox_engine; -struct cbox_midi_pattern; -struct cbox_track; -struct cbox_song; - -///////////////////////////////////////////////////////////////////////////////////////////////////// - -struct cbox_midi_playback_active_notes -{ - uint16_t channels_active; - uint32_t notes[16][4]; // 0..127 -}; - -extern void cbox_midi_playback_active_notes_init(struct cbox_midi_playback_active_notes *notes); -extern void cbox_midi_playback_active_notes_copy(struct cbox_midi_playback_active_notes *dest, const struct cbox_midi_playback_active_notes *src); -extern void cbox_midi_playback_active_notes_clear(struct cbox_midi_playback_active_notes *notes); -extern int cbox_midi_playback_active_notes_release(struct cbox_midi_playback_active_notes *notes, struct cbox_midi_buffer *buf, struct cbox_midi_playback_active_notes *leftover_notes); - -///////////////////////////////////////////////////////////////////////////////////////////////////// - -struct cbox_midi_pattern_playback -{ - struct cbox_midi_event *events; - uint32_t event_count; - int ref_count; - GSequence *note_lookup; - struct cbox_midi_playback_active_notes note_bitmask; -}; - -extern struct cbox_midi_pattern_playback *cbox_midi_pattern_playback_new(struct cbox_midi_pattern *pattern); -extern void cbox_midi_pattern_playback_ref(struct cbox_midi_pattern_playback *mppb); -extern void cbox_midi_pattern_playback_unref(struct cbox_midi_pattern_playback *mppb); -extern void cbox_midi_pattern_playback_destroy(struct cbox_midi_pattern_playback *mppb); -extern gboolean cbox_midi_pattern_playback_is_note_active_at(struct cbox_midi_pattern_playback *mppb, uint32_t time_ppqn, uint32_t channel, uint32_t note); - -///////////////////////////////////////////////////////////////////////////////////////////////////// - -struct cbox_midi_clip_playback -{ - struct cbox_midi_pattern_playback *pattern; - struct cbox_master *master; - - uint32_t pos; - int rel_time_samples; - // [start, end) of the pattern slice - uint32_t start_time_samples, end_time_samples; - uint32_t item_start_ppqn, min_time_ppqn; - int offset_ppqn; - struct cbox_midi_playback_active_notes *active_notes; -}; - -extern void cbox_midi_clip_playback_init(struct cbox_midi_clip_playback *pb, struct cbox_midi_playback_active_notes *active_notes, struct cbox_master *master); -extern void cbox_midi_clip_playback_render(struct cbox_midi_clip_playback *pb, struct cbox_midi_buffer *buf, uint32_t offset, uint32_t nsamples, gboolean mute); -extern void cbox_midi_clip_playback_seek_ppqn(struct cbox_midi_clip_playback *pb, uint32_t time_ppqn, uint32_t min_time_ppqn); -extern void cbox_midi_clip_playback_seek_samples(struct cbox_midi_clip_playback *pb, uint32_t time_samples, uint32_t min_time_ppqn); -extern void cbox_midi_clip_playback_set_pattern(struct cbox_midi_clip_playback *pb, struct cbox_midi_pattern_playback *pattern, int start_time_samples, int end_time_samples, int item_start_ppqn, int offset_ppqn); - -///////////////////////////////////////////////////////////////////////////////////////////////////// - -// all times in this structure are in PPQN -struct cbox_track_playback_item -{ - uint32_t time; - struct cbox_midi_pattern_playback *pattern; - uint32_t offset; - uint32_t length; - // in future, it should also contain a pre-calculated list of notes to release -}; - -struct cbox_track_playback -{ - struct cbox_uuid track_uuid; // used as identification only - struct cbox_track_playback_item *items; - struct cbox_master *master; - uint32_t items_count; - uint32_t pos; - uint32_t generation; // of the original track - int ref_count; - struct cbox_midi_buffer output_buffer; - struct cbox_midi_clip_playback playback; - struct cbox_midi_playback_active_notes active_notes; - struct cbox_midi_merger *external_merger; - struct cbox_song_playback *spb; - struct cbox_track_playback *old_state; - gboolean state_copied; - gboolean mute; -}; - -extern struct cbox_track_playback *cbox_track_playback_new_from_track(struct cbox_track *track, struct cbox_master *master, struct cbox_song_playback *spb, struct cbox_track_playback *old_state); -extern void cbox_track_playback_render(struct cbox_track_playback *pb, uint32_t offset, uint32_t nsamples); -extern void cbox_track_playback_seek_ppqn(struct cbox_track_playback *pb, uint32_t time_ppqn, uint32_t min_time_ppqn); -extern void cbox_track_playback_seek_samples(struct cbox_track_playback *pb, uint32_t time_samples); -extern void cbox_track_playback_start_item(struct cbox_track_playback *pb, int time, int is_ppqn, int skip_this_pos); -extern void cbox_track_confirm_stuck_notes(struct cbox_track_playback *pb, struct cbox_midi_playback_active_notes *stuck_notes, uint32_t new_pos); -extern void cbox_track_playback_destroy(struct cbox_track_playback *pb); - -///////////////////////////////////////////////////////////////////////////////////////////////////// - -struct cbox_tempo_map_item -{ - uint32_t time_ppqn; - uint32_t time_samples; - double tempo; - int timesig_num, timesig_denom; - - struct cbox_bbt bbt; -}; - -///////////////////////////////////////////////////////////////////////////////////////////////////// - -struct cbox_adhoc_pattern -{ - struct cbox_adhoc_pattern *next; - - struct cbox_master *master; - struct cbox_midi_pattern *pattern; - struct cbox_midi_pattern_playback *pattern_playback; - - struct cbox_midi_playback_active_notes active_notes; - struct cbox_midi_clip_playback playback; - - struct cbox_midi_buffer output_buffer; - int id; - gboolean completed; -}; - -extern struct cbox_adhoc_pattern *cbox_adhoc_pattern_new(struct cbox_engine *engine, int id, struct cbox_midi_pattern *pattern); -extern void cbox_adhoc_pattern_render(struct cbox_adhoc_pattern *adp, uint32_t offset, uint32_t nsamples); -extern void cbox_adhoc_pattern_destroy(struct cbox_adhoc_pattern *ap); - -///////////////////////////////////////////////////////////////////////////////////////////////////// - -struct cbox_song_playback -{ - struct cbox_master *master; - struct cbox_song *song; // for identification only - struct cbox_track_playback **tracks; - uint32_t track_count; - struct cbox_tempo_map_item *tempo_map_items; - int tempo_map_item_count; - int tempo_map_pos; - uint32_t song_pos_samples, song_pos_ppqn, min_time_ppqn; - uint32_t loop_start_ppqn, loop_end_ppqn; - GHashTable *pattern_map; - struct cbox_midi_merger track_merger; - struct cbox_engine *engine; -}; - -extern struct cbox_song_playback *cbox_song_playback_new(struct cbox_song *song, struct cbox_master *master, struct cbox_engine *engine, struct cbox_song_playback *old_state); -extern void cbox_song_playback_prepare_render(struct cbox_song_playback *spb); -extern void cbox_song_playback_render(struct cbox_song_playback *spb, struct cbox_midi_buffer *output, uint32_t nsamples); -extern int cbox_song_playback_active_notes_release(struct cbox_song_playback *old_spb, struct cbox_song_playback *new_spb, uint32_t new_pos, struct cbox_midi_buffer *buf); -extern void cbox_song_playback_seek_ppqn(struct cbox_song_playback *spb, int time_ppqn, int skip_this_pos); -extern void cbox_song_playback_seek_samples(struct cbox_song_playback *spb, uint32_t time_samples); -extern int cbox_song_playback_tmi_from_ppqn(struct cbox_song_playback *spb, uint32_t time_ppqn); -extern int cbox_song_playback_tmi_from_samples(struct cbox_song_playback *spb, uint32_t time_samples); -struct cbox_midi_pattern_playback *cbox_song_playback_get_pattern(struct cbox_song_playback *spb, struct cbox_midi_pattern *pattern); -extern void cbox_song_playback_apply_old_state(struct cbox_song_playback *spb); -uint32_t cbox_song_playback_correct_for_looping(struct cbox_song_playback *spb, uint32_t abs_samples); -extern void cbox_song_playback_destroy(struct cbox_song_playback *spb); - -///////////////////////////////////////////////////////////////////////////////////////////////////// - -struct cbox_song_time_mapper -{ - struct cbox_time_mapper tmap; - struct cbox_engine *engine; -}; - -extern void cbox_song_time_mapper_init(struct cbox_song_time_mapper *tmap, struct cbox_engine *engine); - -#endif diff --git a/template/calfbox/setup.py b/template/calfbox/setup.py deleted file mode 100755 index 79c1476..0000000 --- a/template/calfbox/setup.py +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/env python3 - -from distutils.core import setup, Extension -import glob -import os -import sys - -support_ext_module = False - -ext_modules = [] -if support_ext_module: - if sys.version_info[0] < 3: - raise Exception("Python 3 required.") - - packages = ['glib-2.0', 'sndfile'] - - if '#define USE_FLUIDSYNTH 1' in open('config.h').read(): - packages.append('fluidsynth') - if '#define USE_JACK 1' in open('config.h').read(): - packages.append('jack') - if '#define USE_LIBUSB 1' in open('config.h').read(): - packages.append('libusb-1.0') - if '#define USE_LIBSMF 1' in open('config.h').read(): - packages.append('smf') - - eargs = os.popen("pkg-config --cflags %s" % (" ".join(packages)), "r").read().split() - eargs.append("-std=c99") - # Workaround for Python3.4 headers - eargs.append("-Wno-error=declaration-after-statement") - - libs = os.popen("pkg-config --libs %s" % (" ".join(packages)), "r").read().split() - libs.append("-luuid") - - csources = [ - "app.c", - "auxbus.c", - "blob.c", - "@chorus.c", - "cmd.c", - "@compressor.c", - "config-api.c", - "@delay.c", - "@distortion.c", - "dom.c", - "eq.c", - "engine.c", - "errors.c", - "@fbr.c", - "fifo.c", - "@fluid.c", - "@fuzz.c", - "@fxchain.c", - "@gate.c", - "hwcfg.c", - "instr.c", - "io.c", - "@jackinput.c", - "@jackio.c", - "layer.c", - "@limiter.c", - "master.c", - "meter.c", - "midi.c", - "mididest.c", - "module.c", - "pattern.c", - "pattern-maker.c", - "@phaser.c", - "prefetch_pipe.c", - "recsrc.c", - "@reverb.c", - "rt.c", - "sampler.c", - "@sampler_channel.c", - "@sampler_gen.c", - "sampler_layer.c", - "@sampler_nif.c", - "@sampler_prevoice.c", - "sampler_prg.c", - "@sampler_rll.c", - "@sampler_voice.c", - "scene.c", - "scripting.c", - "seq.c", - "@seq-adhoc.c", - "sfzloader.c", - "sfzparser.c", - "song.c", - "@streamplay.c", - "@streamrec.c", - "tarfile.c", - "@tonectl.c", - "@tonewheel.c", - "track.c", - "@usbaudio.c", - "@usbio.c", - "@usbmidi.c", - "@usbprobe.c", - "wavebank.c", - ] - - headers = [ - "biquad-float.h", - "config.h", - "dspmath.h", - "envelope.h", - "ioenv.h", - "onepole-float.h", - "onepole-int.h", - "sampler_impl.h", - "stm.h", - "usbio_impl.h", - ] - - headers += [fn[:-2] + ".h" for fn in csources if fn.endswith(".c") and not fn.startswith("@")] - csources = [fn.lstrip("@") for fn in csources] - - if '#define USE_SSE 1' in open('config.h').read(): - eargs.append('-msse') - eargs.append('-ffast-math') - if '#define USE_NEON 1' in open('config.h').read(): - eargs.append('-mfloat-abi=hard') - eargs.append('-mfpu=neon') - eargs.append('-ffast-math') - - ext_modules.append( - Extension('_cbox', csources, - extra_compile_args = eargs, - include_dirs=['.'], - extra_link_args=libs, - define_macros=[("_GNU_SOURCE","1"),("_POSIX_C_SOURCE", "199309L"),("USE_PYTHON","1"),("CALFBOX_AS_MODULE", "1")], - undef_macros=['NDEBUG'], - depends = ['setup.py'] + headers - ) - ) -setup(name="CalfBox", - version="0.0.0.2", description="Assorted music-related code", - author="Krzysztof Foltman", author_email="wdev@foltman.com", - url="https://github.com/kfoltman/calfbox", - packages=["calfbox"], - package_dir={'calfbox':'py'}, - ext_modules=ext_modules, -) diff --git a/template/calfbox/sfzloader.c b/template/calfbox/sfzloader.c deleted file mode 100644 index 83465c5..0000000 --- a/template/calfbox/sfzloader.c +++ /dev/null @@ -1,298 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "sampler.h" -#include "sfzparser.h" -#include "sampler_impl.h" - -#define DUMP_LAYER_ATTRIBS 0 - -enum sfz_load_section_type -{ - slst_normal, - slst_control, - slst_effect, - slst_curve, -}; - -struct sfz_load_state -{ - struct sampler_module *m; - const char *filename, *default_path; - struct sampler_program *program; - struct sampler_layer *global, *master, *group, *region, *target; - struct sampler_midi_curve *curve; - enum sfz_load_section_type section_type; - uint32_t curve_index; - GError **error; -}; - -static void load_sfz_end_region(struct sfz_parser_client *client) -{ - struct sfz_load_state *ls = client->user_data; - // printf("-- copy current region to the list of layers\n"); - struct sampler_layer *l = ls->region; - sampler_layer_data_finalize(&l->data, l->parent ? &l->parent->data : NULL, ls->program); - sampler_layer_reset_switches(l, ls->m); - sampler_layer_update(l); - sampler_program_add_layer(ls->program, ls->region); - - ls->region = NULL; -} - -static void end_token(struct sfz_parser_client *client) -{ - struct sfz_load_state *ls = client->user_data; -#if DUMP_LAYER_ATTRIBS - if (ls->group) - { - fprintf(stdout, ""); - sampler_layer_dump(&ls->group, stdout); - } - if (ls->region) - { - fprintf(stdout, ""); - sampler_layer_dump(&ls->region, stdout); - } -#endif - if (ls->section_type == slst_curve) - { - uint32_t i = ls->curve_index; - if (i < MAX_MIDI_CURVES) - { - if (ls->program->curves[i]) - g_free(ls->program->curves[i]); - else - ls->program->interpolated_curves[i] = g_new(float, 128); - sampler_midi_curve_interpolate(ls->curve, ls->program->interpolated_curves[i], 0, 1, FALSE); - ls->program->curves[i] = ls->curve; - } - else - { - if (i == (uint32_t)-1) - g_warning("Curve index not specified"); - else - g_warning("Curve number %u is greater than the maximum of %u", (unsigned)i, (unsigned)MAX_MIDI_CURVES); - g_free(ls->curve); - } - ls->curve = NULL; - } - if (ls->region) - load_sfz_end_region(client); - ls->region = NULL; - ls->section_type = slst_normal; -} - -static gboolean load_sfz_global(struct sfz_parser_client *client) -{ - struct sfz_load_state *ls = client->user_data; - // printf("-- start global\n"); - ls->target = ls->global = ls->program->global; - ls->master = ls->global->default_child; - ls->group = ls->master->default_child; - return TRUE; -} - -static gboolean load_sfz_master(struct sfz_parser_client *client) -{ - struct sfz_load_state *ls = client->user_data; - // printf("-- start master\n"); - ls->target = ls->master = sampler_layer_new(ls->m, ls->program, ls->program->global); - ls->group = ls->master->default_child = sampler_layer_new(ls->m, ls->program, ls->master); - return TRUE; -} - -static gboolean load_sfz_group(struct sfz_parser_client *client) -{ - struct sfz_load_state *ls = client->user_data; - // printf("-- start group\n"); - ls->target = ls->group = sampler_layer_new(ls->m, ls->program, ls->master); - return TRUE; -} - -static gboolean load_sfz_region(struct sfz_parser_client *client) -{ - struct sfz_load_state *ls = client->user_data; - - ls->target = ls->region = sampler_layer_new(ls->m, ls->program, ls->group); - // g_warning("-- start region"); - return TRUE; -} - -static gboolean load_sfz_control(struct sfz_parser_client *client) -{ - struct sfz_load_state *ls = client->user_data; - ls->section_type = slst_control; - return TRUE; -} - -static gboolean load_sfz_curve(struct sfz_parser_client *client) -{ - struct sfz_load_state *ls = client->user_data; - ls->section_type = slst_curve; - ls->curve = g_new0(struct sampler_midi_curve, 1); - ls->curve_index = -1; - sampler_midi_curve_init(ls->curve); - return TRUE; -} - -static gboolean load_sfz_key_value(struct sfz_parser_client *client, const char *key, const char *value) -{ - struct sfz_load_state *ls = client->user_data; - - if (ls->section_type == slst_curve) - { - if (key[0] == 'v' && isdigit(key[1])) - { - int pos = atoi(key + 1); - if (pos >= 0 && pos < 128) - { - double fvalue = -1; - if (!atof_C_verify(key, value, &fvalue, ls->error)) - return FALSE; - ls->curve->values[pos] = fvalue; - return TRUE; - } - else - g_warning("Out of range curve point: %s", key); - } - else if (!strcmp(key, "curve_index")) - { - ls->curve_index = atoi(value); - return TRUE; - } - else - g_warning("Unknown parameter in curve section: %s=%s", key, value); - return TRUE; - } - if (ls->section_type == slst_effect) - { - g_warning("Parameter found in unsupported effect section: %s=%s", key, value); - return TRUE; - } - if (ls->section_type == slst_control) - { - if (!strncmp(key, "label_cc", 8)) - { - int ctrl = atoi(key + 8); - sampler_program_add_controller_label(ls->program, ctrl, g_strdup(value)); - } - else if (!strncmp(key, "label_key", 9)) - { - int pitch = atoi(key + 9); - sampler_program_add_pitch_label(ls->program, pitch, g_strdup(value)); - } - else if (!strncmp(key, "set_cc", 6)) - { - int ctrl = atoi(key + 6); - int val = atoi(value); - if (ctrl >= 0 && ctrl < CC_COUNT && val >=0 && val <= 127) - sampler_program_add_controller_init(ls->program, ctrl, val); - else - g_warning("Invalid CC initialisation: %s=%s", key, value); - } - else if (!strcmp(key, "default_path")) - { - g_free(ls->program->sample_dir); - gchar *dir = g_path_get_dirname(ls->filename); - char value2[strlen(value) + 1]; - int i; - for (i = 0; value[i]; ++i) - value2[i] = value[i] == '\\' ? '/' : value[i]; - value2[i] = '\0'; - gchar *combined = g_build_filename(dir, value2, NULL); - ls->program->sample_dir = combined; - g_free(dir); - } - else - g_warning("Unrecognized SFZ key in control section: %s", key); - return TRUE; - } - - struct sampler_layer *l = ls->target; - if (!ls->target) - { - g_warning("Parameter '%s' entered outside of global, master, region or group", key); - return TRUE; - } - - if (!sampler_layer_apply_param(l, key, value, ls->error)) - return FALSE; - - return TRUE; -} - -static gboolean handle_token(struct sfz_parser_client *client, const char *token, GError **error) -{ - struct sfz_load_state *ls = client->user_data; - end_token(client); - - if (!strcmp(token, "region")) - return load_sfz_region(client); - - if (!strcmp(token, "group")) - return load_sfz_group(client); - - if (!strcmp(token, "master")) - return load_sfz_master(client); - - if (!strcmp(token, "global")) - return load_sfz_global(client); - - if (!strcmp(token, "control")) - return load_sfz_control(client); - - if (!strcmp(token, "curve")) - return load_sfz_curve(client); - - if (!strcmp(token, "effect")) - { - ls->section_type = slst_effect; - return TRUE; - } - - g_set_error(error, CBOX_SFZPARSER_ERROR, CBOX_SFZ_PARSER_ERROR_INVALID_HEADER, "Unexpected header <%s>", token); - return FALSE; -} - -gboolean sampler_module_load_program_sfz(struct sampler_module *m, struct sampler_program *prg, const char *sfz, int is_from_string, GError **error) -{ - struct sfz_load_state ls = { .global = prg->global, .master = prg->global->default_child, .group = prg->global->default_child->default_child, .target = NULL, .m = m, .filename = sfz, .region = NULL, .error = error, .program = prg, .section_type = slst_normal, .default_path = NULL }; - struct sfz_parser_client c = { .user_data = &ls, .token = handle_token, .key_value = load_sfz_key_value }; - g_clear_error(error); - - gboolean status; - if (is_from_string) - status = load_sfz_from_string(sfz, strlen(sfz), &c, error); - else - { - status = load_sfz(sfz, prg->tarfile, &c, error); //Loads the audio files but also sets fields, like prg->sample_dir. After this we cannot modify any values anymore. - } - if (!status) - { - if (ls.region) - CBOX_DELETE(ls.region); - return FALSE; - } - - end_token(&c); - - prg->all_layers = g_slist_reverse(prg->all_layers); - sampler_program_update_layers(prg); - return TRUE; -} diff --git a/template/calfbox/sfzloader.h b/template/calfbox/sfzloader.h deleted file mode 100644 index 4dfa47e..0000000 --- a/template/calfbox/sfzloader.h +++ /dev/null @@ -1,26 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_SFZLOADER_H -#define CBOX_SFZLOADER_H - -#include "sampler.h" - -gboolean sampler_module_load_program_sfz(struct sampler_module *m, struct sampler_program *prg, const char *sfz, int is_from_string, GError **error); - -#endif diff --git a/template/calfbox/sfzparser.c b/template/calfbox/sfzparser.c deleted file mode 100644 index c848b05..0000000 --- a/template/calfbox/sfzparser.c +++ /dev/null @@ -1,532 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "sfzparser.h" -#include "tarfile.h" -#include -#include -#include -#include -#include - -int debug_variable_definitions = 0; -int debug_variable_substitutions = 0; -int debug_includes = 0; - -struct sfz_parser_state -{ - struct sfz_parser_client *client; - gboolean (*handler)(struct sfz_parser_state *state, int ch); - const char *filename; - const char *buf; - int pos, len, line; - int token_start; - int key_start, key_end; - int value_start, value_end; - GHashTable *variables; - struct cbox_tarfile *tarfile; - GError **error; -}; - -static gboolean load_sfz_into_state(struct sfz_parser_state *s, const char *name); -static gboolean handle_char(struct sfz_parser_state *state, int ch); - -static void unexpected_char(struct sfz_parser_state *state, int ch) -{ - g_set_error(state->error, CBOX_SFZPARSER_ERROR, CBOX_SFZ_PARSER_ERROR_INVALID_CHAR, "Unexpected character '%c' (%d)", ch, ch); -} - -static gboolean handle_header(struct sfz_parser_state *state, int ch) -{ - if (ch >= 'a' && ch <= 'z') - return TRUE; - if (ch == '>') - { - char *token = g_strndup(state->buf + state->token_start, state->pos - 1 - state->token_start); - gboolean result = state->client->token(state->client, token, state->error); - g_free(token); - state->handler = handle_char; - return result; - } - unexpected_char(state, ch); - return FALSE; -} - -static void scan_for_value(struct sfz_parser_state *state) -{ - state->value_start = state->pos; - while(state->pos < state->len) - { - if (state->pos < state->len + 2 && state->buf[state->pos] == '/' && state->buf[state->pos + 1] == '/') - { - state->value_end = state->pos; - while(state->value_end > state->value_start && isspace(state->buf[state->value_end - 1])) - state->value_end--; - state->pos += 2; - while (state->pos < state->len && state->buf[state->pos] != '\r' && state->buf[state->pos] != '\n') - state->pos++; - return; - } - int ch = state->buf[state->pos]; - if (ch == 0 || ch == '\r' || ch == '\n' || ch == '<') - { - state->value_end = state->pos; - // remove spaces before next key - while(state->value_end > state->value_start && isspace(state->buf[state->value_end - 1])) - state->value_end--; - return; - } - if (ch == '=') - { - state->value_end = state->pos; - // remove next key - while(state->value_end > state->value_start && !isspace(state->buf[state->value_end - 1])) - state->value_end--; - // remove spaces before next key - while(state->value_end > state->value_start && isspace(state->buf[state->value_end - 1])) - state->value_end--; - state->pos = state->value_end; - return; - } - state->pos++; - } - state->value_end = state->pos; - while(state->value_end > state->value_start && isspace(state->buf[state->value_end - 1])) - state->value_end--; -} - -static gchar *expand_variables(struct sfz_parser_state *state, gchar *text) -{ - gchar *pos = strchr(text, '$'); - // No variables, no changes. - if (!pos) - return text; - - GString *result = g_string_new_len(text, pos - text); - pos++; - // Start of the variable name - gchar *start = pos; - if (!*start) - return text; - pos = start + 1; - while(1) - { - gchar ch = *pos; - gpointer value; - if (ch) - { - *pos = '\0'; - value = g_hash_table_lookup(state->variables, start); - *pos = ch; - } - else - value = g_hash_table_lookup(state->variables, start); - if (value) - { - g_string_append(result, value); - // pos = first char that is not part of the variable name - - if (!ch) - break; - start = strchr(pos, '$'); - if (!start) - { - // Remainder has no variable references, add and stop - g_string_append(result, pos); - break; - } - // Add everything up to the next variable reference - if (start != pos) - g_string_append_len(result, pos, start - pos); - // Restart, variable name starts at the next character - start++; - if (!*start) - break; - pos = start + 1; - } - else - { - if (!ch) - { - // Might throw an error here, but just quote the var name verbatim instead for now - g_string_append(result, "$"); - g_string_append(result, start); - break; - } - pos++; - } - } - // Replace with a substituted version - gchar *substituted = g_string_free(result, FALSE); - if (debug_variable_substitutions) - printf("Substitute: '%s' -> '%s'\n", text, substituted); - g_free(text); - return substituted; -} - -static gboolean handle_key(struct sfz_parser_state *state, int ch) -{ - if (isalpha(ch) || isdigit(ch) || ch == '_') - return TRUE; - if(ch == '=') - { - state->key_end = state->pos - 1; - scan_for_value(state); - - gchar *key = g_strndup(state->buf + state->key_start, state->key_end - state->key_start); - gchar *value = g_strndup(state->buf + state->value_start, state->value_end - state->value_start); - key = expand_variables(state, key); - value = expand_variables(state, value); - gboolean result = state->client->key_value(state->client, key, value); - g_free(key); - g_free(value); - state->handler = handle_char; - return result; - } - unexpected_char(state, ch); - return FALSE; -} - -static gboolean do_include(struct sfz_parser_state *state, const char *name) -{ - if (debug_includes) - printf("Include file: %s\n", name); - gchar *dir = g_path_get_dirname(state->filename); - gchar *combined = g_build_filename(dir, name, NULL); - gboolean result = load_sfz_into_state(state, combined); - g_free(combined); - g_free(dir); - if (debug_includes) - printf("End include file: %s\n", name); - return result; -} - -static void do_define(struct sfz_parser_state *state, char *key, char *value) -{ - if (debug_variable_definitions) - printf("Define: '%s' -> '%s'\n", key, value); - g_hash_table_insert(state->variables, key, value); -} - -static gboolean handle_include_filename(struct sfz_parser_state *state, int ch) -{ - if (ch == '"') - { - char *token = g_strndup(state->buf + state->token_start, state->pos - 1 - state->token_start); - gboolean result = do_include(state, token); - g_free(token); - state->handler = handle_char; - return result; - } - if ((unsigned)(ch) >= ' ') - return TRUE; - unexpected_char(state, ch); - return FALSE; -} - -static gboolean handle_include_skipwhite(struct sfz_parser_state *state, int ch) -{ - if (isspace(ch)) - return TRUE; - if (ch == '"') - { - state->token_start = state->pos; - state->handler = handle_include_filename; - return TRUE; - } - unexpected_char(state, ch); - return FALSE; -} - -static gboolean handle_define_value(struct sfz_parser_state *state, int ch) -{ - if (ch == '\n' || ch == '\r' || ch == -1) - { - char *key = g_strndup(state->buf + state->key_start, state->key_end - state->key_start); - char *value = g_strndup(state->buf + state->value_start, state->pos - state->value_start - 1); - do_define(state, key, value); - state->handler = handle_char; - return TRUE; - } - return TRUE; -} - -static gboolean handle_define_skipwhite2(struct sfz_parser_state *state, int ch) -{ - if (ch == '\n' || ch == '\r' || ch == -1) - { - char *key = g_strndup(state->buf + state->key_start, state->key_end - state->key_start); - g_set_error(state->error, CBOX_SFZPARSER_ERROR, CBOX_SFZ_PARSER_ERROR_INVALID_CHAR, "Unspecified variable value for '%s'", key); - g_free(key); - return FALSE; - } - if (isspace(ch)) - return TRUE; - state->value_start = state->pos - 1; - state->handler = handle_define_value; - return TRUE; -} - -static gboolean handle_define_varname(struct sfz_parser_state *state, int ch) -{ - if (ch == '\n' || ch == '\r' || ch == -1) - { - char *key = g_strndup(state->buf + state->key_start, state->key_end - state->key_start); - g_set_error(state->error, CBOX_SFZPARSER_ERROR, CBOX_SFZ_PARSER_ERROR_INVALID_CHAR, "Unspecified variable name"); - g_free(key); - return FALSE; - } - if (isspace(ch)) - { - state->key_end = state->pos - 1; - state->handler = handle_define_skipwhite2; - return TRUE; - } - if (ch >= 33 && ch <= 127 && ch != '$') - return TRUE; - unexpected_char(state, ch); - return FALSE; -} - -static gboolean handle_define_skipwhite(struct sfz_parser_state *state, int ch) -{ - if (isspace(ch)) - return TRUE; - if (ch == '$') - { - state->key_start = state->pos; - state->handler = handle_define_varname; - return TRUE; - } - - unexpected_char(state, ch); - return FALSE; -} - -static gboolean handle_preprocessor(struct sfz_parser_state *state, int ch) -{ - if (isalpha(ch)) - return TRUE; - if (isspace(ch)) - { - if (!memcmp(state->buf + state->token_start, "include", state->pos - state->token_start - 1)) - { - state->handler = handle_include_skipwhite; - return TRUE; - } - if (!memcmp(state->buf + state->token_start, "define", state->pos - state->token_start - 1)) - { - state->handler = handle_define_skipwhite; - return TRUE; - } - char *preproc = g_strndup(state->buf + state->token_start, state->pos - state->token_start - 1); - g_set_error(state->error, CBOX_SFZPARSER_ERROR, CBOX_SFZ_PARSER_ERROR_INVALID_CHAR, "%s:%d Unsupported parser directive '%s'", state->filename, state->line, preproc); - g_free(preproc); - state->handler = handle_char; - return FALSE; - } - unexpected_char(state, ch); - return FALSE; -} - -static gboolean handle_char(struct sfz_parser_state *state, int ch) -{ - if (isalpha(ch) || isdigit(ch)) - { - state->key_start = state->pos - 1; - state->handler = handle_key; - return TRUE; - } - switch(ch) - { - case '_': - return TRUE; - - case '\r': - case '\n': - case ' ': - case '\t': - case -1: - return TRUE; - case '<': - state->token_start = state->pos; - state->handler = handle_header; - return TRUE; - case '#': - state->token_start = state->pos; - state->handler = handle_preprocessor; - return TRUE; - default: - unexpected_char(state, ch); - return FALSE; - } -} - -gboolean load_sfz_from_string_into_state(struct sfz_parser_state *s, const char *buf, int len) -{ - const char *oldbuf = s->buf; - int oldpos = s->pos, oldlen = s->len, oldline = s->line; - gboolean ok = FALSE; - s->buf = buf; - s->pos = 0; - s->len = len; - s->handler = handle_char; - s->token_start = 0; - if (len >= 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF) - { - // UTF-8 BOM - s->pos += 3; - } - while(s->pos < len && s->handler != NULL) - { - if (s->pos < len + 2 && buf[s->pos] == '/' && buf[s->pos + 1] == '/') - { - s->pos += 2; - while (s->pos < len && buf[s->pos] != '\r' && buf[s->pos] != '\n') - s->pos++; - continue; - } - char ch = buf[s->pos++]; - gboolean newline = FALSE, eat_lf = FALSE; - // Convert CR or CR/LF to LF - if (ch == '\r') { - newline = TRUE; - eat_lf = (buf[s->pos] == '\n'); - ch = '\n'; - } else if (ch == '\n') - newline = TRUE; - - if (!(*s->handler)(s, ch)) - goto restore; - if (newline) - s->line++; - if (eat_lf) - s->pos++; - } - if (s->handler) - { - if (!(*s->handler)(s, -1)) - goto restore; - } - ok = TRUE; -restore: - s->buf = oldbuf; - s->pos = oldpos; - s->line = oldline; - s->len = oldlen; - s->handler = handle_char; - s->token_start = oldpos; - return ok; -} - -/* - * This is not only called when literally constructing a sfz string - * but also when loading a null instrument e.g. to first create the jack ports and only later - * actually load the sfz and samples, which can be costly. - */ -gboolean load_sfz_from_string(const char *buf, int len, struct sfz_parser_client *c, GError **error) -{ - struct sfz_parser_state s; - memset(&s, 0, sizeof(s)); - s.line = 1; - s.filename = ""; - s.tarfile = NULL; - s.client = c; - s.error = error; - s.variables = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - gboolean result = load_sfz_from_string_into_state(&s, buf, len); - g_hash_table_destroy(s.variables); - return result; -} - -/* - * Called once per sfz. - * Does not load samples, but only the sfz file. - */ -gboolean load_sfz_into_state(struct sfz_parser_state *s, const char *name) -{ - g_clear_error(s->error); - FILE *f; - int len = -1; - if (s->tarfile) - { //This only extracts the .sfz file itself and will not attempt to load any sample waveforms, eventhough cbox_tarfile_get_item_by_name will later be used to extract the sample as well. - struct cbox_taritem *item = cbox_tarfile_get_item_by_name(s->tarfile, name, TRUE); - if (!item) - { - g_set_error(s->error, G_FILE_ERROR, g_file_error_from_errno (2), "Cannot find '%s' in the tarfile", name); - return FALSE; - } - int fd = cbox_tarfile_openitem(s->tarfile, item); - if (fd < 0) - { - g_set_error(s->error, G_FILE_ERROR, g_file_error_from_errno (errno), "Cannot open '%s' in the tarfile", name); - return FALSE; - } - f = fdopen(fd, "rb"); - len = item->size; - } - else - f = fopen(name, "rb"); - - if (!f) - { - g_set_error(s->error, G_FILE_ERROR, g_file_error_from_errno (errno), "Cannot open '%s'", name); - return FALSE; - } - - if (len == -1) - { - fseek(f, 0, SEEK_END); - len = ftell(f); - fseek(f, 0, SEEK_SET); - } - - unsigned char *buf = malloc(len + 1); - buf[len] = '\0'; - if (fread(buf, 1, len, f) != (size_t)len) - { - g_set_error(s->error, G_FILE_ERROR, g_file_error_from_errno (errno), "Cannot read '%s'", name); - fclose(f); - return FALSE; - } - fclose(f); - - gboolean result = load_sfz_from_string_into_state(s, (char *)buf, len); - free(buf); - return result; -} - -gboolean load_sfz(const char *name, struct cbox_tarfile *tarfile, struct sfz_parser_client *c, GError **error) -{ - struct sfz_parser_state s; - memset(&s, 0, sizeof(s)); - s.line = 1; - s.filename = name; - s.tarfile = tarfile; - s.client = c; - s.error = error; - s.variables = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - gboolean result = load_sfz_into_state(&s, name); - g_hash_table_destroy(s.variables); - return result; -} - -GQuark cbox_sfz_parser_error_quark(void) -{ - return g_quark_from_string("cbox-sfz-parser-error-quark"); -} diff --git a/template/calfbox/sfzparser.h b/template/calfbox/sfzparser.h deleted file mode 100644 index a76f829..0000000 --- a/template/calfbox/sfzparser.h +++ /dev/null @@ -1,47 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_SFZPARSER_H -#define CBOX_SFZPARSER_H - -#include - -#define CBOX_SFZPARSER_ERROR cbox_sfz_parser_error_quark() - -enum CboxSfzParserError -{ - CBOX_SFZ_PARSER_ERROR_FAILED, - CBOX_SFZ_PARSER_ERROR_INVALID_CHAR, - CBOX_SFZ_PARSER_ERROR_INVALID_HEADER, -}; - -struct cbox_tarfile; - -struct sfz_parser_client -{ - void *user_data; - gboolean (*token)(struct sfz_parser_client *client, const char *token, GError **error); - gboolean (*key_value)(struct sfz_parser_client *client, const char *key, const char *value); -}; - -extern gboolean load_sfz(const char *name, struct cbox_tarfile *tarfile, struct sfz_parser_client *c, GError **error); -extern gboolean load_sfz_from_string(const char *buf, int len, struct sfz_parser_client *c, GError **error); - -extern GQuark cbox_sfz_parser_error_quark(void); - -#endif diff --git a/template/calfbox/py/sfzparser.py b/template/calfbox/sfzparser.py similarity index 100% rename from template/calfbox/py/sfzparser.py rename to template/calfbox/sfzparser.py diff --git a/template/calfbox/skel.c b/template/calfbox/skel.c deleted file mode 100644 index 572d7df..0000000 --- a/template/calfbox/skel.c +++ /dev/null @@ -1,105 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config.h" -#include "config-api.h" -#include "dspmath.h" -#include "module.h" -#include -#include -#include -#include -#include -#include -#include - -#define MODULE_PARAMS {name}_params - -struct {name}_params -{ -}; - -struct {name}_module -{ - struct cbox_module module; - - struct {name}_params *params, *old_params; -}; - -gboolean {name}_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct {name}_module *m = (struct {name}_module *)ct->user_data; - - // EFFECT_PARAM("/wet_dry", "f", wet_dry, double, , 0, 1) else - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - // return cbox_execute_on(fb, NULL, "/wet_dry", "f", error, m->params->wet_dry); - return CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error); - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); - return TRUE; -} - -void {name}_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) -{ - struct {name}_module *m = module->user_data; -} - -void {name}_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) -{ - struct {name}_module *m = module->user_data; - - if (m->params != m->old_params) - { - // update calculated values - } -} - -MODULE_SIMPLE_DESTROY_FUNCTION({name}) - -MODULE_CREATE_FUNCTION({name}) -{ - static int inited = 0; - if (!inited) - { - inited = 1; - } - - struct {name}_module *m = malloc(sizeof(struct {name}_module)); - CALL_MODULE_INIT(m, 0, 2, {name}); - m->module.process_event = {name}_process_event; - m->module.process_block = {name}_process_block; - struct {name}_params *p = malloc(sizeof(struct {name}_params)); - m->params = p; - m->old_params = NULL; - - return &m->module; -} - - -struct cbox_module_keyrange_metadata {name}_keyranges[] = { -}; - -struct cbox_module_livecontroller_metadata {name}_controllers[] = { -}; - -DEFINE_MODULE({name}, 0, 2) - diff --git a/template/calfbox/song.c b/template/calfbox/song.c deleted file mode 100644 index d8b0978..0000000 --- a/template/calfbox/song.c +++ /dev/null @@ -1,357 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "app.h" -#include "engine.h" -#include "errors.h" -#include "song.h" -#include "track.h" -#include -#include - -CBOX_CLASS_DEFINITION_ROOT(cbox_song) - -///////////////////////////////////////////////////////////////////////////////////////////////// - -void cbox_master_track_item_destroy(struct cbox_master_track_item *item) -{ - free(item); -} - -///////////////////////////////////////////////////////////////////////////////////////////////// - -gboolean cbox_song_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct cbox_song *song = ct->user_data; - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - for(GList *p = song->tracks; p; p = g_list_next(p)) - { - struct cbox_track *trk = p->data; - if (!cbox_execute_on(fb, NULL, "/track", "sio", error, trk->name, g_list_length(trk->items), trk)) - return FALSE; - } - for(GList *p = song->patterns; p; p = g_list_next(p)) - { - struct cbox_midi_pattern *pat = p->data; - if (!cbox_execute_on(fb, NULL, "/pattern", "sio", error, pat->name, pat->loop_end, pat)) - return FALSE; - } - uint32_t pos = 0; - for(GList *p = song->master_track_items; p; p = g_list_next(p)) - { - struct cbox_master_track_item *mti = p->data; - // Omit dummy item at 0 position. - if (pos || (mti->timesig_num && mti->timesig_denom) || mti->tempo) - { - if (!cbox_execute_on(fb, NULL, "/mti", "ifii", error, pos, mti->tempo, mti->timesig_num, mti->timesig_denom)) - return FALSE; - } - pos += mti->duration_ppqn; - } - return cbox_execute_on(fb, NULL, "/loop_start", "i", error, (int)song->loop_start_ppqn) && - cbox_execute_on(fb, NULL, "/loop_end", "i", error, (int)song->loop_end_ppqn) && - CBOX_OBJECT_DEFAULT_STATUS(song, fb, error); - } - else - if (!strcmp(cmd->command, "/set_loop") && !strcmp(cmd->arg_types, "ii")) - { - song->loop_start_ppqn = CBOX_ARG_I(cmd, 0); - song->loop_end_ppqn = CBOX_ARG_I(cmd, 1); - return TRUE; - } - else - if (!strcmp(cmd->command, "/set_mti") && !strcmp(cmd->arg_types, "ifii")) - { - cbox_song_set_mti(song, CBOX_ARG_I(cmd, 0), CBOX_ARG_F(cmd, 1), CBOX_ARG_I(cmd, 2), CBOX_ARG_I(cmd, 3)); - return TRUE; - } - else - if (!strcmp(cmd->command, "/clear") && !strcmp(cmd->arg_types, "")) - { - cbox_song_clear(song); - return TRUE; - } - else - if (!strcmp(cmd->command, "/add_track") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - struct cbox_track *track = cbox_track_new(CBOX_GET_DOCUMENT(song)); - cbox_song_add_track(song, track); - if (!cbox_execute_on(fb, NULL, "/uuid", "o", error, track)) - { - CBOX_DELETE(track); - return FALSE; - } - - return TRUE; - } - else - if (!strcmp(cmd->command, "/load_pattern") && !strcmp(cmd->arg_types, "si")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - struct cbox_midi_pattern *pattern = cbox_midi_pattern_load(song, CBOX_ARG_S(cmd, 0), CBOX_ARG_I(cmd, 1), app.engine->master->ppqn_factor); - if (!cbox_execute_on(fb, NULL, "/uuid", "o", error, pattern)) - { - CBOX_DELETE(pattern); - return FALSE; - } - - return TRUE; - } - else - if (!strcmp(cmd->command, "/load_track") && !strcmp(cmd->arg_types, "si")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - struct cbox_midi_pattern *pattern = cbox_midi_pattern_load_track(song, CBOX_ARG_S(cmd, 0), CBOX_ARG_I(cmd, 1), app.engine->master->ppqn_factor); - if (!cbox_execute_on(fb, NULL, "/uuid", "o", error, pattern)) - { - CBOX_DELETE(pattern); - return FALSE; - } - - return TRUE; - } - else - if (!strcmp(cmd->command, "/load_metronome") && !strcmp(cmd->arg_types, "i")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - struct cbox_midi_pattern *pattern = cbox_midi_pattern_new_metronome(song, CBOX_ARG_I(cmd, 0), app.engine->master->ppqn_factor); - if (!cbox_execute_on(fb, NULL, "/uuid", "o", error, pattern)) - { - CBOX_DELETE(pattern); - return FALSE; - } - - return TRUE; - } - else - if (!strcmp(cmd->command, "/load_blob") && !strcmp(cmd->arg_types, "bi")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - struct cbox_midi_pattern *pattern = cbox_midi_pattern_new_from_blob(song, CBOX_ARG_B(cmd, 0), CBOX_ARG_I(cmd, 1), app.engine->master->ppqn_factor); - if (!cbox_execute_on(fb, NULL, "/uuid", "o", error, pattern)) - { - CBOX_DELETE(pattern); - return FALSE; - } - - return TRUE; - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); - return TRUE; -} - - -///////////////////////////////////////////////////////////////////////////////////////////////// - -struct cbox_song *cbox_song_new(struct cbox_document *document) -{ - struct cbox_song *p = calloc(1, sizeof(struct cbox_song)); - CBOX_OBJECT_HEADER_INIT(p, cbox_song, document); - - // Create the first, dummy tempo map item - struct cbox_master_track_item *mti = calloc(1, sizeof(struct cbox_master_track_item)); - mti->timesig_num = 0; - mti->timesig_denom = 0; - mti->tempo = 0; - mti->duration_ppqn = 0; - - p->master_track_items = g_list_append(NULL, mti); - p->tracks = NULL; - p->patterns = NULL; - p->lyrics_sheet = NULL; - p->chord_sheet = NULL; - p->loop_start_ppqn = 0; - p->loop_end_ppqn = 0; - cbox_command_target_init(&p->cmd_target, cbox_song_process_cmd, p); - CBOX_OBJECT_REGISTER(p); - - return p; -} - -void cbox_song_set_mti(struct cbox_song *song, uint32_t pos, double tempo, int timesig_num, int timesig_denom) -{ - uint32_t tstart = 0, tend = 0; - GList *prev = NULL; - // A full no-op - if (tempo < 0 && timesig_num < 0) - return; - gboolean is_noop = tempo == 0 && timesig_num == 0; - - struct cbox_master_track_item *mti = NULL; - for(GList *p = song->master_track_items; p; p = g_list_next(p)) - { - mti = p->data; - tend = tstart + mti->duration_ppqn; - // printf("range %d-%d %f %d\n", tstart, tend, mti->tempo, mti->timesig_num); - if (pos == tstart) - { - double new_tempo = tempo >= 0 ? tempo : mti->tempo; - int new_timesig_num = timesig_num >= 0 ? timesig_num : mti->timesig_num; - // Is this operation going to become a no-op after the change? - gboolean is_noop_here = new_tempo <= 0 && new_timesig_num <= 0; - // If the new item is a no-op and not the first item, delete it - // and extend the previous item by deleted item's duration - if (is_noop_here) - { - uint32_t deleted_duration = mti->duration_ppqn; - if (prev) { - song->master_track_items = g_list_remove(song->master_track_items, mti); - mti = prev->data; - mti->duration_ppqn += deleted_duration; - } else { - // Instead of deleting the first item, make it a dummy one. - mti->tempo = 0; - mti->timesig_num = 0; - mti->timesig_denom = 0; - } - return; - } - goto set_values; - } - if (pos >= tstart && pos < tend) - { - if (is_noop || (tempo <= 0 && timesig_num <= 0)) - return; - // Split old item's duration - mti->duration_ppqn = pos - tstart; - mti = calloc(1, sizeof(struct cbox_master_track_item)); - mti->duration_ppqn = tend - pos; - p = g_list_next(p); - song->master_track_items = g_list_insert_before(song->master_track_items, p, mti); - goto set_values; - } - prev = p; - tstart = tend; - } - // The new item is a no-op and it's not deleting any of the current MTIs. - // Ignore it then. - if (is_noop) - return; - // The add position is past the end of the current MTIs. - if (pos > tend) - { - // Either extend the previous item, if there's any - if (prev) - { - mti = prev->data; - mti->duration_ppqn += pos - tend; - } - else - { - // ... or add a dummy 'pad' item - mti = calloc(1, sizeof(struct cbox_master_track_item)); - mti->duration_ppqn = pos; - assert(!song->master_track_items); - song->master_track_items = g_list_append(song->master_track_items, mti); - prev = song->master_track_items; - } - } - // Add the new item at the end - mti = calloc(1, sizeof(struct cbox_master_track_item)); - song->master_track_items = g_list_append(song->master_track_items, mti); -set_values: - // No effect if -1 - if (tempo >= 0) - mti->tempo = tempo; - if ((timesig_num > 0 && timesig_denom > 0) || - (timesig_num == 0 && timesig_denom == 0)) - { - mti->timesig_num = timesig_num; - mti->timesig_denom = timesig_denom; - } -} - -void cbox_song_add_track(struct cbox_song *song, struct cbox_track *track) -{ - track->owner = song; - song->tracks = g_list_append(song->tracks, track); -} - -void cbox_song_remove_track(struct cbox_song *song, struct cbox_track *track) -{ - assert(track->owner == song); - song->tracks = g_list_remove(song->tracks, track); - track->owner = NULL; -} - -void cbox_song_add_pattern(struct cbox_song *song, struct cbox_midi_pattern *pattern) -{ - pattern->owner = song; - song->patterns = g_list_append(song->patterns, pattern); -} - -void cbox_song_remove_pattern(struct cbox_song *song, struct cbox_midi_pattern *pattern) -{ - assert(pattern->owner == song); - pattern->owner = NULL; - song->patterns = g_list_remove(song->patterns, pattern); -} - -void cbox_song_clear(struct cbox_song *song) -{ - while(song->tracks) - cbox_object_destroy(song->tracks->data); - while(song->patterns) - cbox_object_destroy(song->patterns->data); - while(song->master_track_items) - { - struct cbox_master_track_item *mti = song->master_track_items->data; - song->master_track_items = g_list_remove(song->master_track_items, mti); - cbox_master_track_item_destroy(mti); - } -} - -void cbox_song_use_looped_pattern(struct cbox_song *song, struct cbox_midi_pattern *pattern) -{ - assert(pattern->owner == song); - song->patterns = g_list_remove(song->patterns, pattern); - pattern->owner = NULL; - - cbox_song_clear(song); - struct cbox_track *trk = cbox_track_new(CBOX_GET_DOCUMENT(song)); - cbox_song_add_track(song, trk); - cbox_song_add_pattern(song, pattern); - song->loop_start_ppqn = 0; - song->loop_end_ppqn = pattern->loop_end; - cbox_track_add_item(trk, 0, pattern, 0, pattern->loop_end); - cbox_engine_update_song_playback(app.engine); -} - -void cbox_song_destroyfunc(struct cbox_objhdr *objhdr) -{ - struct cbox_song *song = CBOX_H2O(objhdr); - cbox_song_clear(song); - free(song); -} - diff --git a/template/calfbox/song.h b/template/calfbox/song.h deleted file mode 100644 index cf54bee..0000000 --- a/template/calfbox/song.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_SONG_H -#define CBOX_SONG_H - -#include "dom.h" -#include "pattern.h" - -CBOX_EXTERN_CLASS(cbox_song) - -struct cbox_track; - -struct cbox_master_track_item -{ - uint32_t duration_ppqn; - // May be zero (= no change) - double tempo; - // Either both are zero (= no change) or both are non-zero - int timesig_num, timesig_denom; -}; - -struct cbox_master_track -{ - GList *items; -}; - -struct cbox_song -{ - CBOX_OBJECT_HEADER() - struct cbox_command_target cmd_target; - GList *master_track_items; - GList *tracks; - GList *patterns; - gchar *lyrics_sheet, *chord_sheet; - uint32_t loop_start_ppqn, loop_end_ppqn; -}; - -extern struct cbox_song *cbox_song_new(struct cbox_document *document); -extern void cbox_song_add_track(struct cbox_song *song, struct cbox_track *track); -extern void cbox_song_remove_track(struct cbox_song *song, struct cbox_track *track); -extern void cbox_song_clear(struct cbox_song *song); -extern void cbox_song_use_looped_pattern(struct cbox_song *song, struct cbox_midi_pattern *pattern); -extern void cbox_song_set_mti(struct cbox_song *song, uint32_t pos, double tempo, int timesig_num, int timesig_denom); -extern void cbox_song_destroy(struct cbox_song *song); - -#endif diff --git a/template/calfbox/song_api_example.py b/template/calfbox/song_api_example.py deleted file mode 100644 index a44a07c..0000000 --- a/template/calfbox/song_api_example.py +++ /dev/null @@ -1,76 +0,0 @@ -import os -import sys -import struct -import time -import unittest - -sys.path = ["./py"] + sys.path - -import cbox - -global Document -global Transport -Document = cbox.Document -Transport = cbox.Transport - -# Make sure playback doesn't start prematurely -Transport.stop() - -song = Document.get_song() - -# Delete all the tracks and patterns -song.clear() - -# Add the first track -trk = song.add_track() -trk.clear_clips() - -# Create a binary blob that contains the MIDI events -pblob = bytes() -for noteindex in range(20): - # note on - pblob += cbox.Pattern.serialize_event(noteindex * 24, 0x90, 36+noteindex*3, 127) - # note off - pblob += cbox.Pattern.serialize_event(noteindex * 24 + 23, 0x90, 36+noteindex*3, 0) - -# This will be the length of the pattern (in pulses). It should be large enough -# to fit all the events -pattern_len = 10 * 24 * 2 - -# Create a new pattern object using events from the blob -pattern = song.pattern_from_blob(pblob, pattern_len) - -# Add an instance (clip) of the pattern to the track at position 0 -# The clip will contain the whole pattern (it is also possible to insert -# a single slice of the pattern) -clip = trk.add_clip(0, 0, pattern_len, pattern) - -# Stop the song at the end -#song.set_loop(pattern_len, pattern_len) -song.set_loop(pattern_len, pattern_len) - -# Set tempo - the argument must be a float -Transport.set_tempo(160.0) - -# Send the updated song data to the realtime thread -song.update_playback() - -# Flush -Transport.stop() - -print ("Song length (seconds) is %f" % (cbox.Transport.ppqn_to_samples(pattern_len) * 1.0 / Transport.status().sample_rate)) - -# The /master object API doesn't have any nice Python wrapper yet, so accessing -# it is a bit ugly, still - it works - -# Start playback -Transport.play() -print ("Playing") - -while True: - # Get transport information - current position (samples and pulses), current tempo etc. - master = Transport.status() - print (master.pos_ppqn) - # Query JACK ports, new USB devices etc. - cbox.call_on_idle() - time.sleep(0.1) diff --git a/template/calfbox/song_api_example2.py b/template/calfbox/song_api_example2.py deleted file mode 100644 index 6bbfd0d..0000000 --- a/template/calfbox/song_api_example2.py +++ /dev/null @@ -1,91 +0,0 @@ -import os -import sys -import struct -import time -import unittest - -sys.path = ["./py"] + sys.path - -import cbox - -global Document -global Transport -Document = cbox.Document -Transport = cbox.Transport - -# Make sure playback doesn't start prematurely -Transport.stop() - -song = Document.get_song() - -# Delete all the tracks and patterns -song.clear() - -# Add the first track -trk = song.add_track() - -# Create a binary blob that contains zero MIDI events -emptyblob = bytes() - -# Create a new empty pattern -empty_pattern = song.pattern_from_blob(emptyblob, 16) - -# This will be the length of the pattern (in pulses). It should be large enough -# to fit all the events -pattern_len = 10 * 24 * 2 - -# Add an instance (clip) of the empty pattern to the track -clip1 = trk.add_clip(pattern_len, 0, 16, empty_pattern) - -# Add another instance after it -clip2 = trk.add_clip(2 * pattern_len, 0, 16, empty_pattern) - -# Create a binary blob that contains the MIDI events -pblob = bytes() -for noteindex in range(20): - # note on - pblob += cbox.Pattern.serialize_event(noteindex * 24, 0x90, 36+noteindex*3, 127) - # note off - pblob += cbox.Pattern.serialize_event(noteindex * 24 + 23, 0x90, 36+noteindex*3, 0) - -# Create a new pattern object using events from the blob -pattern = song.pattern_from_blob(pblob, pattern_len) - -# Update all attributes of the second clip, rearranging the order -clip2.set_pattern(pattern) -clip2.set_pos(0) -clip2.set_offset(0) -clip2.set_length(pattern_len) - -# Verify that the clips have been reordered -clips = [o.clip for o in trk.status().clips] -assert clips == [clip2, clip1] - -# Stop the song at the end -song.set_loop(pattern_len, pattern_len) - -# Set tempo - the argument must be a float -Transport.set_tempo(160.0) - -# Send the updated song data to the realtime thread -song.update_playback() - -# Flush -Transport.stop() - -print ("Song length (seconds) is %f" % (cbox.Transport.ppqn_to_samples(pattern_len) * 1.0 / Transport.status().sample_rate)) - -# The /master object API doesn't have any nice Python wrapper yet, so accessing -# it is a bit ugly, still - it works - -# Start playback -Transport.play() -print ("Playing") - -while True: - # Get transport information - current position (samples and pulses), current tempo etc. - master = Transport.status() - print (master.pos_ppqn) - # Query JACK ports, new USB devices etc. - cbox.call_on_idle() - time.sleep(0.1) diff --git a/template/calfbox/stm.h b/template/calfbox/stm.h deleted file mode 100644 index a6c81f6..0000000 --- a/template/calfbox/stm.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef CBOX_STM_H -#define CBOX_STM_H - -#include -#include - -static inline void **stm_array_clone_insert(void **old_array, int old_count, int index, void *data) -{ - size_t ps = sizeof(void *); - if (index == -1) - index = old_count; - void **new_array = malloc(ps * (old_count + 1)); - memcpy(&new_array[0], &old_array[0], ps * index); - new_array[index] = data; - if (index != old_count) - memcpy(&new_array[index + 1], &old_array[index], ps * (old_count - index)); - return new_array; -} - -static inline void **stm_array_clone_remove(void **old_array, int old_count, int index) -{ - size_t ps = sizeof(void *); - if (old_count == 1) - return NULL; - void **new_array = malloc(ps * (old_count - 1)); - memcpy(&new_array[0], &old_array[0], ps * index); - memcpy(&new_array[index], &old_array[index + 1], ps * (old_count - index - 1)); - return new_array; -} - -#define STM_ARRAY_FREE(old_array, count, destructor) \ - for (uint32_t i = 0; i < (count); i++) \ - destructor((old_array)[i]); \ - free(old_array); - -#define STM_ARRAY_FREE_OBJS(old_array, count) \ - do { \ - for (uint32_t i = 0; i < (count); i++) \ - cbox_object_destroy(&(old_array)[i]->_obj_hdr); \ - free(old_array); \ - } while(0) - -#endif \ No newline at end of file diff --git a/template/calfbox/streamplay.c b/template/calfbox/streamplay.c deleted file mode 100644 index abbd9ac..0000000 --- a/template/calfbox/streamplay.c +++ /dev/null @@ -1,734 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "assert.h" -#include "config.h" -#include "config-api.h" -#include "dspmath.h" -#include "fifo.h" -#include "module.h" -#include "rt.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define CBOX_STREAM_PLAYER_ERROR cbox_stream_player_error_quark() - -enum CboxStreamPlayerError -{ - CBOX_STREAM_PLAYER_ERROR_FAILED, -}; - -GQuark cbox_stream_player_error_quark(void) -{ - return g_quark_from_string("cbox-stream-player-error-quark"); -} - -#define CUE_BUFFER_SIZE 16000 -#define PREFETCH_THRESHOLD ((uint32_t)(CUE_BUFFER_SIZE / 4)) -#define MAX_READAHEAD_BUFFERS 3 - -#define NO_SAMPLE_LOOP ((uint64_t)-1ULL) - -struct stream_player_cue_point -{ - volatile uint64_t position; - volatile uint32_t size, length; - float *data; - int queued; -}; - -enum stream_state_phase -{ - STOPPED, - PLAYING, - STOPPING, - STARTING -}; - -struct stream_state -{ - SNDFILE *sndfile; - SF_INFO info; - uint64_t readptr; - uint64_t restart; - uint64_t readptr_new; - - volatile int buffer_in_use; - - struct stream_player_cue_point cp_start, cp_loop, cp_readahead[MAX_READAHEAD_BUFFERS]; - int cp_readahead_ready[MAX_READAHEAD_BUFFERS]; - struct stream_player_cue_point *pcp_current, *pcp_next; - - struct cbox_fifo *rb_for_reading, *rb_just_read; - float gain, fade_gain, fade_increment; - enum stream_state_phase phase; - - pthread_t thr_preload; - int thread_started; - - gchar *filename; -}; - -struct stream_player_module -{ - struct cbox_module module; - - struct stream_state *stream; - float fade_increment; -}; - -static void init_cue(struct stream_state *ss, struct stream_player_cue_point *pt, uint32_t size, uint64_t pos) -{ - pt->data = malloc(size * sizeof(float) * ss->info.channels); - pt->size = size; - pt->length = 0; - pt->queued = 0; - pt->position = pos; -} - -static void destroy_cue(struct stream_player_cue_point *pt) -{ - free(pt->data); - pt->data = NULL; -} - -static void load_at_cue(struct stream_state *ss, struct stream_player_cue_point *pt) -{ - if (pt->position != NO_SAMPLE_LOOP) - { - sf_seek(ss->sndfile, pt->position, 0); - pt->length = sf_readf_float(ss->sndfile, pt->data, pt->size); - } - pt->queued = 0; -} - -static int is_contained(struct stream_player_cue_point *pt, uint64_t ofs) -{ - return pt->position != NO_SAMPLE_LOOP && ofs >= pt->position && ofs < pt->position + pt->length; -} - -static int is_queued(struct stream_player_cue_point *pt, uint64_t ofs) -{ - return pt->queued && pt->position != NO_SAMPLE_LOOP && ofs >= pt->position && ofs < pt->position + pt->size; -} - -struct stream_player_cue_point *get_cue(struct stream_state *ss, uint64_t pos) -{ - int i; - - if (is_contained(&ss->cp_loop, pos)) - return &ss->cp_loop; - if (is_contained(&ss->cp_start, pos)) - return &ss->cp_start; - - for (i = 0; i < MAX_READAHEAD_BUFFERS; i++) - { - if (ss->cp_readahead_ready[i] && is_contained(&ss->cp_readahead[i], pos)) - return &ss->cp_readahead[i]; - } - return NULL; -} - -struct stream_player_cue_point *get_queued_buffer(struct stream_state *ss, uint64_t pos) -{ - int i; - - for (i = 0; i < MAX_READAHEAD_BUFFERS; i++) - { - if (!ss->cp_readahead_ready[i] && is_queued(&ss->cp_readahead[i], pos)) - return &ss->cp_readahead[i]; - } - return NULL; -} - -void request_load(struct stream_state *ss, int buf_idx, uint64_t pos) -{ - unsigned char cidx = (unsigned char)buf_idx; - struct stream_player_cue_point *pt = &ss->cp_readahead[buf_idx]; - - ss->cp_readahead_ready[buf_idx] = 0; - pt->position = pos; - pt->length = 0; - pt->queued = 1; - -#ifdef NDEBUG - cbox_fifo_write_atomic(ss->rb_for_reading, &cidx, 1); -#else - gboolean result = cbox_fifo_write_atomic(ss->rb_for_reading, &cidx, 1); - assert(result); -#endif -} - -int get_unused_buffer(struct stream_state *ss) -{ - int i = 0; - int notbad = -1; - - // return first buffer that is not currently played or in queue; XXXKF this is a very primitive strategy, a good one would at least use the current play position - for (i = 0; i < MAX_READAHEAD_BUFFERS; i++) - { - int64_t rel; - if (&ss->cp_readahead[i] == ss->pcp_current) - continue; - if (ss->cp_readahead[i].queued) - continue; - // If there's any unused buffer, return it - if (ss->cp_readahead[i].position == NO_SAMPLE_LOOP) - return i; - // If this has already been played, return it - rel = ss->readptr - ss->cp_readahead[i].position; - if (rel >= ss->cp_readahead[i].length) - return i; - // Use as second chance - notbad = i; - } - return notbad; -} - -static void *sample_preload_thread(void *user_data) -{ - struct stream_state *ss = user_data; - - do { - unsigned char buf_idx; - if (!cbox_fifo_read_atomic(ss->rb_for_reading, &buf_idx, 1)) - { - usleep(5000); - continue; - } - if (buf_idx == 255) - break; - // fprintf(stderr, "Preload: %d, %lld\n", (int)buf_idx, (long long)m->cp_readahead[buf_idx].position); - load_at_cue(ss, &ss->cp_readahead[buf_idx]); - // fprintf(stderr, "Preloaded\n", (int)buf_idx, (long long)m->cp_readahead[buf_idx].position); - cbox_fifo_write_atomic(ss->rb_just_read, &buf_idx, 1); - } while(1); - return NULL; -} - -void stream_player_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) -{ - // struct stream_player_module *m = (struct stream_player_module *)module; -} - -static void request_next(struct stream_state *ss, uint64_t pos) -{ - // Check if we've requested a next buffer, if not, request it - - // First verify if our idea of 'next' buffer is correct - // XXXKF This is technically incorrect, it won't tell whether the next "block" that's there - // isn't actually a single sample. I worked it around by ensuring end of blocks are always - // at CUE_BUFFER_SIZE boundary, and this works well, but causes buffers to be of uneven size. - if (ss->pcp_next && (is_contained(ss->pcp_next, pos) || is_queued(ss->pcp_next, pos))) - { - // We're still waiting for the requested buffer, but that's OK - return; - } - - // We don't know the next buffer, or the next buffer doesn't contain - // the sample we're looking for. - ss->pcp_next = get_queued_buffer(ss, pos); - if (!ss->pcp_next) - { - // It hasn't even been requested - request it - int buf_idx = get_unused_buffer(ss); - if(buf_idx == -1) - { - printf("Ran out of buffers\n"); - return; - } - request_load(ss, buf_idx, pos); - ss->pcp_next = &ss->cp_readahead[buf_idx]; - - // printf("@%lld: Requested load into buffer %d at %lld\n", (long long)m->readptr, buf_idx, (long long) pos); - } -} - -static void copy_samples(struct stream_state *ss, cbox_sample_t **outputs, float *data, int count, int ofs, int pos) -{ - int i; - float gain = ss->gain * ss->fade_gain; - if (ss->phase == STARTING) - { - ss->fade_gain += ss->fade_increment; - if (ss->fade_gain >= 1) - { - ss->fade_gain = 1; - ss->phase = PLAYING; - } - } - else - if (ss->phase == STOPPING) - { - ss->fade_gain -= ss->fade_increment; - if (ss->fade_gain < 0) - { - ss->fade_gain = 0; - ss->phase = STOPPED; - } - } - float new_gain = ss->gain * ss->fade_gain; - float gain_delta = (new_gain - gain) * (1.0 / CBOX_BLOCK_SIZE); - - if (ss->info.channels == 1) - { - for (i = 0; i < count; i++) - { - outputs[0][ofs + i] = outputs[1][ofs + i] = gain * data[pos + i]; - gain += gain_delta; - } - } - else - if (ss->info.channels == 2) - { - for (i = 0; i < count; i++) - { - outputs[0][ofs + i] = gain * data[pos << 1]; - outputs[1][ofs + i] = gain * data[(pos << 1) + 1]; - gain += gain_delta; - pos++; - } - } - else - { - uint32_t ch = ss->info.channels; - for (i = 0; i < count; i++) - { - outputs[0][ofs + i] = gain * data[pos * ch]; - outputs[1][ofs + i] = gain * data[pos * ch + 1]; - gain += gain_delta; - pos++; - } - } - ss->readptr += count; - if (ss->readptr >= (uint32_t)ss->info.frames) - { - ss->readptr = ss->restart; - } -} - -void stream_player_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) -{ - struct stream_player_module *m = (struct stream_player_module *)module; - struct stream_state *ss = m->stream; - int i, optr; - unsigned char buf_idx; - - if (!ss || ss->readptr == NO_SAMPLE_LOOP) - { - for (int i = 0; i < CBOX_BLOCK_SIZE; i++) - { - outputs[0][i] = outputs[1][i] = 0; - } - return; - } - - // receive buffer completion messages from the queue - while(cbox_fifo_read_atomic(ss->rb_just_read, &buf_idx, 1)) - { - ss->cp_readahead_ready[buf_idx] = 1; - } - - optr = 0; - do { - if (ss->phase == STOPPED) - break; - if (ss->readptr == NO_SAMPLE_LOOP) - { - ss->phase = STOPPED; - break; - } - - if (ss->pcp_current && !is_contained(ss->pcp_current, ss->readptr)) - ss->pcp_current = NULL; - - if (!ss->pcp_current) - { - if (ss->pcp_next && is_contained(ss->pcp_next, ss->readptr)) - { - ss->pcp_current = ss->pcp_next; - ss->pcp_next = NULL; - } - else - ss->pcp_current = get_cue(ss, ss->readptr); - } - - if (!ss->pcp_current) - { - printf("Underrun at %d\n", (int)ss->readptr); - // Underrun; request/wait for next block and output zeros - request_next(ss, ss->readptr); - break; - } - assert(!ss->pcp_current->queued); - - uint64_t data_end = ss->pcp_current->position + ss->pcp_current->length; - uint32_t data_left = data_end - ss->readptr; - - // If we're close to running out of space, prefetch the next bit - if (data_left < PREFETCH_THRESHOLD && data_end < (uint64_t)ss->info.frames) - request_next(ss, data_end); - - float *data = ss->pcp_current->data; - uint32_t pos = ss->readptr - ss->pcp_current->position; - uint32_t count = data_end - ss->readptr; - if (count > (uint32_t)(CBOX_BLOCK_SIZE - optr)) - count = (uint32_t)(CBOX_BLOCK_SIZE - optr); - - // printf("Copy samples: copying %d, optr %d, %lld = %d @ [%lld - %lld], left %d\n", count, optr, (long long)m->readptr, pos, (long long)m->pcp_current->position, (long long)data_end, (int)data_left); - copy_samples(ss, outputs, data, count, optr, pos); - optr += count; - } while(optr < CBOX_BLOCK_SIZE); - - for (i = optr; i < CBOX_BLOCK_SIZE; i++) - { - outputs[0][i] = outputs[1][i] = 0; - } -} - -static void stream_state_destroy(struct stream_state *ss) -{ - unsigned char cmd = 255; - - if (ss->rb_for_reading && ss->thread_started) - { - cbox_fifo_write_atomic(ss->rb_for_reading, &cmd, 1); - pthread_join(ss->thr_preload, NULL); - } - destroy_cue(&ss->cp_start); - destroy_cue(&ss->cp_loop); - for (int i = 0; i < MAX_READAHEAD_BUFFERS; i++) - destroy_cue(&ss->cp_readahead[i]); - if (ss->rb_for_reading) - cbox_fifo_destroy(ss->rb_for_reading); - if (ss->rb_just_read) - cbox_fifo_destroy(ss->rb_just_read); - if (ss->sndfile) - sf_close(ss->sndfile); - if (ss->filename) - g_free(ss->filename); - free(ss); -} - -void stream_player_destroyfunc(struct cbox_module *module) -{ - struct stream_player_module *m = (struct stream_player_module *)module; - if (m->stream) - stream_state_destroy(m->stream); -} - -static struct stream_state *stream_state_new(const char *context, const gchar *filename, uint64_t loop, float fade_increment, GError **error) -{ - struct stream_state *stream = malloc(sizeof(struct stream_state)); - memset(&stream->info, 0, sizeof(stream->info)); - stream->sndfile = sf_open(filename, SFM_READ, &stream->info); - - if (!stream->sndfile) - { - g_set_error(error, CBOX_STREAM_PLAYER_ERROR, CBOX_STREAM_PLAYER_ERROR_FAILED, "instrument '%s': cannot open file '%s': %s", context, filename, sf_strerror(NULL)); - free(stream); - return NULL; - } - // g_message("Frames %d channels %d", (int)stream->info.frames, (int)stream->info.channels); - - stream->rb_for_reading = cbox_fifo_new(MAX_READAHEAD_BUFFERS + 1); - stream->rb_just_read = cbox_fifo_new(MAX_READAHEAD_BUFFERS + 1); - - stream->phase = STOPPED; - stream->readptr = 0; - stream->restart = loop; - stream->pcp_current = &stream->cp_start; - stream->pcp_next = NULL; - stream->gain = 1.0; - stream->fade_gain = 0.0; - stream->fade_increment = fade_increment; - stream->thread_started = 0; - stream->filename = g_strdup(filename); - - init_cue(stream, &stream->cp_start, CUE_BUFFER_SIZE, 0); - load_at_cue(stream, &stream->cp_start); - if (stream->restart > 0 && (stream->restart % CUE_BUFFER_SIZE) > 0) - init_cue(stream, &stream->cp_loop, CUE_BUFFER_SIZE + (CUE_BUFFER_SIZE - (stream->restart % CUE_BUFFER_SIZE)), stream->restart); - else - init_cue(stream, &stream->cp_loop, CUE_BUFFER_SIZE, stream->restart); - load_at_cue(stream, &stream->cp_loop); - for (int i = 0; i < MAX_READAHEAD_BUFFERS; i++) - { - init_cue(stream, &stream->cp_readahead[i], CUE_BUFFER_SIZE, NO_SAMPLE_LOOP); - stream->cp_readahead_ready[i] = 0; - } - if (pthread_create(&stream->thr_preload, NULL, sample_preload_thread, stream)) - { - stream_state_destroy(stream); - g_set_error(error, CBOX_STREAM_PLAYER_ERROR, CBOX_STREAM_PLAYER_ERROR_FAILED, "cannot create streaming thread: %s", strerror(errno)); - return NULL; - } - stream->thread_started = 1; - - return stream; -} - -/////////////////////////////////////////////////////////////////////////////////// - -static int stream_player_seek_execute(void *p) -{ - struct stream_player_module *m = p; - - m->stream->readptr = m->stream->readptr_new; - - return 1; -} - -static struct cbox_rt_cmd_definition stream_seek_command = { - .prepare = NULL, - .execute = stream_player_seek_execute, - .cleanup = NULL -}; - -/////////////////////////////////////////////////////////////////////////////////// - -static int stream_player_play_execute(void *p) -{ - struct stream_player_module *m = p; - - if (m->stream->readptr == NO_SAMPLE_LOOP) - m->stream->readptr = 0; - if (m->stream->phase != PLAYING) - { - if (m->stream->readptr == 0) - { - m->stream->fade_gain = 1.0; - m->stream->phase = PLAYING; - } - else - m->stream->phase = STARTING; - } - return 1; -} - -static struct cbox_rt_cmd_definition stream_play_command = { - .prepare = NULL, - .execute = stream_player_play_execute, - .cleanup = NULL -}; - -/////////////////////////////////////////////////////////////////////////////////// - -static int stream_player_stop_execute(void *p) -{ - struct stream_player_module *m = p; - - if (m->stream->phase != STOPPED) - m->stream->phase = STOPPING; - return 1; -} - -static struct cbox_rt_cmd_definition stream_stop_command = { - .prepare = NULL, - .execute = stream_player_stop_execute, - .cleanup = NULL -}; - -/////////////////////////////////////////////////////////////////////////////////// - -struct load_command_data -{ - struct stream_player_module *module; - gchar *context; - gchar *filename; - int loop_start; - struct stream_state *stream, *old_stream; - GError **error; -}; - -static int stream_player_load_prepare(void *p) -{ - struct load_command_data *c = p; - - if (!c->filename) - return 0; - c->stream = stream_state_new(c->context, c->filename, c->loop_start, c->module->fade_increment, c->error); - c->old_stream = NULL; - if (!c->stream) - { - g_free(c->filename); - return -1; - } - return 0; -} - -static int stream_player_load_execute(void *p) -{ - struct load_command_data *c = p; - - c->old_stream = c->module->stream; - c->module->stream = c->stream; - return 1; -} - -static void stream_player_load_cleanup(void *p) -{ - struct load_command_data *c = p; - - if (c->filename) - g_free(c->filename); - if (c->old_stream && c->old_stream != c->stream) - stream_state_destroy(c->old_stream); -} - -static struct cbox_rt_cmd_definition stream_load_command = { - .prepare = stream_player_load_prepare, - .execute = stream_player_load_execute, - .cleanup = stream_player_load_cleanup -}; - -/////////////////////////////////////////////////////////////////////////////////// - -static gboolean require_stream(struct stream_player_module *m, GError **error) -{ - if (!m->stream) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "No stream loaded"); - return FALSE; - } - return TRUE; -} - -gboolean stream_player_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct stream_player_module *m = (struct stream_player_module *)ct->user_data; - if (!strcmp(cmd->command, "/seek") && !strcmp(cmd->arg_types, "i")) - { - if (!require_stream(m, error)) - return FALSE; - m->stream->readptr_new = CBOX_ARG_I(cmd, 0); - cbox_rt_execute_cmd_async(m->module.rt, &stream_seek_command, m); - } - else if (!strcmp(cmd->command, "/play") && !strcmp(cmd->arg_types, "")) - { - if (!require_stream(m, error)) - return FALSE; - cbox_rt_execute_cmd_async(m->module.rt, &stream_play_command, m); - } - else if (!strcmp(cmd->command, "/stop") && !strcmp(cmd->arg_types, "")) - { - if (!require_stream(m, error)) - return FALSE; - cbox_rt_execute_cmd_async(m->module.rt, &stream_stop_command, m); - } - else if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - if (m->stream) - { - return cbox_execute_on(fb, NULL, "/filename", "s", error, m->stream->filename) && - cbox_execute_on(fb, NULL, "/pos", "i", error, m->stream->readptr) && - cbox_execute_on(fb, NULL, "/length", "i", error, m->stream->info.frames) && - cbox_execute_on(fb, NULL, "/channels", "i", error, m->stream->info.channels) && - cbox_execute_on(fb, NULL, "/sample_rate", "i", error, m->stream->info.samplerate) && - cbox_execute_on(fb, NULL, "/playing", "i", error, m->stream->phase != STOPPED); - } - else - return - cbox_execute_on(fb, NULL, "/filename", "s", error, ""); - } - else if (!strcmp(cmd->command, "/load") && !strcmp(cmd->arg_types, "si")) - { - struct load_command_data *c = malloc(sizeof(struct load_command_data)); - c->context = m->module.instance_name; - c->module = m; - c->stream = NULL; - c->old_stream = NULL; - c->filename = g_strdup(CBOX_ARG_S(cmd, 0)); - c->loop_start = CBOX_ARG_I(cmd, 1); - c->error = error; - cbox_rt_execute_cmd_sync(m->module.rt, &stream_load_command, c); - gboolean success = c->stream != NULL; - free(c); - return success; - } - else if (!strcmp(cmd->command, "/unload") && !strcmp(cmd->arg_types, "")) - { - struct load_command_data *c = malloc(sizeof(struct load_command_data)); - c->context = m->module.instance_name; - c->module = m; - c->stream = NULL; - c->old_stream = NULL; - c->filename = NULL; - c->loop_start = 0; - c->error = error; - cbox_rt_execute_cmd_sync(m->module.rt, &stream_load_command, c); - gboolean success = c->stream == NULL; - free(c); - return success; - } - else - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown command '%s'", cmd->command); - return FALSE; - } - return TRUE; -} - -MODULE_CREATE_FUNCTION(stream_player) -{ - static int inited = 0; - - if (!inited) - { - inited = 1; - } - - struct stream_player_module *m = malloc(sizeof(struct stream_player_module)); - gchar *filename = cbox_config_get_string(cfg_section, "file"); - CALL_MODULE_INIT(m, 0, 2, stream_player); - m->module.process_event = stream_player_process_event; - m->module.process_block = stream_player_process_block; - m->fade_increment = 1.0 / (cbox_config_get_float(cfg_section, "fade_time", 0.01) * (m->module.srate / CBOX_BLOCK_SIZE)); - if (filename) - { - m->stream = stream_state_new(cfg_section, filename, (uint64_t)(int64_t)cbox_config_get_int(cfg_section, "loop", -1), m->fade_increment, error); - if (!m->stream) - { - CBOX_DELETE(&m->module); - return NULL; - } - } - else - m->stream = NULL; - - return &m->module; -} - -struct cbox_module_keyrange_metadata stream_player_keyranges[] = { -}; - -struct cbox_module_livecontroller_metadata stream_player_controllers[] = { -}; - -DEFINE_MODULE(stream_player, 0, 2) - diff --git a/template/calfbox/streamrec.c b/template/calfbox/streamrec.c deleted file mode 100644 index 9b2ed71..0000000 --- a/template/calfbox/streamrec.c +++ /dev/null @@ -1,244 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "engine.h" -#include "errors.h" -#include "recsrc.h" -#include "rt.h" -#include -#include -#include -#include -#include -#include -#include -#include - -// XXXKF the syncing model here is flawed in several ways: -// - it's not possible to do block-accurate syncing -// - it's not possible to flush the output buffer and stop recording -// - rb_for_writing is being written from two threads (audio and UI), -// which is not guaranteed to work -// - - -// 1/8s for 44.1kHz stereo float -#define STREAM_BUFFER_SIZE 16384 -#define STREAM_BUFFER_COUNT 8 - -#define STREAM_CMD_QUIT (-1) -#define STREAM_CMD_SYNC (-2) - -struct recording_buffer -{ - float data[STREAM_BUFFER_SIZE]; - uint32_t write_ptr; -}; - -struct stream_recorder -{ - struct cbox_recorder iface; - struct recording_buffer buffers[STREAM_BUFFER_COUNT]; - - struct cbox_rt *rt; - struct cbox_engine *engine; - gchar *filename; - SNDFILE *volatile sndfile; - SF_INFO info; - pthread_t thr_writeout; - sem_t sem_sync_completed; - - struct recording_buffer *cur_buffer; - uint32_t write_ptr; - - struct cbox_fifo *rb_for_writing, *rb_just_written; -}; - -static void *stream_recorder_thread(void *user_data) -{ - struct stream_recorder *self = user_data; - - do { - int8_t buf_idx; - if (!cbox_fifo_read_atomic(self->rb_for_writing, &buf_idx, 1)) - { - usleep(10000); - continue; - } - if (buf_idx == STREAM_CMD_QUIT) - break; - if (buf_idx == STREAM_CMD_SYNC) - { - // this assumes that the recorder is already detached from any source - if (self->cur_buffer && self->cur_buffer->write_ptr) - sf_write_float(self->sndfile, self->cur_buffer->data, self->cur_buffer->write_ptr); - - sf_command(self->sndfile, SFC_UPDATE_HEADER_NOW, NULL, 0); - sf_write_sync(self->sndfile); - sem_post(&self->sem_sync_completed); - continue; - } - else - { - sf_write_float(self->sndfile, self->buffers[buf_idx].data, self->buffers[buf_idx].write_ptr); - self->buffers[buf_idx].write_ptr = 0; - cbox_fifo_write_atomic(self->rb_just_written, &buf_idx, 1); - sf_command(self->sndfile, SFC_UPDATE_HEADER_NOW, NULL, 0); - } - } while(1); - return NULL; -} - -static gboolean stream_recorder_attach(struct cbox_recorder *handler, struct cbox_recording_source *src, GError **error) -{ - struct stream_recorder *self = handler->user_data; - - if (self->sndfile) - { - if (error) - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Recorder already attached to a different source"); - return FALSE; - } - - memset(&self->info, 0, sizeof(self->info)); - self->info.frames = 0; - self->info.samplerate = self->engine->io_env.srate; - self->info.channels = src->channels; - self->info.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT; // XXXKF make format configurable on instantiation - self->info.sections = 0; - self->info.seekable = 0; - - self->sndfile = sf_open(self->filename, SFM_WRITE, &self->info); - if (!self->sndfile) - { - if (error) - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot open sound file '%s': %s", self->filename, sf_strerror(NULL)); - return FALSE; - } - - pthread_create(&self->thr_writeout, NULL, stream_recorder_thread, self); - return TRUE; -} - -void stream_recorder_record_block(struct cbox_recorder *handler, const float **buffers, uint32_t offset, uint32_t numsamples) -{ - struct stream_recorder *self = handler->user_data; - - if (!self->sndfile) - return; - - if (self->cur_buffer && (self->cur_buffer->write_ptr + numsamples * self->info.channels) * sizeof(float) >= STREAM_BUFFER_SIZE) - { - int8_t idx = self->cur_buffer - self->buffers; - cbox_fifo_write_atomic(self->rb_for_writing, &idx, 1); - self->cur_buffer = NULL; - } - if (!self->cur_buffer) - { - int8_t buf_idx = -1; - if (!cbox_fifo_read_atomic(self->rb_just_written, &buf_idx, 1)) // underrun - return; - self->cur_buffer = &self->buffers[buf_idx]; - } - - unsigned int nc = self->info.channels; - - float *wbuf = self->cur_buffer->data + self->cur_buffer->write_ptr; - for (unsigned int c = 0; c < nc; c++) - for (uint32_t i = 0; i < numsamples; i++) - wbuf[c + i * nc] = buffers[c][i]; - self->cur_buffer->write_ptr += nc * numsamples; -} - -gboolean stream_recorder_detach(struct cbox_recorder *handler, GError **error) -{ - struct stream_recorder *self = handler->user_data; - - if (!self->sndfile) - { - if (error) - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "No sound file associated with stream recorder"); - return FALSE; - } - - int8_t cmd = STREAM_CMD_SYNC; - cbox_fifo_write_atomic(self->rb_for_writing, (char *)&cmd, 1); - sem_wait(&self->sem_sync_completed); - return TRUE; -} - -void stream_recorder_destroy(struct cbox_recorder *handler) -{ - struct stream_recorder *self = handler->user_data; - - if (self->sndfile) - { - int8_t cmd = STREAM_CMD_QUIT; - cbox_fifo_write_atomic(self->rb_for_writing, (char *)&cmd, 1); - pthread_join(self->thr_writeout, NULL); - } - - cbox_fifo_destroy(self->rb_for_writing); - cbox_fifo_destroy(self->rb_just_written); - free(self); -} - - -static gboolean stream_recorder_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct stream_recorder *rec = ct->user_data; - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - if (!cbox_execute_on(fb, NULL, "/filename", "s", error, rec->filename)) - return FALSE; - return CBOX_OBJECT_DEFAULT_STATUS(&rec->iface, fb, error); - } - return cbox_object_default_process_cmd(ct, fb, cmd, error); -} - -struct cbox_recorder *cbox_recorder_new_stream(struct cbox_engine *engine, struct cbox_rt *rt, const char *filename) -{ - struct stream_recorder *self = malloc(sizeof(struct stream_recorder)); - self->rt = rt; - self->engine = engine; - CBOX_OBJECT_HEADER_INIT(&self->iface, cbox_recorder, CBOX_GET_DOCUMENT(engine)); - cbox_command_target_init(&self->iface.cmd_target, stream_recorder_process_cmd, self); - - self->iface.user_data = self; - self->iface.attach = stream_recorder_attach; - self->iface.record_block = stream_recorder_record_block; - self->iface.detach = stream_recorder_detach; - self->iface.destroy = stream_recorder_destroy; - - self->sndfile = NULL; - self->filename = g_strdup(filename); - self->cur_buffer = NULL; - - self->rb_for_writing = cbox_fifo_new(STREAM_BUFFER_COUNT + 1); - self->rb_just_written = cbox_fifo_new(STREAM_BUFFER_COUNT + 1); - sem_init(&self->sem_sync_completed, 0, 0); - - CBOX_OBJECT_REGISTER(&self->iface); - - for (uint8_t i = 0; i < STREAM_BUFFER_COUNT; i++) - cbox_fifo_write_atomic(self->rb_just_written, (char *)&i, 1); - - return &self->iface; -} diff --git a/template/calfbox/synthbass.sfz b/template/calfbox/synthbass.sfz deleted file mode 100644 index 1290475..0000000 --- a/template/calfbox/synthbass.sfz +++ /dev/null @@ -1,12 +0,0 @@ - -sample=*sine loop_mode=loop_continuous tune=0 transpose=0 volume=-12 amp_velcurve_1=1 amp_velcurve_127=1 ampeg_release=0.1 - - -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 -tonectl_freq=3000 tonectl_cc84=24 - - -tune=-5 fil_veltrack=1200 - - -tune=+5 transpose=+12 diff --git a/template/calfbox/tarfile.c b/template/calfbox/tarfile.c deleted file mode 100644 index 08c3400..0000000 --- a/template/calfbox/tarfile.c +++ /dev/null @@ -1,341 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config-api.h" -#include "errors.h" -#include "tarfile.h" -#include -#include -#include -#include -#include -#include -#include -#include - -struct tar_record -{ - char name[100]; - char mode[8]; - char uid[8]; - char gid[8]; - char size[12]; - char mtime[12]; - char checksum[8]; - char typeflag; - char linkname[100]; - char ustar[6]; - char ustarver[2]; - char uname[32]; - char gname[32]; - char devmajor[8]; - char devminor[8]; - char prefix[155]; - char padding[12]; -}; - -static void remove_item_if(gpointer p, struct cbox_tarfile *tf); - -struct cbox_tarfile *cbox_tarfile_open(const char *pathname, GError **error) -{ - gboolean debug = cbox_config_get_int("debug", "tarfile", 0); - gchar *canonical = realpath(pathname, NULL); - if (!canonical) - { - if (error) - g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "cannot determine canonical name of '%s'", pathname); - return NULL; - } - int fd = open(canonical, O_RDONLY | O_LARGEFILE); - if (fd < 0) - { - free(canonical); - if (error) - g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "cannot open '%s'", pathname); - return NULL; - } - GHashTable *byname = NULL, *byname_nc = NULL; - - byname = g_hash_table_new(g_str_hash, g_str_equal); - byname_nc = g_hash_table_new(g_str_hash, g_str_equal); - if (!byname || !byname_nc) - goto error; - - struct cbox_tarfile *tf = calloc(1, sizeof(struct cbox_tarfile)); - if (!tf) - goto error; - tf->fd = fd; - tf->items_byname = byname; - tf->items_byname_nc = byname_nc; - tf->refs = 1; - tf->file_pathname = canonical; - while(1) - { - struct tar_record rec; - int nbytes = read(fd, &rec, sizeof(rec)); - if (nbytes != sizeof(rec)) - break; - - int len = sizeof(rec.name); - while(len > 0 && (rec.name[len - 1] == ' ' || rec.name[len - 1] == '\0')) - len--; - - char sizetext[13]; - memcpy(sizetext, rec.size, 12); - sizetext[12] = '\0'; - unsigned long long size = strtoll(sizetext, NULL, 8); - - // skip block if name is empty - if (!len) - goto skipitem; - struct cbox_taritem *ti = calloc(1, sizeof(struct cbox_taritem)); - if (ti) - { - int offset = 0; - if (len >= 2 && rec.name[0] == '.' && rec.name[1] == '/') - offset = 2; - ti->filename = g_strndup(rec.name + offset, len - offset); - ti->filename_nc = g_utf8_casefold(rec.name + offset, len - offset); - if (!ti->filename || !ti->filename_nc) - goto itemerror; - ti->offset = lseek64(fd, 0, SEEK_CUR); - ti->size = size; - ti->refs = 2; - - // Overwrite old items by the same name and/or same case-folded name - remove_item_if(g_hash_table_lookup(tf->items_byname, ti->filename), tf); - remove_item_if(g_hash_table_lookup(tf->items_byname_nc, ti->filename_nc), tf); - - g_hash_table_insert(tf->items_byname, ti->filename, ti); - g_hash_table_insert(tf->items_byname_nc, ti->filename_nc, ti); - if (debug) - printf("name = %s len = %d offset = %d readsize = %d\n", ti->filename, len, (int)ti->offset, (int)size); - - goto skipitem; - } - itemerror: - rec.name[99] = '\0'; - g_warning("Could not allocate memory for tar item %s", rec.name); - if (ti) - { - if (ti->filename_nc) - g_free(ti->filename_nc); - if (ti->filename) - g_free(ti->filename); - free(ti); - } - skipitem: - lseek64(fd, (size + 511) &~ 511, SEEK_CUR); - } - return tf; - -error: - if (byname) - g_hash_table_destroy(byname); - if (byname_nc) - g_hash_table_destroy(byname_nc); - free(canonical); - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot allocate memory for tarfile data"); - return NULL; -} - -void remove_item_if(gpointer p, struct cbox_tarfile *tf) -{ - if (!p) - return; - - struct cbox_taritem *ti = p; - // If all references (by name and by case-folded name) gone, remove the item - if (!--ti->refs) - { - g_hash_table_remove(tf->items_byname, ti->filename); - g_hash_table_remove(tf->items_byname_nc, ti->filename_nc); - g_free(ti->filename); - g_free(ti->filename_nc); - free(ti); - } -} - -struct cbox_taritem *cbox_tarfile_get_item_by_name(struct cbox_tarfile *tarfile, const char *item_filename, gboolean ignore_case) -{ - if (item_filename[0] == '.' && item_filename[1] == '/') - item_filename += 2; - if (ignore_case) - { - gchar *folded = g_utf8_casefold(item_filename, -1); - struct cbox_taritem *item = g_hash_table_lookup(tarfile->items_byname_nc, folded); - g_free(folded); - return item; - } - else - return g_hash_table_lookup(tarfile->items_byname, item_filename); -} - -int cbox_tarfile_openitem(struct cbox_tarfile *tarfile, struct cbox_taritem *item) -{ - int fd = open(tarfile->file_pathname, O_RDONLY | O_LARGEFILE); - if (fd >= 0) - lseek64(fd, item->offset, SEEK_SET); - return fd; -} - -void cbox_tarfile_closeitem(struct cbox_tarfile *tarfile, struct cbox_taritem *item, int fd) -{ - if (fd >= 0) - close(fd); -} - -static void delete_foreach_func(gpointer key, gpointer value, gpointer user_data) -{ - struct cbox_taritem *ti = value; - if (!--ti->refs) - { - g_free(ti->filename); - g_free(ti->filename_nc); - free(ti); - } -} - -void cbox_tarfile_destroy(struct cbox_tarfile *tf) -{ - g_hash_table_foreach(tf->items_byname, delete_foreach_func, NULL); - g_hash_table_foreach(tf->items_byname_nc, delete_foreach_func, NULL); - close(tf->fd); - g_hash_table_destroy(tf->items_byname); - g_hash_table_destroy(tf->items_byname_nc); - free(tf->file_pathname); - free(tf); -} - -//////////////////////////////////////////////////////////////////////////////// - -sf_count_t tarfile_get_filelen(void *user_data) -{ - struct cbox_tarfile_sndstream *ss = user_data; - - return ss->item->size; -} - -sf_count_t tarfile_seek(sf_count_t offset, int whence, void *user_data) -{ - struct cbox_tarfile_sndstream *ss = user_data; - switch(whence) - { - case SEEK_SET: - ss->filepos = offset; - break; - case SEEK_CUR: - ss->filepos += offset; - break; - case SEEK_END: - ss->filepos = ss->item->size; - break; - } - if (((int64_t)ss->filepos) < 0) - ss->filepos = 0; - if (((int64_t)ss->filepos) >= (int64_t)ss->item->size) - ss->filepos = ss->item->size; - return ss->filepos; -} - -sf_count_t tarfile_read(void *ptr, sf_count_t count, void *user_data) -{ - struct cbox_tarfile_sndstream *ss = user_data; - ssize_t len = pread64(ss->file->fd, ptr, count, ss->item->offset + ss->filepos); - if (len > 0) - ss->filepos += len; - return len; -} - -sf_count_t tarfile_tell(void *user_data) -{ - struct cbox_tarfile_sndstream *ss = user_data; - return ss->filepos; -} - -struct SF_VIRTUAL_IO cbox_taritem_virtual_io = { - .get_filelen = tarfile_get_filelen, - .seek = tarfile_seek, - .read = tarfile_read, - .write = NULL, - .tell = tarfile_tell, -}; - -SNDFILE *cbox_tarfile_opensndfile(struct cbox_tarfile *tarfile, struct cbox_taritem *item, struct cbox_tarfile_sndstream *stream, SF_INFO *sfinfo) -{ - stream->file = tarfile; - stream->item = item; - stream->filepos = 0; - return sf_open_virtual(&cbox_taritem_virtual_io, SFM_READ, sfinfo, stream); -} - -//////////////////////////////////////////////////////////////////////////////// - -struct cbox_tarpool *cbox_tarpool_new(void) -{ - struct cbox_tarpool *pool = calloc(1, sizeof(struct cbox_tarpool)); - pool->files = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); - return pool; -} - -struct cbox_tarfile *cbox_tarpool_get_tarfile(struct cbox_tarpool *pool, const char *name, GError **error) -{ - gchar *c = realpath(name, NULL); - if (!c) - { - g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "cannot find a real path for '%s': %s", name, strerror(errno)); - return NULL; - } - struct cbox_tarfile *tf = g_hash_table_lookup(pool->files, c); - if (tf) - tf->refs++; - else - { - tf = cbox_tarfile_open(c, error); - if (!tf) - { - free(c); - return NULL; - } - g_hash_table_insert(pool->files, c, tf); - } - return tf; -} - -void cbox_tarpool_release_tarfile(struct cbox_tarpool *pool, struct cbox_tarfile *file) -{ - if (!--file->refs) - { - // XXXKF the insertion key is realpath(name) but the removal key is realpath(realpath(name)) - // usually it shouldn't cause problems, but it should be improved. - if (!g_hash_table_lookup(pool->files, file->file_pathname)) - g_warning("Removing tarfile %s not in the pool hash", file->file_pathname); - g_hash_table_remove(pool->files, file->file_pathname); - cbox_tarfile_destroy(file); - } -} - -void cbox_tarpool_destroy(struct cbox_tarpool *pool) -{ - int nelems = g_hash_table_size(pool->files); - if (nelems) - g_warning("%d unfreed elements in tar pool %p.", nelems, pool); - g_hash_table_destroy(pool->files); - free(pool); -} - diff --git a/template/calfbox/tarfile.h b/template/calfbox/tarfile.h deleted file mode 100644 index a08ae68..0000000 --- a/template/calfbox/tarfile.h +++ /dev/null @@ -1,74 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_TARFILE_H -#define CBOX_TARFILE_H - -#include -#include -#include - -struct cbox_taritem -{ - gchar *filename; - gchar *filename_nc; - uint64_t offset; - uint64_t size; - int refs; -}; - -struct cbox_tarfile -{ - int fd; - int refs; - GHashTable *items_byname; - GHashTable *items_byname_nc; - char *file_pathname; //full path to the .tar file with filename.ext -}; - -struct cbox_tarpool -{ - GHashTable *files; -}; - -struct cbox_tarfile_sndstream -{ - struct cbox_tarfile *file; - struct cbox_taritem *item; - uint64_t filepos; -}; - -extern struct SF_VIRTUAL_IO cbox_taritem_virtual_io; - -extern struct cbox_tarfile *cbox_tarfile_open(const char *pathname, GError **error); - -extern struct cbox_taritem *cbox_tarfile_get_item_by_name(struct cbox_tarfile *tarfile, const char *item_filename, gboolean ignore_case); -extern int cbox_tarfile_openitem(struct cbox_tarfile *tarfile, struct cbox_taritem *item); -extern void cbox_tarfile_closeitem(struct cbox_tarfile *tarfile, struct cbox_taritem *item, int fd); - -extern SNDFILE *cbox_tarfile_opensndfile(struct cbox_tarfile *tarfile, struct cbox_taritem *item, struct cbox_tarfile_sndstream *stream, SF_INFO *sfinfo); -// No need to close - it reuses the cbox_tarfile file descriptor - -extern void cbox_tarfile_destroy(struct cbox_tarfile *tf); - -extern struct cbox_tarpool *cbox_tarpool_new(void); -extern struct cbox_tarfile *cbox_tarpool_get_tarfile(struct cbox_tarpool *pool, const char *name, GError **error); -extern void cbox_tarpool_release_tarfile(struct cbox_tarpool *pool, struct cbox_tarfile *file); -extern void cbox_tarpool_destroy(struct cbox_tarpool *pool); - -#endif diff --git a/template/calfbox/test.py b/template/calfbox/test.py deleted file mode 100644 index 13a7ae3..0000000 --- a/template/calfbox/test.py +++ /dev/null @@ -1,364 +0,0 @@ -import os -import sys -import struct -import time -import unittest - -# This is for locale testing -from gi.repository import GObject, Gdk, Gtk - -from calfbox import cbox -cbox.init_engine("") -cbox.start_noaudio(44100) - -cbox.Config.add_section("drumpattern:pat1", """ -title=Straight - Verse -beats=4 -track1=bd -track2=sd -track3=hh -track4=ho -bd_note=c1 -sd_note=d1 -hh_note=f#1 -ho_note=a#1 -bd_trigger=9... .... 9.6. .... -sd_trigger=.... 9..5 .2.. 9... -hh_trigger=9353 7353 7353 73.3 -ho_trigger=.... .... .... ..3. -""") -cbox.Config.add_section("fxpreset:piano_reverb", """ -engine=reverb -""") -cbox.Config.add_section("instrument:vintage", """ -engine=sampler -""") - -global Document -Document = cbox.Document - -Document.dump() - -class TestCbox(unittest.TestCase): - def verify_uuid(self, uuid, class_name, path = None): - self.assertEqual(cbox.GetThings(Document.uuid_cmd(uuid, "/get_class_name"), ['class_name'], []).class_name, class_name) - if path is not None: - self.assertEqual(cbox.GetThings(path + "/status", ['uuid'], []).uuid, uuid) - self.assertEqual(cbox.GetThings(Document.uuid_cmd(uuid, "/status"), ['uuid'], []).uuid, uuid) - - def test_scene(self): - scene = Document.get_scene() - self.assertEqual(Document.get_engine().status().scenes[0], scene) - - scene.clear() - scene.add_new_instrument_layer("test_instr", "sampler") - - scene_status = scene.status() - layer = scene_status.layers[0] - self.verify_uuid(scene.uuid, "cbox_scene", "/scene") - self.verify_uuid(layer.uuid, "cbox_layer", "/scene/layer/1") - - layers = scene.status().layers - self.assertEqual(len(layers), 1) - self.assertEqual(layers[0].uuid, layer.uuid) - layers[0].set_consume(0) - self.assertEqual(layers[0].status().consume, 0) - layers[0].set_consume(1) - self.assertEqual(layers[0].status().consume, 1) - layers[0].set_enable(0) - self.assertEqual(layers[0].status().enable, 0) - layers[0].set_enable(1) - self.assertEqual(layers[0].status().enable, 1) - - layer_status = layers[0].status() - instr_uuid = layer_status.instrument.uuid - iname = layer_status.instrument_name - self.assertEqual(iname, 'test_instr') - self.verify_uuid(instr_uuid, "cbox_instrument", "/scene/instr/%s" % iname) - - aux = scene.load_aux("piano_reverb") - module = aux.slot.engine - self.verify_uuid(aux.uuid, "cbox_aux_bus", "/scene/aux/piano_reverb") - scene.delete_aux("piano_reverb") - - def test_aux_scene(self): - engine = Document.new_engine(44100, 1024) - scene = engine.new_scene() - self.assertEqual(engine.status().scenes[0], scene) - scene.add_instrument_layer("vintage") - scene_status = scene.status() - layer = scene_status.layers[0] - self.verify_uuid(scene.uuid, "cbox_scene") - self.verify_uuid(layer.uuid, "cbox_layer", scene.make_path("/layer/1")) - - layers = scene.status().layers - self.assertEqual(len(layers), 1) - self.assertEqual(layers[0].uuid, layer.uuid) - layers[0].set_consume(0) - self.assertEqual(layers[0].status().consume, 0) - layers[0].set_consume(1) - self.assertEqual(layers[0].status().consume, 1) - layers[0].set_enable(0) - self.assertEqual(layers[0].status().enable, 0) - layers[0].set_enable(1) - self.assertEqual(layers[0].status().enable, 1) - - layer_status = layers[0].status() - instr_uuid = layer_status.instrument.uuid - iname = layer_status.instrument_name - self.verify_uuid(instr_uuid, "cbox_instrument", scene.make_path("/instr/%s" % iname)) - - aux = scene.load_aux("piano_reverb") - module = aux.slot.engine - self.verify_uuid(aux.uuid, "cbox_aux_bus", scene.make_path("/aux/piano_reverb")) - scene.delete_aux("piano_reverb") - scene2 = engine.new_scene() - with self.assertRaises(Exception) as context: - layer_status.instrument.move_to(scene2, 1) - self.assertEqual(str(context.exception), "Invalid position 2 (valid are 1..1 or 0 for append)") - layer_status.instrument.move_to(scene2, 0) - - layers = scene.status().layers - self.assertEqual(len(layers), 0) - layers = scene2.status().layers - self.assertEqual(len(layers), 1) - scene.add_instrument_layer("vintage") - with self.assertRaises(Exception) as context: - layer_status.instrument.move_to(scene, 0) - self.assertEqual(str(context.exception), "Instrument 'vintage' already exists in target scene") - - def test_sampler_api(self): - engine = Document.new_engine(44100, 1024) - scene = engine.new_scene() - scene.add_new_instrument_layer("temporary", "sampler") - scene_status = scene.status() - layer = scene_status.layers[0] - self.verify_uuid(scene.uuid, "cbox_scene") - self.verify_uuid(layer.uuid, "cbox_layer", scene.make_path("/layer/1")) - instrument = layer.get_instrument() - self.assertEqual(instrument.status().engine, "sampler") - - program0 = instrument.engine.load_patch_from_file(0, 'synthbass.sfz', 'test_sampler_sfz_loader') - self.assertNotEqual(program0, None) - self.assertEqual(program0.status().in_use, 16) - program1 = instrument.engine.load_patch_from_string(0, '.', ' resonance=3 unknown=123 key=36 sample=impulse.wav cutoff=1000 key=37 cutoff=2000 sample=impulse.wav ', 'test_sfz_parser_trailing_spaces') - self.assertNotEqual(program1, None) - self.assertEqual(program1.status().in_use, 16) - self.assertEqual(program1.status().name, 'test_sfz_parser_trailing_spaces') - self.assertRegex(program1.get_regions()[0].as_string(), 'sample=.*impulse\.wav') - program2 = instrument.engine.load_patch_from_string(0, '.', ' resonance=3 unknown=123 key=36 sample=impulse.wav cutoff=1000.5 key=37 sample=impulse.wav cutoff=2000', 'test_sampler_api') - self.assertNotEqual(program2, None) - self.assertEqual(program2.status().in_use, 16) - try: - program1.status() - self.assertTrue(False) - except Exception as e: - self.assertTrue('UUID not found' in str(e)) - patches = instrument.engine.get_patches() - patches_dict = {} - self.assertEqual(len(patches), 1) - for (patchid, patchdata) in patches.items(): - patchname, program, patchchannelcount = patchdata - self.verify_uuid(program.uuid, 'sampler_program') - self.assertEqual(program.status().program_no, patchid) - self.assertEqual(program.status().name, 'test_sampler_api') - self.assertEqual(program.status().sample_dir, '.') - self.assertEqual(program.status().program_no, 0) - self.assertEqual(program.status().in_use, 16) - instrument.engine.set_patch(1, 0) - self.assertEqual(program.status().in_use, 16) - instrument.engine.set_patch(2, 0) - self.assertEqual(program.status().in_use, 16) - regions = program.get_regions() - patches_dict[patchid] = (patchname, len(regions)) - for region in regions: - region_str = Document.map_uuid(region.uuid).as_string() - print (patchname, region.uuid, region_str) - if patchname == 'test_sampler_api': - self.assertTrue('impulse.wav' in region_str) - self.assertTrue('key=c' in region_str) - if 'key=c2' in region_str: - self.assertTrue('unknown=123' in region_str) - self.assertTrue('cutoff=1000.5' in region_str) - else: - self.assertFalse('unknown=123' in region_str) - self.assertTrue('cutoff=2000' in region_str) - program.add_control_init(11, 64) - self.assertTrue((11,64) in program.get_control_inits()) - program.delete_control_init(11, 0) - program.add_control_init(11, 0) - program.add_control_init(11, 64) - self.assertTrue((11,0) in program.get_control_inits()) - self.assertTrue((11,64) in program.get_control_inits()) - program.delete_control_init(11, 0) - self.assertTrue((11,0) not in program.get_control_inits()) - self.assertTrue((11,64) in program.get_control_inits()) - program.delete_control_init(11, 0) - self.assertTrue((11,0) not in program.get_control_inits()) - self.assertTrue((11,64) not in program.get_control_inits()) - program.add_control_init(11, 0) - program.add_control_init(11, 64) - program.delete_control_init(11, -1) - self.assertTrue((11,0) not in program.get_control_inits()) - self.assertTrue((11,64) not in program.get_control_inits()) - self.assertEqual(patches_dict, {0 : ('test_sampler_api', 2)}) - group = region.status().parent_group - self.assertTrue("resonance=3" in group.as_string()) - region.set_param("cutoff", 9000) - self.assertTrue('cutoff=9000' in region.as_string()) - region.set_param("sample", 'test.wav') - self.assertTrue('test.wav' in region.as_string()) - region.set_param("key", '12') - self.assertTrue('key=c0' in region.as_string()) - print (region.status()) - print (group.as_string()) - print (region.as_string()) - print ("Engine:", instrument.engine) - print ("Patches:", instrument.engine.get_patches()) - program3 = program2.clone_to(instrument, 1) - print ("Program 1") - print (program2.status(), program2) - print (program2.get_groups()) - print ("Program 2") - print (program3.status(), program3) - print (program3.get_groups()) - print (instrument.engine.get_patches()) - program3.delete() - - def test_rt(self): - rt = Document.get_rt() - self.assertEqual(cbox.GetThings(Document.uuid_cmd(rt.uuid, "/status"), ['uuid'], []).uuid, rt.uuid) - - def test_recorder_api(self): - engine = Document.new_engine(44100, 512) - scene = engine.new_scene() - scene.add_new_instrument_layer("temporary", "sampler") - layer = scene.status().layers[0] - instr = layer.status().instrument - self.assertEqual(instr.get_things("/output/1/rec_dry/status", ['*handler']).handler, []) - - meter_uuid = cbox.GetThings("/new_meter", ['uuid'], []).uuid - instr.cmd('/output/1/rec_dry/attach', None, meter_uuid) - self.assertEqual(instr.get_things("/output/1/rec_dry/status", ['*handler']).handler, [meter_uuid]) - instr.cmd('/output/1/rec_dry/detach', None, meter_uuid) - self.assertEqual(instr.get_things("/output/1/rec_dry/status", ['*handler']).handler, []) - if os.path.exists("test.wav"): - os.unlink('test.wav') - - rec = engine.new_recorder('test.wav') - self.assertEqual(rec.status().filename, 'test.wav') - rec_uuid = rec.uuid - instr.cmd('/output/1/rec_dry/attach', None, rec_uuid) - self.assertEqual(instr.get_things("/output/1/rec_dry/status", ['*handler']).handler, [rec_uuid]) - instr.cmd('/output/1/rec_dry/detach', None, rec_uuid) - self.assertEqual(instr.get_things("/output/1/rec_dry/status", ['*handler']).handler, []) - self.assertTrue(os.path.exists('test.wav')) - self.assertTrue(os.path.getsize('test.wav') < 512) - os.unlink('test.wav') - - rec = engine.new_recorder('test.wav') - self.assertEqual(rec.status().filename, 'test.wav') - rec_uuid = rec.uuid - instr.cmd('/output/1/rec_dry/attach', None, rec_uuid) - self.assertEqual(instr.get_things("/output/1/rec_dry/status", ['*handler']).handler, [rec_uuid]) - data = struct.unpack_from("512f", engine.render_stereo(512)) - instr.cmd('/output/1/rec_dry/detach', None, rec_uuid) - self.assertEqual(instr.get_things("/output/1/rec_dry/status", ['*handler']).handler, []) - rec.delete() - self.assertTrue(os.path.exists('test.wav')) - self.assertTrue(os.path.getsize('test.wav') > 512 * 4 * 2) - - def test_song(self): - song = Document.get_song() - song.clear() - tp = song.status() - self.assertEqual(tp.tracks, []) - self.assertEqual(tp.patterns, []) - self.assertEqual(tp.mtis, []) - - track = song.add_track() - pattern = song.load_drum_pattern('pat1') - track.add_clip(0, 0, 192, pattern) - - song = Document.get_song() - tp = song.status() - self.assertEqual(tp.tracks[0].name, 'Unnamed') - self.assertEqual(tp.patterns[0].name, 'pat1') - track = tp.tracks[0].track - pattern = tp.patterns[0].pattern - - track.set_name("Now named") - self.assertEqual(track.status().name, 'Now named') - pattern.set_name("pat1alt") - self.assertEqual(pattern.status().name, 'pat1alt') - - tp = song.status() - self.assertEqual(tp.tracks[0].name, 'Now named') - self.assertEqual(tp.patterns[0].name, 'pat1alt') - - clips = track.status().clips - self.assertEqual(clips[0].pos, 0) - self.assertEqual(clips[0].offset, 0) - self.assertEqual(clips[0].length, 192) - self.assertEqual(clips[0].pattern, pattern) - clip1 = clips[0].clip - - clip2 = track.add_clip(192, 96, 48, pattern) - - clip2_data = clip2.status() - self.assertEqual(clip2_data.pos, 192) - self.assertEqual(clip2_data.offset, 96) - self.assertEqual(clip2_data.length, 48) - self.assertEqual(clip2_data.pattern, pattern) - - clips = track.status().clips - self.assertEqual(clips, [cbox.ClipItem(0, 0, 192, pattern.uuid, clip1.uuid), cbox.ClipItem(192, 96, 48, pattern.uuid, clip2.uuid)]) - - clip1.delete() - - clips = track.status().clips - self.assertEqual(clips, [cbox.ClipItem(192, 96, 48, pattern.uuid, clip2.uuid)]) - - def test_mti(self): - MtiItem = cbox.MtiItem - song = Document.get_song() - song.clear() - tp = song.status() - self.assertEqual(tp.tracks, []) - self.assertEqual(tp.patterns, []) - self.assertEqual(tp.mtis, []) - song.set_mti(0, 120.0) - self.assertEqual(song.status().mtis, [MtiItem(0, 120.0, 0, 0)]) - song.set_mti(60, 150.0) - self.assertEqual(song.status().mtis, [MtiItem(0, 120.0, 0, 0), MtiItem(60, 150.0, 0, 0)]) - song.set_mti(90, 180.0) - self.assertEqual(song.status().mtis, [MtiItem(0, 120.0, 0, 0), MtiItem(60, 150.0, 0, 0), MtiItem(90, 180.0, 0, 0)]) - song.set_mti(60, 180.0) - self.assertEqual(song.status().mtis, [MtiItem(0, 120.0, 0, 0), MtiItem(60, 180.0, 0, 0), MtiItem(90, 180.0, 0, 0)]) - song.set_mti(65, 210.0) - self.assertEqual(song.status().mtis, [MtiItem(0, 120.0, 0, 0), MtiItem(60, 180.0, 0, 0), MtiItem(65, 210.0, 0, 0), MtiItem(90, 180.0, 0, 0)]) - - song.set_mti(60, 0.0, 0, 0) - self.assertEqual(song.status().mtis, [MtiItem(0, 120.0, 0, 0), MtiItem(65, 210.0, 0, 0), MtiItem(90, 180.0, 0, 0)]) - song.set_mti(65, 0.0, 0, 0) - self.assertEqual(song.status().mtis, [MtiItem(0, 120.0, 0, 0), MtiItem(90, 180.0, 0, 0)]) - song.set_mti(68, 0.0, 0, 0) - self.assertEqual(song.status().mtis, [MtiItem(0, 120.0, 0, 0), MtiItem(90, 180.0, 0, 0)]) - song.set_mti(0, 0.0, 0, 0) - self.assertEqual(song.status().mtis, [MtiItem(0, 0, 0, 0), MtiItem(90, 180.0, 0, 0)]) - song.set_mti(90, 0.0, 0, 0) - self.assertEqual(song.status().mtis, [MtiItem(0, 0, 0, 0)]) - - def test_error(self): - thrown = False - try: - Document.get_scene().cmd('transpose', None, cbox) - except ValueError as ve: - self.assertTrue("class 'module'" in str(ve)) - thrown = True - self.assertTrue(thrown) - -unittest.main() - -cbox.stop_audio() -cbox.shutdown_engine() diff --git a/template/calfbox/tests.c b/template/calfbox/tests.c deleted file mode 100644 index a2cba95..0000000 --- a/template/calfbox/tests.c +++ /dev/null @@ -1,741 +0,0 @@ -#include "module.h" -#include "engine.h" -#include "sampler.h" -#include "sfzloader.h" -#include "tests.h" - -static struct sampler_module *create_sampler_instance(struct test_env *env, const char *cfg_section, const char *instance_name) -{ - extern struct cbox_module_manifest sampler_module; - - GError *error = NULL; - struct cbox_module *module = cbox_module_manifest_create_module(&sampler_module, cfg_section, env->doc, NULL, env->engine, instance_name, &error); - if (!module) - { - if (error) - fprintf(stderr, "Error: %s\n", error->message); - test_assert(module); - } - test_assert_equal_str(module->engine_name, "sampler"); - test_assert_equal_str(module->instance_name, instance_name); - return (struct sampler_module *)module; -} - -static int count_free_voices(struct test_env *env, struct sampler_module *m) -{ - int count = 0; - for (struct sampler_voice *v = m->voices_free; v; count++, v = v->next) - test_assert(count < MAX_SAMPLER_VOICES); - return count; -} - -static int count_channel_voices_if(struct test_env *env, struct sampler_module *m, int channel, gboolean (*cond_func)(struct sampler_voice *v, void *user_data), void *user_data) -{ - struct sampler_channel *c = &m->channels[channel]; - int count = 0; - for (struct sampler_voice *v = c->voices_running; v; v = v->next) - { - test_assert(count < MAX_SAMPLER_VOICES); - count += cond_func ? (cond_func(v, user_data) ? 1 : 0) : 1; - } - return count; -} - -static void verify_sampler_voices_if(struct test_env *env, struct sampler_module *m, int voices[16], gboolean (*cond_func)(struct sampler_voice *v, void *user_data), void *user_data) -{ - int total = 0; - for (int i = 0; i < 16; ++i) - { - int count = count_channel_voices_if(env, m, i, cond_func, user_data); - test_assert_equal(int, count, voices[i]); - total += count; - } - if (!cond_func) - test_assert_equal(int, count_free_voices(env, m), MAX_SAMPLER_VOICES - total); -} - -static void verify_sampler_voices(struct test_env *env, struct sampler_module *m, int voices[16]) -{ - verify_sampler_voices_if(env, m, voices, NULL, NULL); -} - -static gboolean is_voice_released(struct sampler_voice *v, void *ignore) -{ - return v->released; -} - -static struct sampler_program *load_sfz_into_sampler(struct test_env *env, struct sampler_module *m, const char *sfz_data) -{ - GError *error = NULL; - - struct sampler_program *prg = sampler_program_new(m, 0, "note_test", NULL, NULL, &error); - test_assert(prg); - test_assert_no_error(error); - - test_assert(sampler_module_load_program_sfz(m, prg, sfz_data, 1, &error)); - test_assert_no_error(error); - - sampler_register_program(m, prg); - test_assert(sampler_select_program(m, 0, prg->name, &error)); - test_assert_no_error(error); - - return prg; -} - -//////////////////////////////////////////////////////////////////////////////// - -void test_sampler_setup(struct test_env *env) -{ - struct sampler_module *m = create_sampler_instance(env, "test_setup", "smp1"); - - int expected_voices[16] = {}; - verify_sampler_voices(env, m, expected_voices); - - CBOX_DELETE(&m->module); -} - -//////////////////////////////////////////////////////////////////////////////// - -void test_sampler_midicurve(struct test_env *env) -{ - struct sampler_midi_curve curve; - float values[128]; - for (int i = 0; i < 128; ++i) - { - curve.values[i] = SAMPLER_CURVE_GAP; - values[i] = -100; - } - curve.values[0] = 0; - curve.values[64] = 0; - curve.values[127] = 1; - // Linear - sampler_midi_curve_interpolate(&curve, values, 0, 1, FALSE); - for (int i = 0; i < 128; ++i) - { - float expected = i < 64 ? 0 : (i - 64) / 63.0; - test_assert(fabs(values[i] - expected) < 0.001); - } - // Quadratic - sampler_midi_curve_interpolate(&curve, values, 0, 1, TRUE); - for (int i = 0; i < 128; ++i) - { - float expected = i < 64 ? 0 : (i - 64) / 63.0; - expected = expected * expected; - test_assert(fabs(values[i] - expected) < 0.001); - } -} - -//////////////////////////////////////////////////////////////////////////////// - -void test_sampler_midicurve2(struct test_env *env) -{ - struct sampler_module *m = create_sampler_instance(env, "test_setup", "smp1"); - struct sampler_program *prg = load_sfz_into_sampler(env, m, - " curve_index=8 v0=-1 v32=0 v96=0 v127=1\n" - " curve_index=9 v32=0\n" - ); - for (int i = 0; i < 128; ++i) - { - float expected, actual; - if (i < 32) - expected = (-1 + (i / 32.0)); - else if (i <= 96) - expected = 0; - else - expected = (i - 96) * 1.f / (127 - 96); - actual = sampler_program_get_curve_value(prg, 8, i / 127.0); - test_assert(fabs(actual - expected) < 0.001); - - // Test interpolation - expected = 0.25f * actual + 0.75f * sampler_program_get_curve_value(prg, 8, (i + 1) / 127.0); - actual = sampler_program_get_curve_value(prg, 8, (i + 0.75) / 127.0); - test_assert(fabs(actual - expected) < 0.001); - - // Another curve - expected = i < 32 ? 0 : (i - 32) * 1.f / (127 - 32); - actual = sampler_program_get_curve_value(prg, 9, i / 127.0); - test_assert(fabs(actual - expected) < 0.001); - } - -#define VERIFY_BUILTIN_CURVE(curve, point, expected) \ - test_assert(fabs(sampler_program_get_curve_value(prg, curve, point) - expected) < 0.001) - - VERIFY_BUILTIN_CURVE(0, 0.f, 0.f); - VERIFY_BUILTIN_CURVE(0, 0.5f, 0.5f); - VERIFY_BUILTIN_CURVE(0, 1.0f, 1.0f); - - VERIFY_BUILTIN_CURVE(1, 0.0f, -1.0f); - VERIFY_BUILTIN_CURVE(1, 63.f/127.f, 0.0f); - VERIFY_BUILTIN_CURVE(1, 0.5f, 0.0f); - VERIFY_BUILTIN_CURVE(1, 64.f/127.f, 0.0f); - VERIFY_BUILTIN_CURVE(1, 1.0f, 1.0f); - - VERIFY_BUILTIN_CURVE(2, 0.f, 1.f); - VERIFY_BUILTIN_CURVE(2, 0.5f, 0.5f); - VERIFY_BUILTIN_CURVE(2, 1.0f, 0.f); - - VERIFY_BUILTIN_CURVE(3, 0.0f, 1.0f); - VERIFY_BUILTIN_CURVE(3, 63.f/127.f, 0.0f); - VERIFY_BUILTIN_CURVE(3, 0.5f, 0.0f); - VERIFY_BUILTIN_CURVE(3, 64.f/127.f, 0.0f); - VERIFY_BUILTIN_CURVE(3, 1.0f, -1.0f); - - VERIFY_BUILTIN_CURVE(4, 0.0f, 0.0f); - VERIFY_BUILTIN_CURVE(4, 0.5f, 0.25f); - VERIFY_BUILTIN_CURVE(4, 1.0f, 1.0f); - - VERIFY_BUILTIN_CURVE(5, 0.0f, 0.0f); - VERIFY_BUILTIN_CURVE(5, 0.25f, 0.5f); - VERIFY_BUILTIN_CURVE(5, 1.0f, 1.0f); - - sampler_unselect_program(m, prg); - CBOX_DELETE(prg); - CBOX_DELETE(&m->module); -} - -//////////////////////////////////////////////////////////////////////////////// - -void test_sampler_note_basic(struct test_env *env) -{ - struct sampler_module *m = create_sampler_instance(env, "test_setup", "smp1"); - struct sampler_program *prg = load_sfz_into_sampler(env, m, - " sample=*saw loop_mode=loop_continuous\n"); - - for (int i = 0; i < 5; ++i) - { - uint8_t midi_data[3] = { 0x90, 48 + i, 127 }; - m->module.process_event(&m->module, midi_data, sizeof(midi_data)); - int expected_voices[16] = {[0] = 1 + i}; - verify_sampler_voices(env, m, expected_voices); - } - for (int i = 0; i < 5; ++i) - { - uint8_t midi_data[3] = { 0x91, 48 + i, 127 }; - m->module.process_event(&m->module, midi_data, sizeof(midi_data)); - int expected_voices[16] = {[0] = 5, [1] = 1 + i}; - verify_sampler_voices(env, m, expected_voices); - int expected_released_voices[16] = {}; - verify_sampler_voices_if(env, m, expected_released_voices, is_voice_released, NULL); - } - - // Send some MIDI off to the first channel - for (int i = 0; i < 5; ++i) - { - uint8_t midi_data[3] = { (i & 1) ? 0x90 : 0x80, 48 + i, (i & 1) ? 0 : 127 }; - m->module.process_event(&m->module, midi_data, sizeof(midi_data)); - int expected_voices[16] = {[0] = 5, [1] = 5}; - verify_sampler_voices(env, m, expected_voices); - int expected_released_voices[16] = {[0] = 1 + i}; - verify_sampler_voices_if(env, m, expected_released_voices, is_voice_released, NULL); - } - sampler_unselect_program(m, prg); - CBOX_DELETE(prg); - CBOX_DELETE(&m->module); -} - -//////////////////////////////////////////////////////////////////////////////// - -struct region_logic_test_setup_step -{ - const uint8_t *midi_data; - uint32_t midi_data_len; - uint32_t voices[16]; -}; - -struct region_logic_test_setup -{ - const char *name; - const char *sfz_data; - const struct region_logic_test_setup_step *steps; -}; - -void test_sampler_note_region_logic(struct test_env *env) -{ - struct region_logic_test_setup *setup = env->arg; - struct sampler_module *m = create_sampler_instance(env, "test_setup", "smp1"); - struct sampler_program *prg = load_sfz_into_sampler(env, m, setup->sfz_data); - - int expected_voices[16] = {}; - for (int i = 0; setup->steps[i].midi_data; ++i) - { - env->context = g_strdup_printf("%s[%d]", setup->name, i); - const struct region_logic_test_setup_step *step = &setup->steps[i]; - m->module.process_event(&m->module, step->midi_data, step->midi_data_len); - for (int c = 0; c < 16; ++c) - expected_voices[c] += step->voices[c]; - verify_sampler_voices(env, m, expected_voices); - - g_free(env->context); - env->context = NULL; - } - sampler_unselect_program(m, prg); - CBOX_DELETE(prg); - CBOX_DELETE(&m->module); -} - -//////////////////////////////////////////////////////////////////////////////// - -#define MIDI_DATA_STEP(data, voices) { (const uint8_t *)data, sizeof(data) - 1, {voices} } -#define MIDI_DATA_END { NULL, 0, {} } -#define MIDI_DATA_STEP_MT(data, ...) { (const uint8_t *)data, sizeof(data) - 1, {__VA_ARGS__} } -#define REGION_LOGIC_TEST_SETUP(_name, sfz) \ - struct region_logic_test_setup setup_##_name = { \ - .name = #_name, \ - .sfz_data = sfz, \ - .steps = steps_##_name \ - } - -struct region_logic_test_setup_step steps_lokeyhikey[] = { - MIDI_DATA_STEP("\x90\x24\x7F", 0), - MIDI_DATA_STEP("\x90\x1F\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 1), - MIDI_DATA_STEP("\x90\x21\x7F", 1), - MIDI_DATA_STEP("\x90\x22\x7F", 1), - MIDI_DATA_STEP("\x90\x23\x7F", 1), - MIDI_DATA_END, -}; - -REGION_LOGIC_TEST_SETUP(lokeyhikey, - "lokey=32 hikey=35 sample=*saw" -); - -struct region_logic_test_setup_step steps_lokeyhikey2[] = { - MIDI_DATA_STEP("\x90\x0E\x7F", 0), - MIDI_DATA_STEP("\x90\x0F\x7F", 1), - MIDI_DATA_STEP("\x90\x10\x7F", 1), - MIDI_DATA_STEP("\x90\x11\x7F", 0), - MIDI_DATA_STEP("\x90\x1F\x7F", 0), - MIDI_DATA_STEP("\x90\x24\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 1), - MIDI_DATA_STEP("\x90\x21\x7F", 2), - MIDI_DATA_STEP("\x90\x22\x7F", 2), - MIDI_DATA_STEP("\x90\x23\x7F", 1), - MIDI_DATA_STEP("\x90\x47\x7F", 0), - MIDI_DATA_STEP("\x90\x48\x7F", 1), - MIDI_DATA_STEP("\x90\x49\x7F", 0), - MIDI_DATA_END, -}; - -REGION_LOGIC_TEST_SETUP(lokeyhikey2, - "lokey=15 hikey=16 sample=*saw\n" - "lokey=32 hikey=35 sample=*saw\n" - "lokey=33 hikey=34 sample=*saw\n" - "key=72 sample=*saw\n" -); - -struct region_logic_test_setup_step steps_lovelhivel[] = { - MIDI_DATA_STEP("\x90\x20\x1F", 0), - MIDI_DATA_STEP("\x90\x20\x24", 0), - MIDI_DATA_STEP("\x90\x20\x20", 1), - MIDI_DATA_STEP("\x90\x20\x21", 1), - MIDI_DATA_STEP("\x90\x20\x22", 1), - MIDI_DATA_STEP("\x90\x20\x23", 1), - MIDI_DATA_END, -}; - -REGION_LOGIC_TEST_SETUP(lovelhivel, - "lovel=32 hivel=35 sample=*saw" -); - -struct region_logic_test_setup_step steps_lochanhichan[] = { - MIDI_DATA_STEP_MT("\x90\x20\x7F", 0, 0, 0, 0, 0, 0, 0), - MIDI_DATA_STEP_MT("\x91\x20\x7F", 0, 1, 0, 0, 0, 0, 0), - MIDI_DATA_STEP_MT("\x92\x20\x7F", 0, 0, 1, 0, 0, 0, 0), - MIDI_DATA_STEP_MT("\x93\x20\x7F", 0, 0, 0, 0, 0, 0, 0), - MIDI_DATA_STEP_MT("\x94\x20\x7F", 0, 0, 0, 0, 2, 0, 0), - MIDI_DATA_STEP_MT("\x95\x20\x7F", 0, 0, 0, 0, 0, 2, 0), - MIDI_DATA_STEP_MT("\x96\x20\x7F", 0, 0, 0, 0, 0, 0, 0), - MIDI_DATA_END, -}; - -REGION_LOGIC_TEST_SETUP(lochanhichan, - "lochan=2 hichan=3 sample=*saw " - "lochan=5 hichan=6 sample=*saw " - "lochan=5 hichan=6 sample=*saw " -); - -struct region_logic_test_setup_step steps_chanaft[] = { - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_STEP("\xD0\x1F", 0), - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_STEP("\xD0\x20", 0), - MIDI_DATA_STEP("\x90\x20\x20", 1), - MIDI_DATA_STEP("\xD0\x21", 0), - MIDI_DATA_STEP("\x90\x20\x20", 1), - MIDI_DATA_STEP("\xD0\x22", 0), - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_END, -}; - -REGION_LOGIC_TEST_SETUP(chanaft, - "lochanaft=32 hichanaft=33 sample=*saw" -); - -struct region_logic_test_setup_step steps_polyaft[] = { - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_STEP("\xA0\x10\x1F", 0), - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_STEP("\xA0\x10\x20", 0), // note that this does not care about which key - it uses the last poly aft value - MIDI_DATA_STEP("\x90\x20\x20", 1), - MIDI_DATA_STEP("\xA0\x10\x21", 0), - MIDI_DATA_STEP("\x90\x20\x20", 1), - MIDI_DATA_STEP("\xA0\x10\x22", 0), - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_END, -}; - -REGION_LOGIC_TEST_SETUP(polyaft, - "lopolyaft=32 hipolyaft=33 sample=*saw" -); - -struct region_logic_test_setup_step steps_cc[] = { - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_STEP("\xB0\x10\x1F", 0), - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_STEP("\xB0\x10\x20", 0), - MIDI_DATA_STEP("\x90\x20\x20", 1), - MIDI_DATA_STEP("\xB0\x11\x7F", 0), // try a different CC, just in case (positive test) - MIDI_DATA_STEP("\x90\x20\x20", 1), - MIDI_DATA_STEP("\xB0\x10\x21", 0), - MIDI_DATA_STEP("\x90\x20\x20", 1), - MIDI_DATA_STEP("\xB0\x10\x22", 0), - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_STEP("\xB0\x11\x21", 0), // try a different CC, just in case (negative test) - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_STEP("\xB0\x10\x1F", 0), - MIDI_DATA_STEP("\xB0\x11\x20", 0), - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_END, -}; - -REGION_LOGIC_TEST_SETUP(cc, - "locc16=32 hicc16=33 sample=*saw" -); - -struct region_logic_test_setup_step steps_cc2[] = { - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_STEP("\xB0\x10\x1F", 0), - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_STEP("\xB0\x10\x20", 0), - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_STEP("\xB0\x11\x41", 0), - MIDI_DATA_STEP("\x90\x20\x20", 1), - MIDI_DATA_STEP("\xB0\x10\x21", 0), - MIDI_DATA_STEP("\x90\x20\x20", 1), - MIDI_DATA_STEP("\xB0\x10\x22", 0), - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_STEP("\xB0\x10\x21", 0), - MIDI_DATA_STEP("\x90\x20\x20", 1), - MIDI_DATA_STEP("\xB0\x11\x42", 0), - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_STEP("\xB0\x11\x41", 0), - MIDI_DATA_STEP("\xB0\x10\x22", 0), - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_STEP("\xB0\x11\x3F", 0), - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_STEP("\xB0\x10\x22", 0), - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_END, -}; - -REGION_LOGIC_TEST_SETUP(cc2, - "locc16=32 hicc16=33 locc17=64 hicc17=65 sample=*saw" -); - -struct region_logic_test_setup_step steps_cc3[] = { // CC16 <= 33, CC17 >= 64 - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_STEP("\xB0\x10\x1F", 0), - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_STEP("\xB0\x10\x20", 0), - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_STEP("\xB0\x11\x41", 0), - MIDI_DATA_STEP("\x90\x20\x20", 1), - MIDI_DATA_STEP("\xB0\x11\x71", 0), - MIDI_DATA_STEP("\x90\x20\x20", 1), - MIDI_DATA_STEP("\xB0\x10\x21", 0), - MIDI_DATA_STEP("\x90\x20\x20", 1), - MIDI_DATA_STEP("\xB0\x10\x22", 0), - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_STEP("\xB0\x10\x21", 0), - MIDI_DATA_STEP("\x90\x20\x20", 1), - MIDI_DATA_STEP("\xB0\x10\x1F", 0), - MIDI_DATA_STEP("\x90\x20\x20", 1), - MIDI_DATA_STEP("\xB0\x11\x42", 0), - MIDI_DATA_STEP("\x90\x20\x20", 1), - MIDI_DATA_STEP("\xB0\x11\x41", 0), - MIDI_DATA_STEP("\xB0\x10\x22", 0), - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_STEP("\xB0\x11\x3F", 0), - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_STEP("\xB0\x10\x22", 0), - MIDI_DATA_STEP("\x90\x20\x20", 0), - MIDI_DATA_END, -}; - -REGION_LOGIC_TEST_SETUP(cc3, - "hicc16=33 locc17=64 sample=*saw" -); - -struct region_logic_test_setup_step steps_oncc[] = { - MIDI_DATA_STEP("\xB0\x10\x1F", 0), - MIDI_DATA_STEP("\xB0\x10\x20", 1), - MIDI_DATA_STEP("\xB0\x10\x21", 1), // should probably be 1 according to test file 16, but that's madness - MIDI_DATA_STEP("\xB0\x10\x22", 0), - MIDI_DATA_STEP("\xB0\x10\x21", 1), - MIDI_DATA_STEP("\xB0\x10\x20", 1), // same - MIDI_DATA_STEP("\xB0\x10\x1F", 0), - MIDI_DATA_END, -}; - -REGION_LOGIC_TEST_SETUP(oncc, - "on_locc16=32 on_hicc16=33 sample=*saw" -); - -struct region_logic_test_setup_step steps_release[] = { - MIDI_DATA_STEP("\x90\x20\x7F", 0), - MIDI_DATA_STEP("\x80\x20\x7F", 1), - MIDI_DATA_STEP("\x80\x20\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x00", 1), - MIDI_DATA_STEP("\x90\x20\x00", 0), - MIDI_DATA_END, -}; - -REGION_LOGIC_TEST_SETUP(release, - "trigger=release sample=*saw" -); - -struct region_logic_test_setup_step steps_firstlegato[] = { - MIDI_DATA_STEP_MT("\x90\x20\x7F", 1), - MIDI_DATA_STEP_MT("\x90\x21\x7F", 2), - MIDI_DATA_STEP_MT("\x90\x22\x7F", 2), - MIDI_DATA_STEP_MT("\x91\x20\x7F", 0, 1), // a different channel has its own counter - MIDI_DATA_STEP_MT("\x91\x21\x7F", 0, 2), - MIDI_DATA_STEP_MT("\x91\x22\x7F", 0, 2), - MIDI_DATA_STEP_MT("\x80\x20\x7F", 0), - MIDI_DATA_STEP_MT("\x80\x21\x7F", 0), - MIDI_DATA_STEP_MT("\x80\x22\x7F", 0), - MIDI_DATA_STEP_MT("\x90\x20\x7F", 1), - MIDI_DATA_STEP_MT("\x90\x21\x7F", 2), - MIDI_DATA_STEP_MT("\x90\x22\x7F", 2), - MIDI_DATA_END, -}; - -REGION_LOGIC_TEST_SETUP(firstlegato, - "trigger=first sample=*saw" - "trigger=legato sample=*saw" - "trigger=legato sample=*saw" -); - -struct region_logic_test_setup_step steps_switches[] = { - MIDI_DATA_STEP("\x90\x12\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 0), - MIDI_DATA_STEP("\x90\x10\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 1), - MIDI_DATA_STEP("\x90\x11\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 2), - MIDI_DATA_STEP("\x90\x10\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 1), - MIDI_DATA_STEP("\x90\x0F\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 1), - MIDI_DATA_STEP("\x90\x14\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 1), - MIDI_DATA_STEP("\x90\x12\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 0), - MIDI_DATA_END, -}; - -REGION_LOGIC_TEST_SETUP(switches, - "sw_lokey=16 sw_hikey=19 sw_last=16 lokey=32 hikey=35 sample=*saw" - "sw_lokey=16 sw_hikey=19 sw_last=17 lokey=32 hikey=35 sample=*saw" - "sw_lokey=16 sw_hikey=19 sw_last=17 lokey=32 hikey=35 sample=*saw" -); - -struct region_logic_test_setup_step steps_switches2[] = { - MIDI_DATA_STEP("\x90\x20\x7F", 0), - MIDI_DATA_STEP("\x90\x10\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 1), - MIDI_DATA_STEP("\x80\x10\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 0), - MIDI_DATA_STEP("\x90\x11\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 2), - MIDI_DATA_STEP("\x80\x11\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 0), - MIDI_DATA_STEP("\x90\x10\x7F", 0), - MIDI_DATA_STEP("\x90\x11\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 3), - MIDI_DATA_STEP("\x80\x10\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 2), - MIDI_DATA_STEP("\x80\x11\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 0), - MIDI_DATA_END, -}; - -REGION_LOGIC_TEST_SETUP(switches2, - "sw_lokey=16 sw_hikey=19 sw_down=16 lokey=32 hikey=35 sample=*saw" - "sw_lokey=16 sw_hikey=19 sw_down=17 lokey=32 hikey=35 sample=*saw" - "sw_lokey=16 sw_hikey=19 sw_down=17 lokey=32 hikey=35 sample=*saw" -); - -struct region_logic_test_setup_step steps_switches3[] = { - MIDI_DATA_STEP("\x90\x20\x7F", 2), // [0] - MIDI_DATA_STEP("\x90\x12\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 1), - MIDI_DATA_STEP("\x90\x10\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 2), - MIDI_DATA_STEP("\x90\x11\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 3), - MIDI_DATA_STEP("\x90\x10\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 2), - MIDI_DATA_STEP("\x90\x0F\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 2), // [10] - MIDI_DATA_STEP("\x90\x14\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 2), - MIDI_DATA_STEP("\x90\x12\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 1), - - MIDI_DATA_STEP("\x90\x09\x7F", 0), - MIDI_DATA_STEP("\x90\x12\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 2), - MIDI_DATA_STEP("\x90\x10\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 3), - MIDI_DATA_STEP("\x90\x11\x7F", 0), // [20] - MIDI_DATA_STEP("\x90\x20\x7F", 4), - MIDI_DATA_STEP("\x90\x10\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 3), - MIDI_DATA_STEP("\x90\x0F\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 3), - MIDI_DATA_STEP("\x90\x14\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 3), - MIDI_DATA_STEP("\x90\x12\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 2), - - MIDI_DATA_STEP("\xB0\x79\x7F", 0), // [30] reset all controllers - MIDI_DATA_STEP("\x90\x20\x7F", 2), - MIDI_DATA_END, -}; - -REGION_LOGIC_TEST_SETUP(switches3, - "lokey=32 hikey=35 sample=*saw" - "sw_lokey=8 sw_hikey=9 sw_last=9 lokey=32 hikey=35 sample=*saw" - "sw_lokey=16 sw_hikey=19 sw_last=16 lokey=32 hikey=35 sample=*saw" - "sw_lokey=16 sw_hikey=19 sw_last=17 lokey=32 hikey=35 sample=*saw" - "sw_lokey=16 sw_hikey=19 sw_last=17 lokey=32 hikey=35 sample=*saw" -); - -struct region_logic_test_setup_step steps_switches4[] = { - MIDI_DATA_STEP("\x90\x20\x7F", 2), - MIDI_DATA_STEP("\x90\x10\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 1), - MIDI_DATA_STEP("\x90\x11\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 2), - MIDI_DATA_STEP("\x90\x10\x7F", 0), - MIDI_DATA_STEP("\xB0\x79\x7F", 0), // reset all controllers - MIDI_DATA_STEP("\x90\x20\x7F", 2), - MIDI_DATA_STEP("\x90\x08\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 3), - MIDI_DATA_STEP("\x90\x09\x7F", 0), - MIDI_DATA_STEP("\x90\x20\x7F", 4), - MIDI_DATA_STEP("\xB0\x79\x7F", 0), // reset all controllers - MIDI_DATA_STEP("\x90\x20\x7F", 2), - MIDI_DATA_END, -}; - -REGION_LOGIC_TEST_SETUP(switches4, - "sw_lokey=16 sw_hikey=19 sw_default=17 sw_last=16 lokey=32 hikey=35 sample=*saw" - "sw_lokey=16 sw_hikey=19 sw_last=17 lokey=32 hikey=35 sample=*saw" - "sw_lokey=16 sw_hikey=19 sw_last=17 lokey=32 hikey=35 sample=*saw" - "sw_lokey=8 sw_hikey=10 sw_last=8 lokey=32 hikey=35 sample=*saw" - "sw_lokey=8 sw_hikey=10 sw_last=9 lokey=32 hikey=35 sample=*saw" - "sw_lokey=8 sw_hikey=10 sw_default=10 sw_last=9 lokey=32 hikey=35 sample=*saw" -); - -//////////////////////////////////////////////////////////////////////////////// - -void test_assert_failed(struct test_env *env, const char *file, int line, const char *check) -{ - if (env->context) - fprintf(stderr, "FAIL @%s:%d Assertion '%s' failed, context: %s.\n", file, line, check, env->context); - else - fprintf(stderr, "FAIL @%s:%d Assertion '%s' failed.\n", file, line, check); - longjmp(env->on_fail, 1); -} - -void test_assert_failed_free(struct test_env *env, const char *file, int line, gchar *check) -{ - if (env->context) - fprintf(stderr, "FAIL @%s:%d %s, context: %s\n", file, line, check, env->context); - else - fprintf(stderr, "FAIL @%s:%d %s.\n", file, line, check); - g_free(check); - longjmp(env->on_fail, 1); -} - -//////////////////////////////////////////////////////////////////////////////// - -struct test_info { - const char *name; - void (*func)(struct test_env *env); - void *arg; -} tests[] = { - { "test_sampler_setup", test_sampler_setup }, - { "test_sampler_midicurve", test_sampler_midicurve }, - { "test_sampler_midicurve2", test_sampler_midicurve2 }, - { "test_sampler_note_basic", test_sampler_note_basic }, - { "test_sampler_note_region_logic/key", test_sampler_note_region_logic, &setup_lokeyhikey }, - { "test_sampler_note_region_logic/key2", test_sampler_note_region_logic, &setup_lokeyhikey2 }, - { "test_sampler_note_region_logic/vel", test_sampler_note_region_logic, &setup_lovelhivel }, - { "test_sampler_note_region_logic/ch", test_sampler_note_region_logic, &setup_lochanhichan }, - { "test_sampler_note_region_logic/chanaft", test_sampler_note_region_logic, &setup_chanaft }, - { "test_sampler_note_region_logic/polyaft", test_sampler_note_region_logic, &setup_polyaft }, - { "test_sampler_note_region_logic/cc", test_sampler_note_region_logic, &setup_cc }, - { "test_sampler_note_region_logic/cc2", test_sampler_note_region_logic, &setup_cc2 }, - { "test_sampler_note_region_logic/cc3", test_sampler_note_region_logic, &setup_cc3 }, - { "test_sampler_note_region_logic/oncc", test_sampler_note_region_logic, &setup_oncc }, - { "test_sampler_note_region_logic/release", test_sampler_note_region_logic, &setup_release }, - { "test_sampler_note_region_logic/firstlegato", test_sampler_note_region_logic, &setup_firstlegato }, - { "test_sampler_note_region_logic/switches", test_sampler_note_region_logic, &setup_switches }, - { "test_sampler_note_region_logic/switches2", test_sampler_note_region_logic, &setup_switches2 }, - { "test_sampler_note_region_logic/switches3", test_sampler_note_region_logic, &setup_switches3 }, - { "test_sampler_note_region_logic/switches4", test_sampler_note_region_logic, &setup_switches4 }, -}; - -int main(int argc, char *argv[]) -{ - uint32_t tests_run = 0, tests_failed = 0; - for (unsigned int i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i) - { - struct test_env env; - env.doc = cbox_document_new(); - env.engine = cbox_engine_new(env.doc, NULL); - env.arg = tests[i].arg; - env.context = NULL; - cbox_config_init(""); - cbox_wavebank_init(); - tests_run++; - if (0 == setjmp(env.on_fail)) - { - printf("Running %s... ", tests[i].name); - fflush(stdout); - tests[i].func(&env); - printf("PASS\n"); - } - else - tests_failed++; - - CBOX_DELETE(env.engine); - env.engine = NULL; - cbox_document_destroy(env.doc); - env.doc = NULL; - cbox_wavebank_close(); - cbox_config_close(); - if (env.context) - { - g_free(env.context); - env.context = NULL; - } - } - printf("%d tests ran, %d tests failed.\n", tests_run, tests_failed); - return tests_failed != 0; -} - diff --git a/template/calfbox/tests.h b/template/calfbox/tests.h deleted file mode 100644 index 3f29996..0000000 --- a/template/calfbox/tests.h +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include -#include -#include - -struct test_env -{ - struct cbox_document *doc; - struct cbox_engine *engine; - void *arg; - gchar *context; - jmp_buf on_fail; -}; - -#define test_assert(condition) \ - if (!(condition)) \ - test_assert_failed(env, __FILE__, __LINE__, #condition); - -#define STR_FORMAT_int "%d" -#define STR_FORMAT_unsigned "%u" -#define STR_FORMAT_uint32_t PRIu32 - -#define test_assert_equal(type, val1, val2) \ - do { \ - type _v1 = (val1), _v2 = (val2); \ - if ((_v1) != (_v2)) \ - test_assert_failed_free(env, __FILE__, __LINE__, g_strdup_printf("%s equal to " STR_FORMAT_##type ", not " STR_FORMAT_##type, #val1, _v1, _v2)); \ - } while(0) - -#define test_assert_equal_str(val1, val2) \ - do { \ - const char *_v1 = (val1), *_v2 = (val2); \ - if (strcmp(_v1, _v2)) \ - test_assert_failed_free(env, __FILE__, __LINE__, g_strdup_printf("%s equal to '%s', not '%s'", #val1, _v1, _v2)); \ - } while(0) - -#define test_assert_no_error(error) \ - do { \ - if (error) { \ - gchar *copy = g_strdup(error->message); \ - g_error_free(error); \ - test_assert_failed_free(env, __FILE__, __LINE__, copy); \ - } \ - } while(0) - -extern void test_assert_failed(struct test_env *env, const char *file, int line, const char *check); -extern void test_assert_failed_free(struct test_env *env, const char *file, int line, gchar *check); - diff --git a/template/calfbox/tonectl.c b/template/calfbox/tonectl.c deleted file mode 100644 index d0f19e5..0000000 --- a/template/calfbox/tonectl.c +++ /dev/null @@ -1,133 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config.h" -#include "config-api.h" -#include "dspmath.h" -#include "module.h" -#include "onepole-float.h" -#include -#include -#include -#include -#include -#include -#include - -#define MODULE_PARAMS tone_control_params - -struct tone_control_params -{ - float lowpass, highpass; -}; - -struct tone_control_module -{ - struct cbox_module module; - - struct tone_control_params *params, *old_params; - - struct cbox_onepolef_coeffs lowpass_coeffs, highpass_coeffs; - - struct cbox_onepolef_state lowpass_state[2], highpass_state[2]; - - float tpdsr; // 2 pi / sr -}; - -gboolean tone_control_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct tone_control_module *m = (struct tone_control_module *)ct->user_data; - - EFFECT_PARAM("/lowpass", "f", lowpass, double, , 5, 20000) else - EFFECT_PARAM("/highpass", "f", highpass, double, , 5, 20000) else - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - return cbox_execute_on(fb, NULL, "/lowpass", "f", error, m->params->lowpass) - && cbox_execute_on(fb, NULL, "/highpass", "f", error, m->params->highpass) - && CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error) - ; - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); - return TRUE; -} - -void tone_control_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) -{ - // struct tone_control_module *m = (struct tone_control_module *)module; -} - -void tone_control_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) -{ - struct tone_control_module *m = (struct tone_control_module *)module; - - if (m->params != m->old_params) - { - cbox_onepolef_set_lowpass(&m->lowpass_coeffs, m->params->lowpass * m->tpdsr); - cbox_onepolef_set_highpass(&m->highpass_coeffs, m->params->highpass * m->tpdsr); - m->old_params = m->params; - } - - cbox_onepolef_process_to(&m->lowpass_state[0], &m->lowpass_coeffs, inputs[0], outputs[0]); - cbox_onepolef_process_to(&m->lowpass_state[1], &m->lowpass_coeffs, inputs[1], outputs[1]); - cbox_onepolef_process(&m->highpass_state[0], &m->highpass_coeffs, outputs[0]); - cbox_onepolef_process(&m->highpass_state[1], &m->highpass_coeffs, outputs[1]); -} - -MODULE_SIMPLE_DESTROY_FUNCTION(tone_control) - -MODULE_CREATE_FUNCTION(tone_control) -{ - static int inited = 0; - if (!inited) - { - inited = 1; - } - - struct tone_control_module *m = malloc(sizeof(struct tone_control_module)); - CALL_MODULE_INIT(m, 2, 2, tone_control); - m->module.process_event = tone_control_process_event; - m->module.process_block = tone_control_process_block; - - m->tpdsr = 2 * M_PI * m->module.srate_inv; - - m->old_params = NULL; - m->params = malloc(sizeof(struct tone_control_params)); - - m->params->lowpass = cbox_config_get_float(cfg_section, "lowpass", 8000.f); - m->params->highpass = cbox_config_get_float(cfg_section, "highpass", 75.f); - - cbox_onepolef_reset(&m->lowpass_state[0]); - cbox_onepolef_reset(&m->lowpass_state[1]); - cbox_onepolef_reset(&m->highpass_state[0]); - cbox_onepolef_reset(&m->highpass_state[1]); - - return &m->module; -} - - -struct cbox_module_keyrange_metadata tone_control_keyranges[] = { -}; - -struct cbox_module_livecontroller_metadata tone_control_controllers[] = { -}; - -DEFINE_MODULE(tone_control, 2, 2) - diff --git a/template/calfbox/tonewheel.c b/template/calfbox/tonewheel.c deleted file mode 100644 index dd76053..0000000 --- a/template/calfbox/tonewheel.c +++ /dev/null @@ -1,586 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config.h" -#include "config-api.h" -#include "cmd.h" -#include "dspmath.h" -#include "module.h" -#include "onepole-int.h" -#include -#include -#include -#include -#include -#include - -// a0 a1 a2 b1 b2 for scanner vibrato filter @4kHz with sr=44.1: 0.057198 0.114396 0.057198 -1.218829 0.447620 - -static int64_t scanner_a0 = (int64_t)(0.057198 * 1048576); -static int64_t scanner_b1 = (int64_t)(-1.218829 * 1048576); -static int64_t scanner_b2 = (int64_t)(0.447620 * 1048576); - -static int sine_table[2048]; -static int complex_table[2048]; -static int distortion_table[8192]; - -struct biquad -{ - int x1; - int y1; - int x2; - int y2; -}; - -struct tonewheel_organ_module -{ - struct cbox_module module; - - uint32_t frequency[91]; - uint32_t phase[91]; - uint64_t pedalmasks; - uint64_t upper_manual, lower_manual; - int amp_scaling[91]; - struct biquad scanner_delay[18]; - struct cbox_onepole_state filter_anticlick, filter_overdrive; - struct cbox_onepole_coeffs filter_anticlick_coeffs, filter_overdrive_coeffs; - float percussion; - int enable_percussion, enable_vibrato_upper, enable_vibrato_lower, vibrato_mode, vibrato_mix, percussion_3rd; - int do_filter; - int cc91; - uint32_t vibrato_phase, vibrato_dphase; - - int pedal_drawbar_settings[2]; - int upper_manual_drawbar_settings[9]; - int lower_manual_drawbar_settings[9]; -}; - -static const int drawbars[9] = {0, 19, 12, 24, 24 + 7, 36, 36 + 4, 36 + 7, 48}; - -static void set_keymask(struct tonewheel_organ_module *m, int channel, int key, int value) -{ - uint64_t mask = 0; - uint64_t *manual = NULL; - if (key >= 24 && key < 36) - { - mask = 1 << (key - 24); - manual = &m->pedalmasks; - } - else if (key >= 36 && key < 36 + 61) - { - manual = (channel == 0) ? &m->upper_manual : &m->lower_manual; - mask = ((int64_t)1) << (key - 36); - } - else - return; - - if (value) - *manual |= mask; - else - *manual &= ~mask; -} - -void tonewheel_organ_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len) -{ - struct tonewheel_organ_module *m = (struct tonewheel_organ_module *)module; - if (len > 0) - { - int cmd = data[0] >> 4; - if (cmd == 9 && data[2]) - { - int channel = data[0] & 0x0F; - int key = data[1] & 127; - set_keymask(m, channel, key, 1); - if (m->percussion < 0 && key >= 36 && m->enable_percussion && channel == 0) - m->percussion = 16.0; - } - if (cmd == 8 || (cmd == 9 && !data[2])) - { - int channel = data[0] & 0x0F; - int key = data[1] & 127; - set_keymask(m, channel, key, 0); - - if (channel == 0 && !m->upper_manual) - m->percussion = -1; - } - if (cmd == 11) - { - int *drawbars = (data[0] & 0xF0) != 0 ? m->lower_manual_drawbar_settings : m->upper_manual_drawbar_settings; - if (data[1] >= 21 && data[1] <= 29) - drawbars[data[1] - 21] = data[2] * 8 / 127; - if (data[1] == 82) - drawbars[8] = data[2] * 8 / 127; - if (data[1] == 64) - m->do_filter = data[2] >= 64; - if (data[1] == 91) - m->cc91 = data[2]; - if (data[1] == 93) - m->vibrato_mix = data[2] > 0; - if (data[1] == 120 || data[1] == 123) - { - for (int i = 24; i < 36 + 61; i++) - set_keymask(m, data[0] & 0xF, i, 0); - } - //if (data[1] == 6) - // cbox_onepole_set_lowpass(&m->filter_overdrive_coeffs, hz2w(data[2] * 10000 / 127, 44100.0)); - } - } -} - -static inline int check_keymask(uint64_t keymasks, int note) -{ - if (note < 0 || note > 127) - return 0; - if (note >= 24 && note < 36) - return 0 != (keymasks & (1 << (note - 24))); - if (note >= 36 && note < 36 + 61) - return 0 != (keymasks & (1ULL << (note - 36))); - return 0; -} - -static inline int tonegenidx_pedals(int note, int shift) -{ - if (note < 24 || note > 24 + 11) - return 91; - - note -= 24; - return note + shift; -} - -static inline int tonegenidx(int note, int shift) -{ - // ignore everything below the lowest key - if (note < 36) - return 91; - - note -= 36; - - // harmonic foldback in the first octave of the manual - if (note < 12 && shift < 12) - return note + 12; - - while (note + shift > 90) - note -= 12; - - return note + shift; -} - -static int drawbar_amp_mapping[9] = { 0, 1, 2, 3, 4, 6, 8, 11, 16 }; - -static void calc_crosstalk(int *wheel1, int *wheel2) -{ - int w1 = *wheel1; - int w2 = *wheel2; - *wheel1 += w2 >> 9; - *wheel2 += w1 >> 9; -} - -static int compress_amp(int iamp, int scaling) -{ - if (iamp > 512) - iamp = 512 + 3 * ((iamp - 512) >> 2); - return (iamp * scaling) >> 10; -} - -static void set_tonewheels(struct tonewheel_organ_module *m, int tonegens[2][92]) -{ - int n, i; - int pshift = m->percussion_3rd ? 24 + 7 : 24; - - int upper_manual_drawbar_amp[9], lower_manual_drawbar_amp[9]; - - for (i = 0; i < 9; i++) - { - upper_manual_drawbar_amp[i] = drawbar_amp_mapping[m->upper_manual_drawbar_settings[i]] * 8; - lower_manual_drawbar_amp[i] = drawbar_amp_mapping[m->lower_manual_drawbar_settings[i]] * 8; - } - - memset(tonegens, 0, 2 * 92 * sizeof(tonegens[0][0])); - // pedalboard - for (n = 24; n < 24 + 12; n++) - { - if (check_keymask(m->pedalmasks, n)) - { - tonegens[0][tonegenidx_pedals(n, 0)] += 3 * 16 * m->pedal_drawbar_settings[0]; - tonegens[0][tonegenidx_pedals(n, 12)] += 3 * 16 * m->pedal_drawbar_settings[1]; - } - } - // manual - for (n = 36; n < 36 + 61; n++) - { - if (check_keymask(m->upper_manual, n)) - { - int tgf = m->enable_vibrato_upper; - for (i = 0; i < 9; i++) - { - int tg = tonegenidx(n, drawbars[i]); - tonegens[tgf][tg] += upper_manual_drawbar_amp[i]; - } - if (m->percussion > 0) - tonegens[0][tonegenidx(n, pshift)] += m->percussion * 10; - } - if (check_keymask(m->lower_manual, n)) - { - int tgf = m->enable_vibrato_lower; - for (i = 0; i < 9; i++) - { - int tg = tonegenidx(n, drawbars[i]); - tonegens[tgf][tg] += lower_manual_drawbar_amp[i]; - } - } - } - for (n = 0; n < 91; n++) - { - int scaling = m->amp_scaling[n]; - tonegens[0][n] = compress_amp(tonegens[0][n], scaling); - tonegens[1][n] = compress_amp(tonegens[1][n], scaling); - } - for (n = 0; n < 36; n++) - { - calc_crosstalk(&tonegens[0][n], &tonegens[0][n + 48]); - calc_crosstalk(&tonegens[1][n], &tonegens[1][n + 48]); - } -} - -void tonewheel_organ_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) -{ - struct tonewheel_organ_module *m = (struct tonewheel_organ_module *)module; - int n, i; - - static const uint32_t frac_mask = (1 << 21) - 1; - - int internal_out_for_vibrato[CBOX_BLOCK_SIZE]; - int internal_out[CBOX_BLOCK_SIZE]; - - for (i = 0; i < CBOX_BLOCK_SIZE; i++) - { - internal_out[i] = 0; - internal_out_for_vibrato[i] = 0; - } - // 91 tonewheels + 1 dummy - int tonegens[2][92]; - set_tonewheels(m, tonegens); - if (m->percussion > 0) - m->percussion *= 0.99f; - for (n = 0; n < 91; n++) - { - if (tonegens[0][n] > 0 || tonegens[1][n]) - { - int iamp1, iamp2; - - iamp1 = tonegens[0][n]; - iamp2 = tonegens[1][n]; - - int *table = n < 12 ? complex_table : sine_table; - uint32_t phase = m->phase[n]; - for (i = 0; i < CBOX_BLOCK_SIZE; i++) - { - uint32_t pos = phase >> 21; - int val0 = table[(pos - 1) & 2047]; - int val1 = table[pos]; - // phase & frac_mask has 21 bits of resolution, but we only have 14 bits of headroom here - int frac_14bit = (phase & frac_mask) >> (21-14); - int val = (val1 * frac_14bit + val0 * ((1 << 14) - frac_14bit)) >> 14; - internal_out[i] += val * iamp1 >> 3; - internal_out_for_vibrato[i] += val * iamp2 >> 3; - phase += m->frequency[n]; - } - } - m->phase[n] += m->frequency[n] * CBOX_BLOCK_SIZE; - } - - static const int v1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8 }; - static const int v2[] = { 0, 1, 2, 4, 6, 8, 9, 10, 12 }; - static const int v3[] = { 0, 1, 3, 6, 11, 12, 15, 17, 18, 18, 18 }; - static const int *vtypes[] = { v1, v2, v3 }; - const int *dmap = vtypes[m->vibrato_mode]; - int32_t mix = m->vibrato_mix; - for (i = 0; i < CBOX_BLOCK_SIZE; i++) - { - int x0 = internal_out_for_vibrato[i] >> 1; - int delay[19]; - int64_t accum; - delay[0] = x0; - for (n = 0; n < 18; n++) - { - struct biquad *bq = &m->scanner_delay[n]; - accum = 0; - accum += (x0 + (bq->x1 << 1) + bq->x2) * scanner_a0; - accum -= bq->y1 * scanner_b1; - accum -= bq->y2 * scanner_b2; - accum = accum >> 20; - bq->x2 = bq->x1; - bq->x1 = x0; - bq->y2 = bq->y1; - bq->y1 = accum; - - delay[1 + n] = x0 = accum; - } - m->vibrato_phase += m->vibrato_dphase; - - uint32_t vphase = m->vibrato_phase; - if (vphase >= 0x80000000) - vphase = ~vphase; - uint32_t vphint = vphase >> 28; - - accum = 0; - - accum += delay[dmap[vphint]] * ((1ULL << 28) - (vphase & ~0xF0000000)); - accum += delay[dmap[vphint + 1]] * (vphase & ~0xF0000000ULL); - - - internal_out[i] += (accum >> 28) + mix * delay[0]; - } - - int32_t filtered[CBOX_BLOCK_SIZE]; - cbox_onepole_process_to(&m->filter_overdrive, &m->filter_overdrive_coeffs, internal_out, filtered); - - for (i = 0; i < CBOX_BLOCK_SIZE; i++) - { - int value = filtered[i] >> 9; - int sign = (value >= 0 ? 1 : -1); - int a, b, idx; - - value = abs(value); - if (value > 8192 * 8 - 2 * 8) - value = 8192 * 8 - 2 * 8; - idx = value >> 3; - a = distortion_table[idx]; - b = distortion_table[idx + 1]; - internal_out[i] = (internal_out[i] >> 11) + sign * (a + ((b - a) * (value & 7) >> 3)); - //internal_out[i] = 32767 * value2; - } - - cbox_onepole_process(&m->filter_anticlick, &m->filter_anticlick_coeffs, internal_out); - - for (i = 0; i < CBOX_BLOCK_SIZE; i++) - { - float value = internal_out[i] * (1.0 / 32768.0); - outputs[1][i] = outputs[0][i] = value; - } -} - -static void biquad_init(struct biquad *bq) -{ - bq->x1 = bq->y1 = bq->x2 = bq->y2 = 0; -} - -static void read_drawbars(int *drawbars, int count, const char *registration) -{ - int i; - - memset(drawbars, 0, count * sizeof(int)); - for (i = 0; i < count; i++) - { - if (!registration[i]) - { - g_error("registration too short: %s (%d digits required)", registration, count); - break; - } - if (registration[i] < '0' || registration[i] > '8') - { - g_error("registration invalid: %s (%c is not in 0..8)", registration, registration[i]); - break; - } - drawbars[i] = registration[i] - '0'; - } -} - -static void tonewheel_organ_destroyfunc(struct cbox_module *module) -{ -} - -#define SINGLE_SETTING(flag, max, field) \ - else if (!strcmp(cmd->command, flag) && !strcmp(cmd->arg_types, "i")) \ - { \ - int setting = CBOX_ARG_I(cmd, 0); \ - if (setting >= 0 && setting <= max) \ - m->field = setting; \ - return TRUE; \ - } \ - -gboolean tonewheel_organ_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct tonewheel_organ_module *m = ct->user_data; - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - for (int i = 0; i < 9; i++) - { - if (!cbox_execute_on(fb, NULL, "/upper_drawbar", "ii", error, i, m->upper_manual_drawbar_settings[i])) - return FALSE; - if (!cbox_execute_on(fb, NULL, "/lower_drawbar", "ii", error, i, m->lower_manual_drawbar_settings[i])) - return FALSE; - } - for (int i = 0; i < 2; i++) - { - if (!cbox_execute_on(fb, NULL, "/pedal_drawbar", "ii", error, i, m->pedal_drawbar_settings[i])) - return FALSE; - } - return cbox_execute_on(fb, NULL, "/upper_vibrato", "i", error, m->enable_vibrato_upper) && - cbox_execute_on(fb, NULL, "/lower_vibrato", "i", error, m->enable_vibrato_lower) && - cbox_execute_on(fb, NULL, "/vibrato_mode", "i", error, m->vibrato_mode) && - cbox_execute_on(fb, NULL, "/vibrato_chorus", "i", error, m->vibrato_mix) && - cbox_execute_on(fb, NULL, "/percussion_enable", "i", error, m->enable_percussion) && - cbox_execute_on(fb, NULL, "/percussion_3rd", "i", error, m->percussion_3rd) && - CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error); - } - else if (!strcmp(cmd->command, "/upper_drawbar") && !strcmp(cmd->arg_types, "ii")) - { - int drawbar = CBOX_ARG_I(cmd, 0); - int setting = CBOX_ARG_I(cmd, 1); - if (drawbar >= 0 && drawbar <= 8 && setting >= 0 && setting <= 8) - m->upper_manual_drawbar_settings[drawbar] = setting; - return TRUE; - } - else if (!strcmp(cmd->command, "/lower_drawbar") && !strcmp(cmd->arg_types, "ii")) - { - int drawbar = CBOX_ARG_I(cmd, 0); - int setting = CBOX_ARG_I(cmd, 1); - if (drawbar >= 0 && drawbar <= 8 && setting >= 0 && setting <= 8) - m->lower_manual_drawbar_settings[drawbar] = setting; - return TRUE; - } - SINGLE_SETTING("/upper_vibrato", 1, enable_vibrato_upper) - SINGLE_SETTING("/lower_vibrato", 1, enable_vibrato_lower) - SINGLE_SETTING("/vibrato_mode", 2, vibrato_mode) - SINGLE_SETTING("/vibrato_chorus", 1, vibrato_mix) - SINGLE_SETTING("/percussion_enable", 1, enable_percussion) - SINGLE_SETTING("/percussion_3rd", 1, percussion_3rd) - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); - return TRUE; -} - -MODULE_CREATE_FUNCTION(tonewheel_organ) -{ - static int inited = 0; - int i, srate; - const char *vibrato_mode; - if (!inited) - { - for (i = 0; i < 2048; i++) - { - float ph = i * M_PI / 1024; - sine_table[i] = (int)(32000 * sin(ph)); - complex_table[i] = (int)(32000 * (sin(ph) + sin(3 * ph) / 3 + sin(5 * ph) / 5 + sin(7 * ph) / 7 + sin(9 * ph) / 9 + sin(11 * ph) / 11)); - } - for (i = 0; i < 8192; i++) - { - float value = atan(sqrt(i * (4.0 / 8192))); - distortion_table[i] = ((int)(i * 2 + 32767 * value * value)) >> 1; - } - inited = 1; - } - - struct tonewheel_organ_module *m = malloc(sizeof(struct tonewheel_organ_module)); - CALL_MODULE_INIT(m, 0, 2, tonewheel_organ); - srate = m->module.srate; - m->module.process_event = tonewheel_organ_process_event; - m->module.process_block = tonewheel_organ_process_block; - cbox_onepole_reset(&m->filter_anticlick); - cbox_onepole_reset(&m->filter_overdrive); - cbox_onepole_set_lowpass(&m->filter_anticlick_coeffs, hz2w(180.0, srate)); - cbox_onepole_set_lowpass(&m->filter_overdrive_coeffs, hz2w(2500.0, srate)); - m->percussion = -1; - m->do_filter = 0; - m->cc91 = 0; - m->vibrato_phase = 0; - read_drawbars(m->upper_manual_drawbar_settings, 9, cbox_config_get_string_with_default(cfg_section, "upper_drawbars", "888000000")); - read_drawbars(m->lower_manual_drawbar_settings, 9, cbox_config_get_string_with_default(cfg_section, "lower_drawbars", "888800000")); - read_drawbars(m->pedal_drawbar_settings, 2, cbox_config_get_string_with_default(cfg_section, "pedal_drawbars", "82")); - m->enable_percussion = cbox_config_get_int(cfg_section, "percussion", 1); - m->enable_vibrato_upper = cbox_config_get_int(cfg_section, "vibrato_upper", 1); - m->enable_vibrato_lower = cbox_config_get_int(cfg_section, "vibrato_lower", 0); - m->percussion_3rd = cbox_config_get_int(cfg_section, "percussion_3rd", 1); - m->vibrato_dphase = (int)(6.6 / srate * 65536 * 65536); - - vibrato_mode = cbox_config_get_string_with_default(cfg_section, "vibrato_mode", "c3"); - if (vibrato_mode[0] == 'c') - m->vibrato_mix = 1; - else if (vibrato_mode[0] == 'v') - m->vibrato_mix = 0; - else - { - g_error("Unknown vibrato mode: %s (allowed: v1, v2, v3, c1, c2, c3)", vibrato_mode); - m->vibrato_mix = 0; - } - if (vibrato_mode[1] >= '1' && vibrato_mode[1] <= '3') - m->vibrato_mode = vibrato_mode[1] - '1'; - else - { - g_error("Unknown vibrato mode: %s (allowed: v1, v2, v3, c1, c2, c3)", vibrato_mode); - m->vibrato_mode = 2; - } - - for (i = 0; i < 18; i++) - { - biquad_init(&m->scanner_delay[i]); - } - for (i = 0; i < 91; i++) - { - float freq_hz = 440 * pow(2.0, (i - 45) / 12.0); - float scaling = freq_hz / 120.0; - if (scaling < 1) - scaling = 1; - if (scaling > 24) - scaling = 24 + ((scaling - 24) / 2.5); - m->frequency[i] = (uint32_t)(freq_hz * 65536 * 65536 / srate); - m->phase[i] = 0; - m->amp_scaling[i] = (int)(1024 * scaling); - } - m->upper_manual = 0; - m->lower_manual = 0; - m->pedalmasks = 0; - - return &m->module; -} - -struct cbox_module_keyrange_metadata tonewheel_organ_keyranges[] = { - { 0, 24, 35, "Pedal keyboard" }, - { 1, 36, 36 + 60, "Upper Manual" }, - { 2, 36, 36 + 60, "Lower Manual" }, -}; - -struct cbox_module_livecontroller_metadata tonewheel_organ_controllers[] = { - { 0, cmlc_onoffcc, 93, "Vib/Chr", NULL}, - - { 1, cmlc_continuouscc, 21, "Upper Drawbar 1", NULL}, - { 1, cmlc_continuouscc, 22, "Upper Drawbar 2", NULL}, - { 1, cmlc_continuouscc, 23, "Upper Drawbar 3", NULL}, - { 1, cmlc_continuouscc, 24, "Upper Drawbar 4", NULL}, - { 1, cmlc_continuouscc, 25, "Upper Drawbar 5", NULL}, - { 1, cmlc_continuouscc, 26, "Upper Drawbar 6", NULL}, - { 1, cmlc_continuouscc, 27, "Upper Drawbar 7", NULL}, - { 1, cmlc_continuouscc, 28, "Upper Drawbar 8", NULL}, - { 1, cmlc_continuouscc, 29, "Upper Drawbar 9", NULL}, - - { 2, cmlc_continuouscc, 21, "Lower Drawbar 1", NULL}, - { 2, cmlc_continuouscc, 22, "Lower Drawbar 2", NULL}, - { 2, cmlc_continuouscc, 23, "Lower Drawbar 3", NULL}, - { 2, cmlc_continuouscc, 24, "Lower Drawbar 4", NULL}, - { 2, cmlc_continuouscc, 25, "Lower Drawbar 5", NULL}, - { 2, cmlc_continuouscc, 26, "Lower Drawbar 6", NULL}, - { 2, cmlc_continuouscc, 27, "Lower Drawbar 7", NULL}, - { 2, cmlc_continuouscc, 28, "Lower Drawbar 8", NULL}, - { 2, cmlc_continuouscc, 29, "Lower Drawbar 9", NULL}, -}; - -DEFINE_MODULE(tonewheel_organ, 0, 2) - diff --git a/template/calfbox/track.c b/template/calfbox/track.c deleted file mode 100644 index d3f3150..0000000 --- a/template/calfbox/track.c +++ /dev/null @@ -1,279 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "errors.h" -#include "master.h" -#include "pattern.h" -#include "rt.h" -#include "seq.h" -#include "track.h" -#include "song.h" -#include -#include - -CBOX_CLASS_DEFINITION_ROOT(cbox_track) -CBOX_CLASS_DEFINITION_ROOT(cbox_track_item) - -static gboolean cbox_track_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error); -static gboolean cbox_track_item_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error); - -void cbox_track_item_destroyfunc(struct cbox_objhdr *hdr) -{ - struct cbox_track_item *item = CBOX_H2O(hdr); - item->owner->items = g_list_remove(item->owner->items, item); - free(item); -} - -struct cbox_track *cbox_track_new(struct cbox_document *document) -{ - struct cbox_track *p = malloc(sizeof(struct cbox_track)); - CBOX_OBJECT_HEADER_INIT(p, cbox_track, document); - - p->name = g_strdup("Unnamed"); - p->items = NULL; - p->pb = NULL; - p->owner = NULL; - p->external_output_set = FALSE; - p->generation = 0; - p->mute = FALSE; - - cbox_command_target_init(&p->cmd_target, cbox_track_process_cmd, p); - CBOX_OBJECT_REGISTER(p); - return p; -} - -#define CBTI(it) ((struct cbox_track_item *)(it)->data) - -void cbox_track_add_item_to_list(struct cbox_track *track, struct cbox_track_item *item) -{ - GList *it = track->items; - while(it != NULL && CBTI(it)->time < item->time) - it = g_list_next(it); - // all items earlier than the new one -> append - if (it == NULL) - { - track->items = g_list_append(track->items, item); - cbox_track_set_dirty(track); - return; - } - // Here, I don't really care about overlaps - it's more important to preserve - // all clips as sent by the caller. - track->items = g_list_insert_before(track->items, it, item); - cbox_track_set_dirty(track); -} - -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) -{ - struct cbox_track_item *item = malloc(sizeof(struct cbox_track_item)); - CBOX_OBJECT_HEADER_INIT(item, cbox_track_item, CBOX_GET_DOCUMENT(track)); - item->owner = track; - item->time = time; - item->pattern = pattern; - item->offset = offset; - item->length = length; - cbox_command_target_init(&item->cmd_target, cbox_track_item_process_cmd, item); - - cbox_track_add_item_to_list(track, item); - CBOX_OBJECT_REGISTER(item); - return item; -} - -void cbox_track_clear_clips(struct cbox_track *track) -{ - while(track->items) { - cbox_object_destroy(track->items->data); - } - cbox_track_set_dirty(track); -} - -void cbox_track_set_dirty(struct cbox_track *track) -{ - ++track->generation; -} - -void cbox_track_destroyfunc(struct cbox_objhdr *objhdr) -{ - struct cbox_track *track = CBOX_H2O(objhdr); - if (track->owner) - cbox_song_remove_track(track->owner, track); - // XXXKF I'm not sure if I want the lifecycle of track playback objects to be managed by the track itself - if (track->pb && track->pb->ref_count == 1) - cbox_track_playback_destroy(track->pb); - // The items will unlink themselves from the list in destructor - while(track->items) - cbox_object_destroy(track->items->data); - g_free((gchar *)track->name); - free(track); -} - -gboolean cbox_track_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct cbox_track *track = ct->user_data; - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - GList *it = track->items; - while(it != NULL) - { - struct cbox_track_item *trki = it->data; - if (!cbox_execute_on(fb, NULL, "/clip", "iiioo", error, trki->time, trki->offset, trki->length, trki->pattern, trki)) - return FALSE; - it = g_list_next(it); - } - - return cbox_execute_on(fb, NULL, "/mute", "i", error, track->mute) && - cbox_execute_on(fb, NULL, "/name", "s", error, track->name) && - (track->external_output_set ? cbox_uuid_report_as(&track->external_output, "/external_output", fb, error) : TRUE) && - CBOX_OBJECT_DEFAULT_STATUS(track, fb, error); - } - else if (!strcmp(cmd->command, "/clear_clips") && !strcmp(cmd->arg_types, "")) - { - cbox_track_clear_clips(track); - return TRUE; - } - else if (!strcmp(cmd->command, "/add_clip") && !strcmp(cmd->arg_types, "iiis")) - { - int pos = CBOX_ARG_I(cmd, 0); - int offset = CBOX_ARG_I(cmd, 1); - int length = CBOX_ARG_I(cmd, 2); - if (pos < 0) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid pattern position %d (cannot be negative)", pos); - return FALSE; - } - if (offset < 0) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid pattern offset %d (cannot be negative)", offset); - return FALSE; - } - if (length <= 0) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid pattern length %d (must be positive)", length); - return FALSE; - } - struct cbox_objhdr *pattern = CBOX_ARG_O(cmd, 3, track, cbox_midi_pattern, error); - if (!pattern) - return FALSE; - struct cbox_midi_pattern *mp = CBOX_H2O(pattern); - struct cbox_track_item *trki = cbox_track_add_item(track, pos, mp, offset, length); - if (fb) - return cbox_execute_on(fb, NULL, "/uuid", "o", error, trki); - cbox_track_set_dirty(track); - return TRUE; - } - else if (!strcmp(cmd->command, "/name") && !strcmp(cmd->arg_types, "s")) - { - char *old_name = track->name; - track->name = g_strdup(CBOX_ARG_S(cmd, 0)); - g_free(old_name); - cbox_track_set_dirty(track); - return TRUE; - } - else if (!strcmp(cmd->command, "/external_output") && !strcmp(cmd->arg_types, "s")) - { - if (*CBOX_ARG_S(cmd, 0)) - { - if (cbox_uuid_fromstring(&track->external_output, CBOX_ARG_S(cmd, 0), error)) { - track->external_output_set = TRUE; - cbox_track_set_dirty(track); - } - } - else { - track->external_output_set = FALSE; - cbox_track_set_dirty(track); - } - return TRUE; - } - else if (!strcmp(cmd->command, "/mute") && !strcmp(cmd->arg_types, "i")) - { - if (CBOX_ARG_I(cmd, 0) == (int)track->mute) // no-op - return TRUE; - track->mute = CBOX_ARG_I(cmd, 0) != 0; - cbox_track_set_dirty(track); - return TRUE; - } - else - return cbox_object_default_process_cmd(ct, fb, cmd, error); -} - -gboolean cbox_track_item_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct cbox_track_item *trki = ct->user_data; - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - return cbox_execute_on(fb, NULL, "/pos", "i", error, trki->time) && - cbox_execute_on(fb, NULL, "/offset", "i", error, trki->offset) && - cbox_execute_on(fb, NULL, "/length", "i", error, trki->length) && - cbox_execute_on(fb, NULL, "/pattern", "o", error, trki->pattern) && - CBOX_OBJECT_DEFAULT_STATUS(trki, fb, error); - } - if (!strcmp(cmd->command, "/delete") && !strcmp(cmd->arg_types, "")) - { - cbox_track_set_dirty(trki->owner); - cbox_object_destroy(CBOX_O2H(trki)); - return TRUE; - } - if (!strcmp(cmd->command, "/pattern") && !strcmp(cmd->arg_types, "s")) - { - struct cbox_objhdr *pattern = CBOX_ARG_O(cmd, 0, trki->owner, cbox_midi_pattern, error); - if (!pattern) - return FALSE; - if (trki->pattern == CBOX_H2O(pattern)) // no-op - return TRUE; - trki->pattern = CBOX_H2O(pattern); - cbox_track_item_set_dirty(trki); - return TRUE; - } - if (!strcmp(cmd->command, "/length") && !strcmp(cmd->arg_types, "i")) - { - if (CBOX_ARG_I(cmd, 0) == (int)trki->length) // no-op - return TRUE; - trki->length = CBOX_ARG_I(cmd, 0); - cbox_track_item_set_dirty(trki); - return TRUE; - } - if (!strcmp(cmd->command, "/pos") && !strcmp(cmd->arg_types, "i")) - { - if (CBOX_ARG_I(cmd, 0) == (int)trki->time) // no-op - return TRUE; - trki->owner->items = g_list_remove(trki->owner->items, trki); - trki->time = CBOX_ARG_I(cmd, 0); - cbox_track_add_item_to_list(trki->owner, trki); - cbox_track_item_set_dirty(trki); - return TRUE; - } - if (!strcmp(cmd->command, "/offset") && !strcmp(cmd->arg_types, "i")) - { - if (CBOX_ARG_I(cmd, 0) == (int)trki->offset) // no-op - return TRUE; - cbox_track_item_set_dirty(trki); - trki->offset = CBOX_ARG_I(cmd, 0); - return TRUE; - } - return cbox_object_default_process_cmd(ct, fb, cmd, error); -} - -extern void cbox_track_item_set_dirty(struct cbox_track_item *track_item) -{ - cbox_track_set_dirty(track_item->owner); -} diff --git a/template/calfbox/track.h b/template/calfbox/track.h deleted file mode 100644 index 8147084..0000000 --- a/template/calfbox/track.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_TRACK_H -#define CBOX_TRACK_H - -#include "dom.h" - -CBOX_EXTERN_CLASS(cbox_track_item) -CBOX_EXTERN_CLASS(cbox_track) - -struct cbox_midi_pattern; -struct cbox_track; - -struct cbox_track_item -{ - CBOX_OBJECT_HEADER() - struct cbox_command_target cmd_target; - struct cbox_track *owner; - uint32_t time; - struct cbox_midi_pattern *pattern; - uint32_t offset; - uint32_t length; -}; - -struct cbox_track -{ - CBOX_OBJECT_HEADER() - struct cbox_command_target cmd_target; - gchar *name; - gboolean external_output_set; - struct cbox_uuid external_output; - GList *items; - struct cbox_song *owner; - struct cbox_track_playback *pb; - uint32_t generation; - gboolean mute; -}; - -extern struct cbox_track *cbox_track_new(struct cbox_document *document); -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); -extern void cbox_track_update_playback(struct cbox_track *track, struct cbox_master *master); -extern void cbox_track_clear_clips(struct cbox_track *track); -extern void cbox_track_set_dirty(struct cbox_track *track); -extern void cbox_track_item_set_dirty(struct cbox_track_item *track_item); - -#endif diff --git a/template/calfbox/ui.c b/template/calfbox/ui.c deleted file mode 100644 index 62abc0d..0000000 --- a/template/calfbox/ui.c +++ /dev/null @@ -1,65 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "ui.h" - -#if USE_NCURSES - -#include -#include -#include - -void cbox_ui_start() -{ - initscr(); - cbreak(); - noecho(); - start_color(); - keypad(stdscr, TRUE); -} - -int cbox_ui_run(struct cbox_ui_page *page) -{ - int ch, res; - if (page->draw) - page->draw(page); - if (page->on_idle) - halfdelay(1); - while(1) - { - ch = getch(); - if (ch == ERR && page->on_idle) - { - res = page->on_idle(page); - if (res != 0) - return res; - continue; - } - res = page->on_key(page, ch); - if (res != 0) - return res; - } - cbreak(); -} - -void cbox_ui_stop() -{ - endwin(); -} - -#endif \ No newline at end of file diff --git a/template/calfbox/ui.h b/template/calfbox/ui.h deleted file mode 100644 index 1f977b9..0000000 --- a/template/calfbox/ui.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_UI_H -#define CBOX_UI_H - -#include "config.h" - -#if USE_NCURSES - -struct cbox_ui_page -{ - void *user_data; - void (*draw)(struct cbox_ui_page *page); - int (*on_key)(struct cbox_ui_page *page, int ch); - int (*on_idle)(struct cbox_ui_page *page); -}; - -extern void cbox_ui_start(void); -extern int cbox_ui_run(struct cbox_ui_page *page); -extern void cbox_ui_stop(void); - -#endif - -#endif diff --git a/template/calfbox/usb_api_example.py b/template/calfbox/usb_api_example.py deleted file mode 100644 index 15e518e..0000000 --- a/template/calfbox/usb_api_example.py +++ /dev/null @@ -1,61 +0,0 @@ -from calfbox import cbox - -def cmd_dumper(cmd, fb, args): - print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) - -cbox.init_engine() - -cbox.Config.add_section("drumpattern:pat1", """ -title=Straight - Verse -beats=4 -track1=bd -track2=sd -track3=hh -track4=ho -bd_note=c1 -sd_note=d1 -hh_note=f#1 -ho_note=a#1 -bd_trigger=9... .... 9.6. .... -sd_trigger=.... 9..5 .2.. 9... -hh_trigger=9353 7353 7353 73.3 -ho_trigger=.... .... .... ..3. -""") - -cbox.Config.set("io", "use_usb", 1) -cbox.start_audio(cmd_dumper) - -global Document -Document = cbox.Document - -status = cbox.JackIO.status() -client_name = status.client_name -print ("Client type: %s" % status.client_type) -print ("Client name: %s" % client_name) -print ("Audio inputs: %d, outputs: %d" % (status.audio_inputs, status.audio_outputs)) -print ("Period: %d frames" % (status.buffer_size)) -print ("Sample rate: %d frames/sec" % (status.sample_rate)) -print ("Output resolution: %d bits/sample" % (status.output_resolution)) -print ("MIDI input devices: %s" % (status.midi_input)) -#cbox.JackIO.create_midi_output('drums', 'system:midi_playback_1') - -scene = Document.get_scene() -scene.clear() -instrument = scene.add_new_instrument_layer("test_sampler", "sampler").get_instrument() -pgm_no = instrument.engine.get_unused_program() -pgm = instrument.engine.load_patch_from_file(pgm_no, 'synthbass.sfz', 'SynthBass') -instrument.engine.set_patch(1, pgm_no) -instrument.engine.set_patch(10, pgm_no) - -song = Document.get_song() -track = song.add_track() -pattern = song.load_drum_pattern("pat1") -track.add_clip(0, 0, pattern.status().loop_end, pattern) -song.set_loop(0, pattern.status().loop_end) -song.update_playback() -cbox.Transport.play() - -print("Ready!") - -while True: - cbox.call_on_idle(cmd_dumper) diff --git a/template/calfbox/usbaudio.c b/template/calfbox/usbaudio.c deleted file mode 100644 index deffaa0..0000000 --- a/template/calfbox/usbaudio.c +++ /dev/null @@ -1,679 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config.h" - -#if USE_LIBUSB - -#include -#include -#include "usbio_impl.h" - -#include -#include -#include - -#define NUM_MULTIMIX_SYNC_PACKETS 10 - -#define MULTIMIX_EP_PLAYBACK 0x02 -//#define MULTIMIX_EP_CAPTURE 0x86 -#define MULTIMIX_EP_SYNC 0x81 - -#define NUM_CPUTIME_ENTRIES 100 - -static int register_cpu_time = 1; -static float real_time_registry[NUM_CPUTIME_ENTRIES]; -static float cpu_time_registry[NUM_CPUTIME_ENTRIES]; -static int cpu_time_write_ptr = 0; -static void usbio_play_buffer_adaptive(struct cbox_usb_io_impl *uii); - -/////////////////////////////////////////////////////////////////////////////// - -static gboolean set_endpoint_sample_rate(struct libusb_device_handle *h, int sample_rate, int ep) -{ - uint8_t freq_data[3]; - freq_data[0] = sample_rate & 0xFF; - freq_data[1] = (sample_rate & 0xFF00) >> 8; - freq_data[2] = (sample_rate & 0xFF0000) >> 16; - if (libusb_control_transfer(h, 0x22, 0x01, 256, ep, freq_data, 3, USB_DEVICE_SETUP_TIMEOUT) != 3) - return FALSE; - return TRUE; -} - -/////////////////////////////////////////////////////////////////////////////// - -gboolean usbio_open_audio_interface(struct cbox_usb_io_impl *uii, struct cbox_usb_audio_info *uainf, struct libusb_device_handle *handle, GError **error) -{ - if (uii->output_resolution != 2 && uii->output_resolution != 3) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Only 16-bit or 24-bit output resolution is supported."); - return FALSE; - } - if (!configure_usb_interface(handle, uainf->udi->bus, uainf->udi->devadr, uainf->intf, uainf->alt_setting, "audio (class driver)", error)) - return FALSE; - if (!set_endpoint_sample_rate(handle, uii->sample_rate, uainf->epdesc.bEndpointAddress)) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot set sample rate on class-compliant USB audio device."); - return FALSE; - } - uii->is_hispeed = FALSE; - uii->sync_protocol = uainf->sync_protocol; - uii->play_function = uainf->sync_protocol == USBAUDIOSYNC_PROTOCOL_CLASS ? usbio_play_buffer_asynchronous : usbio_play_buffer_adaptive; - uii->handle_audiodev = handle; - uii->audio_output_endpoint = uainf->epdesc.bEndpointAddress; - uii->audio_output_pktsize = uainf->epdesc.wMaxPacketSize; // 48 * 2 * uii->output_resolution; - uii->audio_sync_endpoint = uainf->sync_protocol == USBAUDIOSYNC_PROTOCOL_CLASS ? uainf->sync_epdesc.bEndpointAddress : 0; - return TRUE; -} - -/////////////////////////////////////////////////////////////////////////////// - -static gboolean claim_multimix_interfaces(struct cbox_usb_io_impl *uii, struct libusb_device_handle *handle, int bus, int devadr, GError **error) -{ - for (int ifno = 0; ifno < 2; ifno++) - { - if (!configure_usb_interface(handle, bus, devadr, ifno, 1, "audio (MultiMix driver)", error)) - return FALSE; - } - return TRUE; -} - -gboolean usbio_open_audio_interface_multimix(struct cbox_usb_io_impl *uii, int bus, int devadr, struct libusb_device_handle *handle, GError **error) -{ - if (uii->output_resolution != 3) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Only 24-bit output resolution is supported."); - return FALSE; - } - if (!claim_multimix_interfaces(uii, handle, bus, devadr, error)) - return FALSE; - if (!set_endpoint_sample_rate(handle, uii->sample_rate, MULTIMIX_EP_PLAYBACK)) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot set sample rate on Alesis Multimix."); - return FALSE; - } - - uii->is_hispeed = TRUE; - uii->play_function = usbio_play_buffer_asynchronous; - uii->handle_audiodev = handle; - uii->sync_protocol = USBAUDIOSYNC_PROTOCOL_MULTIMIX8; - uii->audio_output_endpoint = MULTIMIX_EP_PLAYBACK; - uii->audio_output_pktsize = 156; - uii->audio_sync_endpoint = MULTIMIX_EP_SYNC; - return TRUE; -} - -/////////////////////////////////////////////////////////////////////////////// - -static void calc_output_buffer(struct cbox_usb_io_impl *uii) -{ - struct timespec tvs1, tve1, tvs2, tve2; - if (register_cpu_time) - { - clock_gettime(CLOCK_MONOTONIC_RAW, &tvs1); - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tvs2); - } - struct cbox_io *io = uii->ioi.pio; - uint32_t buffer_size = io->io_env.buffer_size; - for (int b = 0; b < uii->output_channels; b++) - memset(io->output_buffers[b], 0, buffer_size * sizeof(float)); - for (GList *p = uii->rt_midi_ports; p; p = p->next) - { - struct cbox_usb_midi_interface *umi = p->data; - if (umi->input_port->hdr.enable_appsink && umi->input_port && umi->input_port->hdr.buffer.count) - cbox_midi_appsink_supply(&umi->input_port->hdr.appsink, &umi->input_port->hdr.buffer, io->free_running_frame_counter); - } - io->cb->process(io->cb->user_data, io, buffer_size); - for (GList *p = uii->rt_midi_ports; p; p = p->next) - { - struct cbox_usb_midi_interface *umi = p->data; - if (umi->input_port) - cbox_midi_buffer_clear(&umi->input_port->hdr.buffer); - } - for (GSList *p = io->midi_outputs; p; p = p->next) - { - struct cbox_usb_midi_output *umo = p->data; - usbio_fill_midi_output_buffer(umo); - } - if (register_cpu_time) - { - clock_gettime(CLOCK_MONOTONIC_RAW, &tve1); - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tve2); - float time1 = tve1.tv_sec - tvs1.tv_sec + (tve1.tv_nsec - tvs1.tv_nsec) / 1000000000.0; - float time2 = tve2.tv_sec - tvs2.tv_sec + (tve2.tv_nsec - tvs2.tv_nsec) / 1000000000.0; - real_time_registry[cpu_time_write_ptr] = time1; - cpu_time_registry[cpu_time_write_ptr] = time2; - cpu_time_write_ptr = (cpu_time_write_ptr + 1) % NUM_CPUTIME_ENTRIES; - if (time1 > 0.0008 || time2 > 0.0008) - g_warning("CPU time = %f ms, real time = %f ms", time2 * 1000, time1 * 1000); - } - io->free_running_frame_counter += buffer_size; -} - -static void fill_playback_buffer(struct cbox_usb_io_impl *uii, struct libusb_transfer *transfer) -{ - struct cbox_io *io = uii->ioi.pio; - uint32_t buffer_size = io->io_env.buffer_size; - uint8_t *data8 = (uint8_t*)transfer->buffer; - int16_t *data = (int16_t*)transfer->buffer; - int resolution = uii->output_resolution; - unsigned int oc = uii->output_channels; - uint32_t rptr = uii->read_ptr; - uint32_t nframes = transfer->length / (resolution * oc); - uint32_t i, b, j; - - for (i = 0; i < nframes; ) - { - if (rptr == buffer_size) - { - calc_output_buffer(uii); - rptr = 0; - } - unsigned int left1 = nframes - i; - unsigned int left2 = buffer_size - rptr; - if (left1 > left2) - left1 = left2; - - for (b = 0; b < oc; b++) - { - float *obuf = io->output_buffers[b] + rptr; - if (resolution == 2) - { - int16_t *tbuf = data + oc * i + b; - for (j = 0; j < left1; j++) - { - float v = 32767 * obuf[j]; - if (v < -32768) - v = -32768; - if (v > +32767) - v = +32767; - *tbuf = (int16_t)v; - tbuf += oc; - } - } - if (resolution == 3) - { - uint8_t *tbuf = data8 + (oc * i + b) * 3; - for (j = 0; j < left1; j++) - { - float v = 0x7FFFFF * obuf[j]; - if (v < -0x800000) - v = -0x800000; - if (v > +0x7FFFFF) - v = +0x7FFFFF; - int vi = (int)v; - tbuf[0] = vi & 255; - tbuf[1] = (vi >> 8) & 255; - tbuf[2] = (vi >> 16) & 255; - tbuf += oc * 3; - } - } - } - i += left1; - rptr += left1; - } - uii->read_ptr = rptr; -} - -static void play_callback_adaptive(struct libusb_transfer *transfer) -{ - struct usbio_transfer *xf = transfer->user_data; - struct cbox_usb_io_impl *uii = xf->user_data; - xf->pending = FALSE; - if (transfer->status == LIBUSB_TRANSFER_CANCELLED) - { - xf->cancel_confirm = 1; - return; - } - if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) - { - uii->device_removed++; - return; - } - - int resolution = uii->output_resolution; - int oc = uii->output_channels; - gboolean init_finished = uii->playback_counter == uii->playback_buffers; - if (uii->playback_counter < uii->playback_buffers) - { - // send another URB for the next transfer before re-submitting - // this one - usbio_play_buffer_adaptive(uii); - } - // printf("Play Callback! %d %p status %d\n", (int)transfer->length, transfer->buffer, (int)transfer->status); - - int tlen = 0, olen = 0; - for (int i = 0; i < transfer->num_iso_packets; i++) - { - tlen += transfer->iso_packet_desc[i].actual_length; - olen += transfer->iso_packet_desc[i].length; - if (transfer->iso_packet_desc[i].status) - printf("ISO error: index = %d i = %d status = %d\n", (int)xf->index, i, transfer->iso_packet_desc[i].status); - } - uii->samples_played += olen / (oc * resolution); - uint32_t nsamps = uii->sample_rate / 1000; - // If time elapsed is greater than - int lag = uii->desync / (1000 * transfer->num_iso_packets); - if (lag > 0 && nsamps < uii->audio_output_pktsize) - { - nsamps++; - lag--; - } - - transfer->length = nsamps * transfer->num_iso_packets * oc * resolution; - libusb_set_iso_packet_lengths(transfer, nsamps * oc * resolution); - - if (init_finished) - { - fill_playback_buffer(uii, transfer); - } - // desync value is expressed in milli-frames, i.e. desync of 1000 means 1 frame of lag - // It takes 1ms for each iso packet to be transmitted. Each transfer consists of - // num_iso_packets packets. So, this transfer took uii->sample_rate milli-frames. - uii->desync += transfer->num_iso_packets * uii->sample_rate; - // ... but during that time, tlen/4 samples == tlen/4*1000 millisamples have been - // transmitted. - uii->desync -= transfer->num_iso_packets * nsamps * 1000; - - if (uii->no_resubmit) - return; - int err = usbio_transfer_submit(xf); - if (err) - { - if (err == LIBUSB_ERROR_NO_DEVICE) - { - uii->device_removed++; - transfer->status = LIBUSB_TRANSFER_NO_DEVICE; - } - g_warning("Cannot resubmit isochronous transfer, error = %s", libusb_error_name(err)); - } -} - -void usbio_play_buffer_adaptive(struct cbox_usb_io_impl *uii) -{ - struct usbio_transfer *t; - int err; - int packets = uii->iso_packets; - t = usbio_transfer_new(uii->usbctx, "play", uii->playback_counter, packets, uii); - int tsize = uii->sample_rate * 2 * uii->output_resolution / 1000; - uint8_t *buf = (uint8_t *)calloc(packets, uii->audio_output_pktsize); - - libusb_fill_iso_transfer(t->transfer, uii->handle_audiodev, uii->audio_output_endpoint, buf, tsize * packets, packets, play_callback_adaptive, t, 20000); - libusb_set_iso_packet_lengths(t->transfer, tsize); - uii->playback_transfers[uii->playback_counter++] = t; - - err = usbio_transfer_submit(t); - if (!err) - return; - g_warning("Cannot resubmit isochronous transfer, error = %s", libusb_error_name(err)); - uii->playback_counter--; - free(buf); - usbio_transfer_destroy(t); - uii->playback_transfers[uii->playback_counter] = NULL; -} - -////////////////////////////////////////////////////////////////////////////////////////// - -static int calc_packet_lengths(struct cbox_usb_io_impl *uii, struct libusb_transfer *t, int packets) -{ - int packets_per_sec = uii->is_hispeed ? 8000 : 1000; - int tsize = 0; - int i; - // printf("sync_freq = %d\n", sync_freq); - for (i = 0; i < packets; i++) - { - int nsamps = (uii->samples_per_sec - uii->desync) / packets_per_sec; - // assert(nsamps > 0); - if ((uii->samples_per_sec - uii->desync) % packets_per_sec) - nsamps++; - //printf("%d sfreq=%d desync=%d nsamps=%d\n", i, uii->sync_freq, uii->desync, nsamps); - - uii->desync = (uii->desync + nsamps * packets_per_sec) % uii->samples_per_sec; - int v = (nsamps) * 2 * uii->output_resolution; - t->iso_packet_desc[i].length = v; - tsize += v; - } - return tsize; -} - -void play_callback_asynchronous(struct libusb_transfer *transfer) -{ - struct usbio_transfer *xf = transfer->user_data; - struct cbox_usb_io_impl *uii = xf->user_data; - xf->pending = FALSE; - - if (transfer->status == LIBUSB_TRANSFER_CANCELLED) - { - xf->cancel_confirm = TRUE; - return; - } - if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) - { - xf->cancel_confirm = TRUE; - uii->device_removed++; - return; - } - - gboolean init_finished = uii->playback_counter == uii->playback_buffers; - if (uii->playback_counter < uii->playback_buffers) - { - // send another URB for the next transfer before re-submitting - // this one - usbio_play_buffer_asynchronous(uii); - } - - /* - printf("Play Callback! %d status %d\n", (int)transfer->length, (int)transfer->status); - for (i = 0; i < transfer->num_iso_packets; i++) { - if (transfer->iso_packet_desc[i].actual_length) - { - printf("%d: %d %d\n", i, transfer->iso_packet_desc[i].actual_length, transfer->iso_packet_desc[i].status); - } - } - */ - transfer->length = calc_packet_lengths(uii, transfer, transfer->num_iso_packets); - if (init_finished) - { - fill_playback_buffer(uii, transfer); - } - if (uii->no_resubmit) - return; - int err = usbio_transfer_submit(xf); - if (err) - { - if (err == LIBUSB_ERROR_NO_DEVICE) - { - transfer->status = LIBUSB_TRANSFER_NO_DEVICE; - uii->device_removed++; - } - g_warning("Cannot submit isochronous transfer, error = %s", libusb_error_name(err)); - } -} - -static struct usbio_transfer *sync_stuff_asynchronous(struct cbox_usb_io_impl *uii, int index); - -void usbio_play_buffer_asynchronous(struct cbox_usb_io_impl *uii) -{ - struct usbio_transfer *t; - int err; - int packets = uii->iso_packets_multimix; - t = usbio_transfer_new(uii->usbctx, "play", uii->playback_counter, packets, uii); - int tsize = calc_packet_lengths(uii, t->transfer, packets); - int bufsize = uii->audio_output_pktsize * packets; - uint8_t *buf = (uint8_t *)calloc(1, bufsize); - - if (!uii->playback_counter) - { - for(uii->sync_counter = 0; uii->sync_counter < uii->sync_buffers; uii->sync_counter++) - uii->sync_transfers[uii->sync_counter] = sync_stuff_asynchronous(uii, uii->sync_counter); - } - - libusb_fill_iso_transfer(t->transfer, uii->handle_audiodev, uii->audio_output_endpoint, buf, tsize, packets, play_callback_asynchronous, t, 20000); - uii->playback_transfers[uii->playback_counter++] = t; - err = usbio_transfer_submit(t); - if (err) - { - g_warning("Cannot submit playback urb: %s, error = %s (index = %d, tsize = %d)", libusb_error_name(err), strerror(errno), uii->playback_counter, tsize); - free(buf); - usbio_transfer_destroy(t); - uii->playback_transfers[--uii->playback_counter] = NULL; - } -} - -////////////////////////////////////////////////////////////////////////////////////////// - -/* - * The Multimix device controls the data rate of the playback stream using a - * device-to-host isochronous endpoint. The incoming packets consist of 3 bytes: - * a current value of sample rate (kHz) + 2 historical values. I'm only using - * the first byte, I haven't yet encountered a situation where using the - * second and third byte would be necessary. This seems to work for all - * sample rates supported by the Windows driver - 44100, 48000, 88200 and - * 96000. It is possible to set sample rate to 64000, but it doesn't work - * correctly, and isn't supported by the Windows driver either - it may - * require special handling or may be a half-implemented feature in hardware. - * The isochronous transfer using 10 packets seems to give acceptable resolution - * and latency to avoid over/underruns with supported sample rates. - * - * The non-integer multiples of 1 kHz (like 44.1) are passed as a sequence of - * values that average to a desired value (9 values of 44 and one value of 45). - * - * In order to compensate for clock rate difference - * between host clock and DAC clock, the sample rate values sent by the device - * are either larger (to increase data rate from the host) or smaller than - * the nominal frequency value - the driver uses that to adjust the sample frame - * counts of individual packets in an isochronous transfer. - * - * A similar mechanism is used with USB audio class asynchronous sinks: the - * device sends a 10.14 representation of number of audio samples (frames) - * per USB frame, and this is used to control packet sizes. - */ - -static void use_audioclass_sync_feedback(const uint8_t *data, uint32_t actual_length, int *prate) -{ - // 10.14 representation of 'samples' per ms value, see 5.12.4.2 of the USB spec - uint32_t value = data[0] + 256 * data[1] + 65536 * data[2]; - // printf("Ff estimate = %f Hz\n", value * 1000 / 16384.0); - // only 1 packet - *prate = (1000 * value + 8192) >> 14; -} - -static inline void use_multimix_sync_feedback(const uint8_t *data, uint32_t actual_length, int *prate, const uint8_t *prev_data) -{ - // Those assertions never failed, so my understanding of the protocol was likely - // correct. They should probably be removed just to not crash the program when - // used with flaky devices. - assert(actual_length == 3); - // this is averaged across all packets in the transfer - *prate += 1000 * data[0]; - if (prev_data) - assert(data[1] == prev_data[0]); -} - -static void sync_callback(struct libusb_transfer *transfer) -{ - struct usbio_transfer *xf = transfer->user_data; - struct cbox_usb_io_impl *uii = xf->user_data; - uint8_t *data = transfer->buffer; - int i, ofs, pkts; - int rate_est = 0; - xf->pending = FALSE; - - if (transfer->status == LIBUSB_TRANSFER_CANCELLED) - { - xf->cancel_confirm = 1; - return; - } - if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) - { - xf->cancel_confirm = 1; - return; - } - // XXXKF handle device disconnected error - - if (uii->debug_sync) - printf("Sync callback! %p %d packets:", transfer, transfer->num_iso_packets); - ofs = 0; - pkts = 0; - rate_est = 0; - const uint8_t *prev_data = NULL; - for (i = 0; i < transfer->num_iso_packets; i++) { - if (transfer->iso_packet_desc[i].status) - { - printf("[%02d: actual length is %4d, status is %2d] ", i, transfer->iso_packet_desc[i].actual_length, transfer->iso_packet_desc[i].status); - continue; - } - else if (transfer->iso_packet_desc[i].actual_length) - { - if (uii->sync_protocol == USBAUDIOSYNC_PROTOCOL_MULTIMIX8) - use_multimix_sync_feedback(&data[ofs], transfer->iso_packet_desc[i].actual_length, &rate_est, prev_data); - else - use_audioclass_sync_feedback(&data[ofs], transfer->iso_packet_desc[i].actual_length, &rate_est); - prev_data = &data[ofs]; - //printf("%d\n", (int)data[ofs]); - if (uii->debug_sync) - printf("%3d ", (int)data[ofs]); - pkts++; - } - else - if (uii->debug_sync) - printf("? "); - ofs += transfer->iso_packet_desc[i].length; - } - if (uii->debug_sync) - printf(" (%d of %d)", pkts, transfer->num_iso_packets); - if (pkts == transfer->num_iso_packets) - { - // Divide by the number of packets to compute the mean rate (in case - // of Multimix we don't have a proper fractional value, just an integer, - // so averaging is required to get more accuracy). - if (rate_est) - uii->samples_per_sec = rate_est / pkts; - if (uii->debug_sync) - printf("rate_est = %d sync_freq = %d\n", rate_est, uii->samples_per_sec); - } - if (uii->no_resubmit) - return; - int err = usbio_transfer_submit(xf); - if (err) - { - if (err == LIBUSB_ERROR_NO_DEVICE) - { - return; - } - } - if (uii->debug_sync) - printf("\n"); -} - -struct usbio_transfer *sync_stuff_asynchronous(struct cbox_usb_io_impl *uii, int index) -{ - struct usbio_transfer *t; - int err; - int syncbufsize = uii->sync_protocol == USBAUDIOSYNC_PROTOCOL_MULTIMIX8 ? 64 : 3; - int syncbufcount = uii->sync_protocol == USBAUDIOSYNC_PROTOCOL_MULTIMIX8 ? NUM_MULTIMIX_SYNC_PACKETS : 1; - t = usbio_transfer_new(uii->usbctx, "sync", index, syncbufcount, uii); - uint8_t *sync_buf = (uint8_t *)calloc(syncbufcount, syncbufsize); - libusb_fill_iso_transfer(t->transfer, uii->handle_audiodev, uii->audio_sync_endpoint, sync_buf, syncbufsize * syncbufcount, syncbufcount, sync_callback, t, 20000); - libusb_set_iso_packet_lengths(t->transfer, syncbufsize); - - err = libusb_submit_transfer(t->transfer); - if (err) - { - g_warning("Cannot submit sync urb: %s", libusb_error_name(err)); - free(sync_buf); - usbio_transfer_destroy(t); - return NULL; - } - return t; -} - -/////////////////////////////////////////////////////////////////////////// - -void cbox_usb_audio_info_init(struct cbox_usb_audio_info *uai, struct cbox_usb_device_info *udi) -{ - uai->udi = udi; - uai->intf = -1; - uai->alt_setting = -1; - uai->epdesc.found = FALSE; - uai->sync_protocol = USBAUDIOSYNC_PROTOCOL_NONE; - uai->sync_epdesc.found = FALSE; -} - -void usbio_start_audio_playback(struct cbox_usb_io_impl *uii) -{ - uii->desync = 0; - uii->samples_played = 0; - uii->read_ptr = uii->ioi.pio->io_env.buffer_size; - - uii->playback_transfers = malloc(sizeof(struct libusb_transfer *) * uii->playback_buffers); - uii->sync_transfers = malloc(sizeof(struct libusb_transfer *) * uii->sync_buffers); - - uii->playback_counter = 0; - uii->device_removed = 0; - uii->samples_per_sec = uii->sample_rate; - uii->play_function(uii); - uii->setup_error = uii->playback_counter == 0; - - if (!uii->setup_error) - { - while(uii->playback_counter < uii->playback_buffers && !uii->device_removed) - libusb_handle_events(uii->usbctx); - } -} - -void usbio_stop_audio_playback(struct cbox_usb_io_impl *uii) -{ - if (uii->device_removed) - { - // Wait until all the transfers pending are finished - while(uii->device_removed < uii->playback_counter) - libusb_handle_events(uii->usbctx); - } - if (uii->device_removed || uii->setup_error) - { - // Run the DSP code and send output to bit bucket until engine is stopped. - // This ensures that the command queue will still be processed. - // Otherwise the GUI thread would hang waiting for the command from - // the queue to be completed. - g_message("USB Audio output device has been disconnected - switching to null output."); - usbio_run_idle_loop(uii); - } - else - { - // Cancel all transfers pending, and wait until they get cancelled - for (unsigned int i = 0; i < uii->playback_counter; i++) - { - if (uii->playback_transfers[i]) - usbio_transfer_shutdown(uii->playback_transfers[i]); - } - } - - // Free the transfers for the buffers allocated so far. In case of setup - // failure, some buffers transfers might not have been created yet. - for (unsigned int i = 0; i < uii->playback_counter; i++) - { - if (uii->playback_transfers[i]) - { - free(uii->playback_transfers[i]->transfer->buffer); - usbio_transfer_destroy(uii->playback_transfers[i]); - uii->playback_transfers[i] = NULL; - } - } - if (uii->playback_counter && uii->audio_sync_endpoint) - { - for (unsigned int i = 0; i < uii->sync_counter; i++) - { - if (uii->sync_transfers[i]) - usbio_transfer_shutdown(uii->sync_transfers[i]); - } - for (unsigned int i = 0; i < uii->sync_counter; i++) - { - if (uii->sync_transfers[i]) - { - free(uii->sync_transfers[i]->transfer->buffer); - usbio_transfer_destroy(uii->sync_transfers[i]); - uii->sync_transfers[i] = NULL; - } - } - } - free(uii->playback_transfers); - free(uii->sync_transfers); -} - -#endif diff --git a/template/calfbox/usbio.c b/template/calfbox/usbio.c deleted file mode 100644 index ee7c13f..0000000 --- a/template/calfbox/usbio.c +++ /dev/null @@ -1,502 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -/* - -Note: this is a silly experimental driver for a number of USB MIDI devices. - -It only supports audio output and MIDI input, as those are my immediate -needs, to be able to use a machine running CalfBox as a standalone MIDI -instrument. Plug-and-play is supported, as long as current user running -calfbox has write access to the USB devices involved. This can be done by -running calfbox as root, or by setting right permissions in udev scripts -- this may be considered a safer method. - -Devices supported: -* Class-compliant audio output devices (tested with Lexicon Omega and some - cheap no-brand C-Media USB soundcard dongle) -* Alesis Multimix 8 USB 2.0 (audio output only) -* Class-compliant MIDI input devices (tested with several devices) - -Yes, code quality is pretty awful, especially in areas involving clock -sync. I'm going to clean it up iteratively later. - -*/ - -#include "config.h" - -#if USE_LIBUSB - -#include "app.h" -#include "config-api.h" -#include "errors.h" -#include "hwcfg.h" -#include "io.h" -#include "meter.h" -#include "midi.h" -#include "recsrc.h" -#include "usbio_impl.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/////////////////////////////////////////////////////////////////////////////// - -int cbox_usbio_get_sample_rate(struct cbox_io_impl *impl) -{ - struct cbox_usb_io_impl *uii = (struct cbox_usb_io_impl *)impl; - - return uii->sample_rate; -} - -gboolean cbox_usbio_get_status(struct cbox_io_impl *impl, GError **error) -{ - // XXXKF: needs a flag that would indicate whether device is present - // XXXKF: needs to return that flag with appropriate message - return TRUE; -} - -static void run_audio_loop(struct cbox_usb_io_impl *uii) -{ - while(!uii->stop_engine && !uii->device_removed) { - struct cbox_io *io = uii->ioi.pio; - struct timeval tv = { - .tv_sec = 0, - .tv_usec = 1000 - }; - libusb_handle_events_timeout(uii->usbctx, &tv); - for (GSList *p = io->midi_outputs; p; p = p->next) - { - struct cbox_usb_midi_output *umo = p->data; - usbio_send_midi_to_output(umo); - } - } -} - -void usbio_run_idle_loop(struct cbox_usb_io_impl *uii) -{ - while(!uii->stop_engine) - { - struct cbox_io *io = uii->ioi.pio; - for (int b = 0; b < uii->output_channels; b++) - memset(io->output_buffers[b], 0, io->io_env.buffer_size * sizeof(float)); - io->cb->process(io->cb->user_data, io, io->io_env.buffer_size); - for (GList *p = uii->rt_midi_ports; p; p = p->next) - { - struct cbox_usb_midi_interface *umi = p->data; - cbox_midi_buffer_clear(&umi->input_port->hdr.buffer); - } - for (GSList *p = io->midi_outputs; p; p = p->next) - { - struct cbox_usb_midi_output *umo = p->data; - usbio_send_midi_to_output(umo); - } - - struct timeval tv = { - .tv_sec = 0, - .tv_usec = 1 - }; - libusb_handle_events_timeout(uii->usbctx, &tv); - usleep((int)(io->io_env.buffer_size * 1000000.0 / uii->sample_rate)); - } -} - -static void *engine_thread(void *user_data) -{ - struct cbox_usb_io_impl *uii = user_data; - - usbio_start_midi_capture(uii); - - if (uii->handle_audiodev) - { - uii->no_resubmit = FALSE; - struct sched_param p; - memset(&p, 0, sizeof(p)); - p.sched_priority = cbox_config_get_int("io", "rtpriority", 10); - pid_t tid = syscall(SYS_gettid); - if (0 != sched_setscheduler(tid, SCHED_FIFO, &p)) - g_warning("Cannot set realtime priority for the processing thread: %s.", strerror(errno)); - - usbio_start_audio_playback(uii); - if (!uii->setup_error) - { - run_audio_loop(uii); - } - uii->no_resubmit = TRUE; - memset(&p, 0, sizeof(p)); - p.sched_priority = 0; - if (0 != sched_setscheduler(tid, SCHED_OTHER, &p)) - g_warning("Cannot unset realtime priority for the processing thread: %s.", strerror(errno)); - usbio_stop_audio_playback(uii); - } - else - { - uii->no_resubmit = TRUE; - g_message("No audio device found - running idle loop."); - // notify the UI thread that the (fake) audio loop is running - uii->playback_counter = uii->playback_buffers; - usbio_run_idle_loop(uii); - } - - usbio_stop_midi_capture(uii); - return NULL; -} - -static void cbox_usbio_destroy_midi_out(struct cbox_io_impl *ioi, struct cbox_midi_output *midiout) -{ - g_free(midiout->name); - free(midiout); -} - -static struct cbox_usb_midi_interface *cur_midi_interface = NULL; - -struct cbox_midi_input *cbox_usbio_create_midi_in(struct cbox_io_impl *impl, const char *name, GError **error) -{ - // struct cbox_usb_io_impl *uii = (struct cbox_usb_io_impl *)impl; - struct cbox_usb_midi_input *input = calloc(1, sizeof(struct cbox_usb_midi_input)); - input->hdr.name = g_strdup(name); - input->hdr.removing = FALSE; - cbox_uuid_generate(&input->hdr.uuid); - cbox_midi_buffer_init(&input->hdr.buffer); - input->ifptr = cur_midi_interface; - cbox_midi_appsink_init(&input->hdr.appsink, NULL, NULL); - input->hdr.enable_appsink = FALSE; - - return (struct cbox_midi_input *)input; -} - -struct cbox_midi_output *cbox_usbio_create_midi_out(struct cbox_io_impl *impl, const char *name, GError **error) -{ - // struct cbox_usb_io_impl *uii = (struct cbox_usb_io_impl *)impl; - struct cbox_usb_midi_output *output = calloc(1, sizeof(struct cbox_usb_midi_output)); - output->hdr.name = g_strdup(name); - output->hdr.removing = FALSE; - cbox_uuid_generate(&output->hdr.uuid); - cbox_midi_buffer_init(&output->hdr.buffer); - cbox_midi_merger_init(&output->hdr.merger, &output->hdr.buffer); - output->ifptr = cur_midi_interface; - - return (struct cbox_midi_output *)output; -} - -static void create_midi_ports(struct cbox_usb_io_impl *uii) -{ - uii->ioi.createmidiinfunc = cbox_usbio_create_midi_in; - uii->ioi.createmidioutfunc = cbox_usbio_create_midi_out; - for (GList *p = uii->midi_ports; p; p = p->next) - { - struct cbox_usb_midi_interface *umi = p->data; - char buf[80]; - sprintf(buf, "usb:%03d:%03d", umi->devinfo->bus, umi->devinfo->devadr); - cur_midi_interface = umi; - if (umi->epdesc_in.found) - umi->input_port = (struct cbox_usb_midi_input *)cbox_io_create_midi_input(uii->ioi.pio, buf, NULL); - else - umi->input_port = NULL; - if (umi->epdesc_out.found) - umi->output_port = (struct cbox_usb_midi_output *)cbox_io_create_midi_output(uii->ioi.pio, buf, NULL); - else - umi->output_port = NULL; - } - uii->ioi.createmidiinfunc = NULL; - uii->ioi.createmidioutfunc = NULL; - cur_midi_interface = NULL; -} - -gboolean cbox_usbio_start(struct cbox_io_impl *impl, struct cbox_command_target *fb, GError **error) -{ - struct cbox_usb_io_impl *uii = (struct cbox_usb_io_impl *)impl; - - // XXXKF: needs to queue the playback and capture transfers - - uii->stop_engine = FALSE; - uii->setup_error = FALSE; - uii->playback_counter = 0; - - create_midi_ports(uii); - - struct cbox_io *io = uii->ioi.pio; - // XXXKF There is a short period of time when the playback is in 'started' state - // but is not really processing the event loop. This is likely harmless, but - // should be kept in mind when adding any code between pthread_create - // and usbio_update_port_routing. - if (io->cb->on_started) - io->cb->on_started(io->cb->user_data); - - if (pthread_create(&uii->thr_engine, NULL, engine_thread, uii)) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "cannot create engine thread: %s", strerror(errno)); - return FALSE; - } - while(!uii->setup_error && uii->playback_counter < uii->playback_buffers) - usleep(10000); - usbio_update_port_routing(&uii->ioi); - - return TRUE; -} - -gboolean cbox_usbio_stop(struct cbox_io_impl *impl, GError **error) -{ - struct cbox_usb_io_impl *uii = (struct cbox_usb_io_impl *)impl; - - // XXXKF: needs to kill the playback and capture transfers, and - // wait for them to be killed - - uii->stop_engine = TRUE; - pthread_join(uii->thr_engine, NULL); - return TRUE; -} - -void cbox_usbio_poll_ports(struct cbox_io_impl *impl, struct cbox_command_target *fb) -{ - struct cbox_usb_io_impl *uii = (struct cbox_usb_io_impl *)impl; - - // Dry run, just to detect if anything changed - if (usbio_scan_devices(uii, TRUE)) - { - struct cbox_io_callbacks *cb = uii->ioi.pio->cb; - g_debug("Restarting I/O due to device being connected or disconnected"); - cbox_io_stop(uii->ioi.pio); - // Re-scan, this time actually create the MIDI inputs - usbio_scan_devices(uii, FALSE); - cbox_io_start(uii->ioi.pio, cb, fb); - } -} - -gboolean cbox_usbio_cycle(struct cbox_io_impl *impl, struct cbox_command_target *fb, GError **error) -{ - // struct cbox_usb_io_impl *uii = (struct cbox_usb_io_impl *)impl; - // XXXKF: this is for restarting the thing; not implemented for now, - // the implementation will be something like in case of JACK - close and - // reopen. - return TRUE; -} - -int cbox_usbio_get_midi_data(struct cbox_io_impl *impl, struct cbox_midi_buffer *destination) -{ - struct cbox_usb_io_impl *uii = (struct cbox_usb_io_impl *)impl; - - cbox_midi_merger_render_to(&uii->midi_input_merger, destination); - return 0; -} - -void cbox_usbio_destroy(struct cbox_io_impl *impl) -{ - struct cbox_usb_io_impl *uii = (struct cbox_usb_io_impl *)impl; - - GList *prev_keys = g_hash_table_get_values(uii->device_table); - for (GList *p = prev_keys; p; p = p->next) - { - struct cbox_usb_device_info *udi = p->data; - if (udi->status == CBOX_DEVICE_STATUS_OPENED) - usbio_forget_device(uii, udi); - } - g_list_free(prev_keys); - g_hash_table_destroy(uii->device_table); - - libusb_exit(uii->usbctx_probe); - libusb_exit(uii->usbctx); - cbox_midi_merger_close(&uii->midi_input_merger, app.rt); - free(uii); -} - -/////////////////////////////////////////////////////////////////////////////// - -struct usbio_transfer *usbio_transfer_new(struct libusb_context *usbctx, const char *transfer_type, int index, int isopackets, void *user_data) -{ - struct usbio_transfer *p = malloc(sizeof(struct usbio_transfer)); - p->usbctx = usbctx; - p->transfer = libusb_alloc_transfer(isopackets); - p->index = index; - p->cancel_confirm = FALSE; - p->pending = FALSE; - p->transfer_type = transfer_type; - p->user_data = user_data; - return p; -} - -int usbio_transfer_submit(struct usbio_transfer *xfer) -{ - int res = libusb_submit_transfer(xfer->transfer); - if (res != 0) - { - g_warning("usbio_transfer_submit: cannot submit transfer '%s:%d', error = %s", xfer->transfer_type, xfer->index, libusb_error_name(res)); - return res; - } - xfer->pending = TRUE; - return 0; -} - -void usbio_transfer_shutdown(struct usbio_transfer *xfer) -{ - if (xfer->pending) - { - int res = libusb_cancel_transfer(xfer->transfer); - if (res != LIBUSB_ERROR_NO_DEVICE) - { - int tries = 100; - while(!xfer->cancel_confirm && tries > 0 && xfer->pending) - { - struct timeval tv = { - .tv_sec = 0, - .tv_usec = 1000 - }; - libusb_handle_events_timeout(xfer->usbctx, &tv); - tries--; - } - if (!tries) - g_warning("Timed out waiting for transfer '%s:%d' to complete; status = %d", xfer->transfer_type, xfer->index, xfer->transfer->status); - } - } -} - -void usbio_transfer_destroy(struct usbio_transfer *xfer) -{ - libusb_free_transfer(xfer->transfer); - free(xfer); -} - - -/////////////////////////////////////////////////////////////////////////////// - -static gboolean cbox_usb_io_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - struct cbox_usb_io_impl *uii = (struct cbox_usb_io_impl *)ct->user_data; - struct cbox_io *io = uii->ioi.pio; - gboolean handled = FALSE; - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - for (GList *p = uii->midi_ports; p; p = g_list_next(p)) - { - struct cbox_usb_midi_interface *midi = p->data; - struct cbox_usb_device_info *di = midi->devinfo; - if (midi->epdesc_in.found && !cbox_execute_on(fb, NULL, "/usb_midi_input", "iiiiu", error, di->bus, di->devadr, di->vid, di->pid, &midi->input_port->hdr.uuid)) - return FALSE; - if (midi->epdesc_out.found && !cbox_execute_on(fb, NULL, "/usb_midi_output", "iiiiu", error, di->bus, di->devadr, di->vid, di->pid, &midi->output_port->hdr.uuid)) - return FALSE; - } - - return cbox_execute_on(fb, NULL, "/client_type", "s", error, "USB") && - cbox_execute_on(fb, NULL, "/output_resolution", "i", error, 8 * uii->output_resolution) && - cbox_io_process_cmd(io, fb, cmd, error, &handled); - } - else - { - gboolean result = cbox_io_process_cmd(io, fb, cmd, error, &handled); - if (!handled) - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown combination of target path and argument: '%s', '%s'", cmd->command, cmd->arg_types); - return result; - } -} - -/////////////////////////////////////////////////////////////////////////////// - -gboolean cbox_io_init_usb(struct cbox_io *io, struct cbox_open_params *const params, struct cbox_command_target *fb, GError **error) -{ - struct cbox_usb_io_impl *uii = malloc(sizeof(struct cbox_usb_io_impl)); - if (libusb_init(&uii->usbctx)) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot initialise libusb."); - return FALSE; - } - if (libusb_init(&uii->usbctx_probe)) - { - libusb_exit(uii->usbctx); - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot initialise libusb."); - return FALSE; - } -#if LIBUSB_API_VERSION >= 0x01000106 - libusb_set_option(uii->usbctx, LIBUSB_OPTION_LOG_LEVEL, 3); - libusb_set_option(uii->usbctx_probe, LIBUSB_OPTION_LOG_LEVEL, 3); -#else - libusb_set_debug(uii->usbctx, 3); - libusb_set_debug(uii->usbctx_probe, 3); -#endif - uii->device_table = g_hash_table_new(g_direct_hash, NULL); - - uii->sample_rate = cbox_config_get_int(cbox_io_section, "sample_rate", 44100); - uii->sync_buffers = cbox_config_get_int(cbox_io_section, "sync_buffers", 2); - uii->debug_sync = cbox_config_get_int(cbox_io_section, "debug_sync", 0); - uii->playback_buffers = cbox_config_get_int(cbox_io_section, "playback_buffers", 2); - // shouldn't be more than 4, otherwise it will crackle due to limitations of - // the packet length adjustment. It might work better if adjustment - // was per-packet and not per-transfer. - uii->iso_packets = cbox_config_get_int(cbox_io_section, "iso_packets", 1); - // The USB 2.0 device uses a higher packet rate (125us I think), so the - // default number of packets per transfer needs to be different, too - - // 1ms is a minimum reasonable value - uii->iso_packets_multimix = cbox_config_get_int(cbox_io_section, "iso_packets_multimix", 16); - uii->output_resolution = cbox_config_get_int(cbox_io_section, "output_resolution", 16) / 8; - uii->output_channels = 2; - uii->handle_audiodev = NULL; - cbox_midi_merger_init(&uii->midi_input_merger, NULL); - - // fixed processing buffer size, as we have to deal with packetisation anyway - io->io_env.srate = uii->sample_rate; - io->io_env.buffer_size = 64; - io->cb = NULL; - // input and output count is hardcoded for simplicity - in future, it may be - // necessary to add support for the extra inputs (needs to be figured out) - io->io_env.input_count = 2; //cbox_config_get_int("io", "inputs", 0); - io->input_buffers = malloc(sizeof(float *) * io->io_env.input_count); - for (uint32_t i = 0; i < io->io_env.input_count; i++) - io->input_buffers[i] = calloc(io->io_env.buffer_size, sizeof(float)); - io->io_env.output_count = 2; // cbox_config_get_int("io", "outputs", 2); - io->output_buffers = malloc(sizeof(float *) * io->io_env.output_count); - for (uint32_t i = 0; i < io->io_env.output_count; i++) - io->output_buffers[i] = calloc(io->io_env.buffer_size, sizeof(float)); - io->impl = &uii->ioi; - cbox_command_target_init(&io->cmd_target, cbox_usb_io_process_cmd, uii); - - uii->ioi.pio = io; - uii->ioi.getsampleratefunc = cbox_usbio_get_sample_rate; - uii->ioi.startfunc = cbox_usbio_start; - uii->ioi.stopfunc = cbox_usbio_stop; - uii->ioi.getstatusfunc = cbox_usbio_get_status; - uii->ioi.pollfunc = cbox_usbio_poll_ports; - uii->ioi.cyclefunc = cbox_usbio_cycle; - uii->ioi.getmidifunc = cbox_usbio_get_midi_data; - uii->ioi.destroymidioutfunc = cbox_usbio_destroy_midi_out; - uii->ioi.destroyfunc = cbox_usbio_destroy; - uii->ioi.controltransportfunc = NULL; - uii->ioi.getsynccompletedfunc = NULL; - uii->ioi.updatemidiinroutingfunc = usbio_update_port_routing; - uii->midi_ports = NULL; - - usbio_scan_devices(uii, FALSE); - - if (cbox_config_get_int("io", "lockall", 0)) - mlockall(MCL_CURRENT|MCL_FUTURE); - - return TRUE; - -} - -#endif diff --git a/template/calfbox/usbio_impl.h b/template/calfbox/usbio_impl.h deleted file mode 100644 index dd8bc41..0000000 --- a/template/calfbox/usbio_impl.h +++ /dev/null @@ -1,255 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -/* - -Note: -Those are private structures and functions for the USB driver implementation. -I'm not too happy with the organization of the code yet, it's just a first -try at breaking it up into manageable parts. -*/ - - -#ifndef USBIO_IMPL_H -#define USBIO_IMPL_H - -#include - -#include "errors.h" -#include "io.h" -#include "midi.h" -#include "mididest.h" - -struct usbio_endpoint_descriptor -{ - uint8_t found:1; - uint8_t interrupt:1; - uint8_t reserved:6; - uint8_t bEndpointAddress; - uint16_t wMaxPacketSize; -}; - -struct usbio_transfer -{ - struct libusb_context *usbctx; - struct libusb_transfer *transfer; - void *user_data; - gboolean pending; - gboolean cancel_confirm; - const char *transfer_type; - int index; -}; - -enum usb_sync_protocol_type -{ - USBAUDIOSYNC_PROTOCOL_NONE = 0, - USBAUDIOSYNC_PROTOCOL_CLASS, - USBAUDIOSYNC_PROTOCOL_MULTIMIX8, -}; - -struct cbox_usb_io_impl -{ - struct cbox_io_impl ioi; - - struct libusb_context *usbctx; - struct libusb_context *usbctx_probe; - - GHashTable *device_table; - - struct libusb_device_handle *handle_audiodev; - int sample_rate, buffer_size, output_resolution; - int output_channels; // always 2 for now - - unsigned int playback_buffers; - unsigned int sync_buffers; - unsigned int playback_counter; - unsigned int sync_counter; - unsigned int device_removed; - enum usb_sync_protocol_type sync_protocol; - - unsigned int iso_packets, iso_packets_multimix; - - pthread_t thr_engine; - volatile gboolean stop_engine, setup_error, no_resubmit; - - int desync; - uint64_t samples_played; - struct usbio_transfer **playback_transfers; - struct usbio_transfer **sync_transfers; - int read_ptr; - - GList *midi_ports; - GList *rt_midi_ports; - struct cbox_midi_merger midi_input_merger; - void (*play_function)(struct cbox_usb_io_impl *uii); - int8_t audio_output_endpoint; - int8_t audio_sync_endpoint; - gboolean is_hispeed; - uint32_t samples_per_sec; // current estimated sample rate / 100 - uint32_t audio_output_pktsize; - int debug_sync; -}; - -enum cbox_usb_device_status -{ - CBOX_DEVICE_STATUS_PROBING, - CBOX_DEVICE_STATUS_UNSUPPORTED, - CBOX_DEVICE_STATUS_OPENED, -}; - -struct cbox_usb_device_info -{ - struct libusb_device *dev; - struct libusb_device_handle *handle; - enum cbox_usb_device_status status; - uint8_t bus; - uint8_t devadr; - uint16_t busdevadr; - struct { - uint16_t vid; - uint16_t pid; - }; - int active_config; - gboolean is_midi, is_audio; - uint32_t configs_with_midi; - uint32_t configs_with_audio; - time_t last_probe_time; - int failures; -}; - -struct cbox_usb_audio_info -{ - struct cbox_usb_device_info *udi; - int intf; - int alt_setting; - struct usbio_endpoint_descriptor epdesc; - enum usb_sync_protocol_type sync_protocol; - struct usbio_endpoint_descriptor sync_epdesc; -}; - -enum usb_midi_protocol_type -{ - USBMIDI_PROTOCOL_CLASS, - USBMIDI_PROTOCOL_MPD16, - USBMIDI_PROTOCOL_NOCTURN, -}; - -struct cbox_usb_midi_info -{ - struct cbox_usb_device_info *udi; - int intf; - int alt_setting; - struct usbio_endpoint_descriptor epdesc_in; - struct usbio_endpoint_descriptor epdesc_out; - enum usb_midi_protocol_type protocol; -}; - -#define MAX_MIDI_PACKET_SIZE 256 -#define MAX_SYSEX_SIZE CBOX_MIDI_MAX_LONG_DATA - -struct cbox_usb_midi_interface -{ - struct cbox_usb_io_impl *uii; - struct cbox_usb_device_info *devinfo; - struct libusb_device_handle *handle; - int busdevadr; - struct usbio_endpoint_descriptor epdesc_in, epdesc_out; - struct usbio_transfer *transfer_in, *transfer_out; - uint8_t midi_recv_data[MAX_MIDI_PACKET_SIZE]; - uint8_t sysex_data[MAX_SYSEX_SIZE]; - uint32_t current_sysex_length; - enum usb_midi_protocol_type protocol; - struct cbox_usb_midi_input *input_port; - struct cbox_usb_midi_output *output_port; -}; - -#define USB_MIDI_OUTPUT_QUEUE 256 - -struct cbox_usb_midi_input -{ - struct cbox_midi_input hdr; - struct cbox_usb_midi_interface *ifptr; -}; - -struct cbox_usb_midi_output -{ - struct cbox_midi_output hdr; - struct cbox_usb_midi_interface *ifptr; - uint8_t endpoint_buffer[USB_MIDI_OUTPUT_QUEUE]; - uint32_t endpoint_buffer_pos; -}; - -extern struct usbio_transfer *usbio_transfer_new(struct libusb_context *usbctx, const char *transfer_type, int index, int isopackets, void *user_data); -extern void usbio_transfer_shutdown(struct usbio_transfer *xfer); -extern int usbio_transfer_submit(struct usbio_transfer *xfer); -extern void usbio_transfer_destroy(struct usbio_transfer *xfer); - -extern void cbox_usb_midi_info_init(struct cbox_usb_midi_info *umi, struct cbox_usb_device_info *udi); -extern void usbio_start_midi_capture(struct cbox_usb_io_impl *uii); -extern void usbio_stop_midi_capture(struct cbox_usb_io_impl *uii); -extern struct cbox_usb_midi_interface *usbio_open_midi_interface(struct cbox_usb_io_impl *uii, - const struct cbox_usb_midi_info *uminf, struct libusb_device_handle *handle); -extern void usbio_send_midi_to_output(struct cbox_usb_midi_output *umo); -extern void usbio_fill_midi_output_buffer(struct cbox_usb_midi_output *umo); - -extern void cbox_usb_audio_info_init(struct cbox_usb_audio_info *uai, struct cbox_usb_device_info *udi); -extern void usbio_start_audio_playback(struct cbox_usb_io_impl *uii); -extern void usbio_stop_audio_playback(struct cbox_usb_io_impl *uii); -extern gboolean usbio_open_audio_interface(struct cbox_usb_io_impl *uii, - struct cbox_usb_audio_info *uainf, struct libusb_device_handle *handle, GError **error); -extern gboolean usbio_open_audio_interface_multimix(struct cbox_usb_io_impl *uii, int bus, int devadr, struct libusb_device_handle *handle, GError **error); -extern void usbio_update_port_routing(struct cbox_io_impl *ioi); - - -extern void usbio_play_buffer_asynchronous(struct cbox_usb_io_impl *uii); -extern gboolean usbio_scan_devices(struct cbox_usb_io_impl *uii, gboolean probe_only); - -extern void usbio_forget_device(struct cbox_usb_io_impl *uii, struct cbox_usb_device_info *devinfo); -extern void usbio_run_idle_loop(struct cbox_usb_io_impl *uii); - -#define USB_DEVICE_SETUP_TIMEOUT 2000 - -static inline gboolean configure_usb_interface(struct libusb_device_handle *handle, int bus, int devadr, int ifno, int altset, const char *purpose, GError **error) -{ - int err = 0; - if (libusb_kernel_driver_active(handle, ifno) == 1) - { - err = libusb_detach_kernel_driver(handle, ifno); - if (err) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot detach kernel driver from interface %d on device %03d:%03d: %s. Please rmmod snd-usb-audio as root.", ifno, bus, devadr, libusb_error_name(err)); - return FALSE; - } - } - err = libusb_claim_interface(handle, ifno); - if (err) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot claim interface %d on device %03d:%03d for %s: %s", ifno, bus, devadr, purpose, libusb_error_name(err)); - return FALSE; - } - err = altset ? libusb_set_interface_alt_setting(handle, ifno, altset) : 0; - if (err) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot set alt-setting %d for interface %d on device %03d:%03d for %s: %s", altset, ifno, bus, devadr, purpose, libusb_error_name(err)); - return FALSE; - } - return TRUE; -} - - -#endif diff --git a/template/calfbox/usbmidi.c b/template/calfbox/usbmidi.c deleted file mode 100644 index a546f97..0000000 --- a/template/calfbox/usbmidi.c +++ /dev/null @@ -1,397 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config.h" -#if USE_LIBUSB - -#include "app.h" -#include "config-api.h" -#include "errors.h" -#include "hwcfg.h" -#include "io.h" -#include "meter.h" -#include "midi.h" -#include "recsrc.h" -#include "usbio_impl.h" - -#include - -static void decode_events_mpd16(struct cbox_usb_midi_interface *umi, struct libusb_transfer *transfer) -{ - for (int i = 0; i < transfer->actual_length;) - { - uint8_t *data = &transfer->buffer[i]; - uint8_t len = data[0] & 15; - if (!len || i + len >= transfer->actual_length) - break; - cbox_midi_buffer_write_inline(&umi->input_port->hdr.buffer, 0, data[1], len > 1 ? data[2] : 0, len > 2 ? data[3] : 0); - i += len + 1; - } -} - -static void decode_events_nocturn(struct cbox_usb_midi_interface *umi, struct libusb_transfer *transfer) -{ - uint8_t control = 0; - for (int i = 0; i < transfer->actual_length;) - { - uint8_t *data = &transfer->buffer[i]; - if (*data >= 128) - { - control = *data; - i++; - continue; - } - if (control != 176 || i + 2 > transfer->actual_length) - { - g_warning("Unrecognized combination of control, data and length: %02x %02x %02x", control, data[0], transfer->actual_length - i); - break; - } - cbox_midi_buffer_write_inline(&umi->input_port->hdr.buffer, 0, control, data[0], data[1]); - i += 2; - } -} - -static void decode_events_class(struct cbox_usb_midi_interface *umi, struct libusb_transfer *transfer) -{ - for (int i = 0; i + 3 < transfer->actual_length; i += 4) - { - uint8_t *data = &transfer->buffer[i]; - uint8_t etype = data[0] & 15; - if (etype >= 8) - { - // normalise: note on with vel 0 -> note off - if ((data[1] & 0xF0) == 0x90 && data[3] == 0) - cbox_midi_buffer_write_inline(&umi->input_port->hdr.buffer, 0, data[1] - 0x10, data[2], data[3]); - else - cbox_midi_buffer_write_event(&umi->input_port->hdr.buffer, 0, data + 1, midi_cmd_size(data[1])); - } - else - if (etype == 2 || etype == 3) - { - cbox_midi_buffer_write_event(&umi->input_port->hdr.buffer, 0, data + 1, etype); - } - else - if (etype == 4) - { - if (umi->current_sysex_length + 3 <= MAX_SYSEX_SIZE) - memcpy(&umi->sysex_data[umi->current_sysex_length], data + 1, 3); - umi->current_sysex_length += 3; - } - else - if (etype >= 5 && etype <= 7) - { - int len = etype - 4; - if (umi->current_sysex_length + len <= MAX_SYSEX_SIZE) - memcpy(&umi->sysex_data[umi->current_sysex_length], data + 1, len); - umi->current_sysex_length += len; - if (umi->current_sysex_length <= MAX_SYSEX_SIZE) - cbox_midi_buffer_write_event(&umi->input_port->hdr.buffer, 0, umi->sysex_data, umi->current_sysex_length); - else - g_warning("Dropping oversized SysEx: length %d", umi->current_sysex_length); - umi->current_sysex_length = 0; - - } - else - g_warning("Unrecognized USB MIDI initiating byte %02x\n", data[0]); - } -} - -static void midi_transfer_cb(struct libusb_transfer *transfer) -{ - struct usbio_transfer *xf = transfer->user_data; - struct cbox_usb_midi_interface *umi = xf->user_data; - xf->pending = FALSE; - - if (transfer->status == LIBUSB_TRANSFER_CANCELLED) - { - xf->cancel_confirm = 1; - return; - } - if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT || transfer->status == LIBUSB_TRANSFER_ERROR || transfer->status == LIBUSB_TRANSFER_STALL) - { - if (transfer->status != LIBUSB_TRANSFER_TIMED_OUT) - g_warning("USB error on device %03d:%03d: transfer status %d", umi->busdevadr >> 8, umi->busdevadr & 255, transfer->status); - if (umi->uii->no_resubmit) - return; - int res = usbio_transfer_submit(xf); - if (res != 0) - g_warning("Error submitting URB to MIDI endpoint: error code %d", res); - return; - } - if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) - { - g_debug("No device %03d:%03d, unlinking", umi->busdevadr >> 8, umi->busdevadr & 255); - umi->uii->rt_midi_ports = g_list_remove(umi->uii->rt_midi_ports, umi); - return; - } - - if (umi->protocol == USBMIDI_PROTOCOL_MPD16) // MPD16 - decode_events_mpd16(umi, transfer); - else - if (umi->protocol == USBMIDI_PROTOCOL_NOCTURN) // Nocturn - decode_events_nocturn(umi, transfer); - else - decode_events_class(umi, transfer); - - if (umi->uii->no_resubmit) - return; - usbio_transfer_submit(xf); -} - -static gboolean push_data_to_umo(struct cbox_usb_midi_output *umo, const uint8_t *pdata, uint32_t size, uint8_t first_byte) -{ - if (umo->endpoint_buffer_pos + 4 <= USB_MIDI_OUTPUT_QUEUE) - { - umo->endpoint_buffer[umo->endpoint_buffer_pos] = first_byte; - memcpy(&umo->endpoint_buffer[umo->endpoint_buffer_pos + 1], pdata, size); - umo->endpoint_buffer_pos += 4; - } - else - { - g_warning("Class MIDI buffer overflow."); - return FALSE; - } - return TRUE; -} - -static void encode_events_class(struct cbox_usb_midi_output *umo) -{ - for (uint32_t i = 0; i < umo->hdr.buffer.count; i++) - { - const struct cbox_midi_event *event = cbox_midi_buffer_get_event(&umo->hdr.buffer, i); - const uint8_t *pdata = cbox_midi_event_get_data(event); - if (event->size <= 3) - { - if (!push_data_to_umo(umo, pdata, event->size, pdata[0] >> 4)) - return; - } - else - { - int i = 0; - while(i + 3U < event->size) - { - push_data_to_umo(umo, pdata + i, 3, 4); - i += 3; - } - push_data_to_umo(umo, pdata + i, 3, 4 + event->size - i); - } - } -} - -static void encode_events_nocturn(struct cbox_usb_midi_output *umo) -{ - for (uint32_t i = 0; i < umo->hdr.buffer.count; i++) - { - const struct cbox_midi_event *event = cbox_midi_buffer_get_event(&umo->hdr.buffer, i); - const uint8_t *pdata = cbox_midi_event_get_data(event); - // Only encode control change events on channel 1 - it's the only - // recognized type of events - if (event->size == 3 && pdata[0] == 0xB0) - { - if (umo->endpoint_buffer_pos + 2 <= 8) - { - umo->endpoint_buffer[umo->endpoint_buffer_pos] = pdata[1]; - umo->endpoint_buffer[umo->endpoint_buffer_pos + 1] = pdata[2]; - umo->endpoint_buffer_pos += 2; - } - else - g_warning("Nocturn MIDI buffer overflow."); - } - } -} - -void usbio_fill_midi_output_buffer(struct cbox_usb_midi_output *umo) -{ - cbox_midi_merger_render(&umo->hdr.merger); - if (!umo->hdr.buffer.count) - return; - - if (umo->ifptr->protocol == USBMIDI_PROTOCOL_CLASS) - encode_events_class(umo); - else - if (umo->ifptr->protocol == USBMIDI_PROTOCOL_NOCTURN) - encode_events_nocturn(umo); -} - -void usbio_send_midi_to_output(struct cbox_usb_midi_output *umo) -{ - if (!umo->endpoint_buffer_pos) - return; - - int transferred = 0; - int res = 0; - if (umo->ifptr->epdesc_out.interrupt) - res = libusb_interrupt_transfer(umo->ifptr->handle, umo->ifptr->epdesc_out.bEndpointAddress, umo->endpoint_buffer, umo->endpoint_buffer_pos, &transferred, 10); - else - res = libusb_bulk_transfer(umo->ifptr->handle, umo->ifptr->epdesc_out.bEndpointAddress, umo->endpoint_buffer, umo->endpoint_buffer_pos, &transferred, 10); - if (res == 0 && (uint32_t)transferred == umo->endpoint_buffer_pos) - umo->endpoint_buffer_pos = 0; - else - g_warning("Failed to send MIDI events, transferred = %d out of %d, result = %d", (int)transferred, (int)umo->endpoint_buffer_pos, res); -} - -void usbio_update_port_routing(struct cbox_io_impl *ioi) -{ - struct cbox_usb_io_impl *uii = (struct cbox_usb_io_impl *)ioi; - for(GList *p = uii->rt_midi_ports; p; p = p->next) - { - struct cbox_usb_midi_interface *umi = p->data; - if (umi->input_port) - { - if (!umi->input_port->hdr.output_set) - cbox_midi_merger_connect(&uii->midi_input_merger, &umi->input_port->hdr.buffer, app.rt, NULL); - else - cbox_midi_merger_disconnect(&uii->midi_input_merger, &umi->input_port->hdr.buffer, app.rt); - } - } -} - -void usbio_start_midi_capture(struct cbox_usb_io_impl *uii) -{ - uii->rt_midi_ports = g_list_copy(uii->midi_ports); - - for(GList *p = uii->rt_midi_ports; p; p = p->next) - { - struct cbox_usb_midi_interface *umi = p->data; - cbox_midi_buffer_clear(&umi->input_port->hdr.buffer); - if (umi->epdesc_in.found) - { - umi->current_sysex_length = 0; - umi->transfer_in = usbio_transfer_new(uii->usbctx, "MIDI In", 0, 0, umi); - int pktsize = umi->epdesc_in.wMaxPacketSize; - if (pktsize > MAX_MIDI_PACKET_SIZE) - pktsize = MAX_MIDI_PACKET_SIZE; - if (umi->epdesc_in.interrupt) - libusb_fill_interrupt_transfer(umi->transfer_in->transfer, umi->handle, umi->epdesc_in.bEndpointAddress, umi->midi_recv_data, pktsize, midi_transfer_cb, umi->transfer_in, 0); - else - libusb_fill_bulk_transfer(umi->transfer_in->transfer, umi->handle, umi->epdesc_in.bEndpointAddress, umi->midi_recv_data, pktsize, midi_transfer_cb, umi->transfer_in, 0); - } - else - umi->transfer_in = NULL; - if (umi->epdesc_out.found) - umi->transfer_out = usbio_transfer_new(uii->usbctx, "MIDI Out", 0, 0, umi); - else - umi->transfer_out = NULL; - } - for(GList *p = uii->rt_midi_ports; p; p = p->next) - { - struct cbox_usb_midi_interface *umi = p->data; - int res = usbio_transfer_submit(umi->transfer_in); - if (res != 0) - { - usbio_transfer_destroy(umi->transfer_in); - umi->transfer_in = NULL; - } - } -} - -void usbio_stop_midi_capture(struct cbox_usb_io_impl *uii) -{ - for(GList *p = uii->rt_midi_ports; p; p = p->next) - { - struct cbox_usb_midi_interface *umi = p->data; - - if (umi->transfer_in) - { - usbio_transfer_shutdown(umi->transfer_in); - usbio_transfer_destroy(umi->transfer_in); - umi->transfer_in = NULL; - cbox_midi_buffer_clear(&umi->input_port->hdr.buffer); - } - if (umi->transfer_out) - { - usbio_transfer_shutdown(umi->transfer_out); - usbio_transfer_destroy(umi->transfer_out); - umi->transfer_out = NULL; - } - } - for(GList *p = uii->rt_midi_ports; p; p = p->next) - { - struct cbox_usb_midi_interface *umi = p->data; - if (umi->epdesc_in.found) - cbox_midi_merger_disconnect(&uii->midi_input_merger, &umi->input_port->hdr.buffer, NULL); - } - g_list_free(uii->rt_midi_ports); -} - -void cbox_usb_midi_info_init(struct cbox_usb_midi_info *umi, struct cbox_usb_device_info *udi) -{ - umi->udi = udi; - umi->intf = -1; - umi->alt_setting = -1; - umi->epdesc_in.found = FALSE; - umi->epdesc_out.found = FALSE; -} - -struct cbox_usb_midi_interface *usbio_open_midi_interface(struct cbox_usb_io_impl *uii, const struct cbox_usb_midi_info *uminf, struct libusb_device_handle *handle) -{ - struct cbox_usb_device_info *devinfo = uminf->udi; - int bus = devinfo->bus; - int devadr = devinfo->devadr; - GError *error = NULL; - // printf("Has MIDI port\n"); - // printf("Output endpoint address = %02x\n", ep->bEndpointAddress); - if (!configure_usb_interface(handle, bus, devadr, uminf->intf, uminf->alt_setting, "MIDI (class driver)", &error)) - { - g_warning("%s", error->message); - g_error_free(error); - return NULL; - } - - struct cbox_usb_midi_interface *umi = calloc(sizeof(struct cbox_usb_midi_interface), 1); - umi->uii = uii; - umi->devinfo = devinfo; - umi->handle = handle; - umi->busdevadr = devinfo->busdevadr; - uii->midi_ports = g_list_prepend(uii->midi_ports, umi); - umi->protocol = uminf->protocol; - memcpy(&umi->epdesc_in, &uminf->epdesc_in, sizeof(umi->epdesc_in)); - memcpy(&umi->epdesc_out, &uminf->epdesc_out, sizeof(umi->epdesc_out)); - - // Drain the output buffer of the device - otherwise playing a few notes and running the program will cause - // those notes to play. - unsigned char flushbuf[256]; - int transferred = 0; - int len = umi->epdesc_in.wMaxPacketSize; - if (len > MAX_MIDI_PACKET_SIZE) - len = MAX_MIDI_PACKET_SIZE; - if (umi->epdesc_in.interrupt) - { - while(0 == libusb_interrupt_transfer(handle, umi->epdesc_in.bEndpointAddress, flushbuf, len, &transferred, 10) && transferred > 0) - usleep(1000); - } - else - { - while(0 == libusb_bulk_transfer(handle, umi->epdesc_in.bEndpointAddress, flushbuf, len, &transferred, 10) && transferred > 0) - usleep(1000); - } - devinfo->status = CBOX_DEVICE_STATUS_OPENED; - - // Initialise device - only used for Novation Nocturn, and it performs a - // device reset. Perhaps not the best implementation of hot swap - it - // will reset the device even when some other device was connected. - if (umi->protocol == USBMIDI_PROTOCOL_NOCTURN) - { - static uint8_t data1[] = { 0xB0, 0, 0 }; - libusb_interrupt_transfer(handle, umi->epdesc_out.bEndpointAddress, data1, sizeof(data1), &transferred, 10); - } - - return umi; -} - -#endif diff --git a/template/calfbox/usbprobe.c b/template/calfbox/usbprobe.c deleted file mode 100644 index 8ee5e7a..0000000 --- a/template/calfbox/usbprobe.c +++ /dev/null @@ -1,751 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2013 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "config.h" - -#if USE_LIBUSB -#include "usbio_impl.h" -#include - -//////////////////////////////////////////////////////////////////////////////// - -static const struct libusb_endpoint_descriptor *get_midi_endpoint(const struct libusb_interface_descriptor *asdescr, gboolean is_output) -{ - for (int epi = 0; epi < asdescr->bNumEndpoints; epi++) - { - const struct libusb_endpoint_descriptor *ep = &asdescr->endpoint[epi]; - if ((ep->bEndpointAddress >= 0x80) == !is_output) - return ep; - } - return NULL; -} - -static const struct libusb_endpoint_descriptor *get_audio_output_endpoint(const struct libusb_interface_descriptor *asdescr, const struct libusb_endpoint_descriptor **sync_endpoint) -{ - for (int epi = 0; epi < asdescr->bNumEndpoints; epi++) - { - const struct libusb_endpoint_descriptor *ep = &asdescr->endpoint[epi]; - uint8_t ep_type = (ep->bmAttributes & 0xF); - if (ep->bEndpointAddress < 0x80 && (ep_type == 9 || ep_type == 13)) // output, isochronous, adaptive or synchronous - { - if (sync_endpoint) - *sync_endpoint = NULL; - return ep; - } - if (ep->bEndpointAddress < 0x80 && ep_type == 5) // output, isochronous, asynchronous - { - // Look for corresponding synch endpoint. It must be placed after the original output endpoint. - if (sync_endpoint && ep->bSynchAddress >= 0x81) - { - *sync_endpoint = NULL; - for(int epi2 = epi + 1; epi2 < asdescr->bNumEndpoints; epi2++) - { - const struct libusb_endpoint_descriptor *ep2 = &asdescr->endpoint[epi2]; - if (ep2->bEndpointAddress == ep->bSynchAddress && (ep2->bmAttributes & 0xF) == 1) // input, isochronous, no sync - { - *sync_endpoint = ep2; - break; - } - } - } - return ep; - } - } - return NULL; -} - -static void fill_endpoint_desc(struct usbio_endpoint_descriptor *epdesc, const struct libusb_endpoint_descriptor *ep) -{ - epdesc->found = TRUE; - epdesc->interrupt = FALSE; - epdesc->bEndpointAddress = ep->bEndpointAddress; - epdesc->wMaxPacketSize = ep->wMaxPacketSize; -} - -static gboolean fill_endpoint_by_address(const struct libusb_interface_descriptor *asdescr, uint8_t addr, struct usbio_endpoint_descriptor *epdesc) -{ - epdesc->found = FALSE; - epdesc->interrupt = FALSE; - for (int epi = 0; epi < asdescr->bNumEndpoints; epi++) - { - const struct libusb_endpoint_descriptor *ep = &asdescr->endpoint[epi]; - if (ep->bEndpointAddress == addr) - { - fill_endpoint_desc(epdesc, ep); - return TRUE; - } - } - return FALSE; -} - -//////////////////////////////////////////////////////////////////////////////// - -struct usb_ut_descriptor_header -{ - uint8_t bLength; - uint8_t bDescriptorType; - uint8_t bDescriptorSubtype; -}; - -struct usb_class_specific_header -{ - struct usb_ut_descriptor_header hdr; - uint8_t bcdADC[2]; - uint8_t wTotalLengthLo, wTotalLengthHi; - uint8_t bInCollection; - uint8_t baInterfaceNr[0]; -}; - -struct usbaudio_input_terminal_descriptor -{ - uint8_t bTerminalID; - uint8_t wTerminalTypeLo, wTerminalTypeHi; - uint8_t bAssocTerminal; - uint8_t bNrChannels; - uint8_t wChannelConfigLo, wChannelConfigHi; - uint8_t iChannelNames; - uint8_t iTerminal; -}; - -struct usbaudio_output_terminal_descriptor -{ - uint8_t bTerminalID; - uint8_t wTerminalTypeLo, wTerminalTypeHi; - uint8_t bAssocTerminal; - uint8_t bSourceID; - uint8_t iTerminal; -}; - -struct usbaudio_mixer_unit_descriptor1 -{ - uint8_t bUnitID; - uint8_t bNrInPins; - uint8_t baSourceID[0]; -}; - -struct usbaudio_mixer_unit_descriptor2 -{ - uint8_t bNrChannels; - uint8_t wChannelConfigLo, wChannelConfigHi; - uint8_t bmControls[0]; - // after the last index, 8-bit iMixer -}; - -struct usbaudio_selector_unit_descriptor1 -{ - uint8_t bUnitID; - uint8_t bNrInPins; - uint8_t baSourceID[0]; - // after the last index, 8-bit iSelector -}; - -struct usbaudio_feature_unit_descriptor1 -{ - uint8_t bUnitID; - uint8_t bSourceID; - uint8_t bControlSize; - uint8_t bmaControls[0]; - // after the last index, 8-bit iFeature -}; - -// Unit/Terminal Descriptor header from the spec -#include - -#if 0 -// termt10.pdf -static const char *get_terminal_type_name(int type) -{ - switch(type) - { - case 0x101: return "USB Streaming"; - case 0x1FF: return "USB vendor specific"; - case 0x201: return "Microphone"; - case 0x202: return "Desktop microphone"; - case 0x203: return "Personal microphone"; - case 0x204: return "Omni-directional microphone"; - case 0x205: return "Microphone array"; - case 0x206: return "Processing microphone array"; - case 0x301: return "Speaker"; - case 0x302: return "Headphones"; - case 0x303: return "Head-mounted display audio"; - case 0x304: return "Desktop speaker"; - case 0x305: return "Room speaker"; - case 0x306: return "Communication speaker"; - case 0x307: return "Low-frequency effects speaker"; - case 0x400: return "Bi-directional Undefined"; - case 0x401: return "Handset (handheld)"; - case 0x402: return "Handset (head-mounted)"; - case 0x403: return "Speakerphone"; - case 0x404: return "Echo-suppressing speakerphone"; - case 0x405: return "Echo-cancelling speakerphone"; - case 0x501: return "Phone line"; - case 0x502: return "Telephone"; - case 0x503: return "Down line phone"; - case 0x600: return "External Undefined"; - case 0x601: return "Analog connector"; - case 0x602: return "Digital audio interface"; - case 0x603: return "Line connector"; - case 0x604: return "Legacy audio"; - case 0x605: return "S/PDIF"; - case 0x606: return "1394 D/A Stream"; - case 0x607: return "1394 D/V Stream Soundtrack"; - case 0x700: return "Embedded undefined"; - case 0x701: return "Level calibration noise source"; - case 0x702: return "Equalization noise"; - case 0x703: return "CD player"; - case 0x704: return "DAT"; - case 0x705: return "DCC"; - case 0x706: return "MiniDisc"; - case 0x707: return "Analog tape"; - case 0x708: return "Phono"; - case 0x709: return "VCR Audio"; - case 0x70A: return "VideoDisc Audio"; - case 0x70B: return "DVD Audio"; - case 0x70C: return "TV Tuner Audio"; - case 0x70D: return "Satellite Receiver Audio"; - case 0x70E: return "Cable Tuner Audio"; - case 0x70F: return "DSS Audio"; - case 0x710: return "Radio Receiver"; - case 0x711: return "Radio Transmitter"; - case 0x712: return "Multitrack Recorder"; - case 0x713: return "Synthesizer"; - default: - return "Unknown"; - } -} -#endif - -static gboolean parse_audio_control_class(struct cbox_usb_audio_info *uai, const struct libusb_interface_descriptor *asdescr, gboolean other_config) -{ -#if 0 - if (asdescr->extra_length < 8) - { - g_warning("AudioControl interface descriptor length is %d, should be at least 8", (int)asdescr->extra_length); - return FALSE; - } - const struct usb_class_specific_header *extra = (const struct usb_class_specific_header *)asdescr->extra; - uint16_t wTotalLength = extra->wTotalLengthLo + 256 * extra->wTotalLengthHi; - if (wTotalLength > asdescr->extra_length) - { - g_warning("AudioControl interface descriptor total length is %d, but libusb value is %d", (int)wTotalLength, (int)asdescr->extra_length); - return FALSE; - } - if (extra->hdr.bDescriptorType != 36) - { - g_warning("AudioControl interface descriptor type is %d, but expected 36 (CS_INTERFACE)", (int)extra->hdr.bDescriptorType); - return FALSE; - } - if (extra->hdr.bDescriptorSubtype != 1) - { - g_warning("AudioControl interface descriptor type is %d, but expected 1 (HEADER)", (int)extra->hdr.bDescriptorSubtype); - return FALSE; - } - - printf("Device %04x:%04x\n", uai->udi->vid, uai->udi->pid); - printf("hdrlen=%d dt=%d dst=%d ver=%02x%02x total len=%d\n", extra->hdr.bLength, extra->hdr.bDescriptorType, extra->hdr.bDescriptorSubtype, extra->bcdADC[0], extra->bcdADC[1], wTotalLength); - for(int i = 0; i < extra->bInCollection; i++) - { - printf("interface nr %d = %d\n", i, extra->baInterfaceNr[i]); - } - - for(uint32_t pos = extra->hdr.bLength; pos < wTotalLength; pos += asdescr->extra[pos]) - { - const struct usb_ut_descriptor_header *hdr = (const struct usb_ut_descriptor_header *)(asdescr->extra + pos); - if (hdr->bDescriptorType != 36) - { - g_warning("Skipping unit/terminal descriptor type %d,%d", (int)hdr->bDescriptorType, (int)hdr->bDescriptorSubtype); - continue; - } - printf("hdr %d,%d len %d\n", hdr->bDescriptorType, hdr->bDescriptorSubtype, hdr->bLength); - switch(hdr->bDescriptorSubtype) - { - case 2: // INPUT_TERMINAL - { - const struct usbaudio_input_terminal_descriptor *itd = (const struct usbaudio_input_terminal_descriptor *)(hdr + 1); - int wTerminalType = itd->wTerminalTypeHi * 256 + itd->wTerminalTypeLo; - printf("INPUT TERMINAL %d: type %04x (%s)\n", (int)itd->bTerminalID, wTerminalType, get_terminal_type_name(wTerminalType)); - break; - } - case 3: // OUTPUT_TERMINAL - { - const struct usbaudio_output_terminal_descriptor *otd = (const struct usbaudio_output_terminal_descriptor *)(hdr + 1); - int wTerminalType = otd->wTerminalTypeHi * 256 + otd->wTerminalTypeLo; - printf("OUTPUT TERMINAL %d: type %04x (%s) source %d\n", (int)otd->bTerminalID, wTerminalType, get_terminal_type_name(wTerminalType), otd->bSourceID); - break; - } - case 4: // MIXER_UNIT - { - const struct usbaudio_mixer_unit_descriptor1 *mud = (const struct usbaudio_mixer_unit_descriptor1 *)(hdr + 1); - printf("MIXER UNIT %d\n", (int)mud->bUnitID); - for (int i = 0; i < mud->bNrInPins; i++) - { - printf("Input[%d] = %d\n", i, mud->baSourceID[i]); - } - break; - } - case 5: // SELECTOR_UNIT - { - const struct usbaudio_selector_unit_descriptor1 *sud = (const struct usbaudio_selector_unit_descriptor1 *)(hdr + 1); - printf("SELECTOR UNIT %d (%d pins)\n", (int)sud->bUnitID, sud->bNrInPins); - for (int i = 0; i < sud->bNrInPins; i++) - { - printf("Input[%d] = %d\n", i, sud->baSourceID[i]); - } - break; - } - case 6: // FEATURE_UNIT - { - static const char *features[] = {"Mute", "Volume", "Bass", "Mid", "Treble", "EQ", "AGC", "Delay", "Bass Boost", "Loudness" }; - const struct usbaudio_feature_unit_descriptor1 *fud = (const struct usbaudio_feature_unit_descriptor1 *)(hdr + 1); - printf("FEATURE UNIT %d (source = %d, control size %d)\n", (int)fud->bUnitID, fud->bSourceID, fud->bControlSize); - for (int i = 0; i < fud->bControlSize * 8; i++) - { - if (i >= sizeof(features) / sizeof(features[0])) - break; - if (fud->bmaControls[i >> 3] & (1 << (i & 7))) - printf("Master %s\n", features[i]); - } - for (int ch = 1; ch < (hdr->bLength - 7) / fud->bControlSize; ch++) - { - int chofs = ch * fud->bControlSize; - for (int i = 0; i < fud->bControlSize * 8; i++) - { - if (i >= sizeof(features) / sizeof(features[0])) - break; - if (fud->bmaControls[(i >> 3) + chofs] & (1 << (i & 7))) - printf("Channel %d %s\n", ch, features[i]); - } - } - break; - } - default: - printf("Unsupported unit type %d\n", hdr->bDescriptorSubtype); - break; - } - } -#endif - return FALSE; -} - -struct usbaudio_streaming_interface_descriptor_general -{ - struct usb_ut_descriptor_header hdr; // 7, 36, 1 - uint8_t bTerminalLink; - uint8_t bDelay; - uint8_t wFormatTagLo, wFormatTagHi; -}; - -struct usbaudio_streaming_interface_descriptor_format_type_pcm -{ - struct usb_ut_descriptor_header hdr; // 7, 36, 2 - uint8_t bFormatType; - uint8_t bNrChannels; - uint8_t bSubframeSize; - uint8_t bBitResolution; - uint8_t bSamFreqType; - uint8_t taSamFreq[0][3]; -}; - -static gboolean parse_audio_class(struct cbox_usb_io_impl *uii, struct cbox_usb_audio_info *uai, const struct libusb_interface_descriptor *asdescr, gboolean other_config) -{ - // omit alternate setting 0, as it's used to describe a 'standby' setting of the interface (no bandwidth) - if (asdescr->bAlternateSetting == 0) - return FALSE; - if (asdescr->extra_length < 7) - { - g_warning("AudioStreaming interface descriptor length is %d, should be at least 7", (int)asdescr->extra_length); - return FALSE; - } - const struct usbaudio_streaming_interface_descriptor_general *extra = (struct usbaudio_streaming_interface_descriptor_general *)asdescr->extra; - if (extra->hdr.bLength != 7 || extra->hdr.bDescriptorType != 36 || extra->hdr.bDescriptorSubtype != 1) - { - g_warning("The AudioStreaming descriptor does not start with the general descriptor (len %d, type %d, subtype %d)", (int)extra->hdr.bLength, (int)extra->hdr.bDescriptorType, (int)extra->hdr.bDescriptorSubtype); - return FALSE; - } - if (extra->wFormatTagLo != 1 || extra->wFormatTagHi != 0) - { - g_warning("The AudioStreaming descriptor does not describe a PCM device"); - return FALSE; - } - const struct usbaudio_streaming_interface_descriptor_format_type_pcm *fmt = (struct usbaudio_streaming_interface_descriptor_format_type_pcm *)(asdescr->extra + extra->hdr.bLength); - if (fmt->hdr.bLength < 11 || ((fmt->hdr.bLength - 11) % 3) || fmt->hdr.bDescriptorType != 36 || fmt->hdr.bDescriptorSubtype != 2) - { - g_warning("The AudioStreaming descriptor does not have a format type descriptor after general descriptor (len %d, type %d, subtype %d)", (int)fmt->hdr.bLength, (int)fmt->hdr.bDescriptorType, (int)fmt->hdr.bDescriptorSubtype); - return FALSE; - } - - // We need this to tell inputs from outputs - until I implement reasonable - // Audio Control support - const struct libusb_endpoint_descriptor *sync_ep = NULL; - const struct libusb_endpoint_descriptor *ep = get_audio_output_endpoint(asdescr, &sync_ep); - if (ep) - { - if (fmt->bBitResolution != uii->output_resolution * 8) - { - g_warning("Interface %d alternate setting %d does not support %d bit resolution, only %d", asdescr->bInterfaceNumber, asdescr->bAlternateSetting, uii->output_resolution * 8, fmt->bBitResolution); - return FALSE; - } - if (fmt->bSamFreqType) - { - gboolean found = FALSE; - for (int i = 0; i < fmt->bSamFreqType; i++) - { - int sf = fmt->taSamFreq[i][0] + 256 * fmt->taSamFreq[i][1] + 65536 * fmt->taSamFreq[i][2]; - if (sf == uii->sample_rate) - { - found = TRUE; - break; - } - } - if (!found) - { - g_warning("Interface %d alternate setting %d does not support sample rate of %d Hz", asdescr->bInterfaceNumber, asdescr->bAlternateSetting, uii->sample_rate); - for (int i = 0; i < fmt->bSamFreqType; i++) - { - int sf = fmt->taSamFreq[i][0] + 256 * fmt->taSamFreq[i][1] + 65536 * fmt->taSamFreq[i][2]; - g_warning("Sample rate[%d] = %d Hz", i, sf); - } - return FALSE; - } - } - // XXXKF in case of continuous sample rate, check the limits... assuming - // that there are any devices with continuous sample rate, that is. - if (other_config) - return TRUE; - if (uai->epdesc.found) - return FALSE; - if (sync_ep) - g_warning("Interface %d alt-setting %d endpoint %02x (synched via %02x) looks promising", asdescr->bInterfaceNumber, asdescr->bAlternateSetting, ep->bEndpointAddress, sync_ep->bEndpointAddress); - else - g_warning("Interface %d alt-setting %d endpoint %02x looks promising", asdescr->bInterfaceNumber, asdescr->bAlternateSetting, ep->bEndpointAddress); - uai->intf = asdescr->bInterfaceNumber; - uai->alt_setting = asdescr->bAlternateSetting; - fill_endpoint_desc(&uai->epdesc, ep); - uai->sync_protocol = (sync_ep != NULL) ? USBAUDIOSYNC_PROTOCOL_CLASS : USBAUDIOSYNC_PROTOCOL_NONE; - if (sync_ep) - fill_endpoint_desc(&uai->sync_epdesc, sync_ep); - else - uai->sync_epdesc.found = FALSE; - return TRUE; - } - return FALSE; -} - -static gboolean parse_midi_class(struct cbox_usb_midi_info *umi, const struct libusb_interface_descriptor *asdescr, gboolean other_config, gboolean is_output) -{ - const struct libusb_endpoint_descriptor *ep = get_midi_endpoint(asdescr, is_output); - if (!ep) - return FALSE; - - if (other_config) - return TRUE; - - struct usbio_endpoint_descriptor *epd = is_output ? &umi->epdesc_out : &umi->epdesc_in; - - if (epd->found) - return FALSE; - umi->intf = asdescr->bInterfaceNumber; - umi->alt_setting = asdescr->bAlternateSetting; - fill_endpoint_desc(epd, ep); - return TRUE; -} - -static gboolean inspect_device(struct cbox_usb_io_impl *uii, struct libusb_device *dev, uint16_t busdevadr, gboolean probe_only) -{ - struct libusb_device_descriptor dev_descr; - int bus = busdevadr >> 8; - int devadr = busdevadr & 255; - - if (0 != libusb_get_device_descriptor(dev, &dev_descr)) - { - g_warning("USB device %03d:%03d - cannot get device descriptor (will retry)", bus, devadr); - return FALSE; - } - - struct cbox_usb_device_info *udi = g_hash_table_lookup(uii->device_table, GINT_TO_POINTER(busdevadr)); - if (!udi) - { - struct libusb_config_descriptor *cfg_descr = NULL; - if (0 != libusb_get_active_config_descriptor(dev, &cfg_descr)) - return FALSE; - udi = malloc(sizeof(struct cbox_usb_device_info)); - udi->dev = dev; - udi->handle = NULL; - udi->status = CBOX_DEVICE_STATUS_PROBING; - udi->active_config = cfg_descr->bConfigurationValue; - udi->bus = bus; - udi->devadr = devadr; - udi->busdevadr = busdevadr; - udi->vid = dev_descr.idVendor; - udi->pid = dev_descr.idProduct; - udi->configs_with_midi = 0; - udi->configs_with_audio = 0; - udi->is_midi = FALSE; - udi->is_audio = FALSE; - udi->last_probe_time = time(NULL); - udi->failures = 0; - g_hash_table_insert(uii->device_table, GINT_TO_POINTER(busdevadr), udi); - libusb_free_config_descriptor(cfg_descr); - } - else - if (udi->vid == dev_descr.idVendor && udi->pid == dev_descr.idProduct) - { - // device already open or determined to be - if (udi->status == CBOX_DEVICE_STATUS_OPENED || - udi->status == CBOX_DEVICE_STATUS_UNSUPPORTED) - return FALSE; - // give up after 10 attempts to query or open the device - if (udi->failures > 10) - return FALSE; - // only do ~1 attempt per second - if (probe_only && time(NULL) == udi->last_probe_time) - return FALSE; - udi->last_probe_time = time(NULL); - } - - struct cbox_usb_audio_info uainf; - cbox_usb_audio_info_init(&uainf, udi); - - struct cbox_usb_midi_info uminf; - cbox_usb_midi_info_init(&uminf, udi); - - gboolean is_audio = FALSE; - - // printf("%03d:%03d Device %04X:%04X\n", bus, devadr, dev_descr.idVendor, dev_descr.idProduct); - for (int ci = 0; ci < (int)dev_descr.bNumConfigurations; ci++) - { - struct libusb_config_descriptor *cfg_descr = NULL; - // if this is not the current config, and another config with MIDI input - // has already been found, do not look any further - if (0 != libusb_get_config_descriptor(dev, ci, &cfg_descr)) - { - udi->failures++; - g_warning("%03d:%03d - cannot get configuration descriptor (try %d)", bus, devadr, udi->failures); - return FALSE; - } - - int cur_config = cfg_descr->bConfigurationValue; - gboolean other_config = cur_config != udi->active_config; - uint32_t config_mask = 0; - // XXXKF not sure about legal range for bConfigurationValue - if(cfg_descr->bConfigurationValue >= 0 && cfg_descr->bConfigurationValue < 32) - config_mask = 1 << cfg_descr->bConfigurationValue; - else - g_warning("Unexpected configuration value %d", cfg_descr->bConfigurationValue); - - for (int ii = 0; ii < cfg_descr->bNumInterfaces; ii++) - { - const struct libusb_interface *idescr = &cfg_descr->interface[ii]; - for (int as = 0; as < idescr->num_altsetting; as++) - { - const struct libusb_interface_descriptor *asdescr = &idescr->altsetting[as]; - if (asdescr->bInterfaceClass == LIBUSB_CLASS_AUDIO && asdescr->bInterfaceSubClass == 1) // Audio control - { - if (parse_audio_control_class(&uainf, asdescr, other_config) && other_config) - udi->configs_with_audio |= config_mask; - } - else if (asdescr->bInterfaceClass == LIBUSB_CLASS_AUDIO && asdescr->bInterfaceSubClass == 2) // Audio streaming - { - if (parse_audio_class(uii, &uainf, asdescr, other_config) && other_config) - udi->configs_with_audio |= config_mask; - } - else if (asdescr->bInterfaceClass == LIBUSB_CLASS_AUDIO && asdescr->bInterfaceSubClass == 3) // MIDI streaming - { - if (parse_midi_class(&uminf, asdescr, other_config, FALSE) && other_config) - udi->configs_with_midi |= config_mask; - if (parse_midi_class(&uminf, asdescr, other_config, TRUE) && other_config) - udi->configs_with_midi |= config_mask; - uminf.protocol = USBMIDI_PROTOCOL_CLASS; - } - else if (udi->vid == 0x09e8 && udi->pid == 0x0062) // Akai MPD16 - { - uminf.intf = asdescr->bInterfaceNumber; - uminf.alt_setting = asdescr->bAlternateSetting; - uminf.protocol = USBMIDI_PROTOCOL_MPD16; - fill_endpoint_by_address(asdescr, 0x82, &uminf.epdesc_in); - } - else if (udi->vid == 0x1235 && udi->pid == 0x000a) // Novation Nocturn - { - uminf.intf = asdescr->bInterfaceNumber; - uminf.alt_setting = asdescr->bAlternateSetting; - fill_endpoint_by_address(asdescr, 0x81, &uminf.epdesc_in); - fill_endpoint_by_address(asdescr, 0x02, &uminf.epdesc_out); - uminf.epdesc_in.interrupt = TRUE; - uminf.epdesc_out.interrupt = TRUE; - uminf.protocol = USBMIDI_PROTOCOL_NOCTURN; - } - } - } - libusb_free_config_descriptor(cfg_descr); - } - if (!uminf.epdesc_in.found && !uminf.epdesc_out.found && udi->configs_with_midi) - g_warning("%03d:%03d - MIDI port available on different configs: mask=0x%x", bus, devadr, udi->configs_with_midi); - - if (uainf.epdesc.found) // Class-compliant USB audio device - is_audio = TRUE; - if (udi->vid == 0x13b2 && udi->pid == 0x0030) // Alesis Multimix 8 - { - uainf.sync_protocol = USBAUDIOSYNC_PROTOCOL_MULTIMIX8; // not used later - is_audio = TRUE; - } - - // All configs/interfaces/alts scanned, nothing interesting found -> mark as unsupported - udi->is_midi = uminf.epdesc_in.found || uminf.epdesc_out.found; - udi->is_audio = is_audio; - if (!udi->is_midi && !udi->is_audio) - { - udi->status = CBOX_DEVICE_STATUS_UNSUPPORTED; - return FALSE; - } - - gboolean opened = FALSE; - struct libusb_device_handle *handle = NULL; - int err = libusb_open(dev, &handle); - if (0 != err) - { - g_warning("Cannot open device %03d:%03d: %s; errno = %s", bus, devadr, libusb_error_name(err), strerror(errno)); - udi->failures++; - return FALSE; - } - - if (probe_only) - { - libusb_close(handle); - // Make sure that the reconnection code doesn't bail out due to - // last_probe_time == now. - udi->last_probe_time = 0; - return udi->is_midi || udi->is_audio; - } - - if (uminf.epdesc_in.found || uminf.epdesc_out.found) - { - g_debug("Found MIDI device %03d:%03d, trying to open", bus, devadr); - if (0 != usbio_open_midi_interface(uii, &uminf, handle)) - opened = TRUE; - } - if (uainf.epdesc.found) - { - GError *error = NULL; - if (usbio_open_audio_interface(uii, &uainf, handle, &error)) - { - // should have already been marked as opened by the MIDI code, but - // I might add the ability to disable some MIDI interfaces at some point - udi->status = CBOX_DEVICE_STATUS_OPENED; - opened = TRUE; - } - else - { - g_warning("Cannot open class-compliant USB audio output: %s", error->message); - g_error_free(error); - } - } - else if (udi->vid == 0x13b2 && udi->pid == 0x0030) - { - GError *error = NULL; - if (usbio_open_audio_interface_multimix(uii, bus, devadr, handle, &error)) - { - // should have already been marked as opened by the MIDI code, but - // I might add the ability to disable some MIDI interfaces at some point - udi->status = CBOX_DEVICE_STATUS_OPENED; - opened = TRUE; - } - else - { - g_warning("Cannot open Alesis Multimix audio output: %s", error->message); - g_error_free(error); - } - } - - if (!opened) - { - udi->failures++; - libusb_close(handle); - } - else - udi->handle = handle; - - return opened; -} - -gboolean usbio_scan_devices(struct cbox_usb_io_impl *uii, gboolean probe_only) -{ - struct libusb_device **dev_list; - size_t i, num_devices; - gboolean added = FALSE; - gboolean removed = FALSE; - - num_devices = libusb_get_device_list(probe_only ? uii->usbctx_probe : uii->usbctx, &dev_list); - - uint16_t *busdevadrs = malloc(sizeof(uint16_t) * num_devices); - for (i = 0; i < num_devices; i++) - { - struct libusb_device *dev = dev_list[i]; - int bus = libusb_get_bus_number(dev); - int devadr = libusb_get_device_address(dev); - busdevadrs[i] = (bus << 8) | devadr; - } - - GList *prev_keys = g_hash_table_get_values(uii->device_table); - for (GList *p = prev_keys; p; p = p->next) - { - gboolean found = FALSE; - struct cbox_usb_device_info *udi = p->data; - for (i = 0; !found && i < num_devices; i++) - found = busdevadrs[i] == udi->busdevadr; - if (!found) - { - // Only specifically trigger removal if the device is ours - if (udi->status == CBOX_DEVICE_STATUS_OPENED) - { - g_message("Disconnected: %03d:%03d (%s)", udi->bus, udi->devadr, probe_only ? "probe" : "reconfigure"); - removed = TRUE; - } - if (!probe_only) - usbio_forget_device(uii, udi); - } - } - g_list_free(prev_keys); - - for (i = 0; i < num_devices; i++) - added = inspect_device(uii, dev_list[i], busdevadrs[i], probe_only) || added; - - free(busdevadrs); - libusb_free_device_list(dev_list, 0); - return added || removed; -} - -void usbio_forget_device(struct cbox_usb_io_impl *uii, struct cbox_usb_device_info *devinfo) -{ - g_hash_table_remove(uii->device_table, GINT_TO_POINTER(devinfo->busdevadr)); - for (GList *p = uii->midi_ports, *pNext = NULL; p; p = pNext) - { - pNext = p->next; - struct cbox_usb_midi_interface *umi = p->data; - if (umi->busdevadr == devinfo->busdevadr) - { - uii->midi_ports = g_list_delete_link(uii->midi_ports, p); - free(umi); - } - } - if (uii->handle_audiodev == devinfo->handle) - uii->handle_audiodev = NULL; - libusb_close(devinfo->handle); - free(devinfo); -} - -#endif \ No newline at end of file diff --git a/template/calfbox/wavebank.c b/template/calfbox/wavebank.c deleted file mode 100644 index 949224d..0000000 --- a/template/calfbox/wavebank.c +++ /dev/null @@ -1,545 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "cmd.h" -#include "config.h" -#include "config-api.h" -#include "dspmath.h" -#include "errors.h" -#include "tarfile.h" -#include "wavebank.h" -#include -#include -#include -#include -#include -#include - -#define STD_WAVEFORM_FRAMES 1024 -#define STD_WAVEFORM_BITS 10 - -/////////////////////////////////////////////////////////////////////////////// - -// Sine table -static complex float euler_table[STD_WAVEFORM_FRAMES]; - -// Bit reversal table -static int map_table[STD_WAVEFORM_FRAMES]; - -// Initialise tables using for FFT -static void init_tables(void) -{ - int rev = 1 << (STD_WAVEFORM_BITS - 1); - for (int i = 0; i < STD_WAVEFORM_FRAMES; i++) - { - euler_table[i] = cos(i * 2 * M_PI / STD_WAVEFORM_FRAMES) + I * sin(i * 2 * M_PI / STD_WAVEFORM_FRAMES); - int ni = 0; - for (int j = 0; j < STD_WAVEFORM_BITS; j++) - { - if (i & (rev >> j)) - ni = ni | (1 << j); - } - map_table[i] = ni; - } -} - -// Trivial implementation of Cooley-Tukey, only works for even values of ANALYSIS_BUFFER_BITS -static void my_fft_main(complex float output[STD_WAVEFORM_FRAMES]) -{ - complex float temp[STD_WAVEFORM_FRAMES]; - - for (int i = 0; i < STD_WAVEFORM_BITS; i++) - { - complex float *src = (i & 1) ? temp : output; - complex float *dst = (i & 1) ? output : temp; - int invi = STD_WAVEFORM_BITS - i - 1; - int disp = 1 << i; - int mask = disp - 1; - - for (int j = 0; j < STD_WAVEFORM_FRAMES / 2; j++) - { - int jj1 = (j & mask) + ((j & ~mask) << 1); // insert 0 at i'th bit to get the left arm of the butterfly - int jj2 = jj1 + disp; // insert 1 at i'th bit to get the right arm - assert((jj1 + disp) == (jj1 | disp)); - - // e^iw - complex float eiw1 = euler_table[(jj1 << invi) & (STD_WAVEFORM_FRAMES - 1)]; - complex float eiw2 = euler_table[(jj2 << invi) & (STD_WAVEFORM_FRAMES - 1)]; - - // printf("%d -> %d, %d\n", j, jj, jj + disp); - butterfly(&dst[jj1], &dst[jj2], src[jj1], src[jj2], eiw1, eiw2); - } - } -} - -static void my_fft_r2c(complex float output[STD_WAVEFORM_FRAMES], int16_t input[STD_WAVEFORM_FRAMES]) -{ - assert(!(STD_WAVEFORM_BITS&1)); - // Copy + bit reversal addressing - for (int i = 0; i < STD_WAVEFORM_FRAMES; i++) - output[i] = input[map_table[i]] * (1.0 / STD_WAVEFORM_FRAMES); - - my_fft_main(output); - -} - -static void my_ifft_c2r(int16_t output[STD_WAVEFORM_FRAMES], complex float input[STD_WAVEFORM_FRAMES]) -{ - complex float temp2[STD_WAVEFORM_FRAMES]; - for (int i = 0; i < STD_WAVEFORM_FRAMES; i++) - temp2[i] = input[map_table[i]]; - assert(!(STD_WAVEFORM_BITS&1)); - - my_fft_main(temp2); - // Copy + bit reversal addressing - float maxv = 0; - for (int i = 0; i < STD_WAVEFORM_FRAMES; i++) - { - float value = creal(temp2[i]); - if (value < -32768) value = -32768; - if (value > 32767) value = 32767; - if (fabs(value) > maxv) - maxv = fabs(value); - output[i] = (int16_t)value; - } -} - -struct wave_bank -{ - int64_t bytes, maxbytes, serial_no; - GHashTable *waveforms_by_name, *waveforms_by_id; - GSList *std_waveforms; - uint32_t streaming_prefetch_size; -}; - -static struct wave_bank bank; - -GQuark cbox_waveform_error_quark(void) -{ - return g_quark_from_string("cbox-waveform-error-quark"); -} - -float func_sine(float v, void *user_data) -{ - return sin(2 * M_PI * v); -} - -float func_silence(float v, void *user_data) -{ - return 0.f; -} - -float func_sqr(float v, void *user_data) -{ - return v < 0.5 ? -1 : 1; -} - -float func_saw(float v, void *user_data) -{ - return 2 * v - 1; -} - -float func_tri(float v, void *user_data) -{ - if (v <= 0.25f) - return v * 4; - if (v <= 0.75f) - return 1 - (v - 0.25f) * 4; - return -1 + 4 * (v - 0.75f); -} - -void cbox_waveform_generate_levels(struct cbox_waveform *waveform, int levels, double ratio) -{ - complex float output[STD_WAVEFORM_FRAMES], bandlimited[STD_WAVEFORM_FRAMES]; - my_fft_r2c(output, waveform->data); - int N = STD_WAVEFORM_FRAMES; - - waveform->levels = calloc(levels, sizeof(struct cbox_waveform_level)); - double rate = 65536.0 * 65536.0; // / waveform->info.frames; - double orig_rate = 65536.0 * 65536.0; // / waveform->info.frames; - for (int i = 0; i < levels; i++) - { - int harmonics = N / 2 / (rate / orig_rate); - bandlimited[0] = 0; - - if (harmonics > 0) - { - for (int j = 1; j <= harmonics; j++) - { - bandlimited[j] = output[j]; - bandlimited[N - j] = output[N - j]; - } - for (int j = harmonics; j <= N / 2; j++) - bandlimited[j] = bandlimited [N - j] = 0; - } - - waveform->levels[i].data = calloc(N + MAX_INTERPOLATION_ORDER, sizeof(int16_t)); - my_ifft_c2r(waveform->levels[i].data, bandlimited); - memcpy(waveform->levels[i].data + N, waveform->levels[i].data, MAX_INTERPOLATION_ORDER * sizeof(int16_t)); - waveform->levels[i].max_rate = (uint64_t)(rate); - rate *= ratio; - } - waveform->level_count = levels; -} - -void cbox_wavebank_add_std_waveform(const char *name, float (*getfunc)(float v, void *user_data), void *user_data, int levels) -{ - int nsize = STD_WAVEFORM_FRAMES; - int16_t *wave = calloc(nsize, sizeof(int16_t)); - for (int i = 0; i < nsize; i++) - { - float v = getfunc(i * 1.0 / nsize, user_data); - if (fabs(v) > 1) - v = (v < 0) ? -1 : 1; - // cannot use full scale here, because bandlimiting will introduce - // some degree of overshoot - wave[i] = (int16_t)(25000 * v); - } - struct cbox_waveform *waveform = calloc(1, sizeof(struct cbox_waveform)); - waveform->data = wave; - waveform->info.channels = 1; - waveform->preloaded_frames = waveform->info.frames = nsize; - waveform->info.samplerate = (int)(nsize * 261.6255); - waveform->id = ++bank.serial_no; - waveform->bytes = waveform->info.channels * 2 * (waveform->info.frames + 1); - waveform->refcount = 1; - waveform->canonical_name = g_strdup(name); - waveform->display_name = g_strdup(name); - waveform->has_loop = TRUE; - waveform->loop_start = 0; - waveform->loop_end = nsize; - waveform->levels = NULL; - waveform->level_count = 0; - - if (levels) - cbox_waveform_generate_levels(waveform, levels, 2); - - g_hash_table_insert(bank.waveforms_by_name, waveform->canonical_name, waveform); - g_hash_table_insert(bank.waveforms_by_id, &waveform->id, waveform); - bank.std_waveforms = g_slist_prepend(bank.std_waveforms, waveform); - // These waveforms are not included in the bank size, I don't think it has - // much value for the user. -} - -void cbox_wavebank_init() -{ - init_tables(); - - bank.bytes = 0; - bank.maxbytes = 0; - bank.serial_no = 0; - bank.waveforms_by_name = g_hash_table_new(g_str_hash, g_str_equal); - bank.waveforms_by_id = g_hash_table_new(g_int_hash, g_int_equal); - bank.std_waveforms = NULL; - bank.streaming_prefetch_size = cbox_config_get_int("streaming", "prefetch_size", 65536); - - cbox_wavebank_add_std_waveform("*sine", func_sine, NULL, 0); - // XXXKF this should not be a real waveform - cbox_wavebank_add_std_waveform("*silence", func_silence, NULL, 0); - cbox_wavebank_add_std_waveform("*saw", func_saw, NULL, 11); - cbox_wavebank_add_std_waveform("*square", func_sqr, NULL, 11); - cbox_wavebank_add_std_waveform("*triangle", func_tri, NULL, 11); -} - -struct cbox_waveform *cbox_wavebank_get_waveform(const char *context_name, struct cbox_tarfile *tarfile, const char *sample_dir, const char *filename, GError **error) -{ - if (!filename) - { - g_set_error(error, CBOX_WAVEFORM_ERROR, CBOX_WAVEFORM_ERROR_FAILED, "%s: no filename specified", context_name); - return NULL; - } - - // Built in waveforms don't go through path canonicalization - if (filename[0] == '*') - { - if (!strcmp(filename + 1, "sqr")) - filename = "*square"; - else if (!strcmp(filename + 1, "tri")) - filename = "*triangle"; - gpointer value = g_hash_table_lookup(bank.waveforms_by_name, filename); - if (value) - { - struct cbox_waveform *waveform = value; - cbox_waveform_ref(waveform); - return waveform; - } - } - - gchar *value_copy = g_strdup(filename); - for (int i = 0; value_copy[i]; i++) - { - if (value_copy[i] == '\\') - value_copy[i] = '/'; - } - gchar *pathname = value_copy[0] == '/' ? g_strdup(value_copy) : g_build_filename(sample_dir, value_copy, NULL); - g_free(value_copy); - - char *canonical = NULL; - if (tarfile) - canonical = g_strdup_printf("sbtar:%s;%s", tarfile->file_pathname, pathname); - else - { - // make sure canonical is always allocated on the same (glib) heap - char *p = realpath(pathname, NULL); - if (p) - { - canonical = g_strdup(p); - free(p); - } - } - if (!canonical) - { - g_set_error(error, CBOX_WAVEFORM_ERROR, CBOX_WAVEFORM_ERROR_FAILED, "%s: cannot find a real path for '%s': %s", context_name, pathname, strerror(errno)); - g_free(pathname); - return NULL; - } - gpointer value = g_hash_table_lookup(bank.waveforms_by_name, canonical); - if (value) - { - g_free(pathname); - g_free(canonical); - - struct cbox_waveform *waveform = value; - cbox_waveform_ref(waveform); - return waveform; - } - - struct cbox_waveform *waveform = calloc(1, sizeof(struct cbox_waveform)); - SNDFILE *sndfile = NULL; - struct cbox_taritem *taritem = NULL; - if (tarfile) - { - taritem = cbox_tarfile_get_item_by_name(tarfile, pathname, TRUE); - if (taritem) - sndfile = cbox_tarfile_opensndfile(tarfile, taritem, &waveform->sndstream, &waveform->info); - } - else - sndfile = sf_open(pathname, SFM_READ, &waveform->info); - if (!sndfile) - { - g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "%s: cannot open '%s'", context_name, pathname); - g_free(pathname); - g_free(canonical); - free(waveform); - return NULL; - } - - SF_INSTRUMENT instrument; - uint32_t nshorts; - if (waveform->info.channels != 1 && waveform->info.channels != 2) - { - g_set_error(error, CBOX_WAVEFORM_ERROR, CBOX_WAVEFORM_ERROR_FAILED, - "%s: cannot open file '%s': unsupported channel count %d", context_name, pathname, (int)waveform->info.channels); - sf_close(sndfile); - free(canonical); - g_free(pathname); - return NULL; - } - g_free(pathname); - uint32_t preloaded_frames = waveform->info.frames; - // If sample is larger than 2x prefetch buffer size, then load only - // a prefetch buffer worth of data, and stream the rest. - if (preloaded_frames > 2 * bank.streaming_prefetch_size) - preloaded_frames = bank.streaming_prefetch_size; - waveform->id = ++bank.serial_no; - waveform->bytes = waveform->info.channels * 2 * preloaded_frames; - waveform->data = malloc(waveform->bytes); - waveform->refcount = 1; - waveform->canonical_name = canonical; - waveform->display_name = g_filename_display_name(canonical); - waveform->has_loop = FALSE; - waveform->levels = NULL; - waveform->level_count = 0; - waveform->preloaded_frames = preloaded_frames; - waveform->tarfile = tarfile; - waveform->taritem = taritem; - - if (sf_command(sndfile, SFC_GET_INSTRUMENT, &instrument, sizeof(SF_INSTRUMENT))) - { - for (int i = 0; i < instrument.loop_count; i++) - { - if (instrument.loops[i].mode == SF_LOOP_FORWARD) - { - waveform->loop_start = instrument.loops[i].start; - waveform->loop_end = instrument.loops[i].end; - waveform->has_loop = TRUE; - break; - } - } - } - - nshorts = waveform->info.channels * preloaded_frames; - for (uint32_t i = 0; i < nshorts; i++) - waveform->data[i] = 0; - sf_readf_short(sndfile, waveform->data, preloaded_frames); - sf_close(sndfile); - bank.bytes += waveform->bytes; - if (bank.bytes > bank.maxbytes) - bank.maxbytes = bank.bytes; - g_hash_table_insert(bank.waveforms_by_name, waveform->canonical_name, waveform); - g_hash_table_insert(bank.waveforms_by_id, &waveform->id, waveform); - - return waveform; -} - -int64_t cbox_wavebank_get_bytes() -{ - return bank.bytes; -} - -int64_t cbox_wavebank_get_maxbytes() -{ - return bank.maxbytes; -} - -int cbox_wavebank_get_count() -{ - return g_hash_table_size(bank.waveforms_by_id); -} - -struct cbox_waveform *cbox_wavebank_peek_waveform_by_id(int id) -{ - return g_hash_table_lookup(bank.waveforms_by_id, &id); -} - -void cbox_wavebank_foreach(void (*cb)(void *, struct cbox_waveform *), void *user_data) -{ - GHashTableIter iter; - gpointer key, value; - - g_hash_table_iter_init (&iter, bank.waveforms_by_id); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - (*cb)(user_data, value); - } -} - -void cbox_wavebank_close() -{ - if (bank.bytes > 0) - g_warning("Warning: %lld bytes in unfreed samples", (long long int)bank.bytes); - while(bank.std_waveforms) - { - cbox_waveform_unref((struct cbox_waveform *)bank.std_waveforms->data); - bank.std_waveforms = g_slist_delete_link(bank.std_waveforms, bank.std_waveforms); - } - g_hash_table_destroy(bank.waveforms_by_id); - g_hash_table_destroy(bank.waveforms_by_name); - bank.waveforms_by_id = NULL; - bank.waveforms_by_name = NULL; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////// - -void cbox_waveform_ref(struct cbox_waveform *waveform) -{ - ++waveform->refcount; -} - -void cbox_waveform_unref(struct cbox_waveform *waveform) -{ - if (--waveform->refcount > 0) - return; - - g_hash_table_remove(bank.waveforms_by_name, waveform->canonical_name); - g_hash_table_remove(bank.waveforms_by_id, &waveform->id); - bank.bytes -= waveform->bytes; - - g_free(waveform->display_name); - g_free(waveform->canonical_name); - for (int i = 0; i < waveform->level_count; i++) - free(waveform->levels[i].data); - free(waveform->levels); - free(waveform->data); - free(waveform); - -} - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -struct waves_foreach_data -{ - struct cbox_command_target *fb; - GError **error; - gboolean success; -}; - -void wave_list_cb(void *user_data, struct cbox_waveform *waveform) -{ - struct waves_foreach_data *wfd = user_data; - - wfd->success = wfd->success && cbox_execute_on(wfd->fb, NULL, "/waveform", "i", wfd->error, (int)waveform->id); -} - -static gboolean waves_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) -{ - if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - // XXXKF this only supports 4GB - not a big deal for now yet? - return cbox_execute_on(fb, NULL, "/bytes", "i", error, (int)cbox_wavebank_get_bytes()) && - cbox_execute_on(fb, NULL, "/max_bytes", "i", error, (int)cbox_wavebank_get_maxbytes()) && - cbox_execute_on(fb, NULL, "/count", "i", error, (int)cbox_wavebank_get_count()) - ; - } - else if (!strcmp(cmd->command, "/list") && !strcmp(cmd->arg_types, "")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - struct waves_foreach_data wfd = { fb, error, TRUE }; - cbox_wavebank_foreach(wave_list_cb, &wfd); - return wfd.success; - } - else if (!strcmp(cmd->command, "/info") && !strcmp(cmd->arg_types, "i")) - { - if (!cbox_check_fb_channel(fb, cmd->command, error)) - return FALSE; - - int id = CBOX_ARG_I(cmd, 0); - struct cbox_waveform *waveform = cbox_wavebank_peek_waveform_by_id(id); - if (waveform == NULL) - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Waveform %d not found", id); - return FALSE; - } - assert(id == waveform->id); - if (!cbox_execute_on(fb, NULL, "/filename", "s", error, waveform->canonical_name)) // XXXKF convert to utf8 - return FALSE; - if (!cbox_execute_on(fb, NULL, "/name", "s", error, waveform->display_name)) - return FALSE; - if (!cbox_execute_on(fb, NULL, "/bytes", "i", error, (int)waveform->bytes)) - return FALSE; - if (waveform->has_loop && !cbox_execute_on(fb, NULL, "/loop", "ii", error, (int)waveform->loop_start, (int)waveform->loop_end)) - return FALSE; - return TRUE; - } - else - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown combination of target path and argument: '%s', '%s'", cmd->command, cmd->arg_types); - return FALSE; - } -} - -struct cbox_command_target cbox_waves_cmd_target = -{ - .process_cmd = waves_process_cmd, - .user_data = NULL -}; diff --git a/template/calfbox/wavebank.h b/template/calfbox/wavebank.h deleted file mode 100644 index 62239d8..0000000 --- a/template/calfbox/wavebank.h +++ /dev/null @@ -1,77 +0,0 @@ -/* -Calf Box, an open source musical instrument. -Copyright (C) 2010-2011 Krzysztof Foltman - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#ifndef CBOX_WAVEBANK_H -#define CBOX_WAVEBANK_H - -#include -#include -#include - -#define MAX_INTERPOLATION_ORDER 3 - -#define CBOX_WAVEFORM_ERROR cbox_waveform_error_quark() - -enum CboxWaveformError -{ - CBOX_WAVEFORM_ERROR_FAILED, -}; - -struct cbox_waveform_level -{ - int16_t *data; - uint64_t max_rate; -}; - -struct cbox_waveform -{ - int16_t *data; - SF_INFO info; - int id; - int refcount; - size_t bytes; - size_t preloaded_frames; - gchar *canonical_name; - gchar *display_name; - gboolean has_loop; - uint32_t loop_start, loop_end; - struct cbox_tarfile *tarfile; - struct cbox_taritem *taritem; - struct cbox_tarfile_sndstream sndstream; - - struct cbox_waveform_level *levels; - int level_count; -}; - -extern struct cbox_command_target cbox_waves_cmd_target; - -extern void cbox_wavebank_init(void); -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); -extern struct cbox_waveform *cbox_wavebank_peek_waveform_by_id(int id); -extern void cbox_wavebank_foreach(void (*cb)(void *user_data, struct cbox_waveform *waveform), void *user_data); -extern void cbox_wavebank_add_std_waveform(const char *name, float (*getfunc)(float v, void *user_data), void *user_data, int levels); -extern int cbox_wavebank_get_count(void); -extern int64_t cbox_wavebank_get_bytes(void); -extern int64_t cbox_wavebank_get_maxbytes(void); -extern void cbox_wavebank_close(void); - -extern void cbox_waveform_ref(struct cbox_waveform *waveform); -extern void cbox_waveform_unref(struct cbox_waveform *waveform); - - -#endif diff --git a/template/documentation/readme.template b/template/documentation/readme.template index 7a1da05..c058d1f 100644 --- a/template/documentation/readme.template +++ b/template/documentation/readme.template @@ -1,7 +1,7 @@ [//]: # (Generated . Changes belong into template/documentation/readme.template) -# +# Program version @@ -120,3 +120,4 @@ Or even shorter: This is the minimal run mode which is only useful for testing and development. But if you only want to look at the GUI and are not in the mood to install anything -including dependencies-, go ahead. + diff --git a/template/engine/api.py b/template/engine/api.py index 3e8f91c..5af910f 100644 --- a/template/engine/api.py +++ b/template/engine/api.py @@ -28,7 +28,7 @@ import os.path from datetime import timedelta #Third Party Modules -from calfbox import cbox +from template.calfbox import cbox #Our own template modules from .session import Session @@ -505,5 +505,3 @@ session.history.apiCallback_historySequenceStarted = callbacks._historySequenceS session.history.apiCallback_historySequenceStopped = callbacks._historySequenceStopped #Import complete. Now the parent module, like a gui, will call startEngine() and provide an event loop. - - diff --git a/template/engine/duration.py b/template/engine/duration.py index f439de1..6e21d84 100644 --- a/template/engine/duration.py +++ b/template/engine/duration.py @@ -124,6 +124,8 @@ def jackBBTicksToDuration(beatTypeAsTraditionalNumber:int, jackTicks:int, jackBe if None in (beatTypeAsTraditionalNumber, jackTicks, jackBeatTicks): return None else: + if not beatTypeAsTraditionalNumber in traditionalNumberToBaseDuration: + raise ValueError(f"{beatTypeAsTraditionalNumber} must be one of {traditionalNumberToBaseDuration}") beatTypeDuration = traditionalNumberToBaseDuration[beatTypeAsTraditionalNumber] #print (beatTypeAsTraditionalNumber, beatTypeDuration, jackTicks, jackBeatTicks) factor = beatTypeDuration / jackBeatTicks diff --git a/template/engine/input_midi.py b/template/engine/input_midi.py index ade4b4b..fc7470a 100644 --- a/template/engine/input_midi.py +++ b/template/engine/input_midi.py @@ -25,7 +25,7 @@ import logging; logger = logging.getLogger(__name__); logger.info("import") #Python Standard Library #Third Party Modules -from calfbox import cbox +from template.calfbox import cbox #Template Modules from . import pitch @@ -260,4 +260,3 @@ class MidiProcessor(object): del self.callbacks2[(MidiProcessor.SIMPLE_EVENT, MidiProcessor.M_NOTE_ON)] except KeyError: pass - diff --git a/template/engine/ly2cbox.py b/template/engine/ly2cbox.py index a03192b..07d1222 100644 --- a/template/engine/ly2cbox.py +++ b/template/engine/ly2cbox.py @@ -29,7 +29,7 @@ import re from typing import Dict #Third Party Modules -from calfbox import cbox +from template.calfbox import cbox #Template Modules from . import pitch @@ -84,4 +84,3 @@ def pattern(lilypondString:str, channel, velocity:int=100): pattern = cbox.Document.get_song().pattern_from_blob(patternBlob, tickCounter) #return patternBlob, startTick return pattern - diff --git a/template/engine/metronome.py b/template/engine/metronome.py index 4052cf9..496ed40 100644 --- a/template/engine/metronome.py +++ b/template/engine/metronome.py @@ -28,7 +28,7 @@ from typing import Tuple import os.path #Third Party Modules -from calfbox import cbox +from template.calfbox import cbox #Our template modules from ..start import PATHS @@ -125,5 +125,3 @@ class Metronome(object): "enabled" : self.enabled, "label" : self.label, } - - diff --git a/template/engine/sampler_sf2.py b/template/engine/sampler_sf2.py index ec49133..b5f0d10 100644 --- a/template/engine/sampler_sf2.py +++ b/template/engine/sampler_sf2.py @@ -29,7 +29,7 @@ from os import PathLike from typing import List, Dict, Union #Third Party -from calfbox import cbox +from template.calfbox import cbox #Template Modules from .data import Data diff --git a/template/engine/sequencer.py b/template/engine/sequencer.py index 762bab1..b8ffee1 100644 --- a/template/engine/sequencer.py +++ b/template/engine/sequencer.py @@ -26,7 +26,7 @@ import logging; logger = logging.getLogger(__name__); logger.info("import") from typing import List, Dict, Tuple, Iterable, Union #Third Party Modules -from calfbox import cbox +from template.calfbox import cbox #Template Modules from .data import Data diff --git a/template/engine/session.py b/template/engine/session.py index f61cfb9..c963cd9 100644 --- a/template/engine/session.py +++ b/template/engine/session.py @@ -30,7 +30,7 @@ from sys import exit as sysexit import base64 #for jack metadata icon #Third Party Modules -from calfbox import cbox +from template.calfbox import cbox #Our Template Modules from .history import History diff --git a/template/qtgui/midiinquickwidget.py b/template/qtgui/midiinquickwidget.py index a17064d..9fe80e1 100644 --- a/template/qtgui/midiinquickwidget.py +++ b/template/qtgui/midiinquickwidget.py @@ -24,7 +24,7 @@ import logging; logging.info("import {}".format(__file__)) #Third Party Modules from PyQt5 import QtWidgets, QtCore, QtGui -from calfbox import cbox +from template.calfbox import cbox #Template Modules diff --git a/template/qtgui/submenus.py b/template/qtgui/submenus.py index 7437ce7..920c6d9 100644 --- a/template/qtgui/submenus.py +++ b/template/qtgui/submenus.py @@ -21,7 +21,7 @@ along with this program. If not, see . import logging; logger = logging.getLogger(__name__); logger.info("import") -from calfbox import cbox +from template.calfbox import cbox from typing import Iterable, Callable, Tuple from PyQt5 import QtCore, QtGui, QtWidgets diff --git a/template/start.py b/template/start.py index ad9e9e2..6303112 100644 --- a/template/start.py +++ b/template/start.py @@ -23,7 +23,7 @@ along with this program. If not, see . """ We use a 'wrong' scheme of importing modules here because there are multiple exit conditions, good and bad. -We don't want to use all the libraries, including the big Qt one, only to end up displaying the --version and exit. +We don't want to load all the libraries, including the big Qt one, only to end up displaying the --version and exit. Same with the tests if jack or nsm are running. """ @@ -101,8 +101,6 @@ except ModuleNotFoundError as e: logger.info("Compiled version: {}".format(compiledVersion)) -cboxSharedObjectVersionedName = "lib"+METADATA["shortName"]+".so." + METADATA["version"] # type: ignore -logger.info("Our calfbox extension cpython library should be named " + cboxSharedObjectVersionedName) #ZippApp with compiledprefix.py if compiledVersion: @@ -116,7 +114,6 @@ if compiledVersion: #"lib": os.path.join(prefix, "lib", METADATA["shortName"]), #cbox is found via the PYTHONPATH } - cboxSharedObjectPath = os.path.join(prefix, "lib", METADATA["shortName"], cboxSharedObjectVersionedName) # type: ignore _root = os.path.dirname(__file__) _root = os.path.abspath(os.path.join(_root, "..")) @@ -127,14 +124,6 @@ if compiledVersion: assert zipfile.is_zipfile(zipfilePath), (zipfilePath) #in our tests this worked. but in lss this results not in a zip file header. linux file also says it is no zip. However, unzip works. #Extract included .so to tmp dir, tmp dir gets garbage collected at the end of our program. libsharedDir = tempfile.TemporaryDirectory() - with zipfile.ZipFile(zipfilePath, mode="r") as ourzipappfile: - ourzipappfile.extract(f"sitepackages/{cboxSharedObjectVersionedName}", path=libsharedDir.name) - - sys.path.append(os.path.join(zipfilePath,"sitepackages")) - - cboxso = os.path.join(libsharedDir.name, f"sitepackages/{cboxSharedObjectVersionedName}") - logger.info(f"Shared library extracted to: {cboxso}") - os.environ["CALFBOXLIBABSPATH"] = cboxso #Not compiled, not installed. Running pure python directly in the source tree. else: @@ -150,17 +139,6 @@ else: #"lib": "", #use only system paths } - if os.path.exists (os.path.join(_root, "sitepackages", cboxSharedObjectVersionedName)): - os.environ["CALFBOXLIBABSPATH"] = os.path.join(_root, "sitepackages", cboxSharedObjectVersionedName) - #else use system-wide. - - if os.path.exists (os.path.join(_root, "sitepackages", "calfbox", "cbox.py")): - #add to the front to have higher priority than system sitepackages - logger.info("Will attempt to start with local calfbox python module: {}".format(os.path.join(_root, "sitepackages", "calfbox", "cbox.py"))) - sys.path.insert(0, os.path.join(os.path.join(_root, "sitepackages"))) - #else try to use system-wide calfbox. Check for this and if the .so exists at the end of this file. - - logger.info("PATHS: {}".format(PATHS)) #Construct QAppliction before constantsAndCOnfigs, which has the fontDB @@ -350,48 +328,20 @@ if args.mute: 4) impossible: run program installed, calfbox in local tree """ - if not compiledVersion: - import template.calfbox.py - sys.modules["calfbox"] = sys.modules["template.calfbox.py"] - - import calfbox.nullbox - - -#Make sure calfbox is available. -pycboxfound = False + #if not compiledVersion: + #import template.calfbox.py + # sys.modules["calfbox"] = sys.modules["template.calfbox.py"] -if "CALFBOXLIBABSPATH" in os.environ: - logger.info("Looking for calfbox shared library in absolute path: {}".format(os.environ["CALFBOXLIBABSPATH"])) + import template.calfbox.nullbox else: - logger.info("Looking for calfbox shared library systemwide through ctypes.util.find_library") - - -if compiledVersion and not args.mute: - try: - from sitepackages.calfbox import cbox - logger.info(f"Calbox Python module loaded: {os.path.abspath(cbox.__file__)}") - pycboxfound = True - except Exception as e: - print (e) -else: - try: - from calfbox import cbox - logger.info("{}: using cbox python module from {} . Local version has higher priority than system wide.".format(METADATA["name"], os.path.abspath(cbox.__file__))) - pycboxfound = True - except Exception as e: - print (e) - -if not pycboxfound: - print ("Here is some information. Please show this to the developers.") - print (sys.modules["calfbox"].__file__) - if "calfbox" in sys.modules and sys.modules["calfbox"].__file__: - print (sys.modules["calfbox"], "->", os.path.abspath(sys.modules["calfbox"].__file__)) + import ctypes.util + libname = ctypes.util.find_library("calfbox-lss") #returns something like 'libcalfbox-lss.so.1' without a path, but with "lib" + if libname: + logger.info(f"libcalfbox-lss name is {libname}") + os.environ["CALFBOXLIBFILENAME"] = "calfbox-lss" #let _cbox2 use ctypes to find the name and path again. We just need to tell it that we are not the standard cbox file name. else: - print ("calfbox python module is not in sys.modules. This means it truly can't be found or you forgot --mute") - - print ("sys.path start and tail:", sys.path[0:5], sys.path[-5:-1]) - exitWithMessage("Calfbox module could not be loaded.") - + logger.error("libcalfbox-lss not installed on system") + raise RuntimeError("libcalfbox-lss not installed on system") #Capture Ctlr+C / SIGINT and let @atexit handle the rest. import signal