From e24a41a4471e9135d186257098b0746c0e6a0199 Mon Sep 17 00:00:00 2001 From: jjceresa Date: Sat, 3 Mar 2018 14:22:51 +0100 Subject: [PATCH] Add Polymono Support (#306) This commit implements the polymono functionality as per MIDI spec. closes #306 resolves #158 Squashed commit of the following: commit b56e92b378f21b7fbc189e8c580d85e679c2ee3f Merge: 07f4c53 30c9dda Author: derselbst Date: Sat Mar 3 14:20:23 2018 +0100 Merge branch 'master' into polymono commit 07f4c53b9a0e46f511134d39d72073523fc3e202 Author: derselbst Date: Fri Mar 2 16:51:46 2018 +0100 update API docs for polymono commit 82c74ff07251e0fb34b15a066f501bf1e968a51c Merge: f07e515 ba2c053 Author: derselbst Date: Fri Mar 2 10:41:43 2018 +0100 Merge branch 'master' into polymono commit f07e515ebdeb3d075bd9307ebf8768a6ffaae3e3 Author: derselbst Date: Fri Mar 2 10:39:44 2018 +0100 clarify comment commit 7635825325a168f9ee383afd8023d82d4c33c185 Author: jjceresa Date: Thu Mar 1 04:13:52 2018 +0100 Enhancement of portamento performance calculation. - The lost of performance is now -0.5% rather -12%. commit 09cb1d3ec6ced215adec9faa09fac5cae6c0f8fc Author: jjceresa Date: Wed Feb 21 19:40:17 2018 +0100 moving polymono commands among synth commands commit 63b8c480226b15d5d5bee5de8e1722a93ff69493 Author: jjceresa Date: Wed Feb 21 03:16:10 2018 +0100 fixes comments, FLUID_INLINE, update pdf commit 49c1ee39d2ef525ac0af22ab22a4d8f8d49ab0e9 Merge: 200c860 4a0a736 Author: derselbst Date: Sun Feb 11 17:38:31 2018 +0100 Merge branch 'master' into polymono commit 200c8609582560d781c0d49b99a9d02bdb7d2a2a Author: jjceresa Date: Thu Feb 8 23:27:28 2018 +0100 Fixes typos, update /doc/polymono commit 9f6474cee712f947a0227957acbb4d1e4f56f2d6 Merge: cb7f43f 28b1bef Author: jjceresa Date: Wed Feb 7 00:44:52 2018 +0100 merge before updating pdf document. commit cb7f43feb56edeed016b0b3ad7293fbd64beb7ae Author: jjceresa Date: Wed Feb 7 00:40:52 2018 +0100 fixes typos in pdf document commit 28b1bef97a498ea5e15fdffb57b861998642bd5a Author: derselbst Date: Tue Feb 6 23:02:05 2018 +0100 avoid type redefinition warning commit 551940854053ccf0ff0d4cfd21d0e6328a9e657c Author: jjceresa Date: Tue Feb 6 18:58:16 2018 +0100 cleanup fluid_synth.c commit 263f9badcb7f75bf8027df60abb20b3354ea67e2 Author: derselbst Date: Tue Feb 6 16:36:55 2018 +0100 cleanup fluid_synth.h commit fd58bc0c22756b67e75c80ed8efecfed9baf2901 Author: derselbst Date: Tue Feb 6 16:30:46 2018 +0100 move synth API macros back to fluid_synth.c commit 101ea0961a18c4724d7bd7f4d47f5a7fc27bb679 Author: derselbst Date: Tue Feb 6 16:17:15 2018 +0100 rename fluid_synth_mono.c to fluid_synth_monopoly.c commit ec60840304fdebf4d854a0879045249e9a90af79 Author: derselbst Date: Tue Feb 6 16:05:06 2018 +0100 move fluid_synth_polymono functions back to fluid_synth.c commit b18e99d34fcfef1444f235bed4c866985433f072 Author: derselbst Date: Tue Feb 6 15:43:18 2018 +0100 explicitly check for default_vel2att_mod commit ceeba2af7824c3e12f6a5302d79d4e582732141d Author: jjceresa Date: Sun Feb 4 22:30:17 2018 +0100 Using INVALID_NOTE instead of -1 - polishing fluid_synth_noteon_monopoly_legato(). - Adds chapter "introducing Poly/mono basic channels API" (2.6.1) in pdf. commit 04d1a2119ad4847797c9cfc848a4158c6b0b3ce8 Author: derselbst Date: Sun Feb 4 19:49:25 2018 +0100 const correctness commit 7583bc24ad21df5e01b73b685f50e8a2eef3b2f3 Author: derselbst Date: Sun Feb 4 19:45:05 2018 +0100 fix naming convention commit a8e109f9f1ec3a55b6e0b9bfd9163e6dd4ea9e09 Author: jjceresa Date: Sun Feb 4 01:39:58 2018 +0100 fixes comments, update pdf commit 1f33394a6579f9ee7341246df3e110db6926abeb Author: derselbst Date: Sat Feb 3 17:19:33 2018 +0100 remove duplicate comments commit e9451695aafece991af74ee2cfb9368d256c728a Author: derselbst Date: Sat Feb 3 17:06:09 2018 +0100 specify enum types explicitly commit f810f1942eaf19447ed29487209e227d7c4f8bd6 Author: jjceresa Date: Fri Feb 2 18:54:03 2018 +0100 fix construction of new_mode - cleanup comment commit 3108da38ecc877e2f7428db0a988e1f05d324743 Author: derselbst Date: Fri Feb 2 11:42:44 2018 +0100 cleanup fluid_synth_cc_LOCAL() commit d61eea6aeff75cfb051e4baf546ff26073c2e62b Author: derselbst Date: Fri Feb 2 11:18:46 2018 +0100 inline warn string commit b7ed427470309e85c44da632f6fe9a319fc49683 Author: derselbst Date: Fri Feb 2 11:16:14 2018 +0100 unlock API mutex commit bc12603c993bf4af8ad260e18fd79f46e98c0785 Author: jjceresa Date: Fri Feb 2 00:33:04 2018 +0100 fix comments in fluid_synth_polymono.c commit 34d8b73d856e3a0f664bfc816f20beff5f340627 Author: jjceresa Date: Thu Feb 1 23:59:27 2018 +0100 fixes typos in comments commit eba067d735118077bf6bd8bb4ebb4bdab5046258 Author: jjceresa Date: Thu Feb 1 23:47:58 2018 +0100 moving fluid_synth_check_next_basic_channel() commit 7976be599a4940ec0dc4420954ef8cfe310dd386 Author: jjceresa Date: Wed Jan 31 23:26:42 2018 +0100 fluid_synth_set_basic_channel_LOCAL cannot change an existing basic channels. - Both fluid_synth_set_basic_channel_LOCAL() and fluid_synth_set_basic_channel_LOCAL() behave the same way. - Update documentation pdf. commit a22d1a35fac5b4f697c1d41ae3e368a61a27d653 Author: jjceresa Date: Tue Jan 30 14:07:50 2018 +0100 command setbasicchannels can change an existing basic channel. - fix comments. - update documentation pdf. commit 4777f7b1f428935691be7d1289cb244c56137793 Author: jjceresa Date: Mon Jan 29 23:37:16 2018 +0100 fluid_synth_set_basic_channel() is not allowed to change an existing basic channel commit 5161e4cec71a5c09eed08510458544257b7a66b2 Merge: 435d062 2a4b208 Author: derselbst Date: Sat Jan 27 14:40:58 2018 +0100 Merge branch 'master' into polymono commit 435d062513583dea397686ec8fd442eba732f820 Author: jjceresa Date: Sat Jan 27 00:09:42 2018 +0100 Fixes typos, update pdf documentation commit 13a8c6c6dd1a7dc861e367aed286c7f071b4e0ea Author: jjceresa Date: Fri Jan 26 19:49:18 2018 +0100 correct comments of fluid_synth_set_basic_channel() - comments compliant with MIDI spec when val is 0. "means all possible channels from basic channel to MIDI count - 1". commit 0689257d8f1e880617a6039b677c1fe26a02d27b Author: derselbst Date: Thu Jan 25 20:03:07 2018 +0100 remove redundant channel mode flags from public API commit d632e30c419652cbb5e986fb35aeaf055dfc5b43 Author: derselbst Date: Thu Jan 25 19:57:11 2018 +0100 make fluid_synth_get_basic_channel() only return MIDI modes not fluidsynth's internal flags commit d1aa433e4a9aa1121bfaa78e1aa33e7ea5a88bae Author: derselbst Date: Thu Jan 25 19:43:42 2018 +0100 update API doc of fluid_synth_reset_basic_channel() commit b96f17b0c95a43e172da876108cb50756d023f94 Author: derselbst Date: Thu Jan 25 19:34:21 2018 +0100 make val of fluid_synth_set_basic_channel() compliant to MIDI spec commit 904fd36bc1fa7013665190d3a54469094892c0ec Author: derselbst Date: Thu Jan 25 19:25:16 2018 +0100 rename fluid_synth_reset_basic_channels() to fluid_synth_reset_basic_channel() commit 15a52f6bdfda37f5b25909ad566405543b4c0572 Author: derselbst Date: Thu Jan 25 19:21:00 2018 +0100 API doc cleanup and clarification commit 74c22da6fbfc3444cba448945579e451c28a333b Author: jjceresa Date: Wed Jan 24 13:50:45 2018 +0100 Fixes mores comments in synth.h, fluid_cmd.c, fluid_synth_polymono.c commit 6718cfe68e8e4209bfc396d53bdc71f27edea1ba Author: jjceresa Date: Tue Jan 23 20:19:17 2018 +0100 fix build, fix minor comments commit 9a4d9afc4230a3ff1ebfe8f5035ddd4aee1d4484 Author: jjceresa Date: Tue Jan 23 19:53:12 2018 +0100 Cleanup basic channels API. - Removing fluid_basic_channels_infos struct. - Removing fluid_synth_get_basic_channels() API. - Clarifying fluid_synth_reset_basic_channels() API and fluid_synth_set_basic_channel() APIs. - Changes commands resetbasicchannels , setbasicchannels accordling to API changes. - Replace fluid_synth_get_channel_mode() API by fluid_synth_get_basic_channel() API. commit a2ec80ded11f95fcbde842c69b69efd8023c0e44 Author: jjceresa Date: Fri Jan 19 13:35:32 2018 +0100 update examples in /doc/polymono, fix comments commit 1f283c49551d546db2394ba376291f716f8f3bbc Author: jjceresa Date: Thu Jan 18 18:15:47 2018 +0100 remame SIZE_MONOLIST to FLUID_CHANNEL_SIZE_MONOLIST commit 06093a23e42379ba05ba888937bc9351208583c0 Author: jjceresa Date: Thu Jan 18 16:08:43 2018 +0100 Adding comments compliant with MIDI specs. - Adding comments compliant with MIDI spec. for val parameters. - Cleanup comments. - Cleanup pdf document. commit 87e1dbda569b91f5f585232e4ca9086ac579b350 Author: jjceresa Date: Mon Jan 15 10:23:43 2018 +0100 Adding fluid_synth_get_previous_basic_channel() commit 82fa1702643a506667c6d997962ac63381ce81ce Author: jjceresa Date: Sun Jan 14 11:28:37 2018 +0100 adding fluid_channel_update_legato_staccato_state() commit 3309b6f172c56e9cb7c95fbeb09f6ebfe88351da Author: jjceresa Date: Fri Jan 12 22:49:22 2018 +0100 integrates fluid_channel_keep_lastnote_monolist() commit 92f1e9230d065c8153baf64b77222891de417c99 Author: jjceresa Date: Thu Jan 11 02:24:53 2018 +0100 adds comments commit f4241272d8fdd70da268fbdf04458055f084c446 Author: jjceresa Date: Thu Jan 11 01:32:53 2018 +0100 Removing prev field in mononote struct - this is replaced by adding i_prev parameter in functions fluid_channel_search_monolist(),fluid_channel_remove_monolist(). commit b09da5ed213ba36279755a306c379d33e3cd3716 Author: jjceresa Date: Thu Jan 11 01:28:11 2018 +0100 update comment API, update pdf commit 3308ca252d1f5894be6d9c26bb1763a2efb6a5a9 Author: derselbst Date: Wed Jan 10 11:01:45 2018 +0100 use fluid_cb2amp rather than fluid_atten2amp fixes build commit 6d648f44aeb1132e0f576521c2f35c4e30e7122f Merge: f881a08 37218ba Author: derselbst Date: Wed Jan 10 11:00:41 2018 +0100 Merge branch 'master' into polymono commit f881a08e6b3019f07a675e3dc11972364f1e4620 Author: jjceresa Date: Wed Jan 10 09:51:23 2018 +0100 naming convention for fluid_synth_polymono commit bfb359a3299c7c6cc3384aa11a63eac354d93b62 Author: jjceresa Date: Tue Jan 9 13:16:52 2018 +0100 apply allman braces commit 9275d7ae677463f8354c1163d9fd5af92a669b40 Author: jjceresa Date: Tue Jan 9 11:23:18 2018 +0100 adding result checking functions commit 6e3c804ca4a43394929b6f77b6f43d486ea1db24 Author: jjceresa Date: Mon Jan 8 12:48:39 2018 +0100 removing tabs mixed with spaces commit 94c46a8b689db7ce2e214f574d061d1f61f73e4b Author: jjceresa Date: Sun Jan 7 19:29:01 2018 +0100 fix comment commit 1bedf8d5d4c547a7d7c9b920e7bfefa8204ed894 Author: jjceresa Date: Sun Jan 7 19:05:06 2018 +0100 rename key_sustained, cleanup, fix typos commit 959144acad2312085ade3071c2cfa5b82ab87511 Author: jjceresa Date: Sun Jan 7 04:36:27 2018 +0100 fix typos in comments commit c7b25cfd120f70c5b4092e5c625e903eeb40afdc Author: jjceresa Date: Sun Jan 7 04:17:15 2018 +0100 cleanup on search/remove monolist functions commit 756424065e67913b09a1945413cde4513f727426 Author: derselbst Date: Sat Jan 6 20:22:22 2018 +0100 fix typo commit da991a236fac2e273aa9d60acb89061db956e980 Author: derselbst Date: Sat Jan 6 20:20:51 2018 +0100 use FLUID_STRCMP wrapper macro commit 0dc6d9bcd06615f9d33dfea7beec5fa29dc7230c Author: jjceresa Date: Sat Jan 6 00:22:48 2018 +0100 simplify arguments checking functions commit 0103aa4ab08ab3cc8fe12906e8193f6a2a1984bd Author: jjceresa Date: Fri Jan 5 16:57:12 2018 +0100 fix build warning commit 26773a567b894e2720038a26752b56b8705f4464 Author: jjceresa Date: Fri Jan 5 16:04:17 2018 +0100 declare release_voice_on_same_note_LOCAL in fluid_synth.h commit 693ba8d300d824674b3d3d0dea0d89eb46a83e69 Author: jjceresa Date: Fri Jan 5 15:50:39 2018 +0100 fix global cc, update comment, update pdf commit 9d35a509480aef4ea5be09c453fc0c5ce7e53077 Author: jjceresa Date: Fri Jan 5 14:44:22 2018 +0100 adding arguments checking functions commit bd63cd2021bf46a78454884ba5c76a9659d3e5ee Author: jjceresa Date: Thu Jan 4 18:01:57 2018 +0100 missing static const commit 4d24109b457a038cfb70d7e2fbe7fbbd1e25d05c Author: derselbst Date: Wed Jan 3 20:04:38 2018 +0100 apply naming convention to is_valid_note commit b1b6738b982cddd4c08839f557a46a6c8b23fa02 Author: derselbst Date: Wed Jan 3 20:00:46 2018 +0100 reoder fluid_channel_* declarations commit 56279b2622d738c1c910d7c64a30ab055670a6a1 Author: derselbst Date: Wed Jan 3 19:56:40 2018 +0100 move fluid_channel_*() functions to fluid_chan.c commit 2e54feb3beb0b89ec2203de259fa93c5343f1dfe Author: derselbst Date: Wed Jan 3 19:30:46 2018 +0100 more naming convention for fluid_synth_mono.c commit cd84bf96af7b4219920b5b070fbbf606eb5a21ef Author: jjceresa Date: Wed Jan 3 18:57:13 2018 +0100 simplify portamemto pitch calc. commit af98b362ed0d471c46b7f1475c3c0de469d4f432 Author: derselbst Date: Wed Jan 3 10:37:42 2018 +0100 readd accidently removed custom_breath2att_mod fix build commit 1a4d5d392bee44677ed8e633d2acf4589b3dfdf6 Author: derselbst Date: Wed Jan 3 10:35:16 2018 +0100 make fluid_gen_info static again commit da4481dc278069cdc7452cd5e533613191df4830 Author: derselbst Date: Wed Jan 3 10:35:08 2018 +0100 fix comments commit fa6734bcdc66c581de39ff8db56d737852111a86 Merge: 47a0718 dd2b78a Author: derselbst Date: Wed Jan 3 10:14:35 2018 +0100 Merge branch 'master' into polymono commit 47a0718175ca8bd900d8ed823d502f0ef004a7d5 Author: derselbst Date: Wed Jan 3 10:11:36 2018 +0100 cleanup fluid_synth_*() forward decl. commit c730bcf296c0e5895158ef08bbee278a33e43e93 Author: derselbst Date: Wed Jan 3 10:06:32 2018 +0100 move polymono fluid_channel_* macros to fluid_chan.h commit 228c7bca6a17c13ac5a82d8faaf3fbc1fba39162 Author: derselbst Date: Wed Jan 3 09:57:09 2018 +0100 apply naming convention to polymono helper macros commit 0d2e16497f93b02649041a7dd5ff0697e94b443c Author: jjceresa Date: Mon Jan 1 22:36:10 2018 +0100 move param checking, FLUID_API_ENTRY_CHAN to API, cleanup commit 1d73cec83bc4da75b754191db0105097874c8f39 Author: derselbst Date: Mon Jan 1 17:52:59 2018 +0100 remove redundant null termination for FLUID_SNPRINTF commit bca5472c347ef17203dfe7d37311a24b1c608b64 Merge: e64b08a 70dffe7 Author: derselbst Date: Mon Jan 1 17:49:25 2018 +0100 Merge branch 'master' into polymono commit e64b08a800976bd5176a594f93ea4e050db5846c Author: jjceresa Date: Sun Dec 31 15:22:54 2017 +0100 fix scope mode names commit fba23b6a018a96c4cb3779b43de79eb1b82b0495 Author: jjceresa Date: Sun Dec 31 14:53:11 2017 +0100 fix comments, typo errors, update doc commit 330826eb4739e8521a31d58205359a353e7fc9ef Author: jjceresa Date: Sun Dec 31 05:34:37 2017 +0100 adding mode name for shell command commit 52689198d1224540b36fc584aad0d885c9eea39a Author: jjceresa Date: Sat Dec 30 22:48:37 2017 +0100 update doc API fluid_synth_set_basic_channel() commit 329df43f5d67a99b8dcdf2c7c6dd35bec6b3e356 Author: jjceresa Date: Sat Dec 30 19:18:45 2017 +0100 fluid_synth_get_channel_mode() return basicchan commit 29de66116b2bf7a1545818a0604adf5a0559420f Author: jjceresa Date: Sat Dec 30 17:59:32 2017 +0100 Simplfying fluid_synth_set_basic_channel signature - also cleanup variable declarations (static, scope, ...). - calling fluid_synth_set_basic_channel_LOCAL() instead of fluid_synth_set_basic_channel(). commit 5e9bc3977a9a3616ac4697c6b16e186656cd2baf Author: jjceresa Date: Sat Dec 30 03:39:24 2017 +0100 cleanup status variable and rename variables commit 650ad51af9fcb1fa88ab64cfad868ce0d80fc917 Author: jjceresa Date: Sat Dec 30 00:55:16 2017 +0100 remove basic channels settings update FluidPolyMono-0004.pdf - removing basic channels settings. - addding comment for Global channel. commit 1bbb520ab9b5c9b5cb954f3bc3d0ecf929237369 Author: jjceresa Date: Sat Dec 30 00:49:31 2017 +0100 update comments on breat flags commit 795321aa8710c409dccd4773ccd8491f5427a7d9 Author: derselbst Date: Fri Dec 29 20:28:30 2017 +0100 reorder synth API macros commit e1589234d01a62e304e8edf53107e5a404829367 Author: derselbst Date: Fri Dec 29 20:20:52 2017 +0100 introduce FLUID_API_RETURN_IF_CHAN_DISABLED macro for synth to reduce nesting commit f8dee9b6ab02140d2104167fe7d273bfcf597abb Author: derselbst Date: Fri Dec 29 19:42:15 2017 +0100 correct API comment of fluid_synth_cc() commit 41083f4b970fa46e652609f92302f228dc6fe550 Author: derselbst Date: Fri Dec 29 10:49:43 2017 +0100 fix build forgot to commit fluid_synth.c commit ac04f365c715615f44885445ba7666050c655525 Author: derselbst Date: Fri Dec 29 10:49:03 2017 +0100 remove redundant fluid_channel_get_cc_portamento helper macro commit d707f5e0bc8807c4c43b51eb0503ef91f0d5c8b9 Author: derselbst Date: Fri Dec 29 10:45:13 2017 +0100 fix naming of fluid_channel helper macros commit fa38f6a9b2c3250f9dd77e5fd94a7107790ad1f6 Author: derselbst Date: Fri Dec 29 10:44:49 2017 +0100 handle BREATH_MSB is switch-case commit c68c7eb40cd020f0a0b068d8497328692c8e0800 Author: jjceresa Date: Fri Dec 29 00:20:10 2017 +0100 fix comment in enum fluid_basic_channel_modes commit 56a63af296bc90f8b0e8bd075e7b4b57d2425310 Merge: c29e401 d35601a Author: derselbst Date: Thu Dec 28 20:33:19 2017 +0100 Merge branch 'polymono' of https://github.com/FluidSynth/fluidsynth into polymono commit c29e40157921a2de0abe8e8d7ff467ea63bc33a7 Author: derselbst Date: Thu Dec 28 20:32:33 2017 +0100 reorder polymono API calls to place them after the other synth functions commit 6ac82ff2b7a4b4079ec5121b40fe06bd13a4d08c Author: derselbst Date: Thu Dec 28 20:29:52 2017 +0100 cleanup polymono API comments commit d35601ae5a71bfbb11f2e6ea79c5a7bf766f6983 Author: jjceresa Date: Thu Dec 28 19:15:10 2017 +0100 Fix typo in FluidPolyMono-0004.pdf Cleanup fluid_synth.c commit 731c59bcd64ac4e1ef06decf36774ed771c7bd41 Author: jjceresa Date: Thu Dec 28 16:50:03 2017 +0100 Fix parameters checking in commands with no arguments. commit 6d0fa0f53ed07edaf0a71bc219d807ee35a5f50d Author: jjceresa Date: Wed Dec 27 11:33:58 2017 +0100 update doc/polymono/readme.txt commit 3fa932e9188e27241be4acb4c4d3360ba630eda7 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Sun Dec 24 22:20:05 2017 +0100 - Fix typos errors. commit 59e2ed3cc52c782dbad17847f083b96fff004165 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Sun Dec 24 00:03:57 2017 +0100 Comments additions. commit 88f11588750b1e8196508c19e32e98ca540bf3f2 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Sat Dec 23 23:42:21 2017 +0100 Removing fluid_voice_update_release() signature. commit 9b8cd5e5532a6f87ed516b83f728509f2ad0d890 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Sat Dec 23 23:38:50 2017 +0100 Removing fluid_voice_update_release(). - removing fluid_voice_update_release(). - adding comments. commit 2787d150deb0c64519507f4fbfa8ef58a28d11b9 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Sat Dec 23 23:24:55 2017 +0100 Using fluid_voice_release() in legato mode 0. substitute fluid_voice_update_release() by fluid_voice_release(). commit 68f7c7778b07a384d3d1648bb34bcf705a05affb Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Sat Dec 23 23:14:12 2017 +0100 Add comments to poly/mono public API. commit 51cedfdfed88883e00f08e4e9253f5bf08cb5287 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Sat Dec 23 00:55:09 2017 +0100 Update FluidPolyMono-0004.pdf -Cleanup chapter 1. -Update doc API. commit 1ac23daf6dd10f1d1c0afc52bf1d7182d4d0538f Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Dec 22 23:12:38 2017 +0100 Change enum in fluid_cmd.c commit 902f603c102689b58f86dfd789c01155d26c8bd8 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Dec 22 23:09:38 2017 +0100 Change enum names in fluid_synth_polymono.c commit df52819193c058cbce9201af647e740931889900 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Dec 22 23:03:59 2017 +0100 Change enum names in fluid_synth_mono.c commit 8aea95e0f4276d9904f9428e9a2cc93d8188a3e3 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Dec 22 22:56:29 2017 +0100 Change enum names in fluid_chan.c commit da0e94c2a8d8d7af8c5bbd81de7fa4cb7953372b Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Dec 22 22:52:54 2017 +0100 Change legato and portamento enum -Change enum LegatoMode to enum fluid__channel_legato_mode. -Change enum PortamentoModeto enum fluid__fluid__channel_portamento_mode commit 75309ef3a322050b4d452144028ca909fef0977c Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Dec 22 19:22:30 2017 +0100 Channel mode and Breath in commands Make use of enum fluid_basic_channel_mode_flags. commit 0aca0a4e9405dc2201f7201904a0192af0f4f5d4 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Dec 22 19:14:12 2017 +0100 Channel mode and breath in fluid_synth_polymono.c Make use of new enum fluid_basic_channel_mode_flags commit 4828760f45326884322d850ec50401825ded997b Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Dec 22 18:52:59 2017 +0100 Rename enum in Fluid_synth_mono.c Make use of new values enum fluid_basic_channel_mode_flags commit a0246d16ed5db3d5771a9a60c13d66c628e11447 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Dec 22 18:44:31 2017 +0100 Rename enum in fluid_synth.c Make use of new enum fluid_basic_channel_mode_flags commit ba6dea233ab6667361eaefbb0e9b8cacbab6724f Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Dec 22 18:10:34 2017 +0100 Change MONO name Change MONO to FLUID_CHANNEL_POLY_OFF commit 091bf7f3eeab75f1d5766592da9e6a0b1b1da5f4 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Dec 22 18:04:48 2017 +0100 Renaming enum fluid_basic_channel_mode_flags -Renaming enum fluid_basic_channel_mode_flags values. -Integrating Breath mode in enum fluid_basic_channel_mode_flags values. commit de945f2bb3af2692f9268e3afb37093709d47845 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Dec 22 03:56:40 2017 +0100 Move signatures to fluid_chan.h commit 614b74202b7c2d6aaf6b75cc524c908523549e37 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Dec 22 03:54:36 2017 +0100 Add sigature functions. void fluid_channel_set_onenote_monolist(fluid_channel_t* chan, unsigned char key, unsigned char vel); void fluid_channel_clear_monolist(fluid_channel_t* chan); void fluid_channel_invalid_prev_note_staccato(fluid_channel_t* chan); void fluid_channel_cc_legato(fluid_channel_t* chan, int value); void fluid_channel_cc_breath_note_on_off(fluid_channel_t* chan, int value); commit 963a37a36f3c2b2d494deeb472efe8b4ce5365a7 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Dec 22 01:39:25 2017 +0100 fix incorrect cast fix incorrect unsigned cast. remove unused extern. commit 67ff72659f4e83a0e0e24912e919071ae692f590 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 23:45:12 2017 +0100 fix comments - fix help commands strings commit 2c4aff8773a363fa5902f1425c302bd4da8c8c68 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 23:11:53 2017 +0100 Fix comments. commit 301712ac4686d2768aa6d441492a172f805c7fc4 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 23:02:55 2017 +0100 Update FluidPolyMono-0004.pdf commit d4c4d0fc1382ee8795d3565d2373e8c2f36600a4 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 22:45:07 2017 +0100 Update FluidPolyMono-0004.pdf commit 410b073565395ce6bc73194c99d9f8d48d632642 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 22:33:44 2017 +0100 Add files via upload commit 0ce5bc65a676144631a3264ef27a6602b87152ae Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 19:27:16 2017 +0100 Update poly_mono_4.txt commit 088fd806464187dc23e07c843c3a25925a94313f Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 19:22:36 2017 +0100 Adds files leg_port_00.txt - leg_port_01.txt commit 9b98446a3f1c49c7bfcd51eb00a726eb918cdb70 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 19:13:29 2017 +0100 Delete leg_por_4.txt commit c832a9b123af5456bce860197786f50555111652 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 19:13:12 2017 +0100 Delete leg_por_3.txt commit 8cacbb99375b37310e30c1a681bcdcb204a9bd91 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 19:12:57 2017 +0100 Delete leg_por_2.txt commit af04826db6ca384ed1797fcedf9200db40bbe458 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 19:12:42 2017 +0100 Delete leg_por_1.txt commit 0cdf52de97779720917839f842d776eb7e1a7856 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 19:12:25 2017 +0100 Delete leg_por_0.txt commit d750fe34dbd918bf0f3e6be391befc16c962bd1d Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 19:10:12 2017 +0100 Update leg_01.txt commit 1d9541d8cddb3a761d26739556275560d43acc10 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 19:08:53 2017 +0100 Update leg_00.txt commit 237f6d500c375aea569930c675ff3463796e71b6 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 19:03:44 2017 +0100 Delete Legato_demo.sf2 commit e875c02fbe8bf11fb3cb3ffc847e1d786c952a15 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 19:01:34 2017 +0100 Update readme.txt Removing soundfont file commit 07335cfdb93a6b65d595debdef7973b53adfb62e Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 00:36:23 2017 +0100 Add FluidPolyMono-0004.pdf commit aa94b3fe12a1301a23c10a3f69a8212d18c36d8e Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 00:34:36 2017 +0100 Update readme.txt commit 3aa942922149541102f2c46584aac1aa1341c5ab Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 00:33:54 2017 +0100 Delete FluidPolyMono-0003.pdf commit ddd9b2de37d056a20b430f113e351ea6c786fc8e Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 00:30:52 2017 +0100 documentation files adding/update leg_00.txt leg_01.txt poly_mono_0.txt poly_mono_1.txt poly_mono_2.txt poly_mono_3.txt poly_mono_4.txt poly_mono_5.txt commit 89983fb42eb54a6bab5b6045e5a31a6800ef23ba Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 00:26:50 2017 +0100 Update readme.txt Removing legato modes 0,3,4 Tutorials examples addition chapter 2.1 poly_mono_0.txt poly_mono_1.txt poly_mono_2.txt poly_mono_3.txt poly_mono_4.txt poly_mono_5.txt commit dcedce20d79d317c834bfd6c961a3fdf87ed1f7f Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 00:17:56 2017 +0100 Delete leg_2.txt commit a9d765def0170e204027abfa42fc02acfdd47704 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 00:17:29 2017 +0100 Delete leg_1.txt commit 8e0fc533d69cb261c0cfd6a8f58fe729b758ab0e Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 00:17:00 2017 +0100 Delete leg_4.txt commit ae670075e592241830ef539c9bf26c4cd84a3a47 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 00:16:14 2017 +0100 Delete leg_3.txt commit 6040f7575277f53c8803155d58fd158cb8e29a5f Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Dec 20 00:15:23 2017 +0100 Delete leg_0.txt commit 1e8c4ad4dc7b6ecd0727a73b0ac814679b9fd4f3 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Dec 19 23:34:03 2017 +0100 fix build type conflict. commit a0fa4debd6fc21d652edfabaaf963b31babcd045 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Dec 19 23:21:10 2017 +0100 Removing legato modes (0,3,4). commit f53476bfc0c46b313f5b293243a3f3b6d9c3af64 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Dec 19 23:01:50 2017 +0100 Removing legato modes (0,3,4). commit d69868bdb8179e1eb75661d6b02799de0d7a3e48 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Dec 19 22:57:22 2017 +0100 Removing legato modes (0,3,4). commit ebd37fdb680e3062f0174fe7c414b3fce6b8c60f Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Dec 19 22:53:38 2017 +0100 Removing legato modes (0,3,4). commit 265eeb4f321a8bab54ae6aa700474d96a3c83951 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Dec 19 22:47:25 2017 +0100 Removing legato modes (0,3,4). commit 601216e83e01953c58ceabd0e44cc4f361d1c4ae Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Dec 19 22:44:18 2017 +0100 Removing legato modes (0,3,4). commit 16b0e65697d57d3bc653e3419d924799b37e3a01 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Dec 19 22:39:17 2017 +0100 Removing legato modes (0,3,4). commit 5c4b9322e7b6f8600be9849d869233f20beb3d4c Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Dec 19 22:31:16 2017 +0100 Two legato modes 1 and 2 (rnumbered 0 and 1). Removing legato modes (0,3,4). Legato modes 1,2 are numbered 0,1 respectively. commit 8c4096910e9e44c896f7169345d6a262a17e522d Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Dec 19 22:25:33 2017 +0100 Two legato modes 0 and 1 (dfefault mode: 1). commit fb4205b215648efc1949708073f68237eed8679a Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Dec 19 22:20:56 2017 +0100 Two legato modes 1 and 2 (rnumbered 0 and 1). Removing legato modes (0,3,4). Legato modes 1,2 are numbered 0,1 respectively. commit d0adfe31f4961d034483a95705ac8b86839cf11d Author: derselbst Date: Sat Dec 16 11:42:31 2017 +0100 refactor poly mono bit flags commit da5ac8dd79479b6a19f5c3608be7ec3507a5b637 Merge: 13754fa 07a167c Author: derselbst Date: Sat Dec 16 10:37:34 2017 +0100 Merge branch 'master' into polymono commit 13754fad6acbce51bdf5b57526a4ca243948a3ed Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Dec 15 19:38:04 2017 +0100 Fix bug in new_fluid_preset_zone(). Add missing initialization since moving ignoreInstrumentZone check to fluid_zone_inside_range(). commit 64dc235485a004e7b0f6e8f9afae1375321f6afa Author: derselbst Date: Wed Dec 13 13:41:32 2017 +0100 move ignoreInstrumentZone check to fluid_zone_inside_range() commit 427a513e2caac645bc571a8fbe7e3a513f874fa1 Author: derselbst Date: Wed Dec 13 13:20:36 2017 +0100 fix spelling of IGNORE_INST_ZONE commit c9b74903f1e54976b0ed3e5d4aa5e5a2ba2f8c1c Author: derselbst Date: Wed Dec 13 13:17:32 2017 +0100 remove redundant NULL check fixes #302 commit 9c8f41be408475ebc8a16dbf468067c795ee5830 Merge: 09fcf6c 5806d9e Author: derselbst Date: Sun Dec 10 10:03:31 2017 +0100 Merge branch 'master' into polymono commit 09fcf6c3fc36739a29b79008224dd0b46f827cd1 Merge: 9440884 1629f9f Author: derselbst Date: Sat Dec 9 15:16:51 2017 +0100 Merge branch 'master' into polymono commit 944088440f4c91ce712909a12af1332b09b9a1c5 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Dec 5 00:51:55 2017 +0100 fix bug GEN_VELRANGE commit 73caf02a323776bd320d30624ebe4dd60a1c940f Author: derselbst Date: Sun Dec 3 14:59:30 2017 +0100 cleanup commit b0f43feac5de166f5791d6203d4c78f9f4f3ab38 Author: derselbst Date: Sun Dec 3 14:31:45 2017 +0100 rename fluid_inst_zone_range_t to fluid_zone_range_t find . -type f -exec sed -i 's/fluid_inst_zone_range_t/fluid_zone_range_t/g' {} + commit 133b418ca5641579eb22cacb5059c48b642b8f5e Author: derselbst Date: Sun Dec 3 14:28:45 2017 +0100 make use of range struct for presets as well remove code duplications commit 42e23a5e398520959bf1124439539e0f278a80fc Author: derselbst Date: Sun Dec 3 13:44:33 2017 +0100 remove zone_range from _fluid_synth_t prefer explicit parameter passing rather than hacky non-threadsafe param struct saving commit 2bc061557b9aedb9a2c0f891488aeb1122272395 Merge: 6037bdb cdfe8d3 Author: derselbst Date: Sun Dec 3 13:31:57 2017 +0100 Merge branch 'master' into polymono commit 6037bdbd30f793b007c589b5f870cabea2c85c6a Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Nov 29 23:45:10 2017 +0100 fix parameter flfuid_voice_init() commit bb0da222bd420989b63b0229568b178e8da3e293 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Nov 29 23:24:12 2017 +0100 fix IGNORE_INST_Z0NE commit d8752cc835b52eda6122dba7d6ab2354a7e83deb Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Nov 29 23:16:56 2017 +0100 fix fluid_voice_init in voice.h commit 41e4b77fe5f6fc0766b1974d5031d516a7ff0c11 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Nov 29 23:04:45 2017 +0100 Add zone_range tp poly_mono.c commit f3550d18e74845a09800af8aef12f5c5da21fc7b Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Nov 29 22:51:30 2017 +0100 Add zone_range to fluid_voice_init() commit 2e9ebad6cb5bd44e82674e87ef4fb7b88b9b9445 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Nov 29 22:48:13 2017 +0100 Add zone_range to fluid_voice_t commit d779583c490d85ea8b14481fcedbe50efda27aed Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Nov 29 22:41:45 2017 +0100 restore fluid_synth_alloc_voice signature restore fluid_synth_alloc_voice signature to synth.h commit 27a4e4407843cfb7c7c86b6a99c0f10d43ad4326 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Nov 29 22:36:06 2017 +0100 Add zone_range to fluid_synth.c Restore API signature luid_synth_alloc_voice() commit f4749fc88372a32b055a6dcb6a1b00e247d15c6b Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Nov 29 22:27:03 2017 +0100 Add zone_range to fluid_synth.h commit 7250f40d7aa3dc074c3ffd6dfb1e6aa2821c26f2 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Nov 29 22:24:37 2017 +0100 Add zone_range to ramsfont commit a3ffce39355c7fcb86c25ae5c0254758a502b1f3 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Nov 29 22:19:39 2017 +0100 Adding zone_range. commit e2f99a91e58950373f99032eb1fa752805ed8eee Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Nov 29 22:03:44 2017 +0100 type fluid_inst_zone_range_t commit adb5e74311b6010289d7f672985cc24f9153bcfd Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Nov 29 21:57:05 2017 +0100 Adding fluid_inst_zone_range_t in fluid_inst_zone_t commit 89ed46f063301096f0ce4a3b673a8fce3b6c4cde Merge: 45773e1 7ed5d1f Author: derselbst Date: Fri Nov 24 17:40:17 2017 +0100 Merge branch 'master' into polymono commit 45773e14d21a56085da7344d0168fb87a9a58607 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 23 23:44:10 2017 +0100 Ignore voices when there is no instrument zone. commit 156eb2f547cff9e0bca43830ed35f9bb315e48c4 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Sat Nov 11 00:03:29 2017 +0100 Fix warning build commit 1b1233014a3d8b1fdf043d1fdd4ea51cb6b0aaf9 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Nov 10 20:06:23 2017 +0100 Fix fluid_voice_is_on(voice) commit 88317aaaa8e33f999393a0a582110642e1726e93 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Nov 10 19:55:30 2017 +0100 Replacement of macro _ON(voice ) Replacement of macro _ON() by fluid_voice_is_on() Note : fluid_voice_is_on() isn't yet in fluid_voice.h, so to avoid a failing build we define fluid_voice_is_on() extern. commit 0ba247f2c6c5b93c3354069b791b46be9d9b6235 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Nov 10 18:35:34 2017 +0100 Fix functions naming in chapter 4. commit aeaceefa24b03f0ff28b6fb734c56ab40d16fbf8 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Nov 10 18:34:25 2017 +0100 Delete FluidPolyMono-0003.pdf commit 24f1fa56974dde407007b48f065f0779d78ddfb5 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Nov 10 18:31:25 2017 +0100 Addition of missing comments Comments in fluid_synth_noteon_LOCAL(), fluid_synth_damp_voices_by_sustain_LOCAL(), fluid_synth_damp_voices_by_sostenuto_LOCAL() commit 023a101642fc246c542e900cabdbc1dae24829f0 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 23:15:59 2017 +0100 renaming functions commit 87e26ff24b7f30731ff59878f444c58747dc3277 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 23:08:46 2017 +0100 Renaming functions commit 1b41b67e6fd9ec4ee789abc0448bec8db8923046 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 23:04:54 2017 +0100 renaming functions commit 486dfed111bce8e48b4bad59d0330e4593e92cbf Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 22:59:41 2017 +0100 Renaming functions commit 0d5269520b652114251c13f0d87b2c8ec062a60c Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 22:01:24 2017 +0100 Fix build commit 7c0c3c2944ee15a69d7e1b6c1ae6d4c35f062156 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 21:58:46 2017 +0100 Fix build commit 1c7958250d48bdfa46b7df8918d087323ee9bc4c Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 20:15:35 2017 +0100 fix build commit dd4c86a2245c76c74781fa3dfa236cd9acfd225e Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 20:02:32 2017 +0100 Fix build commit 490d0745dc6e6338b2641f68bac8da86a83b6ed7 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 19:49:29 2017 +0100 Add macro #define _ON(voice) to fix build commit 553a1dc13b87ea0158c7dd2cab8aa7787dc60f2f Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 18:26:04 2017 +0100 Fix build in legato_on_off() commit e075c1e7ba695f3529422c74a77ed2571368a729 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 18:22:10 2017 +0100 Fix comments commit fe763339e3ac3872afcb7bbf557884d31306868a Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 18:19:01 2017 +0100 Fix comments commit 662a3632d863fab6e9a949e4030f3018cea49660 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 16:41:37 2017 +0100 Updating Chapter 4 commit e15a0198fe6af9c52004159933faeb7cc7bdb0fc Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 16:40:30 2017 +0100 Delete FluidPolyMono-0003.pdf commit 5bec835f3aa7c4d605a04d66b7eadce9aaf2e276 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 16:01:00 2017 +0100 Renaming fluid_synth_noteon_mono_legato() Renaming fluid_synth_noteon_mono_legato() to fluid_synth_noteon_monopoly_legato() commit 1192769461366dbe496ae41d3ba24f97418013eb Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 15:56:17 2017 +0100 Renaming fluid_synth_noteon_mono_legato() Renaming fluid_synth_noteon_mono_legato() to fluid_synth_noteon_monopoly_legato() commit f76b3062f8ec32ad0cc361af3b4755db128e020e Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 15:52:51 2017 +0100 Renaming fluid_synth_noteon_mono_legato() Renaming fluid_synth_noteon_mono_legato() to fluid_synth_noteon_monopoly_legato() commit b80684df57523f55f346c95e4731756715821572 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 15:47:57 2017 +0100 Renaming fluid_synth_noteon_mono_legato() Renaming fluid_synth_noteon_mono_legato() to fluid_synth_noteon_monopoly_legato() commit 181affa17f9099fb3606ee326ee819ad4736cc6b Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 15:45:51 2017 +0100 Renaming fluid_synth_noteon_mono_legato() Renaming fluid_synth_noteon_mono_legato() to fluid_synth_noteon_monopoly_legato() commit 2efbfa98c7a2f80a11a8847ff3d89cc2d545f971 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 00:11:39 2017 +0100 Update documentation Addition of figures in chapter 4. Part 4: Appendices for understanding implementations in FluidSynth. commit a32b12b15c37669c1c1bda9228955bacd9844400 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 00:08:26 2017 +0100 Delete FluidPolyMono-0003.pdf commit ee3820fedd5024131ecbf0f6a66936792c651818 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 9 00:01:45 2017 +0100 Comments addition Comment addition about the size of monolist SIZE_MONOLIST. commit fbf496d8f7c79ed8a72ffb22a12f0ead28815084 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Nov 8 23:57:59 2017 +0100 Comments addition Comments addition relative to FluidPolyMono-0003.pdf chapter 4: "Appendices for understanding implementations in FluidSynth" commit af551859b7558e73915c6fefab873c0ebb46221c Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Nov 7 00:29:30 2017 +0100 Update FluidPolyMono.pdf Removing obsoletes chapters. Adding appendices for understanding implementations in FluidSynth (see chapter 4). commit 8953f099bcfbeb58ecbf0b4ad69e97d004cef7bd Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Nov 7 00:26:36 2017 +0100 Delete FluidPolyMono-0003.pdf commit d000ddac93df0c12ecbe4b2de6b3d4fee1b6b06e Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Nov 7 00:23:33 2017 +0100 Fix typos errors commit 539c0b3c5db214765ada1ac214391bea33aa2f97 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Nov 7 00:19:06 2017 +0100 Fix typos errors commit 168669bb603fff83166e3c8df1aff972415dd921 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Sun Nov 5 03:11:11 2017 +0100 correction typos erros commit 8afb104f880660c08556f712aa804c2cb889eb2d Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Sun Nov 5 02:16:02 2017 +0100 Adding a new chapter. Adding chapter : 3.11. PART 3: APPENDICES FOR UNDERSTANDING IMPLEMENTATIONS IN FS. commit d21a528e474e13d62ac4141d224b9d1d16ca8a29 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Sun Nov 5 02:13:16 2017 +0100 delete FluidPolyMono-0003.pdf commit 942d911dc886ef391cb8975b93c4380c1c4d8977 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Sun Nov 5 02:07:08 2017 +0100 changes functions name invalid_prev_note_staccato(), legato_on_off(),breath_note_on_off() commit 62c2529ae3f983797713b64cdaaa2e4c2cb2e3c9 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Sun Nov 5 02:01:03 2017 +0100 Changes names functions invalid_prev_note_staccato(),legato_on_off(), breath_note_on_off() commit 10271467d86530b16ef606ddbda964215e73690e Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Sun Nov 5 01:53:35 2017 +0100 update functions documentation Documentation addition. Correct get_fromkey_portamento_legato() name. Changes names: legato_on_off(), breath_note_on_off(),invalid_prev_note_staccato() commit decf7817706a5ade25903730d11135acfee77817 Merge: 84ee8b3 30c0a72 Author: derselbst Date: Sat Nov 4 08:52:21 2017 +0100 Merge branch 'master' into polymono commit 84ee8b3304d87485cd21d802214d670183e51c17 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 2 20:18:03 2017 +0100 Polishing and documenting commit 99eaf83cef87555926c0078eb38912901a80b003 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 2 20:14:36 2017 +0100 Polishing and documenting commit 172a9b0cae9aef9a18abb31aa4c2d7543b4ea054 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 2 19:43:51 2017 +0100 Polishing commit 0d0b47181df5cea717ab848e88e5e87a2ce643e1 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 2 19:00:41 2017 +0100 Polishing commit f55273986d847e99814bd92d8fc109dea02a85f3 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 2 18:50:38 2017 +0100 Polishing commit fb6f0eac8ce50a987ff438e942d59d1c24b769c4 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 2 18:43:54 2017 +0100 Polishing commit fe8e7f692d89be52a95ae2726b1b20870c7fe042 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 2 18:35:56 2017 +0100 Polishing commit 8e37f562c14141c573a13bb43e9612a3586e672e Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 2 18:23:06 2017 +0100 polishing commit 8a20a766f995c3c5d9928bb46270758fc92c29a1 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Thu Nov 2 18:18:25 2017 +0100 polishing commit 3dab1a0edc973c0ddfa762b214a22dff4d2e9bb5 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Mon Oct 30 01:23:11 2017 +0100 Again avoid macros Avoid GetPortamentoMode, GetLegatoMode macros commit d39cfe5a199cb7179a174d11dadbfd2bcb992088 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Mon Oct 30 01:18:44 2017 +0100 Again avoiding macros Avoid GetChanPortamentoMode, GetChanLegatoMode macros commit bb519495956f5e3d0bc5264d3aea491e5de85afb Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Mon Oct 30 01:11:54 2017 +0100 Again avoiding macros Avoiding GetChanLegatoMode, GetChanPortamentoMode macros commit c31449626ad195b3fd44215f8bd51be8cfcc6a29 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Mon Oct 30 00:40:05 2017 +0100 Add FluidPolyMono-0003.pdf Adding explanations about the legato detector (3.4.3). Adding example about breathmode (3.4.5). commit 53f215e602156ecfbb8aeef81ba7a19e201313d4 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Mon Oct 30 00:38:15 2017 +0100 Delete FluidPolyMono-0003.pdf commit 7dcde051986ae8b17c0613e83f0253db647dd1ee Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Mon Oct 30 00:30:20 2017 +0100 Again avoiding macros commit 8afbb4503a98d9caa9a85f703d19740bd07ade36 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Mon Oct 30 00:24:57 2017 +0100 Again avoid macros commit 4e2a6750c15973cb2515c2f9b7971d821b04031a Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Mon Oct 30 00:16:04 2017 +0100 Again avoid macros commit 21ae600907287e87f35a8f9c1cd1e0fe6e920565 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Mon Oct 30 00:02:41 2017 +0100 Again avoiding macros commit a0ec029bd886682b3d88ed8150ef90c29a75eec7 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Sun Oct 29 23:46:53 2017 +0100 Again avoiding macros commit 1e583799f6690253bfcad4cbc4fb5fb0f5b9fa99 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Sun Oct 29 23:18:26 2017 +0100 avoiding macros commit d92445b99f0a3fd7f896bd03afca05eaa1271b9c Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Oct 27 16:37:34 2017 +0200 Avoid nested macros commit 71d6a180cb574a754e30a821b52b1a975bc4b2f6 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Oct 27 16:13:30 2017 +0200 avoid nested macros commit e22013c37d01734c4bc535eba65e6b2cd8db0aad Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Oct 27 15:40:47 2017 +0200 Avoid nested macros commit 92ba702ffca8284470f45d6568ffa6d81be3a5db Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Oct 27 14:54:19 2017 +0200 Cleanup macro. commit 2a31e93320907779cd7f56d62991bcce00bedaba Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Oct 27 14:49:52 2017 +0200 Removing nested macros. commit 9433f2bf2c91939fddb67ba24057225c9e1be7a7 Merge: 71f7c35 cdd9f75 Author: derselbst Date: Thu Oct 26 19:56:02 2017 +0200 Merge branch 'master' into polymono commit 71f7c35a77e8192596f95df6894dae5e2a653dc2 Author: derselbst Date: Wed Oct 25 10:38:05 2017 +0200 remove static from fluid_gen_info to fix build commit 6c70db9065f81bc323eadcd654a5d5800ca4622c Merge: 1dbae28 e485129 Author: derselbst Date: Wed Oct 25 09:55:15 2017 +0200 Merge branch 'master' into polymono commit 1dbae2874f68d1eb62db2ab34dcb4e3e1105f47d Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Oct 24 03:02:32 2017 +0200 Add FluidPolyMono-0003.pdf Correction API documentation (see 2.6.1, 2.6.4) commit f01b955de22c4551821199aa670c713a4456e14a Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Oct 24 02:57:18 2017 +0200 Delete FluidPolyMono-0003.pdf commit b557289b5f6c3ccb531c6c38b45fc61e089a83bc Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Oct 24 02:55:31 2017 +0200 Replacing FLUID_POLYMONO_WARNING by FLUID_FAILED commit 2d06adef8af6e8d2cef2e03ab669270a727a1742 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Oct 24 02:52:06 2017 +0200 Replacing FLUID_POLYMONO_WARNING by FLUID_FAILED commit 06108d9f7a61f8773cac02bc32749859e415794e Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Oct 24 02:38:23 2017 +0200 Replacing FLUID_POLYMONO_WARNING by FLUID_FAILED commit 66b85fb38ec24bb3468e5b8c205a8b15a0002272 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Oct 20 16:08:46 2017 +0200 Polymono documentation has moved to ./doc/polymono commit 1a03ef03e1ad1043152224cf604ec2bb47756f74 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Oct 20 15:41:21 2017 +0200 Adapt functions names to follow fluidsynth conventions. commit d40cf8798612eea9accf41aba7e325d6a69f326e Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Oct 20 15:33:42 2017 +0200 Adapt functions naming to follow fluidsynth conventions. commit 49423cb8bfb7b19b9164d6e642b4549468f003f7 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Oct 20 15:28:08 2017 +0200 Adapt functions names to fluidsynth conventions Avoiding abbreviations. Change functions names to follow fluidsynth conventions commit bc5345ab1c37173cf075b6572dbff5d8bd57a85a Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Oct 20 14:56:41 2017 +0200 Clarify variables spelling and bug fix Avoiding abbreviations in comments and variables names. Bug fix. commit 1f0a1ad3cbf1180ed242eb5997b18b79a98db6ba Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Oct 20 14:48:00 2017 +0200 Clarify variables meaning avoiding abbreviations in comment and variables. commit c947686bfc8d6adde47c831a411d639938df8428 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Oct 20 14:35:05 2017 +0200 Clarify comments. Avoiding abbreviations on comments commit 7e7296e276b1c9ef254788634e5e2bc565dec211 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Oct 20 01:34:53 2017 +0200 Adding others documentation files. commit 4a6417ff8fb3afa60f6cd39f6b299759e122398c Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri Oct 20 01:32:02 2017 +0200 Create readme.txt commit 3babbed01e6fb3a264bb61d6e0be0d67e45cd853 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed Oct 18 16:27:48 2017 +0200 Update and fix API parameters checking in fluid_synth_polymono.c Fix correction in Poly/Mono API fluid_synth_get_channel_mode() fluid_synth_set_legato_mode(), fluid_synth_get_legato_mode() fluid_synth_set_portamento_mode(), fluid_synth_get_portamento_mode() fluid_synth_set_breath_mode(),fluid_synth_get_breath_mode() commit 98234eb7c146c75bd25cef0813b1f1b12d93ad84 Merge: bb7b483 b908c04 Author: derselbst Date: Wed Oct 18 13:18:05 2017 +0200 Merge branch 'master' into polymono commit bb7b48340cde04ee7c8e0d19942e45de3fa5197e Author: derselbst Date: Wed Oct 18 12:51:19 2017 +0200 fix param check for fluid_synth_set_basic_channel() commit b2157bdc4aa614647ff0a32d46b53941999d6fed Author: derselbst Date: Wed Oct 18 12:34:14 2017 +0200 another param check fix for fluid_synth_reset_basic_channels() commit 2ff7dccb555099f1f508e5318aa295338638123a Author: derselbst Date: Wed Oct 18 10:52:40 2017 +0200 correctly check parameters and enter synth API for fluid_synth_reset_basic_channels() commit 55ca184b1fa1a2bfa61918d155e9b8d7a32d2c0e Author: derselbst Date: Wed Oct 18 10:29:05 2017 +0200 move synth API entry macros to header so they are usable in polymono and mono synth files commit deb1ea53a121625eaa913b9b16f4a63a8eb4183e Author: derselbst Date: Wed Oct 18 10:25:17 2017 +0200 Revert "Update API parameter checking" This reverts commit 8ca4c7d16130c928580f410af9c79663fc8d069e. Crashes if synth == NULL commit 8ca4c7d16130c928580f410af9c79663fc8d069e Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Tue Oct 17 20:50:26 2017 +0200 Update API parameter checking Update Poly mono API parameters checking to be thread safe. commit d5b95232efcc14c072d9237c26a5bd68fe647b69 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Mon Oct 16 18:25:48 2017 +0200 Cleaning up the documentation Cleaning up the documentation, comments, typos errors. commit 72d9cdfc99cef9da1638adfde86138444a8f6817 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Mon Oct 16 17:45:52 2017 +0200 cleaning up the API documentation cleaning up the API documentation and comments so it is usable with doxygen. commit bf587f4998267ebfbbc7682abdb1abec3fc12009 Author: derselbst Date: Sun Oct 15 13:31:24 2017 +0200 move polymono shell commands to fluid_cmd and fix build commit 20d417e83b42ea0403f484d4c45d3723a3616c13 Author: derselbst Date: Sun Oct 15 13:30:49 2017 +0200 fix public synth portamento function decl commit f1a9856f5f79c1fd7e9e31bdf2c1c3276545ad94 Author: derselbst Date: Sun Oct 15 11:34:48 2017 +0200 move function decl. to header commit 4c75d038973413e97e9c52b19237cae4850e592b Author: derselbst Date: Sun Oct 15 11:12:17 2017 +0200 fluid_synth_noteoff_monopoly(): use voice getter functions commit fa00735fa1af49089026abf30f6763e3481bccf2 Author: derselbst Date: Sun Oct 15 11:05:00 2017 +0200 fix declaration of fluid_voice_noteoff() commit f53c00f2e0defa91a49185919b4ea451e4b58ac9 Author: derselbst Date: Sun Oct 15 11:02:18 2017 +0200 fix broken merge of fluid_synth_program_change() commit c18f88df9a8fd80a0ad58ad3a08ea12c6eb10681 Author: derselbst Date: Sun Oct 15 11:01:58 2017 +0200 reorder modulator decalaration commit 12b378244be1b137dfc1e18e4e928bed8073e7c2 Author: derselbst Date: Sun Oct 15 10:42:30 2017 +0200 rename breath2att modulator commit ba3b3c855a8dbcab1a4b8601f52577600061fd42 Author: derselbst Date: Sun Oct 15 10:35:42 2017 +0200 manually resolved conflict in fluid_synth_alloc_voice() for polymono commit 7b31b8605c94bea44dbacc0779ded1ef7b3eaaab Author: derselbst Date: Sat Oct 14 11:25:49 2017 +0200 fix indentation commit 1d1d03cb3a27a99643e5f6a4d701b137191cd1b1 Author: derselbst Date: Sat Oct 14 11:24:52 2017 +0200 manually merge fluid_synth_noteoff_monopoly() commit f8fa5d6534553fbb7d617f06620fd3b746349cdd Author: derselbst Date: Sat Oct 14 11:12:37 2017 +0200 refactor fluid_voice_noteoff() to return void commit 3430db0dce1ff0084da60beb157fb36ca1a23c7d Author: derselbst Date: Sat Oct 14 10:35:45 2017 +0200 add PDF documenting polymono functionality commit e3818abe427a14bcd05d7c959cd6d125533b4219 Author: derselbst Date: Sat Oct 14 10:34:52 2017 +0200 manually resolve fluid_voice_calculate_pitch() conflict commit b94d42b3923bfcab0a270903ad69bba2d3531089 Author: derselbst Date: Tue Oct 10 22:49:16 2017 +0200 move polymono synth files to correct dir commit 2719e03cfce460a132477080d383e5de5de70fed Merge: badddc5 0902b26 Author: derselbst Date: Tue Oct 10 21:03:49 2017 +0200 Merge branch 'master' into polymono a few too complex conflicts remain unresolved commit badddc5b43e064b597c72992434a9786f1367597 Author: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Mon Oct 9 17:50:58 2017 +0200 applied jjc's poly mono patch start of poly/mono implementation, addressing #158 --- doc/fluidsynth-v11-devdoc.txt | 7 + doc/polymono/FluidPolyMono-0004.pdf | Bin 0 -> 254390 bytes doc/polymono/leg_00.txt | 103 +++ doc/polymono/leg_01.txt | 103 +++ doc/polymono/leg_por_00.txt | 107 +++ doc/polymono/leg_por_01.txt | 107 +++ doc/polymono/poly_mono_0.txt | 8 + doc/polymono/poly_mono_1.txt | 25 + doc/polymono/poly_mono_2.txt | 22 + doc/polymono/poly_mono_3.txt | 26 + doc/polymono/poly_mono_4.txt | 25 + doc/polymono/poly_mono_5.txt | 9 + doc/polymono/readme.txt | 22 + include/fluidsynth/synth.h | 87 +- src/CMakeLists.txt | 1 + src/bindings/fluid_cmd.c | 870 ++++++++++++++++++- src/bindings/fluid_cmd.h | 14 + src/bindings/fluid_lash.c | 2 +- src/bindings/fluid_rtkit.c | 10 +- src/drivers/fluid_portaudio.c | 4 +- src/drivers/fluid_pulse.c | 6 +- src/fluidsynth.c | 8 +- src/midi/fluid_midi.c | 2 +- src/midi/fluid_midi.h | 2 +- src/midi/fluid_seqbind.c | 2 +- src/rvoice/fluid_adsr_env.h | 4 +- src/rvoice/fluid_rvoice.c | 135 ++- src/rvoice/fluid_rvoice.h | 10 +- src/rvoice/fluid_rvoice_event.c | 3 + src/sfloader/fluid_defsfont.c | 121 +-- src/sfloader/fluid_defsfont.h | 33 +- src/sfloader/fluid_ramsfont.c | 29 +- src/synth/fluid_chan.c | 329 ++++++- src/synth/fluid_chan.h | 147 +++- src/synth/fluid_synth.c | 1245 +++++++++++++++++++++------ src/synth/fluid_synth.h | 25 +- src/synth/fluid_synth_monopoly.c | 685 +++++++++++++++ src/synth/fluid_voice.c | 128 ++- src/synth/fluid_voice.h | 14 +- src/utils/fluid_hash.c | 2 +- src/utils/fluid_sys.c | 2 +- src/utils/fluid_sys.h | 2 +- src/utils/fluidsynth_priv.h | 1 + 43 files changed, 4055 insertions(+), 432 deletions(-) create mode 100644 doc/polymono/FluidPolyMono-0004.pdf create mode 100644 doc/polymono/leg_00.txt create mode 100644 doc/polymono/leg_01.txt create mode 100644 doc/polymono/leg_por_00.txt create mode 100644 doc/polymono/leg_por_01.txt create mode 100644 doc/polymono/poly_mono_0.txt create mode 100644 doc/polymono/poly_mono_1.txt create mode 100644 doc/polymono/poly_mono_2.txt create mode 100644 doc/polymono/poly_mono_3.txt create mode 100644 doc/polymono/poly_mono_4.txt create mode 100644 doc/polymono/poly_mono_5.txt create mode 100644 doc/polymono/readme.txt create mode 100644 src/synth/fluid_synth_monopoly.c diff --git a/doc/fluidsynth-v11-devdoc.txt b/doc/fluidsynth-v11-devdoc.txt index f26f49b4..9603b2e9 100644 --- a/doc/fluidsynth-v11-devdoc.txt +++ b/doc/fluidsynth-v11-devdoc.txt @@ -125,6 +125,13 @@ Changes in FluidSynth 2.0.0 concerning developers: - struct _fluid_preset_t - add an additional general-purpose IIR filter, see fluid_synth_set_custom_filter() - add a custom sinusoidal modulator mapping function, see #FLUID_MOD_SIN +- implement polymono support according to MIDI specs: + - add basic channel support, see fluid_synth_reset_basic_channel(), fluid_synth_set_basic_channel(), fluid_synth_get_basic_channel() + - implement MIDI modes Omni On, Omni Off, Poly, Mono, see #fluid_basic_channel_modes + - implement portamento control, see fluid_synth_set_portamento_mode(), fluid_synth_get_portamento_mode() + - implement legato control, see fluid_synth_set_legato_mode(), fluid_synth_get_legato_mode() + - implement breath control, see fluid_synth_set_breath_mode(), fluid_synth_get_breath_mode() + \section NewIn1_1_9 Whats new in 1.1.9? diff --git a/doc/polymono/FluidPolyMono-0004.pdf b/doc/polymono/FluidPolyMono-0004.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a496807981ca911b7a1d25d71c76dc55b76a8a1e GIT binary patch literal 254390 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

wA|E;lM& zUDZ*J%3P*B;pR2fF#GZeIPN)v8yq`@G*~icoB-23J_lxBpE4JDz7*!-GaHt{5dZbV z;bpTbXLh>D?%XXmg!OLCF}}4Xsvf_?VcDm^7LO&an;1`LZGDi#Jm{kz(R;OUlOSQVWoMoeG271^yn@p_9c zB=bRhrQ_FA)0@3;9O(EO#k*ibI`>fCi(V|=*eDOx`pLBQlHeMfYR80$;)~JP$a=E2*r%$D83<+ZU;X~7M{zZVTL|hvnNrlIfM&UZTOi(&fNqrpD><5!y04q&0qAAuuQxM%OyTtX!KpXXsA*Cm zgrj%SNr#FNbM@;Ibzi3=#(9V&8Gx_8Eu945r*-F+mJy*ZoK*KuQ4C$^>3ZsFJJTC7KkO_f2F(vZCib51hk;elNHCNMyN~dfrD?d8e!~9jceiykD3qDU>ImCb z+HaQLWgK~n-uQxbyd6BGk6qg_&te;OCH7avuc-*K}&KqQx>j1)UMd zM98F`V2l@OH>>PcTB)E2f7cZIc7>$nc*o0UH7#e+qfBISekz^IP}=6495A|D)3r@N`6@7U9DOw<>O1EqL5duHJpg;MY^yth?}wzZ35O8a+&M!FXJXN2u z(b8`~*Oeofi0-)}CTf&m^L%Mt95O9Lo~HA&xe(DFQJy>dR8o=Y@fjJ7ZaPF>Q{N-~ zRBLvX7RfvCPdQJJz>sP;B9vQ9R>w;#wcJ52dy z$%K4h2OoBhsL7VwVz;{Fs(!0NN4GMOdMi71F}k|6bZ2+MzMRbbQE{G^31y;G>xfpt zFtRg{_WCx(sfm&v(Y*7Qu|8Q9UHDm}_jhcBq~E4y(`{*_hEn_ad7x!x2S6rs@ zqcEkRJiUE{(!1DdNlXOEX2MZI+*!XQ`Kxh3jjdTFB#my4>qARf$ibpPY9(9f&SO<# zq!KLq^lTJw4rQn6Di@NAxm^5Fp%VO89vkb5=1?V**pdM=`UVc%Z0T6*-FoJO(lp2+ z&wc2up?fCTt1T;IJ9m0wio98op@8I@z+rg&(hT2R^5K+2V97*jBrg{6HiAUEcR8Qx z{%laO%pV!k(J{JN`BBrnmM`+_b8@Z=VpR@&#bdI!#dd?E0z`t1@2~YWHN7(vxVsw4 z!IJHUngh?c#&L&7f^77TxcCCEhh1nQa>Kz^ujprLKHt54aH28Eo3>40t>3)io5BF73ZP7(dv&zzKs_$nQI}3vg4evA%$=GsVc%f>o}cpQ)HI zGHcdb@&*f)c`h^<4ba}%vtTljihqG$R_qKZ=8&$1o@)GTxQ9nJFK5%ZXAZPy@)j%y zi}K0Jn?Lp@*CN?RwA*ptx$#%&uC`3JTVv1tig70Fle^ zeYbLvxHD*dK)KX62-Eh1ZYSWI>ZpPQq|;+H|J(kmsZcwO88CidkEQeSy?fWhq)!3v zXR9(1LfXP)9qTJx5e-~yj}sF^GAc85U+?oc76}gFvtNI20G^${gT748!~JU~`9H}Z ztjr8d|Jl9$M;68ZT^htcvn1$QVE!aY&??Zg!u*{L@sGp^dN!E9^CJF-6p4Q%R{a07 zp-unSxrV=vKG6T!o&H}>KG6TkSoz1E_8-CDNgRJ~YSaJThyS0CJ!q@ftv1Ga-&e_v z$15@W*U)-jpN+X6!?ht2vr2nZPEa}rIlf1j#<>P>?WUYpzL1c})TODem8jmi$c>a{-qh*6i;OEn& ztIKeto|2f+Mapf;>^YjO@)UwKaX1^#*0?|{Qt)=QdF!<+zp5eVB5ifzJj+Llq(^;+ zJb<+utqv7uzEYcDj^i}J9#IuXzN<;dLJn>rRh{H#N^`L>v$h0zio}8nU~P{^g?_fd zxvR4g+qe6O_vRin(zWUzE4rlLXlXV!)xzh0yg#_~-Sq0tT&_cvjk@H)O*?N}kmh=_ z8Z_L!BT1x8)#6J%e`BqzoeEUa;m_I5_15&bA4|GrJc_u~0L?j;k_2>6v=EO}XOA4M zIOuYIol&jvM5QexlvTA5^$f|su8L$QEWh~-b#%KMON)^+xWJaPt*q7_N24NzNd407 z3TzYE!27L>^sw_h&1d|(nVMphKvt(jAno}PK zUq~FpxgLVI$B)-!>5@|SGtr5TDTflufr!G8>hti?GzZ@I*m?g)lH(yw5&qgiJwTk22t;ST~!WHqU#1-Udxy_`k>X( zkB+D8;eQ*?{-!#+i{x?S5-Dg>v;@3>m&Khg&y1PH9zHqQ2dCnC?(-NOmTn|CWw$BA>%i{e&;mA^FeyCpuh+DIHjYn%a6QX%E&4 z6?oMmUUiSkb>5n?e8qTOt^hj$RvJG=sCAXWb;|2mI#@y)vL=_lcQRrn#iJG(Enhc= zDb6^4+u?s2_c*v25N;->>WDsI=_Y&LHE=@PM0(4Kq_>CVDAA37fNX_<)mn6+)Qzpv zf^?~Gy+x+J^=N>FI|Iw4Jp&sQ$R5v1rrgb8?QijdS0f4hWiR=>bu>QLt$G771-dmf zs7AswaNhBNi1StIR?}vJKb_hF{RuY+Zd|jy^6TSKh>YfJhGhQ?a?Z_0rtwGLee00w z-5ps5dZ_S*88zx#<}kIb?F=|?n~tPpV41hnr5M$f9cI3ob)H!??k5!*WUEdK%y)UC z$)BF)Gf_jEp=k9|tK^@+yx~?1Ay6MGFelgAeylBYsREC&xb#K-VYJUw{=s_q?XZp^A&U=Ftq4eqLI#c?tjghWDI; zR=u^F(rp{S<1Kpm6W6^jnFMbRc>jIMc=p=?X+5ZwQOv-c-2!j2kG}gSri2CTzBm~z zh$rxSbR_jL_H9mGy-#%;5={5lliU-eWuulhpD>* z$}@B$jabPx(0ce{#9rL|o`u39=)nYMm#e}oNS0PySce38&~)k+Hb1bZknUQ?hMA8F zqYy1e`AQ2`jYrfC;*5Nf3A_rsmY-Za8Wb?g;#zjMxslBVVy#`?9CK<*9|ZQ?XKL4O$}=t6EwTy(UT7 zpg_a6y_URh=~#sBp;n5ON1qJp*?_s3kSDW~UX-5#SqB?R24Ls!7>)SAbign8IAr{G zt^71>a*ugw>8X0YKlwhfx_T0H8ZRvOO;VztWux~nTLD&9&O6RG^}*%ap7EC2mluaj zf>{{s?wBjq0&k; z``K=j_H8eeML)J?(nHlDwNa#hXXcLnNiAq13G2^c4(l(71UKcT=V#KK1!l>1Hskn2 zKdAthjcHqT#409(xq;>LU`!EHjY1HxFwkL9W8UR#;pLAMxtwX2jeDmS*? zVy*GmH>Xv?i_>Or>dE8H^yr2JVvyle8eu+wlm`0~&wI?&YLE?kiE0(%cAs;{1m%04 zx2QhVHG0*t*oMhL{kF{Fw#>8p!$rmjn2V=RvqwtI@fR|Y?Z-IV?n9xLubG0N4Lh&B z=T=#7D_mH}-EPZBP016qP0tDlk^Ray&}ChzHMzyS%1>B^+uacXN!^)LJc>P}aB13R zm1{{KhiaNbyPC`-i2&8^)i{?e4UY1g;t);#xX$>M({T1DzzVOr)s?~LBJ8}1-(~PB z$0Y)ZNk(7%_eEmhkJq;L;PyGJ5Z-zQ`e{dvxi2{^@TYql_N&aQE{%3R;^|U^59XE=fnYMDOO*=sJ<( z1t{xmO?Nushl6IQ!#K8yw5)Xc+swxBr7Lw1$;jPh5>t3jw1<9mJZm%B7_bE z;MV%%(VqOH!QUaa3SO+IUq~aXSeXdK(l5V(ERdi33I$X<3>65SgY; zIJbAfTDe&hQ8^oR!c#9>oP8Ux2nqae`{-IurLMwn0>+be*KB6Q`|7=+y%9X3^gWV`FdTCU1_i^va^m&A zFJl4-@PHia3ajqLcs#$&K?u(-QJ@N~@L*}%;q)Iqg>2&=M+X3zUns^t=%6jlu`|b+ zp9!-MTqJwv&1mEbv#j#A_zW3hL>Fu>AEt@g(+KX#tey;#3 zTId(BdFwXsJ!JFg){={aCv5~Z2FK^*=S*N}PIT28k4`MD5kv3<36OKjc`?B!Y$lvg z>cYKF84mYxo*B`nYy#_M_3y z#fby6C^SEp(;l+voXzL`^y;|=_A_*P`QEBE0>osJJ)3p?l8Qh0pjX>>jN~1EORZqQ z7#hAh{GRUYD|74qOy#!GWEXy@oS_$uB>G|4!V=8z5(MYPgHwd)mds{mRqW^M%wZlX+&~-jr8o7 zI;PjpXWB*-c3-*A%YlMWL9@D4K}8a}cH6gnX|xiF-L+`NrhO5TzwUAMi(lI%E%FT- zXu77AZ|j(ht>}AA8ac*1Y22Dp#H!Kk4hD{qPiS@|YThM>XFS(d(Uw@Qt@)0;4Lf=v zn3~SEx}-iki&#bFU)S60_&pw;t2WXQN9H|R4~Lg{fFQNIn$KSnwMnK)598rE^?2LC zN|0yYCMqPrC5M5Ll#k^wl&OBx|DI~%&RL{UoojSC5I)8t{e`9Vg0vN>%Fk-ArjC>Q zTLu;(#!QNBYZ=?ZZLU-MvK%NpbCID*bTAI*Z24eWR*}ABrh6Y%h{F(JV@+vBHuahvh8}RaHZ|7}PG0X4oPY8Sx?h^ZI|YXR za0Ry~-$dV*l7pA4ypNZFF%Rb0^w_2N_6NF@i$)Q_z&%^W>6ejEO*t7fB+tg|#cuyh z3v+KNO4>xYUGIxAT;3H%f7i%s{DDSkav8OA+enm2#A#Pp5m?4+_dd(HS~~N(!a@8@ z=fpZV%ko$RrkIn~p6=i*vsQCGP8GxfzWQTWo;7b{iWqz=eopByqsp2sq&82}lu&wY7|y+9sEgmqC_#o&@k#52aUkjMTXrYh9@Ec>5^QAVuUE&>|__2*$JPvb~5l)USM9g>F4W*fk>bL^qFtkTBdzr za7U=9!V$XFA$@tIiUb#!;_POlkO0evB_)BOh$|C|72;}s5_copM-6Hb|EyWZZMb5u z@q~By!$-Ku`5nzr<2aM#!lHK07XQZieTxKr|QVu_OAZ2CeuV zw4v%iVy-EEjt6Dmeg@lDNAmXdn*2#DtB^jk$SHeWRmTRoA0;r`5@vPqYjt<;joCkwQDM$^$lKbbrb`iMySGXV@2;I&k1i2{o>!OvKXF6 zsqpmO^?J$Diy(JD_-=e;ks!;YfDT4368pP}ZwDD(KVGUe0+u!=PcruK@p@i@;ey!R zP4a);Ywet5;C`X#yVHQ8!h}h5O#nE{xhWNm9#@?PK@n0ZL!nv=3WEjm#~kP>S%Nt( zM02fQ<(?nH#;B*9!lA+ltV@xyq@7x3+b%dFAk1RzxMoQuvRUpRrJHCR_s=Ts+&a&0 zDhg|X^tP47=!caN`37g95?+sJb(W`3aL#*OK+(%#sQFXJQ8z_rbn$DcGhNT`Dy-Mf zxBCz@+|AO_g|4db}a1 z|B3XRS2~k!4Xbq}s!NbDT>PkD*FHEsz39*)p}L~$`^suL{ugH#*hwzud3M8KBfRU) zlmy14eKX)Xv}loa%Fp|Qy)Ee6OOd-TH$?`=f=%wVFk4HcZOJ!X7A~;y-uTx#KA(%r znhrRjQ{Z<(k21i@S97s-(XyzYET^N*r#Y=yvl}oGiA>~Yxj|YBwdDAkIiMW>FW%lU zO0ur))=b;BZQHhOR@yczZCBd1ZQDkrZC0}Ld0u>PpYQZJ-9JwMi4kkBF=FjKcEpIa z=e*~2AFMHTt5#WUNO1p{D`{%QT6lHtB~PUj}Dmw zO@<1(SfgA;w#mdevdWY$%svT}C^GK7tM+9{2@Qc?rHFo^=~)sa?vBo1FDxygY_`SO znRFXmEwH;V0zek=puNm@Sorji^EA$B;801EagarSqWKCH;;hE5shacbX>sy5 z7q(O#_9|2JFCrWf5Qcxz>=Gr2m|eqW2l#{swVDo&lR3=ptM*zBtlqhFK6Tbk&Z`5~ zKNkH{T`?I&?ZZLz)$(A!g$F#!-N=>6;4BR?I-#6*?nR^I<-(n1EC^;NpF}eB_Nu|U zbU^OU;9+=ODG=K3mt_(*tDrKD^d-A(MA8fKg`9x;8dSFl@{tDJ^RdS_qSi!eAv0l; zxt?%yODy?uPJI-EtnD=r%bqF6#j~3(ZA8JGEB>rDY)nN3!;5KIZ-_gfmScv zKi#?AiSec0RY0IONXK`!g)J%RLt@4hpI*pOG$$t6va3x-?D6{>rdhCdrEgYOVjjfRxa}(aD73Mtdi#E{#jFfJmKdmukGy`ED#yrQ zQ-B)!eI7CM3h<`F7MMeWYuY*S#y7|XzJjQQ8t^4{ChnCW4G10b=YpQCxjs+wFuR(U z+wrGL360#?`VVN2@BBE#`m`oef- zD0TpEjdD@wVZY!sqS4d_!0bl06Zt>{hP&yns3XaUeRie>&YYp3$4K}rx%c`62{W(X zsayy>!BE`jW^QFh?5`avxyJ?eWm*)SdHsC~w^Us?`lyI5F4=EE8TA~b#=HZ2E@<$f zqJqG!b6R1|5Qb|m6pe#pcoLg&P)Kc`L71zLm}AJK5X zVID<}I=&dX1L=vrhC=}#iw$TcrKTH)F~nq~ll#qp213#`1gDx@($-pM)2u0sad^*J zVPzFk9LE2*8Z^eEvO>@b5w-W$QM#nc1FPaUI}igA#Qh~w2P)6|1q%p|!+X1Uvt=zb zX|*WEz1?%(BVbS+Bvrv}egr2Mwde#EvdP~uIfyAx-N5nItxl+hR~+>sg{_Ik`vYPd zS1)3KI(x&?o~q3K@yJ&Ex)&xC$1JNK8v|;1Q(i-(P{LCIF&;SmMtv;rGw*Dm%NaJ; zv5?yj<)e2aUhCb03Te>ZB1 zB)^C_it~dZhvy2}BJ@2Uuis`H_TJ=Rr^7CTtXsO{*XCqIUo}#;H{?w#Py#8zUa(~E zDtUKNKn(m5$7O15msZrEHg0Qg7;-z`(8IsMw1$`fy#R18_B$Xo8x zusdRXgKdGr;kaM36WY?nrGnmG60PqKBXj`B?}v^7KnQu`2O>ZO2D_IZDBUck83H}t0)+$n7hL7l*>$gBP&%ioen}Vpmo~sEwFgq|d zGY5l|m<#9N+BOdc(Hfy(q^L_itsGcu$OGwS0^;ci@+Jfy*+LrvbAZ`7BuDtpRUVyo=Vh08Td0!* z;#D0pZ?)Q(y60Ds?ty^L%!+{lkzSU{5}0YdPjkfzje3PWY@v6_f8s?gxh{S{G|U_G zY>z$ljd`8UN^nUA;-+?LSctvhz{CfJw3@Gew%L3p4aHC6tjCQ(>vZ@z?DxFyd;!jnzr$YXZa8iZBUW`@sb3JY75!vz>$Pj%FHEZ6 zk4y^P>k@qK&;Z}D~NDPOizc7oo-u4n;9rj9CE|Xldh_y=-#rH)#Tg=+dGe@NUltx`>a>& z4k#~=v}5Z}uAfUmdtVSs?Pt}ROBFt^ru4nWwp20Bm@O<7xiu@j&!^XjEPlc5gu!JZ zyH?QdIFGYv#NjeIFbQqGbPFyM)m52QxFf;RM#swTH(6VculDI+EV9BfjG$+i8}G^b za3GDQDMCK4^3!K?^l@#vtIGTssF7`W0{*5YSvRuDb<8h+tcOLT10Hf0ywbn<{xa%X zgF267YtEtkns47*miNzLB##W8YNm=t(Tdy?))rXOS{Z_a(&<~k%ckp~S6!E%Q0S?v zcgN=YJreS0$F3IUnv|6b8+7Yrkj!#kWjMB{$vnP3m(C z_-ve&Pft*?(y0d#Z+W_GSGei=4qPQ5ZwBwQW_4Y`ESl>E&UdG8f!q8T{bkSTLJK2S z{iDN~BZjSEd$ry&^J~`rQ0hvopuGH!>k6=A8NTE;vLh&8;}j@+TadG|kIe~I zwO@y0sdRQq>{SoWQy+s-g3_MV8&2WBF$n8eUc;qrSk z>YEsdb1fn`0(ydU3R2Ix>UXSG(n^(E3F5ml z-8FBdv$Q&zC@I|TL`3#J5`}==7CwQBI^X{pvhDkW31X3(ny*gR@ZR7xI@uR7< z*LhU>!9Ef@h|m`R$D$S?0^rTj7uiPOvuEIuZA0Ji$%7+TDSt%H2MP(e&>=#gl;A2u zDu(2bTAFzOqG(|FWF^PGO%GwkRyQhJrMe_t$8Ssz2cAK$w; zUs|(EU6cxfBnBcx{x~3&(S%y*_|83LVE72>woDW&sfz0Tw8tKlfKTe!m8})Ilj2%Z4x!@2KA6f@)765@F-MGyZ1-098Gh2;uM`ccd(9kF^S79{!`WN z7}8CW4B^pMFm*hBU}NQxG?(CrU4E~)Nyl;hXy54^3>#&{)#x|o*)vLr2bL(mz6p90 zp;RIGBw1?-64sog#c7|ySn1Ih11Rpr$;%YOx5XsCB4Rbh2aiG6*RHfC#pwSe;{gGW zkH`=QGCrFW%+S|xV5+6$L|T&0&H~k^_#~-nR8EN397h!ZHY3UO3R(^%vzU-7l8Erb zzgp$&Jcf%?ha(DwBo@QpyG>~_H(G*+If zGR0F4ECcD7lhF?ZKtV`M;hz~QLQ^-HpL~Tg`pkW&I&4iSyttQl1fAIPBQxps)xDpI z^w|QkWMBS+~t<*YTus%L4 z-8nCxL}rSFL?tI-p7*#138}krmpCDA-+P3lj9iz*Xok2so~d#rVC*yo^8jg=rf>JF zThH-hkL}$);j0EPC$k|eFAbjD!uRsgrO5;i6#JcjEYG0Y$rtV?m_$RSL?}#D7_w9g zzhf_gC9g*>ar`Y$jH8SRPs|E~WPITh@Fe$n;4N-UFIE(QU0%%8H)Wy6$XAYF-69+^ z?8!Ht`qAK-qtf;b$wYmnrYs3mnNt3g;HCqGFL`n_k&J41f$nmTHr`cI?4Ht8ObPYg zO7EcHK(tY17iUT-6BH{diO%*!IGBho{B=vwczTd)BoX0s44dfWfOcJl<-7WmZ5@_d zLn)9aCOvAS)NcmV>YBNFOU( zSLpNl@iG=JFqTc7!Xkr{^be2{&S}};1d_CkBeF9%%bGTUt;t8r$oli8%yzvG*B#Q8 zNMdi1_qC}_^1=I(S?uwVdfdZxVpp7?dlr-{}WOUb$M) zwJNFIKvU0A#&?=yG9sm_q4Jmj%Cmyp)7sK%+G_JuX zM2oeJo>0fL(QkvW(xd1jISiovctfpwJXD1il~!!b(1Mmxo-izgJ0;Yw2SORnfdmtG z*aT*AUA-Ht4d*~)v|Or8vgbO=p7aOdEZg|AP`Ru^ln3*A){gE_&?R0Z1vv+vgJ?M1 z=?RZUJgBx-V|L^v86EdNs`vYdjSkj{qhwXdcBbMtqN<+i!ydxbiY(TrBdQw`HTIZ2 zkTAKMBY22T1ki1Rqi`vC_n4=(qaW)dv~Kxsw#^ByPV?FmrN-wy&~bQ}*(P9y>TFY! z5+O=U_3<#)H$2R4lG#5G4DIR7T(5ZflGuJaOy9=J4IQf@U8OAyj&@j~G{ zJqyq1jsUNXm&w$E!IWWUeAu5`wO7~gYrzo+4wjDUH*((sy#YQ+MvX9i^41=ch2~rU z3H{W27HndN+HU-5L{1_SG)EfmUBZqDqn)sY4AYhJ=M3XJ=Qc1A?mk`Yx}W>#_K4iT z?gM5{YVZ#tX>0&-4c6u^Gp&PnEYBd>Yh&|kk-#_1tjsCMe%Wefu@m6V_s*X1YR1Nc zRaV3`133)u-_92be zwWMVziC;c|shBYqHJb_bp~HHCSnr^O!4MsrY31X0OU1Xog=LA}kC_6X*x>?+?_hkB zcoe&d1&U9JT}l+4GRAjV2yxMEn~1h(pvo~U9o&&!QRfUAJW(;m3JYl`*!+>vGUs~8p~i^%xLy2N z_heO0^$NX!)p;0v2BcT^+k<gkCs57>S1DR1p z@#JjSd_F}6aP=1^YMx_1QqcTCW$fi$09k4{OmEg|{kO^}sSmZ>6XvqhksN=0lWXfT z$>U?&6JF}jE$xpJ<7OJ9;!moiZFyHCU&&mbdH~h>prA9xd#`7KlY#Lk-N2YaEzR6X zE17GLwK{i`kQL3nF!s3#0BCwzq334CxgG7Bo*o`eX6K=&w}POxJ>&w3v7{FYCC}nx zzg(C%VeuqR5HhEY$N)>HAroZ1 zq!ZqY@2J(s>?un0MIC956J+u595$KrXg2f4O)_Y*C*22^c5k!0{-~c6TEw}w!`XG3 zE{z9ZTf^cTXSSEdN{#LM$Px>oA+Qeh+Lm$z&^*(W-AV&s;L8zfewr&5r)~__?Qim) zvYat`R>@CK)&fTL$cTisS&fHN84?Rm=voAyzhULcD)@p+$8lyKyyJRz4KbDjt879x z51c8IQ`C=eMtNM^hc?9+#@E|^olFMOAYo;akhS!3OF)4SN5*bh5RSq@0Z5f%dGWN0z; z*Oq-sz@>5`!~}X4J-DaQY(R{l3SPEcG?R7#Xu z{TrnA7k|#_Z<$BL)XCV0f2a{~=)gM^W_O1zYywXP6XeWHbE!jzxeLo2fwlGhV~Ms7G~!E9RBtBf6XyK(Mx^<>Me`~ z|6{XUf`H#1soBnJDBg|KhYiEZ`APEIZ0AZw< zxXIQV%Sy!QF4kwj%aBNbq!U{HQf>TAwrZ0%ZLS&^0S4kNtpGgWOo1gc;#$ePJ}0!~ zx_6?nf1*-o3_gFqEvsatz47a_fVs1w=dI&8^M1PP*thc;4FN!j5DMqa)|!G2^ljWd zCn6(#MU+c)qYW1zmBr0w9v=02Lp*{R;4YsvtEI~8Fmi~@KLMDas=KPdEBZ#>Ny(wK zigo_(+{OwEus(mWyEz)=*Fpjwki@#oCH2O#%{t9H^8~=^Ma<(={Oy~(f?vt9D4W|| zMiS@27Y5}cH_^G)$jCDkYW4IZHhZ|M+G@R7R6Q$l0j@Fn)A~DcYmSlMG}x-7>wGg^QEO95C;}S+eByRQ3o2Y+hkKy+RY;Zz zH+*<2YfIo);8)-1T!_qORb#{2jgvfMz2easK?tif?B z!jyff1cWiL&A{gc?*|+5ThrKj;tSR%>?b^IL;Qvr zPJ3|usf-t~?$qn!n){U^%QE?O;;i`TQHqQw8B;3F@hT~J$(l2TmGb8bepP&ReVKG< zW{Mi2@d`9GCAF=jG2U8Sqo;CGiI`q!l7XALWbm<7ce0eE0`!m8K$74JhNyFUV%zM= z6AN#!w_AwAJG8M4cdV~qzdm^z&JmQ9DJZi03S(GnQtb)6AF;I~t4@u?;HwT#Y+k93 zIEH4FBPQ8nCHK^=v2T)Cf2O>dcgErG65mcIf|Tp&E*dEU{qU99tNZZkfiDR^-OHwC=^q zTK8|TDEfprZUqLKr_>m;3xR73C$Gp6;I~b>8ai&phwIgAf{wjsz(4dlN4y=3BirLF{d)wccgDmzF+4ouQ?oNppI< zsJOeYX8Fv(bYxC!<;kYGs6wpoU>EH8CL!K2MGC@LUOK0(%QmOg=ft6KiV~U@O$O2 z1WRu2(Q&ph>yOwM?MZg*N@csQ3%Kp{B~uj{7|TAzJr%S^&|Z{!M4V;grIxZ(RKX%j zOsa`xKVGA!L0+k6s~LLizTKh}sRAY|*qWJ2#?EB~ne5}p#x$2l!JL&t*U$(k*-A^c zbQuP!sRy79(DDmgnWAx(OWOxmYTT1K44m3&N4ljpEC z9v`~IoT5L2USy1ComF6oSioY1E;U#bksm#U)$D$$_SrPQ!8qnPtb|35cynI1FGUSC zpZ?JIxiPBPpb{p8(o(_Dz;j+Gh1IXIEvn-&Hxm$KEJ*{iIIUz6?XL=%nGd#GkquR} zXdJSIUobixmeJV0x^jbBq(sHbC*)wssGu~j}-N?wdrg^0=8C@RyXSrQKdn|PS4DQbl|m_9KYU5zjw z)x7Zj(|B5RS!mQlPB=CTbRA;&gj|_5-XS{SmkFheLZ!eV^O)+*TxjCWQ9mRSoQ#RfNBC$vvl2Z2d* zwLUYgRz}}YGaor+Y-0e-=9t*PXI7TI>3vMUo!KkB#wI>|ZO6bC)s>5nkC$-Fqpsb+ zmt=W=9Dc-8xRJ$|E21uKXZEAco`#77&4$Uj1W_6Us7_Abov5g<4w;v3VFBAxI9-QeP}@cANM*L%o%Du)p?!VtW0E@M@)$+7)0 zv5xV)>sk`9Cr5{gnMJ;dZ?u|MM^~zGiHwKfgj4VhJFkDp;1JkjX*SxBn(<;CVJ2eq6E^9;63|>X;-u#I`|k891;`EU!qDG7Y*zdQGcRLSNjLp))R86%-YB(1Ec#k;pL^*AjaE>rz$iYxNdrkO(0)LLDUbq#GP@y>8 zn;P}$B(>b?H;&1{pSen$egav4A}lSiC9qqX9HhTrINe*}q=4hIZw_zlfWGeg?Hc87 zADXmJmqC%SR6@#a5qWwO5-Dqq({4fKMguh;gqrjCr z>o!BM>^aZkn}bsHQ}iWVSnRyEOr9%ZCN_n8#w=-I{vM@;r=ZgWPieJcletu&#CdB+ z-!|pXtom`NeY47(iR#6%$S3Ka^vd_?aF@DUb^)|Yw04HnT!q8FuY3Jxw~vIAj!i03 zD_=fg&*$jW#o`648F(SGDg1(TA#zx?<{bxZXv2b!?4j02(Jy|z^~EEzo3&LGP&pW= z{8W3$9grp28NW~cr;Mf?4HTrK`w_Jgdd`ZrL9k6P`(w~Z4?*Y_;F16YS)f)M&z3NU>G`*?J&w1`9mahAWxB^l|^(NWejnw z&KW?zPyO~0Mg?$Ggs_4=L8}m+aCS9*Vgj^C->mY`Xt;kzg~PX_<*Om9t8LA=f!(X; zs!c6~|HH2JJ8}5*7+`nyBeO+j3Qq1>W5XrOXn@1JSk`KB(qX}pncKbN*>Rf(}*2e#-qZta?>K$c&#gTD$48v-hX&fHq~C%Do@UJbZ6jhTo3;MuZ&Z=1r(r)26G` z-Z)G9<lI(&WtZk^5nXg3+LOtAf7kTqJs0lM@{T){$SNnSQm8m}d zk1YxA`<8UO5Oj3os=Eew^nn2#B_^Bx#{5GuEAgfAKb4yHWg;7xJv7^v6~8ibgiG8$ zpVh*u8}3OT`0*k#H~!cVYaO4zyq6pL5=KD=dHMJkGh?8^`nT%}A%^G*Dc?Ppv;_}~ zZ)}I|S=f34CcAy)>QIC}`^?`cRCWA;o%}K!QcAj*SIK_y`FiJGCs=2*^pb7vGPgcQ zS%H#&W5cOL8)ruHu~m=W<(Zvh2lwK?q@@1g3$=~KjU157nbDj!keeo-vo1h6jJm-0M(7 zV>MSKNpwqI1vmaR$1H035aHVrx3h4u7O}|b7L;YsI|_RD_^S!%Ahj~R6Kkh3@a_|M zIc>xkY8k6LP|z#@^$$2#^w@=4d%xS`NFBEzb0aWfQ6-0F{$cm?c%}Cy!BYf$M{DpE zSKxt3tcCh%E)H_iIn@DEx)LupppRF+FgTkk5i5TeU1Sp`EKR22P8QGfs|fI0>#QD} zIW0yUz7w9`VTn3gO2^UiC|gszVJ-bkauDgE#@-7wT=Ea;n3LQqAhgDE5~20*y1!M( zG>Bv%p81zR)jB4vywmMaUBsT7C%u7+Fl*-++d~^Re>0?3Gdpbwm6%qGlw_N)7^BDg ziNj1_SIMHSin#|&RYC)8*y&DQ6yJGbwR#vJ46?daIRW#vPOJNh+Us>v86VeBYI?Kg zqhpY5_F4&d~{_+gs0~z zFxEBd4T-=6I!71w8dg~~#0;w60x}VEiEw*$Mh2^5kt1Lat76%G^pI&hN9?JaLk#1Hzhkvw*7@iZ2ApqywPiS{ zGUT!R^r?1Y8| z=FzvtNV+*I%V4ztl@L|c@hxbzQGR})(B3ZA1c2f)Dg>s+gd>IQ-Oqj}3FXS%N9LdC zGPXsS=dE07bb*U5g-fo3Dymo$G7NV<>O)~6N))#$_3`gzJm9+M=VsdOHgr6`f6gZ| zj)#)f-iX#NTNXVZ@UjG?W$kJj8IzuA5c=zychryb{D|8^RP+|Dcf}SfOzbc>Sfz(~ z5xlUxLh)8*_=Y-jl&0sDSNbM$e8aUlg{z5r=r1zl^BbNt&a)jTU9OM`%!!XuekCdW z%mW1Segv6m5*2ia{$t4}&kgTyoOf=d+__J#j~LioxG850MuUYL08m}`E-7zwbx6Pj zI_`CM*Wzv5GR#jIB$s*}W)!tCzF_zim}MSfyl=Q-FXGnzmdZ9@b5Fzd3%6(!?xnHU3n49=9eH+1eU$(8b zze@HXWc%Hv_c#zFG?zJmvxb?0ymE8o!&rO$!vp7(dhYfeezehoMzJ-^BGe`4TxDKQ1P}4U;GcZa_PfJA5kMzlUMwCGmv@5C@__*PAahPj@2GWUhR~tifS?&Frg_to*<*`mHZ3o~xwKRijIEFrOSUcuyYxgF zMzDBc^pHVb&cp4W!46!-`LKqDrWU5B8k}b}ai&K!?!ixZf@a#PeR)cy6(O?#GiAow`XJa?Y--#sR@>l38Ge+ZjnQ+~3A{P17p z)1rZDDPZ{G1bI*lVpqt#M7b6B08+itr&vht^Ruka*N$!fY0Vv{W)45mFNjeOUZ^hJ z6*TKXZFheW;9{VKxixHn5`nwWiZK$9$Ze0x07p7IN=g$uUCqU(-jB_+=#}YUjyw3Y z4>lFMP$+HrfWm`F0@D#Z$lj)`@;8#IlQ1eB-i!74rIA>q*>(d4Od7NA#|Euvb^ z{1lv(RQa7gVV$k1zLhT$q#vCzA@tbQtJumUufKIbkvT z#c^*bp7%ylluW;{VYu}vY1<;G|6*ZsS7N-fNnUU zv8kydm=I5XTal}$vIDJSytyq4aWXEWjdV?&Gugh3k6hv&9syt$AT?kgP(WuGSyk#H zQ3ojh;xcYqNH_$#XgmACb&>%g=?JT0;50m#yfh@r&>(DhOx^-^`P*Wa>6~(IS0R6! zL!UNmGw9L#uFZAJSPZlc4g^aNY#69Mm6&Ik z-!&nlzi}*~rL`=!?|`7~iasGD(;hOSgJy}tL5R!__9NJ(>WNE%Pej-%qxRy{@>Liz zcQu@1y!h49lf{lu>+k#z0$Le`Z6?J+sXZRpB5*Fps3S8!&B$A6GG##2{-C~VCz)`| z)DL!XG#JTD-gT&6jK>u&Ga8u}2XRM3G~4|Y z4e}BWhS!7FPmnv#KeRo7!&TCd58=xeMo*ISlt;kFy5)NA&Sjy&x?_0@n{GHRIYHhP zBlLJT4B=TI)RP?t5IDD&AxR>*$sVfBX~ybJT!v=db_fbsAnc=5Y41y-uLOfmEbTdR zV9N1`){fRv`bDFCdHmzyp6o0YtW~~@24s-b4CEFVs8b9k-p*#Bl9Gwh1SHn@yB}wZ z_O5iiW0;BUp|s|%;q7=MY~5~8u#{>4^$Fn+EsTG}vF}{hHKP~cw#hU2oo(i!)WJwA z#;cxsN~irKm~{7j$UCsf>)-+sQzuJ0+X25BT{rl(5Cse;2tVOD5eCA&va6x)sOG!3 z2YT6$E3M(RIP@B$yFSI`E`LBl@f_uLR4FqNv)K{_t$gB{XAj{s&I;}0aM$2k>3sLh z@u|1XhBtBz2VAh##w!mvPZhb&`9R@bHm`Ws+HT5PuZ198+pZIpUreR$OQ^UYV?)G1 zx(g|%jF-_`&y&0LG@@4ZAyC;*m<72`=<}tuZJ`La2?h$K9Y4?4!b*S*am>1d=QWSC zpor0Va6zB7vY$d+CveH#UrF_(Q)UTHDSZYhe>OPS32utPK@^OxQz!5{-X7y970OnT zCJ*|*1lrH^dgiLkyI$5z!H9K)#Qo4$!rO?QfOqiq2SlF>bJ^Cr=+zgl4_T~a^-fZ> z_oK^Y*&uchJa#A!NkWfX>|p3S8}y@!$6%bW6S)A^A^JLPz~HV(G6RZuB7}o$>V4 z8(9I?#?PdfI6CRcJ>$)$)H(ee>inn<57Z(ScT(`X=q*v>Ckw>aPBCCj0uwTz0cO-F zbdP#j84BdlNd~h(=p4gV6dWwPE}mFaKepI>!X|)J6rL1;qxWN(OrSi_1O6|2$Txr< z%2S8nn@dx;;6#hUg}$BN`0Ih_!O(I=LQ*vW#OIu#{1@YyNScP~;Wn9h)~B&S%*}hF zW8_K;N>F;Cb|E`%0CY)i=%(Oyk|rC_o1dQu@w#S#0XL9Lh=`XcoDh=ZX!J`)t$VvC zQ!bxorxL-nP0mC4e{KQ$(Xc+D(Xy?!*ODdqZ=dHwDl6MuoO76nF@h;uegjatY}lAR z*$ffhq%bRb{lel^@QT)L+=v$DxPLQDNE#g@^I{0ldBm95UkX<1fNn+@ZIe%#!d^{C z8aZ&v+t)pCYqufkiKR%#DN&E2z>4bLx~aGR)%YZxG#-1d>F2B{|8j&Ux?FQ-ANQ@Q z5*w&x1H=pSy76Zm_*WU$KT0Eav|Tpu#l1tLM>> zxQBj82jP)bfBYVym~df{M{%RjN84URzqgi?4u${)3Or^O>Bo?Z8N5))lHJMIamrk* z@lf;_&_{YTBi|s%L!Y{ca<5RyR;mh%P$o(ga8szK#BB4EqGOqENFS#d-L{he>Gc3x zW1v+kizp0$NfMg(Ur^qOt}uXz=-?MjNQ;njBa_Mc*cX?*4Conq-QNw=;07yRxg2iP zkw|0~-C-rv9!$Th6Q*6J7Q{$UsY~Tjoa9s^=@l+>7IW^NbW!Ms0?n(2&3BitO)r!e zps+B_3XP1{OJGONm?{g0Z(8HHU7vF8WYuZYW#_JAOTuj5U7u%Atg%3!!a9AmD=waQ z9=1UPlYJ_Ipky%O$`@o^`&yk}flE`5@uCxa;u*A z2>fH&p?IFXv3pwHkq@6p1j1n}$W|g;I9b~dk|<+l-~~W>Or7m?s4JKMEJEw*=JGch zJ)I8-9Pf}=8Baxjhe{yGqfIDM-XntCs{^5P+gX|3iwNjPG&C1%j-zFzPfLPdarUH5 zrk0E!cdXug`()|V&Xl}%?GpN*tJA8&D9gg$-%}p9QN^N~>OQCGr4@LJR9lwGjGwKB z?lry{lr1v~fV3w(Dh7umA#>nwzk?9s)**8)LI!8T-M9g|=uatQ@?Kxy3`IX(|C=|?^sfX33o9r4zr69k zHK_lUo%ECV4|D~(_ z2VCr*x*GF8+R^>*f*U$(j)%>NJ}c@sQzn*lp&AhW_rH^}T$EfpMJUxRGo>t26EG$5 zBxA_=k9MSkNM>PkJZ@qCQEP8n z=2GU*!S_=2+lJV(G!^;KY&hGPP8VFvVaZpmS8o3lFUSq3c6uy{uC2mp%me*eKT%s1 zq<&>;v<-cYot@1EW1f_akX!VNbu~o%Od6Fpbg)D(xvjaIag#_b$DnqJ^qoN;w`7VnR~ zeuHHBUIl?aoEYNMeJ`j|x{8re{l177ncB|{Bx^O9$ThxLp3dy1q?(|*EzjJhD74X1 zlstcemwAfzILGR@81+(JD_Xdi<_k))w}yUn2TM1bm#xV@ZzbzVFyYbeFo9Fdj3x~SYh z+05|r=?y2C-Ja5F!Fx-V&+GtJFnl*$N%+k6(UmIl7OqtZlEvbR0gc^5wm|A}VuIbI z(s~1t)?;4=gX_xZIz!XVs zbQZGI9sbj+R`ZLzI;U`q&h-lOveTl&FmBMi=eFl;TT*nrn}~K8Yt9MfZZG508_8K= z4ZGO*`bSwIYj?F`$Vp??9O1NaT6vqC)eTG(cS2(2dERGxpW23)*0Gr{YOycr+uYF0 zA9$@@nTzZyyMp=5A@clckwm(+1m9+yv?0wKu)`xrOg}&19xFx&;gT%m5#@a4dyH(q zG8(LG!9Vel1kxjjN(!`)XU?LiMDJ1ZsWOM>FK{^u{QAa0ZhfVEUyi+3nh5ka{mRb< zFn!OVJBF-<_H52yzM1UM&>q5Ics&l|5M;?|+G2YO9vo`}`ekQZ#sQ%kzsD;CnpM4DMUy-YO|7EJ)oN|mWtBg6PZYS%QBY~?orSI1x6efC0ZtwUb0!l^ zo#SX~;K^^zyxUEvzHeZ-$yCL(G+KP7>PZfM(tzN7UONwomT?md}`bdkuRwN<4vlLcy=r18?b6X;>$8XoZ1s zS3Ld*zBgKwMxYvtF?B67>qt~EeenSw-HPtb8x`osOgf+e`r4qY}eEJb@Zge*aSBU?H%^{1BUfS`tT$Fp##+aH@rISeB-% z_Ze&M=1=UxLvq1{W1dqG3NmiI3@ zesWlXwI5r@HX9e)mwciBayPmVQM8oPjBAVlyer0xDW==|{|0Lc!)Lev(4wScWTX{MT(L8#jAy*ID zJMl+F`pjfzI89ANX!kG7ywFibq-jeM zb(ny-3NF;A=D8-fH5Q~5T}=Q;0?wv}STo+!)G$A+Vn%FfQ&0OJaz`A9T+D@adwyjiRW9CA*w ziHIBSMqqEht-iFwj$arC!d0az9%RQ3K!r$nAcwg`o@>@p2cdIwpi=S3Fk=5Z;chB| zrZxZIm}|m*d>nKI5aLOIJn^?p6#DV+Dz_vwzmefcCC}EY1Y76T+S~5Zor~plood<7tr!vE|L=A&9s{NGCA` zA<6oq0metv1x?wma0`?0)qifauJQw{C&dPIC%BMu3bIRdS?;DPVWjmn9wCdDCPBqb?$K;0Syy#dRBs=LVGLwLIGJT!I zmA{25O?0#+hy!rwl{F5gXtV4MCOE|5TMw+D%MC5bts!n~)q;w0^~d7^w=9ktJ{gsjr+Jht43YskKQ=7Gnm4bmy#OqV>&PMmEn$4Uwm?D* zNso>!d1#3Slst@ZEq5<*JlCCCN1=u`aU4vtv1FVA|BDuFdS>p~Cnu4L;|G{a_KmZftGCdkkheUAK=Q#TO|NEAg6XKP?x1 zGITUQFj?a1?PoUY?hP^7iy5QoMPG7?=Kd?j6W&SL@U*)2L5vi>F(oE}03>BB`L2R& zoy&-%46BOd>-;H*+@tnX!%=DcJLw~*QDFT^;dnlaT=C4UkLgi#^3y zBmgU-l|4)+Ej8c@IXH(!kkZUp^RB@JZgkLAVm#yiv6}#?9$7p47yB>c*;#dLx-Sqy zgAE8DdhQYAlM35JXCwPZo;rKY65|enOBIc zaCc`kVE@E@NafOz2}k>aN>Ttdh-w>mIQIMMKT8{Lk%^fuEsj3Ha~`L2oc4~FPTw`s+M4v7B5h9^NaOv zHwN$&7jL|b$6(6@)4Vb-4T#oQlT(lQOAq21>ohxF0@JmKl2OeQICz{`&w<(4cLHkl zJjdd$iRfF?>9HCk)u`0#%Fu7I@8Cjzi3Xh8Ip-KIB#2P!efj6MXNv3iQ9drgF;>0^ zVShr`z0DlmsNkNzkB0P&3yR!{G9MUOmTgeYAn(Ty=p2$2p=;76Woo3QcxW$=9ey#Y zFmsdehD!i8*lj55)B-4%7hyd#m4|4~j|jcg;sxvrYhby-cGpV{2#$Y0iQ--Dj6K;d ztWwp^TX3#=AimcLJuL+#Zv1RZw;$j+B$&;z)xLC&qXJZT0?Kne_BOV^eRtt2G*7zt zW%6iOV=;gz?Uttzq?8}MKL%#i6ALv4Y^KCUSky-4A1>1D$%rAp_ zx&d7aq)^omp3gLFLqNr>j19y}vfNtxKx}*EQu*qAe8h(wfaZ%QJCOTxG;8h*!w zP8oqQx^`F1*x878>@l+N(Cm}h3Ho?)z*Lj4royVDk-XZKeS3KJDk_`Np#9UMrdOfd z^ct<~q|G9RRbkrt1KG>SC0sgr`b%85C|4=RALAN^H0}pcpY&dN~Aqze&Ao{2`@$v^Jb-r zu4&ui z&bkuW;dBYhAee7qLeHc+3PJC9|1`OH6KIqg)Wv3KiV*x_)WN>tI8heGQ5kMlCy9e_ z3Bu(@YUtnmR_BWs%xCUM)rvpPF_XmG{YA3#=_gztPw!d0Me zxK5U^g4vn?hM)GxK{6RO9vybVb=$Pwbw_E}7`&As@@a=@RY@Pu@| zz^xC^z1G(IU4P!Rs-9N(xhKUnX|$SgH8m_@A*!;)mDXrQ{TadeLPHWCb1`vP3bv_0^WQYRKS?GeeRkNG8oqzyoIO;*tcJd5{aChWjF20ZTDD0-mnAX7m2q^yY|H!^ z8Q-Cf;)c=gh2bA?DI>Pu`5h$_r&YwEZrIZfP-tPCI`*9a3NKK2rDKZaWF1B8wI!Sp=$y_DgY2{q!s0W>K z6_ghlYg%;QbvgNAei7mg=mt0n{5Ydrc}>yj^BShaCWiY>q*QwoXK}Hjz$QAs{gG>ld^inn$pOzNRv2OTYEu4#W2Nnd1;rqr_=k>$wZR6O(rzHV9#4ADxl-d zvntDe>3q@TT&mz~Cqdh*eQ7*>t$nd)84Ys5N%O<1Du8~>ZR_^hqQp^j`ffbiU9^G+ zIL|OaCaAFb@l1#Hh(>Fq^w$}Jsv6n?<1etoxl8}_1IeD;VM=P#gyT7>1v@3eJUKB^ zTorhU!&*;ROS7CRz<2risk7&E-ASM5FL%Qq1(LIi;Acwoi?*pcW==*q-%d5$Lv!93M>Nfh{sQVpp3DLFjOstnPel+J;I#$4_=EKmPEs|AK zg-&$7F~#HQr?_G$vJcE>L~~Leu-R3rdnlCvK`6=@1sG(DO)al@5+ib@eL zy)dh^7n>1i9DqgRMAM0vDuuP_&o}GrIj9jNKj_!m91ORQv@jKRK<-m z#3zS`r>r~{YRIA}vk-;Pc*G~Ms@G%;n;-$it(?cfg7>A)mBaF4@()5rt zHL(HPRZNaxLKB9$CxCOcLriDX3}UfX!dtx&4^lHW3m?BE8n?1q!xcMqGch0iL!~=Y zA}i=2(X*ZUL%3??K1by{aM~?X8x+YQ$jLM{Y(7lQ@06JW7e5vAbtT4WSx>t6DJ+du z`Hmf8h`%Wi>s^wXRt`I=zqXCcKRWLkCQ(VRv{!3gDE43z&s{8Vw)H0ZBIWnd)IL~+ zd}@E&hmT)KRDTJfXmZu^&l_x^_cZEUl^gXH^0%J&i8oMO65mrpgeG@r%ECBM$W8M;Y(FTrU%wVe!Q zeQYonXxKdlg`p#u-F-aq82Z=%rUNZjXsI;?Srlo?!5X9J612d-<->V$-g$+nga8>i zRSUX^0XRz4u*I~JMpR3>E={~9s4vC;(=L_IzlFe-s7ikydKzfq+W^ZN zOBy0jg;2?3I5RmU~^ZgtW!tlCtlNGq>y7hX8_d0gNn=yG84jlJnbAXdw zD7qOM4eL9!HD=Al?o#Nzb#6kvbc+cMR}x`zA<{e(E1v-T!GvVH=BkpmjwAIxY+P;=9&_l2IDpe=Y*2m}6G z;?7P8{r*xt-0;C)C8m?L4-t?Tn1zf{zoxXOWMV&wq>oRNxzS4l>bN!P_H}w{B0qKp zER~PS@!~*=xmk_DVJ7_Y5F$&S!b&tYlC-5XOd10DIl!;6U@xZ(b+NCD)>AL8Zf*vY zTi$@{dNu61Zls|yHrhqdvWtY0_a|#yH~o7T-QF+l>xD>Ah(w9b_T=OaIr^^krqQuy zvlyd@J}uLa^E2`Qa0?~qx_7zPbA8=o+Abg=hpSyrl8y*FOFDqfScWiUN-BNSv{vos zJVI!Ku3hb{y7KAnujNvfLw-cS)UYPBqbVepSKYFl^KbW%OP7zStC$fH9V|9qS(ApV z!0{8sptGZk6VH}qD*-weF4tA(F)|J9W-0A}CZ~k`pS4u2^8>pUbxSf98ZAUjm}ehn zGqq5NJM8BWPoRjtYpN|=9^x*Flv+*jodQULKbe7^{}|D})0DlKXU&WRMZzQ3p|5ze z{fcfXIR}kZ)fh-|<LRP=9F~Aa0`pYfZ2A>GWig$X(60<+3UNk9XVxK_O|iJ z6Gosec}2yCVP55)N1SsT!&a?hxZ_Uu#-44c8~|Tuh;mLG(aZzN448uhLeLWIHu)^R z#|q(_tSiZu*tjLQzH8oD|J!E_EU44zXC5VZ9eEw_>|mM6kkgD=C?i`6**rAD^e=6E#N& zzTmz`D{wfHS~$GTg-CGPC{r{F>7c6vliTIt9|g295GySQ%SLG*nkwx*Lx`7mvyIH# zu(uy?IVm46RZ_e|P85lyoqxbh;z%nE^q|ZJSbz5D_yYMPEL!kXl`oG2Nh~5021OIV z{MCtfJM|h%A1?Sj1q#x(MN-J^A5#s%?|yT@k5U;v$Hmxo6DM!Zqe#{yBmZU~N)~(} zO1)aJ=aJgJa__Bn20fAnAlf>e?Y}^Q$`nTl3Z8S=i}?=W;Ac#b5cAhE-|@b>4oSp>q(0{l zU-FX&{)I<9K@YsnYms9u=ihm8*aE74i7`Gz6e;i4*W|@oI%wFnlweEOTj}7G9vA_< zFXtD6r!%Vp?q+w)r)DBP34&F3XxOvWJ}C$_Iv-2;r9k|<4VPlIhSDU)G&&KrFa6qm zX?_8nfoM+o0*RgCTqz(F!j*}39Nr#rU8ZgA0m2_tINM-&!T4y8Yc0BsD3?32Zx|(C zvK|}kwqG)XyC>MuhwPWIB=0=X(^_6~-7%Ip%A1%xo=6Ro=Gu+r0VC>3wc`THPHRG< z`B9-*;S8u#Yn-hv^>_#h7r%2#u^YY-aUGvLtnx7SZDldJ2f@{iY@7nWt@sb-AH8c= zNdxSt#*Oa^P8@>Xhst_eR6X*xZ~(Ts^6j5Jlvn{HAP<0t(GZW$#P>Ci=8=#3YM82l zU0FEqoKy!bcq2?(#hl7p+Fm!;0+P8p$Z4>q=FI zILuA7hMDstdj~$~F1PWKKfjy$`%7Im0S|Y!So`jhi5m{uFEYB{|2}HNRufi8ERN)$ z#96b0<64eA&<Mu>&3EgUWrJ-K@&(Fd)}-FRNkZE>(z zH9qfRAT6T{oad9JzFuZg`MG&tlG()Si_aej9JQI$n$@zmMCeP~rR^GQtlY1?mN2%&IieaR%-?sLjM=svo-R^k&GQ3gKDK&wcNAfed$Cuak1ib?rL59VOq1Dj!)KHs*l8LTo1`&A)Wi6ZkT0Cz%O9BW))g`jQB3 z!@p>y4b6XUC%xt>Id@B$yF(8}&-%0d zrN#c%o&Qg+4u=16R|n%ix;hyDs!H%5T^$Vn+12sa#D5f%jQ>4X2iB@RR?DqdkN$RN z#$A7ah3~F_yb*_8V*LJ!9(hu{Cbc$lnkW%feApxKYw^3bFJ^8B-LHm*4$Z2{_)&CJ z+Sa!#;pb1znn1;S^3UVp)$>(!p4QhtTZ74m?YG-eP_)k_hE+~iwvQVY_oD-ukkQcy zjd7A``mo|AyS=rATt-_;Q|;sX%j3l=Kc$K=>#=9o3jtG=_PY^T6m5HQ#C0+S1-S}~ z{jDQeka7}pgb$^YYHs=a-g=QEKT~lr?x&kspkvfrBSXzq`VekEf0XqZ2^ASd+Y4aQ zZQkp|sNv-Y`!ByrY@DCd)(D1xf;+#;bk14wE5fQ2Z@E7Hu4GH|9@<&)-tsO&k>A#4 zK^8yE&~>P?bUM}FPy-bn_hNRxEv<^vSl>*1QG&#&>NM#bC0*5}aVO2sAxtM% zkozyYceD=DNY}bWO{D%OOBlQwv(G53#&R}3k&O8S0bvx%}hy4j^kuiuTl{ukymq$WQmI&aJlkoI{3PRal|UA#bzp;G(H>x^wLE+{pz!e#HbPP z*s>NnTv|BQ#-MBpshkN$Bp&c1B(`gpJ9d;mHs{yaah{$>8m>)-%iL7^0tu&@7^gO- zwlycUFZfc~0LH|6!JghA^=CLmYhaGkbqSF={V!wk!4xb`nc5NMx6P>uw%evQqtZ14 zCBn@0@KrZub$q>9zMO5rOKvT#33_6Oo`G}dJdCbWcntl-j>A{jlAXRwT;tUWh{dhT z*cY}z{SUCgbxvcF-ND?JyVEtceT@-L4>+J&QAk_ho*+rabZ_LjW1x&|^X-0fysmtL z>v(4x-U~`fm^pd4Lf2f(c4(L*QPXKlokFGs{<@=DHi+r1J0OkLc}(`X0H8sb3!?oJ zTJL^pCoox7!Y;y$IT>m_HqDoGGcDR&gp~?^dkr;qqPy3d~30kR~nFGf?)9 z%gz+F<|kES_zkAvVBVWsfe_m$P}i6#iE*M{t-z=C%`OE@H9f4h#+e#%i)5W^Tg>_w zD81;Xc0ObMb8umgKrYpgE@=@!lM0u+s2~<3HKn@7A|g4_11xSag?kZHR00AGBm;_c zg%GP?EgTI;mqF94GCbQ``u+eQ8(9yqGoXYL*`m&}PODF#UB5X1YNIuyO=I>=-C)!6KZAMWezEX$264 zc|G~IfJv`eoT*%a@cLbYH5}9vtYDcoJ=eU#J@Xz}&L)pc!J;+)mlOp#vIW{N1za{H z@>>7rdf8WS%NK5_W(S;Hr-?k3yUu|V2=8}$Y>z~&>=Rw_faXc{b=LT_Cf&dX{29nZ zuTe6Y9m_(Lu@6WqAhkTC+CR0!#i-b2PJDKt4r9J4VQ#KDUCIrpd7?yi_(t2z>Hu^} zkT?IlFsP^i@;F-0z$_jA2G@SMq3@wr4bjJ0IQXWC^H{g~5$ZB-mxR)Jw@_6J6 zY|)&7I`Q(8s?T9NXWD{nc0pMt)wb-P3pb$Wp~N7@%*GK3rj8?QSB*U2_~5qyNoT#R~taX~FH*AdFSu ze5-@&?{t3Rv z(?yjdxV@m_KC9#+4o(Vtv;sV?3!O<2!jW=&YIZxu>H!+_?#!r0+t0pP`qV}-++&e? z>$Bb~VY}}E4bLl`pcTc(=R|;-0ds87C5ab%JQDrmKZ{x*iKes&ZlIzLjznpMpF(yj z9~S<3G27rIwKGfU0}74JNH1!V)lC_>+sy!%mua2qq$T$Wn!l{>lHHD;ROzYr+9Kzq zU}m1xvuh)kX+clT=@oLo7{KX89Fx)y4UrROQv7<9xkm?rl0CKXO=81 zt;TeN9pRb}2y;(^~91zcML7$j{ z9t~Dg3T`i7Sgw=xqWOG#b!u;N&j;)A^V0XOXqs17y?nsU1UjVCJkc##^iCIyyNntC z%?FDdbuW|nRiNT`Qqt3Ph0f%b(`v4LjNjh=D0;YQo|}BAQ+Jex_u);60}>)CVs`LP z7%F51Svbe!R1|U|)dbG4#xzl@M3umK{s%UUnO1>5@)kbt4P{ZXgVfNUX`V7uXiJG& zNvk*AM0b3nXWNK10-vNL{Yr{GYd<`xp-OkMFLGlT$irO}p4dRK3G_+hNWMn>=KvZ? zRaZ8itbsPN&pa^oE=2>()8|P2mV#PpsDd8fo5xn9y*QxOmAG*inLk`Dx6MaHhcI!M ziZ=;?ANV3-#;Q|3N~@m%216s8gMkl9Jj5z+P+#f0h(GG$X}MijPc~sx)^^PVOaxsN z-yEIaGY}w)l7-CjN^yS^p(#L>#HA1J|M~t)-4PYLR@y5I`zN@wzoRIdu#NJ2cM7ZAGG1xRY!gM~G9E?p#ZQcbryfOpLFDk!o8;8D*yDKMYJsca z-5ns@imRiqfT zS<4$np0f{KBoPdru7X>cCl>V6qgnPF%8gAf5Z;^MuEO$oN9VjMIgbJpFSl0%tfF!n zvmT`ls^|<93K;3{-5YniDVu@TDt}l{e=i-ER_H63Q%Z5__Pvl%s@}R3+4tTh=^G2Hv6lhL)p5M7^P{j4eDp+$h8+;K*;_*&zy7!X_heDYJm+gc%Rt>t)4_ ze%5@DxJNWd-7E~?SXuIG;BR(<5e%hdEU+;$YD<7cdEV-|TYD*`+LGQF?{Z^4D$ZO9RGe0kcTPwtY zaee=8-+T?Dk4aA)p1?s)tXNASm^$5_6836-8D`pMH28<$&YPNi$pt-}>Pk?k8PoGK z$hBCLpcJ*OCU_wHZtWALVE?wx@q}+wlvGEMD*KK-D|Xnqb2>PVy>JO`DS-^ykr8M5 zeq|REe-k4NiJPue==q)G3~5}16yO9K7m#UvxN+&geBe@RG5%~j^|sTZsL(PPkng|x zZcb5DF-s$Y#$%McA#(RyII~Hf4>K%Rqt(-rnGI`Mufo+V3u|%OasyAyUEA=uU>*?=4Icrm%vW3mp(=vt5yz&h>j@O~{d*NBk zRF>NomnIUn1pxgJOum~VyHhVQ{TXKP zwiDxnM(_e9swL=I)DD_(UaB@p&ptWt3|RlYB;l#^e2v5m2S= z4-1})kQ~JiPbHM?gjmvQcxmHg^dC2lVA|5yZ}t$1-(4j>ZpG7eBm_+v`1bcCu#p%C z$>GFn(LOf=15uF}H<93vIPl}rpZN-NIv-+&B}BvRbc^rW3MO8@9p0?x#0==5Qo1bmO8nPrj zYq)T2@&(aA+)VRd1p8kS^j}md12Z%I--7)war?I*|0h-T{?faDU;lp+?2P~8(O<@Y zjQ%qIljtwwe~$kCHSwPW`+qa~s}Y0EV(-nMdK~1dp+zIh%|m+Gf>_$E!UG0WQ6qf18BWsr+p^;c(Hmok6GqmMNsBqBj&&!*^h zg(=qMiw)qH1a4Gul2Cihc@3}Z!rTz0JapUVGyBnbok6VKM~QkGsi74|9;16|Y?N>k zlv>-A!DqbtBY;mpTZPH5gpYv-4GyPi0DlOvcx6ars0$3~~v+#>s>zWZ|tTc>vkelIz7f&rUz;z^0|APW?5POfK96B~YlMH&{G zD9FH9!A&XW5l6^t8mCKr0bsEiy3Cmss8YjCr+azrtF^M_0?RIUcn~pkyTbupTNUoP ziKjfgWfnH>9x`5HtBk@bfh35Vy!AY=Ly0u)R-GgHF}|kI%R!Ff*YN>=HUwOSu2h@Q8&Mm}b(P^pVhDc; z2_t{bb%yIGnN8w=F2^Pj!AvX12Y0RwzY6r!==}Pum&o#ghO>|h^mPnlt$eFrzs*s< zd^N4bVvk|27Eio6_H0Fr6)3^E`av3%_y^enYeb%75YVerfg=qk-l@pAq;5RBs@U)b zt-Wn%74cqtt4Qjn{Q5PE`Z?pN51ZPDkr8j0I)eMitMu5)>($55*`ppcanL6bn;j_| zOSO5IaJnE6+?-&7Ny>DuQxi9a1xqq^%S6$SYWAUs(*BJ!NM8-ZlZ zE9i3HVb^v&EU=}vVn}dC=|HXVt-hPeEht0XZGd`d5l@?($<5T;f6n^DK`Wr8$)Y2$ z?6{@&N;y#WiFWbBY<78kL_5g6Rg?qsbLLQ!z*Gtq#5C$jB+t2oZ~=2Yf|lUf=+P{n znK(QLH6m+w;yZl-VyVrHxlH~tF2=OKFzj7I8ykaCYGu0M2zj}cVkav`6HgxMNeaM- zqLwGDy88fUw7KAuG-7xQhegrIM1;^RH~{juK^5^r5X0RSn@3FJHd4+K*j(hLk~fFG zV!$JfW8Y7=2uJi@#{i8Uy-*ZVq)odQct>tguhQeo0@j1IpxY&Ek-=ez1fb<-|7Wcm4h?Py!qo89$585_be(cNq9I0TmrSa zGSZNeRu`y1Q_U?D^<`xp(FW!HwPP^kUjxKuu|=-87eT~p!wRHW8Nb6*I=(9Ag za^+KeyfR>>vz~vJ&RW$#Wb!nn8B34o#EdLKmgx!mgK? zuPu+X0xt@xQN{KpBxDZ79(6;Zp?1PdoU?NF4feZ_;5ILzcIYNTmQ1B@O8GS#yC@ii z48da76i|0)JaH~-D{_9(Lje-8iM=WGmvj9DWhkg}l)YkER@}KTm8(s*kk+-MqcbPD zTyQNazlJmW#~$T{;Da-fNsx>DX1q(6iRIWZDhbI=ezZ(nUUW&4s}&7s(WtU-L%AH$ z?tqEoaCR)wLzvmQrXzIvJ00#BA;4ze2EGTDeE4;Xdt{QbSH{M@K@VcmEf1{LN({Fm z7`q3G6XO)q2w$C&V%xNV5nOH~N(JX@E6ZW1kT4Tq$}o--H;Agqk<&18O&UO;D*{77 zOHH*}wKTvv4va5Gmo&9iOx4u2@s+wbGcJeOW4PqJfycGa&|G3>iJVELzOIjV$-%!N zQs9nJ8anP_O$)H5QLX2k5QzI$e7+@dWd5eMowmg@7#-hHipdt5PZ;GqWc{r&cy5;p zyg7gEl5xtMHgt7|BvnKs*sqhUoNkYg(?nhyI>K2F*rOFP+I^2DdL zj<@$FMCApy*7r%xBdV7i@piwyY(lzufEp+SGk=g{LqqJnju~H1kqk<)ar4kCw~a> z&a_7B5W=JU4?@p7d2^G=Uv6YnvA&LXzgCAQCAQvnx#Al|Zj}Yi#ql-P21IEo$J$6s zru~M^`#5q62!{kwP~y^7*+-6p;>GD9%V?5kSNZID?~z&0!UbL6J2wqwD17_b~)e{xB^Jt0T-${p<7EbuM4+r)|?^KA+@e zt+PA;OyCsm3$u5dqG<1J;+BLLIzD)NSLn`}pAMt`W`xg|ZcG%FR<3vW0=lNDFqG)D zJWZ)^Z|r`|c7B(lva28F0@xxf(4o=`D&uVDv(a{?t!)dzbNa+EgN=;TB`;dJq(c_G zb-K_soVNj3nJQ*eEBj6Q+Z2uO!M7o>MR^SMGj@9DY%_RAKIlBMBErGIlAPs@QY}H- zjoJB6-^*NXFPnV0u{*<2Qshf42J6tv$N{-;!##iUu3?(9ilSSv{LYibjyk>k*XJMo za5&S&eJ1rky+4M;&Sed1dbY0tRdMv3UZ<+F{|Dt54%e>;Tzn;IaE0E z59#zHampQ%Kk-clJOWr-Z^~bJ_71b}dF@N4GnX^b-!1uBG;~nwIk2YOXINv#W~xv^ zk7-&ovoZEhQXQftiG^eGA(j-W>H$|_SX~BQOFGI+Potst7XgJL?(w28%zunwyqe8m zeMAvzpgcjGm_gXqqo=y^w5^e?4Q(0bXaq(Bt(Iuu?p8KzFJDo%!(CW+O`kC8s#aY&6RMp3*PVXH$L(Nrw7Hrt(a7(P=P_IsToD9a4ZM8 zN$0^_Cg>YsoRksz#v$8wNT62_nG7#PH{3O{Mm*F9@DS|Is||EeMMuctd^96q=nPr> z?k#sUT*MF+UaNcwFK5<@<7bW8D{Tgu3#7TZIk`!J+pXV7(q_Qz@);B7)*fe)WiOOy z`a|^%qJ!ybR?8GB4uvQqM{X4BO`T^e)LfxUo0F$kMTSa}>)3)+0;WJWM+_W(@8<&?W zP4LY6cK)+mt|h8h55*@J-8@3lO3yUjAi;NOU;}ci;Wif~HBlvg_adzAGKq z_6xXi`@|7w2kXi)SB2`_NtWOV#5ua%X^zishP+3_4JW;I(ZCC05YOO#b!EGH<#`Ax z0Wm_L-d?O#pT@d;#u`H^GI%$pshZWZlCYWKjA22vl*8MwX>!4(A@iNzALL>9z)a^T z2CY{9*Ts(nOK~<_h7wYV(KIdFDA7B|=>ocY;Kp+AYcRVJ@tp*3MEIY{TG`mzxu`Au z7poSOIjXqi}2PzXfxSoYz!q`t;{n*sVGKVvcbhY50i zj;osurrI+alU~_1bd@JjDrBw5r-7qDFje1yG~_2_@(EXI(?>XqxTLxwq4mhIZwN^7 zeyO4}@o+H1-1Xv?_gbe+0#U2!_8E1koJOyPLBFkp;pE)zBPjN4Y#e{~2*kf}zjzcN z{FV)W1vk5zwk3E41$PPN-lAwpWQUM@pg>uK$&yaQ8>anjC;f~r+;94 zD%rx-Q;PO;YRD2>VRXuI%&|7x-DtD5yg(&6j~~K&skdFdxKg)J5iOE>dVkXx89#6) zZT{h%Vc&2WDTnwMt$5jF2t-o$JA*KN8}YZ&vl$UsmE`buXe4zXb<9gHd9p(B z7byXhYOJf2Y!cvW=HsHeuC=tYV&ala%QJOP z(G$a_4p^aW(QK5S()35)HLVimc>_z_DCLWFq$J%awK?;IsFg7M^F-1QT%q=y=49&? z!=x`O(r?BzZX0ls$H<*0b7SV~+?re0%9FtE1!{bLqkuApLJ6e}Z##~4d%cXV zd!&%#keqJEBX{BorRevhAWqWNuU|C~IRtzKw%t9zgG4{oNYVRR1AZa_aU+%@kgurY zgo~6Y^&WXOlkqT8I)6dp2VJbQBk+5!u8bRaD#*m5G?nt=BtSmiCT5^NNT^WaQ`V51 z6ovqJoU+mgtDI|pHx2O089p0-*S|=NuzPtB427S5?p64#weOlQ{{36rX}ePJxkiFZ z2j^vAuMR}f_wFWeaZS>qFrKWwrU_f|`^}Fy*D2bu9A)`4AeklScg|rKVKrC#XWY%H z8|dEpukYwv#2#t?2QqAUBo!ECS^~JRua< zU1>XM=|UD{%B$m2OGH9KMwaj_d{pV; zrma@>%{j>f}=l26U)_;X>f4gP=WMTY!58J;$)!%UKpTW2P z?;0qK|Kn~MrhjzHF#Qv^4C8-x%ltL*AK@F*f6pz$xf+c%5_kRa9jv=`T{q>98}g+u zUIhkoJ{-*i7cCh#*kSzub_&1fFy->v%1~KZ*icCkcOcjj1`QvNbY^t4m9YG4D@?mw zy3og9fA8`v0@u^!z7=_M>g>8=_WVHyr{FT8Wi!QumdR26so&9LnPP4PBMRdwF3YKl zK`XYprJBQ|91^S^Mn^}o1V z){D-+a<{DgEAHx^;Hdm)>SU^-*z>Pyi*8I3aLi?&(kw7Mi({aoMNB+E)Sp%y<}|$P zj?s;L!A+$ADfPd&n^NqdKWVO4PhD4x+}fiGpk_g5`m*s1`!ZWxp=9XAdK@-{%@lQpXtR}sx<1Y72t7dt?q6ox_Y*!M$ zVP$m0Cy=kI&H8k-)!ti36i{rSthHCzRK>0**~c4FhG9Spkc7?m4fLb6Ew_EREb;W? zmCiiULM61Td90n^x`E2)9Gqu3=tw}_(p_uoxyv@e+IZ)WBAFf`T?hI#%o>~LsXL#6 z$MJ;RK*s*G?A11ZTJAoif7vy^{Tg>&>@!C8y-I|vPRRH0X=>+=v`P`{Q{~-{D+KDY z_v7{DbKl;&$Cf6Zaj#nqACzHM<^4XI1Qb)(9c&Ub)j63pyF5uq^9sz@w}^mRfVFsm zrvbt#&90*;jyKUGBV}OF820J;=Xf5*Ei6usH>1o7Hshu<_5f2DdA*BaF~4SUffa}6 zh%age*Tr$=$kt(`{)KEOLwiR8LYnlgKcXw;)EYEC@(1v;S!`;MbRg*zQPR+q09O{4 zn+i(~A$OEd6lk=q=U`cDwr1IG64JqMOt``i8Gaoj_7p`VBx@0O2}OJ8^_n1*>ggik~$R?pYa z0%_BC9eh()c2Hjyh$2W^jKWi&dl^9HM+<}oEG66FA_gKtCPX0FOFt?qgsvq}64yUI zsDt+)IA4j)-|+q*-^$Hc``#+tIQHgU-YnVm8j}GxyYl&49L_$U>)_QEKhLX8R#7Y$ zhygklg9@2fSRLO;GLT97(w2))fKQe6;R+up; zMeGQ?%nuZIH0t!yj7LblyPIKZBjBp06Ud9Xs|Q36bW%c+MoFW(8NS;)FhsM{%WE(N zPoN-{z|c4MK-;5i$=31`9(%qR zi?4;+^-~qzfDSxP084=6#6Zkxu{xw4e_VsvJGR7B%;Ls}pRBd`x< zE8||o=fgmvxN$T(Is_~9va27L0x57bdu77xH3MMQ2+Qr|GBSZ5T8q&8SN%wjx3a`w z6~VGT4}@L*#ED!fHYz4PML#eW$=ZZt@G9J=&{xE&AL`k&GpWA@^?Yf8nl;g8&%f5+ zi;IYTw@xI*)B1Nufs$h;fUAX8G|*4{Lv}TS@l?&)C@JQn4=yCb z#ZyI}`1Hh6BI~*>I1khPClI$h7->#HEJZkNcF81}p?9dJKi%_3Os!R@;5?%{1dH*U z^nYV@Tt9c}@1_1pfBJ>f8DIRuYmS2J5=cL9UoDQ)67xb=!S`}J;F$J&qicf2!3ac zy!mciz?c8#Nkyl&iFv6BbDBRFZ`{ISL)2~YEp{Um!7DuS!MojQan@D=f}E^| zIUp^>2%Ru=v*N(LCHTWve6V3hoRVxLPTKPKrLZwrc?5t7y{XE6nI(CTO6e4>d>w;kNIC(GK65@y@JW*8U}X+NqFDn89*!Q3E5a4om3^`d z>g_cZu)V%J0DV!Vyl*o;s>tInia!bqE(;;Mhp2vQ|2v4!2!pb?mxeqQYete=lO?$ zt`i=MY~B#D;R=vTq{#|)aldVkk)ZZ5L&efmSOkja*wfHsKlVVW@7f8pa|`|*Pmtw_7XN(rN@37e;<}11nlg1 zh=U7CpjH=>jV}w>_xd`@F|o;VrYkqddXyG3;Hd3aag`7*=F zE!Ar5g_UOFw!!`!Lif>APJQn-_R(HiVwrTXEO~+K$Iy3AB$dQxQ(~Mc?pW?Dg(3@>p^zFx4?sDAB&og%f1VgpP`!9*s&)KkV_zi;OK== z-0+InFM_YKZ3x?%b!XfAjMFc#4}RE?gz1{sSC0-!(!I~GWPJ8=N;5GsC4-AF%3G*a z7U-q)N74eJv)bHLeRh+vpmI?;*HRI!ws0dZlO~)xwxmfuO}dv#{?x28X(<0@%TsX} zcn;T;DenjHDz)kAF%$>EjA`mBp-d|;9;jv~+YABsMPE!bYN7zd?1_iNmcY24B_PPi-a1op0Tm#)ZXKX1UcnmlQ)SC1 zKo%wqd0P%wY{g5bhHNC)tEoH|=y_C5)0hmV`R|JR6qRZZejTf&YK7`N>sW}Nw7oD> z9mP9YXlsXS8|vvvi_Nipat$y3l6cnhDo!C9$nd!xbF{!#I*SThSc>Cd_25JlL7!^+ z&^um6OQbcy`9W~Kn(T~6uZcFKNpLHxg)R29_$ohQ5@U#>J?+HMuI=j2X^&oi8<;C3 zC5)MIh2?ThN=Vn$9H|P^;Ui$Hj;~W<2q<*7CB$ zg;x59hMpg(O9{HPDUfoPQ=0ROJWGbhP1xGLijZh7A|Ylj*U-a;GL(O7@h^fqeSr=B zEekCRqjC)U`k^PtPr9y$`x6R9pCj=nj^&Awi_X`wVniavxNA-$K~8Z~3ddS6(fhHq znA`;v7GiZO04TbJ+!NXFdC7%^Fb)WQ{HX4@d4 z($6$fICZ=&_QCRZ6p@A8ueb2T4>77~Q1RwgNxaLmT_A-)`75jXfun~D=|*2j)%!*y zFEDELS2l!HnrCy43asPbhcJEHC6i%%S`ASXr+SW~js_an?^{(5xUouBa_;7e{OjGYL3GQu$!IUoXwtdq*A;~)Do^gq z!yC%cj~&Mgc}GW^EHu|S;DU{(M4g$aZDs|H6Jh)8i6YrRCejb`Q!Ty02WBX;dw&MA zZv+{o?R*zTwT8EMHb{~|AW)B)2?7=7+mqNa$S36olW=cJOhvOz)n0gIS%rWkt(qtD z`%A6UA|h!3O6uCmip7xEO?5E1NS`2?`5H5R?E$(q|JJF24KIw zpJH?1j>ww&(@wNW%P5V8DjE*nnRbXTu|2)&_aI$=@>nE~yq$X?XFiPb-+Rqx#U8q| z7$a>w(E6uZsAw*lXc`pR@L^W6cUdn$ zuvO-o#^|S^yx}PURf!OdtVfC#dVB(NV3owdB*ywwk90a|aTSZvzSE>Bw)HRAX@Y2@ ztHP9UNe7K7W9UOsxhje3fqa30`r?L?pBkZ8fszGdVg9zj(7;T0)$$=_xZSG#dbML& z(T|b?C)*6)asZ5Y_r=XB{;axEUu8q#K+^On7xD443@h$ojls6QRLQOk1o>d(x=vEN z0!I}UR^DSo8uu5`ko;d~)PftC^DMkZtZ!q_9l+i{pC z4X`c=#D`nDKw1lN;j*niun7~T{*I{4f$BvB;R@SeBnA+v*#Lw#;X+#PG^Y$fGO(Sx z0E8m!*g_0}Hdaod$}P#lxuFb~g0`>aQS)yP53|L1C~V^ZC5yLt=yCbigz7+vlhh35 zEr;~2B(ZiNrzN0{UKU$caJkd?yj*Bqpc)d@`}&!d4TRp){MFz1jWhV({|&4C1B(8| zj{de5{=bXR{y|XxA6V@lD)sOE|1GDD_5Zlj#`>R~HrD^aX=DAbPTM~V{{^f4FF9?; z+Ltj~EvVnU@`OjLGM9=&+~#?p2PCvuB(fP+zfmN8tGW+4&~H0h7Kyy;d*twoLz5)% zq(YgoqFuVJtufFtE1PjcgyBY1;1dW>pqA2v6UldR$m10PKDx`^rGka>9h54!j&4PDb+keRj<&HK=BW zF!p0_cJ1i$=z`LVJ)qfuc6lYUSjFz$vAvv3yNn9joseVzD1QU-hGYr=CpfzX09{68 z9toeFG}25#vqZRw*ix-}I(;3gzDlEWubPMK{?VoxnLVB9rD;xaL+|;4((Wx=>Ypu1 zh4)6?0-|fLw_b&fn9-xs*c|8S$E7DtHuIId5#)Da{*t49ikCGnx&j@ zDFiL1y`N8wb^|H;v}D~A{L%;E8o$2qDITw5IxuoOTI-EcWD;)ABdn{~Py(%tNV(vi zca9Fw)^BvQ%-(+OV-(DsY0Gv_=~U`X>xPU%n{*b&T5> z(SJ9P4j=b4mZKijQ(>~t5$Lk?ZnUnCQ@lZJzFwI}!?l!wx9LX8B;NTCH{${w8 z+kxk-+8(aVd7_@Gc^p zjZSH~RRfZVOUL~;P#Rl*=bAmCrbz~Fo2cqD7E?=$hh`s&|81|(Dm|4moW^fFTUt@1 zoL?q+@aF{Ps8eiuw4dp~^ar?VO+3i2QSG)Cnm-=Q_6F#46O1J zTeQ9r&MqC|oTQ&G4s@F3vdOCtca)_^qe{q6&BYIXS1@%+!1q3U&1s**f5}j+=zG1D zi__O&uCEgFPstNn>_--PwkGxb$WUtSGa1B&Yc4cQt87enw}iJ$A{I)KOTstbMy-s8 z8D~?D0Q8p@AMcHZ!L8DK4-P7OR@jx@tj9pE3?;$6TUrSlO%|j^*&Ek!`saR7 zu44~sK@dYR8|PAW?LgJBgKZLvZUcN1UXT^pOnC^hTf6O%++rI4fNnBQW9Y{x@$5u1 z&nKqUM5nh}KpWJWiM}nG8~U(Ptu&YAhB4&KQ;aGH?#OUY`!@rh=rI-Q*TpF&vLH)k zYeu+z)`St71e7cdLi2>I@Oz2iJH=!l_K9K;iIKhCg@G7hATz>RrzMpPKnM~G*GJ_> z@th8{R?Nd2B#gRI&YGc!1!J@|dhy}Jim|bnUWWrNJW3$0k7;`TR&0tuoSeHJEwVvR z(GQ&=dHcE(SH{suSy z>exgu*D5Vozd(nl5_#_vN=cRxry8yD86xpScZb_fUpYh8&&tsNAN2x@f#jhZWPr?9 ze^Adc+8UN@%RgXO(+C=(GY2!WrmxGqQKsm%S?Oajba`J{E z5d4ml=;IKWAEy*EUJr+vwi39dQ`#iv(Tf)IW-M6xpy)NA+Os{+Zg3!Q;8P9oDON6AyEJCysCbf$82Va= zq=>ZX^m7?$=mUJj6xQ+ILi+E&AD37=c78$Isc!P_1ShV`YhoMR-r0&i14rgN3xmW= z3}C1+uOh1bBq?bwkH%Q3j@I6=pGF4ARg`B8a=u*o7+R;tzX)0%RS*BjRO5=BMRfMWUh1M-Ovt6f${DXt+zF4%B_ug7U(*}b~##{MPr}; zaF5^5%%b$XFm^_(H*0ehvra58gjHx{jXG36wrq3zQ_1x6BQxWRUw~5viF5BgM7>PC zByBqx`F>Hljrp7}Ml>wsnBT?+V%d4a({Y{`9nd9G|Cz!W3>4s$HGORw9-_Oco?W~B zebhkA?MEmqNG_~j7ZFVv9YWpYO_qE+sojM%1!fHWgF`>P_Azod%B|Eu9+NxNGlN23 zsE$*?z#MyCzNe+zx+|EaRePv>1%4?)786?~H6(vt&l?_AdF9!fqExVJMG@=H7_L8?Kl@}*JdMG#Unl-4rr*`jTV`*(sp{HXAThW?AMLT`w@4ercoqy zOM}>+Y!!lA;tRT)uxq$?G`Nd-?f@T1{kC++xtsF6`TPw9&=GagsP6$6A&GO3YqS?@ zJCv8`&AT|NT~_~ip5kWkm=NWSg?5-kN)gM zR3D~t*e++dNrFKmk`9D+v!T<65lX>C@}8zK@+(E17lT!>I_37XxpY(NHSxvC=y)I= zniTDsy1KDw0UrbdA2JJmDd#(1H2#UY&5}TYm)n&BROsy=ZCs%^+b-#2(&}E$Ws(qMHc$&2gN%JVMPSu8(XEcVqqP* zC+xrpi9xQ_=YC*PI+fiHn~pYEX_AJH&DuC>!)k`c`E`l}fViL-L80*e{hO3Q&lnv7 zlmHZiPUeXFD^^-enbk3XJngyPZ{p!CbiIky;C!JI)bV_-9QCWi=dNQq>(O+J6OK{0 zB}_gSt#mFA2jR)D6edwQ$9hOUw^e^{6ZKb`Rm%OM+0B2$#d8>Ru@R>qR_^|nD1 z+4DPuKnH?n7OBMSy*BsZEDoDcwc2yZIV7xZWBuRLqcuVsZ7RFjDe;X;d%meN!G#pc zd{z?a8m{QS3VIS9q^7C}Iih$VqQ-7O8%W2L`CWADekaMVe9K(Zl<~5`ag?j_m-#IM z$Gk9O(0Cq*C;wrtWOCx{_+dxM#u~x-QoI=b!4OB+|9RsMQ)4zdV1!O6J_byA{(P_@ z{uzQql$^!!1_^>#+Sk^;4JgCcG}+Tebl&j8;S*3e0WD+JC#3JwBWX9zic5X->>TC-0-{RqU6hzdFHo;Y z`)pC3jwN~f1kr8zqg}N*;k^HjH!fj>bFf{K980V}`m^jbG|95_VE8Ef(P(RSipd*V zto~Ts5!|)TeO-|}l>|7LBeNo_ySME6hgO7sw)V@|x24R0s!)Cn^-|nRG=h1)Kc9z` z{|H1#up&M$_cXKY?_ZRmisPD1ujrh_Ve5+~m}F&uKeo;cU}~~d9C}2ygmjP%BaPJC6@zBuQV!P zy~X_J`kJP0F0yxs{AXkezV1V|0e6Asa#I@XCd2RvArncO5mREMi~?AAtko6k*k{W@ zZ@=HG8IQwTx(`bz0w`n2V)(u?*a7fN{68rpZoks}EX$>sL*JF$XWk1{-lv)~qr$pn z%)!T%HuOD6$mGWJYb|iL<(1%Ei+Uym%PtKGYVn{WyG5MmGn`&!z&OQ1{~CBuq?V93 zR>lug(`hDRhs$z&x692FT+k

{^t+jgddHIVuJDhC0e*r({|`lQz1_WhaipTyI) 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�I2z)4p<+o$-G)dtw(`hUuoJu+~tL+K;R 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 z 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

cRxM>B*CF{=p%|kVd_O_ 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 (60->62, vel=127), during 1000 ms" +echo "noteoff C" +noteon 0 62 127 +noteoff 0 60 +sleep 1000 +echo "noteon E,legato up D->E (62->64, vel=127), during 1000 ms" +echo "noteoff D" +noteon 0 64 127 +noteoff 0 62 +sleep 1000 +echo "noteon F,legato up E->F (64->65, vel=127), during 1000 ms" +echo "noteoff E" +noteon 0 65 127 +noteoff 0 64 +sleep 1000 +echo "noteon G,legato up F->G (65->67, vel=127), during 1000 ms" +echo "noteoff F" +noteon 0 67 127 +noteoff 0 65 +sleep 1000 +echo "noteon A,legato up G->A (67->69, vel=127), during 1000 ms" +echo "noteoff G" +noteon 0 69 127 +noteoff 0 67 +sleep 1000 +echo "noteon B,legato up A->B (69->71, vel=127), during 1000 ms" +echo "noteoff A" +noteon 0 71 127 +noteoff 0 69 +sleep 1000 +echo "noteon C,legato up B->C (71->72, vel=127), during 1000 ms" +echo "noteoff B" +noteon 0 72 127 +noteoff 0 71 +sleep 1000 +echo "noteon B,legato down C->B (72->71, vel=127), during 1000 ms" +echo "noteoff C" +noteon 0 71 127 +noteoff 0 72 +sleep 1000 +echo "noteon A,legato down B->A (71->69, vel=127), during 1000 ms" +echo "noteoff B" +noteon 0 69 127 +noteoff 0 71 +sleep 1000 +echo "noteon G,legato down A->G (69->67, vel=127), during 1000 ms" +echo "noteoff A" +noteon 0 67 127 +noteoff 0 69 +sleep 1000 +echo "noteon F,legato down G->F (67->65, vel=127), during 1000 ms" +echo "noteoff G" +noteon 0 65 127 +noteoff 0 67 +sleep 1000 +echo "noteon E,legato down F->E (65->64, vel=127), during 1000 ms" +echo "noteoff F" +noteon 0 64 127 +noteoff 0 65 +sleep 1000 +echo "noteon D,legato down E->D (64->62, vel=127), during 1000 ms" +echo "noteoff E" +noteon 0 62 127 +noteoff 0 64 +sleep 1000 +echo "noteon C,legato down D->C (62->60, vel=127), during 1000 ms" +echo "noteoff D" +noteon 0 60 127 +noteoff 0 62 +sleep 1000 +echo "noteoff C" +noteoff 0 60 +sleep 1000 +echo "legato Off" +cc 0 68 0 +echo "End legato mode 1 (retrigger_1)" diff --git a/doc/polymono/leg_01.txt b/doc/polymono/leg_01.txt new file mode 100644 index 00000000..fa8e19f0 --- /dev/null +++ b/doc/polymono/leg_01.txt @@ -0,0 +1,103 @@ +echo "legato mode 1 (multi-retrigger), egal velocity on n1,n2,n3..." +echo "-------------------------------------------------------------" +# Sounfont: GeneralUser GS 1.471 S. Christian Collins +# Some presets +# 0: Piano 0 ; short release +# 16:organ 24:guitar +# 52: Choir Aahs 53: Voice Oohs +# +# 56:trumpet 57:trombone 58:tuba 59:muted trumpet +# 60:French horn 61:Brass section +# 62:Synth brass 1 63:Synth Brass 2 +# 64:Soprano sax 65:Alto sax 66:Tenor sax 67:Baritone sax. +# 68:Oboe 69:English horn 70:Bassoon 71:Clarinet +# 72:piccolo 73:Flute 74:Recorder 75:Pan flute +# +# 99:atmosphere; attack longer than organ, release longer than piano +# 100:brillance , 101:gobelin + +echo "preset 73:flute" +prog 0 73 +echo "legato mode:1" +setlegatomode 0 1 +echo "legato On" +cc 0 68 127 +echo "noteon C 60 vel=127, during 1000 ms" +noteon 0 60 127 +sleep 1000 +echo "noteon D,legato up C->D (60->62, vel=127), during 1000 ms" +echo "noteoff C" +noteon 0 62 127 +noteoff 0 60 +sleep 1000 +echo "noteon E,legato up D->E (62->64, vel=127), during 1000 ms" +echo "noteoff D" +noteon 0 64 127 +noteoff 0 62 +sleep 1000 +echo "noteon F,legato up E->F (64->65, vel=127), during 1000 ms" +echo "noteoff E" +noteon 0 65 127 +noteoff 0 64 +sleep 1000 +echo "noteon G,legato up F->G (65->67, vel=127), during 1000 ms" +echo "noteoff F" +noteon 0 67 127 +noteoff 0 65 +sleep 1000 +echo "noteon A,legato up G->A (67->69, vel=127), during 1000 ms" +echo "noteoff G" +noteon 0 69 127 +noteoff 0 67 +sleep 1000 +echo "noteon B,legato up A->B (69->71, vel=127), during 1000 ms" +echo "noteoff A" +noteon 0 71 127 +noteoff 0 69 +sleep 1000 +echo "noteon C,legato up B->C (71->72, vel=127), during 1000 ms" +echo "noteoff B" +noteon 0 72 127 +noteoff 0 71 +sleep 1000 +echo "noteon B,legato down C->B (72->71, vel=127), during 1000 ms" +echo "noteoff C" +noteon 0 71 127 +noteoff 0 72 +sleep 1000 +echo "noteon A,legato down B->A (71->69, vel=127), during 1000 ms" +echo "noteoff B" +noteon 0 69 127 +noteoff 0 71 +sleep 1000 +echo "noteon G,legato down A->G (69->67, vel=127), during 1000 ms" +echo "noteoff A" +noteon 0 67 127 +noteoff 0 69 +sleep 1000 +echo "noteon F,legato down G->F (67->65, vel=127), during 1000 ms" +echo "noteoff G" +noteon 0 65 127 +noteoff 0 67 +sleep 1000 +echo "noteon E,legato down F->E (65->64, vel=127), during 1000 ms" +echo "noteoff F" +noteon 0 64 127 +noteoff 0 65 +sleep 1000 +echo "noteon D,legato down E->D (64->62, vel=127), during 1000 ms" +echo "noteoff E" +noteon 0 62 127 +noteoff 0 64 +sleep 1000 +echo "noteon C,legato down D->C (62->60, vel=127), during 1000 ms" +echo "noteoff D" +noteon 0 60 127 +noteoff 0 62 +sleep 1000 +echo "noteoff C" +noteoff 0 60 +sleep 1000 +echo "legato Off" +cc 0 68 0 +echo "End legato mode 2 (multi-retrigger)" diff --git a/doc/polymono/leg_por_00.txt b/doc/polymono/leg_por_00.txt new file mode 100644 index 00000000..b73cf46d --- /dev/null +++ b/doc/polymono/leg_por_00.txt @@ -0,0 +1,107 @@ +echo "legato mode 0 (retrigger), egal velocity on n1,n2,n3..." +echo "with portamento." +echo "---------------------------------------------------------" +# Sounfont: GeneralUser GS 1.471 S. Christian Collins +# Some presets +# 0: Piano 0 ; short release +# 16:organ 24:guitar +# 52: Choir Aahs 53: Voice Oohs +# +# 56:trumpet 57:trombone 58:tuba 59:muted trumpet +# 60:French horn 61:Brass section +# 62:Synth brass 1 63:Synth Brass 2 +# 64:Soprano sax 65:Alto sax 66:Tenor sax 67:Baritone sax. +# 68:Oboe 69:English horn 70:Bassoon 71:Clarinet +# 72:piccolo 73:Flute 74:Recorder 75:Pan flute +# +# 99:atmosphere; attack longer than organ, release longer than piano +# 100:brillance , 101:gobelin + +echo "preset 73:flute" +prog 0 73 +echo "legato mode:0" +setlegatomode 0 0 +echo "legato On, portamento On" +cc 0 68 127 +cc 0 5 2 +cc 0 65 127 +echo "noteon C 60 vel=127, during 1000 ms" +noteon 0 60 127 +sleep 1000 +echo "noteon D,legato up C->D (60->62, vel=127), during 1000 ms" +echo "noteoff C" +noteon 0 62 127 +noteoff 0 60 +sleep 1000 +echo "noteon E,legato up D->E (62->64, vel=127), during 1000 ms" +echo "noteoff D" +noteon 0 64 127 +noteoff 0 62 +sleep 1000 +echo "noteon F,legato up E->F (64->65, vel=127), during 1000 ms" +echo "noteoff E" +noteon 0 65 127 +noteoff 0 64 +sleep 1000 +echo "noteon G,legato up F->G (65->67, vel=127), during 1000 ms" +echo "noteoff F" +noteon 0 67 127 +noteoff 0 65 +sleep 1000 +echo "noteon A,legato up G->A (67->69, vel=127), during 1000 ms" +echo "noteoff G" +noteon 0 69 127 +noteoff 0 67 +sleep 1000 +echo "noteon B,legato up A->B (69->71, vel=127), during 1000 ms" +echo "noteoff A" +noteon 0 71 127 +noteoff 0 69 +sleep 1000 +echo "noteon C,legato up B->C (71->72, vel=127), during 1000 ms" +echo "noteoff B" +noteon 0 72 127 +noteoff 0 71 +sleep 1000 +echo "noteon B,legato down C->B (72->71, vel=127), during 1000 ms" +echo "noteoff C" +noteon 0 71 127 +noteoff 0 72 +sleep 1000 +echo "noteon A,legato down B->A (71->69, vel=127), during 1000 ms" +echo "noteoff B" +noteon 0 69 127 +noteoff 0 71 +sleep 1000 +echo "noteon G,legato down A->G (69->67, vel=127), during 1000 ms" +echo "noteoff A" +noteon 0 67 127 +noteoff 0 69 +sleep 1000 +echo "noteon F,legato down G->F (67->65, vel=127), during 1000 ms" +echo "noteoff G" +noteon 0 65 127 +noteoff 0 67 +sleep 1000 +echo "noteon E,legato down F->E (65->64, vel=127), during 1000 ms" +echo "noteoff F" +noteon 0 64 127 +noteoff 0 65 +sleep 1000 +echo "noteon D,legato down E->D (64->62, vel=127), during 1000 ms" +echo "noteoff E" +noteon 0 62 127 +noteoff 0 64 +sleep 1000 +echo "noteon C,legato down D->C (62->60, vel=127), during 1000 ms" +echo "noteoff D" +noteon 0 60 127 +noteoff 0 62 +sleep 1000 +echo "noteoff C" +noteoff 0 60 +sleep 1000 +echo "legato Off, portamento off" +cc 0 68 0 +cc 0 65 0 +echo "End legato mode 1 (retrigger_1) with portamento" diff --git a/doc/polymono/leg_por_01.txt b/doc/polymono/leg_por_01.txt new file mode 100644 index 00000000..b5cd3948 --- /dev/null +++ b/doc/polymono/leg_por_01.txt @@ -0,0 +1,107 @@ +echo "legato mode 1 (multi-retrigger), egal velocity on n1,n2,n3..." +echo "with portamento." +echo "-------------------------------------------------------------" +# Sounfont: GeneralUser GS 1.471 S. Christian Collins +# Some presets +# 0: Piano 0 ; short release +# 16:organ 24:guitar +# 52: Choir Aahs 53: Voice Oohs +# +# 56:trumpet 57:trombone 58:tuba 59:muted trumpet +# 60:French horn 61:Brass section +# 62:Synth brass 1 63:Synth Brass 2 +# 64:Soprano sax 65:Alto sax 66:Tenor sax 67:Baritone sax. +# 68:Oboe 69:English horn 70:Bassoon 71:Clarinet +# 72:piccolo 73:Flute 74:Recorder 75:Pan flute +# +# 99:atmosphere; attack longer than organ, release longer than piano +# 100:brillance , 101:gobelin + +echo "preset 73:flute" +prog 0 73 +echo "legato mode:1" +setlegatomode 0 1 +echo "legato On, portamento On" +cc 0 68 127 +cc 0 5 2 +cc 0 65 127 +echo "noteon C 60 vel=127, during 1000 ms" +noteon 0 60 127 +sleep 1000 +echo "noteon D,legato up C->D (60->62, vel=127), during 1000 ms" +echo "noteoff C" +noteon 0 62 127 +noteoff 0 60 +sleep 1000 +echo "noteon E,legato up D->E (62->64, vel=127), during 1000 ms" +echo "noteoff D" +noteon 0 64 127 +noteoff 0 62 +sleep 1000 +echo "noteon F,legato up E->F (64->65, vel=127), during 1000 ms" +echo "noteoff E" +noteon 0 65 127 +noteoff 0 64 +sleep 1000 +echo "noteon G,legato up F->G (65->67, vel=127), during 1000 ms" +echo "noteoff F" +noteon 0 67 127 +noteoff 0 65 +sleep 1000 +echo "noteon A,legato up G->A (67->69, vel=127), during 1000 ms" +echo "noteoff G" +noteon 0 69 127 +noteoff 0 67 +sleep 1000 +echo "noteon B,legato up A->B (69->71, vel=127), during 1000 ms" +echo "noteoff A" +noteon 0 71 127 +noteoff 0 69 +sleep 1000 +echo "noteon C,legato up B->C (71->72, vel=127), during 1000 ms" +echo "noteoff B" +noteon 0 72 127 +noteoff 0 71 +sleep 1000 +echo "noteon B,legato down C->B (72->71, vel=127), during 1000 ms" +echo "noteoff C" +noteon 0 71 127 +noteoff 0 72 +sleep 1000 +echo "noteon A,legato down B->A (71->69, vel=127), during 1000 ms" +echo "noteoff B" +noteon 0 69 127 +noteoff 0 71 +sleep 1000 +echo "noteon G,legato down A->G (69->67, vel=127), during 1000 ms" +echo "noteoff A" +noteon 0 67 127 +noteoff 0 69 +sleep 1000 +echo "noteon F,legato down G->F (67->65, vel=127), during 1000 ms" +echo "noteoff G" +noteon 0 65 127 +noteoff 0 67 +sleep 1000 +echo "noteon E,legato down F->E (65->64, vel=127), during 1000 ms" +echo "noteoff F" +noteon 0 64 127 +noteoff 0 65 +sleep 1000 +echo "noteon D,legato down E->D (64->62, vel=127), during 1000 ms" +echo "noteoff E" +noteon 0 62 127 +noteoff 0 64 +sleep 1000 +echo "noteon C,legato down D->C (62->60, vel=127), during 1000 ms" +echo "noteoff D" +noteon 0 60 127 +noteoff 0 62 +sleep 1000 +echo "noteoff C" +noteoff 0 60 +sleep 1000 +echo "legato Off, portamento off" +cc 0 68 0 +cc 0 65 0 +echo "End legato mode 2 (multi-retrigger) with portamento" diff --git a/doc/polymono/poly_mono_0.txt b/doc/polymono/poly_mono_0.txt new file mode 100644 index 00000000..4fa1ed2f --- /dev/null +++ b/doc/polymono/poly_mono_0.txt @@ -0,0 +1,8 @@ +# What are default 'basic channels' in FluidSynth ? +#-------------------------------------------------- +# At initialization the default basic channel is defined by settings (see 2.2). + +# type the command basicchannels to display actual basic channels. +basicchannels + +# end diff --git a/doc/polymono/poly_mono_1.txt b/doc/polymono/poly_mono_1.txt new file mode 100644 index 00000000..2df59088 --- /dev/null +++ b/doc/polymono/poly_mono_1.txt @@ -0,0 +1,25 @@ +# How to change the whole set of actual basic channels ? +# ------------------------------------------------------------- +# Assuming you want to set 2 groups of channels +# Group 1: first channel 5 in poly mode, only one channel +# Group 2: first channel 10 in mono mode , only one channel + +# Group 1 should have following settings: +# basic channel 5, mode poly, omni off, (mode 2). + +# Group 2 should have the following settings: +# basic channel 10, mode mono, omni off, (mode 3), composed of one channel. + +# First use the command resetbasicchannels to reset all basic channels +resetbasicchannels + +# Then use command setbasicchannels using numbered mode: +setbasicchannels 5 2 0 10 3 1 +# or using named mode: +# setbasicchannels 5 poly_omnioff 0 10 mono_omnioff 1 + +# Use basicchannels command to verify your settings +basicchannels + + +# end diff --git a/doc/polymono/poly_mono_2.txt b/doc/polymono/poly_mono_2.txt new file mode 100644 index 00000000..60bc6201 --- /dev/null +++ b/doc/polymono/poly_mono_2.txt @@ -0,0 +1,22 @@ +# How to add a new basic channel among others actual basic channels ? +# ------------------------------------------------------------------- +# Perhaps you have already set several groups of basics channels and +# you want add a new one without modifying actual groups. +# Assuming following actual groups: + +# Basic channel: 5, poly omni off(mode 2), nbr: 1 +# Basic channel: 10, mono omni off(mode 3), nbr: 1 + +# Now we want to add a new group 3: +# Group 3 should have the following settings: +# basic channel 13, mode mono, omni off, (mode 3) composed of 2 channels. + +# Use command setbasicchannels using numbered mode: +setbasicchannels 13 3 2 +# or using named mode: +# setbasicchannels 13 mono_omnioff 2 + +Use basicchannels command to verify your settings +basicchannels + +# end diff --git a/doc/polymono/poly_mono_3.txt b/doc/polymono/poly_mono_3.txt new file mode 100644 index 00000000..cfc4e595 --- /dev/null +++ b/doc/polymono/poly_mono_3.txt @@ -0,0 +1,26 @@ +# How to change an actual basic channel ? +# --------------------------------------- +# Perhaps you have already set several groups of basics channels +# and you want change the settings of one. + +# Assuming following actual groups: +# -Group 1:Basic channel: 5, poly omni off(mode 2), nbr: 1 +# -Group 2:Basic channel: 10, mono omni off(mode 3), nbr: 1 +# -Group 3:Basic channel: 13, mono omni off(mode 3), nbr: 2 + +# Now we want to change group 1: +# Group 1 should have the following settings: +# -basic channel 5, mode poly, omni on, (mode 0) composed of 4 channels in this group. + +#First use the command resetbasicchannels to clear the group intended to be changed. +resetbasicchannels 5 + +# Then use command setbasicchannels using numbered mode: +setbasicchannels 5 0 4 +# or Using named mode: +# setbasicchannels 5 poly_omnion 4 + +# Use basicchannels command to verify your settings +basicchannels + +# end diff --git a/doc/polymono/poly_mono_4.txt b/doc/polymono/poly_mono_4.txt new file mode 100644 index 00000000..bac7b6f0 --- /dev/null +++ b/doc/polymono/poly_mono_4.txt @@ -0,0 +1,25 @@ +# How to change an actual basic channel and an add new one ? +# -------------------------------------------------------- +# Note that command setbasicchannels allows to add or change +# groups of basics channels. +# +# Note also that the commands allows this to more than one +# groups executing only one command: + +# The following command restores Group 1 to the state: +# -Group 1:Basic channel: 5, poly omni off(mode 2), nbr: 1 +# Then adds a new group: +# -Group 0:Basic channel: 2, mono omni on(mode 1), composed of 3 possible channels in this group + +#First use the command resetbasicchannels to clear the group intended to be changed. +resetbasicchannels 5 + +# Use command setbasicchannels to change a group and add a new one, using numbered mode: +setbasicchannels 5 2 0 2 1 3 +# or using named mode: +# setbasicchannels 5 poly_omnioff 0 2 mono_omnion 3 + +# Use basicchannels command to verify your settings +basicchannels + +# end diff --git a/doc/polymono/poly_mono_5.txt b/doc/polymono/poly_mono_5.txt new file mode 100644 index 00000000..a2c17171 --- /dev/null +++ b/doc/polymono/poly_mono_5.txt @@ -0,0 +1,9 @@ +# How to know the state of one or more MIDI channels ? +# ----------------------------------------------------- +# Use the command channelsmode [chan1 chan2 ….] +channelsmode + +# To display the state of MIDI channels 2,5, 10, 13 only +channelsmode 2 5 10 13 + +# end diff --git a/doc/polymono/readme.txt b/doc/polymono/readme.txt new file mode 100644 index 00000000..a70f1a40 --- /dev/null +++ b/doc/polymono/readme.txt @@ -0,0 +1,22 @@ +/fluidSynth/doc/polymono directory contains: + +1) FluidPolyMono-0004.pdf, the documentation of poly/mono functionalities. +2) tutorials examples files: +2.1) tutorials chapter 2.1 + poly_mono_0.txt + poly_mono_1.txt + poly_mono_2.txt + poly_mono_3.txt + poly_mono_4.txt + poly_mono_5.txt + +2.2) tutorials chapter 3.1 + leg_00.txt + leg_01.txt + leg_por_00.txt + leg_por_01.txt + +This tutorials files are usable directly on the FluidSynth console application. +There are described in the pdf file (chapters 2.1 and 3.1). + +jean-jacques ceresa diff --git a/include/fluidsynth/synth.h b/include/fluidsynth/synth.h index 6088569f..254316cb 100644 --- a/include/fluidsynth/synth.h +++ b/include/fluidsynth/synth.h @@ -50,7 +50,6 @@ FLUIDSYNTH_API fluid_synth_t* new_fluid_synth(fluid_settings_t* settings); FLUIDSYNTH_API void delete_fluid_synth(fluid_synth_t* synth); FLUIDSYNTH_API fluid_settings_t* fluid_synth_get_settings(fluid_synth_t* synth); - /* MIDI channel messages */ FLUIDSYNTH_API int fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel); @@ -124,11 +123,6 @@ FLUIDSYNTH_API int fluid_synth_get_bank_offset(fluid_synth_t* synth, int sfont_i /* Reverb */ - /* - * - * Reverb - * - */ FLUIDSYNTH_API int fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize, double damping, double width, double level); @@ -303,7 +297,8 @@ typedef int (*fluid_audio_callback_t)(fluid_synth_t* synth, int len, /* Synthesizer's interface to handle SoundFont loaders */ FLUIDSYNTH_API void fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader); -FLUIDSYNTH_API fluid_voice_t* fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, +FLUIDSYNTH_API fluid_voice_t* fluid_synth_alloc_voice(fluid_synth_t* synth, + fluid_sample_t* sample, int channum, int key, int vel); FLUIDSYNTH_API void fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice); FLUIDSYNTH_API void fluid_synth_get_voicelist(fluid_synth_t* synth, @@ -330,6 +325,84 @@ FLUIDSYNTH_API int fluid_synth_set_custom_filter(fluid_synth_t*, int type, int f FLUIDSYNTH_API fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth); + +/* API: Poly mono mode */ + +/** Interface to poly/mono mode variables + * + * Channel mode bits OR-ed together so that it matches with the midi spec: poly omnion (0), mono omnion (1), poly omnioff (2), mono omnioff (3) + */ +enum fluid_channel_mode_flags +{ + FLUID_CHANNEL_POLY_OFF = 0x01, /**< if flag is set, the basic channel is in mono on state, if not set poly is on */ + FLUID_CHANNEL_OMNI_OFF = 0x02, /**< if flag is set, the basic channel is in omni off state, if not set omni is on */ +}; + +/** Indicates the breath mode a channel is set to */ +enum fluid_channel_breath_flags +{ + FLUID_CHANNEL_BREATH_POLY = 0x10, /**< when channel is poly, this flag indicates that the default velocity to initial attenuation modulator is replaced by a breath to initial attenuation modulator */ + FLUID_CHANNEL_BREATH_MONO = 0x20, /**< when channel is mono, this flag indicates that the default velocity to initial attenuation modulator is replaced by a breath modulator */ + FLUID_CHANNEL_BREATH_SYNC = 0x40, /**< when channel is mono, this flag indicates that the breath controler(MSB)triggers noteon/noteoff on the running note */ +}; + +/** Indicates the mode a basic channel is set to */ +enum fluid_basic_channel_modes +{ + FLUID_CHANNEL_MODE_MASK = (FLUID_CHANNEL_OMNI_OFF | FLUID_CHANNEL_POLY_OFF), /**< Mask Poly and Omni bits of #fluid_channel_mode_flags, usually only used internally */ + FLUID_CHANNEL_MODE_OMNION_POLY = FLUID_CHANNEL_MODE_MASK & (~FLUID_CHANNEL_OMNI_OFF & ~FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 0 */ + FLUID_CHANNEL_MODE_OMNION_MONO = FLUID_CHANNEL_MODE_MASK & (~FLUID_CHANNEL_OMNI_OFF & FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 1 */ + FLUID_CHANNEL_MODE_OMNIOFF_POLY = FLUID_CHANNEL_MODE_MASK & (FLUID_CHANNEL_OMNI_OFF & ~FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 2 */ + FLUID_CHANNEL_MODE_OMNIOFF_MONO = FLUID_CHANNEL_MODE_MASK & (FLUID_CHANNEL_OMNI_OFF | FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 3 */ + FLUID_CHANNEL_MODE_LAST /**< @internal Value defines the count of basic channel modes (#fluid_basic_channel_modes) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ +}; + +FLUIDSYNTH_API int fluid_synth_reset_basic_channel(fluid_synth_t* synth, int chan); + +FLUIDSYNTH_API int fluid_synth_get_basic_channel(fluid_synth_t* synth, int chan, + int *basic_chan_out, + int *mode_chan_out, + int *basic_val_out ); +FLUIDSYNTH_API int fluid_synth_set_basic_channel(fluid_synth_t* synth, int chan, int mode, int val); + +/** Interface to mono legato mode + * + * Indicates the legato mode a channel is set to + * n1,n2,n3,.. is a legato passage. n1 is the first note, and n2,n3,n4 are played legato with previous note. */ +enum fluid_channel_legato_mode +{ + FLUID_CHANNEL_LEGATO_MODE_RETRIGGER, /**< Mode 0 - Release previous note, start a new note */ + FLUID_CHANNEL_LEGATO_MODE_MULTI_RETRIGGER, /**< Mode 1 - On contiguous notes retrigger in attack section using current value, shape attack using current dynamic and make use of previous voices if any */ + FLUID_CHANNEL_LEGATO_MODE_LAST /**< @internal Value defines the count of legato modes (#fluid_channel_legato_mode) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ +}; + +FLUIDSYNTH_API int fluid_synth_set_legato_mode(fluid_synth_t* synth, int chan, int legatomode); +FLUIDSYNTH_API int fluid_synth_get_legato_mode(fluid_synth_t* synth, int chan, int *legatomode); + +/** Interface to portamento mode + * + * Indicates the portamento mode a channel is set to + */ +enum fluid_channel_portamento_mode +{ + FLUID_CHANNEL_PORTAMENTO_MODE_EACH_NOTE, /**< Mode 0 - Portamento on each note (staccato or legato) */ + FLUID_CHANNEL_PORTAMENTO_MODE_LEGATO_ONLY, /**< Mode 1 - Portamento only on legato note */ + FLUID_CHANNEL_PORTAMENTO_MODE_STACCATO_ONLY, /**< Mode 2 - Portamento only on staccato note */ + FLUID_CHANNEL_PORTAMENTO_MODE_LAST /**< @internal Value defines the count of portamento modes (#fluid_channel_portamento_mode) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ +}; + +FLUIDSYNTH_API int fluid_synth_set_portamento_mode(fluid_synth_t* synth, + int chan, int portamentomode); +FLUIDSYNTH_API int fluid_synth_get_portamento_mode(fluid_synth_t* synth, + int chan, int * portamentomode); + +/* Interface to breath mode */ +FLUIDSYNTH_API int fluid_synth_set_breath_mode(fluid_synth_t* synth, + int chan, int breathmode); +FLUIDSYNTH_API int fluid_synth_get_breath_mode(fluid_synth_t* synth, + int chan, int *breathmode); + + #ifdef __cplusplus } #endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 132b59b1..608564bf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -157,6 +157,7 @@ set ( libfluidsynth_SOURCES synth/fluid_mod.h synth/fluid_synth.c synth/fluid_synth.h + synth/fluid_synth_monopoly.c synth/fluid_tuning.c synth/fluid_tuning.h synth/fluid_voice.c diff --git a/src/bindings/fluid_cmd.c b/src/bindings/fluid_cmd.c index b9dbb612..8f111217 100644 --- a/src/bindings/fluid_cmd.c +++ b/src/bindings/fluid_cmd.c @@ -78,30 +78,32 @@ void fluid_shell_settings(fluid_settings_t* settings) /** the table of all handled commands */ static const fluid_cmd_t fluid_commands[] = { + /* general commands */ { "help", "general", fluid_handle_help, - "help Show help topics ('help TOPIC' for more info)" }, + "help Shows help topics ('help TOPIC' for more info)" }, { "quit", "general", fluid_handle_quit, "quit Quit the synthesizer" }, { "source", "general", fluid_handle_source, - "source filename Load a file and parse every line as a command" }, + "source filename Loads a file and parse every line as a command" }, + /* event commands */ { "noteon", "event", fluid_handle_noteon, - "noteon chan key vel Send noteon" }, + "noteon chan key vel Sends noteon" }, { "noteoff", "event", fluid_handle_noteoff, - "noteoff chan key Send noteoff" }, + "noteoff chan key Sends noteoff" }, { "pitch_bend", "event", fluid_handle_pitch_bend, - "pitch_bend chan offset Bend pitch" }, + "pitch_bend chan offset Bends pitch" }, { "pitch_bend_range", "event", fluid_handle_pitch_bend_range, - "pitch_bend chan range Set bend pitch range" }, + "pitch_bend chan range Sets bend pitch range" }, { "cc", "event", fluid_handle_cc, - "cc chan ctrl value Send control-change message" }, + "cc chan ctrl value Sends control-change message" }, { "prog", "event", fluid_handle_prog, - "prog chan num Send program-change message" }, + "prog chan num Sends program-change message" }, { "select", "event", fluid_handle_select, "select chan sfont bank prog Combination of bank-select and program-change" }, { "load", "general", fluid_handle_load, - "load file [reset] [bankofs] Load SoundFont (reset=0|1, def 1; bankofs=n, def 0)" }, + "load file [reset] [bankofs] Loads SoundFont (reset=0|1, def 1; bankofs=n, def 0)" }, { "unload", "general", fluid_handle_unload, - "unload id [reset] Unload SoundFont by ID (reset=0|1, default 1)"}, + "unload id [reset] Unloads SoundFont by ID (reset=0|1, default 1)"}, { "reload", "general", fluid_handle_reload, "reload id Reload the SoundFont with the specified ID" }, { "fonts", "general", fluid_handle_fonts, @@ -114,6 +116,28 @@ static const fluid_cmd_t fluid_commands[] = { "interp num Choose interpolation method for all channels" }, { "interpc", "general", fluid_handle_interpc, "interpc chan num Choose interpolation method for one channel" }, + /* polymono commands */ + { "basicchannels", "polymono", fluid_handle_basicchannels, + "basicchannels Prints the list of basic channels"}, + { "resetbasicchannels", "polymono", fluid_handle_resetbasicchannels, + "resetbasicchannels [chan1 chan2..] Resets all or some basic channels"}, + { "setbasicchannels", "polymono", fluid_handle_setbasicchannels, + "setbasicchannels [chan mode val...] Sets default, adds basic channels"}, + { "channelsmode", "polymono", fluid_handle_channelsmode, + "channelsmode [chan1 chan2..] Prints channels mode"}, + { "legatomode", "polymono", fluid_handle_legatomode, + "legatomode [chan1 chan2..] Prints channels legato mode"}, + { "setlegatomode", "polymono", fluid_handle_setlegatomode, + "setlegatomode chan mode [chan mode..] Sets legato mode"}, + { "portamentomode", "polymono", fluid_handle_portamentomode, + "portamentomode [chan1 chan2..] Prints channels portamento mode"}, + { "setportamentomode", "polymono", fluid_handle_setportamentomode, + "setportamentomode chan mode [chan mode..] Sets portamento mode"}, + { "breathmode", "polymono", fluid_handle_breathmode, + "breathmode [chan1 chan2..] Prints channels breath mode"}, + { "setbreathmode", "polymono", fluid_handle_setbreathmode, + "setbreathmode chan poly(1/0) mono(1/0) breath_sync(1/0) [..] Sets breath mode"}, + /* reverb commands */ { "rev_preset", "reverb", fluid_handle_reverbpreset, "rev_preset num Load preset num into the reverb unit" }, { "rev_setroomsize", "reverb", fluid_handle_reverbsetroomsize, @@ -126,6 +150,7 @@ static const fluid_cmd_t fluid_commands[] = { "rev_setlevel num Change reverb level" }, { "reverb", "reverb", fluid_handle_reverb, "reverb [0|1|on|off] Turn the reverb on or off" }, + /* chorus commands */ { "cho_set_nr", "chorus", fluid_handle_chorusnr, "cho_set_nr n Use n delay lines (default 3)" }, { "cho_set_level", "chorus", fluid_handle_choruslevel, @@ -140,6 +165,7 @@ static const fluid_cmd_t fluid_commands[] = { "gain value Set the master gain (0 < gain < 5)" }, { "voice_count", "general", fluid_handle_voice_count, "voice_count Get number of active synthesis voices" }, + /* tuning commands */ { "tuning", "tuning", fluid_handle_tuning, "tuning name bank prog Create a tuning with name, bank number, \n" " and program number (0 <= bank,prog <= 127)" }, @@ -155,6 +181,7 @@ static const fluid_cmd_t fluid_commands[] = { "dumptuning bank prog Print the pitch details of the tuning" }, { "reset", "general", fluid_handle_reset, "reset System reset (all notes off, reset controllers)" }, + /* settings commands */ { "set", "settings", fluid_handle_set, "set name value Set the value of a controller or settings" }, { "get", "settings", fluid_handle_get, @@ -165,7 +192,10 @@ static const fluid_cmd_t fluid_commands[] = { "settings Print out all settings" }, { "echo", "general", fluid_handle_echo, "echo arg Print arg" }, - /* LADSPA-related commands */ + /* Sleep command, useful to insert a delay between commands */ + { "sleep", "general", fluid_handle_sleep, + "sleep duration sleep duration(in ms)" }, + /* LADSPA-related commands */ #ifdef LADSPA { "ladspa_effect", "ladspa", fluid_handle_ladspa_effect, "ladspa_effect Create a new effect from a LADSPA plugin"}, @@ -184,6 +214,7 @@ static const fluid_cmd_t fluid_commands[] = { { "ladspa_reset", "ladspa", fluid_handle_ladspa_reset, "ladspa_reset Stop and reset LADSPA effects"}, #endif + /* router commands */ { "router_clear", "router", fluid_handle_router_clear, "router_clear Clears all routing rules from the midi router"}, { "router_default", "router", fluid_handle_router_default, @@ -619,7 +650,7 @@ fluid_handle_channels(void* data, int ac, char** av, fluid_ostream_t out) int verbose = 0; int i; - if (ac > 0 && strcmp( av[0], "-verbose") == 0) verbose = 1; + if (ac > 0 && FLUID_STRCMP( av[0], "-verbose") == 0) verbose = 1; for (i = 0; i < fluid_synth_count_midi_channels(handler->synth); i++) { preset = fluid_synth_get_channel_preset(handler->synth, i); @@ -873,9 +904,9 @@ fluid_handle_reverb(void* data, int ac, char** av, fluid_ostream_t out) return FLUID_FAILED; } - if ((strcmp(av[0], "0") == 0) || (strcmp(av[0], "off") == 0)) { + if ((FLUID_STRCMP(av[0], "0") == 0) || (FLUID_STRCMP(av[0], "off") == 0)) { fluid_synth_set_reverb_on(handler->synth,0); - } else if ((strcmp(av[0], "1") == 0) || (strcmp(av[0], "on") == 0)) { + } else if ((FLUID_STRCMP(av[0], "1") == 0) || (FLUID_STRCMP(av[0], "on") == 0)) { fluid_synth_set_reverb_on(handler->synth,1); } else { fluid_ostream_printf(out, "reverb: invalid arguments %s [0|1|on|off]", av[0]); @@ -960,9 +991,9 @@ fluid_handle_chorus(void* data, int ac, char** av, fluid_ostream_t out) return FLUID_FAILED; } - if ((strcmp(av[0], "0") == 0) || (strcmp(av[0], "off") == 0)) { + if ((FLUID_STRCMP(av[0], "0") == 0) || (FLUID_STRCMP(av[0], "off") == 0)) { fluid_synth_set_chorus_on(handler->synth,0); - } else if ((strcmp(av[0], "1") == 0) || (strcmp(av[0], "on") == 0)) { + } else if ((FLUID_STRCMP(av[0], "1") == 0) || (FLUID_STRCMP(av[0], "on") == 0)) { fluid_synth_set_chorus_on(handler->synth,1); } else { fluid_ostream_printf(out, "chorus: invalid arguments %s [0|1|on|off]", av[0]); @@ -990,6 +1021,28 @@ fluid_handle_echo(void* data, int ac, char** av, fluid_ostream_t out) return FLUID_OK; } +/* Purpose: + * Sleep during a time in ms + * The command itself is useful to insert a delay between commands. + * It can help for exemple to build a small song using noteon/noteoff commands + * in a command file. + */ +int +fluid_handle_sleep(void *data, int ac, char** av, fluid_ostream_t out) +{ + if (ac < 1) { + fluid_ostream_printf(out, "sleep: too few arguments.\n"); + return -1; + } + if (!fluid_is_number(av[0])) { + fluid_ostream_printf(out, "sleep: argument should be a number in ms.\n"); + return -1; + } + fluid_msleep(atoi(av[0])); /* delay in milliseconds */ + + return 0; +} + int fluid_handle_source(void* data, int ac, char** av, fluid_ostream_t out) { @@ -1651,7 +1704,7 @@ fluid_handle_help(void* data, int ac, char** av, fluid_ostream_t out) if (ac >= 1) { topic = av[0]; } - if (strcmp(topic,"help") == 0){ + if (FLUID_STRCMP(topic,"help") == 0){ /* "help help": Print a list of all topics */ fluid_ostream_printf(out, "*** Help topics:***\n" @@ -1660,7 +1713,7 @@ fluid_handle_help(void* data, int ac, char** av, fluid_ostream_t out) int listed_first_time = 1; unsigned int ii; for (ii = 0; ii < i; ii++){ - if (strcmp(fluid_commands[i].topic, fluid_commands[ii].topic) == 0){ + if (FLUID_STRCMP(fluid_commands[i].topic, fluid_commands[ii].topic) == 0){ listed_first_time = 0; }; /* if topic has already been listed */ }; /* for all topics (inner loop) */ @@ -1672,7 +1725,7 @@ fluid_handle_help(void* data, int ac, char** av, fluid_ostream_t out) /* help (arbitrary topic or "all") */ for (i = 0; i < FLUID_N_ELEMENTS(fluid_commands); i++) { if (fluid_commands[i].help != NULL) { - if (strcmp(topic,"all") == 0 || strcmp(topic,fluid_commands[i].topic) == 0){ + if (FLUID_STRCMP(topic,"all") == 0 || FLUID_STRCMP(topic,fluid_commands[i].topic) == 0){ fluid_ostream_printf(out, "%s\n", fluid_commands[i].help); count++; }; /* if it matches the topic */ @@ -1869,6 +1922,785 @@ int fluid_handle_router_par2(void* data, int ac, char** av, fluid_ostream_t out) return FLUID_OK; } +/** commands Poly/mono mode *************************************************/ + +static const char * mode_name[]={"poly omni on (0)","mono omni on (1)", + "poly omni off(2)","mono omni off(3)"}; +/* + Prints result message for commands: basicchannels, resetbasicchannels. + Prints all basic channels and print a warning if there is no basic channel. + + @param synth the synth instance. + @param out output stream. +*/ +static int print_basic_channels(fluid_synth_t* synth, fluid_ostream_t out) +{ + static const char * warning_msg = "Warning: no basic channels. All MIDI channels are disabled.\n" + "Make use of setbasicchannels to set at least a default basic channel.\n"; + + int n_chan= synth->midi_channels; + int i , n= 0; + /* prints all basic channels */ + for (i =0; i< n_chan; i++) + { + int basic_chan, mode_chan,val; + if( fluid_synth_get_basic_channel(synth, i, &basic_chan, &mode_chan, &val) == FLUID_OK) + { + if (basic_chan == i) + { + n++; + fluid_ostream_printf(out,"Basic channel:%3d, %s, nbr:%3d\n", i, + mode_name[mode_chan & FLUID_CHANNEL_MODE_MASK ], + val); + } + } + else + { + return FLUID_FAILED; /* error */ + } + } + /* prints a warning if there is no basic channel */ + if (n == 0) + { + fluid_ostream_printf(out, warning_msg); + } + return FLUID_OK; +} + +/*----------------------------------------------------------------------------- + basicchannels + Prints the list of all MIDI basic channels informations + example: + + Basic channel: 0, poly omni on (0), nbr: 3 + Basic channel: 3, poly omni off(2), nbr: 1 + Basic channel: 8, mono omni off(3), nbr: 2 + Basic channel: 13, mono omni on (1), nbr: 3 +*/ +int fluid_handle_basicchannels (void* data, int ac, char** av, + fluid_ostream_t out) +{ + FLUID_ENTRY_COMMAND(data); + fluid_synth_t* synth = handler->synth; + return print_basic_channels(synth, out); +} + +/* + Searchs a mode name and returns the channel mode number. + name must be: poly_omnion, mono_omnion, poly_omnioff, mono_omnioff. + @param name to search. + @return channel mode number (0 to 3) if name is valid, -1 otherwise. +*/ +static int get_channel_mode_num(char * name) +{ + /* argument names for channel mode parameter (see resetbasicchannels and + setbasicchannels commands*/ + static const char * name_channel_mode [FLUID_CHANNEL_MODE_LAST]= + {"poly_omnion","mono_omnion","poly_omnioff","mono_omnioff"}; + int i; + for (i = 0 ; i < FLUID_CHANNEL_MODE_LAST;i++) + { + if ( ! FLUID_STRCMP (name, name_channel_mode[i])) + { + return i; + } + } + return -1; +} + +static const char *invalid_arg_msg ="invalid argument\n"; +/* + checks basic channels arguments: chan1 mode1 val chan2 mode2 val2 ... + All arguments can be numeric. mode parameter can be a name. + Each group entry must have 3 parameters (chan,mode,val). + + @param ac argument count. + @param av argument table. + @param out output stream. + @param name_cde command name prefix. + @return 0 if arguments are valid, -1 otherwise. +*/ +static int check_basicchannels_arguments(int ac, char** av, + fluid_ostream_t out, char const * name_cde) +{ + static const char *too_few_arg_msg = "too few argument, chan mode val [chan mode val]...\n"; + int i; + for (i = 0; i < ac; i++) + { + /* checks parameters for list entries: chan1 mode1 val chan2 mode2 val2 ...*/ + /* all parameters can be numeric. mode parameter can be a name. */ + if (!fluid_is_number(av[i]) && + ( (i % 3 != 1) || get_channel_mode_num(av[i]) < 0 )) + { + fluid_ostream_printf(out, "%s: %s",name_cde, invalid_arg_msg); + return -1; + } + } + if (ac % 3) + { /* each group entry needs 3 parameters: basicchan,mode,val */ + fluid_ostream_printf(out, "%s: channel %d, %s\n",name_cde, + atoi(av[((ac/3) * 3)]),too_few_arg_msg); + return -1; + } + return 0; +} + +/* + checks channels arguments: chan1 chan2 ... + all arguments must be numeric. + + @param ac argument count. + @param av argument table. + @param out output stream. + @param name_cde command name prefix. + @return 0 if arguments are valid, -1 otherwise. +*/ +static int check_channels_arguments(int ac, char** av, + fluid_ostream_t out, char const * name_cde) +{ + int i; + for (i = 0; i < ac; i++) + { + if (!fluid_is_number(av[i])) + { + fluid_ostream_printf(out, "%s: %s",name_cde, invalid_arg_msg); + return -1; + } + } + return 0; +} + +/*----------------------------------------------------------------------------- + resetbasicchannels + + With no parameters the command resets all basic channels. + Note: Be aware than when a synth instance has no basic channels, all channels + are disabled. + In the intend to get some MIDI channels enabled, use the command setbasicchannels. + + resetbasicchannels chan1 [chan2 . . .] + Resets basic channel group chan1, basic channel group chan2 . . . +*/ +int fluid_handle_resetbasicchannels (void* data, int ac, char** av, + fluid_ostream_t out) +{ + static const char * name_cde="resetbasicchannels"; + FLUID_ENTRY_COMMAND(data); + fluid_synth_t* synth = handler->synth; + + /* checks channels arguments: chan1 chan2 .... */ + if (check_channels_arguments(ac,av,out,name_cde ) < 0) + { + return -1; + } + + if (ac ) + { /* resetbasicchannels chan1 [chan2 . . .] */ + int i; + for (i = 0; i < ac; i++) + { + int chan = atoi(av[i]); + int result = fluid_synth_reset_basic_channel(synth, chan); + if (result == FLUID_FAILED) + { + fluid_ostream_printf(out,"%s: channel %3d, %s", name_cde, chan,invalid_arg_msg); + } + } + } + else + { + /* resets all basic channels */ + fluid_synth_reset_basic_channel(synth, -1); + } + /* prints result */ + return print_basic_channels(synth, out); +} + +/*----------------------------------------------------------------------------- + setbasicchannels + + With no parameters the command sets one channel basic at basic channel 0 in + Omni On Poly (i.e all the MIDI channels are polyphonic). + + setbasicchannels chan1 mode1 nbr1 [chan2 mode2 nbr2] ... ... + + Adds basic channel 1 and 2 + + The command fails if any channels overlaps any existing basic channel groups. + To make room if necessary, existing basic channel groups can be cleared using + resetbasicchannels command. + Mode can be a numeric value or a name: + numeric: 0 to 3 or + name: poly_omnion , mono_omnion, poly_omnioff, mono_omnioff. +*/ +int fluid_handle_setbasicchannels (void* data, int ac, char** av, + fluid_ostream_t out) +{ + static const char * name_cde="setbasicchannels"; + FLUID_ENTRY_COMMAND(data); + fluid_synth_t* synth = handler->synth; + int result; + int i,n ; + + if(!ac) + { + /* sets one default basic channel */ + fluid_synth_reset_basic_channel(synth, -1); /* reset all basic channels */ + /* sets one basic channel Omni On Poly (i.e all the MIDI channels are polyphonic) */ + fluid_synth_set_basic_channel( synth, 0, FLUID_CHANNEL_MODE_OMNION_POLY,0); + return 0; + } + /* checks parameters: chan1 mode1 val1 chan2 mode2 val2 */ + if (check_basicchannels_arguments(ac,av,out,name_cde) < 0) + { + return -1; + } + + n = ac / 3; /* number of basic channel information */ + for (i = 0; i < n; i++) + { + int basicchan, mode, val; + + basicchan = atoi(av[(i * 3)]); /* chan is numeric */ + if (fluid_is_number(av[(i * 3)+1])) + { /* chan is numeric */ + mode = atoi(av[(i * 3)+1]); + } + else + { /* mode is a name */ + mode = get_channel_mode_num(av[(i * 3)+1]); + } + val = atoi(av[(i * 3)+2]); /* val is numeric */ + + /* changes or sets basic channels */ + result = fluid_synth_set_basic_channel(synth, basicchan, mode, val); + if (result == FLUID_FAILED) + { + fluid_ostream_printf(out,"%s: channel %3d, mode %3d, nbr %3d, %s", + name_cde, basicchan, mode, val, invalid_arg_msg); + } + } + return 0; +} + +/* + Print result message : "channel:x is outside MIDI channel count(y)" + for commands: channelsmode, portamentomode, legatomode, breathmode,setbreathmode. + @param out output stream. + @param name_cde command name prefix. + @param chan, MIDI channel number x. + @param n_chan, number of MIDI channels y. +*/ +static void print_channel_is_outside_count(fluid_ostream_t out, char const * name_cde, + int chan, int n_chan) +{ + fluid_ostream_printf(out,"%s: channel %3d is outside MIDI channel count(%d)\n", + name_cde, chan, n_chan); +} + + +/*----------------------------------------------------------------------------- + channelsmode + Prints channel mode of all MIDI channels (Poly/mono, Enabled, Basic Channel) + example + + Channel , Status , Type , Mode , Nbr of channels + channel: 0, disabled + channel: 1, disabled + channel: 2, disabled + channel: 3, disabled + channel: 4, disabled + channel: 5, enabled, basic channel, mono omni off(3), nbr: 2 + channel: 6, enabled, -- , mono , -- + channel: 7, disabled + channel: 8, disabled + channel: 9, disabled + channel: 10, enabled, basic channel, mono omni off(3), nbr: 4 + channel: 11, enabled, -- , mono , -- + channel: 12, enabled, -- , mono , -- + channel: 13, enabled, -- , mono , -- + channel: 14, disabled + channel: 15, disabled + + channelsmode chan1 chan2 + Prints only channel mode of MIDI channels chan1, chan2 +*/ +int fluid_handle_channelsmode (void* data, int ac, char** av, + fluid_ostream_t out) +{ + static const char * header = + "Channel , Status , Type , Mode , Nbr of channels\n"; + static const char * name_cde="channelsmode"; + FLUID_ENTRY_COMMAND(data); + fluid_synth_t* synth = handler->synth; + + int i, n, n_chan= synth->midi_channels; + + /* checks parameters: chan1 chan2 .... */ + if (check_channels_arguments(ac,av,out,name_cde) < 0) + { + return -1; + } + if (ac ) + { + n = ac; /* prints ac MIDI channels number */ + } + else + { + n= n_chan; /* prints all MIDI channels number */ + } + /* prints header */ + fluid_ostream_printf(out, header); + for (i = 0; i < n; i++) + { + int basic_chan, mode, val; + int chan = ac ? atoi(av[i]): i; + int result = fluid_synth_get_basic_channel(synth, chan, &basic_chan, &mode, &val); + if (result == FLUID_OK) + { + if(basic_chan != FLUID_FAILED) + { /* This channel is enabled */ + const char * p_basicchan; /* field basic channel */ + const char * p_mode; /* field mode */ + const char *p_nbr; /* field Nbr */ + static const char * blank="--"; /* field empty */ + if (chan == basic_chan) + { /* This channel is a basic channel */ + char nbr[10]; /* field Nbr */ + FLUID_SNPRINTF(nbr,sizeof(nbr),"nbr:%3d",val); + p_nbr = nbr; + p_mode = mode_name[mode]; + p_basicchan = "basic channel"; + } + else + { /* This channel is member of a basic channel group */ + p_basicchan = blank; + if(mode & FLUID_CHANNEL_POLY_OFF) p_mode = "mono"; + else p_mode = "poly"; + p_nbr = blank; + } + fluid_ostream_printf(out, + "channel:%3d, enabled, %-13s, %-16s, %s\n", + chan, + p_basicchan, + p_mode, + p_nbr); + } + else + { + fluid_ostream_printf(out, "channel:%3d, disabled\n", chan); + } + } + else + { + print_channel_is_outside_count(out, name_cde, chan, n_chan); + if(i < n-1) + { + fluid_ostream_printf(out, header); + } + } + } + return 0; +} + +/** commands mono legato mode ***********************************************/ +/* + Prints result message for commands: legatomode, portamentomode. + @param result result from the command (FLUID_OK,FLUID_FAILED). + @param out output stream. + @param name_cde command name prefix. + @param chan MIDI channel number to display. + @param name_mode name of the mode to display. + @param n_chan, number of MIDI channels. +*/ +static void print_result_get_channel_mode(int result, fluid_ostream_t out, + char const * name_cde, int chan, + char const * name_mode, int n_chan ) +{ + if (result == FLUID_OK) + { + fluid_ostream_printf(out,"%s: channel %3d, %s\n", name_cde, chan, name_mode); + } + else + { + print_channel_is_outside_count(out, name_cde, chan, n_chan); + } +} + +/*----------------------------------------------------------------------------- + legatomode + Prints legato mode of all MIDI channels + example + + channel: 0, (1)multi-retrigger + channel: 1, (0)retrigger + channel: 2, (1)multi-retrigger + ..... + + legatomode chan1 chan2 + Prints only legato mode of MIDI channels chan1, chan2 +*/ +int fluid_handle_legatomode(void* data, int ac, char** av, + fluid_ostream_t out) +{ + static const char * name_cde="legatomode"; + static const char * name_legato_mode[FLUID_CHANNEL_LEGATO_MODE_LAST]= + { "(0)retrigger","(1)multi-retrigger" }; + + FLUID_ENTRY_COMMAND(data); + fluid_synth_t* synth = handler->synth; + int mode = 0; + int i, n, n_chan= synth->midi_channels; + + /* checks channels arguments: chan1 chan2 .... */ + if (check_channels_arguments(ac, av, out, name_cde) < 0) + { + return -1; + } + + if (ac ) + { + n = ac; /* prints ac MIDI channels number */ + } + else + { + n= n_chan; /* prints all MIDI channels number */ + } + /* prints header */ + fluid_ostream_printf(out,"Channel , legato mode\n"); + for (i = 0; i < n; i++) + { + int chan = ac ? atoi(av[i]): i; + int result = fluid_synth_get_legato_mode(synth, chan, &mode); + print_result_get_channel_mode(result, out, name_cde, chan, name_legato_mode[mode], n_chan); + } + return 0; +} + +/* + checks channels arguments by group: + -example by group of 2 arguments: chan1 val1 chan2 val2 .. .. + -example by group of 4 arguments: chan1 val1 val2 val3 chan2 val1 val2 val3 .... + + all arguments must be numeric. + + @param ac argument count. + @param av argument table. + @param nbr_arg_group number of arguments by group expected. + @param out output stream. + @param name_cde command name prefix. + @param nbr_arg_group_msg message when the number of argument by group is invalid. + @return 0 if arguments are valid, -1 otherwise. +*/ +static int check_channels_group_arguments(int ac, char** av, int nbr_arg_group, + fluid_ostream_t out, + char const * name_cde, + char const * nbr_arg_group_msg + ) +{ + if (ac) + { + /* checks channels numeric arguments */ + if (check_channels_arguments(ac, av, out, name_cde) < 0) + { + return -1; + } + if (ac % nbr_arg_group) + { /* each group entry needs nbr_arg_group parameters */ + fluid_ostream_printf(out, "%s: channel %d, %s\n",name_cde, + atoi(av[((ac/nbr_arg_group) * nbr_arg_group)]), + nbr_arg_group_msg); + return -1; + } + } + else + { + fluid_ostream_printf(out, "%s: %s",name_cde, nbr_arg_group_msg); + return -1; + } + return 0; +} + +/* + Prints result message for commands: setlegatomode, setportamentomode. + @param result result from the command (FLUID_FAILED). + @param out output stream. + @param name_cde command name prefix. + @param chan, MIDI channel number to display. + @param mode, mode value to display. +*/ +static void print_result_set_channel_mode(int result, fluid_ostream_t out, + char const * name_cde, + int chan, int mode) +{ + if (result == FLUID_FAILED) + { + fluid_ostream_printf(out,"%s: channel %3d, mode %3d, %s", name_cde, chan, mode, invalid_arg_msg); + } +} + +static const char *too_few_arg_chan_mode_msg = "too few argument, chan mode [chan mode]...\n"; +/*----------------------------------------------------------------------------- + setlegatomode chan0 mode1 [chan1 mode0] .. .. + + Changes legato mode for channels chan0 and [chan1] +*/ +int fluid_handle_setlegatomode(void* data, int ac, char** av, + fluid_ostream_t out) +{ + static const char * name_cde="setlegatomode"; + FLUID_ENTRY_COMMAND(data); + fluid_synth_t* synth = handler->synth; + int i,n ; + + /* checks channels arguments by group of 2: chan1 val1 chan2 val1 .. ..*/ + if (check_channels_group_arguments(ac, av, 2, out, name_cde, too_few_arg_chan_mode_msg) < 0) + { + return -1; + } + + n = ac / 2; /* number of legato groups informations */ + for (i = 0; i < n; i++) + { + int chan = atoi(av[(i * 2)]); + int mode = atoi(av[(i * 2)+1]); + /* changes legato mode */ + + int result = fluid_synth_set_legato_mode(synth,chan,mode); + print_result_set_channel_mode(result, out, name_cde, chan, mode); + } + return 0; +} + +/** commands mono/poly portamento mode **************************************/ + +/*----------------------------------------------------------------------------- + portamentomode + Prints portamento mode of all MIDI channels + example + + channel: 0, (2)staccato only + channel: 1, (1)legato only + channel: 2, (0)each note + channel: 3, (1)legato only + ..... + + portamentomode chan1 chan2 + Prints only portamento mode of MIDI channels chan1, chan2 +*/ +int fluid_handle_portamentomode(void* data, int ac, char** av, + fluid_ostream_t out) +{ + static const char * name_cde="portamentomode"; + static const char * name_portamento_mode[FLUID_CHANNEL_PORTAMENTO_MODE_LAST]= + { "(0)each note", "(1)legato only", "(2)staccato only" }; + + FLUID_ENTRY_COMMAND(data); + fluid_synth_t* synth = handler->synth; + int mode = 0; + int i, n, n_chan= synth->midi_channels; + + /* checks channels arguments: chan1 chan2 . . . */ + if (check_channels_arguments(ac, av, out, name_cde) < 0) + { + return -1; + } + if (ac ) + { + n = ac; /* prints ac MIDI channels number */ + } + else + { + n= n_chan; /* prints all MIDI channels number */ + } + /* prints header */ + fluid_ostream_printf(out,"Channel , portamento mode\n"); + for (i = 0; i < n; i++) + { + int chan = ac ? atoi(av[i]): i; + int result = fluid_synth_get_portamento_mode(synth, chan, &mode); + print_result_get_channel_mode(result, out, name_cde, chan, name_portamento_mode[mode], n_chan); + } + return 0; +} + +/*----------------------------------------------------------------------------- + setportamentomode chan1 mode1 [chan2 mode2] .. .. + + Changes portamento mode for channels chan1 and [chan2] +*/ +int fluid_handle_setportamentomode(void* data, int ac, char** av, + fluid_ostream_t out) +{ + static const char * name_cde="setportamentomode"; + FLUID_ENTRY_COMMAND(data); + fluid_synth_t* synth = handler->synth; + int i,n ; + + /* checks channels arguments by group of 2: chan1 val1 chan2 val1 .. .. */ + if (check_channels_group_arguments(ac, av, 2, out, name_cde, too_few_arg_chan_mode_msg) < 0) + { + return -1; + } + + n = ac / 2; /* number of portamento groups informations */ + for (i = 0; i < n; i++) + { + int chan = atoi(av[(i * 2)]); + int mode = atoi(av[(i * 2)+1]); + /* changes portamento mode */ + + int result = fluid_synth_set_portamento_mode(synth,chan,mode); + print_result_set_channel_mode(result, out, name_cde, chan, mode); + } + return 0; +} + +/** commands mono/poly breath mode *******************************************/ +/*----------------------------------------------------------------------------- + breathmode + Prints breath options of all MIDI channels. + poly breath on/off, mono breath on/off, breath sync on/off + + example + + Channel , poly breath , mono breath , breath sync + channel: 0, off , off , off + channel: 1, off , off , off + channel: 2, off , off , off + ..... + + breathmode chan1 chan2 + Prints only breath mode of MIDI channels chan1, chan2 +*/ +int fluid_handle_breathmode(void* data, int ac, char** av, + fluid_ostream_t out) +{ + static const char * name_cde="breathmode"; + static const char * header="Channel , poly breath , mono breath , breath sync\n"; + FLUID_ENTRY_COMMAND(data); + fluid_synth_t* synth = handler->synth; + int breathmode; + int i, n, n_chan= synth->midi_channels; + + /* checks channels arguments: chan1 chan2 . . . */ + if (check_channels_arguments(ac, av, out, name_cde) < 0) + { + return -1; + } + + if (ac ) + { + n = ac; /* prints ac MIDI channels number */ + } + else + { + n= n_chan; /* prints all MIDI channels number */ + } + /* prints header */ + fluid_ostream_printf(out, header); + for (i = 0; i < n; i++) + { + int chan = ac ? atoi(av[i]): i; + int result = fluid_synth_get_breath_mode(synth, chan, &breathmode); + if (result == FLUID_OK) + { + static const char * on_msg ="on"; + static const char * off_msg ="off"; + const char * msg_poly_breath, * msg_mono_breath, * msg_breath_sync; + if (breathmode & FLUID_CHANNEL_BREATH_POLY) + { + msg_poly_breath =on_msg; + } + else + { + msg_poly_breath = off_msg; + } + if (breathmode & FLUID_CHANNEL_BREATH_MONO) + { + msg_mono_breath =on_msg; + } + else + { + msg_mono_breath = off_msg; + } + if (breathmode & FLUID_CHANNEL_BREATH_SYNC) + { + msg_breath_sync =on_msg; + } + else + { + msg_breath_sync = off_msg; + } + fluid_ostream_printf(out,"channel:%3d, %-12s, %-12s, %-11s\n",chan, + msg_poly_breath, msg_mono_breath, msg_breath_sync); + } + else + { + print_channel_is_outside_count(out, name_cde, chan, n_chan); + if(i < n-1) + { + fluid_ostream_printf(out, header); + } + } + } + return 0; +} + +/*----------------------------------------------------------------------------- + setbreathmode chan1 poly_breath_mode(1/0) mono_breath_mode(1/0) mono_breath_sync(1/0) + + Changes breath options for channels chan1 [chan2] .. .. + + Example: setbreathmode 4 0 1 1 + + Parameter 1 is the channel number (i.e 4). + Parameter 2 is the " Breath modulator " enable/disable for poly mode (i.e disabled). + Parameter 3 is the " Breath modulator " enabe/disable for mono mode (i.e enabled). + Parameter 4 is "breath sync noteOn/Off" enable/disable for mono mode only (i.e enabled). + +*/ +int fluid_handle_setbreathmode(void* data, int ac, char** av, + fluid_ostream_t out) +{ + static const char * name_cde="setbreathmode"; + static const char *too_few_arg_breath_msg = + "too few argument:\nchan 1/0(breath poly) 1/0(breath mono) 1/0(breath sync mono)[..]\n"; + + FLUID_ENTRY_COMMAND(data); + fluid_synth_t* synth = handler->synth; + int i,n, n_chan= synth->midi_channels; + + /* checks channels arguments by group of 4: + chan1 val1 val2 val3 chan2 val1 val2 val3 .... ....*/ + if (check_channels_group_arguments(ac, av, 4, out, name_cde, too_few_arg_breath_msg) < 0) + { + return -1; + } + + n = ac / 4; /* number of breath groups informations */ + for (i = 0; i < n; i++) + { + int result; + int chan = atoi(av[(i * 4)]); + int poly_breath = atoi(av[(i * 4)+1]); + int mono_breath = atoi(av[(i * 4)+2]); + int breath_sync = atoi(av[(i * 4)+3]); + int breath_infos = 0; + /* changes breath infos */ + if(poly_breath) breath_infos |= FLUID_CHANNEL_BREATH_POLY; + if(mono_breath) breath_infos |= FLUID_CHANNEL_BREATH_MONO; + if(breath_sync) breath_infos |= FLUID_CHANNEL_BREATH_SYNC; + result = fluid_synth_set_breath_mode(synth,chan,breath_infos); + if (result == FLUID_FAILED) + { + print_channel_is_outside_count(out, name_cde, chan, n_chan); + } + } + return 0; +} + + #ifdef LADSPA #define CHECK_LADSPA_ENABLED(_fx, _out) \ diff --git a/src/bindings/fluid_cmd.h b/src/bindings/fluid_cmd.h index 9a132811..4b9526e4 100644 --- a/src/bindings/fluid_cmd.h +++ b/src/bindings/fluid_cmd.h @@ -84,6 +84,19 @@ int fluid_handle_router_chan(void* data, int ac, char** av, fluid_ostream_t out) int fluid_handle_router_par1(void* data, int ac, char** av, fluid_ostream_t out); int fluid_handle_router_par2(void* data, int ac, char** av, fluid_ostream_t out); +int fluid_handle_basicchannels (void* data, int ac, char** av, fluid_ostream_t out); +int fluid_handle_resetbasicchannels (void* data, int ac, char** av, fluid_ostream_t out); +int fluid_handle_setbasicchannels (void* data, int ac, char** av, fluid_ostream_t out); +int fluid_handle_channelsmode (void* data, int ac, char** av, fluid_ostream_t out); +int fluid_handle_legatomode(void* data, int ac, char** av, fluid_ostream_t out); +int fluid_handle_setlegatomode(void* data, int ac, char** av, fluid_ostream_t out); +int fluid_handle_portamentomode(void* data, int ac, char** av, fluid_ostream_t out); +int fluid_handle_setportamentomode(void* data, int ac, char** av, fluid_ostream_t out); +int fluid_handle_breathmode(void* data, int ac, char** av, fluid_ostream_t out); +int fluid_handle_setbreathmode(void* data, int ac, char** av, fluid_ostream_t out); +int fluid_handle_sleep(void *data, int ac, char** av, fluid_ostream_t out); + + #ifdef LADSPA int fluid_handle_ladspa_effect(void *data, int ac, char **av, fluid_ostream_t out); int fluid_handle_ladspa_link(void *data, int ac, char **av, fluid_ostream_t out); @@ -95,6 +108,7 @@ int fluid_handle_ladspa_stop(void *data, int ac, char **av, fluid_ostream_t out) int fluid_handle_ladspa_reset(void *data, int ac, char **av, fluid_ostream_t out); #endif + /** * Command handler function prototype. * @param data User defined data diff --git a/src/bindings/fluid_lash.c b/src/bindings/fluid_lash.c index 5ac1924a..34d68398 100644 --- a/src/bindings/fluid_lash.c +++ b/src/bindings/fluid_lash.c @@ -145,7 +145,7 @@ fluid_lash_run (void * data) while ( (config = lash_get_config (fluid_lash_client)) ) { - if (strcmp (lash_config_get_key (config), "soundfont count") == 0) + if (FLUID_STRCMP (lash_config_get_key (config), "soundfont count") == 0) pending_restores = lash_config_get_value_int (config); else { diff --git a/src/bindings/fluid_rtkit.c b/src/bindings/fluid_rtkit.c index 72e0ee80..b8d7207f 100644 --- a/src/bindings/fluid_rtkit.c +++ b/src/bindings/fluid_rtkit.c @@ -48,13 +48,13 @@ static pid_t _gettid(void) { } static int translate_error(const char *name) { - if (strcmp(name, DBUS_ERROR_NO_MEMORY) == 0) + if (FLUID_STRCMP(name, DBUS_ERROR_NO_MEMORY) == 0) return -ENOMEM; - if (strcmp(name, DBUS_ERROR_SERVICE_UNKNOWN) == 0 || - strcmp(name, DBUS_ERROR_NAME_HAS_NO_OWNER) == 0) + if (FLUID_STRCMP(name, DBUS_ERROR_SERVICE_UNKNOWN) == 0 || + FLUID_STRCMP(name, DBUS_ERROR_NAME_HAS_NO_OWNER) == 0) return -ENOENT; - if (strcmp(name, DBUS_ERROR_ACCESS_DENIED) == 0 || - strcmp(name, DBUS_ERROR_AUTH_FAILED) == 0) + if (FLUID_STRCMP(name, DBUS_ERROR_ACCESS_DENIED) == 0 || + FLUID_STRCMP(name, DBUS_ERROR_AUTH_FAILED) == 0) return -EACCES; return -EIO; diff --git a/src/drivers/fluid_portaudio.c b/src/drivers/fluid_portaudio.c index ea24ddb8..c86cabb3 100644 --- a/src/drivers/fluid_portaudio.c +++ b/src/drivers/fluid_portaudio.c @@ -216,7 +216,7 @@ new_fluid_portaudio_driver (fluid_settings_t *settings, fluid_synth_t *synth) outputParams.suggestedLatency = (PaTime)period_size / sample_rate; /* Locate the device if specified */ - if (strcmp (device, PORTAUDIO_DEFAULT_DEVICE) != 0) + if (FLUID_STRCMP (device, PORTAUDIO_DEFAULT_DEVICE) != 0) { /* The intended device is not the default device name, so we search a device among available devices */ int numDevices; @@ -239,7 +239,7 @@ new_fluid_portaudio_driver (fluid_settings_t *settings, fluid_synth_t *synth) if(name) { /* We see if the name corresponds to audio.portaudio.device */ - char found = (strcmp (device, name) == 0); + char found = (FLUID_STRCMP (device, name) == 0); FLUID_FREE (name); if(found) diff --git a/src/drivers/fluid_pulse.c b/src/drivers/fluid_pulse.c index f18ac641..553d927f 100644 --- a/src/drivers/fluid_pulse.c +++ b/src/drivers/fluid_pulse.c @@ -115,19 +115,19 @@ new_fluid_pulse_audio_driver2(fluid_settings_t* settings, fluid_settings_getint(settings, "audio.pulseaudio.adjust-latency", &adjust_latency); if (media_role != NULL) { - if (strcmp(media_role, "") != 0) { + if (FLUID_STRCMP(media_role, "") != 0) { g_setenv("PULSE_PROP_media.role", media_role, TRUE); } FLUID_FREE (media_role); /* -- free media_role string */ } - if (server && strcmp (server, "default") == 0) + if (server && FLUID_STRCMP (server, "default") == 0) { FLUID_FREE (server); /* -- free server string */ server = NULL; } - if (device && strcmp (device, "default") == 0) + if (device && FLUID_STRCMP (device, "default") == 0) { FLUID_FREE (device); /* -- free device string */ device = NULL; diff --git a/src/fluidsynth.c b/src/fluidsynth.c index fb5a9999..fed73425 100644 --- a/src/fluidsynth.c +++ b/src/fluidsynth.c @@ -70,13 +70,13 @@ void process_o_cmd_line_option(fluid_settings_t* settings, char* optarg) } /* did user request list of settings */ - if (strcmp (optarg, "help") == 0) + if (FLUID_STRCMP (optarg, "help") == 0) { option_help = 1; return; } - if (strcmp (optarg, "") == 0) { + if (FLUID_STRCMP (optarg, "") == 0) { fprintf (stderr, "Invalid -o option (name part is empty)\n"); return; } @@ -374,7 +374,7 @@ int main(int argc, char** argv) else fluid_settings_setstr(settings, "audio.driver", optarg); break; case 'C': - if ((optarg != NULL) && ((strcmp(optarg, "0") == 0) || (strcmp(optarg, "no") == 0))) { + if ((optarg != NULL) && ((FLUID_STRCMP(optarg, "0") == 0) || (FLUID_STRCMP(optarg, "no") == 0))) { fluid_settings_setint(settings, "synth.chorus.active", FALSE); } else { fluid_settings_setint(settings, "synth.chorus.active", TRUE); @@ -473,7 +473,7 @@ int main(int argc, char** argv) fluid_settings_setstr(settings, "midi.portname", optarg); break; case 'R': - if ((optarg != NULL) && ((strcmp(optarg, "0") == 0) || (strcmp(optarg, "no") == 0))) { + if ((optarg != NULL) && ((FLUID_STRCMP(optarg, "0") == 0) || (FLUID_STRCMP(optarg, "no") == 0))) { fluid_settings_setint(settings, "synth.reverb.active", FALSE); } else { fluid_settings_setint(settings, "synth.reverb.active", TRUE); diff --git a/src/midi/fluid_midi.c b/src/midi/fluid_midi.c index bbe3fb7a..4931a0ea 100644 --- a/src/midi/fluid_midi.c +++ b/src/midi/fluid_midi.c @@ -349,7 +349,7 @@ fluid_midi_file_read_track(fluid_midi_file *mf, fluid_player_t *player, int num) "An non-ascii track header found, corrupt file"); return FLUID_FAILED; - } else if (strcmp((char *) id, "MTrk") == 0) { + } else if (FLUID_STRCMP((char *) id, "MTrk") == 0) { found_track = 1; diff --git a/src/midi/fluid_midi.h b/src/midi/fluid_midi.h index 842ee93c..32cb8ff1 100644 --- a/src/midi/fluid_midi.h +++ b/src/midi/fluid_midi.h @@ -106,7 +106,7 @@ enum fluid_midi_control_change { PORTAMENTO_SWITCH = 0x41, SOSTENUTO_SWITCH = 0x42, SOFT_PEDAL_SWITCH = 0x43, - LEGATO_SWITCH = 0x45, + LEGATO_SWITCH = 0x44, HOLD2_SWITCH = 0x45, SOUND_CTRL1 = 0x46, SOUND_CTRL2 = 0x47, diff --git a/src/midi/fluid_seqbind.c b/src/midi/fluid_seqbind.c index 95d6157b..166703f9 100644 --- a/src/midi/fluid_seqbind.c +++ b/src/midi/fluid_seqbind.c @@ -252,7 +252,7 @@ static fluid_seq_id_t get_fluidsynth_dest(fluid_sequencer_t* seq) for (i = 0; i < j; i++) { id = fluid_sequencer_get_client_id(seq, i); name = fluid_sequencer_get_client_name(seq, id); - if (name && (strcmp(name, "fluidsynth") == 0)) { + if (name && (FLUID_STRCMP(name, "fluidsynth") == 0)) { return id; } } diff --git a/src/rvoice/fluid_adsr_env.h b/src/rvoice/fluid_adsr_env.h index dd11e969..1a5a4c22 100644 --- a/src/rvoice/fluid_adsr_env.h +++ b/src/rvoice/fluid_adsr_env.h @@ -95,9 +95,11 @@ fluid_adsr_env_calc(fluid_adsr_env_t* env, int is_volenv) env->section++; env->count = 0; } + else env->count++; env->val = x; - env->count++; + + } /* This one cannot be inlined since it is referenced in diff --git a/src/rvoice/fluid_rvoice.c b/src/rvoice/fluid_rvoice.c index daeef5b2..7dccded0 100644 --- a/src/rvoice/fluid_rvoice.c +++ b/src/rvoice/fluid_rvoice.c @@ -319,11 +319,37 @@ fluid_rvoice_write (fluid_rvoice_t* voice, fluid_real_t *dsp_buf) * buffer. It is the ratio between the frequencies of original * waveform and output waveform.*/ voice->dsp.phase_incr = fluid_ct2hz_real(voice->dsp.pitch + + voice->dsp.pitchoffset + fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_pitch + fluid_lfo_get_val(&voice->envlfo.viblfo) * voice->envlfo.viblfo_to_pitch + fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_pitch) / voice->dsp.root_pitch_hz; + /******************* portamento ****************/ + /* pitchoffset is updated if enabled. + Pitchoffset will be added to dsp pitch at next phase calculation time */ + + /* In most cases portamento will be disabled. Thus first verify that portamento is + * enabled before updating pitchoffset and before disabling portamento when necessary, + * in order to keep the performance loss at minimum. + * If the algorithm would first update pitchoffset and then verify if portamento + * needs to be disabled, there would be a significant performance drop on a x87 FPU + */ + if (voice->dsp.pitchinc > 0.0f) + { /* portamento is enabled, so update pitchoffset */ + voice->dsp.pitchoffset += voice->dsp.pitchinc; + /* when pitchoffset reaches 0.0f, portamento is disabled */ + if (voice->dsp.pitchoffset > 0.0f) + voice->dsp.pitchoffset = voice->dsp.pitchinc = 0.0f; + } + else if (voice->dsp.pitchinc < 0.0f) + { /* portamento is enabled, so update pitchoffset */ + voice->dsp.pitchoffset += voice->dsp.pitchinc; + /* when pitchoffset reaches 0.0f, portamento is disabled */ + if (voice->dsp.pitchoffset < 0.0f) + voice->dsp.pitchoffset = voice->dsp.pitchinc = 0.0f; + } + fluid_check_fpe ("voice_write phase calculation"); /* if phase_incr is not advancing, set it to the minimum fraction value (prevent stuckage) */ @@ -363,11 +389,11 @@ fluid_rvoice_write (fluid_rvoice_t* voice, fluid_real_t *dsp_buf) /*************** resonant filter ******************/ fluid_iir_filter_calc(&voice->resonant_filter, voice->dsp.output_rate, - fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_fc + - fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_fc); + fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_fc + + fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_fc); fluid_iir_filter_apply(&voice->resonant_filter, dsp_buf, count); - + /* additional custom filter - only uses the fixed modulator, no lfos... */ fluid_iir_filter_calc(&voice->resonant_custom_filter, voice->dsp.output_rate, 0); fluid_iir_filter_apply(&voice->resonant_custom_filter, dsp_buf, count); @@ -478,7 +504,11 @@ fluid_rvoice_reset(fluid_rvoice_t* voice) calculate the volume increment during processing */ - /* mod env initialization*/ + /* legato initialization */ + voice->dsp.pitchoffset = 0.0; /* portamento initialization */ + voice->dsp.pitchinc = 0.0; + + /* mod env initialization*/ fluid_adsr_env_reset(&voice->envlfo.modenv); /* vol env initialization */ @@ -530,6 +560,102 @@ fluid_rvoice_noteoff(fluid_rvoice_t* voice, unsigned int min_ticks) fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVRELEASE); } +/** + * skips to Attack section + * + * Updates vol and attack data + * Correction on volume val to achieve equivalent amplitude at noteOn legato + * + * @param voice the synthesis voice to be updated +*/ +static FLUID_INLINE void fluid_rvoice_local_retrigger_attack (fluid_rvoice_t* voice) +{ + /* skips to Attack section */ + /* Once in Attack section, current count must be reset, to be sure + that the section will be not be prematurely finished. */ + fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVATTACK); + { + /* Correction on volume val to achieve equivalent amplitude at noteOn legato */ + fluid_env_data_t* env_data; + fluid_real_t peak = fluid_cb2amp (voice->dsp.attenuation); + fluid_real_t prev_peak = fluid_cb2amp (voice->dsp.prev_attenuation); + voice->envlfo.volenv.val = (voice->envlfo.volenv.val * prev_peak) / peak; + /* Correction on slope direction for Attack section */ + env_data = &voice->envlfo.volenv.data[FLUID_VOICE_ENVATTACK]; + if(voice->envlfo.volenv.val <=1.0f) + { /* slope attack for legato note needs to be positive from val up to 1 */ + env_data->increment = 1.0f / env_data->count; + env_data->min = -1.0f; env_data->max = 1.0f; + } + else + { /* slope attack for legato note needs to be negative: from val down to 1 */ + env_data->increment = -voice->envlfo.volenv.val / env_data->count; + env_data->min = 1.0f; env_data->max = voice->envlfo.volenv.val; + } + } +} + +/** + * Used by legato Mode : multi_retrigger + * see fluid_synth_noteon_mono_legato_multi_retrigger() + * @param voice the synthesis voice to be updated +*/ +void +fluid_rvoice_multi_retrigger_attack (fluid_rvoice_t* voice) +{ + int section = fluid_adsr_env_get_section(&voice->envlfo.volenv); + /*------------------------------------------------------------------------- + Section skip for volume envelope + --------------------------------------------------------------------------*/ + if (section >= FLUID_VOICE_ENVHOLD) + { + /* DECAY, SUSTAIN,RELEASE section use logarithmic scaling. Calculates new + volenv_val to achieve equivalent amplitude during the attack phase + for seamless volume transition. */ + fluid_real_t amp_cb, env_value; + amp_cb = 960.0f * (1.0f - fluid_adsr_env_get_val(&voice->envlfo.volenv)); + env_value = fluid_cb2amp(amp_cb); /* a bit of optimization */ + fluid_clip (env_value, 0.0, 1.0); + fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value); + /* next, skips to Attack section */ + } + /* skips to Attack section from any section */ + /* Update vol and attack data */ + fluid_rvoice_local_retrigger_attack(voice); + /*------------------------------------------------------------------------- + Section skip for modulation envelope + --------------------------------------------------------------------------*/ + /* Skips from any section to ATTACK section */ + fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVATTACK); + /* Actually (v 1.1.6) all sections are linear, so there is no need to + correct val value. However soundfont 2.01/2.4 spec. says that Attack should + be convex (see issue #153 from Christian Collins). In the case Attack + section would be changed to a non linear shape it will be necessary to do + a correction for seamless val transition. Here is the place to do this */ +} + +/** + * sets the portamento dsp parameters: dsp.pitchoffset, dsp.pitchinc + * @param voice rvoice to set portamento. + * @param countinc increment count number. + * @param pitchoffset pitch offset to apply to voice dsp.pitch. + * + * Notes: + * 1) To get continuous portamento between consecutive noteOn (n1,n2,n3...), + * pitchoffset is accumulated in current dsp pitchoffset. + * 2) And to get constant portamento duration, dsp pitch increment is updated. +*/ +void fluid_rvoice_set_portamento(fluid_rvoice_t * voice, unsigned int countinc, + fluid_real_t pitchoffset) +{ + if (countinc) + { + voice->dsp.pitchoffset += pitchoffset; + voice->dsp.pitchinc = - voice->dsp.pitchoffset / countinc; + } + /* Then during the voice processing (in fluid_rvoice_write()), + dsp.pitchoffset will be incremented by dsp pitchinc. */ +} void fluid_rvoice_set_output_rate(fluid_rvoice_t* voice, fluid_real_t value) @@ -559,6 +685,7 @@ fluid_rvoice_set_pitch(fluid_rvoice_t* voice, fluid_real_t value) void fluid_rvoice_set_attenuation(fluid_rvoice_t* voice, fluid_real_t value) { + voice->dsp.prev_attenuation = voice->dsp.attenuation; voice->dsp.attenuation = value; } diff --git a/src/rvoice/fluid_rvoice.h b/src/rvoice/fluid_rvoice.h index b0dece68..88e1ddf4 100644 --- a/src/rvoice/fluid_rvoice.h +++ b/src/rvoice/fluid_rvoice.h @@ -96,6 +96,10 @@ struct _fluid_rvoice_dsp_t int loopend; /* Note: first point following the loop (superimposed on loopstart) */ enum fluid_loop samplemode; + /* Stuff needed for portamento calculations */ + fluid_real_t pitchoffset; /* the portamento range in midicents */ + fluid_real_t pitchinc; /* the portamento increment in midicents */ + /* Stuff needed for phase calculations */ fluid_real_t pitch; /* the pitch in midicents */ @@ -106,6 +110,8 @@ struct _fluid_rvoice_dsp_t int has_looped; /* Flag that is set as soon as the first loop is completed. */ fluid_real_t attenuation; /* the attenuation in centibels */ + fluid_real_t prev_attenuation; /* the previous attenuation in centibels + used by fluid_rvoice_multi_retrigger_attack() */ fluid_real_t min_attenuation_cB; /* Estimate on the smallest possible attenuation * during the lifetime of the voice */ fluid_real_t amplitude_that_reaches_noise_floor_nonloop; @@ -167,7 +173,9 @@ void fluid_rvoice_buffers_set_mapping(fluid_rvoice_buffers_t* buffers, unsigned int bufnum, int mapping); /* Dynamic update functions */ - +void fluid_rvoice_set_portamento(fluid_rvoice_t * voice, unsigned int countinc, + fluid_real_t pitchoffset); +void fluid_rvoice_multi_retrigger_attack(fluid_rvoice_t* voice); void fluid_rvoice_noteoff(fluid_rvoice_t* voice, unsigned int min_ticks); void fluid_rvoice_voiceoff(fluid_rvoice_t* voice); void fluid_rvoice_reset(fluid_rvoice_t* voice); diff --git a/src/rvoice/fluid_rvoice_event.c b/src/rvoice/fluid_rvoice_event.c index c73b1b93..af5be0e5 100644 --- a/src/rvoice/fluid_rvoice_event.c +++ b/src/rvoice/fluid_rvoice_event.c @@ -103,6 +103,9 @@ fluid_rvoice_event_dispatch(fluid_rvoice_event_t* event) EVENTFUNC_I1(fluid_rvoice_noteoff, fluid_rvoice_t*); EVENTFUNC_0(fluid_rvoice_voiceoff, fluid_rvoice_t*); EVENTFUNC_0(fluid_rvoice_reset, fluid_rvoice_t*); + + EVENTFUNC_0(fluid_rvoice_multi_retrigger_attack, fluid_rvoice_t*); + EVENTFUNC_IR(fluid_rvoice_set_portamento, fluid_rvoice_t*); EVENTFUNC_IIR4(fluid_adsr_env_set_data, fluid_adsr_env_t*); diff --git a/src/sfloader/fluid_defsfont.c b/src/sfloader/fluid_defsfont.c index 223baf00..c21e6d8f 100644 --- a/src/sfloader/fluid_defsfont.c +++ b/src/sfloader/fluid_defsfont.c @@ -25,6 +25,7 @@ #include "fluid_defsfont.h" #include "fluid_sfont.h" #include "fluid_sys.h" +#include "fluid_synth.h" #if LIBSNDFILE_SUPPORT #include @@ -52,7 +53,7 @@ */ fluid_sfloader_t* new_fluid_defsfloader(fluid_settings_t* settings) { - fluid_sfloader_t* loader; + fluid_sfloader_t* loader; fluid_return_val_if_fail(settings != NULL, NULL); loader = new_fluid_sfloader(fluid_defsfloader_load, delete_fluid_sfloader); @@ -64,7 +65,7 @@ fluid_sfloader_t* new_fluid_defsfloader(fluid_settings_t* settings) } fluid_sfloader_set_data(loader, settings); - + return loader; } @@ -91,7 +92,7 @@ fluid_sfont_t* fluid_defsfloader_load(fluid_sfloader_t* loader, const char* file { return NULL; } - + fluid_sfont_set_iteration_start(sfont, fluid_defsfont_sfont_iteration_start); fluid_sfont_set_iteration_next(sfont, fluid_defsfont_sfont_iteration_next); fluid_sfont_set_data(sfont, defsfont); @@ -274,7 +275,7 @@ static int fluid_cached_sampledata_load(char *filename, } for (cached_sampledata = all_cached_sampledata; cached_sampledata; cached_sampledata = cached_sampledata->next) { - if (strcmp(filename, cached_sampledata->filename)) + if (FLUID_STRCMP(filename, cached_sampledata->filename)) continue; if (cached_sampledata->modification_time != modification_time) continue; @@ -854,7 +855,7 @@ fluid_defpreset_noteon(fluid_defpreset_t* preset, fluid_synth_t* synth, int chan /* check if the note falls into the key and velocity range of this preset */ - if (fluid_preset_zone_inside_range(preset_zone, key, vel)) { + if (fluid_zone_inside_range(&preset_zone->range, key, vel)) { inst = fluid_preset_zone_get_inst(preset_zone); global_inst_zone = fluid_inst_get_global_zone(inst); @@ -862,7 +863,6 @@ fluid_defpreset_noteon(fluid_defpreset_t* preset, fluid_synth_t* synth, int chan /* run thru all the zones of this instrument */ inst_zone = fluid_inst_get_zone(inst); while (inst_zone != NULL) { - /* make sure this instrument zone has a valid sample */ sample = fluid_inst_zone_get_sample(inst_zone); if ((sample == NULL) || fluid_sample_in_rom(sample)) { @@ -870,15 +870,14 @@ fluid_defpreset_noteon(fluid_defpreset_t* preset, fluid_synth_t* synth, int chan continue; } - /* check if the note falls into the key and velocity range of this - instrument */ + /* check if the instrument zone is ignored and the note falls into + the key and velocity range of this instrument zone. + An instrument zone must be ignored when its voice is already running + played by a legato passage (see fluid_synth_noteon_monopoly_legato()) */ + if (fluid_zone_inside_range(&inst_zone->range, key, vel)) { - if (fluid_inst_zone_inside_range(inst_zone, key, vel) && (sample != NULL)) { - - /* this is a good zone. allocate a new synthesis process and - initialize it */ - - voice = fluid_synth_alloc_voice(synth, sample, chan, key, vel); + /* this is a good zone. allocate a new synthesis process and initialize it */ + voice = fluid_synth_alloc_voice_LOCAL(synth, sample, chan, key, vel, &inst_zone->range); if (voice == NULL) { return FLUID_FAILED; } @@ -1144,6 +1143,11 @@ fluid_defpreset_get_global_zone(fluid_defpreset_t* preset) return preset->global_zone; } +/*************************************************************** + * + * PRESET_ZONE + */ + /* * fluid_preset_zone_next */ @@ -1176,10 +1180,11 @@ new_fluid_preset_zone(char *name) } FLUID_STRCPY(zone->name, name); zone->inst = NULL; - zone->keylo = 0; - zone->keyhi = 128; - zone->vello = 0; - zone->velhi = 128; + zone->range.keylo = 0; + zone->range.keyhi = 128; + zone->range.vello = 0; + zone->range.velhi = 128; + zone->range.ignore = FALSE; /* Flag all generators as unused (default, they will be set when they are found * in the sound font). @@ -1189,11 +1194,6 @@ new_fluid_preset_zone(char *name) return zone; } -/*************************************************************** - * - * PRESET_ZONE - */ - /* * delete_fluid_preset_zone */ @@ -1230,12 +1230,12 @@ fluid_preset_zone_import_sfont(fluid_preset_zone_t* zone, SFZone *sfzone, fluid_ sfgen = (SFGen *) r->data; switch (sfgen->id) { case GEN_KEYRANGE: - zone->keylo = (int) sfgen->amount.range.lo; - zone->keyhi = (int) sfgen->amount.range.hi; + zone->range.keylo = sfgen->amount.range.lo; + zone->range.keyhi = sfgen->amount.range.hi; break; case GEN_VELRANGE: - zone->vello = (int) sfgen->amount.range.lo; - zone->velhi = (int) sfgen->amount.range.hi; + zone->range.vello = sfgen->amount.range.lo; + zone->range.velhi = sfgen->amount.range.hi; break; case GEN_ATTENUATION: /* EMU8k/10k hardware applies a scale factor to initial attenuation generator values set at @@ -1257,7 +1257,8 @@ fluid_preset_zone_import_sfont(fluid_preset_zone_t* zone, SFZone *sfzone, fluid_ FLUID_LOG(FLUID_ERR, "Out of memory"); return FLUID_FAILED; } - if (fluid_inst_import_sfont(zone->inst, (SFInst *) sfzone->instsamp->data, sfont) != FLUID_OK) { + if (fluid_inst_import_sfont(zone, zone->inst, + (SFInst *) sfzone->instsamp->data, sfont) != FLUID_OK) { return FLUID_FAILED; } } @@ -1404,17 +1405,6 @@ fluid_preset_zone_get_inst(fluid_preset_zone_t* zone) return zone->inst; } -/* - * fluid_preset_zone_inside_range - */ -int -fluid_preset_zone_inside_range(fluid_preset_zone_t* zone, int key, int vel) -{ - return ((zone->keylo <= key) && - (zone->keyhi >= key) && - (zone->vello <= vel) && - (zone->velhi >= vel)); -} /*************************************************************** * @@ -1474,7 +1464,8 @@ fluid_inst_set_global_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone) * fluid_inst_import_sfont */ int -fluid_inst_import_sfont(fluid_inst_t* inst, SFInst *sfinst, fluid_defsfont_t* sfont) +fluid_inst_import_sfont(fluid_preset_zone_t* zonePZ, fluid_inst_t* inst, + SFInst *sfinst, fluid_defsfont_t* sfont) { fluid_list_t *p; SFZone* sfzone; @@ -1500,7 +1491,7 @@ fluid_inst_import_sfont(fluid_inst_t* inst, SFInst *sfinst, fluid_defsfont_t* sf return FLUID_FAILED; } - if (fluid_inst_zone_import_sfont(zone, sfzone, sfont) != FLUID_OK) { + if (fluid_inst_zone_import_sfont(zonePZ,zone, sfzone, sfont) != FLUID_OK) { delete_fluid_inst_zone(zone); return FLUID_FAILED; } @@ -1580,11 +1571,11 @@ new_fluid_inst_zone(char* name) } FLUID_STRCPY(zone->name, name); zone->sample = NULL; - zone->keylo = 0; - zone->keyhi = 128; - zone->vello = 0; - zone->velhi = 128; - + zone->range.keylo = 0; + zone->range.keyhi = 128; + zone->range.vello = 0; + zone->range.velhi = 128; + zone->range.ignore = FALSE; /* Flag the generators as unused. * This also sets the generator values to default, but they will be overwritten anyway, if used.*/ fluid_gen_set_default_values(&zone->gen[0]); @@ -1627,7 +1618,8 @@ fluid_inst_zone_next(fluid_inst_zone_t* zone) * fluid_inst_zone_import_sfont */ int -fluid_inst_zone_import_sfont(fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont) +fluid_inst_zone_import_sfont(fluid_preset_zone_t* preset_zone, fluid_inst_zone_t* zone, + SFZone *sfzone, fluid_defsfont_t* sfont) { fluid_list_t *r; SFGen* sfgen; @@ -1637,12 +1629,12 @@ fluid_inst_zone_import_sfont(fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defs sfgen = (SFGen *) r->data; switch (sfgen->id) { case GEN_KEYRANGE: - zone->keylo = (int) sfgen->amount.range.lo; - zone->keyhi = (int) sfgen->amount.range.hi; + zone->range.keylo = sfgen->amount.range.lo; + zone->range.keyhi = sfgen->amount.range.hi; break; case GEN_VELRANGE: - zone->vello = (int) sfgen->amount.range.lo; - zone->velhi = (int) sfgen->amount.range.hi; + zone->range.vello = sfgen->amount.range.lo; + zone->range.velhi = sfgen->amount.range.hi; break; case GEN_ATTENUATION: /* EMU8k/10k hardware applies a scale factor to initial attenuation generator values set at @@ -1659,6 +1651,13 @@ fluid_inst_zone_import_sfont(fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defs } r = fluid_list_next(r); } + + /* adjust instrument zone keyrange to integrate preset zone keyrange */ + if (preset_zone->range.keylo > zone->range.keylo) zone->range.keylo = preset_zone->range.keylo; + if (preset_zone->range.keyhi < zone->range.keyhi) zone->range.keyhi = preset_zone->range.keyhi; + /* adjust instrument zone to integrate preset zone velrange */ + if (preset_zone->range.vello > zone->range.vello) zone->range.vello = preset_zone->range.vello; + if (preset_zone->range.velhi < zone->range.velhi) zone->range.velhi = preset_zone->range.velhi; /* FIXME */ /* if (zone->gen[GEN_EXCLUSIVECLASS].flags == GEN_SET) { */ @@ -1808,16 +1807,20 @@ fluid_inst_zone_get_sample(fluid_inst_zone_t* zone) return zone->sample; } -/* - * fluid_inst_zone_inside_range - */ + int -fluid_inst_zone_inside_range(fluid_inst_zone_t* zone, int key, int vel) +fluid_zone_inside_range(fluid_zone_range_t* range, int key, int vel) { - return ((zone->keylo <= key) && - (zone->keyhi >= key) && - (zone->vello <= vel) && - (zone->velhi >= vel)); + /* ignoreInstrumentZone is set in mono legato playing */ + int ignore_zone = range->ignore; + + /* Reset the 'ignore' request */ + range->ignore = FALSE; + + return !ignore_zone && ((range->keylo <= key) && + (range->keyhi >= key) && + (range->vello <= vel) && + (range->velhi >= vel)); } /*************************************************************** diff --git a/src/sfloader/fluid_defsfont.h b/src/sfloader/fluid_defsfont.h index c1d1cb6d..2e055938 100644 --- a/src/sfloader/fluid_defsfont.h +++ b/src/sfloader/fluid_defsfont.h @@ -343,7 +343,18 @@ typedef struct _fluid_defsfont_t fluid_defsfont_t; typedef struct _fluid_defpreset_t fluid_defpreset_t; typedef struct _fluid_preset_zone_t fluid_preset_zone_t; typedef struct _fluid_inst_t fluid_inst_t; -typedef struct _fluid_inst_zone_t fluid_inst_zone_t; +typedef struct _fluid_inst_zone_t fluid_inst_zone_t; /**< Soundfont Instrument Zone */ + +/* defines the velocity and key range for a zone */ +struct _fluid_zone_range_t +{ + int keylo; + int keyhi; + int vello; + int velhi; + unsigned char ignore; /* set to TRUE for legato playing to ignore this range zone */ +}; + /* @@ -367,6 +378,7 @@ int fluid_defpreset_preset_get_banknum(fluid_preset_t* preset); int fluid_defpreset_preset_get_num(fluid_preset_t* preset); int fluid_defpreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel); +int fluid_zone_inside_range(fluid_zone_range_t* zone_range, int key, int vel); /* * fluid_defsfont_t @@ -441,10 +453,7 @@ struct _fluid_preset_zone_t fluid_preset_zone_t* next; char* name; fluid_inst_t* inst; - int keylo; - int keyhi; - int vello; - int velhi; + fluid_zone_range_t range; fluid_gen_t gen[GEN_LAST]; fluid_mod_t * mod; /* List of modulators */ }; @@ -453,7 +462,6 @@ fluid_preset_zone_t* new_fluid_preset_zone(char* name); void delete_fluid_preset_zone(fluid_preset_zone_t* zone); fluid_preset_zone_t* fluid_preset_zone_next(fluid_preset_zone_t* preset); int fluid_preset_zone_import_sfont(fluid_preset_zone_t* zone, SFZone* sfzone, fluid_defsfont_t* sfont); -int fluid_preset_zone_inside_range(fluid_preset_zone_t* zone, int key, int vel); fluid_inst_t* fluid_preset_zone_get_inst(fluid_preset_zone_t* zone); /* @@ -467,8 +475,9 @@ struct _fluid_inst_t }; fluid_inst_t* new_fluid_inst(void); +int fluid_inst_import_sfont(fluid_preset_zone_t* zonePZ, fluid_inst_t* inst, + SFInst *sfinst, fluid_defsfont_t* sfont); void delete_fluid_inst(fluid_inst_t* inst); -int fluid_inst_import_sfont(fluid_inst_t* inst, SFInst *sfinst, fluid_defsfont_t* sfont); int fluid_inst_set_global_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone); int fluid_inst_add_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone); fluid_inst_zone_t* fluid_inst_get_zone(fluid_inst_t* inst); @@ -482,19 +491,17 @@ struct _fluid_inst_zone_t fluid_inst_zone_t* next; char* name; fluid_sample_t* sample; - int keylo; - int keyhi; - int vello; - int velhi; + fluid_zone_range_t range; fluid_gen_t gen[GEN_LAST]; fluid_mod_t * mod; /* List of modulators */ }; + fluid_inst_zone_t* new_fluid_inst_zone(char* name); void delete_fluid_inst_zone(fluid_inst_zone_t* zone); fluid_inst_zone_t* fluid_inst_zone_next(fluid_inst_zone_t* zone); -int fluid_inst_zone_import_sfont(fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont); -int fluid_inst_zone_inside_range(fluid_inst_zone_t* zone, int key, int vel); +int fluid_inst_zone_import_sfont(fluid_preset_zone_t* zonePZ, + fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont); fluid_sample_t* fluid_inst_zone_get_sample(fluid_inst_zone_t* zone); diff --git a/src/sfloader/fluid_ramsfont.c b/src/sfloader/fluid_ramsfont.c index c796896b..d243000c 100644 --- a/src/sfloader/fluid_ramsfont.c +++ b/src/sfloader/fluid_ramsfont.c @@ -91,7 +91,7 @@ fluid_ramsfont_create_sfont() if (ramsfont == NULL) { return NULL; } - + sfont = new_fluid_sfont(fluid_ramsfont_sfont_get_name, fluid_ramsfont_sfont_get_preset, fluid_ramsfont_sfont_delete); @@ -99,11 +99,11 @@ fluid_ramsfont_create_sfont() { return NULL; } - + fluid_sfont_set_iteration_start(sfont, fluid_ramsfont_sfont_iteration_start); fluid_sfont_set_iteration_next(sfont, fluid_ramsfont_sfont_iteration_next); fluid_sfont_set_data(sfont, ramsfont); - + return sfont; } @@ -631,8 +631,8 @@ fluid_rampreset_add_sample (fluid_rampreset_t* preset, fluid_sample_t* sample, } izone->sample = sample; - izone->keylo = lokey; - izone->keyhi = hikey; + izone->range.keylo = lokey; + izone->range.keyhi = hikey; // give the preset the name of the sample FLUID_MEMCPY(preset->name, sample->name, 20); @@ -881,7 +881,7 @@ fluid_rampreset_noteon (fluid_rampreset_t* preset, fluid_synth_t* synth, int cha /* check if the note falls into the key and velocity range of this preset */ - if (fluid_preset_zone_inside_range(preset_zone, key, vel)) { + if (fluid_zone_inside_range(&preset_zone->range, key, vel)) { inst = fluid_preset_zone_get_inst(preset_zone); global_inst_zone = fluid_inst_get_global_zone(inst); @@ -889,7 +889,7 @@ fluid_rampreset_noteon (fluid_rampreset_t* preset, fluid_synth_t* synth, int cha /* run thru all the zones of this instrument */ inst_zone = fluid_inst_get_zone(inst); while (inst_zone != NULL) { - + /* make sure this instrument zone has a valid sample */ sample = fluid_inst_zone_get_sample(inst_zone); if ((sample == NULL) || fluid_sample_in_rom(sample)) { @@ -897,15 +897,14 @@ fluid_rampreset_noteon (fluid_rampreset_t* preset, fluid_synth_t* synth, int cha continue; } - /* check if the note falls into the key and velocity range of this - instrument */ + /* check if the instrument zone doesn't be ignored and the note falls into + the key and velocity range of this instrument zone. + An instrument zone must be ignored when its voice is already running + played by a legato passage (see fluid_synth_noteon_monopoly_legato()) */ + if (fluid_zone_inside_range(&inst_zone->range, key, vel)) { - if (fluid_inst_zone_inside_range(inst_zone, key, vel) && (sample != NULL)) { - - /* this is a good zone. allocate a new synthesis process and - initialize it */ - - voice = fluid_synth_alloc_voice(synth, sample, chan, key, vel); + /* this is a good zone. allocate a new synthesis process and initialize it */ + voice = fluid_synth_alloc_voice_LOCAL(synth, sample, chan, key, vel, &inst_zone->range); if (voice == NULL) { return FLUID_FAILED; } diff --git a/src/synth/fluid_chan.c b/src/synth/fluid_chan.c index bc12005d..a2cc6d0f 100644 --- a/src/synth/fluid_chan.c +++ b/src/synth/fluid_chan.c @@ -65,10 +65,27 @@ static void fluid_channel_init(fluid_channel_t* chan) { fluid_preset_t *newpreset; - int prognum, banknum; + int i, prognum, banknum; chan->sostenuto_orderid = 0; - + /*--- Init poly/mono modes variables --------------------------------------*/ + chan->mode = 0; + chan->mode_val = 0; + /* monophonic list initialization */ + for (i=0; i < FLUID_CHANNEL_SIZE_MONOLIST; i++) + { + chan->monolist[i].next = i+1; + } + chan->monolist[FLUID_CHANNEL_SIZE_MONOLIST -1].next = 0; /* ending element chained to the 1st */ + chan->i_last = chan->n_notes = 0; /* clears the list */ + chan->i_first = chan->monolist[chan->i_last].next; /* first note index in the list */ + fluid_channel_clear_prev_note(chan); /* Mark previous note invalid */ + /*---*/ + chan->key_mono_sustained = INVALID_NOTE; /* No previous mono note sustained */ + chan->legatomode = FLUID_CHANNEL_LEGATO_MODE_MULTI_RETRIGGER; /* Default mode */ + chan->portamentomode = FLUID_CHANNEL_PORTAMENTO_MODE_LEGATO_ONLY; /* Default mode */ +/*--- End of poly/mono initialization --------------------------------------*/ + chan->channel_type = (chan->channum == 9) ? CHANNEL_TYPE_DRUM : CHANNEL_TYPE_MELODIC; prognum = 0; banknum = (chan->channel_type == CHANNEL_TYPE_DRUM) ? DRUM_INST_BANK : 0; @@ -131,6 +148,8 @@ fluid_channel_init_ctrl(fluid_channel_t* chan, int is_all_ctrl_off) for (i = 0; i < 128; i++) { fluid_channel_set_cc (chan, i, 0); } + fluid_channel_clear_portamento(chan); /* Clear PTC receive */ + chan->previous_cc_breath = 0;/* Reset previous breath */ } /* Reset polyphonic key pressure on all voices */ @@ -300,3 +319,309 @@ fluid_channel_get_sfont_bank_prog(fluid_channel_t* chan, int *sfont, if (bank) *bank = (sfont_bank_prog & BANK_MASKVAL) >> BANK_SHIFTVAL; if (prog) *prog = (sfont_bank_prog & PROG_MASKVAL) >> PROG_SHIFTVAL; } + +/** + * Updates legato/ staccato playing state + * The function is called: + * - on noteon before adding a note into the monolist. + * - on noteoff after removing a note out of the monolist. + * @param chan fluid_channel_t. +*/ +static void +fluid_channel_update_legato_staccato_state(fluid_channel_t* chan) +{ + /* Updates legato/ staccato playing state */ + if (chan->n_notes) + { + chan->mode |= FLUID_CHANNEL_LEGATO_PLAYING; /* Legato state */ + } + else + { + chan->mode &= ~ FLUID_CHANNEL_LEGATO_PLAYING; /* Staccato state */ + } +} + +/** + * Adds a note into the monophonic list. The function is part of the legato + * detector. fluid_channel_add_monolist() is intended to be called by + * fluid_synth_noteon_mono_LOCAL(). + * + * When a note is added at noteOn each element is use in the forward direction + * and indexed by i_last variable. + * + * @param chan fluid_channel_t. + * @param key MIDI note number (0-127). + * @param vel MIDI velocity (0-127, 0=noteoff). + * @param onenote. When 1 the function adds the note but the monophonic list + * keeps only one note (used on noteOn poly). + * Note: i_last index keeps a trace of the most recent note added. + * prev_note keeps a trace of the note prior i_last note. + * FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing state. + * + * More informations in FluidPolyMono-0004.pdf chapter 4 (Appendices). +*/ +void +fluid_channel_add_monolist(fluid_channel_t* chan, unsigned char key, + unsigned char vel, unsigned char onenote) +{ + unsigned char i_last = chan->i_last; + /* Updates legato/ staccato playing state */ + fluid_channel_update_legato_staccato_state(chan); + if (chan->n_notes) + { + /* keeps trace of the note prior last note */ + chan->prev_note = chan->monolist[i_last].note; + } + /* moves i_last forward before writing new note */ + i_last = chan->monolist[i_last].next; + chan->i_last = i_last; /* now ilast indexes the last note */ + chan->monolist[i_last].note = key; /* we save note and velocity */ + chan->monolist[i_last].vel = vel; + if (onenote) + { /* clears monolist before one note addition */ + chan->i_first = i_last; + chan->n_notes = 0; + } + if(chan->n_notes < FLUID_CHANNEL_SIZE_MONOLIST) + { + chan->n_notes++; /* updates n_notes */ + } + else + { /* The end of buffer is reach. So circular motion for i_first */ + /* i_first index is moved forward */ + chan->i_first = chan->monolist[i_last].next; + } +} + +/** + * Searching a note in the monophonic list. The function is part of the legato + * detector. fluid_channel_search_monolist() is intended to be called by + * fluid_synth_noteoff_mono_LOCAL(). + * + * The search starts from the first note in the list indexed by i_first + + * @param chan fluid_channel_t. + * @param key MIDI note number (0-127) to search. + * @param i_prev pointer on returned index of the note prior the note to search. + * @return index of the note if find, FLUID_FAILED otherwise. + * + */ +int +fluid_channel_search_monolist(fluid_channel_t* chan, unsigned char key , int * i_prev) +{ + short n = chan->n_notes; /* number of notes in monophonic list */ + short j,i= chan->i_first; /* searching starts from i_first included */ + for (j=0 ; j < n ; j++) + { + if(chan->monolist[i].note == key) + { + if (i == chan->i_first) + { /* tracking index of the previous note (i_prev) */ + for (j = chan->i_last ; n < FLUID_CHANNEL_SIZE_MONOLIST; n++) + { + j =chan->monolist[j].next; + } + * i_prev = j; /* returns index of the previous note */ + } + return i; /* returns index of the note to search */ + } + * i_prev = i; /* tracking index of the previous note (i_prev) */ + i = chan->monolist[i].next; /* next element */ + } + return FLUID_FAILED; /* not found */ +} + +/** + * removes a note from the monophonic list. The function is part of + * the legato detector. + * fluid_channel_remove_monolist() is intended to be called by + * fluid_synth_noteoff_mono_LOCAL(). + * + * When a note is removed at noteOff the element concerned is fast unlinked + * and relinked after the i_last element. + * + * @param chan fluid_channel_t. + * @param + * i, index of the note to remove. If i is invalid or the list is + * empty, the function do nothing and returns FLUID_FAILED. + * @param + * On input, i_prev is a pointer on index of the note previous i. + * On output i_prev is a pointer on index of the note previous i if i is the last note + * in the list,FLUID_FAILED otherwise. When the returned index is valid it means + * a legato detection on noteoff. + * + * Note: the following variables in Channel keeps trace of the situation. + * - i_last index keeps a trace of the most recent note played even if + * the list is empty. + * - prev_note keeps a trace of the note removed if it is i_last. + * - FLUID_CHANNEL_LEGATO_PLAYING bit keeps a trace of legato/staccato playing state. + * + * More informations in FluidPolyMono-0004.pdf chapter 4 (Appendices). + */ +void +fluid_channel_remove_monolist(fluid_channel_t* chan, int i, int * i_prev) +{ + unsigned char i_last = chan->i_last; + /* checks if index is valid */ + if( i < 0 || i >= FLUID_CHANNEL_SIZE_MONOLIST || !chan->n_notes) + { + * i_prev = FLUID_FAILED; + } + /* The element is about to be removed and inserted between i_last and next */ + /* Note: when i is egal to i_last or egal to i_first, removing/inserting + isn't necessary */ + if (i == i_last) + { /* Removing/Inserting isn't necessary */ + /* keeps trace of the note prior last note */ + chan->prev_note= chan->monolist[i_last].note; + /* moves i_last backward to the previous */ + chan->i_last = *i_prev; /* i_last index is moved backward */ + } + else + { /* i is before i_last */ + if(i == chan->i_first) + { + /* Removing/inserting isn't necessary */ + /* i_first index is moved forward to the next element*/ + chan->i_first = chan->monolist[i].next; + } + else + { /* i is between i_first and i_last */ + /* Unlinks element i and inserts after i_last */ + chan->monolist[* i_prev].next = chan->monolist[i].next; /* unlinks i */ + /*inserts i after i_last */ + chan->monolist[i].next = chan->monolist[i_last].next; + chan->monolist[i_last].next = i; + } + * i_prev = FLUID_FAILED; + } + chan->n_notes--; /* updates the number of note in the list */ + /* Updates legato/ staccato playing state */ + fluid_channel_update_legato_staccato_state(chan); +} + +/** + * On noteOff on a polyphonic channel,the monophonic list is fully flushed. + * + * @param chan fluid_channel_t. + * Note: i_last index keeps a trace of the most recent note played even if + * the list is empty. + * prev_note keeps a trace of the note i_last . + * FLUID_CHANNEL_LEGATO_PLAYING bit keeps a trace of legato/staccato playing. + */ +void fluid_channel_clear_monolist(fluid_channel_t* chan) +{ + /* keeps trace off the most recent note played */ + chan->prev_note= chan->monolist[chan->i_last].note; + + /* flushes the monolist */ + chan->i_first = chan->monolist[chan->i_last].next; + chan->n_notes = 0; + /* Update legato/ sataccato playing state */ + chan->mode &= ~ FLUID_CHANNEL_LEGATO_PLAYING; /* Staccato state */ +} + +/** + * On noteOn on a polyphonic channel,adds the note into the monophonic list + * keeping only this note. + * @param + * chan fluid_channel_t. + * key, vel, note and velocity added in the monolist + * Note: i_last index keeps a trace of the most recent note inserted. + * prev_note keeps a trace of the note prior i_last note. + * FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing. + */ +void fluid_channel_set_onenote_monolist(fluid_channel_t* chan, unsigned char key, + unsigned char vel) +{ + fluid_channel_add_monolist(chan, key, vel,1); +} + +/** + * The function changes the state (Valid/Invalid) of the previous note played in + * a staccato manner (fluid_channel_prev_note()). + * When potamento mode 'each note' or 'staccato only' is selected, on next + * noteOn a portamento will be started from the most recent note played + * staccato. + * It will be possible that it isn't appropriate. To give the musician the + * possibility to choose a portamento from this note , prev_note will be forced + * to invalid state on noteOff if portamento pedal is Off. + * + * The function is intended to be called when the following event occurs: + * - On noteOff (in poly or mono mode), to mark prev_note invalid. + * - On Portamento Off(in poly or mono mode), to mark prev_note invalid. + * @param chan fluid_channel_t. + */ +void fluid_channel_invalid_prev_note_staccato(fluid_channel_t* chan) +{ + /* checks if the playing is staccato */ + if(!(chan->mode & FLUID_CHANNEL_LEGATO_PLAYING)) + /* checks if portamento pedal is off */ + if(! fluid_channel_portamento(chan)) + { /* forces prev_note invalid */ + fluid_channel_clear_prev_note(chan); + } + /* else prev_note still remains valid for next fromkey portamento */ +} + +/** + * The function handles poly/mono commutation on legato pedal On/Off. + * @param chan fluid_channel_t. + * @param value, value of the CC legato. + */ +void fluid_channel_cc_legato(fluid_channel_t* chan, int value) +{ + /* Special handling of the monophonic list */ + if (!(chan->mode & FLUID_CHANNEL_POLY_OFF) && chan->n_notes) /* The monophonic list have notes */ + { + if (value < 64 ) /* legato is released */ + { /* returns from monophonic to polyphonic with notes in the monophonic list */ + + /* The monophonic list is flushed keeping last note only + Note: i_last index keeps a trace of the most recent note played. + prev_note keeps a trace of the note i_last. + FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing. + */ + chan->i_first = chan->i_last; + chan->n_notes = 1; + } + else /* legato is depressed */ + { /* Inters in monophonic from polyphonic with note in monophonic list */ + /* Stops the running note to remain coherent with Breath Sync mode */ + if ((chan->mode & FLUID_CHANNEL_BREATH_SYNC) && !fluid_channel_breath_msb(chan)) + { + fluid_synth_noteoff_monopoly(chan->synth,chan->channum, + fluid_channel_last_note(chan),1); + } + } + } +} + +/** + * The function handles CC Breath On/Off detection. When a channel is in + * Breath Sync mode and in monophonic playing, the breath controller allows + * to trigger noteon/noteoff note when the musician starts to breath (noteon) and + * stops to breath (noteoff). + * @param chan fluid_channel_t. + * @param value, value of the CC Breath.. + */ +void fluid_channel_cc_breath_note_on_off(fluid_channel_t* chan, int value) +{ + if ((chan->mode & FLUID_CHANNEL_BREATH_SYNC) && fluid_channel_is_playing_mono(chan) && + (chan->n_notes)) + { + /* The monophonic list isn't empty */ + if((value > 0) && (chan->previous_cc_breath == 0)) + { /* CC Breath On detection */ + fluid_synth_noteon_mono_staccato(chan->synth,chan->channum, + fluid_channel_last_note(chan), + fluid_channel_last_vel(chan)); + } + else if( (value == 0) && (chan->previous_cc_breath > 0)) + { /* CC Breath Off detection */ + fluid_synth_noteoff_monopoly(chan->synth, chan->channum, + fluid_channel_last_note(chan), 1); + } + } + chan->previous_cc_breath = value; +} diff --git a/src/synth/fluid_chan.h b/src/synth/fluid_chan.h index 053d0519..bbb6aa1a 100644 --- a/src/synth/fluid_chan.h +++ b/src/synth/fluid_chan.h @@ -25,6 +25,50 @@ #include "fluid_midi.h" #include "fluid_tuning.h" +/* The mononophonic list is part of the legato detector for monophonic mode */ +/* see fluid_synth_monopoly.c about a description of the legato detector device */ +/* Size of the monophonic list + - 1 is the minimum. it allows playing legato passage of any number + of notes on noteon only. + - Size above 1 allows playing legato on noteon but also on noteOff. + This allows the musician to play fast trills. + This feature is particularly usful when the MIDI input device is a keyboard. + Choosing a size of 10 is sufficient (because most musicians have only 10 + fingers when playing a monophonic instrument). +*/ +#define FLUID_CHANNEL_SIZE_MONOLIST 10 + +/* + + The monophonic list + +------------------------------------------------+ + | +----+ +----+ +----+ +----+ | + | |note| |note| |note| |note| | + +--->|vel |-->|vel |-->....-->|vel |-->|vel |----+ + +----+ +----+ +----+ +----+ + /|\ /|\ + | | + i_first i_last + + The monophonic list is a circular buffer of FLUID_CHANNEL_SIZE_MONOLIST elements. + Each element is linked forward at initialisation time. + - when a note is added at noteOn (see fluid_channel_add_monolist()) each + element is use in the forward direction and indexed by i_last variable. + - when a note is removed at noteOff (see fluid_channel_remove_monolist()), + the element concerned is fast unlinked and relinked after the i_last element. + + The most recent note added is indexed by i_last. + The most ancient note added is the first note indexed by i_first. i_first is + moving in the forward direction in a circular manner. + +*/ +struct mononote +{ + unsigned char next; /* next note */ + unsigned char note; /* note */ + unsigned char vel; /* velocity */ +}; + /* * fluid_channel_t * @@ -35,7 +79,23 @@ struct _fluid_channel_t { fluid_synth_t* synth; /**< Parent synthesizer instance */ int channum; /**< MIDI channel number */ - + + /* Poly Mono variables see macro access description */ + int mode; /**< Poly Mono mode */ + int mode_val; /**< number of channel in basic channel group */ + /* monophonic list - legato detector */ + struct mononote monolist[FLUID_CHANNEL_SIZE_MONOLIST]; /**< monophonic list */ + unsigned char i_first; /**< First note index */ + unsigned char i_last; /**< most recent note index since the most recent add */ + unsigned char prev_note; /**< previous note of the most recent add/remove */ + unsigned char n_notes; /**< actual number of notes in the list */ + /*--*/ + int key_mono_sustained; /**< previous sustained monophonic note */ + enum fluid_channel_legato_mode legatomode; /**< legato mode */ + enum fluid_channel_portamento_mode portamentomode; /**< portamento mode */ + int previous_cc_breath; /**< Previous Breath */ + /*- End of Poly/mono variables description */ + int sfont_bank_prog; /**< SoundFont ID (bit 21-31), bank (bit 7-20), program (bit 0-6) */ fluid_preset_t* preset; /**< Selected preset */ @@ -136,6 +196,12 @@ int fluid_channel_get_interp_method(fluid_channel_t* chan); ((chan)->tuning_prog) #define fluid_channel_set_tuning_prog(chan, prog) \ ((chan)->tuning_prog = (prog)) +#define fluid_channel_portamentotime(_c) \ + ((_c)->cc[PORTAMENTO_TIME_MSB] * 128 + (_c)->cc[PORTAMENTO_TIME_LSB]) +#define fluid_channel_portamento(_c) ((_c)->cc[PORTAMENTO_SWITCH] >= 64) +#define fluid_channel_breath_msb(_c) ((_c)->cc[BREATH_MSB] > 0) +#define fluid_channel_clear_portamento(_c) ((_c)->cc[PORTAMENTO_CTRL] = INVALID_NOTE) +#define fluid_channel_legato(_c) ((_c)->cc[LEGATO_SWITCH] >= 64) #define fluid_channel_sustained(_c) ((_c)->cc[SUSTAIN_SWITCH] >= 64) #define fluid_channel_sostenuto(_c) ((_c)->cc[SOSTENUTO_SWITCH] >= 64) #define fluid_channel_set_gen(_c, _n, _v, _a) { (_c)->gen[_n] = _v; (_c)->gen_abs[_n] = _a; } @@ -144,4 +210,83 @@ int fluid_channel_get_interp_method(fluid_channel_t* chan); #define fluid_channel_get_min_note_length_ticks(chan) \ ((chan)->synth->min_note_length_ticks) +/* Macros interface to poly/mono mode variables */ +#define MASK_BASICCHANINFOS (FLUID_CHANNEL_MODE_MASK|FLUID_CHANNEL_BASIC|FLUID_CHANNEL_ENABLED) +/* Set the basic channel infos for a MIDI basic channel */ +#define fluid_channel_set_basic_channel_info(chan,Infos) \ + (chan->mode = (chan->mode & ~MASK_BASICCHANINFOS) | (Infos & MASK_BASICCHANINFOS)) +/* Reset the basic channel infos for a MIDI basic channel */ +#define fluid_channel_reset_basic_channel_info(chan) (chan->mode &= ~MASK_BASICCHANINFOS) + +/* Macros interface to breath variables */ +#define FLUID_CHANNEL_BREATH_MASK (FLUID_CHANNEL_BREATH_POLY|FLUID_CHANNEL_BREATH_MONO|FLUID_CHANNEL_BREATH_SYNC) +/* Set the breath infos for a MIDI channel */ +#define fluid_channel_set_breath_info(chan,BreathInfos) \ +(chan->mode = (chan->mode & ~FLUID_CHANNEL_BREATH_MASK) | (BreathInfos & FLUID_CHANNEL_BREATH_MASK)) +/* Get the breath infos for a MIDI channel */ +#define fluid_channel_get_breath_info(chan) (chan->mode & FLUID_CHANNEL_BREATH_MASK) + +/* Returns true when channel is mono or legato is on */ +#define fluid_channel_is_playing_mono(chan) ((chan->mode & FLUID_CHANNEL_POLY_OFF) ||\ + fluid_channel_legato(chan)) + +/* Macros interface to monophonic list variables */ +#define INVALID_NOTE 255 +/* Returns true when a note is a valid note */ +#define fluid_channel_is_valid_note(n) (n != INVALID_NOTE) +/* Marks prev_note as invalid. */ +#define fluid_channel_clear_prev_note(chan) (chan->prev_note = INVALID_NOTE) + +/* Returns the most recent note from i_last entry of the monophonic list */ +#define fluid_channel_last_note(chan) (chan->monolist[chan->i_last].note) + +/* Returns the most recent velocity from i_last entry of the monophonic list */ +#define fluid_channel_last_vel(chan) (chan->monolist[chan->i_last].vel) + +/* + prev_note is used to determine fromkey_portamento as well as + fromkey_legato (see fluid_synth_get_fromkey_portamento_legato()). + + prev_note is updated on noteOn/noteOff mono by the legato detector as this: + - On noteOn mono, before adding a new note into the monolist,the most + recent note in the list (i.e at i_last position) is kept in prev_note. + - Similarly, on noteOff mono , before removing a note out of the monolist, + the most recent note (i.e those at i_last position) is kept in prev_note. +*/ +#define fluid_channel_prev_note(chan) (chan->prev_note) + +/* Interface to poly/mono mode variables */ +enum fluid_channel_mode_flags_internal +{ + FLUID_CHANNEL_BASIC = 0x04, /**< if flag set the corresponding midi channel is a basic channel */ + FLUID_CHANNEL_ENABLED = 0x08, /**< if flag set the corresponding midi channel is enabled, else disabled, i.e. channel ignores any MIDI messages */ + +/* + FLUID_CHANNEL_LEGATO_PLAYING bit of channel mode keeps trace of the legato /staccato + state playing. + FLUID_CHANNEL_LEGATO_PLAYING bit is updated on noteOn/noteOff mono by the legato detector: + - On noteOn, before inserting a new note into the monolist. + - On noteOff, after removing a note out of the monolist. + + - On noteOn, this state is used by fluid_synth_noteon_mono_LOCAL() + to play the current note legato or staccato. + - On noteOff, this state is used by fluid_synth_noteoff_mono_LOCAL() + to play the current noteOff legato with the most recent note. +*/ +/* bit7, 1: means legato playing , 0: means staccato playing */ + FLUID_CHANNEL_LEGATO_PLAYING = 0x80 +}; + +/* End of interface to monophonic list variables */ + +void fluid_channel_add_monolist(fluid_channel_t* chan, unsigned char key, unsigned char vel, unsigned char onenote); +int fluid_channel_search_monolist(fluid_channel_t* chan, unsigned char key , int * i_prev); +void fluid_channel_remove_monolist(fluid_channel_t* chan, int i, int * i_prev); +void fluid_channel_clear_monolist(fluid_channel_t* chan); +void fluid_channel_set_onenote_monolist(fluid_channel_t* chan, unsigned char key, unsigned char vel); +void fluid_channel_invalid_prev_note_staccato(fluid_channel_t* chan); +void fluid_channel_cc_legato(fluid_channel_t* chan, int value); +void fluid_channel_cc_breath_note_on_off(fluid_channel_t* chan, int value); + + #endif /* _FLUID_CHAN_H */ diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index f437d7be..88314b7c 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -35,7 +35,28 @@ extern int feenableexcept (int excepts); #endif +#define FLUID_API_RETURN(return_value) \ + do { fluid_synth_api_exit(synth); \ + return return_value; } while (0) + +#define FLUID_API_RETURN_IF_CHAN_DISABLED(return_value) \ + do { if (FLUID_LIKELY(synth->channel[chan]->mode & FLUID_CHANNEL_ENABLED)) \ + {} \ + else \ + { FLUID_API_RETURN(return_value); } \ + } while (0) + +#define FLUID_API_ENTRY_CHAN(fail_value) \ + fluid_return_val_if_fail (synth != NULL, fail_value); \ + fluid_return_val_if_fail (chan >= 0, fail_value); \ + fluid_synth_api_enter(synth); \ + if (chan >= synth->midi_channels) { \ + FLUID_API_RETURN(fail_value); \ + } \ + static void fluid_synth_init(void); +static void fluid_synth_api_enter(fluid_synth_t* synth); +static void fluid_synth_api_exit(fluid_synth_t* synth); static int fluid_synth_noteon_LOCAL(fluid_synth_t* synth, int chan, int key, int vel); @@ -45,7 +66,7 @@ static int fluid_synth_sysex_midi_tuning (fluid_synth_t *synth, const char *data int len, char *response, int *response_len, int avail_response, int *handled, int dryrun); -static int fluid_synth_all_notes_off_LOCAL(fluid_synth_t* synth, int chan); +int fluid_synth_all_notes_off_LOCAL(fluid_synth_t* synth, int chan); static int fluid_synth_all_sounds_off_LOCAL(fluid_synth_t* synth, int chan); static int fluid_synth_system_reset_LOCAL(fluid_synth_t* synth); static int fluid_synth_modulate_voices_LOCAL(fluid_synth_t* synth, int chan, @@ -77,7 +98,7 @@ static void fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t* synth, static fluid_sfont_info_t *new_fluid_sfont_info (fluid_synth_t *synth, fluid_sfont_t *sfont); static int fluid_synth_sfunload_callback(void* data, unsigned int msec); -static void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth, +void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth, int chan, int key); static fluid_tuning_t* fluid_synth_get_tuning(fluid_synth_t* synth, int bank, int prog); @@ -110,6 +131,10 @@ static void fluid_synth_handle_important_channels(void *data, const char *name, const char *value); +static void fluid_synth_reset_basic_channel_LOCAL(fluid_synth_t* synth, int chan, int nbr_chan); +static int fluid_synth_check_next_basic_channel(fluid_synth_t* synth, int basicchan, int mode, int val); +static void fluid_synth_set_basic_channel_LOCAL(fluid_synth_t* synth, int basicchan, int mode, int val); + /*************************************************************** * * GLOBAL @@ -141,6 +166,12 @@ static fluid_mod_t default_pitch_bend_mod; /* SF2.01 section 8.4.10 */ static fluid_mod_t custom_balance_mod; /* Non-standard modulator */ +/* custom_breath2att_modulator is not a default modulator specified in SF +it is intended to replace default_vel2att_mod on demand using +API fluid_set_breath_mode() or command shell setbreathmode. +*/ +static fluid_mod_t custom_breath2att_mod; + /* reverb presets */ static const fluid_revmodel_presets_t revmodel_preset[] = { /* name */ /* roomsize */ /* damp */ /* width */ /* level */ @@ -169,7 +200,7 @@ void fluid_synth_settings(fluid_settings_t* settings) #ifdef DEFAULT_SOUNDFONT fluid_settings_register_str(settings, "synth.default-soundfont", DEFAULT_SOUNDFONT, 0); #endif - + fluid_settings_register_int(settings, "synth.polyphony", 256, 1, 65535, 0); fluid_settings_register_int(settings, "synth.midi-channels", 16, 16, 256, 0); fluid_settings_register_num(settings, "synth.gain", 0.2f, 0.0f, 10.0f, 0); @@ -224,19 +255,6 @@ fluid_version_str (void) return FLUIDSYNTH_VERSION; } -#define FLUID_API_ENTRY_CHAN(fail_value) \ - fluid_return_val_if_fail (synth != NULL, fail_value); \ - fluid_return_val_if_fail (chan >= 0, fail_value); \ - fluid_synth_api_enter(synth); \ - if (chan >= synth->midi_channels) { \ - fluid_synth_api_exit(synth); \ - return fail_value; \ - } \ - -#define FLUID_API_RETURN(return_value) \ - do { fluid_synth_api_exit(synth); \ - return return_value; } while (0) - /* * void fluid_synth_init * @@ -258,6 +276,20 @@ fluid_synth_init(void) init_dither(); + /* custom_breath2att_mod is not a default modulator specified in SF2.01. + it is intended to replace default_vel2att_mod on demand using + API fluid_set_breath_mode() or command shell setbreathmode. + */ + fluid_mod_set_source1(&custom_breath2att_mod, /* The modulator we are programming here */ + BREATH_MSB, /* Source. breath MSB corresponds to 2. */ + FLUID_MOD_CC /* MIDI continuous controller */ + | FLUID_MOD_CONCAVE /* Curve shape. Corresponds to 'type=1' */ + | FLUID_MOD_UNIPOLAR /* Polarity. Corresponds to 'P=0' */ + | FLUID_MOD_NEGATIVE /* Direction. Corresponds to 'D=1' */ + ); + fluid_mod_set_source2(&custom_breath2att_mod, 0, 0); /* No 2nd source */ + fluid_mod_set_dest(&custom_breath2att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */ + fluid_mod_set_amount(&custom_breath2att_mod, 960.0); /* Modulation amount: 960 */ /* SF2.01 page 53 section 8.4.1: MIDI Note-On Velocity to Initial Attenuation */ fluid_mod_set_source1(&default_vel2att_mod, /* The modulator we are programming here */ @@ -518,7 +550,6 @@ fluid_synth_update_mixer(fluid_synth_t* synth, void* method, int intparam, intparam, realparam); } - /** * Create new FluidSynth instance. * @param settings Configuration parameters to use (used directly). @@ -569,7 +600,7 @@ new_fluid_synth(fluid_settings_t *settings) fluid_settings_getnum_float(settings, "synth.gain", &synth->gain); fluid_settings_getint(settings, "synth.device-id", &synth->device_id); fluid_settings_getint(settings, "synth.cpu-cores", &synth->cores); - + fluid_settings_getnum_float(settings, "synth.overflow.percussion", &synth->overflow.percussion); fluid_settings_getnum_float(settings, "synth.overflow.released", &synth->overflow.released); fluid_settings_getnum_float(settings, "synth.overflow.sustained", &synth->overflow.sustained); @@ -660,6 +691,9 @@ new_fluid_synth(fluid_settings_t *settings) synth->sfont_info = NULL; synth->sfont_hash = new_fluid_hashtable (NULL, NULL); synth->noteid = 0; + + synth->fromkey_portamento = INVALID_NOTE; /* disable portamento */ + fluid_atomic_int_set(&synth->ticks_since_start, 0); synth->tuning = NULL; fluid_private_init(synth->tuning_iter); @@ -739,7 +773,14 @@ new_fluid_synth(fluid_settings_t *settings) goto error_recovery; } } - + + /* sets a default basic channel */ + /* Sets one basic channel: basic channel 0, mode 0 (Omni On - Poly) */ + /* (i.e all channels are polyphonic) */ + /* Must be called after channel objects allocation */ + fluid_synth_set_basic_channel_LOCAL(synth, 0, FLUID_CHANNEL_MODE_OMNION_POLY, + synth->midi_channels); + fluid_synth_set_sample_rate(synth, synth->sample_rate); fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_polyphony, synth->polyphony, 0.0f); @@ -940,7 +981,7 @@ fluid_synth_error(fluid_synth_t* synth) * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param key MIDI note number (0-127) * @param vel MIDI velocity (0-127, 0=noteoff) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel) @@ -949,7 +990,10 @@ fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel) fluid_return_val_if_fail (key >= 0 && key <= 127, FLUID_FAILED); fluid_return_val_if_fail (vel >= 0 && vel <= 127, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); - + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + result = fluid_synth_noteon_LOCAL (synth, chan, key, vel); FLUID_API_RETURN(result); } @@ -958,16 +1002,17 @@ fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel) static int fluid_synth_noteon_LOCAL(fluid_synth_t* synth, int chan, int key, int vel) { - fluid_channel_t* channel; - + fluid_channel_t* channel ; /* notes with velocity zero go to noteoff */ if (vel == 0) return fluid_synth_noteoff_LOCAL(synth, chan, key); channel = synth->channel[chan]; - - /* make sure this channel has a preset */ - if (channel->preset == NULL) { - if (synth->verbose) { + + /* makes sure this channel has a preset */ + if (channel->preset == NULL) + { + if (synth->verbose) + { FLUID_LOG(FLUID_INFO, "noteon\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d\t%s", chan, key, vel, 0, fluid_synth_get_ticks(synth) / 44100.0f, @@ -976,21 +1021,43 @@ fluid_synth_noteon_LOCAL(fluid_synth_t* synth, int chan, int key, int vel) } return FLUID_FAILED; } + + if(fluid_channel_is_playing_mono(channel)) /* channel is mono or legato CC is On) */ + { /* play the noteOn in monophonic */ + return fluid_synth_noteon_mono_LOCAL(synth, chan, key, vel); + } + else + { /* channel is poly and legato CC is Off) */ - /* If there is another voice process on the same channel and key, - advance it to the release phase. */ - fluid_synth_release_voice_on_same_note_LOCAL(synth, chan, key); + /* plays the noteOn in polyphonic */ + /* Sets the note at first position in monophonic list */ + /* In the case where the musician intends to inter the channel in monophonic + (by depressing the CC legato on), the next noteOn mono could be played legato + with the previous note poly (if the musician choose this). + */ + fluid_channel_set_onenote_monolist(channel, (unsigned char) key, + (unsigned char) vel); - - return fluid_preset_noteon(channel->preset, synth, chan, key, vel); + /* If there is another voice process on the same channel and key, + advance it to the release phase. */ + fluid_synth_release_voice_on_same_note_LOCAL(synth, chan, key); + + /* a noteon poly is passed to fluid_synth_noteon_monopoly_legato(). + This allows an opportunity to get this note played legato with a previous + note if a CC PTC have been received before this noteon. This behavior is + a MIDI specification (see FluidPolymono-0004.pdf chapter 4.3-a ,3.4.11 + for details). + */ + return fluid_synth_noteon_monopoly_legato(synth, chan, INVALID_NOTE, key, vel); + } } /** - * Send a note-off event to a FluidSynth object. + * Sends a note-off event to a FluidSynth object. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param key MIDI note number (0-127) - * @return FLUID_OK on success, FLUID_FAILED otherwise (may just mean that no + * @return #FLUID_OK on success, #FLUID_FAILED otherwise (may just mean that no * voices matched the note off event) */ int @@ -999,9 +1066,11 @@ fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key) int result; fluid_return_val_if_fail (key >= 0 && key <= 127, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); - - result = fluid_synth_noteoff_LOCAL (synth, chan, key); + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + result = fluid_synth_noteoff_LOCAL (synth, chan, key); FLUID_API_RETURN(result); } @@ -1009,67 +1078,79 @@ fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key) static int fluid_synth_noteoff_LOCAL(fluid_synth_t* synth, int chan, int key) { - fluid_voice_t* voice; - int status = FLUID_FAILED; - int i; - - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; - if (fluid_voice_is_on(voice) && (fluid_voice_get_channel(voice) == chan) && (fluid_voice_get_key(voice) == key)) { - if (synth->verbose) { - int used_voices = 0; - int k; - for (k = 0; k < synth->polyphony; k++) { - if (!_AVAILABLE(synth->voice[k])) { - used_voices++; - } - } - FLUID_LOG(FLUID_INFO, "noteoff\t%d\t%d\t%d\t%05d\t%.3f\t%d", - fluid_voice_get_channel(voice), fluid_voice_get_key(voice), 0, fluid_voice_get_id(voice), - (fluid_curtime() - synth->start) / 1000.0f, - used_voices); - } /* if verbose */ - - fluid_voice_noteoff(voice); - status = FLUID_OK; - } /* if voice on */ - } /* for all voices */ + int status; + fluid_channel_t* channel = synth->channel[chan]; + if(fluid_channel_is_playing_mono(channel)) /* channel is mono or legato CC is On) */ + { /* play the noteOff in monophonic */ + status = fluid_synth_noteoff_mono_LOCAL(synth, chan, key); + } + else + { /* channel is poly and legato CC is Off) */ + /* removes the note from the monophonic list */ + if(key == fluid_channel_last_note(channel)) + { + fluid_channel_clear_monolist(channel); + } + status = fluid_synth_noteoff_monopoly(synth, chan, key, 0); + } + /* Changes the state (Valid/Invalid) of the most recent note played in a + staccato manner */ + fluid_channel_invalid_prev_note_staccato(channel); return status; } -/* Damp voices on a channel (turn notes off), if they're sustained by +/* Damps voices on a channel (turn notes off), if they're sustained by sustain pedal */ static int fluid_synth_damp_voices_by_sustain_LOCAL(fluid_synth_t* synth, int chan) { + fluid_channel_t* channel = synth->channel[chan]; fluid_voice_t* voice; int i; - for (i = 0; i < synth->polyphony; i++) { + for (i = 0; i < synth->polyphony; i++) + { voice = synth->voice[i]; - - if ((fluid_voice_get_channel(voice) == chan) && fluid_voice_is_sustained(voice)) - fluid_voice_release(voice); + if ((fluid_voice_get_channel(voice) == chan) && fluid_voice_is_sustained(voice)) + { + if(voice->key == channel->key_mono_sustained) + { + /* key_mono_sustained is a possible mono note sustainted + (by sustain or sostenuto pedal). It must be marked released + (INVALID_NOTE) here because it is released only by sustain pedal */ + channel->key_mono_sustained = INVALID_NOTE; + } + fluid_voice_release(voice); + } } - return FLUID_OK; } -/* Damp voices on a channel (turn notes off), if they're sustained by +/* Damps voices on a channel (turn notes off), if they're sustained by sostenuto pedal */ static int fluid_synth_damp_voices_by_sostenuto_LOCAL(fluid_synth_t* synth, int chan) { + fluid_channel_t* channel = synth->channel[chan]; fluid_voice_t* voice; int i; - for (i = 0; i < synth->polyphony; i++) { + for (i = 0; i < synth->polyphony; i++) + { voice = synth->voice[i]; - if ((fluid_voice_get_channel(voice) == chan) && fluid_voice_is_sostenuto(voice)) - fluid_voice_release(voice); + if ((fluid_voice_get_channel(voice) == chan) && fluid_voice_is_sostenuto(voice)) + { + if(voice->key == channel->key_mono_sustained) + { + /* key_mono_sustained is a possible mono note sustainted + (by sustain or sostenuto pedal). It must be marked released + (INVALID_NOTE) here because it is released only by sostenuto pedal */ + channel->key_mono_sustained = INVALID_NOTE; + } + fluid_voice_release(voice); + } } - return FLUID_OK; } @@ -1080,7 +1161,7 @@ fluid_synth_damp_voices_by_sostenuto_LOCAL(fluid_synth_t* synth, int chan) * @param synth FluidSynth instance * @param mod Modulator info (values copied, passed in object can be freed again immediately) * @param mode Determines how to handle an existing identical modulator (#fluid_synth_add_mod) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_add_default_mod(fluid_synth_t* synth, fluid_mod_t* mod, int mode) @@ -1130,7 +1211,7 @@ fluid_synth_add_default_mod(fluid_synth_t* synth, fluid_mod_t* mod, int mode) * fluid_mod_test_identity() will be used to test modulator matching. * @param synth synth instance * @param mod The modulator to remove - * @return FLUID_OK if a matching modulator was found and successfully removed, FLUID_FAILED otherwise + * @return #FLUID_OK if a matching modulator was found and successfully removed, #FLUID_FAILED otherwise */ int fluid_synth_remove_default_mod(fluid_synth_t* synth, const fluid_mod_t* mod) @@ -1172,21 +1253,57 @@ fluid_synth_remove_default_mod(fluid_synth_t* synth, const fluid_mod_t* mod) * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param num MIDI controller number (0-127) * @param val MIDI controller value (0-127) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @note This function supports MIDI Global Controllers which will be sent to + * all channels of the basic channel if this basic channel is in mode OmniOff/Mono. + * This is accomplished by sending the CC one MIDI channel below the basic + * channel of the receiver. + * Examples: let a synthesizer with 16 MIDI channels: + * - Let a basic channel 7 in mode 3 (Omni Off, Mono). If MIDI channel 6 is disabled it + * could be used as CC global for all channels belonging to basic channel 7. + * - Let a basic channel 0 in mode 3. If MIDI channel 15 is disabled it could be used + * as CC global for all channels belonging to basic channel 0. */ int fluid_synth_cc(fluid_synth_t* synth, int chan, int num, int val) { - int result; + int result = FLUID_FAILED; + fluid_channel_t* channel; fluid_return_val_if_fail (num >= 0 && num <= 127, FLUID_FAILED); fluid_return_val_if_fail (val >= 0 && val <= 127, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); - if (synth->verbose) - FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", chan, num, val); - - fluid_channel_set_cc (synth->channel[chan], num, val); - result = fluid_synth_cc_LOCAL (synth, chan, num); + channel = synth->channel[chan]; + if(channel->mode & FLUID_CHANNEL_ENABLED) + { /* chan is enabled */ + if (synth->verbose) + FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", chan, num, val); + fluid_channel_set_cc (channel, num, val); + result = fluid_synth_cc_LOCAL (synth, chan, num); + } + else /* chan is disabled so it is a candidate for global channel */ + { /* looks for next basic channel */ + int n_chan = synth->midi_channels; /* MIDI Channels number */ + int basicchan ; + if (chan < n_chan-1) basicchan = chan + 1; /* next channel */ + else basicchan = 0; /* wrap to 0 */ + channel = synth->channel[basicchan]; + /* Channel must be a basicchan in mode OMNIOFF_MONO */ + if ((channel->mode & FLUID_CHANNEL_BASIC) && + ((channel->mode & FLUID_CHANNEL_MODE_MASK) == FLUID_CHANNEL_MODE_OMNIOFF_MONO)) + { /* sends cc to all channels in this basic channel */ + int i,nbr = channel->mode_val; + for (i = basicchan; i < basicchan+nbr; i++) + { + if (synth->verbose) + FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", i, num, val); + fluid_channel_set_cc (synth->channel[i], num, val); + result = fluid_synth_cc_LOCAL (synth, i, num); + } + } + /* The channel chan is not a valid 'global channel' */ + else result = FLUID_FAILED; + } FLUID_API_RETURN(result); } @@ -1201,6 +1318,68 @@ fluid_synth_cc_LOCAL (fluid_synth_t* synth, int channum, int num) value = fluid_channel_get_cc (chan, num); switch (num) { + + /* CC omnioff, omnion, mono, poly */ + case POLY_OFF: + case POLY_ON: + case OMNI_OFF: + case OMNI_ON: + /* allowed only if channum is a basic channel */ + if (chan->mode & FLUID_CHANNEL_BASIC) + { + /* Construction of new_mode from current channel mode and this CC mode */ + int new_mode = chan->mode & FLUID_CHANNEL_MODE_MASK; + switch(num) + { + case POLY_OFF: + new_mode |= FLUID_CHANNEL_POLY_OFF; + break; + + case POLY_ON: + new_mode &= ~FLUID_CHANNEL_POLY_OFF; + break; + + case OMNI_OFF: + new_mode |= FLUID_CHANNEL_OMNI_OFF; + break; + + case OMNI_ON: + new_mode &= ~FLUID_CHANNEL_OMNI_OFF; + break; + + default: /* should never happen */ + return FLUID_FAILED; + } + /* MIDI specs: if value is 0 it means all channels from channum to next + basic channel minus 1 (if any) or to MIDI channel count minus 1. + However, if value is > 0 (e.g. 4), the group of channels will be be + limited to 4. + value is ignored for #FLUID_CHANNEL_MODE_OMNIOFF_POLY as this mode + implies a group of only one channel. + */ + /* Checks value range and changes this existing basic channel group */ + value = fluid_synth_check_next_basic_channel(synth, channum, new_mode, value); + if( value != FLUID_FAILED ) + { + /* reset the current basic channel before changing it */ + fluid_synth_reset_basic_channel_LOCAL(synth, channum, chan->mode_val); + fluid_synth_set_basic_channel_LOCAL(synth, channum, new_mode, value); + break; /* FLUID_OK */ + } + } + return FLUID_FAILED; + + case LEGATO_SWITCH: + /* handles Poly/mono commutation on Legato pedal On/Off.*/ + fluid_channel_cc_legato(chan,value); + break; + + case PORTAMENTO_SWITCH: + /* Special handling of the monophonic list */ + /* Invalids the most recent note played in a staccato manner */ + fluid_channel_invalid_prev_note_staccato(chan); + break; + case SUSTAIN_SWITCH: /* Release voices if Sustain switch is released */ if (value < 64) /* Sustain is released */ @@ -1309,6 +1488,11 @@ fluid_synth_cc_LOCAL (fluid_synth_t* synth, int channum, int num) case RPN_LSB: chan->nrpn_active = 0; break; + + case BREATH_MSB: + /* handles CC Breath On/Off noteOn/noteOff mode */ + fluid_channel_cc_breath_note_on_off(chan, value); + /* fall-through */ default: return fluid_synth_modulate_voices_LOCAL (synth, channum, 1, num); } @@ -1322,7 +1506,7 @@ fluid_synth_cc_LOCAL (fluid_synth_t* synth, int channum, int num) * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param num MIDI controller number (0-127) * @param pval Location to store MIDI controller value (0-127) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_get_cc(fluid_synth_t* synth, int chan, int num, int* pval) @@ -1332,6 +1516,9 @@ fluid_synth_get_cc(fluid_synth_t* synth, int chan, int num, int* pval) FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + *pval = fluid_channel_get_cc (synth->channel[chan], num); FLUID_API_RETURN(FLUID_OK); } @@ -1363,7 +1550,7 @@ fluid_synth_handle_device_id (void *data, const char *name, int value) * recognized and handled or not (set to TRUE if it was handled) * @param dryrun TRUE to just do a dry run but not actually execute the SYSEX * command (useful for checking if a SYSEX message would be handled) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.0 */ /* SYSEX format (0xF0 and 0xF7 not passed to this function): @@ -1629,7 +1816,7 @@ fluid_synth_sysex_midi_tuning (fluid_synth_t *synth, const char *data, int len, * Turn off all notes on a MIDI channel (put them into release phase). * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1), (chan=-1 selects all channels) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.4 */ int @@ -1643,12 +1830,17 @@ fluid_synth_all_notes_off(fluid_synth_t* synth, int chan) if (chan >= synth->midi_channels) result = FLUID_FAILED; else + { + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); result = fluid_synth_all_notes_off_LOCAL (synth, chan); + } FLUID_API_RETURN(result); } /* Local synthesis thread variant of all notes off, (chan=-1 selects all channels) */ -static int +//static int +int fluid_synth_all_notes_off_LOCAL(fluid_synth_t* synth, int chan) { fluid_voice_t* voice; @@ -1667,7 +1859,7 @@ fluid_synth_all_notes_off_LOCAL(fluid_synth_t* synth, int chan) * Immediately stop all notes on a MIDI channel (skips release phase). * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1), (chan=-1 selects all channels) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.4 */ int @@ -1681,7 +1873,11 @@ fluid_synth_all_sounds_off(fluid_synth_t* synth, int chan) if (chan >= synth->midi_channels) result = FLUID_FAILED; else + { + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); result = fluid_synth_all_sounds_off_LOCAL (synth, chan); + } FLUID_API_RETURN(result); } @@ -1704,7 +1900,7 @@ fluid_synth_all_sounds_off_LOCAL(fluid_synth_t* synth, int chan) /** * Reset reverb engine * @param synth FluidSynth instance - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_reset_reverb(fluid_synth_t* synth) @@ -1718,7 +1914,7 @@ fluid_synth_reset_reverb(fluid_synth_t* synth) /** * Reset chorus engine * @param synth FluidSynth instance - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_reset_chorus(fluid_synth_t* synth) @@ -1731,10 +1927,10 @@ fluid_synth_reset_chorus(fluid_synth_t* synth) /** - * Send MIDI system reset command (big red 'panic' button), turns off notes and - * resets controllers. + * Send MIDI system reset command (big red 'panic' button), turns off notes, resets + * controllers and restores initial basic channel configuration. * @param synth FluidSynth instance - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_system_reset(fluid_synth_t* synth) @@ -1757,6 +1953,10 @@ fluid_synth_system_reset_LOCAL(fluid_synth_t* synth) for (i = 0; i < synth->midi_channels; i++) fluid_channel_reset(synth->channel[i]); + /* Basic channel 0, Mode Omni On Poly */ + fluid_synth_set_basic_channel(synth, 0, FLUID_CHANNEL_MODE_OMNION_POLY, + synth->midi_channels); + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_fx, 0, 0.0f); return FLUID_OK; @@ -1768,7 +1968,7 @@ fluid_synth_system_reset_LOCAL(fluid_synth_t* synth) * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param is_cc Boolean value indicating if ctrl is a CC controller or not * @param ctrl MIDI controller value - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ static int fluid_synth_modulate_voices_LOCAL(fluid_synth_t* synth, int chan, int is_cc, int ctrl) @@ -1789,7 +1989,7 @@ fluid_synth_modulate_voices_LOCAL(fluid_synth_t* synth, int chan, int is_cc, int * Update voices on a MIDI channel after all MIDI controllers have been changed. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ static int fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t* synth, int chan) @@ -1811,7 +2011,7 @@ fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t* synth, int chan) * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param val MIDI channel pressure value (0-127) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_channel_pressure(fluid_synth_t* synth, int chan, int val) @@ -1821,12 +2021,15 @@ fluid_synth_channel_pressure(fluid_synth_t* synth, int chan, int val) FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + if (synth->verbose) FLUID_LOG(FLUID_INFO, "channelpressure\t%d\t%d", chan, val); fluid_channel_set_channel_pressure (synth->channel[chan], val); - result = fluid_synth_update_channel_pressure_LOCAL (synth, chan); + FLUID_API_RETURN(result); } @@ -1843,7 +2046,7 @@ fluid_synth_update_channel_pressure_LOCAL(fluid_synth_t* synth, int chan) * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param key MIDI key number (0-127) * @param val MIDI key pressure value (0-127) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 2.0.0 */ int @@ -1854,13 +2057,16 @@ fluid_synth_key_pressure(fluid_synth_t* synth, int chan, int key, int val) fluid_return_val_if_fail (val >= 0 && val <= 127, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); - + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + if (synth->verbose) FLUID_LOG(FLUID_INFO, "keypressure\t%d\t%d\t%d", chan, key, val); fluid_channel_set_key_pressure (synth->channel[chan], key, val); - result = fluid_synth_update_key_pressure_LOCAL (synth, chan, key); + FLUID_API_RETURN(result); } @@ -1889,7 +2095,7 @@ fluid_synth_update_key_pressure_LOCAL(fluid_synth_t* synth, int chan, int key) * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param val MIDI pitch bend value (0-16383 with 8192 being center) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_pitch_bend(fluid_synth_t* synth, int chan, int val) @@ -1898,12 +2104,15 @@ fluid_synth_pitch_bend(fluid_synth_t* synth, int chan, int val) fluid_return_val_if_fail (val >= 0 && val <= 16383, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + if (synth->verbose) FLUID_LOG(FLUID_INFO, "pitchb\t%d\t%d", chan, val); fluid_channel_set_pitch_bend (synth->channel[chan], val); - result = fluid_synth_update_pitch_bend_LOCAL (synth, chan); + FLUID_API_RETURN(result); } @@ -1920,16 +2129,22 @@ fluid_synth_update_pitch_bend_LOCAL(fluid_synth_t* synth, int chan) * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param ppitch_bend Location to store MIDI pitch bend value (0-16383 with * 8192 being center) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend) { + int result; fluid_return_val_if_fail (ppitch_bend != NULL, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); - *ppitch_bend = fluid_channel_get_pitch_bend (synth->channel[chan]); - FLUID_API_RETURN(FLUID_OK); + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + *ppitch_bend = fluid_channel_get_pitch_bend (synth->channel[chan]); + result = FLUID_OK; + + FLUID_API_RETURN(result); } /** @@ -1937,7 +2152,7 @@ fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend) * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param val Pitch wheel sensitivity value in semitones - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_pitch_wheel_sens(fluid_synth_t* synth, int chan, int val) @@ -1946,12 +2161,15 @@ fluid_synth_pitch_wheel_sens(fluid_synth_t* synth, int chan, int val) fluid_return_val_if_fail (val >= 0 && val <= 72, FLUID_FAILED); /* 6 octaves!? Better than no limit.. */ FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + if (synth->verbose) FLUID_LOG(FLUID_INFO, "pitchsens\t%d\t%d", chan, val); fluid_channel_set_pitch_wheel_sensitivity (synth->channel[chan], val); - result = fluid_synth_update_pitch_wheel_sens_LOCAL (synth, chan); + FLUID_API_RETURN(result); } @@ -1967,17 +2185,23 @@ fluid_synth_update_pitch_wheel_sens_LOCAL(fluid_synth_t* synth, int chan) * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param pval Location to store pitch wheel sensitivity value in semitones - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since Sometime AFTER v1.0 API freeze. */ int fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval) { + int result; fluid_return_val_if_fail (pval != NULL, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); - *pval = fluid_channel_get_pitch_wheel_sensitivity (synth->channel[chan]); - FLUID_API_RETURN(FLUID_OK); + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + *pval = fluid_channel_get_pitch_wheel_sensitivity (synth->channel[chan]); + result = FLUID_OK; + + FLUID_API_RETURN(result); } /** @@ -1985,7 +2209,7 @@ fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval) * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param preset Preset to assign to channel or NULL to clear (ownership is taken over) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ static int fluid_synth_set_preset (fluid_synth_t *synth, int chan, fluid_preset_t *preset) @@ -2094,7 +2318,7 @@ fluid_synth_find_preset(fluid_synth_t* synth, unsigned int banknum, * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param prognum MIDI program number (0-127) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ /* FIXME - Currently not real-time safe, due to preset allocation and mutex lock, * and may be called from within synthesis context. */ @@ -2106,68 +2330,73 @@ fluid_synth_program_change(fluid_synth_t* synth, int chan, int prognum) { fluid_preset_t* preset = NULL; fluid_channel_t* channel; - int subst_bank, subst_prog, banknum = 0, result; + int subst_bank, subst_prog, banknum = 0, result = FLUID_FAILED; fluid_return_val_if_fail (prognum >= 0 && prognum <= 128, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + channel = synth->channel[chan]; - if (channel->channel_type == CHANNEL_TYPE_DRUM) - banknum = DRUM_INST_BANK; - else - fluid_channel_get_sfont_bank_prog(channel, NULL, &banknum, NULL); + + if (channel->channel_type == CHANNEL_TYPE_DRUM) + banknum = DRUM_INST_BANK; + else + fluid_channel_get_sfont_bank_prog(channel, NULL, &banknum, NULL); - if (synth->verbose) - FLUID_LOG(FLUID_INFO, "prog\t%d\t%d\t%d", chan, banknum, prognum); + if (synth->verbose) + FLUID_LOG(FLUID_INFO, "prog\t%d\t%d\t%d", chan, banknum, prognum); - /* I think this is a hack for MIDI files that do bank changes in GM mode. - * Proper way to handle this would probably be to ignore bank changes when in - * GM mode. - JG - * This is now possible by setting synth.midi-bank-select=gm, but let the hack - * stay for the time being. - DH - */ - if (prognum != FLUID_UNSET_PROGRAM) - { - subst_bank = banknum; - subst_prog = prognum; - - preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); - - /* Fallback to another preset if not found */ - if (!preset) { - /* Percussion: Fallback to preset 0 in percussion bank */ - if (channel->channel_type == CHANNEL_TYPE_DRUM) { - subst_prog = 0; - subst_bank = DRUM_INST_BANK; + /* I think this is a hack for MIDI files that do bank changes in GM mode. + * Proper way to handle this would probably be to ignore bank changes when in + * GM mode. - JG + * This is now possible by setting synth.midi-bank-select=gm, but let the hack + * stay for the time being. - DH + */ + if (prognum != FLUID_UNSET_PROGRAM) + { + subst_bank = banknum; + subst_prog = prognum; + preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); - } - /* Melodic instrument */ - else { - /* Fallback first to bank 0:prognum */ - subst_bank = 0; - preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); - - /* Fallback to first preset in bank 0 (usually piano...) */ - if (!preset) - { - subst_prog = 0; - preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); + + /* Fallback to another preset if not found */ + if (!preset) { + /* Percussion: Fallback to preset 0 in percussion bank */ + if (channel->channel_type == CHANNEL_TYPE_DRUM) { + subst_prog = 0; + subst_bank = DRUM_INST_BANK; + preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); } - } + /* Melodic instrument */ + else { + /* Fallback first to bank 0:prognum */ + subst_bank = 0; + preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); - if (preset) - FLUID_LOG(FLUID_WARN, "Instrument not found on channel %d [bank=%d prog=%d], substituted [bank=%d prog=%d]", - chan, banknum, prognum, subst_bank, subst_prog); - else - FLUID_LOG(FLUID_WARN, "No preset found on channel %d [bank=%d prog=%d]", - chan, banknum, prognum); + /* Fallback to first preset in bank 0 (usually piano...) */ + if (!preset) + { + subst_prog = 0; + preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); + } + } + + if (preset) + FLUID_LOG(FLUID_WARN, "Instrument not found on channel %d [bank=%d prog=%d], substituted [bank=%d prog=%d]", + chan, banknum, prognum, subst_bank, subst_prog); + else + FLUID_LOG(FLUID_WARN, "No preset found on channel %d [bank=%d prog=%d]", + chan, banknum, prognum); + } } - } - /* Assign the SoundFont ID and program number to the channel */ - fluid_channel_set_sfont_bank_prog (channel, preset ? fluid_sfont_get_id (preset->sfont) : 0, - -1, prognum); - result = fluid_synth_set_preset (synth, chan, preset); + /* Assign the SoundFont ID and program number to the channel */ + fluid_channel_set_sfont_bank_prog (channel, preset ? fluid_sfont_get_id (preset->sfont) : 0, + -1, prognum); + result = fluid_synth_set_preset (synth, chan, preset); + FLUID_API_RETURN(result); } @@ -2176,7 +2405,7 @@ fluid_synth_program_change(fluid_synth_t* synth, int chan, int prognum) * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param bank MIDI bank number - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @note This function does not change the instrument currently assigned to \c chan, * as it is usually called prior to fluid_synth_program_change(). If you still want * instrument changes to take effect immediately, call fluid_synth_program_reset() @@ -2186,11 +2415,17 @@ fluid_synth_program_change(fluid_synth_t* synth, int chan, int prognum) int fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank) { + int result; fluid_return_val_if_fail (bank <= 16383, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); - fluid_channel_set_sfont_bank_prog (synth->channel[chan], -1, bank, -1); - FLUID_API_RETURN(FLUID_OK); + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + fluid_channel_set_sfont_bank_prog (synth->channel[chan], -1, bank, -1); + result = FLUID_OK; + + FLUID_API_RETURN(result); } /** @@ -2198,7 +2433,7 @@ fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank) * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param sfont_id ID of a loaded SoundFont - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @note This function does not change the instrument currently assigned to \c chan, * as it is usually called prior to fluid_synth_bank_select() or fluid_synth_program_change(). * If you still want instrument changes to take effect immediately, call fluid_synth_program_reset() @@ -2207,11 +2442,16 @@ fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank) int fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id) { + int result; FLUID_API_ENTRY_CHAN(FLUID_FAILED); - fluid_channel_set_sfont_bank_prog(synth->channel[chan], sfont_id, -1, -1); + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); - FLUID_API_RETURN(FLUID_OK); + fluid_channel_set_sfont_bank_prog(synth->channel[chan], sfont_id, -1, -1); + result = FLUID_OK; + + FLUID_API_RETURN(result); } /** @@ -2228,11 +2468,8 @@ fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id) int fluid_synth_unset_program (fluid_synth_t *synth, int chan) { - int result; FLUID_API_ENTRY_CHAN(FLUID_FAILED); - - result = fluid_synth_program_change (synth, chan, FLUID_UNSET_PROGRAM); - FLUID_API_RETURN(result); + FLUID_API_RETURN(fluid_synth_program_change (synth, chan, FLUID_UNSET_PROGRAM)); } /** @@ -2242,12 +2479,13 @@ fluid_synth_unset_program (fluid_synth_t *synth, int chan) * @param sfont_id Location to store SoundFont ID * @param bank_num Location to store MIDI bank number * @param preset_num Location to store MIDI program number - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_get_program(fluid_synth_t* synth, int chan, unsigned int* sfont_id, unsigned int* bank_num, unsigned int* preset_num) { + int result; fluid_channel_t* channel; fluid_return_val_if_fail (sfont_id != NULL, FLUID_FAILED); @@ -2255,14 +2493,18 @@ fluid_synth_get_program(fluid_synth_t* synth, int chan, unsigned int* sfont_id, fluid_return_val_if_fail (preset_num != NULL, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + channel = synth->channel[chan]; - fluid_channel_get_sfont_bank_prog(channel, (int *)sfont_id, (int *)bank_num, - (int *)preset_num); + fluid_channel_get_sfont_bank_prog(channel, (int *)sfont_id, (int *)bank_num, + (int *)preset_num); - /* 128 indicates that the preset is unset. Set to 0 to be backwards compatible. */ - if (*preset_num == FLUID_UNSET_PROGRAM) *preset_num = 0; - - FLUID_API_RETURN(FLUID_OK); + /* 128 indicates that the preset is unset. Set to 0 to be backwards compatible. */ + if (*preset_num == FLUID_UNSET_PROGRAM) *preset_num = 0; + result = FLUID_OK; + + FLUID_API_RETURN(result); } /** @@ -2272,7 +2514,7 @@ fluid_synth_get_program(fluid_synth_t* synth, int chan, unsigned int* sfont_id, * @param sfont_id ID of a loaded SoundFont * @param bank_num MIDI bank number * @param preset_num MIDI program number - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_program_select(fluid_synth_t* synth, int chan, unsigned int sfont_id, @@ -2283,21 +2525,23 @@ fluid_synth_program_select(fluid_synth_t* synth, int chan, unsigned int sfont_id int result; FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + channel = synth->channel[chan]; + /* ++ Allocate preset */ + preset = fluid_synth_get_preset (synth, sfont_id, bank_num, preset_num); - /* ++ Allocate preset */ - preset = fluid_synth_get_preset (synth, sfont_id, bank_num, preset_num); + if (preset == NULL) { + FLUID_LOG(FLUID_ERR, + "There is no preset with bank number %d and preset number %d in SoundFont %d", + bank_num, preset_num, sfont_id); + FLUID_API_RETURN(FLUID_FAILED); + } - if (preset == NULL) { - FLUID_LOG(FLUID_ERR, - "There is no preset with bank number %d and preset number %d in SoundFont %d", - bank_num, preset_num, sfont_id); - FLUID_API_RETURN(FLUID_FAILED); - } - - /* Assign the new SoundFont ID, bank and program number to the channel */ - fluid_channel_set_sfont_bank_prog (channel, sfont_id, bank_num, preset_num); - result = fluid_synth_set_preset (synth, chan, preset); + /* Assign the new SoundFont ID, bank and program number to the channel */ + fluid_channel_set_sfont_bank_prog (channel, sfont_id, bank_num, preset_num); + result = fluid_synth_set_preset (synth, chan, preset); FLUID_API_RETURN(result); } @@ -2309,7 +2553,7 @@ fluid_synth_program_select(fluid_synth_t* synth, int chan, unsigned int sfont_id * @param sfont_name Name of a loaded SoundFont * @param bank_num MIDI bank number * @param preset_num MIDI program number - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.0 */ int @@ -2323,22 +2567,25 @@ fluid_synth_program_select_by_sfont_name (fluid_synth_t* synth, int chan, fluid_return_val_if_fail (sfont_name != NULL, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + channel = synth->channel[chan]; + /* ++ Allocate preset */ + preset = fluid_synth_get_preset_by_sfont_name (synth, sfont_name, bank_num, + preset_num); + if (preset == NULL) { + FLUID_LOG(FLUID_ERR, + "There is no preset with bank number %d and preset number %d in SoundFont %s", + bank_num, preset_num, sfont_name); + FLUID_API_RETURN(FLUID_FAILED); + } - /* ++ Allocate preset */ - preset = fluid_synth_get_preset_by_sfont_name (synth, sfont_name, bank_num, - preset_num); - if (preset == NULL) { - FLUID_LOG(FLUID_ERR, - "There is no preset with bank number %d and preset number %d in SoundFont %s", - bank_num, preset_num, sfont_name); - FLUID_API_RETURN(FLUID_FAILED); - } - - /* Assign the new SoundFont ID, bank and program number to the channel */ - fluid_channel_set_sfont_bank_prog (channel, fluid_sfont_get_id (preset->sfont), - bank_num, preset_num); - result = fluid_synth_set_preset (synth, chan, preset); + /* Assign the new SoundFont ID, bank and program number to the channel */ + fluid_channel_set_sfont_bank_prog (channel, fluid_sfont_get_id (preset->sfont), + bank_num, preset_num); + result = fluid_synth_set_preset (synth, chan, preset); + FLUID_API_RETURN(result); } @@ -2472,7 +2719,7 @@ fluid_synth_handle_polyphony(void *data, const char* name, int value) * Set synthesizer polyphony (max number of voices). * @param synth FluidSynth instance * @param polyphony Polyphony to assign - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.0.6 */ int @@ -2581,7 +2828,7 @@ fluid_synth_get_internal_bufsize(fluid_synth_t* synth) /** * Resend a bank select and a program change for every channel and assign corresponding instruments. * @param synth FluidSynth instance - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * This function is called mainly after a SoundFont has been loaded, * unloaded or reloaded. @@ -2608,7 +2855,7 @@ fluid_synth_program_reset(fluid_synth_t* synth) * @param right Array of float buffers to store right channel of planar audio (size: dito) * @param fx_left Since 1.1.7: If not \c NULL, array of float buffers to store left effect channels (as many as \c synth.effects-channels buffers, each of \c len in size) * @param fx_right Since 1.1.7: If not \c NULL, array of float buffers to store right effect channels (size: dito) - * @return FLUID_OK on success, FLUID_FAIL otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * @note Should only be called from synthesis thread. * @@ -2787,7 +3034,7 @@ fluid_synth_nwrite_float(fluid_synth_t* synth, int len, * @param in Ignored * @param nout Count of arrays in 'out' * @param out Array of arrays to store audio to - * @return FLUID_OK on success, FLUID_FAIL otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * This function implements the default interface defined in fluidsynth/audio.h. * @@ -2835,7 +3082,7 @@ fluid_synth_process(fluid_synth_t* synth, int len, int nin, float** in, * @param rout Array of floats to store right channel of audio * @param roff Offset index in 'rout' for first sample * @param rincr Increment between samples stored to 'rout' - * @return FLUID_OK on success, FLUID_FAIL otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * Useful for storing interleaved stereo (lout = rout, loff = 0, roff = 1, * lincr = 2, rincr = 2). @@ -2934,7 +3181,7 @@ roundi (float x) * @param rout Array of 16 bit words to store right channel of audio * @param roff Offset index in 'rout' for first sample * @param rincr Increment between samples stored to 'rout' - * @return FLUID_OK on success, FLUID_FAIL otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * Useful for storing interleaved stereo (lout = rout, loff = 0, roff = 1, * lincr = 2, rincr = 2). @@ -3242,17 +3489,23 @@ fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth) * SoundFont loader preset noteon method. */ fluid_voice_t* -fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan, int key, int vel) +fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, + int chan, int key, int vel) +{ + fluid_return_val_if_fail (sample != NULL, NULL); + FLUID_API_ENTRY_CHAN(NULL); + FLUID_API_RETURN (fluid_synth_alloc_voice_LOCAL(synth, sample, chan, key, vel, NULL)); + +} + +fluid_voice_t* +fluid_synth_alloc_voice_LOCAL(fluid_synth_t* synth, fluid_sample_t* sample, int chan, int key, int vel, fluid_zone_range_t* zone_range) { int i, k; fluid_voice_t* voice = NULL; fluid_channel_t* channel = NULL; - fluid_mod_t* default_mod; unsigned int ticks; - fluid_return_val_if_fail (sample != NULL, NULL); - FLUID_API_ENTRY_CHAN(NULL); - /* check if there's an available synthesis process */ for (i = 0; i < synth->polyphony; i++) { if (_AVAILABLE(synth->voice[i])) { @@ -3269,7 +3522,7 @@ fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan, if (voice == NULL) { FLUID_LOG(FLUID_WARN, "Failed to allocate a synthesis process. (chan=%d,key=%d)", chan, key); - FLUID_API_RETURN(NULL); + return NULL; } ticks = fluid_synth_get_ticks(synth); @@ -3289,24 +3542,47 @@ fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan, k); } - if (chan >= 0) { - channel = synth->channel[chan]; - } + channel = synth->channel[chan]; - if (fluid_voice_init (voice, sample, channel, key, vel, + if (fluid_voice_init (voice, sample, zone_range, channel, key, vel, synth->storeid, ticks, synth->gain) != FLUID_OK) { FLUID_LOG(FLUID_WARN, "Failed to initialize voice"); - FLUID_API_RETURN(NULL); + return NULL; } /* add the default modulators to the synthesis process. */ - default_mod = synth->default_mod; - while (default_mod != NULL) { - fluid_voice_add_mod(voice, default_mod, FLUID_VOICE_DEFAULT); - default_mod = default_mod->next; + /* custom_breath2att_modulator is not a default modulator specified in SF + it is intended to replace default_vel2att_mod for this channel on demand using + API fluid_synth_set_breath_mode() or shell command setbreathmode for this channel. + */ + { + int mono = fluid_channel_is_playing_mono(channel); + fluid_mod_t* default_mod =synth->default_mod; + while (default_mod != NULL) + { + if( + /* See if default_mod is the velocity_to_attenuation modulator */ + fluid_mod_test_identity(default_mod, &default_vel2att_mod) && + // See if a replacement by custom_breath2att_modulator has been demanded + // for this channel + ((!mono && (channel->mode & FLUID_CHANNEL_BREATH_POLY)) || + (mono && (channel->mode & FLUID_CHANNEL_BREATH_MONO))) + ) + { + // Replacement of default_vel2att modulator by custom_breath2att_modulator + fluid_voice_add_mod(voice, &custom_breath2att_mod, FLUID_VOICE_DEFAULT); + } + else + { + fluid_voice_add_mod(voice, default_mod, FLUID_VOICE_DEFAULT); + } + + // Next default modulator to add to the voice + default_mod = default_mod->next; + } } - FLUID_API_RETURN(voice); + return voice; } /* Kill all voices on a given channel, which have the same exclusive class @@ -3478,7 +3754,7 @@ new_fluid_sfont_info (fluid_synth_t *synth, fluid_sfont_t *sfont) * @param synth FluidSynth instance * @param id ID of SoundFont to unload * @param reset_presets TRUE to re-assign presets for all MIDI channels - * @return FLUID_OK on success, FLUID_FAILED on error + * @return #FLUID_OK on success, FLUID_FAILED on error */ int fluid_synth_sfunload(fluid_synth_t* synth, unsigned int id, int reset_presets) @@ -3870,7 +4146,7 @@ fluid_synth_set_reverb_on(fluid_synth_t* synth, int on) * Activate a reverb preset. * @param synth FluidSynth instance * @param num Reverb preset number - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * @note Currently private to libfluidsynth. */ @@ -3895,7 +4171,7 @@ fluid_synth_set_reverb_preset(fluid_synth_t* synth, unsigned int num) * @param damping Reverb damping value (0.0-1.0) * @param width Reverb width value (0.0-100.0) * @param level Reverb level value (0.0-1.0) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * @note Not realtime safe and therefore should not be called from synthesis * context at the risk of stalling audio output. @@ -3910,7 +4186,7 @@ fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize, double damping, /** * Set reverb roomsize. See fluid_synth_set_reverb() for further info. - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_set_reverb_roomsize(fluid_synth_t* synth, double roomsize) { @@ -3919,7 +4195,7 @@ int fluid_synth_set_reverb_roomsize(fluid_synth_t* synth, double roomsize) /** * Set reverb damping. See fluid_synth_set_reverb() for further info. - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_set_reverb_damp(fluid_synth_t* synth, double damping) { @@ -3928,7 +4204,7 @@ int fluid_synth_set_reverb_damp(fluid_synth_t* synth, double damping) /** * Set reverb width. See fluid_synth_set_reverb() for further info. - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_set_reverb_width(fluid_synth_t* synth, double width) { @@ -3937,7 +4213,7 @@ int fluid_synth_set_reverb_width(fluid_synth_t* synth, double width) /** * Set reverb level. See fluid_synth_set_reverb() for further info. - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_set_reverb_level(fluid_synth_t* synth, double level) { @@ -3952,7 +4228,7 @@ int fluid_synth_set_reverb_level(fluid_synth_t* synth, double level) * @param damping Reverb damping value (0.0-1.0) * @param width Reverb width value (0.0-100.0) * @param level Reverb level value (0.0-1.0) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * @note Not realtime safe and therefore should not be called from synthesis * context at the risk of stalling audio output. @@ -4082,7 +4358,7 @@ fluid_synth_set_chorus_on(fluid_synth_t* synth, int on) * @param depth_ms Chorus depth (max value depends on synth sample rate, * 0.0-21.0 is safe for sample rate values up to 96KHz) * @param type Chorus waveform type (#fluid_chorus_mod) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level, double speed, double depth_ms, int type) @@ -4093,7 +4369,7 @@ int fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level, /** * Set the chorus voice count. See fluid_synth_set_chorus() for further info. - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_set_chorus_nr(fluid_synth_t* synth, int nr) { @@ -4102,7 +4378,7 @@ int fluid_synth_set_chorus_nr(fluid_synth_t* synth, int nr) /** * Set the chorus level. See fluid_synth_set_chorus() for further info. - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_set_chorus_level(fluid_synth_t* synth, double level) { @@ -4111,7 +4387,7 @@ int fluid_synth_set_chorus_level(fluid_synth_t* synth, double level) /** * Set the chorus speed. See fluid_synth_set_chorus() for further info. - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_set_chorus_speed(fluid_synth_t* synth, double speed) { @@ -4120,7 +4396,7 @@ int fluid_synth_set_chorus_speed(fluid_synth_t* synth, double speed) /** * Set the chorus depth. See fluid_synth_set_chorus() for further info. - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_set_chorus_depth(fluid_synth_t* synth, double depth_ms) { @@ -4129,7 +4405,7 @@ int fluid_synth_set_chorus_depth(fluid_synth_t* synth, double depth_ms) /** * Set the chorus type. See fluid_synth_set_chorus() for further info. - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_set_chorus_type(fluid_synth_t* synth, int type) { @@ -4147,7 +4423,7 @@ int fluid_synth_set_chorus_type(fluid_synth_t* synth, int type) * @param depth_ms Chorus depth (max value depends on synth sample rate, * 0.0-21.0 is safe for sample rate values up to 96KHz) * @param type Chorus waveform type (#fluid_chorus_mod) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_set_chorus_full(fluid_synth_t* synth, int set, int nr, double level, @@ -4273,15 +4549,20 @@ fluid_synth_get_chorus_type(fluid_synth_t* synth) * several voice processes, for example a stereo sample. Don't * release those... */ -static void +void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth, int chan, int key) { int i; fluid_voice_t* voice; - synth->storeid = synth->noteid++; + /* storeid is a parameter for fluid_voice_init() */ + synth->storeid = synth->noteid++; + /* for "monophonic playing" key is the previous sustained note + if it exists (0 to 127) or INVALID_NOTE otherwise */ + if(key == INVALID_NOTE) return; + for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; if (fluid_voice_is_playing(voice) @@ -4302,7 +4583,7 @@ fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth, int chan, * @param synth FluidSynth instance * @param chan MIDI channel to set interpolation method on or -1 for all channels * @param interp_method Interpolation method (#fluid_interp) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_set_interp_method(fluid_synth_t* synth, int chan, int interp_method) @@ -4519,7 +4800,7 @@ fluid_synth_update_voice_tuning_LOCAL (fluid_synth_t *synth, fluid_channel_t *ch * Pass NULL to create a equal tempered (normal) scale. * @param apply TRUE to apply new tuning in realtime to existing notes which * are using the replaced tuning (if any), FALSE otherwise - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.0 */ int @@ -4559,7 +4840,7 @@ fluid_synth_activate_key_tuning(fluid_synth_t* synth, int bank, int prog, * tuning amount) * @param apply TRUE to apply new tuning in realtime to existing notes which * are using the replaced tuning (if any), FALSE otherwise - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.0 */ int @@ -4600,7 +4881,7 @@ fluid_synth_activate_octave_tuning(fluid_synth_t* synth, int bank, int prog, * cents from MIDI note 0) * @param apply TRUE to apply tuning change in realtime to existing notes using * the specified tuning, FALSE otherwise - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * @note Prior to version 1.1.0 it was an error to specify a tuning that didn't * already exist. Starting with 1.1.0, the default equal tempered scale will be @@ -4649,7 +4930,7 @@ fluid_synth_tune_notes(fluid_synth_t* synth, int bank, int prog, * @param bank Tuning bank number (0-127), not related to MIDI instrument bank * @param prog Tuning preset number (0-127), not related to MIDI instrument program * @param apply TRUE to apply tuning change to active notes, FALSE otherwise - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.0 * * @note A default equal tempered scale will be created, if no tuning exists @@ -4722,7 +5003,7 @@ fluid_synth_set_tuning_LOCAL (fluid_synth_t *synth, int chan, * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param apply TRUE to apply tuning change to active notes, FALSE otherwise - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.0 */ int @@ -4810,7 +5091,7 @@ fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog) * @param name Location to store tuning name or NULL to ignore * @param len Maximum number of chars to store to 'name' (including NULL byte) * @param pitch Array to store tuning scale to or NULL to ignore (len of 128) - * @return FLUID_OK if matching tuning was found, FLUID_FAILED otherwise + * @return #FLUID_OK if matching tuning was found, #FLUID_FAILED otherwise */ int fluid_synth_tuning_dump(fluid_synth_t* synth, int bank, int prog, @@ -4853,7 +5134,7 @@ fluid_synth_get_settings(fluid_synth_t* synth) /** * Same as calling \c fluid_synth_set_gen2(synth, chan, param, value, FALSE, FALSE) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_set_gen(fluid_synth_t* synth, int chan, int param, float value) { @@ -4870,7 +5151,7 @@ int fluid_synth_set_gen(fluid_synth_t* synth, int chan, int param, float value) * @param normalized FALSE if value is specified in the native units of the generator, * TRUE to take the value as a 0.0-1.0 range and apply it to the valid * generator effect range (scaled and shifted as necessary). - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * This function allows for setting all effect parameters in real time on a * MIDI channel. Setting absolute to non-zero will cause the value to override @@ -4933,7 +5214,7 @@ fluid_synth_get_gen(fluid_synth_t* synth, int chan, int param) * Handle MIDI event from MIDI router, used as a callback function. * @param data FluidSynth instance * @param event MIDI event to handle - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_synth_handle_midi_event(void* data, fluid_midi_event_t* event) @@ -4992,7 +5273,7 @@ fluid_synth_handle_midi_event(void* data, fluid_midi_event_t* event) * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param key MIDI note number (0-127) * @param vel MIDI velocity number (1-127) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * @note Should only be called from within synthesis thread, which includes * SoundFont loader preset noteon method. @@ -5015,7 +5296,7 @@ fluid_synth_start(fluid_synth_t* synth, unsigned int id, fluid_preset_t* preset, * Stop notes for a given note event voice ID. * @param synth FluidSynth instance * @param id Voice note event ID - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * @note In FluidSynth versions prior to 1.1.0 #FLUID_FAILED would be returned * if no matching voice note event ID was found. Versions after 1.1.0 only @@ -5148,7 +5429,7 @@ void fluid_synth_api_exit(fluid_synth_t* synth) * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param type MIDI channel type (#fluid_midi_channel_type) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.4 */ int fluid_synth_set_channel_type(fluid_synth_t* synth, int chan, int type) @@ -5214,7 +5495,7 @@ int fluid_synth_set_custom_filter(fluid_synth_t* synth, int type, int flags) * * @param synth FluidSynth instance * @param channels comma-separated list of channel numbers - * @return FLUID_OK on success, otherwise FLUID_FAILED + * @return #FLUID_OK on success, otherwise #FLUID_FAILED */ static int fluid_synth_set_important_channels(fluid_synth_t *synth, const char *channels) { @@ -5282,3 +5563,451 @@ static void fluid_synth_handle_important_channels(void *data, const char *name, fluid_synth_set_important_channels(synth, value); fluid_synth_api_exit(synth); } + + +/** API legato mode *********************************************************/ + +/** + * Sets the legato mode of a channel. + * + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param legatomode The legato mode as indicated by #fluid_channel_legato_mode. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a legatomode is invalid. + */ +int fluid_synth_set_legato_mode(fluid_synth_t* synth, int chan, int legatomode) +{ + /* checks parameters first */ + fluid_return_val_if_fail (legatomode >= 0, FLUID_FAILED); + fluid_return_val_if_fail (legatomode < FLUID_CHANNEL_LEGATO_MODE_LAST, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + synth->channel[chan]->legatomode = legatomode; + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Gets the legato mode of a channel. + * + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param legatomode The legato mode as indicated by #fluid_channel_legato_mode. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a legatomode is NULL. + */ +int fluid_synth_get_legato_mode(fluid_synth_t* synth, int chan, int *legatomode) +{ + /* checks parameters first */ + fluid_return_val_if_fail (legatomode!= NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + * legatomode = synth->channel[chan]->legatomode; + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/** API portamento mode *********************************************************/ + +/** + * Sets the portamento mode of a channel. + * + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param portamentomode The portamento mode as indicated by #fluid_channel_portamento_mode. + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a portamentomode is invalid. + */ +int fluid_synth_set_portamento_mode(fluid_synth_t* synth, int chan, + int portamentomode) +{ + /* checks parameters first */ + fluid_return_val_if_fail (portamentomode >= 0, FLUID_FAILED); + fluid_return_val_if_fail (portamentomode < FLUID_CHANNEL_PORTAMENTO_MODE_LAST, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + synth->channel[chan]->portamentomode = portamentomode; + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Gets the portamento mode of a channel. + * + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param portamentomode Pointer to the portamento mode as indicated by #fluid_channel_portamento_mode. + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a portamentomode is NULL. + */ +int fluid_synth_get_portamento_mode(fluid_synth_t* synth, int chan, + int *portamentomode) +{ + /* checks parameters first */ + fluid_return_val_if_fail (portamentomode!= NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + * portamentomode = synth->channel[chan]->portamentomode; + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/** API breath mode *********************************************************/ + +/** + * Sets the breath mode of a channel. + * + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param breathmode The breath mode as indicated by #fluid_channel_breath_flags. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + */ +int fluid_synth_set_breath_mode(fluid_synth_t* synth, int chan, int breathmode) +{ + /* checks parameters first */ + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + fluid_channel_set_breath_info(synth->channel[chan],breathmode); + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Gets the breath mode of a channel. + * + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param breathmode Pointer to the returned breath mode as indicated by #fluid_channel_breath_flags. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a breathmode is NULL. + */ +int fluid_synth_get_breath_mode(fluid_synth_t* synth, int chan, int *breathmode) +{ + /* checks parameters first */ + fluid_return_val_if_fail (breathmode!= NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + * breathmode = fluid_channel_get_breath_info(synth->channel[chan]); + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/** API Poly/mono mode ******************************************************/ + +/* + * Resets a basic channel group of MIDI channels. + * @param synth the synth instance. + * @param chan the beginning channel of the group. + * @param nbr_chan the number of channel in the group. +*/ +static void +fluid_synth_reset_basic_channel_LOCAL(fluid_synth_t* synth, int chan, int nbr_chan) +{ + int i; + for (i = chan; i < chan + nbr_chan; i++) + { + fluid_channel_reset_basic_channel_info(synth->channel[i]); + synth->channel[i]->mode_val = 0; + } +} + +/** + * Disables and unassigns all channels from a basic channel group. + * + * @param synth The synth instance. + * @param chan The basic channel of the group to reset or -1 to reset all channels. + * @note By default (i.e. on creation after new_fluid_synth() and after fluid_synth_system_reset()) + * a synth instance has one basic channel at channel 0 in mode #FLUID_CHANNEL_MODE_OMNION_POLY. + * All other channels belong to this basic channel group. Make sure to call this function before + * setting any custom basic channel setup. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a chan isn't a basic channel. + */ +int fluid_synth_reset_basic_channel(fluid_synth_t* synth, int chan) +{ + int nbr_chan; + + /* checks parameters first */ + if (chan < 0) + { + fluid_return_val_if_fail (synth!= NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + /* The range is all MIDI channels from 0 to MIDI channel count -1 */ + chan = 0; /* beginning chan */ + nbr_chan = synth->midi_channels; /* MIDI Channels number */ + } + else + { + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /* checks if chan is a basic channel */ + if ( ! (synth->channel[chan]->mode & FLUID_CHANNEL_BASIC) ) + { + FLUID_API_RETURN(FLUID_FAILED); + } + /* The range is all MIDI channels in the group from chan */ + nbr_chan = synth->channel[chan]->mode_val; /* nbr of channels in the group */ + } + /* resets the range of MIDI channels */ + fluid_synth_reset_basic_channel_LOCAL(synth, chan, nbr_chan); + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Checks if a new basic channel group overlaps the next basic channel group. + * + * On success the function returns the possible number of channel for this + * new basic channel group. + * The function fails if the new group overlaps the next basic channel group. + * + * @param see fluid_synth_set_basic_channel. + * @return + * - On success, the effective number of channels for this new basic channel group, + * #FLUID_FAILED otherwise. + * - #FLUID_FAILED + * - \a val has a number of channels overlapping next basic channel group or been + * above MIDI channel count. + */ +static int +fluid_synth_check_next_basic_channel(fluid_synth_t* synth, int basicchan, int mode, int val) +{ + int i, n_chan = synth->midi_channels; /* MIDI Channels count */ + int real_val = val; /* real number of channels in the group */ + + /* adjusts val range */ + if (mode == FLUID_CHANNEL_MODE_OMNIOFF_POLY) + { + real_val = 1; /* mode poly omnioff implies a group of only one channel.*/ + } + else if (val == 0) + { + /* mode poly omnion (0), mono omnion (1), mono omni off (3) */ + /* value 0 means all possible channels from basicchan to MIDI channel count -1.*/ + real_val = n_chan - basicchan; + } + /* checks if val range is above MIDI channel count */ + else if ( basicchan + val > n_chan) + { + return FLUID_FAILED; + } + /* checks if this basic channel group overlaps next basic channel group */ + for (i = basicchan + 1; i < basicchan + real_val; i++) + { + if (synth->channel[i]->mode & FLUID_CHANNEL_BASIC) + { + /* A value of 0 for val means all possible channels from basicchan to + to the next basic channel -1 (if any). + When i reachs the next basic channel group, real_val will be + limited if it is possible */ + if (val == 0) + { /* limitation of real_val */ + real_val = i - basicchan; + break; + } + /* overlap with the next basic channel group */ + return FLUID_FAILED; + } + } + return real_val; +} + +/** + * Sets a new basic channel group only. The function doesn't allow to change an + * existing basic channel. + * + * The function fails if any channel overlaps any existing basic channel group. + * To make room if necessary, basic channel groups can be cleared using + * fluid_synth_reset_basic_channel(). + * + * @param synth the synth instance. + * @param chan the basic Channel number (0 to MIDI channel count-1). + * @param mode the MIDI mode to use for chan (see #fluid_basic_channel_modes). + * @param val number of channels in the group. + * @note \a val is only relevant for mode #FLUID_CHANNEL_MODE_OMNION_POLY, + * #FLUID_CHANNEL_MODE_OMNION_MONO and #FLUID_CHANNEL_MODE_OMNIOFF_MONO. A value + * of 0 means all possible channels from \a chan to to next basic channel minus 1 (if any) + * or to MIDI channel count minus 1. Val is ignored for #FLUID_CHANNEL_MODE_OMNIOFF_POLY + * as this mode implies a group of only one channel. + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a mode is invalid. + * - \a val has a number of channels overlapping another basic channel group or been + * above MIDI channel count. + * - When the function fails, any existing basic channels aren't modified. + */ +int fluid_synth_set_basic_channel(fluid_synth_t* synth, int chan, int mode, int val) +{ + /* check parameters */ + fluid_return_val_if_fail (mode >= 0, FLUID_FAILED); + fluid_return_val_if_fail (mode < FLUID_CHANNEL_MODE_LAST, FLUID_FAILED); + fluid_return_val_if_fail (val >= 0, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + if (val > 0 && chan + val > synth->midi_channels) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + /* Checks if there is an overlap with the next basic channel */ + val = fluid_synth_check_next_basic_channel(synth, chan, mode, val); + if( val == FLUID_FAILED || synth->channel[chan]->mode & FLUID_CHANNEL_ENABLED) + { + /* overlap with the next or previous channel group */ + FLUID_LOG(FLUID_INFO, "basic channel %d overlaps another group", chan); + FLUID_API_RETURN(FLUID_FAILED); + } + + /* sets a new basic channel group */ + fluid_synth_set_basic_channel_LOCAL(synth, chan, mode, val); + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/* + * Local version of fluid_synth_set_basic_channel(), called internally: + * - by fluid_synth_set_basic_channel() to set a new basic channel group. + * - during creation new_fluid_synth() or on CC reset to set a default basic channel group. + * - on CC ominoff, CC omnion, CC poly , CC mono to change an existing basic channel group. + * + * @param see fluid_synth_set_basic_channel() +*/ +static void +fluid_synth_set_basic_channel_LOCAL(fluid_synth_t* synth, int basicchan, int mode, int val) +{ + int i; + + /* sets the basic channel group */ + for (i = basicchan; i < basicchan + val; i++) + { + int new_mode = mode; /* OMNI_OFF/ON, MONO/POLY ,others bits are zero */ + int new_val; + /* MIDI specs: when mode is changed, channel must receive ALL_NOTES_OFF */ + fluid_synth_all_notes_off_LOCAL (synth, i); + + if (i == basicchan) + { + new_mode |= FLUID_CHANNEL_BASIC; /* First channel in the group */ + new_val = val; /* number of channels in the group */ + } + else + { + new_val =0; /* val is 0 for other channel than basic channel */ + } + /* Channel is enabled */ + new_mode |= FLUID_CHANNEL_ENABLED; + /* Now new_mode is OMNI OFF/ON,MONO/POLY, BASIC_CHANNEL or not and enabled */ + fluid_channel_set_basic_channel_info(synth->channel[i],new_mode); + synth->channel[i]->mode_val = new_val; + } +} + +/** + * Searchs a previous basic channel starting from chan. + * + * @param synth the synth instance. + * @param chan starting index of the search (including chan). + * @return index of the basic channel if found , FLUID_FAILED otherwise. + */ +static int fluid_synth_get_previous_basic_channel(fluid_synth_t* synth, int chan) +{ + for (; chan >=0; chan--) + { /* searchs previous basic channel */ + if (synth->channel[chan]->mode & FLUID_CHANNEL_BASIC) + { /* chan is the previous basic channel */ + return chan; + } + } + return FLUID_FAILED; +} + +/** + * Returns poly mono mode information of any MIDI channel. + * + * @param synth the synth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param basic_chan_out Buffer to store the basic channel \a chan belongs to or #FLUID_FAILED if \a chan is disabled. + * @param mode_out Buffer to store the mode of \a chan (see #fluid_basic_channel_modes) or #FLUID_FAILED if \a chan is disabled. + * @param val_out Buffer to store the total number of channels in this basic channel group or #FLUID_FAILED if \a chan is disabled. + * @note If any of \a basic_chan_out, \a mode_out, \a val_out pointer is NULL + * the corresponding information isn't returned. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + */ +int fluid_synth_get_basic_channel(fluid_synth_t* synth, int chan, + int *basic_chan_out, + int *mode_out, + int *val_out ) +{ + int basic_chan = FLUID_FAILED; + int mode = FLUID_FAILED; + int val = FLUID_FAILED; + + /* checks parameters first */ + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + if ((synth->channel[chan]->mode & FLUID_CHANNEL_ENABLED) && + /* chan is enabled , we search the basic channel chan belongs to */ + (basic_chan = fluid_synth_get_previous_basic_channel(synth, chan)) != FLUID_FAILED) + { + mode = synth->channel[chan]->mode & FLUID_CHANNEL_MODE_MASK; + val = synth->channel[basic_chan]->mode_val; + } + + /* returns the informations if they are requested */ + if (basic_chan_out) + { + * basic_chan_out = basic_chan; + } + + if (mode_out) + { + * mode_out = mode; + } + + if (val_out) + { + * val_out = val; + } + + FLUID_API_RETURN(FLUID_OK); +} diff --git a/src/synth/fluid_synth.h b/src/synth/fluid_synth.h index e88f05d7..a972fabc 100644 --- a/src/synth/fluid_synth.h +++ b/src/synth/fluid_synth.h @@ -46,7 +46,6 @@ #define FLUID_UNSET_PROGRAM 128 /* Program number used to unset a preset */ - /*************************************************************** * * ENUM @@ -136,6 +135,7 @@ struct _fluid_synth_t int active_voice_count; /**< count of active voices */ unsigned int noteid; /**< the id is incremented for every new note. it's used for noteoff's */ unsigned int storeid; + int fromkey_portamento; /**< fromkey portamento */ fluid_rvoice_eventhandler_t* eventhandler; double reverb_roomsize; /**< Shadow of reverb roomsize */ @@ -174,13 +174,6 @@ fluid_preset_t* fluid_synth_find_preset(fluid_synth_t* synth, unsigned int banknum, unsigned int prognum); void fluid_synth_sfont_unref (fluid_synth_t *synth, fluid_sfont_t *sfont); - - -int fluid_synth_all_notes_off(fluid_synth_t* synth, int chan); -int fluid_synth_all_sounds_off(fluid_synth_t* synth, int chan); -int fluid_synth_kill_voice(fluid_synth_t* synth, fluid_voice_t * voice); - -void fluid_synth_print_voice(fluid_synth_t* synth); void fluid_synth_dither_s16(int *dither_index, int len, float* lin, float* rin, void* lout, int loff, int lincr, @@ -198,8 +191,6 @@ int fluid_synth_set_chorus_full(fluid_synth_t* synth, int set, int nr, double le fluid_sample_timer_t* new_fluid_sample_timer(fluid_synth_t* synth, fluid_timer_callback_t callback, void* data); void delete_fluid_sample_timer(fluid_synth_t* synth, fluid_sample_timer_t* timer); -void fluid_synth_api_enter(fluid_synth_t* synth); -void fluid_synth_api_exit(fluid_synth_t* synth); void fluid_synth_process_event_queue(fluid_synth_t* synth); @@ -209,7 +200,19 @@ int fluid_synth_set_gen2 (fluid_synth_t* synth, int chan, /* * misc */ - void fluid_synth_settings(fluid_settings_t* settings); + +/* extern declared in fluid_synth_monopoly.c */ + +int fluid_synth_noteon_mono_staccato(fluid_synth_t* synth,int chan,int key,int vel); +int fluid_synth_noteon_mono_LOCAL(fluid_synth_t* synth, int chan, int key, int vel); +int fluid_synth_noteoff_mono_LOCAL(fluid_synth_t* synth, int chan, int key); +int fluid_synth_noteon_monopoly_legato(fluid_synth_t* synth, int chan, int fromkey, int tokey, int vel); +int fluid_synth_noteoff_monopoly(fluid_synth_t* synth, int chan, int key, char Mono); + +fluid_voice_t* +fluid_synth_alloc_voice_LOCAL(fluid_synth_t* synth, fluid_sample_t* sample, int chan, int key, int vel, fluid_zone_range_t* zone_range); + +void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth, int chan, int key); #endif /* _FLUID_SYNTH_H */ diff --git a/src/synth/fluid_synth_monopoly.c b/src/synth/fluid_synth_monopoly.c new file mode 100644 index 00000000..c8712949 --- /dev/null +++ b/src/synth/fluid_synth_monopoly.c @@ -0,0 +1,685 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_synth.h" +#include "fluid_chan.h" +#include "fluid_defsfont.h" + + +/****************************************************************************** + The legato detector is composed as this, + variables: + - monolist: monophonic list variable. + - prev_note: to store the most recent note before adding on noteon or before + removing on noteoff. + - FLUID_CHANNEL_LEGATO_PLAYING: legato/staccato state bit that informs on + legato or staccato playing. + functions: + - fluid_channel_add_monolist(), for inserting a new note. + - fluid_channel_search_monolist(), for searching the position of a note + into the list. + - fluid_channel_remove_monolist(), for removing a note from the list. + + The monophonic list + +------------------------------------------------+ + | +----+ +----+ +----+ +----+ | + | |note| |note| |note| |note| | + +--->|vel |-->|vel |-->....-->|vel |-->|vel |----+ + +----+ +----+ +----+ +----+ + /|\ /|\ + | | + i_first i_last + + The list allows an easy automatic detection of a legato passage when it is + played on a MIDI keyboard input device. + It is useful also when the input device is an ewi (electronic wind instrument) + or evi (electronic valve instrument) and these instruments are unable to send + MIDI CC legato on/off. + + The list memorizes the notes in playing order. + - (a) On noteOn n2, if a previous note n1 exists, there is a legato + detection with n1 (with or without portamento from n1 to n2 See note below). + - (b) On noteOff of the running note n2, if a previous note n1 exists, + there is a legato detection from n2 to n1, allowing fast trills playing + (with or without portamento from n2 to n1. See note below). + + Notes in the list are inserted to the end of the list that works like a + circular buffer.The features are: + + 1) It is always possible to play an infinite legato passage in + direct order (n1_On,n2_On,n3_On,....). + + 2) Playing legato in the reverse order (n10_Off, n9_Off,,...) helps in + fast trills playing as the list memorizes 10 most recent notes. + + 3) Playing an infinite lagato passage in ascendant or descendant order, + without playing trills is always possible using the usual way like this: + First we begin with an ascendant passage, + n1On, (n2On,n1Off), (n3On,n2Off) , (n4On,n3Off), then + we continue with a descendant passage + (n3On,n4off), (n2On,n3off), (n1On,n2off), n1Off...and so on + + Each MIDI channel have a legato detector. + + Note: + Portamento is a feature independant of the legato detector. So + portamento isn't part of the lagato detector. However portamento + (when enabled) is triggered at noteOn (like legato). Like in legato + situation it is usual to have a portamento from a note 'fromkey' to another + note 'tokey'. Portamento fromkey note choice is determined at noteOn by + fluid_synth_get_fromkey_portamento_legato() (see below). + + More informations in FluidPolyMono-0004.pdf chapter 4 (Appendices). +******************************************************************************/ + + +/***************************************************************************** + Portamento related functions in Poly or Mono mode +******************************************************************************/ + +/** + * fluid_synth_get_fromkey_portamento_legato returns two informations: + * - fromkey note for portamento. + * - fromkey note for legato. + * +-----> fromkey_portamento + * ______|________ + * portamento modes >------->| | + * | get_fromkey | + * Porta.on/off >------------------------->|_______________| + * (PTC) | + * +-----> fromkey_legato + * + * The functions is intended to be call on noteOn mono + * see fluid_synth_noteon_mono_staccato(), fluid_synth_noteon_monopoly_legato() + * ------- + * 1)The function determines if a portamento must occur on next noteOn. + * The value returned is 'fromkey portamento' which is the pitchstart key + * of a portamento, as function of PTC or (default_fromkey, prev_note) both + * if Portamento On. By order of precedence the result is: + * 1.1) PTC have precedence over Portamento On. + * If CC PTC has been received, its value supersedes and any + * portamento pedal On, default_fromkey,prev_note or portamento mode. + * 1.2) Otherwise ,when Portamento On the function takes the following value: + * - default_fromkey if valid + * - otherwise prev_note(prev_note is the note prior the most recent + * note played). + * Then portamento mode is applied to validate the value choosen. + * Where portamento mode is: + * - each note, a portamento occurs on each note. + * - legato only, portamento only on notes played legato. + * - staccato only, portamento only on notes played staccato. + * 1.3) Otherwise, portamento is off,INVALID_NOTE is returned (portamento is disabled). + * ------ + * 2)The function determines if a legato playing must occur on next noteOn. + * 'fromkey legato note' is returned as a function of default_fromkey, PTC, + * current mono/poly mode,actual 'staccato/legato' playing state and prev_note. + * By order of precedence the result is: + * 2.1) If valid, default_fromkey have precedence over any others values. + * 2.2) Otherwise if CC PTC has been received its value is returned. + * 2.3) Otherwise fromkey legato is determined from the mono/poly mode, + * the actual 'staccato/legato' playing state (FLUID_CHANNEL_LEGATO_PLAYING) and prev_note + * as this: + * - in (poly/Mono) staccato , INVALID_NOTE is returned. + * - in poly legato , actually we don't want playing legato. So + * INVALID_NOTE is returned. + * - in mono legato , prev_note is returned. + * + * On input + * @param chan fluid_channel_t. + * @param defaultFromkey, the defaut 'fromkey portamento' note or 'fromkey legato' + * note (see description above). + * + * @return + * 1)'fromkey portamento' is returned in fluid_synth_t.fromkey_portamento. + * If valid,it means that portamento is enabled . + * + * 2)The 'fromkey legato' note is returned. + * + * Notes about usage: + * The function is intended to be called when the following event occurs: + * - On noteOn (Poly or Mono) after insertion in the monophonic list. + * - On noteOff(mono legato playing). In this case, default_fromkey must be valid. + * + * Typical calling usage: + * - In poly, default_fromkey must be INVALID_NOTE. + * - In mono staccato playing,default_fromkey must be INVALID_NOTE. + * - In mono legato playing,default_fromkey must be valid. + */ +static unsigned char fluid_synth_get_fromkey_portamento_legato(fluid_channel_t* chan, + unsigned char default_fromkey) +{ + unsigned char ptc = fluid_channel_get_cc(chan, PORTAMENTO_CTRL); + if(fluid_channel_is_valid_note(ptc)) + { /* CC PTC has been received */ + fluid_channel_clear_portamento(chan); /* clears the CC PTC receive */ + chan->synth->fromkey_portamento = ptc;/* returns fromkey portamento */ + /* returns fromkey legato */ + if(!fluid_channel_is_valid_note(default_fromkey)) + { + default_fromkey= ptc; + } + } + else + { /* determines and returns fromkey portamento */ + unsigned char fromkey_portamento = INVALID_NOTE; + if(fluid_channel_portamento(chan)) + { /* Portamento when Portamento pedal is On */ + /* 'fromkey portamento'is determined from the portamento mode + and the most recent note played (prev_note)*/ + unsigned char portamentomode = chan->portamentomode; + if(fluid_channel_is_valid_note(default_fromkey)) + { + fromkey_portamento = default_fromkey; /* on each note */ + } + else + { + fromkey_portamento = fluid_channel_prev_note(chan); /* on each note */ + } + if(portamentomode == FLUID_CHANNEL_PORTAMENTO_MODE_LEGATO_ONLY) + { /* Mode portamento:legato only */ + if(!(chan->mode & FLUID_CHANNEL_LEGATO_PLAYING)) + { + fromkey_portamento = INVALID_NOTE; + } + } + else if(portamentomode == FLUID_CHANNEL_PORTAMENTO_MODE_STACCATO_ONLY) + { /* Mode portamento:staccato only */ + if(chan->mode & FLUID_CHANNEL_LEGATO_PLAYING) + { + fromkey_portamento = INVALID_NOTE; + } + } + /* else Mode portamento: on each note (staccato/legato) */ + } + /* Returns fromkey portamento */ + chan->synth->fromkey_portamento = fromkey_portamento; + /* Determines and returns fromkey legato */ + if(!fluid_channel_is_valid_note(default_fromkey)) + { + /* in staccato (poly/Mono) returns INVALID_NOTE */ + /* In mono mode legato playing returns the note prior most + recent note played */ + if (fluid_channel_is_playing_mono(chan) && (chan->mode & FLUID_CHANNEL_LEGATO_PLAYING)) + { + default_fromkey = fluid_channel_prev_note(chan); /* note prior last note */ + } + /* In poly mode legato playing, actually we don't want playing legato. + So returns INVALID_NOTE */ + } + } + return default_fromkey; /* Returns legato fromkey */ +} + +/***************************************************************************** + noteon - noteoff functions in Mono mode +******************************************************************************/ +/* + * noteon - noteoff on a channel in "monophonic playing". + * + * A channel needs to be played monophonic if this channel has been set in + * monophonic mode by basic channel API.(see fluid_synth_polymono.c). + * A channel needs also to be played monophonic if it has been set in + * polyphonic mode and legato pedal is On during the playing. + * When a channel is in "monophonic playing" state, only one note at a time can be + * played in a staccato or legato manner (with or without portamento). + * More informations in FluidPolyMono-0004.pdf chapter 4 (Appendices). + * _______________ + * ________________ | noteon | + * | legato detector| O-->| mono_staccato |--*-> preset_noteon + * noteon_mono ->| (add_monolist) |--O-- |_______________| | (with or without) + * LOCAL |________________| O /|\ | (portamento) + * /|\ set_onenote | | fromkey | + * | | | portamento| + * noteOn poly >---*------------------* | | + * | | | + * | _____ |________ | + * portamento modes >--- | ->| | | + * | | get_fromkey | | + * Porta.on/off >--------------------- | ->|_______________| | + * (PTC) | | | + * | fromkey | fromkey | + * | legato | portamento| + * | _____\|/_______ | + * *-->| noteon |--/ + * | | monopoly | + * | | legato |----> voices + * legato modes >------- | ->|_______________| triggering + * | (with or without) + * | (portamento) + * | + * | + * noteOff poly >---*----------------- | ---------+ + * | clear | | + * _\|/_____________ | | + * | legato detector | O | + * noteoff_mono->|(search_monolist)|-O-- _____\|/_______ + * LOCAL |(remove_monolist)| O-->| noteoff | + * |_________________| | monopoly |----> noteoff + * Sust.on/off >------------------------->|_______________| + * Sost.on/off +------------------------------------------------------------------------------*/ +int fluid_synth_noteoff_monopoly(fluid_synth_t* synth, int chan, int key, + char Mono); + +int fluid_synth_noteon_monopoly_legato(fluid_synth_t* synth, int chan, + int fromkey, int tokey, int vel); + +/** + * Plays a noteon event for a Synth instance in "monophonic playing" state. + * Please see the description above about "monophonic playing". + * _______________ + * ________________ | noteon | + * | legato detector| O-->| mono_staccato |--->preset_noteon + * noteon_mono ->| (add_monolist) |--O-- |_______________| + * LOCAL |________________| O + * | + * | + * | + * | + * | + * | + * | + * | + * | + * | _______________ + * | | noteon | + * +-->| monopoly | + * | legato |---> voices + * |_______________| triggering + * + * The function uses the legato detector (see above) to determine if the note must + * be played staccato or legato. + * + * @param synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param key MIDI note number (0-127). + * @param vel MIDI velocity (0-127). + * @return FLUID_OK on success, FLUID_FAILED otherwise. + */ +int fluid_synth_noteon_mono_LOCAL(fluid_synth_t* synth, int chan, + int key, int vel) +{ + fluid_channel_t* channel = synth->channel[chan]; + + /* Adds the note into the monophonic list */ + fluid_channel_add_monolist(channel,(unsigned char)key,(unsigned char)vel,0); + + /* in Breath Sync mode, the noteon triggering is postponed + until the musician starts blowing in the breath controller */ + if (!(channel->mode & FLUID_CHANNEL_BREATH_SYNC) || + fluid_channel_breath_msb(channel) ) + { + /* legato/staccato playing detection */ + if(channel->mode & FLUID_CHANNEL_LEGATO_PLAYING) + { /* legato playing */ + /* legato from prev_note to key */ + /* the voices from prev_note key number are to be used to play key number */ + /* fromkey must be valid */ + return fluid_synth_noteon_monopoly_legato(synth, chan, + fluid_channel_prev_note(channel), key, vel); + } + else + { /* staccato playing */ + return fluid_synth_noteon_mono_staccato(synth, chan, key, vel); + } + } + else return FLUID_OK; +} + +/** + * Plays a noteoff event for a Synth instance in "monophonic playing" state. + * Please see the description above about "monophonic playing" + * + * _______________ + * | noteon | + * +-->| monopoly | + * | | legato |----> voices + * | |_______________| triggering + * | (with or without) + * | (portamento) + * | + * | + * | + * | + * | + * | + * _________________ | + * | legato detector | O + * noteoff_mono->|(search_monolist)|-O-- _______________ + * LOCAL |(remove_monolist)| O-->| noteoff | + * |_________________| | monopoly |----> noteoff + * |_______________| + * + * The function uses the legato detector (see above) to determine if the noteoff must + * be played staccato or legato. + * + * @param synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param key MIDI note number (0-127). + * @return FLUID_OK on success, FLUID_FAILED otherwise. + */ +int fluid_synth_noteoff_mono_LOCAL(fluid_synth_t* synth, int chan, int key) +{ + int status; + int i,i_prev; + fluid_channel_t* channel = synth->channel[chan]; + /* searching the note in the monophonic list */ + i=fluid_channel_search_monolist(channel, (unsigned char)key , &i_prev); + + if (i >= 0) + { /* the note is in the monophonic list */ + /* Removes the note from the monophonic list */ + fluid_channel_remove_monolist(channel,i , &i_prev); + + /* in Breath Sync mode, the noteoff triggering is done + if the musician is blowing in the breath controller */ + if (!(channel->mode & FLUID_CHANNEL_BREATH_SYNC) || + fluid_channel_breath_msb(channel) ) + { + /* legato playing detection */ + if(channel->mode & FLUID_CHANNEL_LEGATO_PLAYING) + { /* the list contains others notes */ + if(i_prev >= 0) + { /* legato playing detection on noteoff */ + /* legato from key to i_prev key */ + /* the voices from key number are to be used to + play i_prev key number. */ + status = fluid_synth_noteon_monopoly_legato(synth, chan, + key, channel->monolist[i_prev].note, + channel->monolist[i_prev].vel); + } + /* else the note doesn't need to be played off */ + else + { + status = FLUID_OK; + } + } + else + { /* the monophonic list is empty */ + /* plays the monophonic note noteoff and eventually held + by sustain/sostenuto */ + status = fluid_synth_noteoff_monopoly(synth, chan, key, 1); + } + } + else + { + status = FLUID_OK; + } + } + else + { /* the note is not found in the list so the note was + played On when the channel was in polyphonic playing */ + /* plays the noteoff as for polyphonic */ + status = fluid_synth_noteoff_monopoly(synth, chan, key, 0); + } + return status; +} + +/*---------------------------------------------------------------------------- + staccato playing +-----------------------------------------------------------------------------*/ +/** + * Plays noteon for a monophonic note in staccato manner. + * Please see the description above about "monophonic playing". + * _______________ + * | noteon | + * noteon_mono >------------------------>| mono_staccato |----> preset_noteon + * |_______________| (with or without) + * LOCAL /|\ (portamento) + * | fromkey + * | portamento + * | + * | + * ______|________ + * portamento modes >----->| | + * | get_fromkey | + * Porta.on/off >----------------------->|_______________| + * Portamento + * (PTC) + * + * We are in staccato situation (where no previous note have been depressed). + * Before the note been passed to fluid_preset_noteon(), the function must determine + * the from_key_portamento parameter used by fluid_preset_noteon(). + * + * from_key_portamento is returned by fluid_synth_get_fromkey_portamento_legato() function. + * fromkey_portamento is set to valid/invalid key value depending of the portamento + * modes (see portamento mode API) , CC portamento On/Off , and CC portamento control + * (PTC). + * + * @param synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param key MIDI note number (0-127). + * @param vel MIDI velocity (0-127). + * @return FLUID_OK on success, FLUID_FAILED otherwise. + */ +int +fluid_synth_noteon_mono_staccato(fluid_synth_t* synth,int chan,int key,int vel) +{ + fluid_channel_t* channel = synth->channel[chan]; + + /* Before playing a new note, if a previous monophonic note is currently + sustained it needs to be released */ + fluid_synth_release_voice_on_same_note_LOCAL(synth,chan, channel->key_mono_sustained); + /* Get possible 'fromkey portamento' */ + fluid_synth_get_fromkey_portamento_legato( channel, INVALID_NOTE); + /* The note needs to be played by voices allocation */ + return fluid_preset_noteon(channel->preset, synth, chan, key, vel); +} + +/** + * Plays noteoff for a polyphonic or monophonic note + * Please see the description above about "monophonic playing". + * + * + * noteOff poly >---------------------------------+ + * | + * | + * | + * noteoff_mono _____\|/_______ + * LOCAL >------------------------->| noteoff | + * | monopoly |----> noteoff + * Sust.on/off >------------------------->|_______________| + * Sost.on/off + * + * The function has the same behaviour when the noteoff is poly of mono, except + * that for mono noteoff, if any pedal (sustain or sostenuto ) is depressed, the + * key is memorized. This is neccessary when the next mono note will be played + * staccato, as any current mono note currently sustained will need to be released + * (see fluid_synth_noteon_mono_staccato()). + * Note also that for a monophonic legato passage, the function is called only when + * the last noteoff of the passage occurs. That means that if sustain or sostenuto + * is depressed, only the last note of a legato passage will be sustained. + * + * @param synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param key MIDI note number (0-127). + * @param Mono, 1 noteoff on monophonic note. + * 0 noteoff on polyphonic note. + * @return FLUID_OK on success, FLUID_FAILED otherwise. + * + * Note: On return, on monophonic, possible sustained note is memorized in + * key_mono_sustained. Memorization is done here on noteOff. + */ +int fluid_synth_noteoff_monopoly(fluid_synth_t* synth, int chan, int key, + char Mono) +{ + int status = FLUID_FAILED; + fluid_voice_t* voice; + int i; + fluid_channel_t* channel = synth->channel[chan]; + /* Key_sustained is prepared to return no note sustained (INVALID_NOTE) */ + if (Mono) + { + channel->key_mono_sustained = INVALID_NOTE; /* no mono note sustained */ + } + /* noteoff for all voices with same chan and same key */ + for (i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + if (fluid_voice_is_on(voice) && + fluid_voice_get_channel(voice) == chan && + fluid_voice_get_key(voice) == key) + { + if (synth->verbose) + { + int used_voices = 0; + int k; + for (k = 0; k < synth->polyphony; k++) + { + if (!_AVAILABLE(synth->voice[k])) + { + used_voices++; + } + } + FLUID_LOG(FLUID_INFO, "noteoff\t%d\t%d\t%d\t%05d\t%.3f\t%d", + fluid_voice_get_channel(voice), fluid_voice_get_key(voice), 0, + fluid_voice_get_id(voice), + (fluid_curtime() - synth->start) / 1000.0f, + used_voices); + } /* if verbose */ + + fluid_voice_noteoff(voice); + /* noteoff on monophonic note */ + /* Key memorization if the note is sustained */ + if(Mono && + (fluid_voice_is_sustained(voice) || fluid_voice_is_sostenuto(voice))) + { + channel->key_mono_sustained = key; + } + status = FLUID_OK; + } /* if voice on */ + } /* for all voices */ + return status; +} + +/*---------------------------------------------------------------------------- + legato playing +-----------------------------------------------------------------------------*/ +/** + * Plays noteon for a monophonic note played legato. + * Please see the description above about "monophonic playing". + * + * + * _______________ + * portamento modes >----->| | + * | get_fromkey | + * Porta.on/off >----------------------->|_______________| + * Portamento | + * (PTC) | +-->preset_noteon + * fromkey | fromkey | (with or without) + * legato | portamento| (portamento) + * _____\|/_______ | + * | noteon |--+ + * noteon_mono >------------------------>| monopoly | + * LOCAL | legato |----->voices + * |_______________| triggering + * /|\ (with or without) + * | (portamento) + * legato modes >-----------------+ + * + * We are in legato situation (where a previous note has been depressed). + * The function must determine the from_key_portamento and from_key_legato parameters + * used respectively by fluid_preset_noteon() function and voices triggering functions. + * + * from_key_portamento and from_key_legato are returned by + * fluid_synth_get_fromkey_portamento_legato() function. + * fromkey_portamento is set to valid/invalid key value depending of the portamento + * modes (see portamento mode API), CC portamento On/Off, and CC portamento control + * (PTC). + * Then, depending of the legato modes (see legato mode API), the function will call + * the appropriate triggering functions. + * @param synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param fromkey MIDI note number (0-127). + * @param tokey MIDI note number (0-127). + * @param vel MIDI velocity (0-127). + * @return FLUID_OK on success, FLUID_FAILED otherwise. + * + * Note: The voices with key 'fromkey' are to be used to play key 'tokey'. + * The function is able to play legato through Preset Zone(s) (PZ) and + * Instrument Zone(s) (IZ) as far as possible. + * When key tokey is outside the current Instrument Zone, Preset Zone, + * current 'fromkey' voices are released. If necessary new voices + * are restarted when tokey enters inside new Instrument(s) Zones,Preset Zone(s). + * More informations in FluidPolyMono-0004.pdf chapter 4.7 (Appendices). + */ +int fluid_synth_noteon_monopoly_legato(fluid_synth_t* synth, int chan, + int fromkey, int tokey, int vel) +{ + fluid_channel_t* channel = synth->channel[chan]; + unsigned char legatomode = channel->legatomode; + fluid_voice_t* voice; + int i ; + /* Gets possible 'fromkey portamento' and possible 'fromkey legato' note */ + fromkey = fluid_synth_get_fromkey_portamento_legato( channel, (unsigned char)fromkey); + + if (fluid_channel_is_valid_note(fromkey)) for (i = 0; i < synth->polyphony; i++) + { + /* searching fromkey voices: only those who don't have 'note off' */ + voice = synth->voice[i]; + if (fluid_voice_is_on(voice) && + fluid_voice_get_channel(voice) == chan && + fluid_voice_get_key(voice) == fromkey) + { + fluid_zone_range_t * zone_range = voice->zone_range; + /* Ignores voice when there is no instrument zone (i.e no zone_range). Otherwise + checks if tokey is inside the range of the running voice */ + if (zone_range && fluid_zone_inside_range(zone_range, tokey, vel)) + { + switch (legatomode) + { + case FLUID_CHANNEL_LEGATO_MODE_RETRIGGER: /* mode 0 */ + fluid_voice_release(voice); /* normal release */ + break; + + case FLUID_CHANNEL_LEGATO_MODE_MULTI_RETRIGGER: /* mode 1 */ + /* Skip in attack section */ + fluid_voice_update_multi_retrigger_attack(voice,tokey,vel); + + /* Starts portamento if enabled */ + if(fluid_channel_is_valid_note(synth->fromkey_portamento)) + { + /* Sends portamento parameters to the voice dsp */ + fluid_voice_update_portamento(voice, + synth->fromkey_portamento, + tokey); + } + /* The voice is now used to play tokey in legato manner */ + /* Marks this Instrument Zone to be ignored during next + fluid_preset_noteon() */ + zone_range->ignore = TRUE; + break; + + default: /* Invalid mode: this should never happen */ + FLUID_LOG(FLUID_WARN, "Failed to execute legato mode: %d", + legatomode); + return FLUID_FAILED; + } + } + else + { /* tokey note is outside the voice range, so the voice is released */ + fluid_voice_release(voice); + } + } + } + /* May be,tokey will enter in new others Insrument Zone(s),Preset Zone(s), in + this case it needs to be played by voices allocation */ + return fluid_preset_noteon(channel->preset,synth,chan,tokey,vel); +} diff --git a/src/synth/fluid_voice.c b/src/synth/fluid_voice.c index aebb82c7..c1f9bfd7 100644 --- a/src/synth/fluid_voice.c +++ b/src/synth/fluid_voice.c @@ -27,6 +27,7 @@ #include "fluid_sys.h" #include "fluid_sfont.h" #include "fluid_rvoice_event.h" +#include "fluid_defsfont.h" /* used for filter turn off optimization - if filter cutoff is above the specified value and filter q is below the other value, turn filter off */ @@ -241,9 +242,14 @@ delete_fluid_voice(fluid_voice_t* voice) /* fluid_voice_init * * Initialize the synthesis process + * inst_zone, the Instrument Zone contains the sample, Keyrange,Velrange + * of the voice. + * When playing legato (n1,n2) in mono mode, n2 will use n1 voices + * as far as n2 still enters in Keyrange,Velrange of n1. */ int -fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, +fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, + fluid_zone_range_t *inst_zone_range, fluid_channel_t* channel, int key, int vel, unsigned int id, unsigned int start_time, fluid_real_t gain) { @@ -266,6 +272,7 @@ fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, if (voice->sample) fluid_voice_off(voice); + voice->zone_range = inst_zone_range; /* Instrument zone range for legato */ voice->id = id; voice->chan = fluid_channel_get_num(channel); voice->key = (unsigned char) key; @@ -453,14 +460,18 @@ fluid_voice_calculate_gain_amplitude(const fluid_voice_t* voice, fluid_real_t ga return gain * voice->synth_gain / (INT24_MAX * 1.0f); } -void -fluid_voice_calculate_gen_pitch(fluid_voice_t* voice) +/* Useful to return the nominal pitch of a key */ +/* The nominal pitch is dependant of voice->root_pitch,tuning, and + GEN_SCALETUNE generator. + This is useful to set the value of GEN_PITCH generator on noteOn. + This is useful to get the beginning/ending pitch for portamento. +*/ +fluid_real_t fluid_voice_calculate_pitch(fluid_voice_t* voice, int key) { fluid_tuning_t* tuning; - fluid_real_t x; + fluid_real_t x,pitch; - /* The GEN_PITCH is a hack to fit the pitch bend controller into the - * modulator paradigm. Now the nominal pitch of the key is set. + /* Now the nominal pitch of the key is returned. * Note about SCALETUNE: SF2.01 8.1.3 says, that this generator is a * non-realtime parameter. So we don't allow modulation (as opposed * to fluid_voice_gen_value(voice, GEN_SCALETUNE) When the scale tuning is varied, @@ -469,15 +480,22 @@ fluid_voice_calculate_gen_pitch(fluid_voice_t* voice) if (fluid_channel_has_tuning(voice->channel)) { tuning = fluid_channel_get_tuning (voice->channel); x = fluid_tuning_get_pitch (tuning, (int)(voice->root_pitch / 100.0f)); - voice->gen[GEN_PITCH].val = voice->gen[GEN_SCALETUNE].val / 100.0f * - (fluid_tuning_get_pitch (tuning, fluid_voice_get_actual_key(voice)) - x) + x; + pitch = voice->gen[GEN_SCALETUNE].val / 100.0f * + (fluid_tuning_get_pitch (tuning, key) - x) + x; } else { - voice->gen[GEN_PITCH].val = voice->gen[GEN_SCALETUNE].val - * (fluid_voice_get_actual_key(voice) - voice->root_pitch / 100.0f) + voice->root_pitch; + pitch = voice->gen[GEN_SCALETUNE].val + * (key - voice->root_pitch / 100.0f) + voice->root_pitch; } - + return pitch; } +void +fluid_voice_calculate_gen_pitch(fluid_voice_t* voice) +{ + voice->gen[GEN_PITCH].val = fluid_voice_calculate_pitch(voice, fluid_voice_get_actual_key(voice)); +} + + /* * fluid_voice_calculate_runtime_synthesis_parameters * @@ -596,6 +614,17 @@ fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice) fluid_voice_update_param(voice, list_of_generators_to_initialize[n]); } + /* Start portamento if enabled */ + { /* fromkey note comes from "GetFromKeyPortamentoLegato()" detector. + When fromkey is set to ValidNote , portamento is started */ + /* Return fromkey portamento */ + int fromkey = voice->channel->synth->fromkey_portamento; + if(fluid_channel_is_valid_note(fromkey)) + { /* Send portamento parameters to the voice dsp */ + fluid_voice_update_portamento(voice,fromkey, fluid_voice_get_actual_key(voice)); + } + } + /* Make an estimate on how loud this voice can get at any time (attenuation). */ UPDATE_RVOICE_R1(fluid_rvoice_set_min_attenuation_cB, fluid_voice_get_lower_boundary_for_attenuation(voice)); @@ -701,7 +730,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) /* range checking is done in the fluid_pan and fluid_balance functions */ voice->pan = fluid_voice_gen_value(voice, GEN_PAN); voice->balance = fluid_voice_gen_value(voice, GEN_CUSTOM_BALANCE); - + /* left amp */ UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 0, fluid_voice_calculate_gain_amplitude(voice, @@ -798,7 +827,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) case GEN_CUSTOM_FILTERQ: UPDATE_RVOICE_CUSTOM_FILTER1(fluid_iir_filter_set_q, x); break; - + case GEN_MODLFOTOPITCH: fluid_clip(x, -12000.0, 12000.0); UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_pitch, x); @@ -1164,6 +1193,72 @@ int fluid_voice_modulate_all(fluid_voice_t* voice) return FLUID_OK; } +/** legato update functions --------------------------------------------------*/ +/* Updates voice portamento parameters + * + * @voice voice the synthesis voice + * @fromkey the beginning pitch of portamento. + * @tokey the ending pitch of portamento. + * + * The function calculates pitch offset and increment, then these parameters + * are send to the dsp. +*/ +void fluid_voice_update_portamento (fluid_voice_t* voice, int fromkey,int tokey) + +{ + fluid_channel_t* channel= voice->channel; + + /* calculates pitch offset */ + fluid_real_t PitchBeg = fluid_voice_calculate_pitch(voice,fromkey); + fluid_real_t PitchEnd = fluid_voice_calculate_pitch(voice,tokey); + fluid_real_t pitchoffset = PitchBeg - PitchEnd; + + /* Calculates increment countinc */ + /* Increment is function of PortamentoTime (ms)*/ + unsigned int countinc = (unsigned int)(((fluid_real_t)voice->output_rate * + 0.001f * + (fluid_real_t)fluid_channel_portamentotime(channel)) / + (fluid_real_t)FLUID_BUFSIZE +0.5); + + /* Sends portamento parameters to the voice dsp */ + UPDATE_RVOICE2(fluid_rvoice_set_portamento, countinc, pitchoffset); +} + +/*---------------------------------------------------------------*/ +/*legato mode 1: multi_retrigger + * + * Modulates all generators dependent of key,vel. + * Forces the voice envelopes in the attack section (legato mode 1). + * + * @voice voice the synthesis voice + * @tokey the new key to be applied to this voice. + * @vel the new velocity to be applied to this voice. + */ +void fluid_voice_update_multi_retrigger_attack(fluid_voice_t* voice, + int tokey, int vel) +{ + voice->key = tokey; /* new note */ + voice->vel = vel; /* new velocity */ + /* Updates generators dependent of velocity */ + /* Modulates GEN_ATTENUATION (and others ) before calling + fluid_rvoice_multi_retrigger_attack().*/ + fluid_voice_modulate(voice, FALSE, FLUID_MOD_VELOCITY); + + /* Updates generator dependent of voice->key */ + fluid_voice_update_param(voice, GEN_KEYTOMODENVHOLD); + fluid_voice_update_param(voice, GEN_KEYTOMODENVDECAY); + fluid_voice_update_param(voice, GEN_KEYTOVOLENVHOLD); + fluid_voice_update_param(voice, GEN_KEYTOVOLENVDECAY); + + /* Updates pitch generator */ + fluid_voice_calculate_gen_pitch(voice); + fluid_voice_update_param(voice, GEN_PITCH); + + /* updates adsr generator */ + UPDATE_RVOICE0(fluid_rvoice_multi_retrigger_attack); +} +/** end of legato update functions */ + /* Force the voice into release stage. Useful anywhere a voice needs to be damped even if pedals (sustain sostenuto) are depressed. @@ -1183,8 +1278,9 @@ fluid_voice_release(fluid_voice_t* voice) * fluid_voice_noteoff * * Sending a noteoff event will advance the envelopes to section 5 (release). + * The function is convenient for polyphonic or monophonic note */ -int +void fluid_voice_noteoff(fluid_voice_t* voice) { fluid_channel_t* channel; @@ -1201,13 +1297,11 @@ fluid_voice_noteoff(fluid_voice_t* voice) } /* Or sustain a note under Sustain pedal */ else if (fluid_channel_sustained(channel)) { - voice->status = FLUID_VOICE_SUSTAINED; + voice->status = FLUID_VOICE_SUSTAINED; } /* Or force the voice to release stage */ else fluid_voice_release(voice); - - return FLUID_OK; } /* diff --git a/src/synth/fluid_voice.h b/src/synth/fluid_voice.h index d27ea37c..6776fa44 100644 --- a/src/synth/fluid_voice.h +++ b/src/synth/fluid_voice.h @@ -33,7 +33,6 @@ #define NO_CHANNEL 0xff - typedef struct _fluid_overflow_prio_t fluid_overflow_prio_t; struct _fluid_overflow_prio_t @@ -73,6 +72,7 @@ struct _fluid_voice_t fluid_gen_t gen[GEN_LAST]; fluid_mod_t mod[FLUID_NUM_MOD]; int mod_count; + fluid_zone_range_t * zone_range; /* instrument zone range*/ fluid_sample_t* sample; /* Pointer to sample (dupe in rvoice) */ /* basic parameters */ @@ -123,7 +123,8 @@ void fluid_voice_calculate_gen_pitch(fluid_voice_t* voice); int fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf); -int fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, +int fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, + fluid_zone_range_t *inst_zone_range, fluid_channel_t* channel, int key, int vel, unsigned int id, unsigned int time, fluid_real_t gain); @@ -146,8 +147,15 @@ int fluid_voice_set_output_rate(fluid_voice_t* voice, fluid_real_t value); function.*/ void fluid_voice_update_param(fluid_voice_t* voice, int gen); +/** legato modes */ +/* force in the attack section for legato mode multi_retrigger: 1 */ +void fluid_voice_update_multi_retrigger_attack(fluid_voice_t* voice,int tokey, int vel); +/* Update portamento parameter */ +void fluid_voice_update_portamento (fluid_voice_t* voice, int fromkey, int tokey); + + void fluid_voice_release(fluid_voice_t* voice); -int fluid_voice_noteoff(fluid_voice_t* voice); +void fluid_voice_noteoff(fluid_voice_t* voice); void fluid_voice_off(fluid_voice_t* voice); void fluid_voice_stop(fluid_voice_t* voice); void fluid_voice_overflow_rvoice_finished(fluid_voice_t* voice); diff --git a/src/utils/fluid_hash.c b/src/utils/fluid_hash.c index 7dc27032..95b218d2 100644 --- a/src/utils/fluid_hash.c +++ b/src/utils/fluid_hash.c @@ -1209,7 +1209,7 @@ fluid_str_equal (const void *v1, const void *v2) const char *string1 = v1; const char *string2 = v2; - return strcmp (string1, string2) == 0; + return FLUID_STRCMP (string1, string2) == 0; } /** diff --git a/src/utils/fluid_sys.c b/src/utils/fluid_sys.c index 4a0a6dd3..d46c39e0 100644 --- a/src/utils/fluid_sys.c +++ b/src/utils/fluid_sys.c @@ -915,7 +915,7 @@ fluid_istream_gets (fluid_istream_t in, char* buf, int len) * @return Number of bytes written or -1 on error */ int -fluid_ostream_printf (fluid_ostream_t out, char* format, ...) +fluid_ostream_printf (fluid_ostream_t out, const char* format, ...) { char buf[4096]; va_list args; diff --git a/src/utils/fluid_sys.h b/src/utils/fluid_sys.h index c099c23e..33b80794 100644 --- a/src/utils/fluid_sys.h +++ b/src/utils/fluid_sys.h @@ -341,7 +341,7 @@ typedef GModule fluid_module_t; fluid_istream_t fluid_get_stdin (void); fluid_ostream_t fluid_get_stdout (void); int fluid_istream_readline(fluid_istream_t in, fluid_ostream_t out, char* prompt, char* buf, int len); -int fluid_ostream_printf (fluid_ostream_t out, char* format, ...); +int fluid_ostream_printf (fluid_ostream_t out, const char* format, ...); /* The function should return 0 if no error occured, non-zero otherwise. If the function return non-zero, the socket will be diff --git a/src/utils/fluidsynth_priv.h b/src/utils/fluidsynth_priv.h index 54d86fff..2bdda93b 100644 --- a/src/utils/fluidsynth_priv.h +++ b/src/utils/fluidsynth_priv.h @@ -199,6 +199,7 @@ typedef struct _fluid_hashtable_t fluid_hashtable_t; typedef struct _fluid_client_t fluid_client_t; typedef struct _fluid_server_socket_t fluid_server_socket_t; typedef struct _fluid_sample_timer_t fluid_sample_timer_t; +typedef struct _fluid_zone_range_t fluid_zone_range_t; /*************************************************************** *