From 9c4664d162c537ba4dd4fd8220971c0fb727103a Mon Sep 17 00:00:00 2001 From: Florian Jung Date: Sun, 1 Jul 2012 16:42:16 +0000 Subject: final merge --- muse2/AUTHORS | 79 ++- muse2/CMakeLists.txt | 14 +- muse2/ChangeLog | 78 ++- muse2/README | 6 +- muse2/README.de | 38 +- muse2/doc/documentation.tex | 557 +++++++++++++++-- muse2/doc/pics/arrow_tool.png | Bin 0 -> 258 bytes muse2/doc/pics/bad_timing.png | Bin 0 -> 34355 bytes muse2/doc/pics/main_window.png | Bin 0 -> 89649 bytes muse2/doc/pics/main_window_annotated.png | Bin 0 -> 82252 bytes muse2/doc/pics/main_window_with_arrangement.png | Bin 0 -> 169229 bytes muse2/doc/pics/main_window_with_midi_editor.png | Bin 0 -> 140617 bytes muse2/doc/pics/main_window_with_tracks.png | Bin 0 -> 122625 bytes muse2/doc/pics/mixer.png | Bin 0 -> 56812 bytes muse2/doc/pics/mixer_strip.png | Bin 0 -> 13169 bytes muse2/doc/pics/muse2.png | Bin 0 -> 97657 bytes muse2/doc/pics/no_audio.png | Bin 0 -> 32195 bytes muse2/doc/pics/organ_synth.png | Bin 0 -> 66145 bytes muse2/doc/pics/output_routing.png | Bin 0 -> 27489 bytes muse2/grepmidi/grepmidi.cpp | 3 +- muse2/muse/arranger/arranger.cpp | 8 +- muse2/muse/arranger/arranger.h | 2 +- muse2/muse/arranger/pcanvas.cpp | 88 ++- muse2/muse/arranger/pcanvas.h | 2 +- muse2/muse/arranger/tlist.cpp | 186 +++++- muse2/muse/audio.cpp | 110 +++- muse2/muse/audio.h | 22 +- muse2/muse/audiotrack.cpp | 339 ++++++---- muse2/muse/conf.cpp | 19 + muse2/muse/confmport.cpp | 41 +- muse2/muse/ctrl.cpp | 471 +++++++++++++- muse2/muse/ctrl.h | 68 +- muse2/muse/driver/alsamidi.cpp | 10 +- muse2/muse/driver/jack.cpp | 182 ++++-- muse2/muse/driver/jackaudio.h | 2 + muse2/muse/driver/jackmidi.cpp | 38 +- muse2/muse/dssihost.cpp | 173 ++++-- muse2/muse/dssihost.h | 4 + muse2/muse/gconfig.cpp | 2 +- muse2/muse/globals.cpp | 6 + muse2/muse/globals.h | 4 + muse2/muse/midi.cpp | 199 +++++- muse2/muse/midictrl.cpp | 33 + muse2/muse/midictrl.h | 1 + muse2/muse/mididev.cpp | 15 +- muse2/muse/midiseq.cpp | 74 ++- muse2/muse/midiseq.h | 19 +- muse2/muse/mixer/astrip.cpp | 53 +- muse2/muse/mixer/astrip.h | 4 +- muse2/muse/mixer/panknob.cpp | 1 - muse2/muse/mpevent.cpp | 8 +- muse2/muse/mpevent.h | 14 +- muse2/muse/node.cpp | 16 +- muse2/muse/osc.cpp | 4 +- muse2/muse/part.cpp | 61 +- muse2/muse/part.h | 13 +- muse2/muse/plugin.cpp | 624 +++++++++++-------- muse2/muse/plugin.h | 11 +- muse2/muse/seqmsg.cpp | 19 +- muse2/muse/song.cpp | 179 +++++- muse2/muse/song.h | 11 +- muse2/muse/sync.cpp | 174 +++++- muse2/muse/sync.h | 7 +- muse2/muse/tempo.cpp | 86 ++- muse2/muse/tempo.h | 56 +- muse2/muse/thread.cpp | 53 +- muse2/muse/track.cpp | 17 +- muse2/muse/track.h | 5 +- muse2/muse/undo.h | 6 +- muse2/muse/wave.cpp | 55 +- muse2/muse/wavetrack.cpp | 12 +- muse2/muse/widgets/CMakeLists.txt | 3 + muse2/muse/widgets/aboutbox.ui | 4 +- muse2/muse/widgets/bigtime.cpp | 5 +- muse2/muse/widgets/filedialog.cpp | 30 +- muse2/muse/widgets/filedialog.h | 3 + muse2/muse/widgets/midi_audio_control.cpp | 340 ++++++++++ muse2/muse/widgets/midi_audio_control.h | 60 ++ muse2/muse/widgets/midi_audio_control_base.ui | 310 +++++++++ muse2/muse/widgets/midisync.ui | 110 +++- muse2/muse/widgets/midisyncimpl.cpp | 283 ++------- muse2/muse/widgets/musewidgetsplug.cpp | 2 +- muse2/muse/widgets/scldraw.cpp | 5 +- muse2/muse/widgets/sliderbase.cpp | 12 +- muse2/muse/widgets/sliderbase.h | 1 + muse2/share/locale/muse_cs.ts | 795 +++++++++++++++--------- muse2/share/locale/muse_de.ts | 361 ++++++++--- muse2/share/locale/muse_en.ts | 760 +++++++++++++--------- muse2/share/locale/muse_es.ts | 268 ++++++-- muse2/share/locale/muse_fr.ts | 300 ++++++--- muse2/share/locale/muse_pl.ts | 312 +++++++--- muse2/share/locale/muse_ru.ts | 147 ++++- muse2/share/locale/muse_sv_SE.ts | 366 +++++++---- muse2/share/templates/MusE.cfg | 2 +- muse2/synti/deicsonze/deicsonzegui.cpp | 4 +- muse2/synti/deicsonze/deicsonzeplugin.cpp | 8 +- muse2/synti/simpledrums2/simpledrums.cpp | 8 +- muse2/xpm/record_off.xpm | 216 ++----- muse2/xpm/record_on.xpm | 244 +++----- 99 files changed, 6849 insertions(+), 2492 deletions(-) create mode 100644 muse2/doc/pics/arrow_tool.png create mode 100644 muse2/doc/pics/bad_timing.png create mode 100644 muse2/doc/pics/main_window.png create mode 100644 muse2/doc/pics/main_window_annotated.png create mode 100644 muse2/doc/pics/main_window_with_arrangement.png create mode 100644 muse2/doc/pics/main_window_with_midi_editor.png create mode 100644 muse2/doc/pics/main_window_with_tracks.png create mode 100644 muse2/doc/pics/mixer.png create mode 100644 muse2/doc/pics/mixer_strip.png create mode 100644 muse2/doc/pics/muse2.png create mode 100644 muse2/doc/pics/no_audio.png create mode 100644 muse2/doc/pics/organ_synth.png create mode 100644 muse2/doc/pics/output_routing.png create mode 100644 muse2/muse/widgets/midi_audio_control.cpp create mode 100644 muse2/muse/widgets/midi_audio_control.h create mode 100644 muse2/muse/widgets/midi_audio_control_base.ui (limited to 'muse2') diff --git a/muse2/AUTHORS b/muse2/AUTHORS index 3fcac758..22d70210 100644 --- a/muse2/AUTHORS +++ b/muse2/AUTHORS @@ -1,19 +1,80 @@ -Muse -This software has been brought to you by: ++-------------------------------------------+ +| MusE | +| This software has been brought to you by: | ++-------------------------------------------+ Name: user: users sourceforge net --------------- --------- -Administrator: -Werner Schweer wschweer -Developers: -Nil Geisweiller a-lin -Frank Neumann franky -Mathias Lundgren lunar_shuttle -Joachim Schiele qknight +Active Developers: + Robert Jonsson spamatica Tim Donnelly terminator356 Orcan Ogetbil ogetbilo Florian Jung flo93 + +Former Developers: + +Werner Schweer wschweer +Nil Geisweiller a-lin +Frank Neumann franky +Mathias Lundgren lunar_shuttle +Joachim Schiele qknight + Fluidsynth logo designed by Josh "Swami" Green. + + + ++------------------+ +| PGP Public Keys: | ++------------------+ + +Florian Jung: +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.10 (GNU/Linux) + +mQENBE/A9gcBCADKNwa/lSJRz73Vfs+IKmmltxnHqGZQvYGJ32nDQny04hdSzlOQ +XVqyZHYDREpbXHnAPtANJTPozQVWoegxsC9ePEvydw3ezMO9P7hDjanXZvd4egHY +VLR9ZpLvFx7WJDxTQqD1Zaa4sA7ZzSdDu9kv/Bc8Qx2Qtur5T67THV8Pr8izSFSN +MKqR5X5fKqkN0+54hTDnj1YguIloYPKVI6+ns9RGOgvbys4rC1X1EqXdkkRXv3OT +r98udsWNlq+FMmwR+igfzi8+B79V3OAmqUDtpNBAnGgmRW1N6mzv47jRjSX72Ev2 +3HI8GXM2rxrsUyjDuDfgGG2rA0KDnWGpwHZLABEBAAG0JEZsb3JpYW4gSnVuZyA8 +Zmxvcmlhbi5hLmp1bmdAZ214LmRlPokBPgQTAQIAKAUCT8D2BwIbIwUJCWYBgAYL +CQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQc931Ar+c+qX3GAgAuFsDSSItndVq +mPeMtjnQDinWAx//trezRoc3puC4mlrvr8dHD6cZ4n/1dPr58kuuGmS9+CzcDgdo +7HGiY1+rXyZIS2K18Z/YurIfJk7ZRr2QNv0aiuvz5JUh/TunV6OrUqapBb2XBgtW +lDIDlFMQmWQ+pUq3g4FbjWgdoAJp3BDKniO2fAGqBSrWqLFllFhBI/RChdoVWD2v +hbYQw+msbZ/cZjx6rDceOxsYr/ltXmfIHTVNpkQ/B9W84xHT64vqW9Z1wKbJ7yOs +vf1qQ72h6yOTP4Ru17hY6oFNdFkIkkwmJPEmRC0hn2MnvvYOhgl6HwAJc+MM4W/D +5X2lO73StLQkRmxvcmlhbiBKdW5nIDxmbG9yaWFuLmEuanVuZ0B3ZWIuZGU+iQE+ +BBMBAgAoBQJP7gyJAhsjBQkJZgGABgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAK +CRBz3fUCv5z6pZPQB/sHAU2Z1LBVQ+ECw5OygLxXhLJOJzbDmoZpbEMrqkndcDLq +80eyiTx6YfccwqA4NbNItfwMIlVBXg3uCUK9fRKWV3J4BC7geJtkDL2K+7Z/qpbF +V65OnghJtSyLYVSpKInEsKKAmNMZATZsnkdDrdJ7u953eLw1Y5W8aEa+1n4PYG2d +jxySbeMLbvfVDrmDwl87RSH1EpuGQCFX0hNqWpUN9OLmx8IP8/j6/jv9U9+2bqbN +up0O2AkKLNezkGZcXVUzhC8NECd0LTsrGNmSuVo632U2Q0v/AIAAMetqlHfk1DSx +zzB1hNoca6Yosd/+NTnz8jvDqWBiG8aTjU5fdfijtCpGbG9yaWFuIEp1bmcgPGZs +bzkzQHVzZXJzLnNvdXJjZWZvcmdlLm5ldD6JAT4EEwECACgFAk/uEfYCGyMFCQlm +AYAGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEHPd9QK/nPqlHcgH/23gHsd3 +JYUhxAatuVCz1RCcy+iURIBd1Do0kCxCyQVF0NA7Y98AOTLJoyFPNB2Koq0CYunG +InKGKZN8NFCDUFCmSkNu88PkjDRfNKWCq69z/nfE2OlyvEdAdmWn0FY89VFMvhUj +NvG9n8g7s8tZciymRny4otjQjPOEmZUZyt5qm4KPfY58R/F+H1AF0E5V0pjwkBrV +ItI3tsw15bKrvzFddU6pwospRNbyb2dcuedRSbSttnehXGR5w5rr41Myp3slQNVt +TVbJedjWVQWK5ImIzYj8SRmG+dCXIL75RiWE72ctiy65MN8h/gG1PX/Dt/E3f2vG +GaBRR0EHueE9ShK5AQ0ET8D2BwEIAMZEvoloCudmQEZ2L7L6FwzfULDyV1dFojbZ +YiPXXKe4sYrFf7TK5dUsOIsBxQUcUANPzCpFOw84Ii2A7FRDYMzUfyb+KP9vXIVE +W84WQI6zILN3YowhIa+bSTkQEx+7nn2JORnQp74goxxWKY9HaTtN3v6LgjseljSk +YxEh8nLo9l1W3d9AYuMNmSv3amzC+p7P66E/+iFGjmKw0Nlb6eJL9iYkFGt+wjgM +jbm7Rvheq95xtJpYcw6/1iqN7uQdup5ZwAk+PQhDOnM0DAZtbim20OujUP+imUot +oEJAfG1QojKQqn6hr9hz6i/2BdJNwiss4d6ywILryJUnTFzWypcAEQEAAYkBJQQY +AQIADwUCT8D2BwIbDAUJCWYBgAAKCRBz3fUCv5z6paB3B/9UISZmdmZD5ExQan1b +W7Mz/ZvrJe+3jun1C5/ZADvBRgKBYfTA7GbwL6x963ovUXzyWUN16GH7KQca4yaq +oneJk55s9DBXHzcICtSwnojZVXiZEK1765lna8lFvYEBjYWNR7upR4RlwH7N1P1V +AVJ8yNJwNNjUu86CJMZ4rVXIie/o/Y/L2PEHZYiXSDfQtxEBKapXjbN2MP1DK4iq +LexeEJLanIxyWKe1KHFE1W2iPnzVgqfvqv6LuIcMY6vy8/lRXrlne6nEXabMu9tN +EoTXpkh0hYQzx3WP+CIutORts95i6aBAlo5Cv/67z5xBffeWCp9dmt30rlr/JIYy +kkXE +=iCVh +-----END PGP PUBLIC KEY BLOCK----- + diff --git a/muse2/CMakeLists.txt b/muse2/CMakeLists.txt index 12bf4855..3d91aa7e 100644 --- a/muse2/CMakeLists.txt +++ b/muse2/CMakeLists.txt @@ -78,9 +78,9 @@ set(CMAKE_SKIP_RULE_DEPENDENCY TRUE) SET(MusE_VERSION_MAJOR 2) SET(MusE_VERSION_MINOR 0) SET(MusE_VERSION_PATCH 0) -SET(MusE_VERSION "2.0rc2") -SET(MusE_VERSION_FULL "2.0rc2") -SET(MusE_INSTALL_NAME "muse-2.0rc2") +SET(MusE_VERSION "2.0") +SET(MusE_VERSION_FULL "2.0") +SET(MusE_INSTALL_NAME "muse-2.0") SET(MusE_EXEC_NAME "muse2") ## The directory where we will install the shared components: @@ -118,7 +118,7 @@ endif (${SVNVER} STREQUAL "SVNVER-NOTFOUND") option ( ENABLE_LASH "enable LASH Audio Session Handler" ON) option ( ENABLE_OSC "enable Open Sound Control (DSSI also recommended)" ON) option ( ENABLE_DSSI "enable Disposable Soft Synth Interface (OSC also recommended)" ON) -option ( ENABLE_VST "enable VST/win support" OFF) +option ( ENABLE_VST "enable VST/win support (deprecated)" OFF) option ( ENABLE_FLUID "enable fluidsynth softsynth plugins" ON) option ( ENABLE_EXPERIMENTAL "enable building experimental features" OFF) option ( ENABLE_PYTHON "enable Python control support" OFF) @@ -268,10 +268,10 @@ else (ENABLE_DSSI) endif (ENABLE_DSSI) if (ENABLE_VST) - message("VST support enabled") + message("deprecated VST support enabled") set (VST_SUPPORT TRUE) else (ENABLE_VST) - message("VST support disabled") + #message("VST support disabled") set (VST_SUPPORT FALSE) endif (ENABLE_VST) @@ -426,7 +426,7 @@ summary_add("Lash support" HAVE_LASH) summary_add("OSC (Liblo) support" OSC_SUPPORT) summary_add("Python support" PYTHON_SUPPORT) summary_add("DSSI support" DSSI_SUPPORT) -summary_add("VST support" VST_SUPPORT) +#summary_add("VST support" VST_SUPPORT) summary_add("Fluidsynth support" HAVE_FLUIDSYNTH) summary_add("Experimental features" ENABLE_EXPERIMENTAL) summary_show() diff --git a/muse2/ChangeLog b/muse2/ChangeLog index b13301ff..1a526f20 100644 --- a/muse2/ChangeLog +++ b/muse2/ChangeLog @@ -1,3 +1,78 @@ +30.06.2012: + ======================================= + * 2.0 tagged (rj) + ======================================= + - Emergency crash fix: In automation popup midi control 'Clear' item. (Tim) + Cannot delete any actions from menu due to recent December PopupMenu fixes. +29.06.2012: + - Fixed file dialog: disable 'read configuration' for midi files. (Tim) + - Updated swedish translation (rj) + - reworked the REC button somewhat (rj) +28.06.2012: + - External sync fixes: Now have user-selectable of clock filtering and quantization. (Tim) + - Fixed clicking tempo master does not update wave parts. Allow SC_MASTER to call canvas->partsChanged. (Tim) + - Fixed problems with recorded wave part and event sizes, and 'past end' indicator. (Tim) + - Don't resize or destroy wave events upon part resize. (Tim) + - Init support for controlling midi engine from Jack timebase master. Completely disabled for now. (Tim) +26.06.2012: + - Added updated Czech translation from Pavel Fric[pavelfric@seznam.cz] (rj) +23.06.2012: + - automation graphs: deletion with middle-click, fixed automation (flo93) + editing. +18.06.2012: + - Removed printouts about no longer supported VST implementation, + use DSSI implementation instead (rj) + - Some preparations for 2.0 (rj) +16.06.2012: + - MusE as Jack Timebase master: Corrected ticks_per_beat and bar_start_tick fields. (Tim) + - Fixed bug by Geoff B and Flo: Changing a midi port device: We are only interested in altering + tracks using the port being changed in the midi port list. And don't change channel. (Tim) +15.06.2012: + * Sync fix: MusE now records tempo changes from externally clocked midi. (Tim) + Still experimental but non-intrusive: You will be asked at the end of recording if you + want to transfer the recorded tempos to the master tempo graph. + Tested OK so far with ALSA and Jack midi. Best results ATM may be when recording from + position 0, not 'continuing' a recording or skipping 'fwd' or 'rew'. + TODO: Record Jack timebase changes! Find patch someone sent years ago, likely still useful... +11.06.2012: + - Finally! Audio controller playback has smooooth frame resolution even with large audio buffers. (Tim) + CtrlList::value() now accepts a nextFrame*, filled with next event frame, which the + plugin and dssi process use to determine the run sizes. + Bonus! This processing is now guaranteed to be ALIGNED with user's chosen minimum control processing + period in Settings. Before, it was occasionally going to 1, 2, 4 frame runs etc., was not good. + TODO: Sorry, track audio/pan have no 'variable run length' support yet. Choppy with large buffers. + Tip: If a ping-pong effect is desired, try a plugin for now! + - Increased default minimum controller period from 64 to 256. Tests showed 256 is just fine, smooth. +10.06.2012: + - Add midi-to-audio control popups to right-click automation menus. (Tim) + - Track list audio automation popup now can stay open, for colour and 'clear' items. (Tim) +08.06.2012: + * Merge: From branch r2_0_extra. (Tim) + - More automation fixes. In AudioTrack::processAutomationEvents, optimize integer, bool, + discrete controller types to avoid multiple dots on flat straight-line graph segments. (Tim) + - Replaced Song::controllerChange() with a lower-level + CtrlList::guiUpdatePending()/clearGuiUpdatePending(). Checked and cleared in + Song::beat() at the nice slow heartbeat rate instead of direct calls to QWidget::update(). + This means ALL controls should update their graphs now, and at a more reasonable rate. (Tim) + - ARVT_STOP automation events are also recorded now + - When clicking sliders in the plugin's native GUIs or the + mixer with SHIFT held down, straight lines are drawn (no + intermediate control points are recorded) + TODO: does not yet work for muse-native, but customized GUIs (flo) +07.06.2012: + - More automation fixes. (Tim) +06.06.2012: + - Another midi-to-audio mapper classes rewrite. (Tim) +05.06.2012: + - Rewrote midi-to-audio control mapper classes. (Tim...) + - Added save and load midi-to-audio settings. + - Remap or remove mappers when rack items moved or removed. +04.06.2012: + - Side branch r2_0_extra, copied from release_2_0: + * Feature: Midi control of audio paramters. (Tim) + - More important timing, accuracy fixes. +03.06.2012: + - Merged documentation from local source to repository LaTeX document, 80% done (rj) 27.05.2012: * Timing, resolution, accuracy, and automation fixes: (Tim) - Ask audio driver for 'its' current system time instead of wallclock in utils.cpp:curTime(). @@ -154,7 +229,7 @@ use it instead of setValue(). 17.03.2012: - Custom columns can now either affect tick0 or song->cpos() (flo) - - hopefully fixed a big in the custom columns dialog (flo) + - hopefully fixed a bug in the custom columns dialog (flo) 16.03.2012: - Custom columns now send their controller changes to the device automatically if appropriate (flo) @@ -1612,7 +1687,6 @@ * Renamed: Soft synth configuration changed to Synth configuration, as per patch from Geoff King (rj) * Fixed: Shortcuts with modifier keys wasn't working for global shortcuts, should be now (rj) * Added: Shortcuts for changing len of events, default Ctrl+Shift+Left/Right (rj) ->>>>>>> .r459 10.08.2010 * Fixed/Changed: Grid reacts to midi resolution change (rj) 01.08.2010 diff --git a/muse2/README b/muse2/README index 59655059..3650d817 100644 --- a/muse2/README +++ b/muse2/README @@ -99,7 +99,8 @@ details. -------------- There are a few different ways to configure (notice the two dots): - Type "cmake -i .." to run in step-by-step 'Wizard' mode. + Type "cmake -i .." to run in step-by-step 'Wizard' mode, this is the RECOMMENDED way to + setup the build environment. Or type "cmake -L .." to see options, then compose "cmake " yourself. Some are: @@ -210,6 +211,9 @@ details. ============================= - not so many: only some usability quirks we're working on currently + - automation recording displays odd things in the arranger + while recording; the display is corrected once recording is + complete. ==================================================================== Let us know whether MusE works for you !!! diff --git a/muse2/README.de b/muse2/README.de index 04e2c301..75aaf57f 100644 --- a/muse2/README.de +++ b/muse2/README.de @@ -11,7 +11,7 @@ Für Einzelheiten siehe COPYING. ,-----------------------------------------------------------------. | HINWEIS | |-----------------------------------------------------------------| -| Viel Teile des Codes von MusE sind EXPERIMENTELL und können | +| Viele Teile des Codes von MusE sind EXPERIMENTELL und können | | zu Programmabstürzen und wer weiß was sonst noch allem | | führen. | | SIE BENUTZEN DIESE SOFTWARE AUF EIGENES RISIKO! | @@ -26,27 +26,39 @@ Für Einzelheiten siehe COPYING. - qt 4.2.0 oder neuer ftp://ftp.trolltech.com/qt/source - MsE kompiliert nicht mit alter versionen + MsE kompiliert nicht mit älteren Versionen! + + - gcc >= 4.x.x oder clang >= 2.9 + http://gcc.gnu.org/ + http://clang.llvm.org/ - libsndfile >= 1.0.1 - http://www.mega-nerd.com/libsndfile/ + http://www.mega-nerd.com/libsndfile/ + + - libsamplerate + http://www.mega-nerd.com/SRC/ - JACK >= 0.103 - http://jackit.sourceforge.net/ - download: - http://jackit.sourceforge.net/download/ + http://jackaudio.org/ + + - libuuid + e2fsprogs Paket http://e2fsprogs.sourceforge.net/ + Einige Distros bieten das eventuell in einem anderen Paket an. - - fluidsynth-1.0.3 (die synti früher bekannt als "iiwusynth") von - http://savannah.nongnu.org/download/fluid + Optional: - - Linux Kernel mit rtc (RealTimeClock) Treiber (device /dev/rtc) - ("low latency patches" für minimales timing-jitter wären fein) + - fluidsynth >= 1.0.3 (formerly known as iiwusynth) from + http://sourceforge.net/apps/trac/fluidsynth/ - - ALSA von http://www.alsa-project.org/ - Version 0.9.x oder neuer + - liblo Lightweight OSC (Open Sound Control) http://liblo.sourceforge.net/ + - DSSI Disposable Soft Synth Interface. http://dssi.sourceforge.net/ + (Both recommended - DSSI alone will have no native GUIs, OSC alone does nothing, for now.) - - gcc >= 4.x.x + - dssi-vst Unterstützung für DSSI vst plugins http://www.breakfastquay.com/dssi-vst/ + - LASH Audio Session Handler http://lash.nongnu.org/ + Seit neuerem kann LADISH es auch emulieren http://ladish.org/ + - natürlich: eine Soundkarte und/oder irgendwelche Midi Geräte ============================= diff --git a/muse2/doc/documentation.tex b/muse2/doc/documentation.tex index a36fd07c..dabcf738 100644 --- a/muse2/doc/documentation.tex +++ b/muse2/doc/documentation.tex @@ -27,10 +27,20 @@ %% neccessary. %% %% Whenever referring to code symbols, constants or source/header -%% files, please use \texttt{someClass::someSymbol} or \texttt{file.cpp} +%% files, please use \sym{someClass::someSymbol}, \usym{UPPERCASE_THING} +%% or \f{file.cpp}. %% Only specify file paths or namespaces where it would be ambiguous %% otherwise. Specify 'someClass::' until it would disturb the reader -%% and it is obvious. (watch out to prefix _ with \) +%% and it is obvious. you have to replace '_' by {\_} (with the {}!). +%% These macros do automatic hyphenation on Camel-Case, under_-score +%% and scope::-operator boundaries. If you need to insert additional +%% hyphenation points, use {\-}. +%% Example: \sym{someClass::someAb{\-}nor{\-}mal{\-}lyLongName} will +%% hyphenate: some-Class::-some-Ab-nor-mal-ly-Long-Name +%% +%% Whenever referring to URLs, please wrap them in \url{blah}. Key +%% combinations shall look like \key{CTRL+C}. Menu items shall look +%% like \menu{Menu > Submenu > Menu Item}. %% %% Where possible, reference other parts of this documents with %% \label and \ref. Avoid duplicate information under "Internals" by @@ -58,18 +68,343 @@ \documentclass[a4paper]{report} +\usepackage[a4paper, left=2.5cm, right=2.5cm, top=2.5cm, bottom=2.5cm]{geometry} \usepackage[T1]{fontenc} \usepackage[utf8]{inputenc} \usepackage{lmodern} \usepackage[english]{babel} -\author{Florian Jung} +\usepackage{graphicx} +\usepackage{hyphenat} +\usepackage{wrapfig} +\usepackage{fancyhdr} +\pagestyle{fancy} + \lhead{\scriptsize{\slshape\leftmark}} + \chead{} + \rhead{\thepage} + \lfoot{} + \cfoot{} + \rfoot{} + \renewcommand{\headrulewidth}{0.4pt} +\usepackage{ifthen} + +% Hyphenate symbols before each uppercase letter, after each underscore +% (without a "-") and after each ':' (without a "-") +% TODO for any latex crack: also do automatic hyphenation, that is, +% instead of some-Automation-Expression, do some-Au-to-ma-tion-Ex-press-ion +\makeatletter +\newcommand{\camelhyph}[1]{\@fterfirst\c@amelhyph#1\relax } +\newcommand{\underscorehyph}[1]{\@fterfirst\u@nderscorehyph#1\relax } +\def\@fterfirst #1#2{#2#1} +\def\c@amelhyph #1{% +\ifthenelse{\equal{#1}\relax}{}{% Do nothing if the end has been reached + \ifnum`#1=95 \_\hspace{0pt}\else % Check whether #1 is "_", then print _[thin space] + \ifnum`#1=58 :\hspace{0pt}\else + \ifnum`#1>64 + \ifnum`#1<91 \-#1\else#1\fi% Check whether #1 is an uppercase letter, + \else#1\fi + \fi + \fi + % if so, print \-#1, otherwise #1 + \expandafter\c@amelhyph% % insert \c@amelhyph again. +}} +\def\u@nderscorehyph #1{% +\ifthenelse{\equal{#1}\relax}{}{% Do nothing if the end has been reached + \ifnum`#1=95 \_\hspace{0pt}\else % Check whether #1 is "_", then print _\- + \ifnum`#1=58 :\hspace{0pt}\else#1\fi\fi + \expandafter\u@nderscorehyph% % insert \u@nderscorehyph again. +}} +\makeatother + + + +\author{Florian Jung, Robert Jonsson} \title{MusE Documentation} -\begin{document} +\newcommand{\url}[1]{\texttt{#1}} +\newcommand{\key}[1]{\textbf{#1}} +\newcommand{\shell}[1]{\texttt{\textbf{#1}}} +\newcommand{\menu}[1]{\textbf{#1}} +\newcommand{\sym}[1]{\texttt{\camelhyph{#1}}} +\newcommand{\usym}[1]{\texttt{\underscorehyph{#1}}} +\newcommand{\file}[1]{\texttt{\camelhyph{#1}}} +\newcommand{\screenshotwidth}[0]{0.8\textwidth} + + +\begin{document} +\label{Main/Arranger} +\begin{figure}[htp] +\centering +\includegraphics[width=1.0\textwidth]{pics/muse2} +\label{fig:MusE} +\end{figure} +\chapter {What is this?} +You are, if you have printed this document, holding in your hand the +written documentation for the audio and midi sequencer MusE version 2.\\ +\url{http://www.muse-sequencer.org} is MusE's home on the internet where +everything MusE releated should be possible to find, software, this +documentation, forums, mailing lists, bug reporting, FAQs. If you have +this document but not the software head on over there to find what it's +all about. \chapter{User's manual} -someone should really write this! -\section{Basic overview} +\section {Introduction} +\subsection {A brief history of computer audio and MusE} +To quickly summarize over a decades open source development: in 1999 Werner + Schweer released the first version of MusE, muse-0.0.1.tar.gz, in it's first +few releases (actually not few, Werner relentlessly churned out new releases) +MusE was only a midi sequencer. The target was to create a fully fledged +midi sequencer for the Linux operating system. Over the years audio was +added among with other things implemented and sometimes abandoned. +Today MusE is a stable and feature rich music creation environment which +strives to encompass most of the music recording process, creation, editing, +mastering. + +\subsection {Definitions} +\key{CTRL} refers to the control key on the keyboard, e.g. \key{CTRL+C} +means to press and hold the control key while pressing the c key. Make sure +you know where you have it so you won't accidentally lose control +(bad jokes are the best jokes, so say we all!).\\ +\key{SHIFT} refers to the shift key on the keyboard, see above for usage\\ +\key{ALT} refers to the alt key on the keyboard, see above for usage\\ +\shell{\$>} is used as a generic definition for a terminal prompt. When the +manual lists a command that shall be typed, the prompt is not part of the +command.\\ +Keys are always referred to in bold uppercase, e.g. \key{A}. For instance +\key{SHIFT+A} for the key a pressed together with the shift key.\\ +Sometimes terminal examples are written tabbed in with a fixed font to +visualize more closely what something looks like on the screen. +E.g.\\ +\hspace*{1cm}\shell{\$> muse2}\\ + +\subsection {Getting up and running for impatient people} +Install MusE from the repository of your chosen distribution. +To get decent performance start Jack with the following command in a +terminal:\\ +\hspace*{1cm}\shell{\$> jackd -d alsa -d hw:0 -p 256}\\ +Or, if you prefer, use the launcher utility \textbf{QJackCtl} to get some +help starting Jack. +After this, start MusE from the menu or fire up another terminal and type +\shell{muse2}.\\ +If this didn't work out read on for the slighly more complete route for +getting things started. + +\subsection {Getting up and running} +\subsubsection {Installation from binaries} +There are several ways to install MusE depending on your situation. The +most convenient way is to install a prepackaged version from your chosen +distribution. The drawback of this is that it may not be the most recent +version, though often there is a more recent package from a private packager. +\subsubsection {Installation from source} +Building MusE from source is not hard, there are a number of prerequistes +that must be met but the actual building should be painless (ha, famous +last words).\\ +Please follow the README in the source package and/or read the instructions +on the homepage: \url{http://muse-sequencer.org/index.php/Installation} + +\subsubsection {Hardware} +MusE on the Linux platform supports midi through ALSA and Jack-midi and audio +through Jack. For information on what hardware is supported there are some +convenient places to check: +\begin {itemize} +\item Alsa soundcard matrix +\item \url{http://FFADO.org} for firewire devices. +\end {itemize} +Also, as is often a very good approach for Linux and open source, the +various forums available on the internet often contain good information. +Chances are someone has already tried your configuration and/or had your +specific problem and the solution is already written down. +\subsubsection {Launching} +After installation the binary muse2 is installed on the computer. If MusE +was installed from a distribution repository the binary may have a +different name depending on the distribution policies. Most distributions +do however install a menu entry so MusE should be conveniently available +from there. +\subsubsection {Audio preconditions} +In the standard case MusE expects to find and connect to the Jack audio +server \url{http://jackaudio.org}. Make sure jack is installed (if MusE was +installed with a distribution-package Jack will very likely already be +installed) For Jack to run with best performance your system should be +sufficiently tuned to allow it to run with realtime capabilities. The +realtime configuration is configuration of the operating system and roughly +consists of two parts. +\begin {enumerate} +\item By default on most distros only the superuser lets applications setup +realtime capabilities. Please see the APPENDIX for setting up realtime +\item Maximizing performance. A standard linux installation may not able +to reach the performance required by a power user. This requires exchanging +the linux kernel for a so called lowlatency kernel, this is also covered by +the realtime APPENDIX. +\end {enumerate} + +\subsubsection {Running MusE} +Find MusE in the menu or open a terminal and enter muse2. A splash screen +should pop up followed by the main application window and you are off! +\shell{\$> muse2}\\ +If an error like the screenshot below pops up the Jack audio server is +either not running or started as a different user than what you are trying +to start MusE as. +\begin{figure}[htp] +\centering +\includegraphics[width=\screenshotwidth]{pics/no_audio} +\caption{Jack server missing} +\label{fig:no_audio} +\end{figure} +\subsubsection {Midi only} +MusE can be started in Midi-only mode where MusE does not have any external +dependencies apart from ALSA midi. In this case start MusE from a terminal: +\shell{\$> muse2 -a} + +\subsection {Beginners tutorial} +To get a quick grip of what MusE can achieve please follow this beginners +tutorial. +\subsubsection {Setup} +First off, fire up MusE as was described in the previous chapter, making +sure that the jack audio server is started with sufficient configuration +to allow for audio output without breakup. Also make sure your system can +make sound. +\subsubsection {Soft synth test} +With MusE up and running right click in the Track-pane (see screenshot in +~\ref{Main/Arranger}) and select \menu{Add Synth > MESS > Organ}. A Soft +Synth track called Organ-0 should appear as well as a separate GUI for +the synthesizer. + +Now right click once more in the Track-pane and select \menu{Add Midi +Track}. Another track appears called Track-1, you will notice that it is +by default bound to the synth that was just created Organ-0. +\begin{wrapfigure}{r}{0.05\textwidth} +\includegraphics[width=0.05\textwidth]{pics/arrow_tool} +%\hrulefill +\end{wrapfigure} +Now select the drawing tool icon +from the toolbar, alternatively press the shortcut key \key{D}. +Move the mouse over to the arranger canvas as referenced in ~\ref{Main/Arranger} +and point at the midi track, the mouse should have changed to a small pencil. +Draw a Part along the midi track using the mouse. For this exercise it is +not important where or how large the drawn Part is. When you are done double +click on the drawn part. This will open up the Piano Roll editor. To the +left of the Piano Roll there are piano keys in a vertical line, try clicking +on the keys in this virtual keyboard each click should be rewarded with an +organ sound (maybe of questionable quality, a sound nevertheless) +\begin{figure}[htp] +\centering +\includegraphics[width=.5\textwidth]{pics/organ_synth} +\caption{Organ synthesizer} +\label{fig:organ_synth} +\end{figure} + +\subsubsection {Missing sound} +If you got sound from the previous exercise you can carry on to the next, +or keep reading for further enlightenment in case you come upon trouble +later on. If there are no sound we need to do some fault hunting. First +of, click on Arranger window once more and select the Organ-0 track in the +track-pane. +\begin{figure}[ht] +\centering +\includegraphics[width=\screenshotwidth]{pics/main_window_with_midi_editor} +\caption{Midi editor view} +\label{fig:Midi editor} +\end{figure} +Now bring back Piano Roll window and align the windows so you +can see the piano keys as well as the Meter on the Mixer Strip (see the +5 Function by function chapter for more information on these windows). +The result should be something like the following: + +When pressing one of the keys on virtual Keyboard the Meter on the Mixer +Strip should light up in green to visualize that the Synth is making +sound, if it is not try to trace back your steps and and see if you did +anything differently than described. +Now, if the Meter lights up but there is still no sound we need to to +check the routing between the tracks. Click on the Arranger window again +and select the Out 1 track, this is the predefined output which MusE by +default loads at startup, at the bottom of Mixer Strip there are two +buttons looking like tele- jacks, these bring up the inputs and outputs +of the track, click on the right one, the output and make sure that it is +connected to some valid outputs on your system. +\begin{wrapfigure}{r}{0.25\textwidth} +\includegraphics[width=0.25\textwidth]{pics/output_routing} +%\hrulefill +\end{wrapfigure} +Click on the outputs to select them, if you did changes here go back and +try clicking on the Piano Roll keyboard again, hopefully it helped. If there + still are problems make sure your system actually can make sound through +Jack, this is however getting outside the scope of this manual.\\\\ +\textit{This might be the time to bring up the concept of community support. +Open source software could never be what it is without the support given by +individuals on forums and mailinglists, if the information given in this +document is not enough, try googling your problem and/or get in touch with +one of the online forums for MusE or Linux audio in general. See some pointers +in the Support chapter.} + +\subsubsection {Recording} %TODO: walkthrough of recording midi +TBD + + +\section {Basic overview} +In this section we will make a step by step walk-through of all the +different editors, their purpose and what functions they support. + +\subsection{Main/Arranger} + +\label{Main/Arranger} +\begin{figure}[htp] +\centering +\includegraphics[width=\screenshotwidth]{pics/main_window_annotated} +\caption{MusE main window} +\label{fig:Main Window} +\end{figure} +Above is the main window of MusE, the Arranger, this is what greets you +when launching MusE. The Arranger consists of two main parts, the Track-pane +and the Arranger canvas. The Track-pane lists all currently visible tracks +and the Arranger canvas contains all Parts of the composition. The +screenshot above shows an empty project. Below is MusE with a song in +progress, turns out it wasn't a very good song, but for our purposes it +is fine. In the below screenshot there are a lot of tracks visible in the +Track-pane, each have an icon which indicate it's type, wave-trace, input, +output etcetera, more about that later. In the Arranger canvas a number of +parts are visible, the ones in yellow are in this composition wave files, +the multicolored line are different Parts of a drum track. +\label{Main/Arranger} +\begin{figure}[htp] +\centering +\includegraphics[width=\screenshotwidth]{pics/main_window_with_arrangement} +\caption{MusE main window with arrangement} +\label{fig:Main Window with arrangement} +\end{figure} + +\subsection{Mixer} +Choosing \menu{View > Mixer A} or \menu{B} from the menu in the main +window will bring up the mixer as viewed below. The mixer will with all +options enabled show channel strips for all tracks in the current setup, +depending on how far you have gotten this view may become very large, +at which point it may be a good idea to limit what is viewed in the +Mixer. From the view menu all the different kinds of tracks can be +toggled on/off from the mixer. Some may find it a good idea to use the +two mixers A and B setup with different setup and store this in your +song template(s), more about this in the Song Template section. It can +be argued that everything in MusE is a track analogous to the Unix +idiom that everything is a file. +The types of tracks viable in the mixer (and track-pane) are: +\begin{wrapfigure}{r}{0.5\textwidth} +\includegraphics[width=0.5\textwidth]{pics/mixer} +%\hrulefill +\end{wrapfigure} +\begin {itemize} +\item Audio output +\item Audio input +\item Group track +\item Aux track +\item Wave track +\item Synth track +\item Midi track +\end{itemize} + + +There are also a Midi Track variation called Drum Track, they are +however not distinguishable from Midi Tracks in the Mixer. Also the +strips for midi tracks are different in the Mixer than in the +Track-pane view. + + \subsection{Tracks} MusE arranges your music in \emph{tracks} and \emph{parts}. The following section shall provice you an overview of how things are done with MusE. @@ -202,16 +537,16 @@ copied, cloned and deleted independent from other parts. Parts are created by selecting the pencil tool and then drawing onto the right part area in the arranger. You can move them with the arrow -tool, delete them using the \texttt{DEL} key, and a right-click opens +tool, delete them using the \key{DEL} key, and a right-click opens a popup menu. This menu allows you even more stuff, such as setting the part's color, saving the part to disk etc.. You can use -\texttt{CTRL+C} and \texttt{CTRL+V} for copying and pasting parts. -\texttt{CTRL+B} pastes the part as a clone. Pressing \texttt{SHIFT} +\key{CTRL+C} and \key{CTRL+V} for copying and pasting parts. +\key{CTRL+B} pastes the part as a clone. Pressing \key{SHIFT} additionally provides you a dialog which allows you to paste the part multiple times and set more stuff. You can also copy parts with the mouse by moving the part with the mouse -while holding down the \texttt{CTRL} key. +while holding down the \key{CTRL} key. @@ -253,31 +588,94 @@ For details on \emph{why} stuff is done please refer to the following chapter. \section{User controls and automation} \subsection{Handling user input} -\paragraph{Plugins and synthesizers} +\subsubsection{Plugins and synthesizers} +\paragraph{Overview} When the user launches a plugin's GUI, either a MusE-window with the relevant controls is shown, or the native GUI is launched. MusE will communicate with this native GUI through OSC (Open Sound Control). +The relevant classes are \sym{PluginGui}, \sym{PluginIBase} +(in \file{plugin.h}) and \sym{OscIF} (in \sym{osc.h}). -The relevant classes are \texttt{PluginGui}, \texttt{PluginIBase} -(in \texttt{plugin.h}) and \texttt{OscIF} (in \texttt{osc.h}). - -If the user changes a slider, first the corresponding control is +If the user changes a GUI element, first the corresponding control is disabled, making MusE not steadily update it through automation -while the user operates it. \texttt{PluginIBase::setParam} is called, +while the user operates it. Then MusE will update the plugin's parameter +value, and also record the new value. When appropriate, the controller +is enabled again. + +\paragraph{Processing the input, recording} +Upon operating a slider, \sym{PluginIBase::setParam} is called, which usually writes the control change into the ringbuffer -\texttt{PluginI::\_controlFifo}. (\texttt{PluginI::apply()}, -\texttt{DssiSynthIF::getData()} will read this ringbuffer and -do the processing accordingly). Furthermore, the change is written -into a "to be recorded"-list (done by calling \texttt{AudioTrack::recordAutomation}). -This list is processed after recording has finished. %TODO: where and when exactly? +\sym{PluginI::{\_}controlFifo}. (\sym{PluginI::apply()}, +\sym{DssiSynthIF::getData()} will read this ringbuffer and +do the processing accordingly). Furthermore, \sym{AudioTrack::recordAutomation} +is called, which either directly modifies the controller lists or writes +the change into a "to be recorded"-list (\sym{AudioTrack::{\_}recEvents}) +(depending on whether the song is stopped or played). + +The \sym{AudioTrack::{\_}recEvents} list consists of \sym{CtrlRecVal} +items (see \file{ctrl.h}), which hold the following data: +\begin{itemize} +\item the frame where the change occurred +\item the value +\item the type, which can be \usym{ARVT{\_}START}, \usym{ARVT{\_}VAL} or \usym{ARVT{\_}STOP}. + \usym{ARVT{\_}VAL} are written by every \usym{AudioTrack::recordAutomation} + call, \usym{ARVT{\_}START} and \usym{ARVT{\_}STOP} are generated by + \sym{AudioTrack::startAutoRecord} and \sym{stopAutoRecord}, + respectively. +\item and the id of the controller which is affected +\end{itemize} +It is processed when the song is stopped. The call path for this is: +\sym{Song::stopRolling} calls \sym{Song::processAutomationEvents} +calls \sym{AudioTrack::processAutomationEvents}. +This function removes the old events from the track's controller list +and replaces them with the new events from \sym{{\_}recEvents}. In +\usym{AUTO{\_}WRITE} mode, just all controller events within the recorded +range are erased; in \usym{AUTO{\_}TOUCH} mode, the \usym{ARVT{\_}START} +and \usym{ARVT{\_}STOP} types of the \sym{CtrlRecVal} events are used +to determine the range(s) which should be wiped. + +\paragraph{How it's stored} +Automation data is kept % this is copied from +in \sym{AudioTrack::{\_}controller}, which is a \sym{CtrlListList}, % "design decisions" -> "automation" +that is, a list of \sym{CtrlList}s, that is, a list of lists of +controller-objects which hold the control points of the automation graph. +The \sym{CtrlList} also stores whether the list is meant discrete +(a new control point results in a value-jump) or continous (a new control +point results in the value slowly sloping to the new value). +Furthermore, it stores a \sym{{\_}curVal} (accessed by \sym{curVal()}), +which holds the currently active value, which can be different from the +actually stored value because of user interaction. This value is also +used when there is no stored automation data. + +\sym{AudioTrack::addController} and \sym{removeController} are used % TODO: swapControllerIDX?? +to add/remove whole controller types; the most important functions which +access \sym{{\_}controller} are: +\begin{itemize} +\item \sym{processAutomationEvents}, \sym{recordAutomation}, + \sym{startAutoRecord}, \sym{stopAutoRecord}: see above. +\item \sym{seekPrevACEvent}, \sym{seekNextACEvent}, \sym{eraseACEvent}, + \sym{eraseRangeACEvents}, \sym{addACEvent}, \sym{changeACEvent}, + which do the obvious +\item \sym{pluginCtrlVal}, \sym{setPluginCtrlVal}: the first + returns the current value according to the \sym{{\_}controller} + list, the second only sets the \sym{curVal}, but does not + insert any events. +\end{itemize} + +Whenever a \sym{CtrlList} has been manipulated, +\sym{MusEGlobal::song->controllerChange(Track*)} shall be called, +which emits the \sym{MusEGlobal::song->controllerChanged(Track*)} +signal in order to inform any parts of MusE about the change (currently, +only the arranger's part canvas utilizes this). +\paragraph{Enabling and disabling controllers} Disabling the controller is both dependent from the current automation mode and from whether the GUI is native or not. -In \texttt{AUTO\_WRITE} mode, once a slider is touched (for MusE-GUIs) or +In \usym{AUTO{\_}WRITE} mode, once a slider is touched (for MusE-GUIs) or once a OSC control change is received (for native GUIs), the control is disabled until the song is stopped or seeked. -In \texttt{AUTO\_TOUCH} (and currently (r1492) \texttt{AUTO\_READ}, but +In \usym{AUTO{\_}TOUCH} (and currently (r1492) \usym{AUTO{\_}READ}, but that's to be fixed) mode, once a MusE-GUI's slider is pressed down, the corresponding control is disabled. Once the slider is released, the control is re-enabled again. Checkboxes remain in "disabled" mode, @@ -289,31 +687,31 @@ and the last toggle.). For native GUIs, this is a bit tricky, because we don't have direct access to the GUI widgets. That is, we have no way to find out whether the user doesn't touch a control at all, or whether he has it held down, but just doesn't operate it. The current -behaviour for native GUIs is to behave like in \texttt{AUTO\_WRITE} mode. - -The responsible functions are: \texttt{PluginI::oscControl} and -\texttt{DssiSynthIF::oscControl} for handling native GUIs, -\texttt{PluginI::ctrlPressed} and \texttt{ctrlReleased} for MusE -default GUIs and \texttt{PluginI::guiParamPressed}, -\texttt{guiParamReleased}, \texttt{guiSliderPressed} and -\texttt{guiSliderReleased} for MusE GUIs read from a UI file; -\texttt{guiSlider*} obviously handle sliders, while \texttt{guiParam*} +behaviour for native GUIs is to behave like in \usym{AUTO{\_}WRITE} mode. + +The responsible functions are: \sym{PluginI::oscControl} and +\sym{DssiSynthIF::oscControl} for handling native GUIs, +\sym{PluginI::ctrlPressed} and \sym{ctrlReleased} for MusE +default GUIs and \sym{PluginI::guiParamPressed}, +\sym{guiParamReleased}, \sym{guiSliderPressed} and +\sym{guiSliderReleased} for MusE GUIs read from a UI file; +\sym{guiSlider*} obviously handle sliders, while \sym{guiParam*} handle everything else which is not a slider. They call -\texttt{PluginI::enableController} to enable/disable it. +\sym{PluginI::enableController} to enable/disable it. -Furthermore, on every song stop or seek, \texttt{PluginI::enableAllControllers} +Furthermore, on every song stop or seek, \sym{PluginI::enableAllControllers} is called, which re-enables all controllers again. The call paths for this are: \begin{itemize} -\item For stop: \texttt{Song::stopRolling} calls - \texttt{Song::processAutomationEvents} calls - \texttt{Song::clearRecAutomation} calls - \texttt{Track::clearRecAutomation} calls - \texttt{PluginI::enableAllControllers} -\item For seek: \texttt{Audio::seek} sends a message ("\texttt{G}") to - \texttt{Song::seqSignal} which calls - \texttt{Song::clearRecAutomation} which calls - \texttt{PluginI::enableAllControllers} +\item For stop: \sym{Song::stopRolling} calls + \sym{Song::processAutomationEvents} calls + \sym{Song::clearRecAu{\-}to{\-}ma{\-}tion} calls + \sym{Track::clearRecAutomation} calls + \sym{PluginI::enableAllControllers} +\item For seek: \sym{Audio::seek} sends a message ("\sym{G}") to + \sym{Song::seqSignal} which calls + \sym{Song::clearRecAutomation} which calls + \sym{PluginI::enableAllControllers} \end{itemize} @@ -323,15 +721,15 @@ this are: \section{Automation} As of revision 1490, automation is handled in two ways: User-generated (live) automation data (generated by the user moving sliders while playing) -is fed into \texttt{PluginI::\_controlFifo}. Automation data is kept -in \texttt{AudioTrack::\_controller}, which is a \texttt{CtrlListList}, -that is, a list of \texttt{CtrlList}s, that is, a list of lists of +is fed into \sym{PluginI::{\_}controlFifo}. Automation data is kept +in \sym{AudioTrack::{\_}controller}, which is a \sym{CtrlListList}, +that is, a list of \sym{CtrlList}s, that is, a list of lists of controller-objects which hold the control points of the automation graph. -The \texttt{CtrlList} also stores whether the list is meant discrete +The \sym{CtrlList} also stores whether the list is meant discrete (a new control point results in a value-jump) or continous (a new control point results in the value slowly sloping to the new value). -While \texttt{PluginI::\_controlFifo} can be queried very quickly and +While \sym{PluginI::{\_}controlFifo} can be queried very quickly and thus is processed with a very high resolution (only limited by the minimum control period setting), the automation value are expensive to query, and are only processed once in an audio \emph{driver} period. @@ -340,16 +738,16 @@ This might lead to noticeable jumps in value. This could possibly be solved in two ways: \paragraph{Maintaining a slave control list} This approach would maintain a fully redundant slave control list, -similar to \texttt{PluginI::\_controlFifo}. This list must be updated +similar to \sym{PluginI::{\_}controlFifo}. This list must be updated every time any automation-related thing is changed, and shall contain every controller change as a tuple of controller number and value. -This could be processed in the same loop as \texttt{PluginI::\_controlFifo}, +This could be processed in the same loop as \sym{PluginI::{\_}controlFifo}, making it comfortable to implement; furthermore, it allows to cleanly offer automation-settings at other places in future (such as storing automation data in parts or similar). \paragraph{Holding iterators} -We also could hold a list of iterators of the single \texttt{CtrlList}s. +We also could hold a list of iterators of the single \sym{CtrlList}s. This would also cause low CPU usage, because usually, the iterators only need to be incremented once. However, it is pretty complex to implement, because the iterators may become totally wrong (because of a seek in the @@ -550,5 +948,62 @@ slots shall either also display the key change (if they're score slots) or display a gap. Events which happen at the same time shall be at the same x-coordinate, regardless which slot they are. +\section{Controller master values} +All controllers (MIDI-controllers and also automation controllers) +shall have one set of "master values" which allow you to set a gain and +a bias. Instead of the actual set value, $\textrm{value} * \textrm{bias} ++ textrm{bias}$ shall be sent to the MIDI device / the plugin. For +controllers like "pan", the unbiased values shall be transformed, that +is, a pan of 64, with $\textrm{bias}=2$ and $\textrm{gain}=0.5$, shall +be transformed to 66 (because 64 is actually 0, while 0 is actually -64). +These values shall be set in the arranger and whereever the actual +controller/automation values can be edited. + +\section{Enabled-indicator while recording} +The MusE-plugin-GUIs shall display a small LED displaying whether a +controller is currently enabled or disabled. By clicking this LED, the +enabled state shall be switched. + +Furthermore, there shall be a dedicated window which only lets you switch +enabled/disabled states. This will be useful when using external GUIs +or the MIDI-controller-to-automation feature, to re-enable a controller +when in \usym{AUTO{\_}TOUCH} mode. + + + +\section{Linear automation editing} +While holding some modifier key (like shift), operating the MusE-native- +GUI sliders shall only generate control points when clicking and when +releasing the slider. This will result in linear graphs for continous +controllers, and in large steps for discrete controllers (which is in +particular useful for stuff like "which low/high-pass filter type to use"). + +Maybe make this behaviour default for discrete controllers? + + \end{document} + +% TODO: song type etc? kill it! +% song len box: same + +% TODO: unified automation and midi ctrls: +% both shall be editable through the same editors +% four modes: 1. discrete +% 2. continous (plus a global and per-port setting for the max rate) +% 3. switch (not necessarily ON/OFF; signals with color plus text annotation) +% 4. raw (no graph, instead a box with the value sent out (for "all voices off") +% they shall be copy-and-pastable, at least between compatible modes +% they shall be slotted, like pianoroll +% maybe also "overlapping", like arranger (?) +% midi recording tries to make out straight lines (like non-ended automation streams) +% + + +% new song (in general: load as template) plus "overwrite port config" +% should re-create the default jack devices and autoconnect them. + +% what's audio aux for? + +% bug in arranger/pcanvas/automation: if a controlpoint is directly on +% a line of another ctrl graph, you can't click it diff --git a/muse2/doc/pics/arrow_tool.png b/muse2/doc/pics/arrow_tool.png new file mode 100644 index 00000000..1ae872fb Binary files /dev/null and b/muse2/doc/pics/arrow_tool.png differ diff --git a/muse2/doc/pics/bad_timing.png b/muse2/doc/pics/bad_timing.png new file mode 100644 index 00000000..bf5c5c4f Binary files /dev/null and b/muse2/doc/pics/bad_timing.png differ diff --git a/muse2/doc/pics/main_window.png b/muse2/doc/pics/main_window.png new file mode 100644 index 00000000..9739e0a7 Binary files /dev/null and b/muse2/doc/pics/main_window.png differ diff --git a/muse2/doc/pics/main_window_annotated.png b/muse2/doc/pics/main_window_annotated.png new file mode 100644 index 00000000..f8f7701f Binary files /dev/null and b/muse2/doc/pics/main_window_annotated.png differ diff --git a/muse2/doc/pics/main_window_with_arrangement.png b/muse2/doc/pics/main_window_with_arrangement.png new file mode 100644 index 00000000..8d391d9f Binary files /dev/null and b/muse2/doc/pics/main_window_with_arrangement.png differ diff --git a/muse2/doc/pics/main_window_with_midi_editor.png b/muse2/doc/pics/main_window_with_midi_editor.png new file mode 100644 index 00000000..435cc655 Binary files /dev/null and b/muse2/doc/pics/main_window_with_midi_editor.png differ diff --git a/muse2/doc/pics/main_window_with_tracks.png b/muse2/doc/pics/main_window_with_tracks.png new file mode 100644 index 00000000..d69ee427 Binary files /dev/null and b/muse2/doc/pics/main_window_with_tracks.png differ diff --git a/muse2/doc/pics/mixer.png b/muse2/doc/pics/mixer.png new file mode 100644 index 00000000..22d9f2b7 Binary files /dev/null and b/muse2/doc/pics/mixer.png differ diff --git a/muse2/doc/pics/mixer_strip.png b/muse2/doc/pics/mixer_strip.png new file mode 100644 index 00000000..5bfd757b Binary files /dev/null and b/muse2/doc/pics/mixer_strip.png differ diff --git a/muse2/doc/pics/muse2.png b/muse2/doc/pics/muse2.png new file mode 100644 index 00000000..84a28d8a Binary files /dev/null and b/muse2/doc/pics/muse2.png differ diff --git a/muse2/doc/pics/no_audio.png b/muse2/doc/pics/no_audio.png new file mode 100644 index 00000000..1e62245c Binary files /dev/null and b/muse2/doc/pics/no_audio.png differ diff --git a/muse2/doc/pics/organ_synth.png b/muse2/doc/pics/organ_synth.png new file mode 100644 index 00000000..a81ff876 Binary files /dev/null and b/muse2/doc/pics/organ_synth.png differ diff --git a/muse2/doc/pics/output_routing.png b/muse2/doc/pics/output_routing.png new file mode 100644 index 00000000..08af67fa Binary files /dev/null and b/muse2/doc/pics/output_routing.png differ diff --git a/muse2/grepmidi/grepmidi.cpp b/muse2/grepmidi/grepmidi.cpp index 6e1aabbd..4cb0632f 100644 --- a/muse2/grepmidi/grepmidi.cpp +++ b/muse2/grepmidi/grepmidi.cpp @@ -128,7 +128,6 @@ int grepTrack(FILE* f, int trackno) { // printf("TRACK %d\n", trackno); int mtype, mlen; - int b; char* buffer; char tmp[4]; @@ -156,7 +155,7 @@ int grepTrack(FILE* f, int trackno) case 0xa0: case 0xb0: case 0xe0: - b = getc(f); + getc(f); ++cpos; case 0xc0: case 0xd0: diff --git a/muse2/muse/arranger/arranger.cpp b/muse2/muse/arranger/arranger.cpp index 29e69582..8d786311 100644 --- a/muse2/muse/arranger/arranger.cpp +++ b/muse2/muse/arranger/arranger.cpp @@ -531,7 +531,7 @@ Arranger::Arranger(ArrangerView* parent, const char* name) connect(canvas, SIGNAL(dropMidiFile(const QString&)), SIGNAL(dropMidiFile(const QString&))); connect(canvas, SIGNAL(toolChanged(int)), SIGNAL(toolChanged(int))); - connect(MusEGlobal::song, SIGNAL(controllerChanged(MusECore::Track*)), SLOT(controllerChanged(MusECore::Track*))); + connect(MusEGlobal::song, SIGNAL(controllerChanged(MusECore::Track*, int)), SLOT(controllerChanged(MusECore::Track*, int))); configChanged(); // set configuration values if(canvas->part()) @@ -677,7 +677,7 @@ void Arranger::songChanged(int type) // Keep this light, partsChanged is a heavy move! TEST p4.0.36 Try these, may need more. if(type & (SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED | - SC_SIG | SC_TEMPO)) // Maybe sig. Requires tempo. + SC_SIG | SC_TEMPO | SC_MASTER)) // Maybe sig. Requires tempo. canvas->partsChanged(); if (type & SC_SIG) @@ -1110,9 +1110,9 @@ void Arranger::clear() // emit redirectWheelEvent(ev); // } -void Arranger::controllerChanged(MusECore::Track *t) +void Arranger::controllerChanged(MusECore::Track *t, int ctrlId) { - canvas->controllerChanged(t); + canvas->controllerChanged(t, ctrlId); } //--------------------------------------------------------- diff --git a/muse2/muse/arranger/arranger.h b/muse2/muse/arranger/arranger.h index e51ec068..60390a8f 100644 --- a/muse2/muse/arranger/arranger.h +++ b/muse2/muse/arranger/arranger.h @@ -186,7 +186,7 @@ class Arranger : public QWidget { void setTool(int); void updateTrackInfo(int flags); void configChanged(); - void controllerChanged(MusECore::Track *t); + void controllerChanged(MusECore::Track *t, int ctrlId); void focusCanvas(); public: diff --git a/muse2/muse/arranger/pcanvas.cpp b/muse2/muse/arranger/pcanvas.cpp index cc23b59b..c5c3ca6d 100644 --- a/muse2/muse/arranger/pcanvas.cpp +++ b/muse2/muse/arranger/pcanvas.cpp @@ -904,12 +904,25 @@ bool PartCanvas::mousePress(QMouseEvent* event) } } case AutomationTool: - if (event->button() & Qt::RightButton) { - QMenu *automationMenu = new QMenu(this); - QAction* act; - act = automationMenu->addAction(tr("Remove selected")); - act = automationMenu->exec(event->globalPos()); - if (act && automation.currentTrack) { + if (event->button() & Qt::RightButton || + event->button() & Qt::MidButton) { + + bool do_delete; + + if (event->button() & Qt::MidButton) // mid-click + do_delete=true; + else // right-click + { + QMenu *automationMenu = new QMenu(this); + QAction* act; + act = automationMenu->addAction(tr("Remove selected")); + act = automationMenu->exec(event->globalPos()); + if (act) + do_delete=true; + else + do_delete=false; + } + if (do_delete && automation.currentTrack) { foreach(int frame, automation.currentCtrlFrameList) MusEGlobal::audio->msgEraseACEvent((MusECore::AudioTrack*)automation.currentTrack, automation.currentCtrlList->id(), frame); @@ -2683,6 +2696,7 @@ void PartCanvas::cmd(int cmd) case 0: paste_mode=PASTEMODE_MIX; break; case 1: paste_mode=PASTEMODE_MOVEALL; break; case 2: paste_mode=PASTEMODE_MOVESOME; break; + default: paste_mode=PASTEMODE_MIX; // shall never be executed } paste(paste_dialog->clone, paste_mode, paste_dialog->all_in_one_track, @@ -3411,7 +3425,7 @@ void PartCanvas::drawTopItem(QPainter& p, const QRect& rect) yy += th; } - unsigned int startPos = MusEGlobal::audio->getStartRecordPos().tick(); + unsigned int startPos = MusEGlobal::extSyncFlag.value() ? MusEGlobal::audio->getStartExternalRecTick() : MusEGlobal::audio->getStartRecordPos().tick(); if (MusEGlobal::song->punchin()) startPos=MusEGlobal::song->lpos(); int startx = mapx(startPos); @@ -3663,19 +3677,36 @@ void PartCanvas::drawAutomation(QPainter& p, const QRect& rr, MusECore::AudioTra bool checkIfOnLine(double mouseX, double mouseY, double firstX, double lastX, double firstY, double lastY, int circumference) { - double proportion = (mouseX-firstX)/(lastX-firstX); - - // 10 X(15) 20 - // proportion = 0.5 - // 10 - // / - // Y(5) - // / - // 1 - double calcY = (lastY-firstY)*proportion+firstY; - if(ABS(calcY-mouseY) < circumference || (lastX == firstX && ABS(mouseX-lastX) < circumference)) - return true; - return false; + if (lastX==firstX) + return (ABS(mouseX-lastX) < circumference); + else if (mouseX < firstX || mouseX > lastX+circumference) // (*) + return false; + else + { + double proportion = (mouseX-firstX)/(lastX-firstX); // a value between 0 and 1, where firstX->0 and lastX->1 + double calcY = (lastY-firstY)*proportion+firstY; // where the drawn line's y-coord is at mouseX + double slope = (lastY-firstY)/(lastX-firstX); + + return (ABS(calcY-mouseY) < (circumference * sqrt(1+slope*slope))); + // this is equivalent to circumference / cos( atan(slope) ). to + // verify, draw a sloped line (the graph), a 90°-line to it with + // length "circumference". from the (unconnected) endpoint of that + // line, draw a vertical line down to the sloped line. + // use slope=tan(alpha) <==> alpha=atan(slope) and + // cos(alpha) = adjacent side / hypothenuse (hypothenuse is what we + // want, and adjacent = circumference). + // to optimize: this looks similar to abs(slope)+1 + + //return (ABS(calcY-mouseY) < circumference); + } + + /* without the +circumference in the above if statement (*), moving + * the mouse towards a control point from the right would result in + * the line segment from the targeted point to the next to be con- + * sidered, but not the segment from the previous to the targeted. + * however, only points for which the line segment they _end_ is + * under the cursor are considered, so we need to enlengthen this + * a bit (flo93)*/ } //--------------------------------------------------------- @@ -3684,12 +3715,7 @@ bool checkIfOnLine(double mouseX, double mouseY, double firstX, double lastX, do bool checkIfNearPoint(int mouseX, int mouseY, int eventX, int eventY, int circumference) { - int x1 = ABS(mouseX - eventX) ; - int y1 = ABS(mouseY - eventY); - if (x1 < circumference && y1 < circumference) { - return true; - } - return false; + return (ABS(mouseX - eventX) < circumference && ABS(mouseY - eventY) < circumference); } //--------------------------------------------------------- @@ -3703,7 +3729,7 @@ bool checkIfNearPoint(int mouseX, int mouseY, int eventX, int eventY, int circum // controller added. //--------------------------------------------------------- -void PartCanvas::checkAutomation(MusECore::Track * t, const QPoint &pointer, bool NOTaddNewCtrl) +void PartCanvas::checkAutomation(MusECore::Track * t, const QPoint &pointer, bool /*NOTaddNewCtrl*/) { if (t->isMidiTrack()) return; @@ -3751,7 +3777,7 @@ void PartCanvas::checkAutomation(MusECore::Track * t, const QPoint &pointer, boo } else // we have automation, loop through it { - for (; ic !=cl->end(); ic++) + for (; ic!=cl->end(); ic++) { double y = ic->second.val; if (cl->valueType() == MusECore::VAL_LOG ) { // use db scale for volume @@ -3774,7 +3800,7 @@ void PartCanvas::checkAutomation(MusECore::Track * t, const QPoint &pointer, boo eventOldX = eventX; eventOldY = eventY; - + if (onLine) { if (!onPoint) { QWidget::setCursor(Qt::CrossCursor); @@ -3799,7 +3825,7 @@ void PartCanvas::checkAutomation(MusECore::Track * t, const QPoint &pointer, boo // check if we are reasonably close to a line, we only need to check Y // as the line is straight after the last controller //printf("post oldX:%d oldY:%d xpixel:%d ypixel:%d currX:%d currY:%d\n", oldX, oldY, xpixel, ypixel, currX, currY); - if(mouseX >= eventX && eventY == eventOldY && ABS(mouseY-eventY) < circumference) { + if(mouseX >= eventX && ABS(mouseY-eventY) < circumference) { QWidget::setCursor(Qt::CrossCursor); automation.controllerState = addNewController; automation.currentCtrlList = cl; @@ -3817,7 +3843,7 @@ void PartCanvas::checkAutomation(MusECore::Track * t, const QPoint &pointer, boo setCursor(); } -void PartCanvas::controllerChanged(MusECore::Track* t) +void PartCanvas::controllerChanged(MusECore::Track* t, int) { redraw((QRect(0, mapy(t->y()), width(), rmapy(t->height())))); // TODO Check this - correct? } diff --git a/muse2/muse/arranger/pcanvas.h b/muse2/muse/arranger/pcanvas.h index ab227eb2..1b766c5d 100644 --- a/muse2/muse/arranger/pcanvas.h +++ b/muse2/muse/arranger/pcanvas.h @@ -184,7 +184,7 @@ class PartCanvas : public Canvas { public slots: void redirKeypress(QKeyEvent* e) { keyPress(e); } - void controllerChanged(MusECore::Track *t); + void controllerChanged(MusECore::Track *t, int CtrlId); }; } // namespace MusEGui diff --git a/muse2/muse/arranger/tlist.cpp b/muse2/muse/arranger/tlist.cpp index 3d831ba9..05e23321 100644 --- a/muse2/muse/arranger/tlist.cpp +++ b/muse2/muse/arranger/tlist.cpp @@ -22,6 +22,8 @@ #include +#include +#include #include #include #include @@ -35,6 +37,7 @@ #include #include #include +#include #include "popupmenu.h" #include "globals.h" @@ -64,6 +67,8 @@ #include "menutitleitem.h" #include "arranger.h" #include "undo.h" +#include "midi_audio_control.h" +#include "ctrl.h" #ifdef DSSI_SUPPORT #include "dssihost.h" @@ -1409,18 +1414,21 @@ MusECore::TrackList TList::getRecEnabledTracks() void TList::changeAutomation(QAction* act) { - if ( (editAutomation->type() == MusECore::Track::MIDI) || (editAutomation->type() == MusECore::Track::DRUM) || (editAutomation->type() == MusECore::Track::NEW_DRUM) ) { - printf("this is wrong, we can't edit automation for midi tracks from arranger yet!\n"); + if(!editAutomation || editAutomation->isMidiTrack()) + return; + if(act->data().toInt() == -1) return; - } int colindex = act->data().toInt() & 0xff; - int id = (act->data().toInt() & 0x00ffffff) / 256; + int id = (act->data().toInt() & 0x00ffffff) >> 8; + // Is it the midi control action or clear action item? + if (colindex == 254 || colindex == 255) + return; + if (colindex < 100) return; // this was meant for changeAutomationColor // one of these days I'll rewrite this so it's understandable // this is just to get it up and running... - MusECore::CtrlListList* cll = ((MusECore::AudioTrack*)editAutomation)->controller(); for(MusECore::CtrlListList::iterator icll =cll->begin();icll!=cll->end();++icll) { MusECore::CtrlList *cl = icll->second; @@ -1435,13 +1443,81 @@ void TList::changeAutomation(QAction* act) //--------------------------------------------------------- void TList::changeAutomationColor(QAction* act) { - if ( (editAutomation->type() == MusECore::Track::MIDI) || (editAutomation->type() == MusECore::Track::DRUM) || (editAutomation->type() == MusECore::Track::NEW_DRUM) ) { - printf("this is wrong, we can't edit automation for midi tracks from arranger yet!\n"); + if(!editAutomation || editAutomation->isMidiTrack()) + return; + if(act->data().toInt() == -1) return; - } int colindex = act->data().toInt() & 0xff; - int id = (act->data().toInt() & 0x00ffffff) / 256; + int id = (act->data().toInt() & 0x00ffffff) >> 8; + // Is it the clear midi control action item? + if(colindex == 254) + { + MusECore::AudioTrack* track = static_cast(editAutomation); + MusECore::MidiAudioCtrlMap* macp = track->controller()->midiControls(); + MusECore::AudioMidiCtrlStructMap amcs; + macp->find_audio_ctrl_structs(id, &amcs); + if(!amcs.empty()) + MusEGlobal::audio->msgIdle(true); // Gain access to structures, and sync with audio + for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) + macp->erase(*iamcs); + if(!amcs.empty()) + MusEGlobal::audio->msgIdle(false); + + // Hm, need to remove the 'clear' item, and the status lines below it. Try this: + QActionGroup* midi_actgrp = act->actionGroup(); + if(midi_actgrp) + { + QList act_list = midi_actgrp->actions(); + int sz = act_list.size(); + for(int i = 0; i < sz; ++i) + { + QAction* list_act = act_list.at(i); + ///midi_actgrp->removeAction(list_act); + // list_act has no parent now. + ///delete list_act; + list_act->setVisible(false); // HACK Cannot delete any actions! Causes crash with our PopupMenu due to recent fixes. + } + } + return; + } + + // Is it the midi control action item? + if(colindex == 255) + { + MusECore::AudioTrack* track = static_cast(editAutomation); + MusECore::MidiAudioCtrlMap* macm = track->controller()->midiControls(); + MusECore::AudioMidiCtrlStructMap amcs; + macm->find_audio_ctrl_structs(id, &amcs); + + int port = -1, chan = 0, ctrl = 0; + for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) + { + macm->hash_values((*iamcs)->first, &port, &chan, &ctrl); + break; // Only a single item for now, thanks! + } + + MidiAudioControl* pup = new MidiAudioControl(port, chan, ctrl); + + if(pup->exec() == QDialog::Accepted) + { + MusEGlobal::audio->msgIdle(true); // Gain access to structures, and sync with audio + // Erase all for now. + for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) + macm->erase(*iamcs); + + port = pup->port(); chan = pup->chan(); ctrl = pup->ctrl(); + if(port >= 0 && chan >=0 && ctrl >= 0) + // Add will replace if found. + macm->add_ctrl_struct(port, chan, ctrl, MusECore::MidiAudioCtrlStruct(id)); + + MusEGlobal::audio->msgIdle(false); + } + + delete pup; + return; + } + if (colindex > 100) return; // this was meant for changeAutomation // one of these days I'll rewrite this so it's understandable @@ -1461,7 +1537,10 @@ void TList::changeAutomationColor(QAction* act) //--------------------------------------------------------- PopupMenu* TList::colorMenu(QColor c, int id, QWidget* parent) { - PopupMenu * m = new PopupMenu(parent); //, true); //TODO + PopupMenu * m = new PopupMenu(parent, true); + + QActionGroup* col_actgrp = new QActionGroup(m); + col_actgrp->setExclusive(true); for (int i = 0; i< 6; i++) { QPixmap pix(10,10); QPainter p(&pix); @@ -1469,14 +1548,52 @@ PopupMenu* TList::colorMenu(QColor c, int id, QWidget* parent) p.setPen(Qt::black); p.drawRect(0,0,10,10); QIcon icon(pix); - QAction *act = m->addAction(icon,""); + QAction *act = col_actgrp->addAction(icon,""); act->setCheckable(true); if (c == collist[i]) act->setChecked(true); - int data = id * 256; // shift 8 bits - data += i; // color in the bottom 8 bits - act->setData(data); + act->setData((id<<8) + i); // Shift 8 bits. Color in the bottom 8 bits. } + m->addActions(col_actgrp->actions()); + + //m->addSeparator(); + m->addAction(new MenuTitleItem(tr("Midi control"), m)); + + if(editAutomation && !editAutomation->isMidiTrack()) + { + QAction *act = m->addAction(tr("Assign")); + act->setCheckable(false); + act->setData((id<<8) + 255); // Shift 8 bits. Make midi menu the last item at 255. + + MusECore::AudioTrack* track = static_cast(editAutomation); + MusECore::MidiAudioCtrlMap* macm = track->controller()->midiControls(); + MusECore::AudioMidiCtrlStructMap amcs; + macm->find_audio_ctrl_structs(id, &amcs); + + // Group only the clear and status items so they can both be easily removed when clear is clicked. + if(!amcs.empty()) + { + QActionGroup* midi_actgrp = new QActionGroup(m); + QAction *cact = midi_actgrp->addAction(tr("Clear")); + cact->setData((id<<8) + 254); // Shift 8 bits. Make clear the second-last item at 254 + for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) + { + int port, chan, mctrl; + macm->hash_values((*iamcs)->first, &port, &chan, &mctrl); + //QString s = QString("Port:%1 Chan:%2 Ctl:%3-%4").arg(port + 1) + QString s = QString("Port:%1 Chan:%2 Ctl:%3").arg(port + 1) + .arg(chan + 1) + //.arg((mctrl >> 8) & 0xff) + //.arg(mctrl & 0xff); + .arg(MusECore::midiCtrlName(mctrl, true)); + QAction *mact = midi_actgrp->addAction(s); + mact->setEnabled(false); + mact->setData(-1); // Not used + } + m->addActions(midi_actgrp->actions()); + } + } + connect(m, SIGNAL(triggered(QAction*)), SLOT(changeAutomationColor(QAction*))); return m; @@ -1611,14 +1728,53 @@ void TList::mousePressEvent(QMouseEvent* ev) p->setTitle(tr("Viewable automation")); MusECore::CtrlListList* cll = ((MusECore::AudioTrack*)t)->controller(); QAction* act = 0; + int last_rackpos = -1; + bool internal_found = false; + bool synth_found = false; for(MusECore::CtrlListList::iterator icll =cll->begin();icll!=cll->end();++icll) { MusECore::CtrlList *cl = icll->second; if (cl->dontShow()) continue; + + int ctrl = cl->id(); + + if(ctrl < AC_PLUGIN_CTL_BASE) + { + if(!internal_found) + p->addAction(new MusEGui::MenuTitleItem(tr("Internal"), p)); + internal_found = true; + } + else + { + if(ctrl < (int)MusECore::genACnum(MAX_PLUGINS, 0)) // The beginning of the special dssi synth controller block. + { + int rackpos = (ctrl - AC_PLUGIN_CTL_BASE) >> AC_PLUGIN_CTL_BASE_POW; + if(rackpos < PipelineDepth) + { + if(rackpos != last_rackpos) + { + QString s = ((MusECore::AudioTrack*)t)->efxPipe()->name(rackpos); + p->addAction(new MusEGui::MenuTitleItem(s, p)); + } + last_rackpos = rackpos; + } + } + else + { + if(t->type() == MusECore::Track::AUDIO_SOFTSYNTH) + { + if(!synth_found) + p->addAction(new MusEGui::MenuTitleItem(tr("Synth"), p)); + synth_found = true; + } + } + } + act = p->addAction(cl->name()); act->setCheckable(true); act->setChecked(cl->isVisible()); - int data = cl->id() * 256; // shift 8 bits + + int data = ctrl<<8; // shift 8 bits data += 150; // illegal color > 100 act->setData(data); PopupMenu *m = colorMenu(cl->color(), cl->id(), p); diff --git a/muse2/muse/audio.cpp b/muse2/muse/audio.cpp index cbcbd922..6349971b 100644 --- a/muse2/muse/audio.cpp +++ b/muse2/muse/audio.cpp @@ -45,6 +45,19 @@ #include "pos.h" #include "ticksynth.h" +// Experimental for now - allow other Jack timebase masters to control our midi engine. +// TODO: Be friendly to other apps and ask them to be kind to us by using jack_transport_reposition. +// It is actually required IF we want the extra position info to show up +// in the sync callback, otherwise we get just the frame only. +// This information is shared on the server, it is directly passed around. +// jack_transport_locate blanks the info from sync until the timebase callback reads +// it again right after, from some timebase master. +// Sadly not many of us use jack_transport_reposition. So we need to work around it ! +//#define _JACK_TIMEBASE_DRIVES_MIDI_ + +#ifdef _JACK_TIMEBASE_DRIVES_MIDI_ +#include "jackaudio.h" +#endif namespace MusEGlobal { MusECore::Audio* audio; @@ -102,6 +115,7 @@ const char* seqMsgList[] = { "AUDIO_ADD_AC_EVENT", "AUDIO_CHANGE_AC_EVENT", "AUDIO_SET_SOLO", "AUDIO_SET_SEND_METRONOME", + "AUDIO_START_MIDI_LEARN", "MS_PROCESS", "MS_STOP", "MS_SET_RTC", "MS_UPDATE_POLL_FD", "SEQM_IDLE", "SEQM_SEEK" }; @@ -127,6 +141,10 @@ Audio::Audio() _pos.setType(Pos::FRAMES); _pos.setFrame(0); +#ifdef _AUDIO_USE_TRUE_FRAME_ + _previousPos.setType(Pos::FRAMES); + _previousPos.setFrame(0); +#endif nextTickPos = curTickPos = 0; midiClick = 0; @@ -143,6 +161,8 @@ Audio::Audio() startRecordPos.setType(Pos::FRAMES); // Tim endRecordPos.setType(Pos::FRAMES); + startExternalRecTick = 0; + endExternalRecTick = 0; _audioMonitor = 0; _audioMaster = 0; @@ -384,7 +404,10 @@ void Audio::process(unsigned frames) (*i)->processInit(frames); int samplePos = _pos.frame(); int offset = 0; // buffer offset in audio buffers - +#ifdef _JACK_TIMEBASE_DRIVES_MIDI_ + bool use_jack_timebase = false; +#endif + if (isPlaying()) { if (!freewheel()) MusEGlobal::audioPrefetch->msgTick(); @@ -394,7 +417,22 @@ void Audio::process(unsigned frames) write(sigFd, "F", 1); return; } - + +#ifdef _JACK_TIMEBASE_DRIVES_MIDI_ + unsigned curr_jt_tick, next_jt_ticks; + use_jack_timebase = + MusEGlobal::audioDevice->deviceType() == AudioDevice::JACK_AUDIO && + !MusEGlobal::jackTransportMaster && + !MusEGlobal::song->masterFlag() && + !MusEGlobal::extSyncFlag.value() && + static_cast(MusEGlobal::audioDevice)->timebaseQuery( + frames, NULL, NULL, NULL, &curr_jt_tick, &next_jt_ticks); + // NOTE: I would rather trust the reported current tick than rely solely on the stream of + // tempos to correctly advance to the next position (which did actually test OK anyway). + if(use_jack_timebase) + curTickPos = curr_jt_tick; +#endif + // // check for end of song // @@ -452,10 +490,19 @@ void Audio::process(unsigned frames) } else { - - Pos ppp(_pos); - ppp += frames; - nextTickPos = ppp.tick(); + +#ifdef _JACK_TIMEBASE_DRIVES_MIDI_ + if(use_jack_timebase) + // With jack timebase this might not be accurate - + // we are relying on the tempo to figure out the next tick. + nextTickPos = curTickPos + next_jt_ticks; + else +#endif + { + Pos ppp(_pos); + ppp += frames; + nextTickPos = ppp.tick(); + } } } // @@ -468,9 +515,15 @@ void Audio::process(unsigned frames) process1(samplePos, offset, frames); for (iAudioOutput i = ol->begin(); i != ol->end(); ++i) (*i)->processWrite(); + +#ifdef _AUDIO_USE_TRUE_FRAME_ + _previousPos = _pos; +#endif if (isPlaying()) { _pos += frames; - curTickPos = nextTickPos; + // With jack timebase this might not be accurate if we + // set curTickPos (above) from the reported current tick. + curTickPos = nextTickPos; } } @@ -626,6 +679,13 @@ void Audio::processMsg(AudioMsg* msg) msg->snode->setSendMetronome((bool)msg->ival); break; + case AUDIO_START_MIDI_LEARN: + // Reset the values. The engine will fill these from driver events. + MusEGlobal::midiLearnPort = -1; + MusEGlobal::midiLearnChan = -1; + MusEGlobal::midiLearnCtrl = -1; + break; + case AUDIO_SET_SEG_SIZE: MusEGlobal::segmentSize = msg->ival; MusEGlobal::sampleRate = msg->iival; @@ -690,6 +750,9 @@ void Audio::processMsg(AudioMsg* msg) MusEGlobal::song->processMsg(msg); if (isPlaying()) { if (!MusEGlobal::checkAudioDevice()) return; +#ifdef _AUDIO_USE_TRUE_FRAME_ + _previousPos = _pos; +#endif _pos.setTick(curTickPos); int samplePos = _pos.frame(); syncFrame = MusEGlobal::audioDevice->framePos(); @@ -742,10 +805,25 @@ void Audio::seek(const Pos& p) if (MusEGlobal::heavyDebugMsg) printf("Audio::seek frame:%d\n", p.frame()); +#ifdef _AUDIO_USE_TRUE_FRAME_ + _previousPos = _pos; +#endif _pos = p; if (!MusEGlobal::checkAudioDevice()) return; syncFrame = MusEGlobal::audioDevice->framePos(); frameOffset = syncFrame - _pos.frame(); + +#ifdef _JACK_TIMEBASE_DRIVES_MIDI_ + unsigned curr_jt_tick; + if(MusEGlobal::audioDevice->deviceType() == AudioDevice::JACK_AUDIO && + !MusEGlobal::jackTransportMaster && + !MusEGlobal::song->masterFlag() && + !MusEGlobal::extSyncFlag.value() && + static_cast(MusEGlobal::audioDevice)->timebaseQuery( + MusEGlobal::segmentSize, NULL, NULL, NULL, &curr_jt_tick, NULL)) + curTickPos = curr_jt_tick; + else +#endif curTickPos = _pos.tick(); // ALSA support @@ -802,6 +880,7 @@ void Audio::startRolling() if(_loopCount == 0) { startRecordPos = _pos; + startExternalRecTick = curTickPos; } if (MusEGlobal::song->record()) { recording = true; @@ -916,6 +995,7 @@ void Audio::stopRolling() } recording = false; endRecordPos = _pos; + endExternalRecTick = curTickPos; write(sigFd, "0", 1); // STOP } @@ -926,8 +1006,10 @@ void Audio::stopRolling() void Audio::recordStop() { + MusEGlobal::song->processMasterRec(); + if (MusEGlobal::debugMsg) - printf("recordStop - startRecordPos=%d\n", startRecordPos.tick()); + printf("recordStop - startRecordPos=%d\n", MusEGlobal::extSyncFlag.value() ? startExternalRecTick : startRecordPos.tick()); MusEGlobal::audio->msgIdle(true); // gain access to all data structures @@ -937,7 +1019,7 @@ void Audio::recordStop() for (iWaveTrack it = wl->begin(); it != wl->end(); ++it) { WaveTrack* track = *it; if (track->recordFlag() || MusEGlobal::song->bounceTrack == track) { - MusEGlobal::song->cmdAddRecordedWave(track, startRecordPos, endRecordPos); + MusEGlobal::song->cmdAddRecordedWave(track, startRecordPos, endRecordPos); // The track's _recFile pointer may have been kept and turned // into a SndFileR and added to a new part. // Or _recFile may have been discarded (no new recorded part created). @@ -960,7 +1042,8 @@ void Audio::recordStop() // Do SysexMeta. Do loops. buildMidiEventList(el, mpel, mt, MusEGlobal::config.division, true, true); - MusEGlobal::song->cmdAddRecordedEvents(mt, el, startRecordPos.tick()); + MusEGlobal::song->cmdAddRecordedEvents(mt, el, + MusEGlobal::extSyncFlag.value() ? startExternalRecTick : startRecordPos.tick()); el->clear(); mpel->clear(); } @@ -981,6 +1064,7 @@ void Audio::recordStop() msgSetRecord(ao, false); } } + MusEGlobal::audio->msgIdle(false); MusEGlobal::song->endUndo(0); MusEGlobal::song->setRecord(false); @@ -993,7 +1077,11 @@ void Audio::recordStop() unsigned Audio::framesSinceCycleStart() const { - return lrint((curTime() - syncTime) * MusEGlobal::sampleRate); + unsigned f = lrint((curTime() - syncTime) * MusEGlobal::sampleRate); + // Safety due to inaccuracies. It cannot be after the segment, right? + if(f >= MusEGlobal::segmentSize) + f = MusEGlobal::segmentSize - 1; + return f; } //--------------------------------------------------------- diff --git a/muse2/muse/audio.h b/muse2/muse/audio.h index a9d2cc82..7c3d73ce 100644 --- a/muse2/muse/audio.h +++ b/muse2/muse/audio.h @@ -31,6 +31,12 @@ #include "route.h" #include "event.h" +// An experiment to use true frames for time-stamping all recorded input. +// (All recorded data actually arrived in the previous period.) +// TODO: Some more work needs to be done in WaveTrack::getData() in order to +// make everything line up and sync correctly. Cannot use this yet! +//#define _AUDIO_USE_TRUE_FRAME_ + namespace MusECore { class AudioDevice; class AudioTrack; @@ -94,6 +100,7 @@ enum { AUDIO_ADD_AC_EVENT, AUDIO_CHANGE_AC_EVENT, AUDIO_SET_SOLO, AUDIO_SET_SEND_METRONOME, + AUDIO_START_MIDI_LEARN, MS_PROCESS, MS_STOP, MS_SET_RTC, MS_UPDATE_POLL_FD, SEQM_IDLE, SEQM_SEEK, }; @@ -147,7 +154,11 @@ class Audio { int _loopCount; // Number of times we have looped so far Pos _pos; // current play position - + +#ifdef _AUDIO_USE_TRUE_FRAME_ + Pos _previousPos; // previous play position +#endif + unsigned curTickPos; // pos at start of frame during play/record unsigned nextTickPos; // pos at start of next frame during play/record @@ -172,7 +183,8 @@ class Audio { // record values: Pos startRecordPos; Pos endRecordPos; - + unsigned startExternalRecTick; + unsigned endExternalRecTick; AudioOutput* _audioMaster; AudioOutput* _audioMonitor; @@ -288,6 +300,7 @@ class Audio { void msgRemapPortDrumCtlEvents(int, int, int, int); void msgChangeAllPortDrumCtrlEvents(bool, bool); void msgSetSendMetronome(AudioTrack*, bool); + void msgStartMidiLearn(); void msgPlayMidiEvent(const MidiPlayEvent* event); void rescanAlsaPorts(); @@ -295,8 +308,13 @@ class Audio { void midiPortsChanged(); const Pos& pos() const { return _pos; } +#ifdef _AUDIO_USE_TRUE_FRAME_ + const Pos& previousPos() const { return _previousPos; } +#endif const Pos& getStartRecordPos() const { return startRecordPos; } const Pos& getEndRecordPos() const { return endRecordPos; } + unsigned getStartExternalRecTick() const { return startExternalRecTick; } + unsigned getEndExternalRecTick() const { return endExternalRecTick; } int loopCount() { return _loopCount; } // Number of times we have looped so far unsigned loopFrame() { return _loopFrame; } diff --git a/muse2/muse/audiotrack.cpp b/muse2/muse/audiotrack.cpp index b0c52a54..dac496d7 100644 --- a/muse2/muse/audiotrack.cpp +++ b/muse2/muse/audiotrack.cpp @@ -39,6 +39,7 @@ #include "synth.h" #include "dssihost.h" #include "app.h" +#include "controlfifo.h" namespace MusECore { @@ -385,6 +386,10 @@ void AudioTrack::addController(CtrlList* list) void AudioTrack::removeController(int id) { + AudioMidiCtrlStructMap amcs; + _controller.midiControls()->find_audio_ctrl_structs(id, &amcs); + for(ciAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++ iamcs) + _controller.midiControls()->erase(*iamcs); iCtrlList i = _controller.find(id); if (i == _controller.end()) { printf("AudioTrack::removeController id %d not found\n", id); @@ -399,20 +404,14 @@ void AudioTrack::removeController(int id) void AudioTrack::swapControllerIDX(int idx1, int idx2) { - // FIXME This code is ugly. - // At best we would like to modify the keys (IDXs) in-place and - // do some kind of deferred re-sort, but it can't be done... - - if(idx1 == idx2) - return; - - if(idx1 < 0 || idx2 < 0 || idx1 >= PipelineDepth || idx2 >= PipelineDepth) + if(idx1 == idx2 || idx1 < 0 || idx2 < 0 || idx1 >= PipelineDepth || idx2 >= PipelineDepth) return; CtrlList *cl; CtrlList *newcl; int id1 = (idx1 + 1) * AC_PLUGIN_CTL_BASE; int id2 = (idx2 + 1) * AC_PLUGIN_CTL_BASE; + int id_mask = ~((int)AC_PLUGIN_CTL_ID_MASK); int i, j; CtrlListList tmpcll; @@ -422,7 +421,7 @@ void AudioTrack::swapControllerIDX(int idx1, int idx2) { cl = icl->second; i = cl->id() & AC_PLUGIN_CTL_ID_MASK; - j = cl->id() & ~((unsigned long)AC_PLUGIN_CTL_ID_MASK); + j = cl->id() & id_mask; if(j == id1 || j == id2) { newcl = new CtrlList(i | (j == id1 ? id2 : id1)); @@ -460,74 +459,21 @@ void AudioTrack::swapControllerIDX(int idx1, int idx2) _controller.insert(std::pair(newcl->id(), newcl)); } - // DELETETHIS 67 - /* - unsigned int idmask = ~AC_PLUGIN_CTL_ID_MASK; - - CtrlList* cl; - CtrlList* ctl1 = 0; - CtrlList* ctl2 = 0; - CtrlList* newcl1 = 0; - CtrlList* newcl2 = 0; - CtrlVal cv(0, 0.0); - int id1 = (idx1 + 1) * AC_PLUGIN_CTL_BASE; - int id2 = (idx2 + 1) * AC_PLUGIN_CTL_BASE; - int i, j; - double min, max; - - for(ciCtrlList icl = _controller.begin(); icl != _controller.end(); ++icl) + // Remap midi to audio controls... + MidiAudioCtrlMap* macm = _controller.midiControls(); + for(iMidiAudioCtrlMap imacm = macm->begin(); imacm != macm->end(); ++imacm) { - cl = icl->second; - i = cl->id() & AC_PLUGIN_CTL_ID_MASK; - j = cl->id() & idmask; - - if(j == id1) - { - ctl1 = cl; - newcl1 = new CtrlList( i | id2 ); - newcl1->setMode(cl->mode()); - newcl1->setValueType(cl->valueType()); - newcl1->setName(cl->name()); - cl->range(&min, &max); - newcl1->setRange(min, max); - newcl1->setCurVal(cl->curVal()); - newcl1->setDefault(cl->getDefault()); - for(iCtrl ic = cl->begin(); ic != cl->end(); ++ic) - { - cv = ic->second; - newcl1->insert(std::pair(cv.frame, cv)); - } - } - //else - if(j == id2) - { - ctl2 = cl; - newcl2 = new CtrlList( i | id1 ); - newcl2->setMode(cl->mode()); - newcl2->setValueType(cl->valueType()); - newcl2->setName(cl->name()); - cl->range(&min, &max); - newcl2->setRange(min, max); - newcl2->setCurVal(cl->curVal()); - newcl2->setDefault(cl->getDefault()); - for(iCtrl ic = cl->begin(); ic != cl->end(); ++ic) - { - cv = ic->second; - newcl2->insert(std::pair(cv.frame, cv)); - } - } - } - if(ctl1) - _controller.erase(ctl1->id()); - if(ctl2) - _controller.erase(ctl2->id()); - if(newcl1) - //_controller.add(newcl1); - _controller.insert(std::pair(newcl1->id(), newcl1)); - if(newcl2) - _controller.insert(std::pair(newcl2->id(), newcl2)); - //_controller.add(newcl2); - */ + int actrl = imacm->second.audioCtrlId(); + int id = actrl & id_mask; + actrl &= AC_PLUGIN_CTL_ID_MASK; + if(id == id1) + actrl |= id2; + else if(id == id2) + actrl |= id1; + else + continue; + imacm->second.setAudioCtrlId(actrl); + } } //--------------------------------------------------------- @@ -610,11 +556,7 @@ void AudioTrack::processAutomationEvents() if(icr->id == id && icr->type == ARVT_STOP) { int end = icr->frame; - // Erase everything up to, not including, this stop event's frame. - // Because an event was already stored directly when slider released. - if(end > start) - --end; - + iCtrl s = cl->lower_bound(start); iCtrl e = cl->lower_bound(end); @@ -636,8 +578,21 @@ void AudioTrack::processAutomationEvents() // from CtrlRecList and put into cl. for (iCtrlRec icr = _recEvents.begin(); icr != _recEvents.end(); ++icr) { - if (icr->id == id && (icr->type == ARVT_VAL || icr->type == ARVT_START)) + if (icr->id == id) + { + // Must optimize these types otherwise multiple vertices appear on flat straight lines in the graphs. + CtrlValueType vtype = cl->valueType(); + if(!cl->empty() && (cl->mode() == CtrlList::DISCRETE || vtype == VAL_BOOL || vtype == VAL_INT)) + { + iCtrl icl_prev = cl->lower_bound(icr->frame); + if(icl_prev != cl->begin()) + --icl_prev; + if(icl_prev->second.val == icr->val) + continue; + } + // Now add the value. cl->add(icr->frame, icr->val); + } } } @@ -790,7 +745,7 @@ void AudioTrack::changeACEvent(int id, int frame, int newframe, double newval) iCtrl ic = cl->find(frame); if(ic != cl->end()) cl->erase(ic); - cl->insert(std::pair (newframe, CtrlVal(newframe, newval))); + cl->insert(std::pair (newframe, CtrlVal(newframe, newval))); } //--------------------------------------------------------- @@ -825,7 +780,7 @@ void AudioTrack::setVolume(double val) double AudioTrack::pan() const { return _controller.value(AC_PAN, MusEGlobal::audio->curFramePos(), - !MusEGlobal::automation || automationType() == AUTO_OFF || !_volumeEnCtrl || !_volumeEn2Ctrl); + !MusEGlobal::automation || automationType() == AUTO_OFF || !_panEnCtrl || !_panEn2Ctrl); } //--------------------------------------------------------- @@ -848,8 +803,49 @@ void AudioTrack::setPan(double val) double AudioTrack::pluginCtrlVal(int ctlID) const { + bool en_1 = true, en_2 = true; + if(ctlID < AC_PLUGIN_CTL_BASE) + { + if(ctlID == AC_VOLUME) + { + en_1 = _volumeEnCtrl; + en_2 = _volumeEn2Ctrl; + } + else + if(ctlID == AC_PAN) + { + en_1 = _panEnCtrl; + en_2 = _panEn2Ctrl; + } + } + else + { + if(ctlID < (int)genACnum(MAX_PLUGINS, 0)) // The beginning of the special dssi synth controller block. + { + _efxPipe->controllersEnabled(ctlID, &en_1, &en_2); + } + else + { + if(type() == AUDIO_SOFTSYNTH) + { + const SynthI* synth = static_cast(this); + if(synth->synth() && synth->synth()->synthType() == Synth::DSSI_SYNTH) + { + SynthIF* sif = synth->sif(); + if(sif) + { + const DssiSynthIF* dssi_sif = static_cast(sif); + int in_ctrl_idx = ctlID & AC_PLUGIN_CTL_ID_MASK; + en_1 = dssi_sif->controllerEnabled(in_ctrl_idx); + en_2 = dssi_sif->controllerEnabled2(in_ctrl_idx); + } + } + } + } + } + return _controller.value(ctlID, MusEGlobal::audio->curFramePos(), - !MusEGlobal::automation || automationType() == AUTO_OFF); + !MusEGlobal::automation || automationType() == AUTO_OFF || !en_1 || !en_2); } //--------------------------------------------------------- @@ -865,6 +861,140 @@ void AudioTrack::setPluginCtrlVal(int param, double val) cl->second->setCurVal(val); } +//--------------------------------------------------------- +// addScheduledControlEvent +// returns true if event cannot be delivered +//--------------------------------------------------------- + +bool AudioTrack::addScheduledControlEvent(int track_ctrl_id, float val, unsigned frame) +{ + if(track_ctrl_id < AC_PLUGIN_CTL_BASE) // FIXME: These controllers (three so far - vol, pan, mute) have no vari-run-length support. + { + iCtrlList icl = _controller.find(track_ctrl_id); + if(icl == _controller.end()) + return true; + icl->second->setCurVal(val); + return false; + } + else + { + if(track_ctrl_id < (int)genACnum(MAX_PLUGINS, 0)) // The beginning of the special dssi synth controller block. + return _efxPipe->addScheduledControlEvent(track_ctrl_id, val, frame); + else + { + if(type() == AUDIO_SOFTSYNTH) + { + const SynthI* synth = static_cast(this); + if(synth->synth() && synth->synth()->synthType() == Synth::DSSI_SYNTH) + { + SynthIF* sif = synth->sif(); + if(sif) + { + DssiSynthIF* dssi_sif = static_cast(sif); + int in_ctrl_idx = track_ctrl_id & AC_PLUGIN_CTL_ID_MASK; + return dssi_sif->addScheduledControlEvent(in_ctrl_idx, val, frame); + } + } + } + } + } + return true; +} + +//--------------------------------------------------------- +// enableController +// Enable or disable gui controls. +// Used during automation recording to inhibit gui controls +// from playback controller stream +//--------------------------------------------------------- + +void AudioTrack::enableController(int track_ctrl_id, bool en) +{ + if(track_ctrl_id < AC_PLUGIN_CTL_BASE) + { + if(track_ctrl_id == AC_VOLUME) + enableVolumeController(en); + else + if(track_ctrl_id == AC_PAN) + enablePanController(en); + } + else + { + if(track_ctrl_id < (int)genACnum(MAX_PLUGINS, 0)) // The beginning of the special dssi synth controller block. + _efxPipe->enableController(track_ctrl_id, en); + else + { + if(type() == AUDIO_SOFTSYNTH) + { + SynthI* synth = static_cast(this); + if(synth->synth() && synth->synth()->synthType() == Synth::DSSI_SYNTH) + { + SynthIF* sif = synth->sif(); + if(sif) + { + DssiSynthIF* dssi_sif = static_cast(sif); + int in_ctrl_idx = track_ctrl_id & AC_PLUGIN_CTL_ID_MASK; + dssi_sif->enableController(in_ctrl_idx, en); + } + } + } + } + } +} + +//--------------------------------------------------------- +// controllersEnabled +//--------------------------------------------------------- + +void AudioTrack::controllersEnabled(int track_ctrl_id, bool* en1, bool* en2) const + { + bool en_1 = true, en_2 = true; + if(track_ctrl_id < AC_PLUGIN_CTL_BASE) + { + if(track_ctrl_id == AC_VOLUME) + { + en_1 = _volumeEnCtrl; + en_2 = _volumeEn2Ctrl; + } + else + if(track_ctrl_id == AC_PAN) + { + en_1 = _panEnCtrl; + en_2 = _panEn2Ctrl; + } + } + else + { + if(track_ctrl_id < (int)genACnum(MAX_PLUGINS, 0)) // The beginning of the special dssi synth controller block. + { + _efxPipe->controllersEnabled(track_ctrl_id, &en_1, &en_2); + } + else + { + if(type() == AUDIO_SOFTSYNTH) + { + const SynthI* synth = static_cast(this); + if(synth->synth() && synth->synth()->synthType() == Synth::DSSI_SYNTH) + { + SynthIF* sif = synth->sif(); + if(sif) + { + const DssiSynthIF* dssi_sif = static_cast(sif); + int in_ctrl_idx = track_ctrl_id & AC_PLUGIN_CTL_ID_MASK; + en_1 = dssi_sif->controllerEnabled(in_ctrl_idx); + en_2 = dssi_sif->controllerEnabled2(in_ctrl_idx); + } + } + } + } + } + + if(en1) + *en1 = en_1; + if(en2) + *en2 = en_2; + } + void AudioTrack::recordAutomation(int n, double v) { if(!MusEGlobal::automation) @@ -883,7 +1013,7 @@ void AudioTrack::recordAutomation(int n, double v) if (cl == _controller.end()) return; // Add will replace if found. - cl->second->add(MusEGlobal::audio->curFramePos(), v); + cl->second->add(MusEGlobal::audio->curFramePos(), v); } } } @@ -895,10 +1025,10 @@ void AudioTrack::startAutoRecord(int n, double v) if(MusEGlobal::audio->isPlaying()) { if(automationType() == AUTO_TOUCH) - _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v, ARVT_START)); - else + _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v, ARVT_START)); + else if(automationType() == AUTO_WRITE) - _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v)); + _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v)); } else { @@ -926,7 +1056,7 @@ void AudioTrack::stopAutoRecord(int n, double v) { if(automationType() == AUTO_TOUCH) { - MusEGlobal::audio->msgAddACEvent(this, n, MusEGlobal::audio->curFramePos(), v); + MusEGlobal::audio->msgAddACEvent(this, n, MusEGlobal::audio->curFramePos(), v); _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v, ARVT_STOP)); } } @@ -953,26 +1083,7 @@ void AudioTrack::writeProperties(int level, Xml& xml) const if (*ip) (*ip)->writeConfiguration(level, xml); } - for (ciCtrlList icl = _controller.begin(); icl != _controller.end(); ++icl) { - const CtrlList* cl = icl->second; - - QString s= QString("controller id=\"%1\" cur=\"%2\"").arg(cl->id()).arg(cl->curVal()).toAscii().constData(); - s += QString(" color=\"%1\" visible=\"%2\"").arg(cl->color().name()).arg(cl->isVisible()); - xml.tag(level++, s.toAscii().constData()); - int i = 0; - for (ciCtrl ic = cl->begin(); ic != cl->end(); ++ic) { - QString s("%1 %2, "); - xml.nput(level, s.arg(ic->second.frame).arg(ic->second.val).toAscii().constData()); - ++i; - if (i >= 4) { - xml.put(level, ""); - i = 0; - } - } - if (i) - xml.put(level, ""); - xml.etag(level--, "controller"); - } + _controller.write(level, xml); } //--------------------------------------------------------- @@ -1113,6 +1224,8 @@ bool AudioTrack::readProperties(Xml& xml, const QString& tag) l->setMode(p->ctrlMode(m)); } } + else if (tag == "midiMapper") + _controller.midiControls()->read(xml); else return Track::readProperties(xml, tag); return false; @@ -1230,20 +1343,20 @@ void AudioTrack::mapRackPluginsToControllers() unsigned param = id & AC_PLUGIN_CTL_ID_MASK; int idx = (id >> AC_PLUGIN_CTL_BASE_POW) - 1; - PluginIBase* p = 0; + const PluginIBase* p = 0; if(idx >= 0 && idx < PipelineDepth) p = (*_efxPipe)[idx]; // Support a special block for dssi synth ladspa controllers. else if(idx == MAX_PLUGINS && type() == AUDIO_SOFTSYNTH) { - SynthI* synti = dynamic_cast < SynthI* > (this); + const SynthI* synti = dynamic_cast < const SynthI* > (this); if(synti) { SynthIF* sif = synti->sif(); if(sif) { #ifdef DSSI_SUPPORT - DssiSynthIF* dsif = dynamic_cast < DssiSynthIF* > (sif); + const DssiSynthIF* dsif = dynamic_cast < const DssiSynthIF* > (sif); if(dsif) p = dsif; #endif diff --git a/muse2/muse/conf.cpp b/muse2/muse/conf.cpp index 6c5c93db..cca3cb94 100644 --- a/muse2/muse/conf.cpp +++ b/muse2/muse/conf.cpp @@ -561,6 +561,23 @@ void readConfiguration(Xml& xml, bool doReadMidiPortConfig, bool doReadGlobalCon if(MusEGlobal::audioDevice) MusEGlobal::audioDevice->setMaster(MusEGlobal::jackTransportMaster); } + else if (tag == "syncRecFilterPreset") + { + int p = xml.parseInt(); + if(p >= 0 && p < MidiSyncInfo::TYPE_END) + { + MusEGlobal::syncRecFilterPreset = MidiSyncInfo::SyncRecFilterPresetType(p); + if(MusEGlobal::midiSeq) + MusEGlobal::midiSeq->setSyncRecFilterPreset(MusEGlobal::syncRecFilterPreset); + } + } + else if (tag == "syncRecTempoValQuant") + { + double qv = xml.parseDouble(); + MusEGlobal::syncRecTempoValQuant = qv; + if(MusEGlobal::midiSeq) + MusEGlobal::midiSeq->setRecTempoValQuant(qv); + } else if (tag == "mtcoffset") { QString qs(xml.parse1()); QByteArray ba = qs.toLatin1(); @@ -1348,6 +1365,8 @@ void MusE::writeConfiguration(int level, MusECore::Xml& xml) const xml.uintTag(level, "sendClockDelay", MusEGlobal::syncSendFirstClockDelay); xml.intTag(level, "useJackTransport", MusEGlobal::useJackTransport.value()); xml.intTag(level, "jackTransportMaster", MusEGlobal::jackTransportMaster); + xml.intTag(level, "syncRecFilterPreset", MusEGlobal::syncRecFilterPreset); + xml.doubleTag(level, "syncRecTempoValQuant", MusEGlobal::syncRecTempoValQuant); MusEGlobal::extSyncFlag.save(level, xml); xml.intTag(level, "bigtimeVisible", viewBigtimeAction->isChecked()); diff --git a/muse2/muse/confmport.cpp b/muse2/muse/confmport.cpp index 637e927e..32e1dc91 100644 --- a/muse2/muse/confmport.cpp +++ b/muse2/muse/confmport.cpp @@ -888,7 +888,7 @@ void MPConfig::rbClicked(QTableWidgetItem* item) #endif MusEGlobal::midiSeq->msgSetMidiDevice(port, sdev); - MusEGlobal::muse->changeConfig(true); // save configuration file + MusEGlobal::muse->changeConfig(true); // save configuration file // Add all track routes to/from this port... if(sdev) @@ -904,7 +904,7 @@ void MPConfig::rbClicked(QTableWidgetItem* item) MusEGlobal::audio->msgAddRoute(MusECore::Route(no, chbits), MusECore::Route(*it, chbits)); } } - chbits = MusEGlobal::midiPorts[no].defaultOutChannels(); +// chbits = MusEGlobal::midiPorts[no].defaultOutChannels(); // Turn on if and when multiple output routes are supported. DELETETHIS? #if 0 for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) @@ -914,22 +914,27 @@ void MPConfig::rbClicked(QTableWidgetItem* item) MusEGlobal::audio->msgAddRoute(MusECore::Route(no, chbits), MusECore::Route(*it, chbits)); } #else - for(int ch = 0; ch < MIDI_CHANNELS; ++ch) - if(chbits & (1 << ch)) - { - MusEGlobal::audio->msgIdle(true); - for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) - { - // Leave drum track channel at current setting. - if((*it)->type() == MusECore::Track::DRUM) - (*it)->setOutPortAndUpdate(no); - else - (*it)->setOutPortAndChannelAndUpdate(no, ch); - } - MusEGlobal::audio->msgIdle(false); - // Stop at the first output channel found. - break; - } +// REMOVE Tim. +// for(int ch = 0; ch < MIDI_CHANNELS; ++ch) +// if(chbits & (1 << ch)) +// { +// MusEGlobal::audio->msgIdle(true); +// for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) +// { +// // We are only interested in tracks which use this port being changed now. +// if((*it)->outPort() != no) +// continue; +// // Leave drum track channel at current setting. // REMOVE Tim. +// //if((*it)->type() == MusECore::Track::DRUM) +// // (*it)->setOutPortAndUpdate(no); +// //else +// // (*it)->setOutPortAndChannelAndUpdate(no, ch); +// (*it)->setOutPortAndUpdate(no); +// } +// MusEGlobal::audio->msgIdle(false); +// // Stop at the first output channel found. +// break; +// } #endif } diff --git a/muse2/muse/ctrl.cpp b/muse2/muse/ctrl.cpp index 8071491e..d7d42770 100644 --- a/muse2/muse/ctrl.cpp +++ b/muse2/muse/ctrl.cpp @@ -6,7 +6,7 @@ // controller handling for mixer automation // // (C) Copyright 2003 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Time E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2011-2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -24,15 +24,20 @@ // //========================================================= +// Turn on debugging messages +//#define _CTRL_DEBUG_ #include #include +#include + +#include #include "gconfig.h" #include "fastlog.h" -#include "math.h" #include "globals.h" #include "ctrl.h" +#include "midictrl.h" #include "xml.h" namespace MusECore { @@ -48,8 +53,268 @@ void CtrlList::initColor(int i) _visible = false; } +//--------------------------------------------------------- +// midi2AudioCtrlValue +// Apply mapper if it is non-null +//--------------------------------------------------------- + +double midi2AudioCtrlValue(const CtrlList* audio_ctrl_list, const MidiAudioCtrlStruct* /*mapper*/, int midi_ctlnum, int midi_val) +{ + double fmin, fmax; + audio_ctrl_list->range(&fmin, &fmax); + double frng = fmax - fmin; // The audio control range. + + MidiController::ControllerType t = midiControllerType(midi_ctlnum); + CtrlValueType aud_t = audio_ctrl_list->valueType(); + + #ifdef _CTRL_DEBUG_ + printf("midi2AudioCtrlValue: midi_ctlnum:%d val:%d fmin:%f fmax:%f\n", midi_ctlnum, midi_val, fmin, fmax); + #endif + + int ctlmn = 0; + int ctlmx = 127; + + int bval = midi_val; + switch(t) + { + case MidiController::RPN: + case MidiController::NRPN: + case MidiController::Controller7: + ctlmn = 0; + ctlmx = 127; + break; + case MidiController::Controller14: + case MidiController::RPN14: + case MidiController::NRPN14: + ctlmn = 0; + ctlmx = 16383; + break; + case MidiController::Program: + ctlmn = 0; + ctlmx = 0xffffff; + break; + case MidiController::Pitch: + ctlmn = -8192; + ctlmx = 8191; + bval += 8192; + break; + case MidiController::Velo: // cannot happen + default: + break; + } + double fictlrng = double(ctlmx - ctlmn); // Float version of the integer midi range. + double normval = double(bval) / fictlrng; // Float version of the normalized midi value. + + // ---------- TODO: Do stuff with the mapper, if supplied. + + if(aud_t == VAL_LOG) + { + // FIXME: Although this should be correct, some sliders show "---" at top end, some don't. + // Possibly because of use of fast_log10 in value(), and in sliders and automation IIRC. + fmin = 20.0*log10(fmin); + fmax = 20.0*log10(fmax); + frng = fmax - fmin; + double ret = exp10((normval * frng + fmin) / 20.0); + #ifdef _CTRL_DEBUG_ + printf("midi2AudioCtrlValue: is VAL_LOG normval:%f frng:%f returning:%f\n", normval, frng, ret); + #endif + return ret; + } + + if(aud_t == VAL_LINEAR) + { + double ret = normval * frng + fmin; + #ifdef _CTRL_DEBUG_ + printf("midi2AudioCtrlValue: is VAL_LINEAR normval:%f frng:%f returning:%f\n", normval, frng, ret); + #endif + return ret; + } + + if(aud_t == VAL_INT) + { + double ret = int(normval * frng + fmin); + #ifdef _CTRL_DEBUG_ + printf("midi2AudioCtrlValue: is VAL_INT returning:%f\n", ret); + #endif + return ret; + } + + if(aud_t == VAL_BOOL) + { + #ifdef _CTRL_DEBUG_ + printf("midi2AudioCtrlValue: is VAL_BOOL\n"); + #endif + //if(midi_val > ((ctlmx - ctlmn)/2 + ctlmn)) + if((normval * frng + fmin) > (frng/2.0 + fmin)) + return fmax; + else + return fmin; + } + + printf("midi2AudioCtrlValue: unknown audio controller type:%d\n", aud_t); + return 0.0; +} + +//--------------------------------------------------------- +// Midi to audio controller stuff +//--------------------------------------------------------- + +MidiAudioCtrlStruct::MidiAudioCtrlStruct() +{ + _audio_ctrl_id = 0; +}; + +MidiAudioCtrlStruct::MidiAudioCtrlStruct(int audio_ctrl_id) : _audio_ctrl_id(audio_ctrl_id) +{ +}; + +MidiAudioCtrlMap_idx_t MidiAudioCtrlMap::index_hash(int midi_port, int midi_chan, int midi_ctrl_num) const +{ + return ((MidiAudioCtrlMap_idx_t(midi_port) & 0xff) << 24) | + ((MidiAudioCtrlMap_idx_t(midi_chan) & 0xf) << 20) | + (MidiAudioCtrlMap_idx_t(midi_ctrl_num) & 0xfffff); +} + +void MidiAudioCtrlMap::hash_values(MidiAudioCtrlMap_idx_t hash, int* midi_port, int* midi_chan, int* midi_ctrl_num) const +{ + if(midi_ctrl_num) + *midi_ctrl_num = hash & 0xfffff; + if(midi_chan) + *midi_chan = (hash >> 20) & 0xf; + if(midi_port) + *midi_port = (hash >> 24) & 0xff; +} + +iMidiAudioCtrlMap MidiAudioCtrlMap::add_ctrl_struct(int midi_port, int midi_chan, int midi_ctrl_num, + const MidiAudioCtrlStruct& macs) +{ + MidiAudioCtrlMap_idx_t h = index_hash(midi_port, midi_chan, midi_ctrl_num); + std::pair range = equal_range(h); + for(iMidiAudioCtrlMap imacp = range.first; imacp != range.second; ++imacp) + if(imacp->second.audioCtrlId() == macs.audioCtrlId()) + return imacp; + return insert(std::pair(h, macs)); +} +void MidiAudioCtrlMap::erase_ctrl_struct(int midi_port, int midi_chan, int midi_ctrl_num, int audio_ctrl_id) +{ + MidiAudioCtrlMap_idx_t h = index_hash(midi_port, midi_chan, midi_ctrl_num); + std::pair range = equal_range(h); + MidiAudioCtrlMap macm; + macm.insert(range.first, range.second); + for(iMidiAudioCtrlMap imacm = macm.begin(); imacm != macm.end(); ++imacm) + if(imacm->second.audioCtrlId() == audio_ctrl_id) + erase(imacm); +} + +void MidiAudioCtrlMap::find_audio_ctrl_structs(int audio_ctrl_id, AudioMidiCtrlStructMap* amcs) //const +{ + for(iMidiAudioCtrlMap imacm = begin(); imacm != end(); ++imacm) + if(imacm->second.audioCtrlId() == audio_ctrl_id) + amcs->push_back(imacm); +} + +void MidiAudioCtrlMap::write(int level, Xml& xml) const +{ + for(ciMidiAudioCtrlMap imacm = begin(); imacm != end(); ++imacm) + { + int port, chan, mctrl; + hash_values(imacm->first, &port, &chan, &mctrl); + int actrl = imacm->second.audioCtrlId(); + QString s= QString("midiMapper port=\"%1\" ch=\"%2\" mctrl=\"%3\" actrl=\"%4\"") + .arg(port) + .arg(chan) + .arg(mctrl) + .arg(actrl); + xml.tag(level++, s.toAscii().constData()); + + // TODO + //const MidiAudioCtrlStruct& macs = imacs->second; + //xml.intTag(level, "macs ???", macs.); + + xml.etag(level--, "midiMapper"); + } +} + +//--------------------------------------------------------- +// read +//--------------------------------------------------------- + +void MidiAudioCtrlMap::read(Xml& xml) + { + int port = -1, chan = -1, midi_ctrl = -1; + MidiAudioCtrlStruct macs(-1); + + QLocale loc = QLocale::c(); + bool ok; + int errcount = 0; + for (;;) { + Xml::Token token = xml.parse(); + const QString& tag = xml.s1(); + switch (token) { + case Xml::Error: + case Xml::End: + return; + case Xml::Attribut: + if (tag == "port") + { + port = loc.toInt(xml.s2(), &ok); + if(!ok) + { + ++errcount; + printf("MidiAudioCtrlPortMap::read failed reading port string: %s\n", xml.s2().toLatin1().constData()); + } + } + else if (tag == "ch") + { + chan = loc.toInt(xml.s2(), &ok); + if(!ok) + { + ++errcount; + printf("MidiAudioCtrlPortMap::read failed reading ch string: %s\n", xml.s2().toLatin1().constData()); + } + } + else if (tag == "mctrl") + { + midi_ctrl = loc.toInt(xml.s2(), &ok); + if(!ok) + { + ++errcount; + printf("MidiAudioCtrlPortMap::read failed reading mctrl string: %s\n", xml.s2().toLatin1().constData()); + } + } + else if (tag == "actrl") + { + macs.setAudioCtrlId(loc.toInt(xml.s2(), &ok)); + if(!ok) + { + ++errcount; + printf("MidiAudioCtrlPortMap::read failed reading actrl string: %s\n", xml.s2().toLatin1().constData()); + } + } + else + printf("unknown tag %s\n", tag.toLatin1().constData()); + break; + case Xml::TagStart: + // TODO + //if (tag == "???") { + // } + //else + xml.unknown("midiMapper"); + break; + case Xml::TagEnd: + if (xml.s1() == "midiMapper") + { + if(errcount == 0 && port != -1 && chan != -1 && midi_ctrl != -1 && macs.audioCtrlId() != -1) + add_ctrl_struct(port, chan, midi_ctrl, macs); + return; + } + default: + break; + } + } + } //--------------------------------------------------------- // CtrlList //--------------------------------------------------------- @@ -62,6 +327,7 @@ CtrlList::CtrlList() _mode = INTERPOLATE; _dontShow = false; _visible = false; + _guiUpdatePending = false; initColor(0); } @@ -73,6 +339,7 @@ CtrlList::CtrlList(int id) _mode = INTERPOLATE; _dontShow = false; _visible = false; + _guiUpdatePending = false; initColor(id); } @@ -88,6 +355,7 @@ CtrlList::CtrlList(int id, QString name, double min, double max, CtrlValueType v _valueType = v; _dontShow = dontShow; _visible = false; + _guiUpdatePending = false; initColor(id); } @@ -115,39 +383,54 @@ void CtrlList::assign(const CtrlList& l, int flags) if(flags & ASSIGN_VALUES) { *this = l; // Let the vector assign values. + _guiUpdatePending = true; } } //--------------------------------------------------------- // value +// Returns value at frame. +// cur_val_only means read the current 'manual' value, not from the list even if it is not empty. +// If passed a nextFrame, sets nextFrame to the next event frame, or -1 if no next frame (wide-open), or, +// since CtrlList is a map, ZERO if should be replaced with some other frame by the caller (interpolation). //--------------------------------------------------------- -double CtrlList::value(int frame) const +double CtrlList::value(int frame, bool cur_val_only, int* nextFrame) const { - if(empty()) + if(cur_val_only || empty()) + { + if(nextFrame) + *nextFrame = -1; return _curVal; + } double rv; - ciCtrl i = upper_bound(frame); // get the index after current frame + int nframe; + ciCtrl i = upper_bound(frame); // get the index after current frame if (i == end()) { // if we are past all items just return the last value --i; - rv = i->second.val; + if(nextFrame) + *nextFrame = -1; + return i->second.val; } else if(_mode == DISCRETE) { if(i == begin()) { + nframe = i->second.frame; rv = i->second.val; } else { + nframe = i->second.frame; --i; rv = i->second.val; } } - else { + else { // INTERPOLATE if (i == begin()) { + nframe = i->second.frame; rv = i->second.val; } else { @@ -157,6 +440,12 @@ double CtrlList::value(int frame) const int frame1 = i->second.frame; double val1 = i->second.val; + + if(val2 != val1) + nframe = 0; // Zero signifies the next frame should be determined by caller. + else + nframe = frame2; + if (_valueType == VAL_LOG) { val1 = 20.0*fast_log10(val1); if (val1 < MusEGlobal::config.minSlider) @@ -166,10 +455,8 @@ double CtrlList::value(int frame) const val2=MusEGlobal::config.minSlider; } - frame -= frame1; val2 -= val1; - frame2 -= frame1; - val1 += (double(frame) * val2)/double(frame2); + val1 += (double(frame - frame1) * val2)/double(frame2 - frame1); if (_valueType == VAL_LOG) { val1 = exp10(val1/20.0); @@ -178,6 +465,10 @@ double CtrlList::value(int frame) const rv = val1; } } + + if(nextFrame) + *nextFrame = nframe; + return rv; } @@ -196,7 +487,101 @@ double CtrlList::curVal() const //--------------------------------------------------------- void CtrlList::setCurVal(double val) { +#ifdef _CTRL_DEBUG_ + printf("CtrlList::setCurVal val:%f\n", val); +#endif + + bool upd = (val != _curVal); _curVal = val; + // If empty, any controller graphs etc. will be displaying this value. + // Otherwise they'll be displaying the list, so update is not required. + if(empty() && upd) + _guiUpdatePending = true; +} + +//--------------------------------------------------------- +// +// Catch all insert, erase, clear etc. +// +//--------------------------------------------------------- + +CtrlList& CtrlList::operator=(const CtrlList& cl) +{ +#ifdef _CTRL_DEBUG_ + printf("CtrlList::operator= id:%d\n", cl.id()); +#endif + std::map >::operator=(cl); + _guiUpdatePending = true; + return *this; +} + +void CtrlList::swap(CtrlList& cl) +{ +#ifdef _CTRL_DEBUG_ + printf("CtrlList::swap id:%d\n", cl.id()); +#endif + std::map >::swap(cl); + cl.setGuiUpdatePending(true); + _guiUpdatePending = true; +} + +std::pair CtrlList::insert(const std::pair& p) +{ +#ifdef _CTRL_DEBUG_ + printf("CtrlList::insert frame:%d val:%f\n", p.first, p.second.val); +#endif + std::pair res = std::map >::insert(p); + _guiUpdatePending = true; + return res; +} + +iCtrl CtrlList::insert(iCtrl ic, const std::pair& p) +{ +#ifdef _CTRL_DEBUG_ + printf("CtrlList::insert2 frame:%d val:%f\n", p.first, p.second.val); +#endif + iCtrl res = std::map >::insert(ic, p); + _guiUpdatePending = true; + return res; +} + +void CtrlList::erase(iCtrl ictl) +{ +#ifdef _CTRL_DEBUG_ + printf("CtrlList::erase iCtrl frame:%d val:%f\n", ictl->second.frame, ictl->second.val); +#endif + std::map >::erase(ictl); + _guiUpdatePending = true; +} + +std::map >::size_type CtrlList::erase(int frame) +{ +#ifdef _CTRL_DEBUG_ + printf("CtrlList::erase frame:%d\n", frame); +#endif + std::map >::size_type res = std::map >::erase(frame); + _guiUpdatePending = true; + return res; +} + +void CtrlList::erase(iCtrl first, iCtrl last) +{ +#ifdef _CTRL_DEBUG_ + printf("CtrlList::erase range first frame:%d val:%f second frame:%d val:%f\n", + first->second.frame, first->second.val, + last->second.frame, last->second.val); +#endif + std::map >::erase(first, last); + _guiUpdatePending = true; +} + +void CtrlList::clear() +{ +#ifdef _CTRL_DEBUG_ + printf("CtrlList::clear\n"); +#endif + std::map >::clear(); + _guiUpdatePending = true; } //--------------------------------------------------------- @@ -208,7 +593,15 @@ void CtrlList::add(int frame, double val) { iCtrl e = find(frame); if (e != end()) + { + bool upd = (val != e->second.val); e->second.val = val; +#ifdef _CTRL_DEBUG_ + printf("CtrlList::add frame:%d val:%f\n", frame, val); +#endif + if(upd) + _guiUpdatePending = true; + } else insert(std::pair (frame, CtrlVal(frame, val))); } @@ -234,7 +627,13 @@ void CtrlList::del(int frame) void CtrlList::updateCurValue(int frame) { - _curVal = value(frame); + double v = value(frame); + bool upd = (v != _curVal); + _curVal = v; + // If empty, any controller graphs etc. will be displaying this value. + // Otherwise they'll be displaying the list, so update is not required. + if(empty() && upd) + _guiUpdatePending = true; } //--------------------------------------------------------- @@ -361,18 +760,23 @@ void CtrlListList::add(CtrlList* vl) //--------------------------------------------------------- // value +// Returns value at frame for controller with id ctrlId. +// cur_val_only means read the current 'manual' value, not from the list even if it is not empty. +// If passed a nextFrame, sets nextFrame to the next event frame, or -1 if no next frame (wide-open), or, +// since CtrlList is a map, ZERO if should be replaced with some other frame by the caller (interpolation). //--------------------------------------------------------- -double CtrlListList::value(int ctrlId, int frame, bool cur_val_only) const +double CtrlListList::value(int ctrlId, int frame, bool cur_val_only, int* nextFrame) const { ciCtrlList cl = find(ctrlId); if (cl == end()) - return 0.0; - - if(cur_val_only) - return cl->second->curVal(); + { + if(nextFrame) + *nextFrame = -1; + return 0.0; + } - return cl->second->value(frame); + return cl->second->value(frame, cur_val_only, nextFrame); } //--------------------------------------------------------- @@ -391,5 +795,36 @@ void CtrlListList::updateCurValues(int frame) for(ciCtrlList cl = begin(); cl != end(); ++cl) cl->second->updateCurValue(frame); } - + +//--------------------------------------------------------- +// value +//--------------------------------------------------------- + +void CtrlListList::write(int level, Xml& xml) const +{ + for (ciCtrlList icl = begin(); icl != end(); ++icl) { + const CtrlList* cl = icl->second; + + QString s= QString("controller id=\"%1\" cur=\"%2\"").arg(cl->id()).arg(cl->curVal()).toAscii().constData(); + s += QString(" color=\"%1\" visible=\"%2\"").arg(cl->color().name()).arg(cl->isVisible()); + xml.tag(level++, s.toAscii().constData()); + int i = 0; + for (ciCtrl ic = cl->begin(); ic != cl->end(); ++ic) { + QString s("%1 %2, "); + xml.nput(level, s.arg(ic->second.frame).arg(ic->second.val).toAscii().constData()); + ++i; + if (i >= 4) { + xml.put(level, ""); + i = 0; + } + } + if (i) + xml.put(level, ""); + xml.etag(level--, "controller"); + } + + _midi_controls.write(level, xml); +} + + } // namespace MusECore diff --git a/muse2/muse/ctrl.h b/muse2/muse/ctrl.h index 687c5610..c56abe28 100644 --- a/muse2/muse/ctrl.h +++ b/muse2/muse/ctrl.h @@ -6,7 +6,7 @@ // controller for mixer automation // // (C) Copyright 2003-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Time E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2011-2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -29,7 +29,9 @@ #include #include +#include #include +#include #define AC_PLUGIN_CTL_BASE 0x1000 #define AC_PLUGIN_CTL_BASE_POW 12 @@ -84,6 +86,47 @@ class CtrlRecList : public std::list { typedef CtrlRecList::iterator iCtrlRec; +//--------------------------------------------------------- +// MidiAudioCtrlMap +// Describes midi control of audio controllers +//--------------------------------------------------------- + +class MidiAudioCtrlStruct { + int _audio_ctrl_id; + public: + MidiAudioCtrlStruct(); + MidiAudioCtrlStruct(int audio_ctrl_id); + int audioCtrlId() const { return _audio_ctrl_id; } + void setAudioCtrlId(int actrl) { _audio_ctrl_id = actrl; } + }; + +typedef uint32_t MidiAudioCtrlMap_idx_t; + +typedef std::multimap >::iterator iMidiAudioCtrlMap; +typedef std::multimap >::const_iterator ciMidiAudioCtrlMap; + +// Reverse lookup based on audio control. +typedef std::vector::iterator iAudioMidiCtrlStructMap; +typedef std::vector::const_iterator ciAudioMidiCtrlStructMap; +class AudioMidiCtrlStructMap : public std::vector { + public: + + }; + +// Midi to audio controller map. +// The index is a hash of port, chan, and midi control number. +class MidiAudioCtrlMap : public std::multimap > { + public: + MidiAudioCtrlMap_idx_t index_hash(int midi_port, int midi_chan, int midi_ctrl_num) const; + void hash_values(MidiAudioCtrlMap_idx_t hash, int* midi_port, int* midi_chan, int* midi_ctrl_num) const; + iMidiAudioCtrlMap add_ctrl_struct(int midi_port, int midi_chan, int midi_ctrl_num, const MidiAudioCtrlStruct& amcs); + void find_audio_ctrl_structs(int audio_ctrl_id, AudioMidiCtrlStructMap* amcs); // const; + void erase_ctrl_struct(int midi_port, int midi_chan, int midi_ctrl_num, int audio_ctrl_id); + void write(int level, Xml& xml) const; + void read(Xml& xml); + }; + + //--------------------------------------------------------- // CtrlList // arrange controller events of a specific type in a @@ -109,6 +152,7 @@ class CtrlList : public std::map > { QColor _displayColor; bool _visible; bool _dontShow; // when this is true the control exists but is not compatible with viewing in the arranger + volatile bool _guiUpdatePending; // Gui heartbeat routines read this. Checked and cleared in Song::beat(). void initColor(int i); public: @@ -117,6 +161,15 @@ class CtrlList : public std::map > { CtrlList(int id, QString name, double min, double max, CtrlValueType v, bool dontShow=false); void assign(const CtrlList& l, int flags); + void swap(CtrlList&); + std::pair insert(const std::pair& p); + iCtrl insert(iCtrl ic, const std::pair& p); + void erase(iCtrl ictl); + size_type erase(int frame); + void erase(iCtrl first, iCtrl last); + void clear(); + CtrlList& operator=(const CtrlList&); + Mode mode() const { return _mode; } void setMode(Mode m) { _mode = m; } double getDefault() const { return _default; } @@ -138,7 +191,7 @@ class CtrlList : public std::map > { CtrlValueType valueType() const { return _valueType; } void setValueType(CtrlValueType t) { _valueType = t; } - double value(int frame) const; + double value(int frame, bool cur_val_only = false, int* nextFrame = NULL) const; void add(int frame, double value); void del(int frame); void read(Xml& xml); @@ -148,6 +201,8 @@ class CtrlList : public std::map > { void setVisible(bool v) { _visible = v; } bool isVisible() const { return _visible; } bool dontShow() const { return _dontShow; } + bool guiUpdatePending() const { return _guiUpdatePending; } + void setGuiUpdatePending(bool v) { _guiUpdatePending = v; } }; //--------------------------------------------------------- @@ -161,6 +216,8 @@ typedef std::map >::iterator iCtrlList; typedef std::map >::const_iterator ciCtrlList; class CtrlListList : public std::map > { + private: + MidiAudioCtrlMap _midi_controls; // For midi control of audio controllers. public: void add(CtrlList* vl); void clearDelete() { @@ -176,14 +233,19 @@ class CtrlListList : public std::map > { return std::map >::find(id); } - double value(int ctrlId, int frame, bool cur_val_only = false) const; + MidiAudioCtrlMap* midiControls() { return &_midi_controls; } + + double value(int ctrlId, int frame, bool cur_val_only = false, int* nextFrame = NULL) const; void updateCurValues(int frame); void clearAllAutomation() { for(iCtrlList i = begin(); i != end(); ++i) i->second->clear(); } + void write(int level, Xml& xml) const; }; +extern double midi2AudioCtrlValue(const CtrlList* audio_ctrl_list, const MidiAudioCtrlStruct* mapper, int midi_ctlnum, int midi_val); + } // namespace MusECore #endif diff --git a/muse2/muse/driver/alsamidi.cpp b/muse2/muse/driver/alsamidi.cpp index f75b9c33..e3e71365 100644 --- a/muse2/muse/driver/alsamidi.cpp +++ b/muse2/muse/driver/alsamidi.cpp @@ -1318,24 +1318,24 @@ void alsaProcessMidiInput() break; case SND_SEQ_EVENT_CLOCK: - MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_CLOCK); + MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_CLOCK, curTime()); //mdev->syncInfo().trigMCSyncDetect(); break; case SND_SEQ_EVENT_START: - MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_START); + MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_START, curTime()); break; case SND_SEQ_EVENT_CONTINUE: - MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_CONTINUE); + MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_CONTINUE, curTime()); break; case SND_SEQ_EVENT_STOP: - MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_STOP); + MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_STOP, curTime()); break; case SND_SEQ_EVENT_TICK: - MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_TICK); + MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_TICK, curTime()); //mdev->syncInfo().trigTickDetect(); break; diff --git a/muse2/muse/driver/jack.cpp b/muse2/muse/driver/jack.cpp index 4cc8bfb8..05d47955 100644 --- a/muse2/muse/driver/jack.cpp +++ b/muse2/muse/driver/jack.cpp @@ -42,6 +42,7 @@ #include "tempo.h" #include "sync.h" #include "utils.h" +#include "gconfig.h" #include "midi.h" #include "mididev.h" @@ -50,7 +51,7 @@ #include "jackmidi.h" -#define JACK_DEBUG 0 +#define JACK_DEBUG 0 //#include "errorhandler.h" @@ -176,7 +177,7 @@ int JackAudioDevice::processAudio(jack_nframes_t frames, void*) } } } - + //if(jackAudio->getState() != Audio::START_PLAY) // Don't process while we're syncing. TODO: May need to deliver silence in process! MusEGlobal::audio->process((unsigned long)frames); } @@ -196,8 +197,21 @@ int JackAudioDevice::processAudio(jack_nframes_t frames, void*) static int processSync(jack_transport_state_t state, jack_position_t* pos, void*) { if (JACK_DEBUG) - printf("processSync()\n"); + { + printf("processSync frame:%u\n", pos->frame); + if(pos->valid & JackPositionBBT) + { + if(JACK_DEBUG) + { + printf("processSync BBT:\n bar:%d beat:%d tick:%d\n bar_start_tick:%f beats_per_bar:%f beat_type:%f ticks_per_beat:%f beats_per_minute:%f\n", + pos->bar, pos->beat, pos->tick, pos->bar_start_tick, pos->beats_per_bar, pos->beat_type, pos->ticks_per_beat, pos->beats_per_minute); + if(pos->valid & JackBBTFrameOffset) + printf("processSync BBTFrameOffset: %u\n", pos->bbt_offset); + } + } + } + if(!MusEGlobal::useJackTransport.value()) return 1; @@ -237,49 +251,54 @@ static int processSync(jack_transport_state_t state, jack_position_t* pos, void* //--------------------------------------------------------- static void timebase_callback(jack_transport_state_t /* state */, - jack_nframes_t /* nframes */, + jack_nframes_t nframes, jack_position_t* pos, - int /* new_pos */, + int new_pos, void*) { - if (JACK_DEBUG) - printf("Jack timebase_callback pos->frame:%u MusEGlobal::audio->tickPos:%d MusEGlobal::song->cpos:%d\n", pos->frame, MusEGlobal::audio->tickPos(), MusEGlobal::song->cpos()); - - //Pos p(pos->frame, false); + + if (JACK_DEBUG) + { + if(pos->valid & JackPositionBBT) + printf("timebase_callback BBT:\n bar:%d beat:%d tick:%d\n bar_start_tick:%f beats_per_bar:%f beat_type:%f ticks_per_beat:%f beats_per_minute:%f\n", + pos->bar, pos->beat, pos->tick, pos->bar_start_tick, pos->beats_per_bar, pos->beat_type, pos->ticks_per_beat, pos->beats_per_minute); + if(pos->valid & JackBBTFrameOffset) + printf("timebase_callback BBTFrameOffset: %u\n", pos->bbt_offset); + if(pos->valid & JackPositionTimecode) + printf("timebase_callback JackPositionTimecode: frame_time:%f next_time:%f\n", pos->frame_time, pos->next_time); + if(pos->valid & JackAudioVideoRatio) + printf("timebase_callback JackAudioVideoRatio: %f\n", pos->audio_frames_per_video_frame); + if(pos->valid & JackVideoFrameOffset) + printf("timebase_callback JackVideoFrameOffset: %u\n", pos->video_offset); + } + + //Pos p(pos->frame, false); Pos p(MusEGlobal::extSyncFlag.value() ? MusEGlobal::audio->tickPos() : pos->frame, MusEGlobal::extSyncFlag.value() ? true : false); // Can't use song pos - it is only updated every (slow) GUI heartbeat ! //Pos p(MusEGlobal::extSyncFlag.value() ? MusEGlobal::song->cpos() : pos->frame, MusEGlobal::extSyncFlag.value() ? true : false); pos->valid = JackPositionBBT; p.mbt(&pos->bar, &pos->beat, &pos->tick); + pos->bar_start_tick = Pos(pos->bar, 0, 0).tick(); pos->bar++; pos->beat++; - pos->bar_start_tick = Pos(pos->bar, 0, 0).tick(); - - // - // dummy: - // - //pos->beats_per_bar = 4; - //pos->beat_type = 4; - //pos->ticks_per_beat = 384; - // - /* // From example client transport.c : - float time_beats_per_bar = 4.0; - float time_beat_type = 0.25; // Huh? Inverted? From docs: "Time signature 'denominator'" - double time_ticks_per_beat = 1920.0; // Huh? Ticks per beat should be 24 etc. not 384 or 1920 etc. Otherwise it would be called 'frames_per_beat'. - double time_beats_per_minute = 120.0; - */ - // int z, n; AL::sigmap.timesig(p.tick(), z, n); pos->beats_per_bar = z; pos->beat_type = n; - //pos->ticks_per_beat = config.division; - pos->ticks_per_beat = 24; + pos->ticks_per_beat = MusEGlobal::config.division; + //pos->ticks_per_beat = 24; + + double tempo = MusEGlobal::tempomap.tempo(p.tick()); + pos->beats_per_minute = (60000000.0 / tempo) * double(MusEGlobal::tempomap.globalTempo())/100.0; + if (JACK_DEBUG) + { + printf("timebase_callback is new_pos:%d nframes:%u frame:%u tickPos:%d cpos:%d\n", new_pos, nframes, pos->frame, MusEGlobal::audio->tickPos(), MusEGlobal::song->cpos()); + printf(" new: bar:%d beat:%d tick:%d\n bar_start_tick:%f beats_per_bar:%f beat_type:%f ticks_per_beat:%f beats_per_minute:%f\n", + pos->bar, pos->beat, pos->tick, pos->bar_start_tick, pos->beats_per_bar, pos->beat_type, pos->ticks_per_beat, pos->beats_per_minute); + } - int tempo = MusEGlobal::tempomap.tempo(p.tick()); - pos->beats_per_minute = (60000000.0 / tempo) * MusEGlobal::tempomap.globalTempo()/100.0; } //--------------------------------------------------------- @@ -1225,6 +1244,69 @@ jack_transport_state_t JackAudioDevice::transportQuery(jack_position_t* pos) return jack_transport_query(_client, pos); } +//--------------------------------------------------------- +// timebaseQuery +// Given the number of frames in this period, get the bar, beat, tick, +// and current absolute tick, and number of ticks in this period. +// Return false if information could not be obtained. +//--------------------------------------------------------- + +bool JackAudioDevice::timebaseQuery(unsigned frames, unsigned* bar, unsigned* beat, unsigned* tick, unsigned* curr_abs_tick, unsigned* next_ticks) +{ + jack_position_t jp; + jack_transport_query(_client, &jp); + + if(JACK_DEBUG) + printf("timebaseQuery frame:%u\n", jp.frame); + + if(jp.valid & JackPositionBBT) + { + if(JACK_DEBUG) + { + printf("timebaseQuery BBT:\n bar:%d beat:%d tick:%d\n bar_start_tick:%f beats_per_bar:%f beat_type:%f ticks_per_beat:%f beats_per_minute:%f\n", + jp.bar, jp.beat, jp.tick, jp.bar_start_tick, jp.beats_per_bar, jp.beat_type, jp.ticks_per_beat, jp.beats_per_minute); + if(jp.valid & JackBBTFrameOffset) + printf("timebaseQuery BBTFrameOffset: %u\n", jp.bbt_offset); + } + + if(jp.ticks_per_beat > 0.0) + { + unsigned muse_tick = unsigned((double(jp.tick) / jp.ticks_per_beat) * double(MusEGlobal::config.division)); + unsigned curr_tick = ((jp.bar - 1) * jp.beats_per_bar + (jp.beat - 1)) * double(MusEGlobal::config.division) + muse_tick; + // Prefer the reported frame rate over the app's rate if possible. + double f_rate = jp.frame_rate != 0 ? jp.frame_rate : MusEGlobal::sampleRate; + // beats_per_minute is "supposed" to be quantized to period size - that is, computed + // so that mid-period changes are averaged out to produce a single tempo which + // produces the same tick in the end. If we can rely on that, we should be good accuracy. + unsigned ticks = double(MusEGlobal::config.division) * (jp.beats_per_minute / 60.0) * double(frames) / f_rate; + + if(JACK_DEBUG) + printf("timebaseQuery curr_tick:%u f_rate:%f ticks:%u\n", curr_tick, f_rate, ticks); + + if(bar) *bar = jp.bar; + if(beat) *beat = jp.beat; + if(tick) *tick = muse_tick; + + if(curr_abs_tick) *curr_abs_tick = curr_tick; + if(next_ticks) *next_ticks = ticks; + + return true; + } + } + + if(JACK_DEBUG) + { + if(jp.valid & JackPositionTimecode) + printf("timebaseQuery JackPositionTimecode: frame_time:%f next_time:%f\n", jp.frame_time, jp.next_time); + if(jp.valid & JackAudioVideoRatio) + printf("timebaseQuery JackAudioVideoRatio: %f\n", jp.audio_frames_per_video_frame); + if(jp.valid & JackVideoFrameOffset) + printf("timebaseQuery JackVideoFrameOffset: %u\n", jp.video_offset); + } + + return false; +} + //--------------------------------------------------------- // systemTime // Return system time. Depends on selected clock source. @@ -1617,7 +1699,7 @@ void JackAudioDevice::seekTransport(unsigned frame) void JackAudioDevice::seekTransport(const Pos &p) { if (JACK_DEBUG) - printf("JackAudioDevice::seekTransport() frame:%d\n", p.frame()); + printf("JackAudioDevice::seekTransport(Pos) frame:%d\n", p.frame()); if(!MusEGlobal::useJackTransport.value()) { @@ -1628,25 +1710,29 @@ void JackAudioDevice::seekTransport(const Pos &p) } if(!checkJackClient(_client)) return; + +// TODO: Be friendly to other apps... Sadly not many of us use jack_transport_reposition. +// This is actually required IF we want the extra position info to show up +// in the sync callback, otherwise we get just the frame only. +// This information is shared on the server, it is directly passed around. +// jack_transport_locate blanks the info from sync until the timebase callback reads +// it again right after, from some timebase master. See process in audio.cpp + +// jack_position_t jp; +// jp.frame = p.frame(); +// +// jp.valid = JackPositionBBT; +// p.mbt(&jp.bar, &jp.beat, &jp.tick); +// jp.bar_start_tick = Pos(jp.bar, 0, 0).tick(); +// jp.bar++; +// jp.beat++; +// jp.beats_per_bar = 5; // TODO Make this correct ! +// jp.beat_type = 8; // +// jp.ticks_per_beat = MusEGlobal::config.division; +// int tempo = MusEGlobal::tempomap.tempo(p.tick()); +// jp.beats_per_minute = (60000000.0 / tempo) * MusEGlobal::tempomap.globalTempo()/100.0; +// jack_transport_reposition(_client, &jp); - /* - jack_position_t jp; - jp.valid = JackPositionBBT; - p.mbt(&jp.bar, &jp.beat, &jp.tick); - jp.bar++; - jp.beat++; - jp.bar_start_tick = Pos(jp.bar, 0, 0).tick(); - // - // dummy: - // - jp.beats_per_bar = 4; - jp.beat_type = 4; - jp.ticks_per_beat = 384; - int tempo = MusEGlobal::tempomap.tempo(p.tick()); - jp.beats_per_minute = (60000000.0 / tempo) * MusEGlobal::tempomap.globalTempo()/100.0; - - jack_transport_reposition(_client, &jp); - */ jack_transport_locate(_client, p.frame()); } diff --git a/muse2/muse/driver/jackaudio.h b/muse2/muse/driver/jackaudio.h index 9640ca81..aab60d88 100644 --- a/muse2/muse/driver/jackaudio.h +++ b/muse2/muse/driver/jackaudio.h @@ -80,6 +80,7 @@ class JackAudioDevice : public AudioDevice { virtual std::list outputPorts(bool midi = false, int aliases = -1); virtual std::list inputPorts(bool midi = false, int aliases = -1); + jack_client_t* jackClient() const { return _client; } virtual void registerClient(); virtual const char* clientName() { return jackRegisteredName; } @@ -105,6 +106,7 @@ class JackAudioDevice : public AudioDevice { virtual void seekTransport(const Pos &p); virtual void setFreewheel(bool f); jack_transport_state_t transportQuery(jack_position_t* pos); + bool timebaseQuery(unsigned frames, unsigned* bar, unsigned* beat, unsigned* tick, unsigned* curr_abs_tick, unsigned* next_ticks); void graphChanged(); void registrationChanged(); void connectJackMidiPorts(); diff --git a/muse2/muse/driver/jackmidi.cpp b/muse2/muse/driver/jackmidi.cpp index 706fa269..e3e67dfb 100644 --- a/muse2/muse/driver/jackmidi.cpp +++ b/muse2/muse/driver/jackmidi.cpp @@ -30,6 +30,7 @@ //#include #include "jackmidi.h" +#include "jackaudio.h" #include "song.h" #include "globals.h" #include "midi.h" @@ -50,10 +51,6 @@ // Turn on debug messages. //#define JACK_MIDI_DEBUG -namespace MusEGlobal { -extern unsigned int volatile lastExtMidiSyncTick; -} - namespace MusECore { //--------------------------------------------------------- @@ -422,7 +419,7 @@ void MidiJackDevice::recordEvent(MidiRecordEvent& event) // Split the events up into channel fifos. Special 'channel' number 17 for sysex events. unsigned int ch = (typ == ME_SYSEX)? MIDI_CHANNELS : event.channel(); - if(_recordFifo[ch].put(MidiPlayEvent(event))) + if(_recordFifo[ch].put(event)) printf("MidiJackDevice::recordEvent: fifo channel %d overflow\n", ch); } @@ -446,10 +443,18 @@ void MidiJackDevice::eventReceived(jack_midi_event_t* ev) // catch process play // - //int frameOffset = MusEGlobal::audio->getFrameOffset(); - unsigned pos = MusEGlobal::audio->pos().frame(); - - event.setTime(MusEGlobal::extSyncFlag.value() ? MusEGlobal::lastExtMidiSyncTick : (pos + ev->time)); // p3.3.25 + // These Jack events arrived in the previous period, and it may not have been at the audio position before this one (after a seek). + // This is how our ALSA driver works, events there are timestamped asynchronous of any process, referenced to the CURRENT audio + // position, so that by the time of the NEXT process, THOSE events have also occured in the previous period. + // So, technically this is correct. What MATTERS is how we adjust the times for storage, and/or simultaneous playback in THIS period, + // and TEST: we'll need to make sure any non-contiguous previous period is handled correctly by process - will it work OK as is? + // If ALSA works OK than this should too... +#ifdef _AUDIO_USE_TRUE_FRAME_ + event.setTime(MusEGlobal::audio->previousPos().frame() + ev->time); +#else + event.setTime(MusEGlobal::audio->pos().frame() + ev->time); +#endif + event.setTick(MusEGlobal::lastExtMidiSyncTick); event.setChannel(*(ev->buffer) & 0xf); int type = *(ev->buffer) & 0xf0; @@ -509,9 +514,20 @@ void MidiJackDevice::eventReceived(jack_midi_event_t* ev) case ME_START: case ME_CONTINUE: case ME_STOP: - if(_port != -1) - MusEGlobal::midiSeq->realtimeSystemInput(_port, type); + { + if(MusEGlobal::audioDevice && MusEGlobal::audioDevice->deviceType() == JACK_MIDI && _port != -1) + { + MusECore::JackAudioDevice* jad = static_cast(MusEGlobal::audioDevice); + jack_client_t* jc = jad->jackClient(); + if(jc) + { + jack_nframes_t abs_ft = jack_last_frame_time(jc) + ev->time; + double abs_ev_t = double(jack_frames_to_time(jc, abs_ft)) / 1000000.0; + MusEGlobal::midiSeq->realtimeSystemInput(_port, type, abs_ev_t); + } + } return; + } //case ME_SYSEX_END: //break; // return; diff --git a/muse2/muse/dssihost.cpp b/muse2/muse/dssihost.cpp index 5000e338..01f9c0f3 100644 --- a/muse2/muse/dssihost.cpp +++ b/muse2/muse/dssihost.cpp @@ -680,7 +680,7 @@ bool DssiSynthIF::init(DssiSynth* s) // Set current program. if(dssi->select_program) - dssi->select_program(handle, synti->_curBankL, synti->_curProgram); + doSelectProgram(handle, synti->_curBankL, synti->_curProgram); // // For stored initial control values, let SynthI::initInstance() take care of that via ::setParameter(). @@ -839,27 +839,7 @@ float DssiSynthIF::getParameterOut(unsigned long n) const void DssiSynthIF::setParameter(unsigned long n, float v) { - if(n >= synth->_controlInPorts) - { - printf("DssiSynthIF::setParameter param number %lu out of range of ports:%lu\n", n, synth->_controlInPorts); - return; - } - - ControlEvent ce; - ce.unique = false; - ce.idx = n; - ce.value = v; - // Time-stamp the event. This does a possibly slightly slow call to gettimeofday via timestamp(). - // timestamp() is more or less an estimate of the current frame. (This is exactly how ALSA events - // are treated when they arrive in our ALSA driver.) - //ce.frame = MusEGlobal::audio->timestamp(); - // p4.0.23 timestamp() is circular, which is making it impossible to deal with 'modulo' events which - // slip in 'under the wire' before processing the ring buffers. So try this linear timestamp instead: - ce.frame = MusEGlobal::audio->curFrame(); - if(_controlFifo.put(ce)) - { - fprintf(stderr, "DssiSynthIF::setParameter: fifo overflow: in control number:%lu\n", n); - } + addScheduledControlEvent(n, v, MusEGlobal::audio->curFrame()); } //--------------------------------------------------------- @@ -1084,7 +1064,7 @@ bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t synti->_curProgram = prog; if(dssi->select_program) - dssi->select_program(handle, bank, prog); + doSelectProgram(handle, bank, prog); // Event pointer not filled. Return false. return false; @@ -1113,7 +1093,7 @@ bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t synti->_curProgram = prog; if(dssi->select_program) - dssi->select_program(handle, bank, prog); + doSelectProgram(handle, bank, prog); // Event pointer not filled. Return false. return false; @@ -1220,6 +1200,11 @@ bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t // Set the ladspa port value. controls[k].val = val; + // Need to update the automation value, otherwise it overwrites later with the last automation value. + if(id() != -1) + // We're in the audio thread context: no need to send a message, just modify directly. + synti->setPluginCtrlVal(genACnum(id(), k), val); + // Since we absorbed the message as a ladspa control change, return false - the event is not filled. return false; } @@ -1404,7 +1389,7 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP if(fixedsize > nframes) fixedsize = nframes; - unsigned long min_per = MusEGlobal::config.minControlProcessPeriod; + unsigned long min_per = MusEGlobal::config.minControlProcessPeriod; // Must be power of 2 ! if(min_per > nframes) min_per = nframes; @@ -1412,7 +1397,7 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP fprintf(stderr, "DssiSynthIF::getData: Handling inputs...\n"); #endif - // p4.0.38 Handle inputs... + // Handle inputs... if(!((MusECore::AudioTrack*)synti)->noInRoute()) { RouteList* irl = ((MusECore::AudioTrack*)synti)->inRoutes(); @@ -1485,21 +1470,57 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP #ifdef DSSI_DEBUG_PROCESS fprintf(stderr, "DssiSynthIF::getData: Processing automation control values...\n"); #endif - - // Process automation control values now. - // TODO: This needs to be respect frame resolution. Put this inside the sample loop below. - if(MusEGlobal::automation && synti && synti->automationType() != AUTO_OFF && id() != -1) - { - for(unsigned long k = 0; k < synth->_controlInPorts; ++k) - { - if(controls[k].enCtrl && controls[k].en2Ctrl ) - controls[k].val = (static_cast(synti))->controller()->value(genACnum(id(), k), pos); - } - } - + while(sample < nframes) { unsigned long nsamp = usefixedrate ? fixedsize : nframes - sample; + + // + // Process automation control values, while also determining the maximum acceptable + // size of this run. Further processing, from FIFOs for example, can lower the size + // from there, but this section determines where the next highest maximum frame + // absolutely needs to be for smooth playback of the controller value stream... + // + if(id() != -1) + { + unsigned long frame = pos + sample; + AutomationType at = AUTO_OFF; + at = synti->automationType(); + bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; + AudioTrack* track = (static_cast(synti)); + int nextFrame; + for(unsigned long k = 0; k < synth->_controlInPorts; ++k) + { + controls[k].val = track->controller()->value(genACnum(id(), k), frame, + no_auto || !controls[k].enCtrl || !controls[k].en2Ctrl, + &nextFrame); +#ifdef DSSI_DEBUG_PROCESS + printf("DssiSynthIF::getData k:%lu sample:%lu frame:%lu nextFrame:%d nsamp:%lu \n", k, sample, frame, nextFrame, nsamp); +#endif + if(MusEGlobal::audio->isPlaying() && !usefixedrate && nextFrame != -1) + { + // Returned value of nextFrame can be zero meaning caller replaces with some (constant) value. + unsigned long samps = (unsigned long)nextFrame; + if(samps > frame + min_per) + { + unsigned long diff = samps - frame; + unsigned long mask = min_per-1; // min_per must be power of 2 + samps = diff & ~mask; + if((diff & mask) != 0) + samps += min_per; + } + else + samps = min_per; + + if(samps < nsamp) + nsamp = samps; + } + } +#ifdef DSSI_DEBUG + printf("DssiSynthIF::getData sample:%lu nsamp:%lu\n", sample, nsamp); +#endif + } + bool found = false; unsigned long frame = 0; unsigned long index = 0; @@ -1528,11 +1549,13 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP continue; } - if(evframe >= nframes - || (found && !v.unique && (evframe - sample >= min_per)) - || (usefixedrate && found && v.unique && v.idx == index)) + if(evframe >= nframes // Next events are for a later period. + || (!usefixedrate && !found && !v.unique && (evframe - sample >= nsamp)) // Next events are for a later run in this period. (Autom took prio.) + || (found && !v.unique && (evframe - sample >= min_per)) // Eat up events within minimum slice - they're too close. + || (usefixedrate && found && v.unique && v.idx == index)) // Special for dssi-vst: Fixed rate and must reply to all. break; _controlFifo.remove(); // Done with the ring buffer's item. Remove it. + if(v.idx >= synth->_controlInPorts) // Sanity check. break; found = true; @@ -1543,36 +1566,16 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP // Need to update the automation value, otherwise it overwrites later with the last automation value. if(id() != -1) - { - // We're in the audio thread context: no need to send a message, just modify directly. synti->setPluginCtrlVal(genACnum(id(), v.idx), v.value); - - /* Record automation. DELETETHIS? - * NO! Take care of this immediately in the OSC control handler, because we don't want - * any delay. - * OTOH Since this is the actual place and time where the control ports values - * are set, best to reflect what happens here to automation. - * However for dssi-vst it might be best to handle it that way. - - // TODO: Taken from our native gui control handlers. - // This may need modification or may cause problems - - // we don't have the luxury of access to the dssi gui controls ! - AutomationType at = _track->automationType(); - if ((at == AUTO_WRITE) || - (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying())) - enableController(k, false); - _track->recordAutomation(id, v.value); - */ - } } - if(found && !usefixedrate) + if(found && !usefixedrate) // If a control FIFO item was found, takes priority over automation controller stream. nsamp = frame - sample; if(sample + nsamp >= nframes) // Safety check. nsamp = nframes - sample; - // TODO: TESTING: Don't allow zero-length runs. This could/should be checked in the control loop instead. + // TODO: Don't allow zero-length runs. This could/should be checked in the control loop instead. // Note this means it is still possible to get stuck in the top loop (at least for a while). if(nsamp == 0) continue; @@ -1586,8 +1589,13 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP #endif if(start_event->time() >= (pos + sample + nsamp + frameOffset)) // frameOffset? Test again... + { + #ifdef DSSI_DEBUG + fprintf(stderr, " event is for future:%lu, breaking loop now\n", start_event->time() - frameOffset - pos - sample); + #endif break; - + } + // Update hardware state so knobs and boxes are updated. Optimize to avoid re-setting existing values. // Same code as in MidiPort::sendEvent() if(synti->midiPort() != -1) @@ -2083,6 +2091,25 @@ void DssiSynthIF::queryPrograms() } } +void DssiSynthIF::doSelectProgram(LADSPA_Handle handle, int bank, int prog) +{ + const DSSI_Descriptor* dssi = synth->dssi; + dssi->select_program(handle, bank, prog); + + // Need to update the automation value, otherwise it overwrites later with the last automation value. + // "A plugin is permitted to re-write the values of its input control ports when select_program is called. + // The host should re-read the input control port values and update its own records appropriately. + // (This is the only circumstance in which a DSSI plugin is allowed to modify its own input ports.)" From dssi.h + if(id() != -1) + { + for(unsigned long k = 0; k < synth->_controlInPorts; ++k) + { + // We're in the audio thread context: no need to send a message, just modify directly. + synti->setPluginCtrlVal(genACnum(id(), k), controls[k].val); + } + } +} + //--------------------------------------------------------- // getPatchName //--------------------------------------------------------- @@ -2235,14 +2262,30 @@ QString DssiSynthIF::titlePrefix() const { return QString(); MusECore::AudioTrack* DssiSynthIF::track() { return (MusECore::AudioTrack*)synti; } void DssiSynthIF::enableController(unsigned long i, bool v) { controls[i].enCtrl = v; } bool DssiSynthIF::controllerEnabled(unsigned long i) const { return controls[i].enCtrl; } +void DssiSynthIF::enable2Controller(unsigned long i, bool v) { controls[i].en2Ctrl = v; } bool DssiSynthIF::controllerEnabled2(unsigned long i) const { return controls[i].en2Ctrl; } +void DssiSynthIF::enableAllControllers(bool v) +{ + if(!synth) + return; + for(unsigned long i = 0; i < synth->_controlInPorts; ++i) + controls[i].enCtrl = v; +} +void DssiSynthIF::enable2AllControllers(bool v) +{ + if(!synth) + return; + for(unsigned long i = 0; i < synth->_controlInPorts; ++i) + controls[i].en2Ctrl = v; +} + void DssiSynthIF::updateControllers() { } void DssiSynthIF::writeConfiguration(int /*level*/, Xml& /*xml*/) { } bool DssiSynthIF::readConfiguration(Xml& /*xml*/, bool /*readPreset*/) { return false; } unsigned long DssiSynthIF::parameters() const { return synth ? synth->_controlInPorts : 0; } unsigned long DssiSynthIF::parametersOut() const { return synth ? synth->_controlOutPorts : 0; } -void DssiSynthIF::setParam(unsigned long i, float val) { setParameter(i, val); } +void DssiSynthIF::setParam(unsigned long i, float val) { setParameter(i, val); } float DssiSynthIF::param(unsigned long i) const { return getParameter(i); } float DssiSynthIF::paramOut(unsigned long i) const { return getParameterOut(i); } const char* DssiSynthIF::paramName(unsigned long i) { return (synth && synth->dssi) ? synth->dssi->LADSPA_Plugin->PortNames[controls[i].idx] : 0; } diff --git a/muse2/muse/dssihost.h b/muse2/muse/dssihost.h index 238b468e..46c9a07b 100644 --- a/muse2/muse/dssihost.h +++ b/muse2/muse/dssihost.h @@ -128,6 +128,7 @@ class DssiSynthIF : public SynthIF, public PluginIBase std::vector programs; void queryPrograms(); + void doSelectProgram(LADSPA_Handle handle, int bank, int prog); bool processEvent(const MusECore::MidiPlayEvent&, snd_seq_event_t*); float** audioInBuffers; @@ -209,7 +210,10 @@ class DssiSynthIF : public SynthIF, public PluginIBase MusECore::AudioTrack* track(); void enableController(unsigned long i, bool v = true); bool controllerEnabled(unsigned long i) const; + void enable2Controller(unsigned long i, bool v = true); bool controllerEnabled2(unsigned long i) const; + void enableAllControllers(bool v = true); + void enable2AllControllers(bool v = true); void updateControllers(); void writeConfiguration(int level, Xml& xml); bool readConfiguration(Xml& xml, bool readPreset=false); diff --git a/muse2/muse/gconfig.cpp b/muse2/muse/gconfig.cpp index 1a0426a7..302007b3 100644 --- a/muse2/muse/gconfig.cpp +++ b/muse2/muse/gconfig.cpp @@ -186,7 +186,7 @@ GlobalConfigValues config = { QString("./"), // projectBaseFolder true, // projectStoreInFolder true, // useProjectSaveDialog - 64, // minControlProcessPeriod + 256, // minControlProcessPeriod false, // popupsDefaultStayOpen false, // leftMouseButtonCanDecrease false, // rangeMarkerWithoutMMB diff --git a/muse2/muse/globals.cpp b/muse2/muse/globals.cpp index d92e6abf..b3765074 100644 --- a/muse2/muse/globals.cpp +++ b/muse2/muse/globals.cpp @@ -275,6 +275,12 @@ unsigned char rcPlayNote = 29; unsigned char rcSteprecNote = 36; bool automation = true; +// Midi learn params. These will be initialized to -1 by any midi learn function, +// and then filled by the midi engine in response to the drivers. +int midiLearnPort = -1; +int midiLearnChan = -1; +int midiLearnCtrl = -1; + uid_t euid, ruid; // effective user id, real user id bool midiSeqRunning = false; diff --git a/muse2/muse/globals.h b/muse2/muse/globals.h index c64fdf89..bdf383c8 100644 --- a/muse2/muse/globals.h +++ b/muse2/muse/globals.h @@ -172,6 +172,10 @@ extern unsigned char rcGotoLeftMarkNote; extern unsigned char rcPlayNote; extern unsigned char rcSteprecNote; +extern int midiLearnPort; +extern int midiLearnChan; +extern int midiLearnCtrl; + extern bool midiSeqRunning; extern bool automation; diff --git a/muse2/muse/midi.cpp b/muse2/muse/midi.cpp index ae348b5f..503208e6 100644 --- a/muse2/muse/midi.cpp +++ b/muse2/muse/midi.cpp @@ -34,6 +34,7 @@ #include "marker/marker.h" #include "midiport.h" #include "midictrl.h" +#include "sync.h" #include "audio.h" #include "mididev.h" #include "driver/alsamidi.h" @@ -860,31 +861,149 @@ void Audio::collectEvents(MusECore::MidiTrack* track, unsigned int cts, unsigned void Audio::processMidi() { MusEGlobal::midiBusy=true; + + bool extsync = MusEGlobal::extSyncFlag.value(); + // // TODO: syntis should directly write into recordEventList // - for (iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id) { - MidiDevice* md = *id; - - // klumsy hack for synti devices: - if(md->isSynti()) + for (iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id) + { + MidiDevice* md = *id; + + // klumsy hack for MESS synti devices: + if(md->isSynti()) + { + SynthI* s = (SynthI*)md; + while (s->eventsPending()) + { + MusECore::MidiRecordEvent ev = s->receiveEvent(); + md->recordEvent(ev); + } + } + + md->collectMidiEvents(); + + // Take snapshots of the current sizes of the recording fifos, + // because they may change while here in process, asynchronously. + md->beforeProcess(); + + // + // --------- Handle midi events for audio tracks ----------- + // + + int port = md->midiPort(); // Port should be same as event.port() from this device. Same idea event.channel(). + if(port < 0) + continue; + + for(int chan = 0; chan < MIDI_CHANNELS; ++chan) + { + MusECore::MidiRecFifo& rf = md->recordEvents(chan); + int count = md->tmpRecordCount(chan); + for(int i = 0; i < count; ++i) + { + MusECore::MidiRecordEvent event(rf.peek(i)); + + int etype = event.type(); + if(etype == MusECore::ME_CONTROLLER || etype == MusECore::ME_PITCHBEND || etype == MusECore::ME_PROGRAM) { - SynthI* s = (SynthI*)md; - while (s->eventsPending()) + int ctl, val; + if(etype == MusECore::ME_CONTROLLER) { - MusECore::MidiRecordEvent ev = s->receiveEvent(); - md->recordEvent(ev); + ctl = event.dataA(); + val = event.dataB(); } + else if(etype == MusECore::ME_PITCHBEND) + { + ctl = MusECore::CTRL_PITCH; + val = event.dataA(); + } + else if(etype == MusECore::ME_PROGRAM) + { + ctl = MusECore::CTRL_PROGRAM; + val = event.dataA(); + } + + // Midi learn! + MusEGlobal::midiLearnPort = port; + MusEGlobal::midiLearnChan = chan; + MusEGlobal::midiLearnCtrl = ctl; + + // Send to audio tracks... + for (MusECore::iTrack t = MusEGlobal::song->tracks()->begin(); t != MusEGlobal::song->tracks()->end(); ++t) + { + if((*t)->isMidiTrack()) + continue; + MusECore::AudioTrack* track = static_cast(*t); + MidiAudioCtrlMap* macm = track->controller()->midiControls(); + int h = macm->index_hash(port, chan, ctl); + std::pair range = macm->equal_range(h); + for(ciMidiAudioCtrlMap imacm = range.first; imacm != range.second; ++imacm) + { + const MidiAudioCtrlStruct* macs = &imacm->second; + int actrl = macs->audioCtrlId(); + + iCtrlList icl = track->controller()->find(actrl); + if(icl == track->controller()->end()) + continue; + CtrlList* cl = icl->second; + double dval = midi2AudioCtrlValue(cl, macs, ctl, val); + + // Time here needs to be frames always. + unsigned int ev_t = event.time(); + unsigned int t = ev_t; + +#ifdef _AUDIO_USE_TRUE_FRAME_ + unsigned int pframe = _previousPos.frame(); +#else + unsigned int pframe = _pos.frame(); +#endif + if(pframe > t) // Technically that's an error, shouldn't happen + t = 0; + else + // Subtract the current audio position frame + t -= pframe; + + // Add the current running sync frame to make the control processing happy + t += syncFrame; + track->addScheduledControlEvent(actrl, dval, t); + + // Rec automation... + + // For the record time, if stopped we don't want the circular running position, + // just the static one. + unsigned int rec_t = isPlaying() ? ev_t : pframe; + + if(!MusEGlobal::automation) + continue; + AutomationType at = track->automationType(); + // Unlike our built-in gui controls, there is not much choice here but to + // just do this: + if ( (at == AUTO_WRITE) || + (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) + //if(isPlaying() && (at == AUTO_WRITE || at == AUTO_TOUCH)) DELETETHIS + track->enableController(actrl, false); + if(isPlaying()) + { + if(at == AUTO_WRITE || at == AUTO_TOUCH) + track->recEvents()->push_back(CtrlRecVal(rec_t, actrl, dval)); + } + else + { + if(at == AUTO_WRITE) + track->recEvents()->push_back(CtrlRecVal(rec_t, actrl, dval)); + else if(at == AUTO_TOUCH) + // In touch mode and not playing. Send directly to controller list. + // Add will replace if found. + cl->add(rec_t, dval); + } + } + } } - - md->collectMidiEvents(); - - // Take snapshots of the current sizes of the recording fifos, - // because they may change while here in process, asynchronously. - md->beforeProcess(); - } + } + } + } - bool extsync = MusEGlobal::extSyncFlag.value(); for (MusECore::iMidiTrack t = MusEGlobal::song->midis()->begin(); t != MusEGlobal::song->midis()->end(); ++t) { MusECore::MidiTrack* track = *t; @@ -934,15 +1053,30 @@ void Audio::processMidi() for(int i = 0; i < count; ++i) { - MusECore::MidiPlayEvent event(rf.peek(i)); + MusECore::MidiRecordEvent event(rf.peek(i)); event.setPort(port); // dont't echo controller changes back to software // synthesizer: if(!dev->isSynti() && md && track->recEcho()) + { + // All recorded events arrived in the previous period. Shift into this period for playback. + unsigned int et = event.time(); +#ifdef _AUDIO_USE_TRUE_FRAME_ + unsigned int t = et - _previousPos.frame() + _pos.frame() + frameOffset; +#else + unsigned int t = et + frameOffset; +#endif + event.setTime(t); md->addScheduledEvent(event); - // If syncing externally the event time is already in units of ticks, set above. p3.3.25 - if(!extsync) - event.setTime(MusEGlobal::tempomap.frame2tick(event.time())); // set tick time + event.setTime(et); // Restore for recording. + } + + // Make sure the event is recorded in units of ticks. + if(extsync) + event.setTime(event.tick()); // HACK: Transfer the tick to the frame time + else + event.setTime(MusEGlobal::tempomap.frame2tick(event.time())); + if(recording) rl->add(event); } @@ -953,7 +1087,7 @@ void Audio::processMidi() int count = dev->tmpRecordCount(channel); for(int i = 0; i < count; ++i) { - MusECore::MidiPlayEvent event(rf.peek(i)); + MusECore::MidiRecordEvent event(rf.peek(i)); int defaultPort = devport; int drumRecPitch=0; //prevent compiler warning: variable used without initialization MusECore::MidiController *mc = 0; @@ -1073,7 +1207,16 @@ void Audio::processMidi() if (!dev->isSynti()) { - //Check if we're outputting to another port than default: + // All recorded events arrived in previous period. Shift into this period for playback. + // frameoffset needed to make process happy. + unsigned int et = event.time(); +#ifdef _AUDIO_USE_TRUE_FRAME_ + unsigned int t = et - _previousPos.frame() + _pos.frame() + frameOffset; +#else + unsigned int t = et + frameOffset; +#endif + event.setTime(t); + // Check if we're outputting to another port than default: if (devport == defaultPort) { event.setPort(port); if(md && track->recEcho()) @@ -1085,14 +1228,18 @@ void Audio::processMidi() if(mdAlt && track->recEcho()) mdAlt->addScheduledEvent(event); } + event.setTime(et); // Restore for recording. + // Shall we activate meters even while rec echo is off? Sure, why not... if(event.isNote() && event.dataB() > track->activity()) track->setActivity(event.dataB()); } - // If syncing externally the event time is already in units of ticks, set above. p3.3.25 - if(!extsync) - event.setTime(MusEGlobal::tempomap.frame2tick(event.time())); // set tick time + // Make sure the event is recorded in units of ticks. + if(extsync) + event.setTime(event.tick()); // HACK: Transfer the tick to the frame time + else + event.setTime(MusEGlobal::tempomap.frame2tick(event.time())); // Special handling of events stored in rec-lists. a bit hACKish. TODO: Clean up (after 0.7)! :-/ (ml) if (recording) diff --git a/muse2/muse/midictrl.cpp b/muse2/muse/midictrl.cpp index b95ccf77..63ce6fe6 100644 --- a/muse2/muse/midictrl.cpp +++ b/muse2/muse/midictrl.cpp @@ -305,6 +305,39 @@ MidiController::ControllerType midiControllerType(int num) return MidiController::Controller7; } +//--------------------------------------------------------- +// midiCtrlTerms2Number +//--------------------------------------------------------- + +int midiCtrlTerms2Number(int type_num, int ctrl) +{ + ctrl &= 0xffff; + switch(type_num) + { + case MidiController::Controller7: + return ctrl & 0xff; + case MidiController::Controller14: + return CTRL_14_OFFSET + ctrl; + case MidiController::RPN: + return CTRL_RPN_OFFSET + ctrl; + case MidiController::NRPN: + return CTRL_NRPN_OFFSET + ctrl; + case MidiController::Pitch: + return CTRL_PITCH; + case MidiController::Program: + return CTRL_PROGRAM; + case MidiController::Velo: + return CTRL_VELOCITY; + case MidiController::RPN14: + return CTRL_RPN14_OFFSET + ctrl; + case MidiController::NRPN14: + return CTRL_NRPN14_OFFSET + ctrl; + default: + printf("MusE: unknown ctrl type in midiCtrTerms2Number()\n"); + return ctrl; + } +} + //--------------------------------------------------------- // updateBias //--------------------------------------------------------- diff --git a/muse2/muse/midictrl.h b/muse2/muse/midictrl.h index 74902bc2..4c9a4097 100644 --- a/muse2/muse/midictrl.h +++ b/muse2/muse/midictrl.h @@ -242,6 +242,7 @@ typedef MidiControllerList MidiControllerList; extern MidiControllerList defaultMidiController; extern void initMidiController(); extern MidiController::ControllerType midiControllerType(int num); +extern int midiCtrlTerms2Number(int type_num, int ctrl); extern const QString& int2ctrlType(int n); diff --git a/muse2/muse/mididev.cpp b/muse2/muse/mididev.cpp index becab6f7..5ff8bf94 100644 --- a/muse2/muse/mididev.cpp +++ b/muse2/muse/mididev.cpp @@ -46,7 +46,6 @@ namespace MusEGlobal { MusECore::MidiDeviceList midiDevices; -extern unsigned int volatile lastExtMidiSyncTick; } namespace MusECore { @@ -215,10 +214,14 @@ void MidiDevice::beforeProcess() void MidiDevice::recordEvent(MidiRecordEvent& event) { - // TODO: Tested, but record resolution not so good. Switch to wall clock based separate list in MidiDevice. And revert this line. - //event.setTime(MusEGlobal::audio->timestamp()); - event.setTime(MusEGlobal::extSyncFlag.value() ? MusEGlobal::lastExtMidiSyncTick : MusEGlobal::audio->timestamp()); - + // TODO: Tested, but record resolution not so good. Switch to wall clock based separate list in MidiDevice. + unsigned frame_ts = MusEGlobal::audio->timestamp(); +#ifndef _AUDIO_USE_TRUE_FRAME_ + if(MusEGlobal::audio->isPlaying()) + frame_ts += MusEGlobal::segmentSize; // Shift forward into this period if playing +#endif + event.setTime(frame_ts); + event.setTick(MusEGlobal::lastExtMidiSyncTick); if(MusEGlobal::audio->isPlaying()) event.setLoopNum(MusEGlobal::audio->loopCount()); @@ -300,7 +303,7 @@ void MidiDevice::recordEvent(MidiRecordEvent& event) // Split the events up into channel fifos. Special 'channel' number 17 for sysex events. unsigned int ch = (typ == ME_SYSEX)? MIDI_CHANNELS : event.channel(); - if(_recordFifo[ch].put(MidiPlayEvent(event))) + if(_recordFifo[ch].put(event)) printf("MidiDevice::recordEvent: fifo channel %d overflow\n", ch); } diff --git a/muse2/muse/midiseq.cpp b/muse2/muse/midiseq.cpp index 300382e9..2cfc1917 100644 --- a/muse2/muse/midiseq.cpp +++ b/muse2/muse/midiseq.cpp @@ -287,6 +287,18 @@ MidiSeq::MidiSeq(const char* name) lastTempo = 0; storedtimediffs = 0; playStateExt = false; // not playing + + _clockAveragerStages = new int[16]; // Max stages is 16! + setSyncRecFilterPreset(MusEGlobal::syncRecFilterPreset); + + for(int i = 0; i < _clockAveragerPoles; ++i) + { + _avgClkDiffCounter[i] = 0; + _averagerFull[i] = false; + } + _tempoQuantizeAmount = 1.0; + _lastRealTempo = 0.0; + MusEGlobal::doSetuid(); timerFd=selectTimer(); MusEGlobal::undoSetuid(); @@ -300,6 +312,7 @@ MidiSeq::MidiSeq(const char* name) MidiSeq::~MidiSeq() { delete timer; + delete _clockAveragerStages; } //--------------------------------------------------------- @@ -505,10 +518,69 @@ void MidiSeq::checkAndReportTimingResolution() "Timing source frequency is %1hz, which is below the recommended minimum: 500hz!\n" \ "This could lead to audible timing problems for MIDI.\n" \ "Please see the FAQ on http://muse-sequencer.org for remedies.\n" \ - "Also please check console output for any further error messages\n ")).arg(freq) ); + "Also please check console output for any further error messages.\n ")).arg(freq) ); } } +//--------------------------------------------------------- +// setSyncRecFilterPreset +// To be called in realtime thread only. +//--------------------------------------------------------- +void MidiSeq::setSyncRecFilterPreset(MidiSyncInfo::SyncRecFilterPresetType type) +{ + _syncRecFilterPreset = type; + alignAllTicks(); + + switch(_syncRecFilterPreset) + { + // NOTE: Max _clockAveragerPoles is 16 and maximum stages is 48 per pole ! + case MidiSyncInfo::NONE: + _clockAveragerPoles = 0; + _preDetect = false; + break; + case MidiSyncInfo::TINY: + _clockAveragerPoles = 2; + _clockAveragerStages[0] = 4; + _clockAveragerStages[1] = 4; + _preDetect = false; + break; + case MidiSyncInfo::SMALL: + _clockAveragerPoles = 3; + _clockAveragerStages[0] = 12; + _clockAveragerStages[1] = 8; + _clockAveragerStages[2] = 4; + _preDetect = false; + break; + case MidiSyncInfo::MEDIUM: + _clockAveragerPoles = 3; + _clockAveragerStages[0] = 28; + _clockAveragerStages[1] = 12; + _clockAveragerStages[2] = 8; + _preDetect = false; + break; + case MidiSyncInfo::LARGE: + _clockAveragerPoles = 4; + _clockAveragerStages[0] = 48; + _clockAveragerStages[1] = 48; + _clockAveragerStages[2] = 48; + _clockAveragerStages[3] = 48; + _preDetect = false; + break; + case MidiSyncInfo::LARGE_WITH_PRE_DETECT: + _clockAveragerPoles = 4; + _clockAveragerStages[0] = 8; + _clockAveragerStages[1] = 48; + _clockAveragerStages[2] = 48; + _clockAveragerStages[3] = 48; + _preDetect = true; + break; + + default: + printf("MidiSeq::setSyncRecFilterPreset unknown preset type:%d\n", (int)type); + } +} + + //--------------------------------------------------------- // processMidiClock //--------------------------------------------------------- diff --git a/muse2/muse/midiseq.h b/muse2/muse/midiseq.h index b5ed1099..08adcdce 100644 --- a/muse2/muse/midiseq.h +++ b/muse2/muse/midiseq.h @@ -28,6 +28,7 @@ #include "mpevent.h" #include "driver/alsatimer.h" #include "driver/rtctimer.h" +#include "sync.h" namespace MusECore { @@ -55,9 +56,17 @@ class MidiSeq : public Thread { double songtick1, songtick2; int recTick1, recTick2; int lastTempo; - double timediff[24]; + double timediff[16][48]; int storedtimediffs; - + int _avgClkDiffCounter[16]; + double _lastRealTempo; + bool _averagerFull[16]; + int _clockAveragerPoles; + int* _clockAveragerStages; + bool _preDetect; + double _tempoQuantizeAmount; + MidiSyncInfo::SyncRecFilterPresetType _syncRecFilterPreset; + void alignAllTicks(int frameOverride = 0); /* Testing */ @@ -87,13 +96,17 @@ class MidiSeq : public Thread { bool externalPlayState() const { return playStateExt; } void setExternalPlayState(bool v) { playStateExt = v; } - void realtimeSystemInput(int, int); + void realtimeSystemInput(int port, int type, double time = 0.0); void mtcInputQuarter(int, unsigned char); void setSongPosition(int, int); void mmcInput(int, const unsigned char*, int); void mtcInputFull(int, const unsigned char*, int); void nonRealtimeSystemSysex(int, const unsigned char*, int); void checkAndReportTimingResolution(); + MidiSyncInfo::SyncRecFilterPresetType syncRecFilterPreset() const { return _syncRecFilterPreset; } + void setSyncRecFilterPreset(MidiSyncInfo::SyncRecFilterPresetType type); + double recTempoValQuant() const { return _tempoQuantizeAmount; } + void setRecTempoValQuant(double q) { _tempoQuantizeAmount = q; } void msgMsg(int id); void msgSeek(); diff --git a/muse2/muse/mixer/astrip.cpp b/muse2/muse/mixer/astrip.cpp index 7699af41..3b0a8707 100644 --- a/muse2/muse/mixer/astrip.cpp +++ b/muse2/muse/mixer/astrip.cpp @@ -455,10 +455,11 @@ void AudioStrip::auxLabelChanged(double val, unsigned int idx) // volumeChanged //--------------------------------------------------------- -void AudioStrip::volumeChanged(double val) +void AudioStrip::volumeChanged(double val, int, bool shift_pressed) { AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); - if(at == AUTO_WRITE || (MusEGlobal::audio->isPlaying() && at == AUTO_TOUCH)) + if ( (at == AUTO_WRITE) || + (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) track->enableVolumeController(false); double vol; @@ -472,12 +473,7 @@ void AudioStrip::volumeChanged(double val) //MusEGlobal::audio->msgSetVolume((MusECore::AudioTrack*)track, vol); // p4.0.21 MusEGlobal::audio->msgXXX waits. Do we really need to? ((MusECore::AudioTrack*)track)->setVolume(vol); - MusEGlobal::song->controllerChange(track); - - ((MusECore::AudioTrack*)track)->recordAutomation(MusECore::AC_VOLUME, vol); - - //MusEGlobal::song->update(SC_TRACK_MODIFIED); // for graphical automation update - //MusEGlobal::song->controllerChange(track); + if (!shift_pressed) ((MusECore::AudioTrack*)track)->recordAutomation(MusECore::AC_VOLUME, vol); } //--------------------------------------------------------- @@ -487,7 +483,7 @@ void AudioStrip::volumeChanged(double val) void AudioStrip::volumePressed() { AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); - if(at == AUTO_WRITE || (at == AUTO_READ || at == AUTO_TOUCH)) + if (at == AUTO_READ || at == AUTO_TOUCH || at == AUTO_WRITE) track->enableVolumeController(false); double val = slider->value(); @@ -502,8 +498,6 @@ void AudioStrip::volumePressed() //MusEGlobal::audio->msgSetVolume((MusECore::AudioTrack*)track, volume); // p4.0.21 MusEGlobal::audio->msgXXX waits. Do we really need to? ((MusECore::AudioTrack*)track)->setVolume(volume); - MusEGlobal::song->controllerChange(track); - ((MusECore::AudioTrack*)track)->startAutoRecord(MusECore::AC_VOLUME, volume); } @@ -513,7 +507,8 @@ void AudioStrip::volumePressed() void AudioStrip::volumeReleased() { - if(track->automationType() != AUTO_WRITE) + AutomationType at = track->automationType(); + if (at == AUTO_OFF || at == AUTO_READ || at == AUTO_TOUCH) track->enableVolumeController(true); ((MusECore::AudioTrack*)track)->stopAutoRecord(MusECore::AC_VOLUME, volume); @@ -534,7 +529,8 @@ void AudioStrip::volumeRightClicked(const QPoint &p) void AudioStrip::volLabelChanged(double val) { AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); - if(at == AUTO_WRITE || (MusEGlobal::audio->isPlaying() && at == AUTO_TOUCH)) + if ( (at == AUTO_WRITE) || + (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) track->enableVolumeController(false); double vol; @@ -549,8 +545,6 @@ void AudioStrip::volLabelChanged(double val) //audio->msgSetVolume((MusECore::AudioTrack*)track, vol); // p4.0.21 audio->msgXXX waits. Do we really need to? ((MusECore::AudioTrack*)track)->setVolume(vol); - MusEGlobal::song->controllerChange(track); - ((MusECore::AudioTrack*)track)->startAutoRecord(MusECore::AC_VOLUME, vol); } @@ -558,19 +552,18 @@ void AudioStrip::volLabelChanged(double val) // panChanged //--------------------------------------------------------- -void AudioStrip::panChanged(double val) +void AudioStrip::panChanged(double val, int, bool shift_pressed) { AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); - if(at == AUTO_WRITE || (MusEGlobal::audio->isPlaying() && at == AUTO_TOUCH)) + if ( (at == AUTO_WRITE) || + (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) track->enablePanController(false); panVal = val; //MusEGlobal::audio->msgSetPan(((MusECore::AudioTrack*)track), val); // p4.0.21 MusEGlobal::audio->msgXXX waits. Do we really need to? ((MusECore::AudioTrack*)track)->setPan(val); - MusEGlobal::song->controllerChange(track); - - ((MusECore::AudioTrack*)track)->recordAutomation(MusECore::AC_PAN, val); + if (!shift_pressed) ((MusECore::AudioTrack*)track)->recordAutomation(MusECore::AC_PAN, val); } //--------------------------------------------------------- @@ -580,15 +573,13 @@ void AudioStrip::panChanged(double val) void AudioStrip::panPressed() { AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); - if(at == AUTO_WRITE || (at == AUTO_READ || at == AUTO_TOUCH)) + if (at == AUTO_READ || at == AUTO_TOUCH || at == AUTO_WRITE) track->enablePanController(false); panVal = pan->value(); //MusEGlobal::audio->msgSetPan(((MusECore::AudioTrack*)track), panVal); // p4.0.21 MusEGlobal::audio->msgXXX waits. Do we really need to? ((MusECore::AudioTrack*)track)->setPan(panVal); - MusEGlobal::song->controllerChange(track); - ((MusECore::AudioTrack*)track)->startAutoRecord(MusECore::AC_PAN, panVal); } @@ -598,7 +589,8 @@ void AudioStrip::panPressed() void AudioStrip::panReleased() { - if(track->automationType() != AUTO_WRITE) + AutomationType at = track->automationType(); + if (at == AUTO_OFF || at == AUTO_READ || at == AUTO_TOUCH) track->enablePanController(true); ((MusECore::AudioTrack*)track)->stopAutoRecord(MusECore::AC_PAN, panVal); } @@ -617,8 +609,9 @@ void AudioStrip::panRightClicked(const QPoint &p) void AudioStrip::panLabelChanged(double val) { - AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); - if(at == AUTO_WRITE || (MusEGlobal::audio->isPlaying() && at == AUTO_TOUCH)) + AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); + if ( (at == AUTO_WRITE) || + (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) track->enablePanController(false); panVal = val; @@ -626,8 +619,6 @@ void AudioStrip::panLabelChanged(double val) //MusEGlobal::audio->msgSetPan((MusECore::AudioTrack*)track, val); // p4.0.21 MusEGlobal::audio->msgXXX waits. Do we really need to? ((MusECore::AudioTrack*)track)->setPan(val); - MusEGlobal::song->controllerChange(track); - ((MusECore::AudioTrack*)track)->startAutoRecord(MusECore::AC_PAN, val); } @@ -727,11 +718,10 @@ MusEGui::Knob* AudioStrip::addKnob(int type, int id, MusEGui::DoubleLabel** dlab _curGridRow += 2; connect(knob, SIGNAL(valueChanged(double,int)), pl, SLOT(setValue(double))); - //connect(pl, SIGNAL(valueChanged(double, int)), SLOT(panChanged(double))); if (type == 0) { connect(pl, SIGNAL(valueChanged(double, int)), SLOT(panLabelChanged(double))); - connect(knob, SIGNAL(sliderMoved(double,int)), SLOT(panChanged(double))); + connect(knob, SIGNAL(sliderMoved(double,int,bool)), SLOT(panChanged(double,int,bool))); connect(knob, SIGNAL(sliderPressed(int)), SLOT(panPressed())); connect(knob, SIGNAL(sliderReleased(int)), SLOT(panReleased())); connect(knob, SIGNAL(sliderRightClicked(const QPoint &, int)), SLOT(panRightClicked(const QPoint &))); @@ -894,9 +884,8 @@ AudioStrip::AudioStrip(QWidget* parent, MusECore::AudioTrack* at) sl->setValue(MusECore::fast_log10(t->volume()) * 20.0); connect(sl, SIGNAL(valueChanged(double,int)), SLOT(volLabelChanged(double))); - //connect(sl, SIGNAL(valueChanged(double,int)), SLOT(volumeChanged(double))); connect(slider, SIGNAL(valueChanged(double,int)), sl, SLOT(setValue(double))); - connect(slider, SIGNAL(sliderMoved(double,int)), SLOT(volumeChanged(double))); + connect(slider, SIGNAL(sliderMoved(double,int,bool)), SLOT(volumeChanged(double,int,bool))); connect(slider, SIGNAL(sliderPressed(int)), SLOT(volumePressed())); connect(slider, SIGNAL(sliderReleased(int)), SLOT(volumeReleased())); connect(slider, SIGNAL(sliderRightClicked(const QPoint &, int)), SLOT(volumeRightClicked(const QPoint &))); diff --git a/muse2/muse/mixer/astrip.h b/muse2/muse/mixer/astrip.h index f5406652..c0df5360 100644 --- a/muse2/muse/mixer/astrip.h +++ b/muse2/muse/mixer/astrip.h @@ -94,10 +94,10 @@ class AudioStrip : public Strip { void iRoutePressed(); void oRoutePressed(); void auxChanged(double, int); - void volumeChanged(double); + void volumeChanged(double,int,bool); void volumePressed(); void volumeReleased(); - void panChanged(double); + void panChanged(double,int,bool); void panPressed(); void panReleased(); void volLabelChanged(double); diff --git a/muse2/muse/mixer/panknob.cpp b/muse2/muse/mixer/panknob.cpp index dc2564a7..c54a112f 100644 --- a/muse2/muse/mixer/panknob.cpp +++ b/muse2/muse/mixer/panknob.cpp @@ -48,7 +48,6 @@ void PanKnob::valueChanged(double val) //audio->msgSetPan(src, val); // p4.0.21 audio->msgXXX waits. Do we really need to? src->setPan(val); - MusEGlobal::song->controllerChange(src); } } // namespace MusEGui diff --git a/muse2/muse/mpevent.cpp b/muse2/muse/mpevent.cpp index d3709b1f..a8596224 100644 --- a/muse2/muse/mpevent.cpp +++ b/muse2/muse/mpevent.cpp @@ -178,7 +178,7 @@ void MidiFifo::remove() // return true on fifo overflow //--------------------------------------------------------- -bool MidiRecFifo::put(const MidiPlayEvent& event) +bool MidiRecFifo::put(const MidiRecordEvent& event) { if (size < MIDI_REC_FIFO_SIZE) { fifo[wIndex] = event; @@ -193,9 +193,9 @@ bool MidiRecFifo::put(const MidiPlayEvent& event) // get //--------------------------------------------------------- -MidiPlayEvent MidiRecFifo::get() +MidiRecordEvent MidiRecFifo::get() { - MidiPlayEvent event(fifo[rIndex]); + MidiRecordEvent event(fifo[rIndex]); rIndex = (rIndex + 1) % MIDI_REC_FIFO_SIZE; --size; return event; @@ -205,7 +205,7 @@ MidiPlayEvent MidiRecFifo::get() // peek //--------------------------------------------------------- -const MidiPlayEvent& MidiRecFifo::peek(int n) +const MidiRecordEvent& MidiRecFifo::peek(int n) { int idx = (rIndex + n) % MIDI_REC_FIFO_SIZE; return fifo[idx]; diff --git a/muse2/muse/mpevent.h b/muse2/muse/mpevent.h index 9b64f9cd..903a8126 100644 --- a/muse2/muse/mpevent.h +++ b/muse2/muse/mpevent.h @@ -111,6 +111,8 @@ class MEvent { //--------------------------------------------------------- class MidiRecordEvent : public MEvent { + private: + unsigned int _tick; // To store tick when external sync is on, required besides frame. public: MidiRecordEvent() : MEvent() {} MidiRecordEvent(const MEvent& e) : MEvent(e) {} @@ -121,6 +123,9 @@ class MidiRecordEvent : public MEvent { MidiRecordEvent(unsigned t, int p, int type, EvData data) : MEvent(t, p, type, data) {} ~MidiRecordEvent() {} + + unsigned int tick() {return _tick;} + void setTick(unsigned int tick) {_tick = tick;} }; //--------------------------------------------------------- @@ -200,20 +205,19 @@ class MidiFifo { //--------------------------------------------------------- // MidiRecFifo -// (Same as MidiFifo, but with a smaller size.) //--------------------------------------------------------- class MidiRecFifo { - MidiPlayEvent fifo[MIDI_REC_FIFO_SIZE]; + MidiRecordEvent fifo[MIDI_REC_FIFO_SIZE]; volatile int size; int wIndex; int rIndex; public: MidiRecFifo() { clear(); } - bool put(const MidiPlayEvent& event); // returns true on fifo overflow - MidiPlayEvent get(); - const MidiPlayEvent& peek(int = 0); + bool put(const MidiRecordEvent& event); // returns true on fifo overflow + MidiRecordEvent get(); + const MidiRecordEvent& peek(int = 0); void remove(); bool isEmpty() const { return size == 0; } void clear() { size = 0, wIndex = 0, rIndex = 0; } diff --git a/muse2/muse/node.cpp b/muse2/muse/node.cpp index e56949aa..02264a37 100644 --- a/muse2/muse/node.cpp +++ b/muse2/muse/node.cpp @@ -401,10 +401,10 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s // precalculate stereo volume double vol[2]; - //double _volume = volume(); - //double _pan = pan(); - double _volume = controller()->value(AC_VOLUME, pos); - double _pan = controller()->value(AC_PAN, pos); + double _volume = controller()->value(AC_VOLUME, pos, + !MusEGlobal::automation || automationType() == AUTO_OFF || !_volumeEnCtrl || !_volumeEn2Ctrl); + double _pan = controller()->value(AC_PAN, pos, + !MusEGlobal::automation || automationType() == AUTO_OFF || !_panEnCtrl || !_panEn2Ctrl); vol[0] = _volume * (1.0 - _pan); vol[1] = _volume * (1.0 + _pan); @@ -739,10 +739,10 @@ void AudioTrack::addData(unsigned pos, int dstChannels, int srcStartChan, int sr // precalculate stereo volume double vol[2]; - //double _volume = volume(); - //double _pan = pan(); - double _volume = controller()->value(AC_VOLUME, pos); - double _pan = controller()->value(AC_PAN, pos); + double _volume = controller()->value(AC_VOLUME, pos, + !MusEGlobal::automation || automationType() == AUTO_OFF || !_volumeEnCtrl || !_volumeEn2Ctrl); + double _pan = controller()->value(AC_PAN, pos, + !MusEGlobal::automation || automationType() == AUTO_OFF || !_panEnCtrl || !_panEn2Ctrl); vol[0] = _volume * (1.0 - _pan); vol[1] = _volume * (1.0 + _pan); diff --git a/muse2/muse/osc.cpp b/muse2/muse/osc.cpp index 0d4a1750..381e4acc 100644 --- a/muse2/muse/osc.cpp +++ b/muse2/muse/osc.cpp @@ -1092,7 +1092,7 @@ int OscDssiIF::oscControl(lo_arg** argv) if(_oscSynthIF) { _oscSynthIF->oscControl(argv[0]->i, argv[1]->f); - if (portat(port)]=argv[1]->f; } @@ -1170,7 +1170,7 @@ int OscEffectIF::oscControl(lo_arg** argv) if(_oscPluginI) { _oscPluginI->oscControl(argv[0]->i, argv[1]->f); - if (portat(port)]=argv[1]->f; } diff --git a/muse2/muse/part.cpp b/muse2/muse/part.cpp index a632bc9c..9950c362 100644 --- a/muse2/muse/part.cpp +++ b/muse2/muse/part.cpp @@ -864,17 +864,18 @@ void Song::cmdResizePart(Track* track, Part* oPart, unsigned int len, bool doClo unsigned event_endframe = event_startframe + e.lenFrame(); if (event_endframe < new_partlength) continue; - if (event_startframe > new_partlength) { // If event start was after the new length, remove it from part - // Do not do port controller values and clone parts. - operations.push_back(UndoOp(UndoOp::DeleteEvent, e, nPart, false, false)); - continue; - } - if (event_endframe > new_partlength) { // If this event starts before new length and ends after, shrink it - Event newEvent = e.clone(); - newEvent.setLenFrame(new_partlength - event_startframe); - // Do not do port controller values and clone parts. - operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, e, nPart, false,false)); - } +// REMOVE Tim. +// if (event_startframe > new_partlength) { // If event start was after the new length, remove it from part +// // Do not do port controller values and clone parts. +// operations.push_back(UndoOp(UndoOp::DeleteEvent, e, nPart, false, false)); +// continue; +// } +// if (event_endframe > new_partlength) { // If this event starts before new length and ends after, shrink it +// Event newEvent = e.clone(); +// newEvent.setLenFrame(new_partlength - event_startframe); +// // Do not do port controller values and clone parts. +// operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, e, nPart, false,false)); +// } } nPart->setLenFrame(new_partlength); // Do not do port controller values and clone parts. @@ -893,19 +894,20 @@ void Song::cmdResizePart(Track* track, Part* oPart, unsigned int len, bool doClo iEvent i = el->end(); i--; Event last = i->second; - unsigned last_start = last.frame(); +// REMOVE Tim. unsigned last_start = last.frame(); MusECore::SndFileR file = last.sndFile(); if (file.isNull()) return; - unsigned clipframes = (file.samples() - last.spos());// / file.channels(); +// unsigned clipframes = (file.samples() - last.spos());// / file.channels(); Event newEvent = last.clone(); - unsigned new_eventlength = new_partlength - last_start; - if (new_eventlength > clipframes) // Shrink event length if new partlength exceeds last clip - new_eventlength = clipframes; - - newEvent.setLenFrame(new_eventlength); +// REMOVE Tim. +// unsigned new_eventlength = new_partlength - last_start; +// if (new_eventlength > clipframes) // Shrink event length if new partlength exceeds last clip +// new_eventlength = clipframes; +// +// newEvent.setLenFrame(new_eventlength); // Do not do port controller values and clone parts. operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, last, nPart, false, false)); } @@ -1199,13 +1201,12 @@ WavePart* WavePart::clone() const return new WavePart(*this); } - //--------------------------------------------------------- // hasHiddenEvents // Returns combination of HiddenEventsType enum. //--------------------------------------------------------- -int Part::hasHiddenEvents() +int MidiPart::hasHiddenEvents() { unsigned len = lenTick(); @@ -1222,7 +1223,27 @@ int Part::hasHiddenEvents() return _hiddenEvents; } +//--------------------------------------------------------- +// hasHiddenEvents +// Returns combination of HiddenEventsType enum. +//--------------------------------------------------------- +int WavePart::hasHiddenEvents() +{ + unsigned len = lenFrame(); + + // TODO: For now, we don't support events before the left border, only events past the right border. + for(iEvent ev=events()->begin(); ev!=events()->end(); ev++) + { + if(ev->second.endFrame() > len) + { + _hiddenEvents = RightEventsHidden; // Cache the result for later. + return _hiddenEvents; + } + } + _hiddenEvents = NoEventsHidden; // Cache the result for later. + return _hiddenEvents; +} //--------------------------------------------------------- // ClonePart diff --git a/muse2/muse/part.h b/muse2/muse/part.h index f2bc342b..357ec1db 100644 --- a/muse2/muse/part.h +++ b/muse2/muse/part.h @@ -76,14 +76,13 @@ class Part : public PosLen { bool _mute; int _colorIndex; - int _hiddenEvents; // Combination of HiddenEventsType. - protected: Track* _track; EventList* _events; Part* _prevClone; Part* _nextClone; - + int _hiddenEvents; // Combination of HiddenEventsType. + public: Part(Track*); Part(Track*, EventList*); @@ -114,7 +113,7 @@ class Part : public PosLen { void setNextClone(Part* p) { _nextClone = p; } // Returns combination of HiddenEventsType enum. - int hasHiddenEvents(); + virtual int hasHiddenEvents() = 0; // If repeated calls to hasHiddenEvents() are desired, then to avoid re-iteration of the event list, // call this after hasHiddenEvents(). int cachedHasHiddenEvents() const { return _hiddenEvents; } @@ -140,7 +139,9 @@ class MidiPart : public Part { virtual ~MidiPart() {} virtual MidiPart* clone() const; MidiTrack* track() const { return (MidiTrack*)Part::track(); } - + // Returns combination of HiddenEventsType enum. + int hasHiddenEvents(); + virtual void dump(int n = 0) const; }; @@ -161,6 +162,8 @@ class WavePart : public Part { virtual ~WavePart() {} virtual WavePart* clone() const; WaveTrack* track() const { return (WaveTrack*)Part::track(); } + // Returns combination of HiddenEventsType enum. + int hasHiddenEvents(); virtual void dump(int n = 0) const; }; diff --git a/muse2/muse/plugin.cpp b/muse2/muse/plugin.cpp index 8bf35143..e8b0489c 100644 --- a/muse2/muse/plugin.cpp +++ b/muse2/muse/plugin.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -78,6 +79,9 @@ // Turn on debugging messages. //#define PLUGIN_DEBUGIN +// Turn on constant stream of debugging messages. +//#define PLUGIN_DEBUGIN_PROCESS + namespace MusEGlobal { MusECore::PluginList plugins; } @@ -119,7 +123,7 @@ bool ladspa2MidiControlValues(const LADSPA_Descriptor* plugin, unsigned long por *min = 0; *max = 1; - *def = (int)lrint(fdef); + *def = (int)lrintf(fdef); return hasdef; } @@ -156,8 +160,8 @@ bool ladspa2MidiControlValues(const LADSPA_Descriptor* plugin, unsigned long por fmax = 1.0; frng = fmax - fmin; - imin = lrint(fmin); - imax = lrint(fmax); + imin = lrintf(fmin); + imax = lrintf(fmax); int ctlmn = 0; int ctlmx = 127; @@ -230,7 +234,7 @@ bool ladspa2MidiControlValues(const LADSPA_Descriptor* plugin, unsigned long por *min = imin; *max = imax; - *def = (int)lrint(fdef); + *def = (int)lrintf(fdef); return hasdef; } @@ -244,7 +248,7 @@ bool ladspa2MidiControlValues(const LADSPA_Descriptor* plugin, unsigned long por // FIXME: TODO: Incorrect... Fix this somewhat more trivial stuff later.... - *def = (int)lrint(fdef) + bias; + *def = (int)lrintf(fdef) + bias; #ifdef PLUGIN_DEBUGIN printf("ladspa2MidiControlValues: setting default:%d\n", *def); @@ -305,7 +309,7 @@ float midi2LadspaValue(const LADSPA_Descriptor* plugin, unsigned long port, int fmax = 1.0; frng = fmax - fmin; - imin = lrint(fmin); + imin = lrintf(fmin); if(desc & LADSPA_HINT_TOGGLED) { @@ -619,40 +623,6 @@ void ladspaControlRange(const LADSPA_Descriptor* plugin, unsigned long port, flo *max = 1.0; } -// DELETETHIS 35 -/* -//--------------------------------------------------------- -// PluginBase -//--------------------------------------------------------- - -//--------------------------------------------------------- -// range -//--------------------------------------------------------- - -void PluginBase::range(unsigned long i, float* min, float* max) const - { - LADSPA_PortRangeHint range = plugin->PortRangeHints[i]; - LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor; - if (desc & LADSPA_HINT_TOGGLED) { - *min = 0.0; - *max = 1.0; - return; - } - float m = 1.0; - if (desc & LADSPA_HINT_SAMPLE_RATE) - m = float(MusEGlobal::sampleRate); - - if (desc & LADSPA_HINT_BOUNDED_BELOW) - *min = range.LowerBound * m; - else - *min = 0.0; - if (desc & LADSPA_HINT_BOUNDED_ABOVE) - *max = range.UpperBound * m; - else - *max = 1.0; - } -*/ - //--------------------------------------------------------- // Plugin //--------------------------------------------------------- @@ -792,8 +762,7 @@ int Plugin::incReferences(int val) if(dssi) { const DSSI_Descriptor* descr; - //for(int i = 0;; ++i) - for(unsigned long i = 0;; ++i) // p4.0.21 + for(unsigned long i = 0;; ++i) { descr = dssi(i); if(descr == NULL) @@ -817,7 +786,7 @@ int Plugin::incReferences(int val) if(ladspadf) { const LADSPA_Descriptor* descr; - for(unsigned long i = 0;; ++i) // p4.0.21 + for(unsigned long i = 0;; ++i) { descr = ladspadf(i); if(descr == NULL) @@ -913,7 +882,7 @@ int Plugin::incReferences(int val) void Plugin::range(unsigned long i, float* min, float* max) const { - ladspaControlRange(plugin, i, min, max); // p4.0.20 + ladspaControlRange(plugin, i, min, max); } //--------------------------------------------------------- @@ -922,59 +891,9 @@ void Plugin::range(unsigned long i, float* min, float* max) const float Plugin::defaultValue(unsigned long port) const { - // p4.0.21 float val; ladspaDefaultValue(plugin, port, &val); return val; - - // DELETETHIS 50 - /* - if(port >= plugin->PortCount) - return 0.0; - - LADSPA_PortRangeHint range = plugin->PortRangeHints[port]; - LADSPA_PortRangeHintDescriptor rh = range.HintDescriptor; - //double val = 1.0; - float val = 1.0; - if (LADSPA_IS_HINT_DEFAULT_MINIMUM(rh)) - val = range.LowerBound; - else if (LADSPA_IS_HINT_DEFAULT_LOW(rh)) - if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor)) - //val = exp(fast_log10(range.LowerBound) * .75 + - // log(range.UpperBound) * .25); - val = expf(fast_log10(range.LowerBound) * .75 + - logf(range.UpperBound) * .25); - else - val = range.LowerBound*.75 + range.UpperBound*.25; - else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(rh)) - if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor)) - //val = exp(log(range.LowerBound) * .5 + - // log(range.UpperBound) * .5); - val = expf(logf(range.LowerBound) * .5 + - logf(range.UpperBound) * .5); - else - val = range.LowerBound*.5 + range.UpperBound*.5; - else if (LADSPA_IS_HINT_DEFAULT_HIGH(rh)) - if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor)) - //val = exp(log(range.LowerBound) * .25 + - // log(range.UpperBound) * .75); - val = expf(logf(range.LowerBound) * .25 + - logf(range.UpperBound) * .75); - else - val = range.LowerBound*.25 + range.UpperBound*.75; - else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(rh)) - val = range.UpperBound; - else if (LADSPA_IS_HINT_DEFAULT_0(rh)) - val = 0.0; - else if (LADSPA_IS_HINT_DEFAULT_1(rh)) - val = 1.0; - else if (LADSPA_IS_HINT_DEFAULT_100(rh)) - val = 100.0; - else if (LADSPA_IS_HINT_DEFAULT_440(rh)) - val = 440.0; - - return val; - */ } //--------------------------------------------------------- @@ -1013,7 +932,7 @@ static void loadPluginLib(QFileInfo* fi) if(dssi) { const DSSI_Descriptor* descr; - for (unsigned long i = 0;; ++i) // p4.0.21 + for (unsigned long i = 0;; ++i) { descr = dssi(i); if (descr == 0) @@ -1060,7 +979,7 @@ static void loadPluginLib(QFileInfo* fi) } const LADSPA_Descriptor* descr; - for (unsigned long i = 0;; ++i) // p4.0.21 + for (unsigned long i = 0;; ++i) { descr = ladspa(i); if (descr == NULL) @@ -1223,6 +1142,76 @@ Pipeline::~Pipeline() ::free(buffer[i]); } +//--------------------------------------------------------- +// addScheduledControlEvent +// track_ctrl_id is the fully qualified track audio controller number +// Returns true if event cannot be delivered +//--------------------------------------------------------- + +bool Pipeline::addScheduledControlEvent(int track_ctrl_id, float val, unsigned frame) +{ + // If a track controller, or the special dssi synth controller block, just return. + if(track_ctrl_id < AC_PLUGIN_CTL_BASE || track_ctrl_id >= (int)genACnum(MAX_PLUGINS, 0)) + return true; + int rack_idx = (track_ctrl_id - AC_PLUGIN_CTL_BASE) >> AC_PLUGIN_CTL_BASE_POW; + for (int i = 0; i < PipelineDepth; ++i) + { + PluginI* p = (*this)[i]; + if(p && p->id() == rack_idx) + return p->addScheduledControlEvent(track_ctrl_id & AC_PLUGIN_CTL_ID_MASK, val, frame); + } + return true; +} + +//--------------------------------------------------------- +// controllersEnabled +// Returns whether automation control stream is enabled or disabled. +// Used during automation recording to inhibit gui controls +//--------------------------------------------------------- + +void Pipeline::controllersEnabled(int track_ctrl_id, bool* en1, bool* en2) +{ + // If a track controller, or the special dssi synth controller block, just return. + if(track_ctrl_id < AC_PLUGIN_CTL_BASE || track_ctrl_id >= (int)genACnum(MAX_PLUGINS, 0)) + return; + int rack_idx = (track_ctrl_id - AC_PLUGIN_CTL_BASE) >> AC_PLUGIN_CTL_BASE_POW; + for (int i = 0; i < PipelineDepth; ++i) + { + PluginI* p = (*this)[i]; + if(p && p->id() == rack_idx) + { + if(en1) + *en1 = p->controllerEnabled(track_ctrl_id & AC_PLUGIN_CTL_ID_MASK); + if(en2) + *en2 = p->controllerEnabled2(track_ctrl_id & AC_PLUGIN_CTL_ID_MASK); + return; + } + } +} + +//--------------------------------------------------------- +// enableController +// Enable or disable gui automation control stream. +// Used during automation recording to inhibit gui controls +//--------------------------------------------------------- + +void Pipeline::enableController(int track_ctrl_id, bool en) +{ + // If a track controller, or the special dssi synth controller block, just return. + if(track_ctrl_id < AC_PLUGIN_CTL_BASE || track_ctrl_id >= (int)genACnum(MAX_PLUGINS, 0)) + return; + int rack_idx = (track_ctrl_id - AC_PLUGIN_CTL_BASE) >> AC_PLUGIN_CTL_BASE_POW; + for (int i = 0; i < PipelineDepth; ++i) + { + PluginI* p = (*this)[i]; + if(p && p->id() == rack_idx) + { + p->enableController(track_ctrl_id & AC_PLUGIN_CTL_ID_MASK, en); + return; + } + } +} + //--------------------------------------------------------- // setChannels //--------------------------------------------------------- @@ -1524,6 +1513,39 @@ PluginIBase::~PluginIBase() delete _gui; } +//--------------------------------------------------------- +// addScheduledControlEvent +// i is the specific index of the control input port +// Returns true if event cannot be delivered +//--------------------------------------------------------- + +bool PluginIBase::addScheduledControlEvent(unsigned long i, float val, unsigned frame) +{ + if(i >= parameters()) + { + printf("PluginIBase::addScheduledControlEvent param number %lu out of range of ports:%lu\n", i, parameters()); + return true; + } + ControlEvent ce; + ce.unique = false; + ce.idx = i; + ce.value = val; + // Time-stamp the event. This does a possibly slightly slow call to gettimeofday via timestamp(). + // timestamp() is more or less an estimate of the current frame. (This is exactly how ALSA events + // are treated when they arrive in our ALSA driver.) + //ce.frame = MusEGlobal::audio->timestamp(); + // p4.0.23 timestamp() is circular, which is making it impossible to deal with 'modulo' events which + // slip in 'under the wire' before processing the ring buffers. So try this linear timestamp instead: + ce.frame = frame; + + if(_controlFifo.put(ce)) + { + fprintf(stderr, "PluginIBase::addScheduledControlEvent: fifo overflow: in control number:%lu\n", i); + return true; + } + return false; +} + QString PluginIBase::dssi_ui_filename() const { QString libr(lib()); @@ -1658,8 +1680,6 @@ void PluginI::updateControllers() for(unsigned long i = 0; i < controlPorts; ++i) _track->setPluginCtrlVal(genACnum(_id, i), controls[i].val); // TODO A faster bulk message - - MusEGlobal::song->controllerChange(_track); } //--------------------------------------------------------- @@ -1698,7 +1718,7 @@ void PluginI::setChannels(int c) } } - unsigned long curPort = 0; // p4.0.21 + unsigned long curPort = 0; unsigned long curOutPort = 0; unsigned long ports = _plugin->ports(); for (unsigned long k = 0; k < ports; ++k) @@ -1733,27 +1753,7 @@ void PluginI::setChannels(int c) void PluginI::setParam(unsigned long i, float val) { - if(i >= _plugin->_controlInPorts) - { - printf("PluginI::setParameter param number %lu out of range of ports:%lu\n", i, _plugin->_controlInPorts); - return; - } - ControlEvent ce; - ce.unique = false; - ce.idx = i; - ce.value = val; - // Time-stamp the event. This does a possibly slightly slow call to gettimeofday via timestamp(). - // timestamp() is more or less an estimate of the current frame. (This is exactly how ALSA events - // are treated when they arrive in our ALSA driver.) - //ce.frame = MusEGlobal::audio->timestamp(); - // p4.0.23 timestamp() is circular, which is making it impossible to deal with 'modulo' events which - // slip in 'under the wire' before processing the ring buffers. So try this linear timestamp instead: - ce.frame = MusEGlobal::audio->curFrame(); - - if(_controlFifo.put(ce)) - { - fprintf(stderr, "PluginI::setParameter: fifo overflow: in control number:%lu\n", i); - } + addScheduledControlEvent(i, val, MusEGlobal::audio->curFrame()); } //--------------------------------------------------------- @@ -1865,7 +1865,7 @@ bool PluginI::initPluginInstance(Plugin* plug, int c) { if(pd & LADSPA_PORT_INPUT) { - float val = _plugin->defaultValue(k); // p4.0.21 + float val = _plugin->defaultValue(k); controls[curPort].val = val; controls[curPort].tmpVal = val; controls[curPort].enCtrl = true; @@ -1917,11 +1917,11 @@ bool PluginI::initPluginInstance(Plugin* plug, int c) void PluginI::connect(unsigned long ports, unsigned long offset, float** src, float** dst) { - unsigned long port = 0; // p4.0.21 + unsigned long port = 0; for (int i = 0; i < instances; ++i) { for (unsigned long k = 0; k < _plugin->ports(); ++k) { if (isAudioIn(k)) { - _plugin->connectPort(handle[i], k, src[port] + offset); // p4.0.21 + _plugin->connectPort(handle[i], k, src[port] + offset); port = (port + 1) % ports; } } @@ -1930,7 +1930,7 @@ void PluginI::connect(unsigned long ports, unsigned long offset, float** src, fl for (int i = 0; i < instances; ++i) { for (unsigned long k = 0; k < _plugin->ports(); ++k) { if (isAudioOut(k)) { - _plugin->connectPort(handle[i], k, dst[port] + offset); // p4.0.21 + _plugin->connectPort(handle[i], k, dst[port] + offset); port = (port + 1) % ports; // overwrite output? } } @@ -1979,7 +1979,7 @@ bool PluginI::setControl(const QString& s, float val) { for (unsigned long i = 0; i < controlPorts; ++i) { if (_plugin->portName(controls[i].idx) == s) { - setParam(i, val); // p4.0.21 + setParam(i, val); return false; } } @@ -1997,7 +1997,7 @@ void PluginI::writeConfiguration(int level, Xml& xml) xml.tag(level++, "plugin file=\"%s\" label=\"%s\" channel=\"%d\"", Xml::xmlString(_plugin->lib()).toLatin1().constData(), Xml::xmlString(_plugin->label()).toLatin1().constData(), channel); - for (unsigned long i = 0; i < controlPorts; ++i) { // p4.0.21 + for (unsigned long i = 0; i < controlPorts; ++i) { unsigned long idx = controls[i].idx; QString s("control name=\"%1\" val=\"%2\" /"); xml.tag(level, s.arg(Xml::xmlString(_plugin->portName(idx)).toLatin1().constData()).arg(controls[i].tmpVal).toLatin1().constData()); @@ -2040,7 +2040,7 @@ bool PluginI::loadControl(Xml& xml) if (tag == "name") name = xml.s2(); else if (tag == "val") - val = xml.s2().toFloat(); // p4.0.21 + val = xml.s2().toFloat(); break; case Xml::TagEnd: if (tag == "control") { @@ -2102,7 +2102,7 @@ bool PluginI::readConfiguration(Xml& xml, bool readPreset) xml.parse1(); printf("Error initializing plugin instance (%s, %s)\n", file.toLatin1().constData(), label.toLatin1().constData()); - //break; // Don't break - let it read any control tags. DELETETHIS + //break; // Don't break - let it read any control tags. } } } @@ -2352,28 +2352,152 @@ void PluginI::apply(unsigned long n, unsigned long ports, float** bufIn, float** if(min_per > n) min_per = n; - // Process automation control values now. - // TODO: This needs to be respect frame resolution. Put this inside the sample loop below. - if(MusEGlobal::automation && _track && _track->automationType() != AUTO_OFF && _id != -1) + // CtrlListList* cll = NULL; // WIP + AutomationType at = AUTO_OFF; + if(_track) { - for(unsigned long k = 0; k < controlPorts; ++k) - { - if(controls[k].enCtrl && controls[k].en2Ctrl ) - controls[k].tmpVal = _track->controller()->value(genACnum(_id, k), MusEGlobal::audio->pos().frame()); - } + at = _track->automationType(); + //cll = _track->controller(); // WIP } - + bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; + while(sample < n) { // nsamp is the number of samples the plugin->process() call will be supposed to do unsigned long nsamp = usefixedrate ? fixedsize : n - sample; + // + // Process automation control values, while also determining the maximum acceptable + // size of this run. Further processing, from FIFOs for example, can lower the size + // from there, but this section determines where the next highest maximum frame + // absolutely needs to be for smooth playback of the controller value stream... + // + if(_track && _id != -1 && ports != 0) // Don't bother if not 'running'. + { + unsigned long frame = MusEGlobal::audio->pos().frame() + sample; + int nextFrame; + //double val; // WIP + for(unsigned long k = 0; k < controlPorts; ++k) + { + + +#if 0 // WIP - Work in progress. Tim. + + ciCtrlList icl = cll->find(genACnum(_id, k)); + if(icl == cll->end()) + continue; + CtrlList* cl = icl->second; + if(no_auto || !controls[k].enCtrl || !controls[k].en2Ctrl || cl->empty()) + { + nextFrame = -1; + val = cl->curVal(); + } + else + { + ciCtrl i = cl->upper_bound(frame); // get the index after current frame + if (i == cl->end()) { // if we are past all items just return the last value + --i; + nextFrame = -1; + val = i->second.val; + } + else if(cl->mode() == CtrlList::DISCRETE) + { + if(i == cl->begin()) + { + nextFrame = i->second.frame; + val = i->second.val; + } + else + { + nextFrame = i->second.frame; + --i; + val = i->second.val; + } + } + else { // INTERPOLATE + if (i == cl->begin()) { + nextFrame = i->second.frame; + val = i->second.val; + } + else { + int frame2 = i->second.frame; + double val2 = i->second.val; + --i; + int frame1 = i->second.frame; + double val1 = i->second.val; + + + if(val2 != val1) + nextFrame = 0; // Zero signifies the next frame should be determined by caller. + else + nextFrame = frame2; + + if (cl->valueType() == VAL_LOG) { + val1 = 20.0*fast_log10(val1); + if (val1 < MusEGlobal::config.minSlider) + val1=MusEGlobal::config.minSlider; + val2 = 20.0*fast_log10(val2); + if (val2 < MusEGlobal::config.minSlider) + val2=MusEGlobal::config.minSlider; + } + + val2 -= val1; + val1 += (double(frame - frame1) * val2)/double(frame2 - frame1); + + if (cl->valueType() == VAL_LOG) { + val1 = exp10(val1/20.0); + } + + val = val1; + } + } + } + + controls[k].tmpVal = val; + + +#else + controls[k].tmpVal = _track->controller()->value(genACnum(_id, k), frame, + no_auto || !controls[k].enCtrl || !controls[k].en2Ctrl, + &nextFrame); +#endif + + +#ifdef PLUGIN_DEBUGIN_PROCESS + printf("PluginI::apply k:%lu sample:%lu frame:%lu nextFrame:%d nsamp:%lu \n", k, sample, frame, nextFrame, nsamp); +#endif + if(MusEGlobal::audio->isPlaying() && !usefixedrate && nextFrame != -1) + { + // Returned value of nextFrame can be zero meaning caller replaces with some (constant) value. + unsigned long samps = (unsigned long)nextFrame; + if(samps > frame + min_per) + { + unsigned long diff = samps - frame; + unsigned long mask = min_per-1; // min_per must be power of 2 + samps = diff & ~mask; + if((diff & mask) != 0) + samps += min_per; + } + else + samps = min_per; + + if(samps < nsamp) + nsamp = samps; + } + } + +#ifdef PLUGIN_DEBUGIN_PROCESS + printf("PluginI::apply sample:%lu nsamp:%lu\n", sample, nsamp); +#endif + } + + // + // Process all control ring buffer items valid for this time period... + // bool found = false; unsigned long frame = 0; unsigned long index = 0; unsigned long evframe; - - // Get all control ring buffer items valid for this time period... while(!_controlFifo.isEmpty()) { ControlEvent v = _controlFifo.peek(); @@ -2398,9 +2522,10 @@ void PluginI::apply(unsigned long n, unsigned long ports, float** bufIn, float** // but stop after a control event was found (then process(), // then loop here again), but ensure that process() must process // at least min_per frames. - if(evframe >= n - || (found && !v.unique && (evframe - sample >= min_per)) - || (usefixedrate && found && v.unique && v.idx == index)) + if(evframe >= n // Next events are for a later period. + || (!usefixedrate && !found && !v.unique && (evframe - sample >= nsamp)) // Next events are for a later run in this period. (Autom took prio.) + || (found && !v.unique && (evframe - sample >= min_per)) // Eat up events within minimum slice - they're too close. + || (usefixedrate && found && v.unique && v.idx == index)) // Special for dssi-vst: Fixed rate and must reply to all. break; _controlFifo.remove(); // Done with the ring buffer's item. Remove it. @@ -2415,27 +2540,20 @@ void PluginI::apply(unsigned long n, unsigned long ports, float** bufIn, float** // Need to update the automation value, otherwise it overwrites later with the last automation value. if(_track && _id != -1) - { - // We're in the audio thread context: no need to send a message, just modify directly. _track->setPluginCtrlVal(genACnum(_id, v.idx), v.value); - - /* Recording automation is done immediately in the * - * OSC control handler, because we don't want any delay. * - * we might want to handle dssi-vst synthes here, however! */ - } } // Now update the actual values from the temporary values... for(unsigned long k = 0; k < controlPorts; ++k) controls[k].val = controls[k].tmpVal; - if(found && !usefixedrate) - nsamp = frame - sample; + if(found && !usefixedrate) // If a control FIFO item was found, takes priority over automation controller stream. + nsamp = frame - sample; - if(sample + nsamp >= n) // Safety check. + if(sample + nsamp >= n) // Safety check. nsamp = n - sample; - // Don't allow zero-length runs. This could/should be checked in the control loop instead. + // TODO: Don't allow zero-length runs. This could/should be checked in the control loop instead. // Note this means it is still possible to get stuck in the top loop (at least for a while). if(nsamp == 0) continue; @@ -2563,13 +2681,8 @@ int PluginI::oscUpdate() usleep(300000); // Send current control values. - //unsigned long ports = controlPorts; DELETETHIS 2 - //for(int i = 0; i < controlPorts; ++i) for(unsigned long i = 0; i < controlPorts; ++i) { - //unsigned long k = synth->pIdx(i); DELETETHIS 2 - //_oscIF.oscSendControl(k, controls[i], true /*force*/); - //printf("PluginI::oscUpdate() sending control:%lu val:%f\n", i, controls[i].val); _oscif.oscSendControl(controls[i].idx, controls[i].val, true /*force*/); // Avoid overloading the GUI if there are lots and lots of ports. if((i+1) % 50 == 0) @@ -2638,7 +2751,6 @@ int PluginI::oscControl(unsigned long port, float value) } } */ - // p4.0.21 ControlEvent ce; ce.unique = _plugin->_isDssiVst; // Special for messages from vst gui to host - requires processing every message. ce.idx = cport; @@ -2693,27 +2805,6 @@ int PluginI::oscControl(unsigned long port, float value) } */ -// DELETETHIS 20 -#if 0 - int port = argv[0]->i; - LADSPA_Data value = argv[1]->f; - - if (port < 0 || port > instance->plugin->descriptor->LADSPA_Plugin->PortCount) { - fprintf(stderr, "MusE: OSC: %s port number (%d) is out of range\n", - instance->friendly_name, port); - return 0; - } - if (instance->pluginPortControlInNumbers[port] == -1) { - fprintf(stderr, "MusE: OSC: %s port %d is not a control in\n", - instance->friendly_name, port); - return 0; - } - pluginControlIns[instance->pluginPortControlInNumbers[port]] = value; - if (verbose) { - printf("MusE: OSC: %s port %d = %f\n", - instance->friendly_name, port, value); - } -#endif return 0; } @@ -2795,7 +2886,7 @@ PluginDialog::PluginDialog(QWidget* parent) ok_lo->addWidget(cancelB); QGroupBox* plugSelGroup = new QGroupBox(this); - plugSelGroup->setTitle("Show plugs:"); + plugSelGroup->setTitle(tr("Show plugs:")); plugSelGroup->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); QGridLayout* psl = new QGridLayout; plugSelGroup->setLayout(psl); @@ -2972,7 +3063,7 @@ void PluginDialog::fillPlugs() QString type_name; pList->clear(); for (MusECore::iPlugin i = MusEGlobal::plugins.begin(); i != MusEGlobal::plugins.end(); ++i) { - unsigned long ai = i->inports(); // p4.0.21 + unsigned long ai = i->inports(); unsigned long ao = i->outports(); unsigned long ci = i->controlInPorts(); unsigned long co = i->controlOutPorts(); @@ -3115,7 +3206,7 @@ PluginGui::PluginGui(MusECore::PluginIBase* p) const char* name = ba.constData(); if (*name !='P') continue; - unsigned long parameter; // p4.0.21 + unsigned long parameter; int rv = sscanf(name, "P%lu", ¶meter); if(rv != 1) continue; @@ -3126,15 +3217,17 @@ PluginGui::PluginGui(MusECore::PluginIBase* p) nobj = 0; QSignalMapper* mapper = new QSignalMapper(this); - // FIXME: There's no unsigned for gui params. We would need to limit nobj to MAXINT. // p4.0.21 - // FIXME: Our MusEGui::Slider class uses doubles for values, giving some problems with float conversion. // p4.0.21 + // FIXME: There's no unsigned for gui params. We would need to limit nobj to MAXINT. + // FIXME: Our MusEGui::Slider class uses doubles for values, giving some problems with float conversion. connect(mapper, SIGNAL(mapped(int)), SLOT(guiParamChanged(int))); - QSignalMapper* mapperPressed = new QSignalMapper(this); - QSignalMapper* mapperReleased = new QSignalMapper(this); + QSignalMapper* mapperPressed = new QSignalMapper(this); + QSignalMapper* mapperReleased = new QSignalMapper(this); + QSignalMapper* mapperContextMenuReq = new QSignalMapper(this); connect(mapperPressed, SIGNAL(mapped(int)), SLOT(guiParamPressed(int))); connect(mapperReleased, SIGNAL(mapped(int)), SLOT(guiParamReleased(int))); + connect(mapperContextMenuReq, SIGNAL(mapped(int)), SLOT(guiContextMenuReq(int))); for (it = l.begin(); it != l.end(); ++it) { obj = *it; @@ -3142,7 +3235,7 @@ PluginGui::PluginGui(MusECore::PluginIBase* p) const char* name = ba.constData(); if (*name !='P') continue; - unsigned long parameter; // p4.0.21 + unsigned long parameter; int rv = sscanf(name, "P%lu", ¶meter); if(rv != 1) continue; @@ -3150,6 +3243,7 @@ PluginGui::PluginGui(MusECore::PluginIBase* p) mapper->setMapping(obj, nobj); mapperPressed->setMapping(obj, nobj); mapperReleased->setMapping(obj, nobj); + mapperContextMenuReq->setMapping(obj, nobj); gw[nobj].widget = (QWidget*)obj; gw[nobj].param = parameter; @@ -3159,15 +3253,15 @@ PluginGui::PluginGui(MusECore::PluginIBase* p) gw[nobj].type = GuiWidgets::SLIDER; ((Slider*)obj)->setId(nobj); ((Slider*)obj)->setCursorHoming(true); - for(unsigned long i = 0; i < nobj; i++) // p4.0.21 + for(unsigned long i = 0; i < nobj; i++) { if(gw[i].type == GuiWidgets::DOUBLE_LABEL && gw[i].param == parameter) ((DoubleLabel*)gw[i].widget)->setSlider((Slider*)obj); } - connect(obj, SIGNAL(sliderMoved(double,int)), mapper, SLOT(map())); - connect(obj, SIGNAL(sliderPressed(int)), SLOT(guiSliderPressed(int))); - connect(obj, SIGNAL(sliderReleased(int)), SLOT(guiSliderReleased(int))); - connect(obj, SIGNAL(sliderRightClicked(const QPoint &, int)), SLOT(guiSliderRightClicked(const QPoint &, int))); + connect((Slider*)obj, SIGNAL(sliderMoved(double,int)), mapper, SLOT(map())); + connect((Slider*)obj, SIGNAL(sliderPressed(int)), SLOT(guiSliderPressed(int))); + connect((Slider*)obj, SIGNAL(sliderReleased(int)), SLOT(guiSliderReleased(int))); + connect((Slider*)obj, SIGNAL(sliderRightClicked(const QPoint &, int)), SLOT(guiSliderRightClicked(const QPoint &, int))); } else if (strcmp(obj->metaObject()->className(), "MusEGui::DoubleLabel") == 0) { gw[nobj].type = GuiWidgets::DOUBLE_LABEL; @@ -3180,17 +3274,23 @@ PluginGui::PluginGui(MusECore::PluginIBase* p) break; } } - connect(obj, SIGNAL(valueChanged(double,int)), mapper, SLOT(map())); + connect((DoubleLabel*)obj, SIGNAL(valueChanged(double,int)), mapper, SLOT(map())); } else if (strcmp(obj->metaObject()->className(), "QCheckBox") == 0) { gw[nobj].type = GuiWidgets::QCHECKBOX; - connect(obj, SIGNAL(toggled(bool)), mapper, SLOT(map())); - connect(obj, SIGNAL(pressed()), mapperPressed, SLOT(map())); - connect(obj, SIGNAL(released()), mapperReleased, SLOT(map())); + gw[nobj].widget->setContextMenuPolicy(Qt::CustomContextMenu); + connect((QCheckBox*)obj, SIGNAL(toggled(bool)), mapper, SLOT(map())); + connect((QCheckBox*)obj, SIGNAL(pressed()), mapperPressed, SLOT(map())); + connect((QCheckBox*)obj, SIGNAL(released()), mapperReleased, SLOT(map())); + connect((QCheckBox*)obj, SIGNAL(customContextMenuRequested(const QPoint &)), + mapperContextMenuReq, SLOT(map())); } else if (strcmp(obj->metaObject()->className(), "QComboBox") == 0) { gw[nobj].type = GuiWidgets::QCOMBOBOX; - connect(obj, SIGNAL(activated(int)), mapper, SLOT(map())); + gw[nobj].widget->setContextMenuPolicy(Qt::CustomContextMenu); + connect((QComboBox*)obj, SIGNAL(activated(int)), mapper, SLOT(map())); + connect((QComboBox*)obj, SIGNAL(customContextMenuRequested(const QPoint &)), + mapperContextMenuReq, SLOT(map())); } else { printf("unknown widget class %s\n", obj->metaObject()->className()); @@ -3201,7 +3301,6 @@ PluginGui::PluginGui(MusECore::PluginIBase* p) updateValues(); // otherwise the GUI won't have valid data } else { - // p3.4.43 view = new QScrollArea; view->setWidgetResizable(true); setCentralWidget(view); @@ -3212,13 +3311,13 @@ PluginGui::PluginGui(MusECore::PluginIBase* p) mw->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); - unsigned long n = plugin->parameters(); // p4.0.21 + unsigned long n = plugin->parameters(); params = new GuiParam[n]; QFontMetrics fm = fontMetrics(); int h = fm.height() + 4; - for (unsigned long i = 0; i < n; ++i) { // p4.0.21 + for (unsigned long i = 0; i < n; ++i) { QLabel* label = 0; LADSPA_PortRangeHint range = plugin->range(i); double lower = 0.0; // default values @@ -3281,7 +3380,7 @@ PluginGui::PluginGui(MusECore::PluginIBase* p) grid->addWidget(params[i].actuator, i, 0, 1, 3); } if (params[i].type == GuiParam::GUI_SLIDER) { - connect(params[i].actuator, SIGNAL(sliderMoved(double,int)), SLOT(sliderChanged(double,int))); + connect(params[i].actuator, SIGNAL(sliderMoved(double,int,bool)), SLOT(sliderChanged(double,int,bool))); connect(params[i].label, SIGNAL(valueChanged(double,int)), SLOT(labelChanged(double,int))); connect(params[i].actuator, SIGNAL(sliderPressed(int)), SLOT(ctrlPressed(int))); connect(params[i].actuator, SIGNAL(sliderReleased(int)), SLOT(ctrlReleased(int))); @@ -3429,21 +3528,17 @@ void PluginGui::ctrlPressed(int param) if(track) { track->setPluginCtrlVal(id, val); - MusEGlobal::song->controllerChange(track); - track->startAutoRecord(id, val); } } else if(params[param].type == GuiParam::GUI_SWITCH) { - float val = (float)((CheckBox*)params[param].actuator)->isChecked(); // p4.0.21 + float val = (float)((CheckBox*)params[param].actuator)->isChecked(); plugin->setParam(param, val); if(track) { track->setPluginCtrlVal(id, val); - MusEGlobal::song->controllerChange(track); - track->startAutoRecord(id, val); } } @@ -3498,13 +3593,17 @@ void PluginGui::ctrlRightClicked(const QPoint &p, int param) // sliderChanged //--------------------------------------------------------- -void PluginGui::sliderChanged(double val, int param) +void PluginGui::sliderChanged(double val, int param, bool shift_pressed) { AutomationType at = AUTO_OFF; MusECore::AudioTrack* track = plugin->track(); if(track) at = track->automationType(); + if ( (at == AUTO_WRITE) || + (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) + plugin->enableController(param, false); + if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) val = pow(10.0, val/20.0); else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) @@ -3523,9 +3622,7 @@ void PluginGui::sliderChanged(double val, int param) if(track) { track->setPluginCtrlVal(id, val); - MusEGlobal::song->controllerChange(track); - - track->recordAutomation(id, val); + if (!shift_pressed) track->recordAutomation(id, val); //with shift, we get straight lines :) } } @@ -3540,6 +3637,10 @@ void PluginGui::labelChanged(double val, int param) if(track) at = track->automationType(); + if ( (at == AUTO_WRITE) || + (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) + plugin->enableController(param, false); + double dval = val; if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) dval = MusECore::fast_log10(val) * 20.0; @@ -3559,8 +3660,6 @@ void PluginGui::labelChanged(double val, int param) if(track) { track->setPluginCtrlVal(id, val); - MusEGlobal::song->controllerChange(track); - track->startAutoRecord(id, val); } } @@ -3689,7 +3788,7 @@ void PluginGui::setOn(bool val) void PluginGui::updateValues() { if (params) { - for (unsigned long i = 0; i < plugin->parameters(); ++i) { // p4.0.21 + for (unsigned long i = 0; i < plugin->parameters(); ++i) { GuiParam* gp = ¶ms[i]; if (gp->type == GuiParam::GUI_SLIDER) { double lv = plugin->param(i); @@ -3710,10 +3809,10 @@ void PluginGui::updateValues() } } else if (gw) { - for (unsigned long i = 0; i < nobj; ++i) { // p4.0.21 + for (unsigned long i = 0; i < nobj; ++i) { QWidget* widget = gw[i].widget; int type = gw[i].type; - unsigned long param = gw[i].param; // p4.0.21 + unsigned long param = gw[i].param; float val = plugin->param(param); switch(type) { case GuiWidgets::SLIDER: @@ -3766,12 +3865,16 @@ void PluginGui::updateControls() if (params) { - for (unsigned long i = 0; i < plugin->parameters(); ++i) { // p4.0.21 + for (unsigned long i = 0; i < plugin->parameters(); ++i) { GuiParam* gp = ¶ms[i]; if (gp->type == GuiParam::GUI_SLIDER) { - if( plugin->controllerEnabled(i) && plugin->controllerEnabled2(i) ) { - double lv = plugin->track()->pluginCtrlVal(MusECore::genACnum(plugin->id(), i)); + double lv = plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), i), + MusEGlobal::audio->curFramePos(), + !MusEGlobal::automation || + plugin->track()->automationType() == AUTO_OFF || + !plugin->controllerEnabled(i) || + !plugin->controllerEnabled2(i)); double sv = lv; if (LADSPA_IS_HINT_LOGARITHMIC(params[i].hint)) sv = MusECore::fast_log10(lv) * 20.0; @@ -3791,12 +3894,15 @@ void PluginGui::updateControls() gp->label->blockSignals(false); } } - } else if (gp->type == GuiParam::GUI_SWITCH) { - if( plugin->controllerEnabled(i) && plugin->controllerEnabled2(i) ) { - bool v = (int)plugin->track()->pluginCtrlVal(MusECore::genACnum(plugin->id(), i)); + bool v = (int)plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), i), + MusEGlobal::audio->curFramePos(), + !MusEGlobal::automation || + plugin->track()->automationType() == AUTO_OFF || + !plugin->controllerEnabled(i) || + !plugin->controllerEnabled2(i)); if(((CheckBox*)(gp->actuator))->isChecked() != v) { ((CheckBox*)(gp->actuator))->blockSignals(true); @@ -3808,15 +3914,19 @@ void PluginGui::updateControls() } } else if (gw) { - for (unsigned long i = 0; i < nobj; ++i) { // p4.0.21 + for (unsigned long i = 0; i < nobj; ++i) { QWidget* widget = gw[i].widget; int type = gw[i].type; - unsigned long param = gw[i].param; // p4.0.21 + unsigned long param = gw[i].param; switch(type) { case GuiWidgets::SLIDER: - if( plugin->controllerEnabled(param) && plugin->controllerEnabled2(param) ) { - double v = plugin->track()->pluginCtrlVal(MusECore::genACnum(plugin->id(), param)); + double v = plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), param), + MusEGlobal::audio->curFramePos(), + !MusEGlobal::automation || + plugin->track()->automationType() == AUTO_OFF || + !plugin->controllerEnabled(param) || + !plugin->controllerEnabled2(param)); if(((Slider*)widget)->value() != v) { ((Slider*)widget)->blockSignals(true); @@ -3826,9 +3936,13 @@ void PluginGui::updateControls() } break; case GuiWidgets::DOUBLE_LABEL: - if( plugin->controllerEnabled(param) && plugin->controllerEnabled2(param) ) { - double v = plugin->track()->pluginCtrlVal(MusECore::genACnum(plugin->id(), param)); + double v = plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), param), + MusEGlobal::audio->curFramePos(), + !MusEGlobal::automation || + plugin->track()->automationType() == AUTO_OFF || + !plugin->controllerEnabled(param) || + !plugin->controllerEnabled2(param)); if(((DoubleLabel*)widget)->value() != v) { ((DoubleLabel*)widget)->blockSignals(true); @@ -3838,9 +3952,13 @@ void PluginGui::updateControls() } break; case GuiWidgets::QCHECKBOX: - if( plugin->controllerEnabled(param) && plugin->controllerEnabled2(param) ) { - bool b = (bool) plugin->track()->pluginCtrlVal(MusECore::genACnum(plugin->id(), param)); + bool b = (bool) plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), param), + MusEGlobal::audio->curFramePos(), + !MusEGlobal::automation || + plugin->track()->automationType() == AUTO_OFF || + !plugin->controllerEnabled(param) || + !plugin->controllerEnabled2(param)); if(((QCheckBox*)widget)->isChecked() != b) { ((QCheckBox*)widget)->blockSignals(true); @@ -3850,9 +3968,13 @@ void PluginGui::updateControls() } break; case GuiWidgets::QCOMBOBOX: - if( plugin->controllerEnabled(param) && plugin->controllerEnabled2(param) ) { - int n = (int) plugin->track()->pluginCtrlVal(MusECore::genACnum(plugin->id(), param)); + int n = (int) plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), param), + MusEGlobal::audio->curFramePos(), + !MusEGlobal::automation || + plugin->track()->automationType() == AUTO_OFF || + !plugin->controllerEnabled(param) || + !plugin->controllerEnabled2(param)); if(((QComboBox*)widget)->currentIndex() != n) { ((QComboBox*)widget)->blockSignals(true); @@ -3873,7 +3995,7 @@ void PluginGui::updateControls() void PluginGui::guiParamChanged(int idx) { QWidget* w = gw[idx].widget; - unsigned long param = gw[idx].param; // p4.0.21 + unsigned long param = gw[idx].param; int type = gw[idx].type; AutomationType at = AUTO_OFF; @@ -3881,6 +4003,10 @@ void PluginGui::guiParamChanged(int idx) if(track) at = track->automationType(); + if ( (at == AUTO_WRITE) || + (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) + plugin->enableController(param, false); + double val = 0.0; switch(type) { case GuiWidgets::SLIDER: @@ -3897,7 +4023,7 @@ void PluginGui::guiParamChanged(int idx) break; } - for (unsigned long i = 0; i < nobj; ++i) { // p4.0.21 + for (unsigned long i = 0; i < nobj; ++i) { QWidget* widget = gw[i].widget; if (widget == w || param != gw[i].param) continue; @@ -3922,10 +4048,7 @@ void PluginGui::guiParamChanged(int idx) if(track && id != -1) { id = MusECore::genACnum(id, param); - track->setPluginCtrlVal(id, val); - MusEGlobal::song->controllerChange(track); - switch(type) { case GuiWidgets::DOUBLE_LABEL: @@ -3946,7 +4069,7 @@ void PluginGui::guiParamChanged(int idx) void PluginGui::guiParamPressed(int idx) { - unsigned long param = gw[idx].param; // p4.0.21 + unsigned long param = gw[idx].param; AutomationType at = AUTO_OFF; MusECore::AudioTrack* track = plugin->track(); @@ -3965,8 +4088,8 @@ void PluginGui::guiParamPressed(int idx) // NOTE: For this to be of any use, the freeverb gui 2142.ui // would have to be used, and changed to use CheckBox and ComboBox // instead of QCheckBox and QComboBox, since both of those would - // need customization (Ex. QCheckBox doesn't check on click). - /* DELETETHIS 10 plus above + // need customization (Ex. QCheckBox doesn't check on click). RECHECK: Qt4 it does? + /* switch(type) { case GuiWidgets::QCHECKBOX: double val = (double)((CheckBox*)w)->isChecked(); @@ -3986,7 +4109,7 @@ void PluginGui::guiParamPressed(int idx) void PluginGui::guiParamReleased(int idx) { - unsigned long param = gw[idx].param; // p4.0.21 + unsigned long param = gw[idx].param; int type = gw[idx].type; AutomationType at = AUTO_OFF; @@ -4011,8 +4134,8 @@ void PluginGui::guiParamReleased(int idx) // NOTE: For this to be of any use, the freeverb gui 2142.ui // would have to be used, and changed to use CheckBox and ComboBox // instead of QCheckBox and QComboBox, since both of those would - // need customization (Ex. QCheckBox doesn't check on click). - /* DELETETHIS 10 plus above + // need customization (Ex. QCheckBox doesn't check on click). // RECHECK Qt4 it does? + /* switch(type) { case GuiWidgets::QCHECKBOX: double val = (double)((CheckBox*)w)->isChecked(); @@ -4032,7 +4155,7 @@ void PluginGui::guiParamReleased(int idx) void PluginGui::guiSliderPressed(int idx) { - unsigned long param = gw[idx].param; // p4.0.21 + unsigned long param = gw[idx].param; QWidget *w = gw[idx].widget; AutomationType at = AUTO_OFF; @@ -4054,12 +4177,10 @@ void PluginGui::guiSliderPressed(int idx) plugin->setParam(param, val); track->setPluginCtrlVal(id, val); - MusEGlobal::song->controllerChange(track); - track->startAutoRecord(id, val); // Needed so that paging a slider updates a label or other buddy control. - for (unsigned long i = 0; i < nobj; ++i) { // p4.0.21 + for (unsigned long i = 0; i < nobj; ++i) { QWidget* widget = gw[i].widget; if (widget == w || param != gw[i].param) continue; @@ -4126,6 +4247,15 @@ void PluginGui::guiSliderRightClicked(const QPoint &p, int idx) MusEGlobal::song->execAutomationCtlPopup(plugin->track(), p, MusECore::genACnum(id, param)); } +//--------------------------------------------------------- +// guiContextMenuReq +//--------------------------------------------------------- + +void PluginGui::guiContextMenuReq(int idx) +{ + guiSliderRightClicked(QCursor().pos(), idx); +} + //--------------------------------------------------------- // PluginLoader //--------------------------------------------------------- diff --git a/muse2/muse/plugin.h b/muse2/muse/plugin.h index 9c671097..06e99564 100644 --- a/muse2/muse/plugin.h +++ b/muse2/muse/plugin.h @@ -246,12 +246,16 @@ class PluginIBase virtual void enableController(unsigned long i, bool v = true) = 0; virtual bool controllerEnabled(unsigned long i) const = 0; + virtual void enable2Controller(unsigned long i, bool v = true) = 0; virtual bool controllerEnabled2(unsigned long i) const = 0; + virtual void enableAllControllers(bool v = true) = 0; + virtual void enable2AllControllers(bool v = true) = 0; virtual void updateControllers() = 0; virtual void writeConfiguration(int level, Xml& xml) = 0; virtual bool readConfiguration(Xml& xml, bool readPreset=false) = 0; + virtual bool addScheduledControlEvent(unsigned long i, float val, unsigned frame); // returns true if event cannot be delivered virtual unsigned long parameters() const = 0; virtual unsigned long parametersOut() const = 0; virtual void setParam(unsigned long i, float val) = 0; @@ -412,6 +416,10 @@ class Pipeline : public std::vector { void move(int idx, bool up); bool empty(int idx) const; void setChannels(int); + bool addScheduledControlEvent(int track_ctrl_id, float val, unsigned frame); // returns true if event cannot be delivered + void enableController(int track_ctrl_id, bool en); + void enable2Controller(int track_ctrl_id, bool en); + void controllersEnabled(int track_ctrl_id, bool* en1, bool* en2); }; typedef Pipeline::iterator iPluginI; @@ -497,7 +505,7 @@ class PluginGui : public QMainWindow { void load(); void save(); void bypassToggled(bool); - void sliderChanged(double, int); + void sliderChanged(double, int, bool); void labelChanged(double, int); void guiParamChanged(int); void ctrlPressed(int); @@ -508,6 +516,7 @@ class PluginGui : public QMainWindow { void guiSliderReleased(int); void ctrlRightClicked(const QPoint &, int); void guiSliderRightClicked(const QPoint &, int); + void guiContextMenuReq(int idx); protected slots: void heartBeat(); diff --git a/muse2/muse/seqmsg.cpp b/muse2/muse/seqmsg.cpp index d5257f80..f60a2d51 100644 --- a/muse2/muse/seqmsg.cpp +++ b/muse2/muse/seqmsg.cpp @@ -46,6 +46,7 @@ namespace MusECore { // sendMsg //--------------------------------------------------------- +// this function blocks until the request has been processed void Audio::sendMsg(AudioMsg* m) { static int sno = 0; @@ -522,7 +523,6 @@ void Audio::msgSwapControllerIDX(AudioTrack* node, int idx1, int idx2) msg.a = idx1; msg.b = idx2; sendMsg(&msg); - MusEGlobal::song->controllerChange(node); } //--------------------------------------------------------- @@ -537,7 +537,6 @@ void Audio::msgClearControllerEvents(AudioTrack* node, int acid) msg.snode = node; msg.ival = acid; sendMsg(&msg); - MusEGlobal::song->controllerChange(node); } //--------------------------------------------------------- @@ -581,7 +580,6 @@ void Audio::msgEraseACEvent(AudioTrack* node, int acid, int frame) msg.ival = acid; msg.a = frame; sendMsg(&msg); - MusEGlobal::song->controllerChange(node); } //--------------------------------------------------------- @@ -598,7 +596,6 @@ void Audio::msgEraseRangeACEvents(AudioTrack* node, int acid, int frame1, int fr msg.a = frame1; msg.b = frame2; sendMsg(&msg); - MusEGlobal::song->controllerChange(node); } //--------------------------------------------------------- @@ -615,7 +612,6 @@ void Audio::msgAddACEvent(AudioTrack* node, int acid, int frame, double val) msg.a = frame; msg.dval = val; sendMsg(&msg); - MusEGlobal::song->controllerChange(node); } //--------------------------------------------------------- @@ -633,7 +629,6 @@ void Audio::msgChangeACEvent(AudioTrack* node, int acid, int frame, int newFrame msg.b = newFrame; msg.dval = val; sendMsg(&msg); - MusEGlobal::song->controllerChange(node); } //--------------------------------------------------------- @@ -1296,6 +1291,18 @@ void Audio::msgSetSendMetronome(AudioTrack* track, bool b) sendMessage(&msg, false); } +//--------------------------------------------------------- +// msgStartMidiLearn +// Start learning midi +//--------------------------------------------------------- + +void Audio::msgStartMidiLearn() +{ + AudioMsg msg; + msg.id = AUDIO_START_MIDI_LEARN; + sendMessage(&msg, false); +} + //--------------------------------------------------------- // msgBounce // start bounce operation diff --git a/muse2/muse/song.cpp b/muse2/muse/song.cpp index 0c7a0c73..020d620c 100644 --- a/muse2/muse/song.cpp +++ b/muse2/muse/song.cpp @@ -57,11 +57,13 @@ #include "sync.h" #include "midictrl.h" #include "menutitleitem.h" +#include "midi_audio_control.h" #include "tracks_duplicate.h" #include "midi.h" #include "al/sig.h" #include "keyevent.h" #include +#include "tempo.h" namespace MusEGlobal { MusECore::Song* song = 0; @@ -762,18 +764,11 @@ void Song::changeAllPortDrumCtrlEvents(bool add, bool drumonly) void Song::addACEvent(AudioTrack* t, int acid, int frame, double val) { MusEGlobal::audio->msgAddACEvent(t, acid, frame, val); - emit controllerChanged(t); } void Song::changeACEvent(AudioTrack* t, int acid, int frame, int newFrame, double val) { MusEGlobal::audio->msgChangeACEvent(t, acid, frame, newFrame, val); - emit controllerChanged(t); -} - -void Song::controllerChange(Track* t) -{ - emit controllerChanged(t); } //--------------------------------------------------------- @@ -883,7 +878,6 @@ void Song::cmdAddRecordedEvents(MidiTrack* mt, EventList* events, unsigned start if (endTick < tick) endTick = tick; } - // Added by Tim. p3.3.8 // Round the end up (again) using the Arranger part snap raster value. endTick = AL::sigmap.raster2(endTick, arrangerRaster()); @@ -1626,6 +1620,26 @@ void Song::beat() if (MusEGlobal::audio->isPlaying()) setPos(0, MusEGlobal::audio->tickPos(), true, false, true); + // Process external tempo changes: + while(!_tempoFifo.isEmpty()) + MusEGlobal::tempo_rec_list.addTempo(_tempoFifo.get()); + + // Update anything related to audio controller graphs etc. + for(ciTrack it = _tracks.begin(); it != _tracks.end(); ++ it) + { + if((*it)->isMidiTrack()) + continue; + AudioTrack* at = static_cast(*it); + CtrlListList* cll = at->controller(); + for(ciCtrlList icl = cll->begin(); icl != cll->end(); ++icl) + { + CtrlList* cl = icl->second; + if(cl->isVisible() && !cl->dontShow() && cl->guiUpdatePending()) + emit controllerChanged(at, cl->id()); + cl->setGuiUpdatePending(false); + } + } + // Update synth native guis at the heartbeat rate. for(ciSynthI is = _synthIs.begin(); is != _synthIs.end(); ++is) (*is)->guiHeartBeat(); @@ -2078,6 +2092,7 @@ void Song::clear(bool signal, bool clear_all) while (loop); MusEGlobal::tempomap.clear(); + MusEGlobal::tempo_rec_list.clear(); AL::sigmap.clear(); MusEGlobal::keymap.clear(); @@ -2419,13 +2434,14 @@ void Song::recordEvent(MidiTrack* mt, Event& event) int Song::execAutomationCtlPopup(AudioTrack* track, const QPoint& menupos, int acid) { - enum { PREV_EVENT, NEXT_EVENT, ADD_EVENT, CLEAR_EVENT, CLEAR_RANGE, CLEAR_ALL_EVENTS }; + enum { PREV_EVENT=0, NEXT_EVENT, ADD_EVENT, CLEAR_EVENT, CLEAR_RANGE, CLEAR_ALL_EVENTS, MIDI_ASSIGN, MIDI_CLEAR }; QMenu* menu = new QMenu; int count = 0; bool isEvent = false, canSeekPrev = false, canSeekNext = false, canEraseRange = false; bool canAdd = false; double ctlval = 0.0; + int frame = 0; if(track) { ciCtrlList icl = track->controller()->find(acid); @@ -2434,11 +2450,17 @@ int Song::execAutomationCtlPopup(AudioTrack* track, const QPoint& menupos, int a CtrlList *cl = icl->second; canAdd = true; - //int frame = pos[0].frame(); DELETETHIS - int frame = MusEGlobal::audio->pos().frame(); // Try this. p4.0.33 DELETETHIS + frame = MusEGlobal::audio->pos().frame(); + + bool en1, en2; + track->controllersEnabled(acid, &en1, &en2); + + AutomationType at = track->automationType(); + if(!MusEGlobal::automation || at == AUTO_OFF || !en1 || !en2) + ctlval = cl->curVal(); + else + ctlval = cl->value(frame); - ctlval = cl->curVal(); - count = cl->size(); if(count) { @@ -2491,6 +2513,40 @@ int Song::execAutomationCtlPopup(AudioTrack* track, const QPoint& menupos, int a clearAction->setData(CLEAR_ALL_EVENTS); clearAction->setEnabled((bool)count); + + menu->addSeparator(); + menu->addAction(new MusEGui::MenuTitleItem(tr("Midi control"), menu)); + + QAction *assign_act = menu->addAction(tr("Assign")); + assign_act->setCheckable(false); + assign_act->setData(MIDI_ASSIGN); + + MidiAudioCtrlMap* macm = track->controller()->midiControls(); + AudioMidiCtrlStructMap amcs; + macm->find_audio_ctrl_structs(acid, &amcs); + + if(!amcs.empty()) + { + QAction *cact = menu->addAction(tr("Clear")); + cact->setData(MIDI_CLEAR); + menu->addSeparator(); + } + + for(iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) + { + int port, chan, mctrl; + macm->hash_values((*iamcs)->first, &port, &chan, &mctrl); + //QString s = QString("Port:%1 Chan:%2 Ctl:%3-%4").arg(port + 1) + QString s = QString("Port:%1 Chan:%2 Ctl:%3").arg(port + 1) + .arg(chan + 1) + //.arg((mctrl >> 8) & 0xff) + //.arg(mctrl & 0xff); + .arg(midiCtrlName(mctrl, true)); + QAction *mact = menu->addAction(s); + mact->setEnabled(false); + mact->setData(-1); // Not used + } + QAction* act = menu->exec(menupos); if (!act || !track) { @@ -2504,10 +2560,10 @@ int Song::execAutomationCtlPopup(AudioTrack* track, const QPoint& menupos, int a switch(sel) { case ADD_EVENT: - MusEGlobal::audio->msgAddACEvent(track, acid, pos[0].frame(), ctlval); + MusEGlobal::audio->msgAddACEvent(track, acid, frame, ctlval); break; case CLEAR_EVENT: - MusEGlobal::audio->msgEraseACEvent(track, acid, pos[0].frame()); + MusEGlobal::audio->msgEraseACEvent(track, acid, frame); break; case CLEAR_RANGE: @@ -2529,6 +2585,45 @@ int Song::execAutomationCtlPopup(AudioTrack* track, const QPoint& menupos, int a MusEGlobal::audio->msgSeekNextACEvent(track, acid); break; + case MIDI_ASSIGN: + { + int port = -1, chan = 0, ctrl = 0; + for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) + { + macm->hash_values((*iamcs)->first, &port, &chan, &ctrl); + break; // Only a single item for now, thanks! + } + + MusEGui::MidiAudioControl* pup = new MusEGui::MidiAudioControl(port, chan, ctrl); + + if(pup->exec() == QDialog::Accepted) + { + MusEGlobal::audio->msgIdle(true); // Gain access to structures, and sync with audio + // Erase all for now. + for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) + macm->erase(*iamcs); + + port = pup->port(); chan = pup->chan(); ctrl = pup->ctrl(); + if(port >= 0 && chan >=0 && ctrl >= 0) + // Add will replace if found. + macm->add_ctrl_struct(port, chan, ctrl, MusECore::MidiAudioCtrlStruct(acid)); + + MusEGlobal::audio->msgIdle(false); + } + + delete pup; + } + break; + + case MIDI_CLEAR: + if(!amcs.empty()) + MusEGlobal::audio->msgIdle(true); // Gain access to structures, and sync with audio + for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) + macm->erase(*iamcs); + if(!amcs.empty()) + MusEGlobal::audio->msgIdle(false); + break; + default: return -1; break; @@ -2755,8 +2850,62 @@ void Song::processAutomationEvents() // Process (and clear) rec events. ((AudioTrack*)(*i))->processAutomationEvents(); } + + MusEGlobal::audio->msgIdle(false); +} + +//--------------------------------------------------------- +// processMasterRec +//--------------------------------------------------------- + +void Song::processMasterRec() +{ + bool do_tempo = false; + + // Wait a few seconds for the tempo fifo to be empty. + int tout = 30; + while(!_tempoFifo.isEmpty()) + { + usleep(100000); + --tout; + if(tout == 0) + break; + } + + int tempo_rec_list_sz = MusEGlobal::tempo_rec_list.size(); + if(tempo_rec_list_sz != 0) + { + if(QMessageBox::question(MusEGlobal::muse, + tr("MusE: Tempo list"), + tr("External tempo changes were recorded.\nTransfer them to master tempo list?"), + QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel) == QMessageBox::Ok) + do_tempo = true; + } + + MusEGlobal::audio->msgIdle(true); // gain access to all data structures + + if(do_tempo) + { + // Erase from master tempo the (approximate) recording start/end tick range according to the recorded tempo map, + //MusEGlobal::tempomap.eraseRange(MusEGlobal::tempo_rec_list.frame2tick(MusEGlobal::audio->getStartRecordPos().frame()), + // MusEGlobal::tempo_rec_list.frame2tick(MusEGlobal::audio->getEndRecordPos().frame())); + // This is more accurate but lacks resolution: + MusEGlobal::tempomap.eraseRange(MusEGlobal::audio->getStartExternalRecTick(), MusEGlobal::audio->getEndExternalRecTick()); + + // Add the recorded tempos to the master tempo list: + for(int i = 0; i < tempo_rec_list_sz; ++i) + MusEGlobal::tempomap.addTempo(MusEGlobal::tempo_rec_list[i].tick, + MusEGlobal::tempo_rec_list[i].tempo, + false); // False: Defer normalize + MusEGlobal::tempomap.normalize(); + } + + MusEGlobal::tempo_rec_list.clear(); MusEGlobal::audio->msgIdle(false); + + if(do_tempo) + update(SC_TEMPO); } //--------------------------------------------------------- diff --git a/muse2/muse/song.h b/muse2/muse/song.h index 82b8cf18..6570ad8d 100644 --- a/muse2/muse/song.h +++ b/muse2/muse/song.h @@ -126,6 +126,8 @@ class Song : public QObject { int noteFifoWindex; int noteFifoRindex; + TempoFifo _tempoFifo; // External tempo changes, processed in heartbeat. + int updateFlags; TrackList _tracks; // tracklist as seen by arranger @@ -263,7 +265,7 @@ class Song : public QObject { // event manipulations //----------------------------------------- - void cmdAddRecordedWave(WaveTrack* track, Pos, Pos); + void cmdAddRecordedWave(WaveTrack* track, Pos, Pos); void cmdAddRecordedEvents(MidiTrack*, EventList*, unsigned); bool addEvent(Event&, Part*); void changeEvent(Event&, Event&, Part*); @@ -274,8 +276,8 @@ class Song : public QObject { void addACEvent(AudioTrack* t, int acid, int frame, double val); void changeACEvent(AudioTrack* t, int acid, int frame, int newFrame, double val); - void controllerChange(Track* t); - + void addExternalTempo(const TempoRecEvent& e) { _tempoFifo.put(e); } + //----------------------------------------- // part manipulations //----------------------------------------- @@ -332,6 +334,7 @@ class Song : public QObject { void msgInsertTrack(Track* track, int idx, bool u = true); void clearRecAutomation(bool clearList); void processAutomationEvents(); + void processMasterRec(); int execAutomationCtlPopup(AudioTrack*, const QPoint&, int); int execMidiAutomationCtlPopup(MidiTrack*, MidiPart*, const QPoint&, int); void connectJackRoutes(AudioTrack* track, bool disconnect); @@ -428,7 +431,7 @@ class Song : public QObject { void markerChanged(int); void midiPortsChanged(); void midiNote(int pitch, int velo); - void controllerChanged(MusECore::Track* t); // maybe DELETETHIS: this only triggers a redraw in pcanvas.cpp; what is this for? + void controllerChanged(MusECore::Track*, int); void newPartsCreated(const std::map< MusECore::Part*, std::set >&); }; diff --git a/muse2/muse/sync.cpp b/muse2/muse/sync.cpp index 56560a5e..bf9d2613 100644 --- a/muse2/muse/sync.cpp +++ b/muse2/muse/sync.cpp @@ -21,6 +21,7 @@ // //========================================================= +#include #include #include "sync.h" #include "song.h" @@ -59,12 +60,13 @@ static unsigned int curExtMidiSyncTick = 0; unsigned int volatile lastExtMidiSyncTick = 0; double volatile curExtMidiSyncTime = 0.0; double volatile lastExtMidiSyncTime = 0.0; +MusECore::MidiSyncInfo::SyncRecFilterPresetType syncRecFilterPreset = MusECore::MidiSyncInfo::SMALL; +double syncRecTempoValQuant = 1.0; // Not used yet. DELETETHIS? // static bool mcStart = false; // static int mcStartTick; -// p3.3.25 // From the "Introduction to the Volatile Keyword" at Embedded dot com /* A variable should be declared volatile whenever its value could change unexpectedly. ... global variables within a multi-threaded application @@ -820,18 +822,25 @@ void MidiSeq::alignAllTicks(int frameOverride) recTick2 = 0; if (MusEGlobal::debugSync) printf("alignAllTicks curFrame=%d recTick=%d tempo=%.3f frameOverride=%d\n",curFrame,recTick,(float)((1000000.0 * 60.0)/tempo), frameOverride); - + + lastTempo = 0; + for(int i = 0; i < _clockAveragerPoles; ++i) + { + _avgClkDiffCounter[i] = 0; + _averagerFull[i] = false; + } + _lastRealTempo = 0.0; } //--------------------------------------------------------- // realtimeSystemInput // real time message received //--------------------------------------------------------- -void MidiSeq::realtimeSystemInput(int port, int c) +void MidiSeq::realtimeSystemInput(int port, int c, double time) { if (MusEGlobal::midiInputTrace) - printf("realtimeSystemInput port:%d 0x%x\n", port+1, c); + printf("realtimeSystemInput port:%d 0x%x time:%f\n", port+1, c, time); MidiPort* mp = &MusEGlobal::midiPorts[port]; @@ -873,6 +882,9 @@ void MidiSeq::realtimeSystemInput(int port, int c) if(p != port && MusEGlobal::midiPorts[p].syncInfo().MCOut()) MusEGlobal::midiPorts[p].sendClock(); + MusEGlobal::lastExtMidiSyncTime = MusEGlobal::curExtMidiSyncTime; + MusEGlobal::curExtMidiSyncTime = time; + if(MusEGlobal::playPendingFirstClock) { MusEGlobal::playPendingFirstClock = false; @@ -887,12 +899,158 @@ void MidiSeq::realtimeSystemInput(int port, int c) // Can't check audio state, might not be playing yet, we might miss some increments. if(playStateExt) { - MusEGlobal::lastExtMidiSyncTime = MusEGlobal::curExtMidiSyncTime; - MusEGlobal::curExtMidiSyncTime = curTime(); int div = MusEGlobal::config.division/24; MusEGlobal::midiExtSyncTicks += div; MusEGlobal::lastExtMidiSyncTick = MusEGlobal::curExtMidiSyncTick; MusEGlobal::curExtMidiSyncTick += div; + + if(MusEGlobal::song->record() && MusEGlobal::lastExtMidiSyncTime > 0.0) + { + double diff = MusEGlobal::curExtMidiSyncTime - MusEGlobal::lastExtMidiSyncTime; + if(diff != 0.0) + { + if(_clockAveragerPoles == 0) + { + double real_tempo = 60.0/(diff * 24.0); + if(_tempoQuantizeAmount > 0.0) + { + double f_mod = fmod(real_tempo, _tempoQuantizeAmount); + if(f_mod < _tempoQuantizeAmount/2.0) + real_tempo -= f_mod; + else + real_tempo += _tempoQuantizeAmount - f_mod; + } + int new_tempo = ((1000000.0 * 60.0) / (real_tempo)); + if(new_tempo != lastTempo) + { + lastTempo = new_tempo; + // Compute tick for this tempo - it is one step back in time. + int add_tick = MusEGlobal::curExtMidiSyncTick - div; + if(MusEGlobal::debugSync) + printf("adding new tempo tick:%d curExtMidiSyncTick:%d avg_diff:%f real_tempo:%f new_tempo:%d = %f\n", add_tick, MusEGlobal::curExtMidiSyncTick, diff, real_tempo, new_tempo, (double)((1000000.0 * 60.0)/new_tempo)); + MusEGlobal::song->addExternalTempo(TempoRecEvent(add_tick, new_tempo)); + } + } + else + { + double avg_diff = diff; + for(int pole = 0; pole < _clockAveragerPoles; ++pole) + { + timediff[pole][_avgClkDiffCounter[pole]] = avg_diff; + ++_avgClkDiffCounter[pole]; + if(_avgClkDiffCounter[pole] >= _clockAveragerStages[pole]) + { + _avgClkDiffCounter[pole] = 0; + _averagerFull[pole] = true; + } + + // Each averager needs to be full before we can pass the data to + // the next averager or use the data if all averagers are full... + if(!_averagerFull[pole]) + break; + else + { + avg_diff = 0.0; + for(int i = 0; i < _clockAveragerStages[pole]; ++i) + avg_diff += timediff[pole][i]; + avg_diff /= _clockAveragerStages[pole]; + + int fin_idx = _clockAveragerPoles - 1; + + // On the first pole? Check for large differences. + if(_preDetect && pole == 0) + { + double real_tempo = 60.0/(avg_diff * 24.0); + double real_tempo_diff = abs(real_tempo - _lastRealTempo); + + // If the tempo changed a large amount, reset. + if(real_tempo_diff >= 10.0) // TODO: User-adjustable? + { + if(_tempoQuantizeAmount > 0.0) + { + double f_mod = fmod(real_tempo, _tempoQuantizeAmount); + if(f_mod < _tempoQuantizeAmount/2.0) + real_tempo -= f_mod; + else + real_tempo += _tempoQuantizeAmount - f_mod; + } + _lastRealTempo = real_tempo; + int new_tempo = ((1000000.0 * 60.0) / (real_tempo)); + + if(new_tempo != lastTempo) + { + lastTempo = new_tempo; + // Compute tick for this tempo - it is way back in time. + int add_tick = MusEGlobal::curExtMidiSyncTick - _clockAveragerStages[0] * div; + if(add_tick < 0) + { + printf("FIXME sync: adding restart tempo curExtMidiSyncTick:%d: add_tick:%d < 0 !\n", MusEGlobal::curExtMidiSyncTick, add_tick); + add_tick = 0; + } + if(MusEGlobal::debugSync) + printf("adding restart tempo tick:%d curExtMidiSyncTick:%d tick_idx_sub:%d avg_diff:%f real_tempo:%f real_tempo_diff:%f new_tempo:%d = %f\n", add_tick, MusEGlobal::curExtMidiSyncTick, _clockAveragerStages[0], avg_diff, real_tempo, real_tempo_diff, new_tempo, (double)((1000000.0 * 60.0)/new_tempo)); + MusEGlobal::song->addExternalTempo(TempoRecEvent(add_tick, new_tempo)); + } + + // Reset all the poles. + //for(int i = 0; i < clockAveragerPoles; ++i) + // We have a value for this pole, let's keep it but reset the other poles. + for(int i = 1; i < _clockAveragerPoles; ++i) + { + _avgClkDiffCounter[i] = 0; + _averagerFull[i] = false; + } + break; + } + } + + // On the last pole? + // All averagers need to be full before we can use the data... + if(pole == fin_idx) + { + double real_tempo = 60.0/(avg_diff * 24.0); + double real_tempo_diff = abs(real_tempo - _lastRealTempo); + + if(real_tempo_diff >= _tempoQuantizeAmount/2.0) // Anti-hysteresis + { + if(_tempoQuantizeAmount > 0.0) + { + double f_mod = fmod(real_tempo, _tempoQuantizeAmount); + if(f_mod < _tempoQuantizeAmount/2.0) + real_tempo -= f_mod; + else + real_tempo += _tempoQuantizeAmount - f_mod; + } + _lastRealTempo = real_tempo; + int new_tempo = ((1000000.0 * 60.0) / (real_tempo)); + + if(new_tempo != lastTempo) + { + lastTempo = new_tempo; + // Compute tick for this tempo - it is way back in time. + int tick_idx_sub = 0; + for(int i = 0; i <= pole; ++i) + tick_idx_sub += _clockAveragerStages[i]; + // Compensate: Each pole > 0 has a delay one less than its number of stages. + // For example three pole {8, 8, 8} has a delay of 22 not 24. + tick_idx_sub -= pole; + int add_tick = MusEGlobal::curExtMidiSyncTick - tick_idx_sub * div; + if(add_tick < 0) + { + printf("FIXME sync: adding new tempo curExtMidiSyncTick:%d: add_tick:%d < 0 !\n", MusEGlobal::curExtMidiSyncTick, add_tick); + add_tick = 0; + } + if(MusEGlobal::debugSync) + printf("adding new tempo tick:%d curExtMidiSyncTick:%d tick_idx_sub:%d avg_diff:%f real_tempo:%f new_tempo:%d = %f\n", add_tick, MusEGlobal::curExtMidiSyncTick, tick_idx_sub, avg_diff, real_tempo, new_tempo, (double)((1000000.0 * 60.0)/new_tempo)); + MusEGlobal::song->addExternalTempo(TempoRecEvent(add_tick, new_tempo)); + } + } + } + } + } + } + } + } } //BEGIN : Original code: DELETETHIS 250 @@ -1185,8 +1343,6 @@ void MidiSeq::realtimeSystemInput(int port, int c) alignAllTicks(); storedtimediffs = 0; - for (int i=0; i<24; i++) - timediff[i] = 0.0; // p3.3.26 1/23/10 DELETETHIS 6 // Changed because msgPlay calls MusEGlobal::audioDevice->seekTransport(song->cPos()) @@ -1243,7 +1399,7 @@ void MidiSeq::realtimeSystemInput(int port, int c) if (MusEGlobal::debugSync) printf("realtimeSystemInput stop\n"); - + //DELETETHIS 7 // Just in case the process still runs a cycle or two and causes the // audio tick position to increment, reset the incrementer and force diff --git a/muse2/muse/sync.h b/muse2/muse/sync.h index 41ad34ad..09ea06e9 100644 --- a/muse2/muse/sync.h +++ b/muse2/muse/sync.h @@ -32,9 +32,11 @@ namespace MusECore { class Xml; - class MidiSyncInfo { + public: + enum SyncRecFilterPresetType { NONE=0, TINY, SMALL, MEDIUM, LARGE, LARGE_WITH_PRE_DETECT, TYPE_END }; + private: int _port; @@ -151,6 +153,9 @@ extern int volatile curMidiSyncInPort; extern MusECore::BValue useJackTransport; extern bool volatile jackTransportMaster; extern unsigned int syncSendFirstClockDelay; // In milliseconds. +extern unsigned int volatile lastExtMidiSyncTick; +extern MusECore::MidiSyncInfo::SyncRecFilterPresetType syncRecFilterPreset; +extern double syncRecTempoValQuant; } // namespace MusEGlobal diff --git a/muse2/muse/tempo.cpp b/muse2/muse/tempo.cpp index 1147fd78..d339f516 100644 --- a/muse2/muse/tempo.cpp +++ b/muse2/muse/tempo.cpp @@ -32,6 +32,7 @@ namespace MusEGlobal { MusECore::TempoList tempomap; +MusECore::TempoRecList tempo_rec_list; } namespace MusECore { @@ -59,7 +60,7 @@ TempoList::~TempoList() // add //--------------------------------------------------------- -void TempoList::add(unsigned tick, int tempo) +void TempoList::add(unsigned tick, int tempo, bool do_normalize) { if (tick > MAX_TICK) tick = MAX_TICK; @@ -74,7 +75,8 @@ void TempoList::add(unsigned tick, int tempo) ne->tick = tick; insert(std::pair (tick, ev)); } - normalize(); + if(do_normalize) + normalize(); } //--------------------------------------------------------- @@ -119,6 +121,33 @@ void TempoList::clear() ++_tempoSN; } +//--------------------------------------------------------- +// eraseRange +//--------------------------------------------------------- + +void TempoList::eraseRange(unsigned stick, unsigned etick) +{ + if(stick >= etick || stick > MAX_TICK) + return; + if(etick > MAX_TICK) + etick = MAX_TICK; + + iTEvent se = MusEGlobal::tempomap.upper_bound(stick); + if(se == end() || (se->first == MAX_TICK+1)) + return; + + iTEvent ee = MusEGlobal::tempomap.upper_bound(etick); + + ee->second->tempo = se->second->tempo; + ee->second->tick = se->second->tick; + + for(iTEvent ite = se; ite != ee; ++ite) + delete ite->second; + erase(se, ee); // Erase range does NOT include the last element. + normalize(); + ++_tempoSN; +} + //--------------------------------------------------------- // tempo //--------------------------------------------------------- @@ -224,9 +253,9 @@ void TempoList::setGlobalTempo(int val) // addTempo //--------------------------------------------------------- -void TempoList::addTempo(unsigned t, int tempo) +void TempoList::addTempo(unsigned t, int tempo, bool do_normalize) { - add(t, tempo); + add(t, tempo, do_normalize); ++_tempoSN; } @@ -538,5 +567,54 @@ int TEvent::read(Xml& xml) return 0; } +//--------------------------------------------------------- +// put +// return true on fifo overflow +//--------------------------------------------------------- + +bool TempoFifo::put(const TempoRecEvent& event) + { + if (size < TEMPO_FIFO_SIZE) { + fifo[wIndex] = event; + wIndex = (wIndex + 1) % TEMPO_FIFO_SIZE; + // q_atomic_increment(&size); + ++size; + return false; + } + return true; + } + +//--------------------------------------------------------- +// get +//--------------------------------------------------------- + +TempoRecEvent TempoFifo::get() + { + TempoRecEvent event(fifo[rIndex]); + rIndex = (rIndex + 1) % TEMPO_FIFO_SIZE; + --size; + return event; + } + +//--------------------------------------------------------- +// peek +//--------------------------------------------------------- + +const TempoRecEvent& TempoFifo::peek(int n) + { + int idx = (rIndex + n) % TEMPO_FIFO_SIZE; + return fifo[idx]; + } + +//--------------------------------------------------------- +// remove +//--------------------------------------------------------- + +void TempoFifo::remove() + { + rIndex = (rIndex + 1) % TEMPO_FIFO_SIZE; + --size; + } + } // namespace MusECore diff --git a/muse2/muse/tempo.h b/muse2/muse/tempo.h index 7a3f413b..71f1580c 100644 --- a/muse2/muse/tempo.h +++ b/muse2/muse/tempo.h @@ -25,11 +25,16 @@ #define __TEMPO_H__ #include +#include #ifndef MAX_TICK #define MAX_TICK (0x7fffffff/100) #endif +// Tempo ring buffer size +#define TEMPO_FIFO_SIZE 1024 + + namespace MusECore { class Xml; @@ -70,8 +75,7 @@ class TempoList : public TEMPOLIST { int _tempo; // tempo if not using tempo list int _globalTempo; // %percent 50-200% - void normalize(); - void add(unsigned tick, int tempo); + void add(unsigned tick, int tempo, bool do_normalize = true); void change(unsigned tick, int newTempo); void del(iTEvent); void del(unsigned tick); @@ -79,7 +83,9 @@ class TempoList : public TEMPOLIST { public: TempoList(); ~TempoList(); + void normalize(); void clear(); + void eraseRange(unsigned stick, unsigned etick); void read(Xml&); void write(int, Xml&) const; @@ -96,18 +102,62 @@ class TempoList : public TEMPOLIST { int tempoSN() const { return _tempoSN; } void setTempo(unsigned tick, int newTempo); - void addTempo(unsigned t, int tempo); + void addTempo(unsigned t, int tempo, bool do_normalize = true); void delTempo(unsigned tick); void changeTempo(unsigned tick, int newTempo); + bool masterFlag() const { return useList; } bool setMasterFlag(unsigned tick, bool val); int globalTempo() const { return _globalTempo; } void setGlobalTempo(int val); }; +//--------------------------------------------------------- +// Tempo Record Event +//--------------------------------------------------------- + +struct TempoRecEvent { + int tempo; + unsigned tick; + TempoRecEvent() { } + TempoRecEvent(unsigned tk, unsigned t) { + tick = tk; + tempo = t; + } + }; + +class TempoRecList : public std::vector +{ + public: + void addTempo(int tick, int tempo) { push_back(TempoRecEvent(tick, tempo)); } + void addTempo(const TempoRecEvent& e) { push_back(e); } +}; + +//--------------------------------------------------------- +// TempoFifo +//--------------------------------------------------------- + +class TempoFifo { + TempoRecEvent fifo[TEMPO_FIFO_SIZE]; + volatile int size; + int wIndex; + int rIndex; + + public: + TempoFifo() { clear(); } + bool put(const TempoRecEvent& event); // returns true on fifo overflow + TempoRecEvent get(); + const TempoRecEvent& peek(int = 0); + void remove(); + bool isEmpty() const { return size == 0; } + void clear() { size = 0, wIndex = 0, rIndex = 0; } + int getSize() const { return size; } + }; + } // namespace MusECore namespace MusEGlobal { extern MusECore::TempoList tempomap; +extern MusECore::TempoRecList tempo_rec_list; } #endif diff --git a/muse2/muse/thread.cpp b/muse2/muse/thread.cpp index 69238922..14f9750e 100644 --- a/muse2/muse/thread.cpp +++ b/muse2/muse/thread.cpp @@ -250,61 +250,11 @@ void Thread::removePollFd(int fd, int action) void Thread::loop() { - // Changed by Tim. p3.3.17 - if (!MusEGlobal::debugMode) { if (mlockall(MCL_CURRENT | MCL_FUTURE)) perror("WARNING: Cannot lock memory:"); } -/* DELETETHIS 46 - pthread_attr_t* attributes = 0; - attributes = (pthread_attr_t*) malloc(sizeof(pthread_attr_t)); - pthread_attr_init(attributes); - - if (MusEGlobal::realTimeScheduling && realTimePriority > 0) { - - doSetuid(); -// if (pthread_attr_setschedpolicy(attributes, SCHED_FIFO)) { -// printf("cannot set FIFO scheduling class for RT thread\n"); -// } -// if (pthread_attr_setscope (attributes, PTHREAD_SCOPE_SYSTEM)) { -// printf("Cannot set scheduling scope for RT thread\n"); -// } -// struct sched_param rt_param; -// memset(&rt_param, 0, sizeof(rt_param)); -// rt_param.sched_priority = realTimePriority; -// if (pthread_attr_setschedparam (attributes, &rt_param)) { -// printf("Cannot set scheduling priority %d for RT thread (%s)\n", -// realTimePriority, strerror(errno)); -// } - - // do the SCHED_FIFO stuff _after_ thread creation: - struct sched_param *param = new struct sched_param; - param->sched_priority = realTimePriority; - int error = pthread_setschedparam(pthread_self(), SCHED_FIFO, param); - if (error != 0) - perror( "error set_schedparam 2:"); - -// if (!MusEGlobal::debugMode) { -// if (mlockall(MCL_CURRENT|MCL_FUTURE)) -// perror("WARNING: Cannot lock memory:"); -// } - - undoSetuid(); - } - -*/ - - -/* -#define BIG_ENOUGH_STACK (1024*1024*1) - char buf[BIG_ENOUGH_STACK]; - for (int i = 0; i < BIG_ENOUGH_STACK; i++) - buf[i] = i; -#undef BIG_ENOUGH_STACK -*/ - #ifdef __APPLE__ #define BIG_ENOUGH_STACK (1024*256*1) #else @@ -318,7 +268,8 @@ void Thread::loop() pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0); pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0); - int policy = 0; + int policy = buf[0]; // Initialize using buf[0] to keep the compiler from complaining about unused buf. + policy = 0; // Now set the true desired inital value. if ((policy = sched_getscheduler (0)) < 0) { printf("Thread: Cannot get current client scheduler: %s\n", strerror(errno)); } diff --git a/muse2/muse/track.cpp b/muse2/muse/track.cpp index 905f9030..d353b4bb 100644 --- a/muse2/muse/track.cpp +++ b/muse2/muse/track.cpp @@ -37,6 +37,7 @@ #include "midictrl.h" #include "helper.h" #include "limits.h" +#include "dssihost.h" namespace MusECore { @@ -384,7 +385,21 @@ void Track::clearRecAutomation(bool clearList) continue; p->enableAllControllers(true); } - + + if(type() == AUDIO_SOFTSYNTH) + { + SynthI* synth = static_cast(this); + if(synth->synth() && synth->synth()->synthType() == Synth::DSSI_SYNTH) + { + SynthIF* sif = synth->sif(); + if(sif) + { + DssiSynthIF* dssi_sif = static_cast(sif); + dssi_sif->enableAllControllers(true); + } + } + } + if(clearList) t->recEvents()->clear(); } diff --git a/muse2/muse/track.h b/muse2/muse/track.h index 93f2f673..3da34912 100644 --- a/muse2/muse/track.h +++ b/muse2/muse/track.h @@ -46,7 +46,7 @@ class PluginI; class SynthI; class Xml; class DrumMap; - +class ControlEvent; //--------------------------------------------------------- // Track @@ -447,6 +447,9 @@ class AudioTrack : public Track { virtual void setAutomationType(AutomationType t); void processAutomationEvents(); CtrlRecList* recEvents() { return &_recEvents; } + bool addScheduledControlEvent(int track_ctrl_id, float val, unsigned frame); // return true if event cannot be delivered + void enableController(int track_ctrl_id, bool en); + void controllersEnabled(int track_ctrl_id, bool* en1, bool* en2) const; void recordAutomation(int n, double v); void startAutoRecord(int, double); void stopAutoRecord(int, double); diff --git a/muse2/muse/undo.h b/muse2/muse/undo.h index b88a9457..2f582d8e 100644 --- a/muse2/muse/undo.h +++ b/muse2/muse/undo.h @@ -125,12 +125,12 @@ struct UndoOp { UndoOp(UndoType type, Track* track, const char* old_name, const char* new_name); UndoOp(UndoType type, Track* track, int old_chan, int new_chan); UndoOp(UndoType type); - }; +}; class Undo : public std::list { public: bool empty() const; - }; +}; typedef Undo::iterator iUndoOp; typedef Undo::reverse_iterator riUndoOp; @@ -141,7 +141,7 @@ class UndoList : public std::list { public: void clearDelete(); UndoList(bool _isUndo) : std::list() { isUndo=_isUndo; } - }; +}; typedef UndoList::iterator iUndo; typedef UndoList::reverse_iterator riUndo; diff --git a/muse2/muse/wave.cpp b/muse2/muse/wave.cpp index 5e19648d..8d17a10d 100644 --- a/muse2/muse/wave.cpp +++ b/muse2/muse/wave.cpp @@ -964,7 +964,7 @@ int ClipList::idx(const Clip& clip) const // cmdAddRecordedWave //--------------------------------------------------------- -void Song::cmdAddRecordedWave(MusECore::WaveTrack* track, MusECore::Pos s, MusECore::Pos e) +void Song::cmdAddRecordedWave(MusECore::WaveTrack* track, MusECore::Pos s, MusECore::Pos e) { if (MusEGlobal::debugMsg) printf("cmdAddRecordedWave - loopCount = %d, punchin = %d", MusEGlobal::audio->loopCount(), punchin()); @@ -976,15 +976,31 @@ void Song::cmdAddRecordedWave(MusECore::WaveTrack* track, MusECore::Pos s, MusEC return; } + // If externally clocking (and therefore master was forced off), + // tempos may have been recorded. We really should temporarily force + // the master tempo map on in order to properly determine the ticks below. + // Else internal clocking, the user decided to record either with or without + // master on, so let it be. + // FIXME: We really should allow the master flag to be on at the same time as + // the external sync flag! AFAIR when external sync is on, no part of the app shall + // depend on the tempo map anyway, so it should not matter whether it's on or off. + // If we do that, then we may be able to remove this section and user simply decides + // whether master is on/off, because we may be able to use the flag to determine + // whether to record external tempos at all, because we may want a switch for it! + bool master_was_on = MusEGlobal::tempomap.masterFlag(); + if(MusEGlobal::extSyncFlag.value() && !master_was_on) + MusEGlobal::tempomap.setMasterFlag(0, true); + if((MusEGlobal::audio->loopCount() > 0 && s.tick() > lPos().tick()) || (punchin() && s.tick() < lPos().tick())) s.setTick(lPos().tick()); // If we are looping, just set the end to the right marker, since we don't know how many loops have occurred. // (Fixed: Added Audio::loopCount) // Otherwise if punchout is on, limit the end to the right marker. - if((MusEGlobal::audio->loopCount() > 0) || (punchout() && e.tick() > rPos().tick()) ) + if((MusEGlobal::audio->loopCount() > 0) || (punchout() && e.tick() > rPos().tick()) ) e.setTick(rPos().tick()); + // No part to be created? Delete the rec sound file. - if(s.tick() >= e.tick()) + if(s.frame() >= e.frame()) { QString st = f->path(); // The function which calls this function already does this immediately after. But do it here anyway. @@ -992,19 +1008,30 @@ void Song::cmdAddRecordedWave(MusECore::WaveTrack* track, MusECore::Pos s, MusEC // counter has dropped by 2 and _recFile will probably deleted then remove(st.toLatin1().constData()); if(MusEGlobal::debugMsg) - printf("Song::cmdAddRecordedWave: remove file %s - start=%d end=%d\n", st.toLatin1().constData(), s.tick(), e.tick()); + printf("Song::cmdAddRecordedWave: remove file %s - startframe=%d endframe=%d\n", st.toLatin1().constData(), s.frame(), e.frame()); + + // Restore master flag. + if(MusEGlobal::extSyncFlag.value() && !master_was_on) + MusEGlobal::tempomap.setMasterFlag(0, false); + return; } // Round the start down using the Arranger part snap raster value. - unsigned startTick = AL::sigmap.raster1(s.tick(), MusEGlobal::song->arrangerRaster()); + int a_rast = MusEGlobal::song->arrangerRaster(); + unsigned sframe = (a_rast == 1) ? s.frame() : Pos(AL::sigmap.raster1(s.tick(), MusEGlobal::song->arrangerRaster())).frame(); // Round the end up using the Arranger part snap raster value. - unsigned endTick = AL::sigmap.raster2(e.tick(), MusEGlobal::song->arrangerRaster()); + unsigned eframe = (a_rast == 1) ? e.frame() : Pos(AL::sigmap.raster2(e.tick(), MusEGlobal::song->arrangerRaster())).frame(); + unsigned etick = Pos(eframe).tick(); + + // Done using master tempo map. Restore master flag. + if(MusEGlobal::extSyncFlag.value() && !master_was_on) + MusEGlobal::tempomap.setMasterFlag(0, false); f->update(); MusECore::WavePart* part = new MusECore::WavePart(track); - part->setTick(startTick); - part->setLenTick(endTick - startTick); + part->setFrame(sframe); + part->setLenFrame(eframe - sframe); part->setName(track->name()); // create Event @@ -1015,20 +1042,18 @@ void Song::cmdAddRecordedWave(MusECore::WaveTrack* track, MusECore::Pos s, MusEC track->setRecFile(0); event.setSpos(0); - // Since the part start was snapped down, we must apply the difference so that the // wave event tick lines up with when the user actually started recording. - // Added by Tim. p3.3.8 - event.setTick(s.tick() - startTick); - - + event.setFrame(s.frame() - sframe); + // NO Can't use this. SF reports too long samples at first part recorded in sequence. See samples() - funny business with SEEK ? + //event.setLenFrame(f.samples()); event.setLenFrame(e.frame() - s.frame()); part->addEvent(event); MusEGlobal::song->cmdAddPart(part); - if (MusEGlobal::song->len() < endTick) - MusEGlobal::song->setLen(endTick); + if (MusEGlobal::song->len() < etick) + MusEGlobal::song->setLen(etick); } //--------------------------------------------------------- diff --git a/muse2/muse/wavetrack.cpp b/muse2/muse/wavetrack.cpp index dd890b42..b55a67d6 100644 --- a/muse2/muse/wavetrack.cpp +++ b/muse2/muse/wavetrack.cpp @@ -223,7 +223,17 @@ bool WaveTrack::getData(unsigned framePos, int channels, unsigned nframe, float* if (MusEGlobal::audio->freewheel()) { } else { - if (fifo.put(channels, nframe, bp, MusEGlobal::audio->pos().frame())) +#ifdef _AUDIO_USE_TRUE_FRAME_ + // TODO: Tested: This is the line that would be needed for Audio Inputs, + // because the data arrived in the previous period! Test OK, the waves are in sync. + // So we need to do Audio Inputs separately above, AND find a way to mix two overlapping + // periods into the file! Nothing wrong with the FIFO per se, we could stamp overlapping + // times. But the soundfile just writes, does not mix. + //if (fifo.put(channels, nframe, bp, MusEGlobal::audio->previousPos().frame())) + // + // Tested: This line is OK for track-to-track recording, the waves are in sync: +#endif + if (fifo.put(channels, nframe, bp, MusEGlobal::audio->pos().frame())) printf("WaveTrack::getData(%d, %d, %d): fifo overrun\n", framePos, channels, nframe); } diff --git a/muse2/muse/widgets/CMakeLists.txt b/muse2/muse/widgets/CMakeLists.txt index fae0d614..88706339 100644 --- a/muse2/muse/widgets/CMakeLists.txt +++ b/muse2/muse/widgets/CMakeLists.txt @@ -57,6 +57,7 @@ QT4_WRAP_CPP (widget_mocs menutitleitem.h meter.h metronome.h + midi_audio_control.h midisyncimpl.h mixdowndialog.h mlabel.h @@ -122,6 +123,7 @@ file (GLOB widgets_ui_files itransformbase.ui metronomebase.ui midisync.ui + midi_audio_control_base.ui mittransposebase.ui mixdowndialogbase.ui mtrackinfobase.ui @@ -169,6 +171,7 @@ file (GLOB widgets_source_files menutitleitem.cpp meter.cpp metronome.cpp + midi_audio_control.cpp midisyncimpl.cpp mixdowndialog.cpp mlabel.cpp diff --git a/muse2/muse/widgets/aboutbox.ui b/muse2/muse/widgets/aboutbox.ui index 250f656f..8b4d5b37 100644 --- a/muse2/muse/widgets/aboutbox.ui +++ b/muse2/muse/widgets/aboutbox.ui @@ -48,7 +48,7 @@ - Version 2 pre-alpha + Version 2 false @@ -58,7 +58,7 @@ - (C) Copyright 1999-2010 Werner Schweer and others. + (C) Copyright 1999-2012 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. diff --git a/muse2/muse/widgets/bigtime.cpp b/muse2/muse/widgets/bigtime.cpp index 0b213f28..5adf4966 100644 --- a/muse2/muse/widgets/bigtime.cpp +++ b/muse2/muse/widgets/bigtime.cpp @@ -32,6 +32,7 @@ #include "song.h" #include "app.h" #include "gconfig.h" +#include "audio.h" namespace MusEGlobal { extern int mtcType; @@ -229,7 +230,9 @@ bool BigTime::setString(unsigned v) return true; } - unsigned absFrame = MusEGlobal::tempomap.tick2frame(v); + // Quick fix: Not much to do but ignore the supplied tick: We need the exact frame here. + unsigned absFrame = MusEGlobal::audio->pos().frame(); + int bar, beat; unsigned tick; AL::sigmap.tickValues(v, &bar, &beat, &tick); diff --git a/muse2/muse/widgets/filedialog.cpp b/muse2/muse/widgets/filedialog.cpp index 6e7d6882..aa8c5df1 100644 --- a/muse2/muse/widgets/filedialog.cpp +++ b/muse2/muse/widgets/filedialog.cpp @@ -102,6 +102,7 @@ void MFileDialog::globalToggled(bool flag) { if (flag) { buttons.readMidiPortsButton->setChecked(false); + readMidiPortsSaved = false; if (lastGlobalDir.isEmpty()) lastGlobalDir = MusEGlobal::museGlobalShare + QString("/") + baseDir; // Initialize if first time setDirectory(lastGlobalDir); @@ -117,6 +118,7 @@ void MFileDialog::userToggled(bool flag) { if (flag) { buttons.readMidiPortsButton->setChecked(true); + readMidiPortsSaved = true; if (lastUserDir.isEmpty()) { //lastUserDir = MusEGlobal::museUser + QString("/") + baseDir; // Initialize if first time lastUserDir = MusEGlobal::configPath + QString("/") + baseDir; // Initialize if first time // p4.0.39 @@ -140,6 +142,7 @@ void MFileDialog::projectToggled(bool flag) { if (flag) { buttons.readMidiPortsButton->setChecked(true); + readMidiPortsSaved = true; QString s; if (MusEGlobal::museProject == MusEGlobal::museProjectInitPath ) { // if project path is uninitialized, meaning it is still set to museProjectInitPath. @@ -158,6 +161,29 @@ void MFileDialog::projectToggled(bool flag) } } +void MFileDialog::fileChanged(const QString& path) +{ + bool is_mid = path.endsWith(".mid", Qt::CaseInsensitive) || + path.endsWith(".midi", Qt::CaseInsensitive) || + path.endsWith(".kar", Qt::CaseInsensitive); + + if (is_mid) + { + readMidiPortsSaved=buttons.readMidiPortsButton->isChecked(); + buttons.readMidiPortsButton->setEnabled(false); + buttons.readMidiPortsButton->setChecked(false); + } + else + { + if (!buttons.readMidiPortsButton->isEnabled()) + { + buttons.readMidiPortsButton->setEnabled(true); + buttons.readMidiPortsButton->setChecked(readMidiPortsSaved); + } + } + +} + //--------------------------------------------------------- // MFileDialog @@ -167,6 +193,7 @@ MFileDialog::MFileDialog(const QString& dir, const QString& filter, QWidget* parent, bool writeFlag) : QFileDialog(parent, QString(), QString("."), filter) { + readMidiPortsSaved = true; showButtons = false; lastUserDir = ""; lastGlobalDir = ""; @@ -201,10 +228,11 @@ MFileDialog::MFileDialog(const QString& dir, buttons.userButton->setAutoExclusive(true); buttons.projectButton->setAutoExclusive(true); - connect(buttons.globalButton, SIGNAL(toggled(bool)), this, SLOT(globalToggled(bool))); + connect(buttons.globalButton, SIGNAL(toggled(bool)), this, SLOT(globalToggled(bool))); connect(buttons.userButton, SIGNAL(toggled(bool)), this, SLOT(userToggled(bool))); connect(buttons.projectButton, SIGNAL(toggled(bool)), this, SLOT(projectToggled(bool))); connect(this, SIGNAL(directoryEntered(const QString&)), SLOT(directoryChanged(const QString&))); + connect(this, SIGNAL(currentChanged(const QString&)), SLOT(fileChanged(const QString&))); if (writeFlag) { setAcceptMode(QFileDialog::AcceptSave); diff --git a/muse2/muse/widgets/filedialog.h b/muse2/muse/widgets/filedialog.h index 1e2616da..582e943d 100644 --- a/muse2/muse/widgets/filedialog.h +++ b/muse2/muse/widgets/filedialog.h @@ -52,9 +52,12 @@ class MFileDialog : public QFileDialog { QString lastUserDir, lastGlobalDir; bool showButtons; QString baseDir; + + bool readMidiPortsSaved; private slots: void directoryChanged(const QString& directory); + void fileChanged(const QString&); public slots: void globalToggled(bool); void userToggled(bool); diff --git a/muse2/muse/widgets/midi_audio_control.cpp b/muse2/muse/widgets/midi_audio_control.cpp new file mode 100644 index 00000000..78c8de3c --- /dev/null +++ b/muse2/muse/widgets/midi_audio_control.cpp @@ -0,0 +1,340 @@ +//========================================================= +// MusE +// Linux Music Editor +// +// midi_audio_control.cpp +// Copyright (C) 2012 by Tim E. Real (terminator356 at users.sourceforge.net) +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//========================================================= +#include "midi_audio_control.h" + +#include "globals.h" +#include "globaldefs.h" +#include "mididev.h" +#include "midiport.h" +#include "midictrl.h" +#include "audio.h" +#include "app.h" + +#include + +namespace MusEGui { + +// ----------------------------------- +// MidiAudioControl +// Set port to -1 to automatically set it to the port of +// the first combo box item (the first readable port). +// ----------------------------------- + +MidiAudioControl::MidiAudioControl(int port, int chan, int ctrl, QWidget* parent) + : QDialog(parent) +{ + setupUi(this); + + _port = port; + _chan = chan; + _ctrl = ctrl; + _is_learning = false; + + update(); + + connect(learnPushButton, SIGNAL(clicked(bool)), SLOT(learnChanged(bool))); + connect(portComboBox, SIGNAL(currentIndexChanged(int)), SLOT(portChanged(int))); + connect(channelSpinBox, SIGNAL(valueChanged(int)), SLOT(chanChanged())); + connect(controlTypeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(ctrlTypeChanged(int))); + connect(ctrlHiSpinBox, SIGNAL(valueChanged(int)), SLOT(ctrlHChanged())); + connect(ctrlLoSpinBox, SIGNAL(valueChanged(int)), SLOT(ctrlLChanged())); + connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(configChanged())); + connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartbeat())); +} + +void MidiAudioControl::heartbeat() +{ + if(_is_learning) + { + if(MusEGlobal::midiLearnPort != -1) + { + int port_item = portComboBox->findData(MusEGlobal::midiLearnPort); + if(port_item != -1 && port_item != portComboBox->currentIndex()) + { + _port = MusEGlobal::midiLearnPort; + portComboBox->blockSignals(true); + portComboBox->setCurrentIndex(port_item); + portComboBox->blockSignals(false); + } + } + + if(MusEGlobal::midiLearnChan != -1 && (MusEGlobal::midiLearnChan + 1) != channelSpinBox->value()) + { + _chan = MusEGlobal::midiLearnChan; + channelSpinBox->blockSignals(true); + channelSpinBox->setValue(_chan + 1); + channelSpinBox->blockSignals(false); + } + + if(MusEGlobal::midiLearnCtrl != -1) + { + int type = MusECore::midiControllerType(MusEGlobal::midiLearnCtrl); + if(type < controlTypeComboBox->count() && type != controlTypeComboBox->currentIndex()) + { + controlTypeComboBox->blockSignals(true); + controlTypeComboBox->setCurrentIndex(type); + controlTypeComboBox->blockSignals(false); + } + + int hv = (MusEGlobal::midiLearnCtrl >> 8) & 0xff; + int lv = MusEGlobal::midiLearnCtrl & 0xff; + if(type == MusECore::MidiController::Program || type == MusECore::MidiController::Pitch) + { + ctrlHiSpinBox->setEnabled(false); + ctrlLoSpinBox->setEnabled(false); + ctrlHiSpinBox->blockSignals(true); + ctrlLoSpinBox->blockSignals(true); + ctrlHiSpinBox->setValue(0); + ctrlLoSpinBox->setValue(0); + ctrlHiSpinBox->blockSignals(false); + ctrlLoSpinBox->blockSignals(false); + } + else if(type == MusECore::MidiController::Controller7) + { + ctrlHiSpinBox->setEnabled(false); + ctrlLoSpinBox->setEnabled(true); + + ctrlHiSpinBox->blockSignals(true); + ctrlHiSpinBox->setValue(0); + ctrlHiSpinBox->blockSignals(false); + + if(lv != ctrlLoSpinBox->value()) + { + ctrlLoSpinBox->blockSignals(true); + ctrlLoSpinBox->setValue(lv); + ctrlLoSpinBox->blockSignals(false); + } + } + else + { + ctrlHiSpinBox->setEnabled(true); + ctrlLoSpinBox->setEnabled(true); + if(hv != ctrlHiSpinBox->value()) + { + ctrlHiSpinBox->blockSignals(true); + ctrlHiSpinBox->setValue(hv); + ctrlHiSpinBox->blockSignals(false); + } + if(lv != ctrlLoSpinBox->value()) + { + ctrlLoSpinBox->blockSignals(true); + ctrlLoSpinBox->setValue(lv); + ctrlLoSpinBox->blockSignals(false); + } + } + + _ctrl = MusECore::midiCtrlTerms2Number(type, (ctrlHiSpinBox->value() << 8) + ctrlLoSpinBox->value()); + } + } +} + +void MidiAudioControl::learnChanged(bool v) +{ + _is_learning = v; + if(_is_learning) + MusEGlobal::audio->msgStartMidiLearn(); // Resets the learn values to -1. +} + +void MidiAudioControl::resetLearn() +{ + _is_learning = false; + learnPushButton->blockSignals(true); + learnPushButton->setChecked(false); + learnPushButton->blockSignals(false); + MusEGlobal::audio->msgStartMidiLearn(); // Resets the learn values to -1. +} + +void MidiAudioControl::portChanged(int idx) +{ + if(idx == -1) + return; + int port_num = portComboBox->itemData(idx).toInt(); + if(port_num < 0 || port_num >= MIDI_PORTS) + return; + + _port = port_num; + resetLearn(); +} + +void MidiAudioControl::chanChanged() +{ + _chan = channelSpinBox->value() - 1; + resetLearn(); +} + +void MidiAudioControl::updateCtrlBoxes() +{ + int idx = controlTypeComboBox->currentIndex(); + if(idx == -1) + return; + + if(idx == MusECore::MidiController::Program || idx == MusECore::MidiController::Pitch) + { + ctrlHiSpinBox->setEnabled(false); + ctrlLoSpinBox->setEnabled(false); + ctrlHiSpinBox->blockSignals(true); + ctrlLoSpinBox->blockSignals(true); + ctrlHiSpinBox->setValue(0); + ctrlLoSpinBox->setValue(0); + ctrlHiSpinBox->blockSignals(false); + ctrlLoSpinBox->blockSignals(false); + } + else if(idx == MusECore::MidiController::Controller7) + { + ctrlHiSpinBox->setEnabled(false); + ctrlLoSpinBox->setEnabled(true); + + ctrlHiSpinBox->blockSignals(true); + ctrlHiSpinBox->setValue(0); + ctrlHiSpinBox->blockSignals(false); + } + else + { + ctrlHiSpinBox->setEnabled(true); + ctrlLoSpinBox->setEnabled(true); + } +} + +void MidiAudioControl::ctrlTypeChanged(int idx) +{ + if(idx == -1) + return; + + updateCtrlBoxes(); + + _ctrl = (ctrlHiSpinBox->value() << 8) + ctrlLoSpinBox->value(); + _ctrl = MusECore::midiCtrlTerms2Number(idx, _ctrl); + + resetLearn(); +} + +void MidiAudioControl::ctrlHChanged() +{ + if(controlTypeComboBox->currentIndex() == -1) + return; + _ctrl = (ctrlHiSpinBox->value() << 8) + ctrlLoSpinBox->value(); + _ctrl = MusECore::midiCtrlTerms2Number(controlTypeComboBox->currentIndex(), _ctrl); + + resetLearn(); +} + +void MidiAudioControl::ctrlLChanged() +{ + if(controlTypeComboBox->currentIndex() == -1) + return; + _ctrl = (ctrlHiSpinBox->value() << 8) + ctrlLoSpinBox->value(); + _ctrl = MusECore::midiCtrlTerms2Number(controlTypeComboBox->currentIndex(), _ctrl); + + resetLearn(); +} + +void MidiAudioControl::configChanged() +{ + update(); +} + +void MidiAudioControl::update() +{ + portComboBox->blockSignals(true); + portComboBox->clear(); + + int item_idx = 0; + for (int i = 0; i < MIDI_PORTS; ++i) { + MusECore::MidiDevice* md = MusEGlobal::midiPorts[i].device(); + if(!md) // In the case of this combo box, don't bother listing empty ports. + continue; + //if(!(md->rwFlags() & 1 || md->isSynti()) && (i != outPort)) + if(!(md->rwFlags() & 2) && (i != _port)) // Only readable ports, or current one. + continue; + QString name; + name.sprintf("%d:%s", i+1, MusEGlobal::midiPorts[i].portname().toLatin1().constData()); + portComboBox->insertItem(item_idx, name, i); + if(_port == -1) + _port = i; // Initialize + if(i == _port) + portComboBox->setCurrentIndex(item_idx); + item_idx++; + } + portComboBox->blockSignals(false); + + channelSpinBox->blockSignals(true); + channelSpinBox->setValue(_chan + 1); + channelSpinBox->blockSignals(false); + + int type = MusECore::midiControllerType(_ctrl); + if(type < controlTypeComboBox->count()) + { + controlTypeComboBox->blockSignals(true); + controlTypeComboBox->setCurrentIndex(type); + controlTypeComboBox->blockSignals(false); + } + + int hv = (_ctrl >> 8) & 0xff; + int lv = _ctrl & 0xff; + if(type == MusECore::MidiController::Program || type == MusECore::MidiController::Pitch) + { + ctrlHiSpinBox->setEnabled(false); + ctrlLoSpinBox->setEnabled(false); + ctrlHiSpinBox->blockSignals(true); + ctrlLoSpinBox->blockSignals(true); + ctrlHiSpinBox->setValue(0); + ctrlLoSpinBox->setValue(0); + ctrlHiSpinBox->blockSignals(false); + ctrlLoSpinBox->blockSignals(false); + } + else if(type == MusECore::MidiController::Controller7) + { + ctrlHiSpinBox->setEnabled(false); + ctrlLoSpinBox->setEnabled(true); + + ctrlHiSpinBox->blockSignals(true); + ctrlHiSpinBox->setValue(0); + ctrlHiSpinBox->blockSignals(false); + + if(lv != ctrlLoSpinBox->value()) + { + ctrlLoSpinBox->blockSignals(true); + ctrlLoSpinBox->setValue(lv); + ctrlLoSpinBox->blockSignals(false); + } + } + else + { + ctrlHiSpinBox->setEnabled(true); + ctrlLoSpinBox->setEnabled(true); + if(hv != ctrlHiSpinBox->value()) + { + ctrlHiSpinBox->blockSignals(true); + ctrlHiSpinBox->setValue(hv); + ctrlHiSpinBox->blockSignals(false); + } + if(lv != ctrlLoSpinBox->value()) + { + ctrlLoSpinBox->blockSignals(true); + ctrlLoSpinBox->setValue(lv); + ctrlLoSpinBox->blockSignals(false); + } + } +} + +} diff --git a/muse2/muse/widgets/midi_audio_control.h b/muse2/muse/widgets/midi_audio_control.h new file mode 100644 index 00000000..887de942 --- /dev/null +++ b/muse2/muse/widgets/midi_audio_control.h @@ -0,0 +1,60 @@ +//========================================================= +// MusE +// Linux Music Editor +// +// midi_audio_control.h +// Copyright (C) 2012 by Tim E. Real (terminator356 at users.sourceforge.net) +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//========================================================= +#ifndef MIDI_AUDIO_CONTROL_H +#define MIDI_AUDIO_CONTROL_H + +#include "ui_midi_audio_control_base.h" + +namespace MusEGui { + +class MidiAudioControl : public QDialog, public Ui::MidiAudioControlBase +{ + Q_OBJECT + +private: + int _port, _chan, _ctrl; + bool _is_learning; + void update(); + void resetLearn(); + void updateCtrlBoxes(); + +private slots: + void heartbeat(); + void learnChanged(bool); + void portChanged(int); + void chanChanged(); + void ctrlTypeChanged(int); + void ctrlHChanged(); + void ctrlLChanged(); + void configChanged(); + +public: + MidiAudioControl(int port = -1, int chan = 0, int ctrl = 0, QWidget* parent = 0); + int port() const { return _port; } + int chan() const { return _chan; } + int ctrl() const { return _ctrl; } +}; + +} + +#endif // MIDI_AUDIO_CONTROL_H diff --git a/muse2/muse/widgets/midi_audio_control_base.ui b/muse2/muse/widgets/midi_audio_control_base.ui new file mode 100644 index 00000000..2e341121 --- /dev/null +++ b/muse2/muse/widgets/midi_audio_control_base.ui @@ -0,0 +1,310 @@ + + + MidiAudioControlBase + + + + 0 + 0 + 341 + 148 + + + + Midi control + + + + + + + + + 0 + 0 + + + + Port: + + + + + + + + + + + 0 + 0 + + + + Channel: + + + + + + + + 0 + 0 + + + + 1 + + + 16 + + + + + + + + + + + Control type: + + + + + + + + 0 + 0 + + + + + Control7 + + + + + Control14 + + + + + RPN + + + + + NRPN + + + + + RPN14 + + + + + NRPN14 + + + + + Pitch + + + + + Program + + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + Hi: + + + + + + + + 0 + 0 + + + + 0 + + + 255 + + + 0 + + + + + + + + 0 + 0 + + + + Lo: + + + + + + + + 0 + 0 + + + + 0 + + + 255 + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 0 + 0 + + + + Learn + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 18 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + MidiAudioControlBase + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + MidiAudioControlBase + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/muse2/muse/widgets/midisync.ui b/muse2/muse/widgets/midisync.ui index a7464aaf..942a4e59 100644 --- a/muse2/muse/widgets/midisync.ui +++ b/muse2/muse/widgets/midisync.ui @@ -10,7 +10,7 @@ configuration dialog 0 0 655 - 419 + 445 @@ -335,6 +335,100 @@ Enabled inputs in the list will + + + + + Averaging applied to recorded external tempo changes. + + + External midi clock can be very jittery. +Tempo is derived from it and recorded. +It is usually desirable to average it and + limit the number of recorded changes. + +Tiny: 2 section 4/4 = 8 stages. +1/8T note averaging, may produce jitter. + +Small: 3 section 12/8/4 = 24 stages. +1/4 note averaging, may still produce jitter. + +Medium: 3 section 28/12/8 = 48 stages. +1/2 note averaging. Less jitter. + +Large: 4 section 48/48/48/48 = 192 stages. +Use this if the song has only one tempo. +Very low quantization values can be used. + +Large pre-detect: 4 section 8/48/48/48 = 152 + stages + first stage large step pre-detector. +Use this if you expect sudden large tempo steps. + +None: Use only if high accuracy is needed for + audio alignment on playback. Caution: Records + thousands of tempo changes per minute. MusE + may slow and the song file will be large. + + + + + + + Tempo record averaging + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + false + + + + + + + + + + + + + + + + + bpm + + + 0.000000000000000 + + + 100.000000000000000 + + + 0.010000000000000 + + + 1.000000000000000 + + + + + + + Tempo record quantization + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + false + + + + + + @@ -379,7 +473,7 @@ Enabled inputs in the list will - + @@ -388,18 +482,18 @@ Enabled inputs in the list will - - + + - Note: Sync delay and MTC sync currently not fully implemented + Note: Sync delay and MTC sync currently not fully implemented - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - false + false - + diff --git a/muse2/muse/widgets/midisyncimpl.cpp b/muse2/muse/widgets/midisyncimpl.cpp index 904e8759..e286ca74 100644 --- a/muse2/muse/widgets/midisyncimpl.cpp +++ b/muse2/muse/widgets/midisyncimpl.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "app.h" #include "song.h" @@ -137,32 +138,12 @@ void MidiSyncConfig::addDevice(QTreeWidgetItem *item, QTreeWidget *tree) tree->addTopLevelItem(item); } -/* -//--------------------------------------------------------- -// MidiSyncLViewItem -// setDevice -//--------------------------------------------------------- - -void MidiSyncLViewItem::setDevice(MusECore::MidiDevice* d) -{ - _device = d; - if(_device) - _syncInfo.copyParams(_device->syncInfo()); -} -*/ - -//--------------------------------------------------------- -// MidiSyncLViewItem -// setPort -//--------------------------------------------------------- - void MidiSyncLViewItem::setPort(int port) { _port = port; if(_port < 0 || port > MIDI_PORTS) return; - //_syncInfo.copyParams(MusEGlobal::midiPorts[port].syncInfo()); copyFromSyncInfo(MusEGlobal::midiPorts[port].syncInfo()); } @@ -221,41 +202,6 @@ MidiSyncConfig::MidiSyncConfig(QWidget* parent) _dirty = false; applyButton->setEnabled(false); - //inHeartBeat = true; - - //for(int i = 0; i < MIDI_PORTS; ++i) - // tmpMidiSyncPorts[i] = midiSyncPorts[i]; - - //bool ext = MusEGlobal::extSyncFlag.value(); - //syncMode->setButton(int(ext)); - //syncChanged(ext); -// extSyncCheckbox->setChecked(MusEGlobal::extSyncFlag.value()); - -// dstDevId->setValue(txDeviceId); -// srcDevId->setValue(rxDeviceId); -// srcSyncPort->setValue(rxSyncPort + 1); -// dstSyncPort->setValue(txSyncPort + 1); - -// mtcSync->setChecked(genMTCSync); -// mcSync->setChecked(genMCSync); -// midiMachineControl->setChecked(genMMC); - -// acceptMTCCheckbox->setChecked(acceptMTC); - //acceptMTCCheckbox->setChecked(false); -// acceptMCCheckbox->setChecked(acceptMC); -// acceptMMCCheckbox->setChecked(acceptMMC); - -// mtcSyncType->setCurrentItem(MusEGlobal::mtcType); - -// mtcOffH->setValue(MusEGlobal::mtcOffset.h()); -// mtcOffM->setValue(MusEGlobal::mtcOffset.m()); -// mtcOffS->setValue(MusEGlobal::mtcOffset.s()); -// mtcOffF->setValue(MusEGlobal::mtcOffset.f()); -// mtcOffSf->setValue(MusEGlobal::mtcOffset.sf()); - - - - devicesListView->setAllColumnsShowFocus(true); QStringList columnnames; columnnames << tr("Port") @@ -284,9 +230,11 @@ MidiSyncConfig::MidiSyncConfig(QWidget* parent) setToolTips(devicesListView->headerItem()); devicesListView->setFocusPolicy(Qt::NoFocus); - //MSyncHeaderTip::add(devicesListView->header(), QString("Midi sync ports")); - -// updateSyncInfoLV(); + syncRecFilterPreset->addItem(tr("None"), MusECore::MidiSyncInfo::NONE); + syncRecFilterPreset->addItem(tr("Tiny"), MusECore::MidiSyncInfo::TINY); + syncRecFilterPreset->addItem(tr("Small"), MusECore::MidiSyncInfo::SMALL); + syncRecFilterPreset->addItem(tr("Large"), MusECore::MidiSyncInfo::LARGE); + syncRecFilterPreset->addItem(tr("Large with pre-detect"), MusECore::MidiSyncInfo::LARGE_WITH_PRE_DETECT); songChanged(-1); @@ -308,14 +256,14 @@ MidiSyncConfig::MidiSyncConfig(QWidget* parent) connect(mtcSyncType, SIGNAL(activated(int)), SLOT(syncChanged())); connect(useJackTransportCheckbox, SIGNAL(clicked()), SLOT(syncChanged())); connect(jackTransportMasterCheckbox, SIGNAL(clicked()), SLOT(syncChanged())); + connect(syncRecFilterPreset, SIGNAL(currentIndexChanged(int)), SLOT(syncChanged())); + connect(syncRecTempoValQuant, SIGNAL(valueChanged(double)), SLOT(syncChanged())); connect(&MusEGlobal::extSyncFlag, SIGNAL(valueChanged(bool)), SLOT(extSyncChanged(bool))); connect(syncDelaySpinBox, SIGNAL(valueChanged(int)), SLOT(syncChanged())); // Done in show(). //connect(MusEGlobal::song, SIGNAL(songChanged(int)), SLOT(songChanged(int))); //connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartBeat())); - - //inHeartBeat = false; } MidiSyncConfig::~MidiSyncConfig() @@ -356,6 +304,17 @@ void MidiSyncConfig::songChanged(int flags) jackTransportMasterCheckbox->blockSignals(false); useJackTransportCheckbox->blockSignals(false); extSyncCheckbox->blockSignals(false); + + int fp_idx = syncRecFilterPreset->findData(MusEGlobal::syncRecFilterPreset); + if(fp_idx != -1) + { + syncRecFilterPreset->blockSignals(true); + syncRecFilterPreset->setCurrentIndex(fp_idx); + syncRecFilterPreset->blockSignals(false); + } + syncRecTempoValQuant->blockSignals(true); + syncRecTempoValQuant->setValue(MusEGlobal::syncRecTempoValQuant); + syncRecTempoValQuant->blockSignals(false); mtcSyncType->setCurrentIndex(MusEGlobal::mtcType); @@ -400,9 +359,6 @@ void MidiSyncConfig::heartBeat() { if(!lvi->_curDet) { - // Added by Tim. p3.3.6 - //printf("MidiSyncConfig::heartBeat setting current red icon\n"); - lvi->_curDet = true; lvi->_inDet = false; lvi->setIcon(DEVCOL_IN, QIcon( *record1_Icon)); @@ -411,9 +367,6 @@ void MidiSyncConfig::heartBeat() else if(!lvi->_inDet) { - // Added by Tim. p3.3.6 - //printf("MidiSyncConfig::heartBeat setting non-current green icon\n"); - lvi->_inDet = true; lvi->_curDet = false; lvi->setIcon(DEVCOL_IN, QIcon( *dotIcon)); @@ -423,9 +376,6 @@ void MidiSyncConfig::heartBeat() { if(lvi->_curDet || lvi->_inDet) { - // Added by Tim. p3.3.6 - //printf("MidiSyncConfig::heartBeat setting off icon\n"); - lvi->_curDet = false; lvi->_inDet = false; lvi->setIcon(DEVCOL_IN, QIcon( *dothIcon)); @@ -437,9 +387,6 @@ void MidiSyncConfig::heartBeat() { if(!lvi->_tickDet) { - // Added by Tim. p3.3.6 - //printf("MidiSyncConfig::heartBeat setting tick on icon\n"); - lvi->_tickDet = true; lvi->setIcon(DEVCOL_TICKIN, QIcon( *dotIcon)); } @@ -448,9 +395,6 @@ void MidiSyncConfig::heartBeat() { if(lvi->_tickDet) { - // Added by Tim. p3.3.6 - //printf("MidiSyncConfig::heartBeat setting tick off icon\n"); - lvi->_tickDet = false; lvi->setIcon(DEVCOL_TICKIN, QIcon( *dothIcon)); } @@ -461,9 +405,6 @@ void MidiSyncConfig::heartBeat() { if(!lvi->_MRTDet) { - // Added by Tim. p3.3.6 - //printf("MidiSyncConfig::heartBeat setting MRT on icon\n"); - lvi->_MRTDet = true; lvi->setIcon(DEVCOL_MRTIN, QIcon( *dotIcon)); } @@ -472,9 +413,6 @@ void MidiSyncConfig::heartBeat() { if(lvi->_MRTDet) { - // Added by Tim. p3.3.6 - //printf("MidiSyncConfig::heartBeat setting MRT off icon\n"); - lvi->_MRTDet = false; lvi->setIcon(DEVCOL_MRTIN, QIcon( *dothIcon)); } @@ -487,9 +425,6 @@ void MidiSyncConfig::heartBeat() { if(!lvi->_MMCDet) { - // Added by Tim. p3.3.6 - //printf("MidiSyncConfig::heartBeat setting MMC on icon\n"); - lvi->_MMCDet = true; lvi->setIcon(DEVCOL_MMCIN, QIcon( *dotIcon)); } @@ -521,9 +456,6 @@ void MidiSyncConfig::heartBeat() { if(lvi->_MMCDet) { - // Added by Tim. p3.3.6 - //printf("MidiSyncConfig::heartBeat setting MMC off icon\n"); - lvi->_MMCDet = false; lvi->setIcon(DEVCOL_MMCIN, QIcon( *dothIcon)); } @@ -535,9 +467,6 @@ void MidiSyncConfig::heartBeat() { if(!lvi->_curMTCDet) { - // Added by Tim. p3.3.6 - //printf("MidiSyncConfig::heartBeat setting current red icon\n"); - lvi->_curMTCDet = true; lvi->_MTCDet = false; lvi->setIcon(DEVCOL_MTCIN, QIcon( *record1_Icon)); @@ -546,9 +475,6 @@ void MidiSyncConfig::heartBeat() else if(!lvi->_MTCDet) { - // Added by Tim. p3.3.6 - //printf("MidiSyncConfig::heartBeat setting MTC on icon\n"); - lvi->_MTCDet = true; lvi->_curMTCDet = false; lvi->setIcon(DEVCOL_MTCIN, QIcon( *dotIcon)); @@ -581,9 +507,6 @@ void MidiSyncConfig::heartBeat() { if(lvi->_curMTCDet || lvi->_MTCDet) { - // Added by Tim. p3.3.6 - //printf("MidiSyncConfig::heartBeat setting MTC off icon\n"); - lvi->_MTCDet = false; lvi->_curMTCDet = false; lvi->setIcon(DEVCOL_MTCIN, QIcon( *dothIcon)); @@ -610,13 +533,6 @@ void MidiSyncConfig::heartBeat() void MidiSyncConfig::syncChanged() { setDirty(); - - //MusEGlobal::jackTransportMasterCheckbox->setEnabled(MusEGlobal::useJackTransport); - - //acceptMTCCheckbox->setEnabled(val); -// acceptMTCCheckbox->setEnabled(false); -// acceptMCCheckbox->setEnabled(val); -// acceptMMCCheckbox->setEnabled(val); } //--------------------------------------------------------- @@ -702,24 +618,14 @@ void MidiSyncConfig::closeEvent(QCloseEvent* e) void MidiSyncConfig::apply() { -// txDeviceId = dstDevId->value(); -// rxDeviceId = srcDevId->value(); -// rxSyncPort = srcSyncPort->value() - 1; -// txSyncPort = dstSyncPort->value() - 1; - -// genMTCSync = mtcSync->isChecked(); -// genMCSync = mcSync->isChecked(); -// genMMC = midiMachineControl->isChecked(); + // Protect all structures. + if(MusEGlobal::audio && MusEGlobal::audio->isRunning()) + MusEGlobal::audio->msgIdle(true); MusEGlobal::syncSendFirstClockDelay = syncDelaySpinBox->value(); MusEGlobal::mtcType = mtcSyncType->currentIndex(); - //MusEGlobal::extSyncFlag.setValue(syncMode->id(syncMode->selected())); - //MusEGlobal::extSyncFlag.blockSignals(true); MusEGlobal::extSyncFlag.setValue(extSyncCheckbox->isChecked()); -// if(MusEGlobal::extSyncFlag.value()) -// MusEGlobal::song->setMasterFlag(false); - //MusEGlobal::extSyncFlag.blockSignals(false); MusEGlobal::useJackTransport.setValue(useJackTransportCheckbox->isChecked()); // if(MusEGlobal::useJackTransport) MusEGlobal::jackTransportMaster = jackTransportMasterCheckbox->isChecked(); @@ -729,33 +635,38 @@ void MidiSyncConfig::apply() if(MusEGlobal::audioDevice) MusEGlobal::audioDevice->setMaster(MusEGlobal::jackTransportMaster); + if(syncRecFilterPreset->currentIndex() != -1) + { + int fp_idx = syncRecFilterPreset->itemData(syncRecFilterPreset->currentIndex()).toInt(); + if(fp_idx >= 0 && fp_idx < MusECore::MidiSyncInfo::TYPE_END) + { + MusEGlobal::syncRecFilterPreset = MusECore::MidiSyncInfo::SyncRecFilterPresetType(fp_idx); + if(MusEGlobal::midiSeq) + MusEGlobal::midiSeq->setSyncRecFilterPreset(MusEGlobal::syncRecFilterPreset); + } + } + MusEGlobal::syncRecTempoValQuant = syncRecTempoValQuant->value(); + if(MusEGlobal::midiSeq) + MusEGlobal::midiSeq->setRecTempoValQuant(MusEGlobal::syncRecTempoValQuant); + MusEGlobal::mtcOffset.setH(mtcOffH->value()); MusEGlobal::mtcOffset.setM(mtcOffM->value()); MusEGlobal::mtcOffset.setS(mtcOffS->value()); MusEGlobal::mtcOffset.setF(mtcOffF->value()); MusEGlobal::mtcOffset.setSf(mtcOffSf->value()); -// acceptMC = acceptMCCheckbox->isChecked(); -// acceptMMC = acceptMMCCheckbox->isChecked(); -// acceptMTC = acceptMTCCheckbox->isChecked(); - - - //MidiSyncLViewItem* lvi = (MidiSyncLViewItem*)devicesListView->firstChild(); - //while(lvi) for (int i = MIDI_PORTS-1; i >= 0; --i) { MidiSyncLViewItem* lvi = (MidiSyncLViewItem*)devicesListView->topLevelItem(i); - //MusECore::MidiDevice* dev = lvi->device(); - // Does the device really exist? - //if(midiDevices.find(dev) != midiDevices.end()) - // dev->syncInfo().copyParams(lvi->syncInfo()); int port = lvi->port(); if(port >= 0 && port < MIDI_PORTS) - //MusEGlobal::midiPorts[port].syncInfo().copyParams(lvi->syncInfo()); lvi->copyToSyncInfo(MusEGlobal::midiPorts[port].syncInfo()); } + if(MusEGlobal::audio && MusEGlobal::audio->isRunning()) + MusEGlobal::audio->msgIdle(false); + //muse->changeConfig(true); // save settings _dirty = false; @@ -777,7 +688,6 @@ void MidiSyncConfig::updateSyncInfoLV() { MusECore::MidiPort* port = &MusEGlobal::midiPorts[i]; MusECore::MidiDevice* dev = port->device(); - // p3.3.31 // Don't show if it is a synthesizer device. // Hmm, some synths might support transport commands or even sync? // If anything, the DSSI or VST synths just might... @@ -791,9 +701,6 @@ void MidiSyncConfig::updateSyncInfoLV() s.setNum(i+1); MidiSyncLViewItem* lvi = new MidiSyncLViewItem(devicesListView); lvi->setPort(i); // setPort will copy parameters. - //MusECore::MidiSyncInfo& si = lvi->syncInfo(); - //si.copyParams(port->syncInfo()); - //lvi.copyFromSyncInfo(port->syncInfo()); MusECore::MidiSyncInfo& portsi = port->syncInfo(); lvi->setText(DEVCOL_NO, s); @@ -925,11 +832,6 @@ void MidiSyncConfig::updateSyncInfoLV() //lvi->setText(DEVCOL_MTCTYPE, "--"); } - //lvi->setText(DEVCOL_RID, QString().setNum(si.idIn()) ); - //lvi->setRenameEnabled(DEVCOL_RID, true); - //lvi->setIcon(DEVCOL_RCLK, QIcon( si.MCIn() ? *dotIcon : *dothIcon)); - //lvi->setIcon(DEVCOL_RMMC, QIcon( si.MMCIn() ? *dotIcon : *dothIcon)); - //lvi->setIcon(DEVCOL_RMTC, QIcon( si.MTCIn() ? *dotIcon : *dothIcon)); lvi->setText(DEVCOL_RID, QString().setNum(lvi->_idIn) ); lvi->setIcon(DEVCOL_RCLK, QIcon( lvi->_recMC ? *dotIcon : *dothIcon)); lvi->setIcon(DEVCOL_RMRT, QIcon( lvi->_recMRT ? *dotIcon : *dothIcon)); @@ -937,11 +839,6 @@ void MidiSyncConfig::updateSyncInfoLV() lvi->setIcon(DEVCOL_RMTC, QIcon( lvi->_recMTC ? *dotIcon : *dothIcon)); lvi->setIcon(DEVCOL_RREWSTART, QIcon( lvi->_recRewOnStart ? *dotIcon : *dothIcon)); - //lvi->setText(DEVCOL_TID, QString().setNum(si.idOut()) ); - //lvi->setRenameEnabled(DEVCOL_TID, true); - //lvi->setIcon(DEVCOL_TCLK, QIcon( si.MCOut() ? *dotIcon : *dothIcon)); - //lvi->setIcon(DEVCOL_TMMC, QIcon( si.MMCOut() ? *dotIcon : *dothIcon)); - //lvi->setIcon(DEVCOL_TMTC, QIcon( si.MTCOut() ? *dotIcon : *dothIcon)); lvi->setText(DEVCOL_TID, QString().setNum(lvi->_idOut) ); lvi->setIcon(DEVCOL_TCLK, QIcon(lvi->_sendMC ? *dotIcon : *dothIcon)); lvi->setIcon(DEVCOL_TMRT, QIcon(lvi->_sendMRT ? *dotIcon : *dothIcon)); @@ -988,38 +885,6 @@ void MidiSyncConfig::updateSyncInfoLV() devicesListView->header()->setResizeMode(DEVCOL_TMRT, QHeaderView::Fixed); devicesListView->header()->setResizeMode(DEVCOL_TMMC, QHeaderView::Fixed); - - /* - for(MusECore::iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) - { - MusECore::MidiDevice* dev = *id; - - //MusECore::MidiPort* port = &MusEGlobal::midiPorts[i]; - //MusECore::MidiDevice* dev = port->device(); - MidiSyncLViewItem* lvi = new MidiSyncLViewItem(devicesListView); - //lvi->setPort(i); - // setDevice will copy parameters. - lvi->setDevice(dev); - MusECore::MidiSyncInfo& si = lvi->syncInfo(); - //si.copyParams(dev->syncInfo()); - - lvi->setText(DEVCOL_NAME, dev->name()); - - lvi->setIcon(DEVCOL_IN, QIcon( si.MCSyncDetect() ? *dotIcon : *dothIcon)); - - lvi->setText(DEVCOL_RID, QString().setNum(si.idIn()) ); - lvi->setIcon(DEVCOL_RCLK, QIcon( si.MCIn() ? *dotIcon : *dothIcon)); - lvi->setIcon(DEVCOL_RMMC, QIcon( si.MMCIn() ? *dotIcon : *dothIcon)); - lvi->setIcon(DEVCOL_RMTC, QIcon( si.MTCIn() ? *dotIcon : *dothIcon)); - - lvi->setText(DEVCOL_TID, QString().setNum(si.idOut()) ); - lvi->setIcon(DEVCOL_TCLK, QIcon( si.MCOut() ? *dotIcon : *dothIcon)); - lvi->setIcon(DEVCOL_TMMC, QIcon( si.MMCOut() ? *dotIcon : *dothIcon)); - lvi->setIcon(DEVCOL_TMTC, QIcon( si.MTCOut() ? *dotIcon : *dothIcon)); - - devicesListView->insertItem(lvi); - } - */ } @@ -1027,7 +892,6 @@ void MidiSyncConfig::updateSyncInfoLV() // dlvClicked //--------------------------------------------------------- -//void MidiSyncConfig::dlvClicked(QListViewItem* item, const QPoint&, int col) void MidiSyncConfig::dlvClicked(QTreeWidgetItem* item, int col) { if (item == 0) @@ -1042,14 +906,6 @@ void MidiSyncConfig::dlvClicked(QTreeWidgetItem* item, int col) //if(midiDevices.find(dev) == midiDevices.end()) // return; - //int n; - //MusECore::MidiPort* port = &MusEGlobal::midiPorts[no]; - //MusECore::MidiDevice* dev = port->device(); - //int rwFlags = dev ? dev->rwFlags() : 0; - //int openFlags = dev ? dev->openFlags() : 0; - //MusECore::MidiSyncInfo& si = lvi->syncInfo(); - //MusECore::MidiSyncInfo& portsi = MusEGlobal::midiPorts[no].syncInfo(); - switch (col) { case DEVCOL_NO: @@ -1060,8 +916,6 @@ void MidiSyncConfig::dlvClicked(QTreeWidgetItem* item, int col) // If this is not the current midi sync in port, and sync in from this port is enabled, // and sync is in fact detected on this port, allow the user to force this port to now be the // current sync in port. - //if(no != MusEGlobal::curMidiSyncInPort && si.MCIn() && MusEGlobal::midiPorts[no].syncInfo().MCSyncDetect()) - //if(no != MusEGlobal::curMidiSyncInPort && lvi->_recMC && MusEGlobal::midiPorts[no].syncInfo().MCSyncDetect()) if(no != MusEGlobal::curMidiSyncInPort) { if(lvi->_recMC && MusEGlobal::midiPorts[no].syncInfo().MCSyncDetect()) @@ -1084,8 +938,6 @@ void MidiSyncConfig::dlvClicked(QTreeWidgetItem* item, int col) // If this is not the current midi sync in port, and sync in from this port is enabled, // and sync is in fact detected on this port, allow the user to force this port to now be the // current sync in port. - //if(no != MusEGlobal::curMidiSyncInPort && si.MTCIn() && MusEGlobal::midiPorts[no].syncInfo().MTCDetect()) - //if(no != MusEGlobal::curMidiSyncInPort && lvi->_recMTC && MusEGlobal::midiPorts[no].syncInfo().MTCDetect()) if(no != MusEGlobal::curMidiSyncInPort) { if(lvi->_recMTC && MusEGlobal::midiPorts[no].syncInfo().MTCDetect()) @@ -1105,8 +957,6 @@ void MidiSyncConfig::dlvClicked(QTreeWidgetItem* item, int col) case DEVCOL_RID: break; case DEVCOL_RCLK: - //si.setMCIn(si.MCIn() ? false : true); - //lvi->setIcon(DEVCOL_RCLK, QIcon( si.MCIn() ? *dotIcon : *dothIcon)); lvi->_recMC = (lvi->_recMC ? false : true); lvi->setIcon(DEVCOL_RCLK, QIcon( lvi->_recMC ? *dotIcon : *dothIcon)); setDirty(); @@ -1117,15 +967,11 @@ void MidiSyncConfig::dlvClicked(QTreeWidgetItem* item, int col) setDirty(); break; case DEVCOL_RMMC: - //si.setMMCIn(si.MMCIn() ? false : true); - //lvi->setIcon(DEVCOL_RMMC, QIcon( si.MMCIn() ? *dotIcon : *dothIcon)); lvi->_recMMC = (lvi->_recMMC ? false : true); lvi->setIcon(DEVCOL_RMMC, QIcon( lvi->_recMMC ? *dotIcon : *dothIcon)); setDirty(); break; case DEVCOL_RMTC: - //si.setMTCIn(si.MTCIn() ? false : true); - //lvi->setIcon(DEVCOL_RMTC, QIcon( si.MTCIn() ? *dotIcon : *dothIcon)); lvi->_recMTC = (lvi->_recMTC ? false : true); lvi->setIcon(DEVCOL_RMTC, QIcon( lvi->_recMTC ? *dotIcon : *dothIcon)); setDirty(); @@ -1138,8 +984,6 @@ void MidiSyncConfig::dlvClicked(QTreeWidgetItem* item, int col) case DEVCOL_TID: break; case DEVCOL_TCLK: - //si.setMCOut(si.MCOut() ? false : true); - //lvi->setIcon(DEVCOL_TCLK, QIcon( si.MCOut() ? *dotIcon : *dothIcon)); lvi->_sendMC = (lvi->_sendMC ? false : true); lvi->setIcon(DEVCOL_TCLK, QIcon( lvi->_sendMC ? *dotIcon : *dothIcon)); setDirty(); @@ -1150,15 +994,11 @@ void MidiSyncConfig::dlvClicked(QTreeWidgetItem* item, int col) setDirty(); break; case DEVCOL_TMMC: - //si.setMMCOut(si.MMCOut() ? false : true); - //lvi->setIcon(DEVCOL_TMMC, QIcon( si.MMCOut() ? *dotIcon : *dothIcon)); lvi->_sendMMC = (lvi->_sendMMC ? false : true); lvi->setIcon(DEVCOL_TMMC, QIcon( lvi->_sendMMC ? *dotIcon : *dothIcon)); setDirty(); break; case DEVCOL_TMTC: - //si.setMTCOut(si.MTCOut() ? false : true); - //lvi->setIcon(DEVCOL_TMTC, QIcon( si.MTCOut() ? *dotIcon : *dothIcon)); lvi->_sendMTC = (lvi->_sendMTC ? false : true); lvi->setIcon(DEVCOL_TMTC, QIcon( lvi->_sendMTC ? *dotIcon : *dothIcon)); setDirty(); @@ -1183,21 +1023,13 @@ void MidiSyncConfig::dlvDoubleClicked(QTreeWidgetItem* item, int col) MidiSyncLViewItem* lvi = (MidiSyncLViewItem*)item; - //if(col == DEVCOL_RID) - // lvi->startRename(DEVCOL_RID); - //else - //if(col == DEVCOL_TID) - // lvi->startRename(DEVCOL_TID); - bool ok = false; if(col == DEVCOL_RID) { - //int val = lvi->syncInfo().idIn(); int val = lvi->_idIn; int newval = QInputDialog::getInteger(this, "Muse: Sync info" , "Enter new id number (127 = all):", val, 0, 127, 1, &ok); if(ok) { - //lvi->syncInfo().setIdIn(newval); lvi->_idIn = newval; lvi->setText(DEVCOL_RID, QString().setNum(newval)); } @@ -1205,12 +1037,10 @@ void MidiSyncConfig::dlvDoubleClicked(QTreeWidgetItem* item, int col) else if(col == DEVCOL_TID) { - //int val = lvi->syncInfo().idOut(); int val = lvi->_idOut; int newval = QInputDialog::getInteger(this, "Muse: Sync info" , "Enter new id number (127 = global):", val, 0, 127, 1, &ok); if(ok) { - //lvi->syncInfo().setIdOut(newval); lvi->_idOut = newval; lvi->setText(DEVCOL_TID, QString().setNum(newval)); } @@ -1220,41 +1050,6 @@ void MidiSyncConfig::dlvDoubleClicked(QTreeWidgetItem* item, int col) setDirty(); } -/* -//--------------------------------------------------------- -// renameOk -//--------------------------------------------------------- -//void MidiSyncConfig::renameOk(QListViewItem* item, int col) -void MidiSyncConfig::renameOk(QListViewItem* item, int col, const QString & text) -{ - if(!item) - return; - - MidiSyncLViewItem* lvi = (MidiSyncLViewItem*)item; - QString t = text; - bool ok; - int id = text.toInt(&ok); - if(!ok) - { - lvi->setText(t); - return; - } - if(col == DEVCOL_RID) - { - //lvi->syncInfo().setIdIn(id); - lvi->_idIn = id; - setDirty(); - } - else - if(col == DEVCOL_TID) - { - //lvi->syncInfo().setIdOut(id); - lvi->_idOut = id; - setDirty(); - } -} -*/ - //--------------------------------------------------------- // MidiSyncConfig::setDirty //--------------------------------------------------------- diff --git a/muse2/muse/widgets/musewidgetsplug.cpp b/muse2/muse/widgets/musewidgetsplug.cpp index bee05d51..9c82b5f5 100644 --- a/muse2/muse/widgets/musewidgetsplug.cpp +++ b/muse2/muse/widgets/musewidgetsplug.cpp @@ -215,7 +215,7 @@ MusEGlobal::GlobalConfigValues config = { QString("./"), // projectBaseFolder true, // projectStoreInFolder true, // useProjectSaveDialog - 64, // minControlProcessPeriod + 256, // minControlProcessPeriod false, // popupsDefaultStayOpen false, // leftMouseButtonCanDecrease false, // rangeMarkerWithoutMMB diff --git a/muse2/muse/widgets/scldraw.cpp b/muse2/muse/widgets/scldraw.cpp index 38adff25..aec769a0 100644 --- a/muse2/muse/widgets/scldraw.cpp +++ b/muse2/muse/widgets/scldraw.cpp @@ -636,7 +636,7 @@ int ScaleDraw::maxHeight(QPainter *p) const //------------------------------------------------------------ QRect ScaleDraw::maxBoundingRect(QPainter *p) const { - int i, wl,h,wmax; + int i, wl; //,wmax; int a, ar, amin, amax; double arc; @@ -645,7 +645,6 @@ QRect ScaleDraw::maxBoundingRect(QPainter *p) const QFontMetrics fm = p->fontMetrics(); wl = maxLabelWidth(p, TRUE); - h = fm.height(); switch(d_orient) { @@ -722,7 +721,7 @@ QRect ScaleDraw::maxBoundingRect(QPainter *p) const r.setBottom(MusECore::qwtInt(d_yCenter - (d_radius + double(d_majLen + d_vpad)) * cos(arc)) + fm.height() ); - wmax = d_len + d_majLen + d_hpad + wl; + //wmax = d_len + d_majLen + d_hpad + wl; DELETETHIS r.setLeft(d_xorg - d_majLen - d_hpad - wl); r.setWidth(d_len + 2*(d_majLen + d_hpad + wl)); diff --git a/muse2/muse/widgets/sliderbase.cpp b/muse2/muse/widgets/sliderbase.cpp index 15497235..5909c64d 100644 --- a/muse2/muse/widgets/sliderbase.cpp +++ b/muse2/muse/widgets/sliderbase.cpp @@ -118,6 +118,7 @@ void SliderBase::wheelEvent(QWheelEvent *e) setValue(value()-inc); emit sliderMoved(value(), _id); + emit sliderMoved(value(), _id, (bool)(e->modifiers() & Qt::ShiftModifier)); } @@ -184,6 +185,7 @@ void SliderBase::mousePressEvent(QMouseEvent *e) d_mouseOffset = 0; DoubleRange::incPages(d_direction); emit sliderMoved(value(), _id); + emit sliderMoved(value(), _id, (bool)(e->modifiers() & Qt::ShiftModifier)); d_tmrID = startTimer(MusECore::qwtMax(250, 2 * d_updTime)); break; @@ -394,6 +396,7 @@ void SliderBase::mouseMoveEvent(QMouseEvent *e) } if (value() != prevValue()) emit sliderMoved(value(), _id); + emit sliderMoved(value(), _id, (bool)(e->modifiers() & Qt::ShiftModifier)); } } @@ -444,7 +447,10 @@ void SliderBase::timerEvent(QTimerEvent*) DoubleRange::incPages(d_direction); if (value() != prevValue()) + { emit sliderMoved(value(), _id); + emit sliderMoved(value(), _id, false); + } if (!d_timerTick) { @@ -456,7 +462,10 @@ void SliderBase::timerEvent(QTimerEvent*) DoubleRange::fitValue(value() + double(d_direction) * inc); if (value() != prevValue()) + { emit sliderMoved(value(), _id); + emit sliderMoved(value(), _id, false); + } if (!d_timerTick) { @@ -620,6 +629,7 @@ void SliderBase::stepPages(int pages) { DoubleRange::incPages(pages); emit sliderMoved(value(), _id); + emit sliderMoved(value(), _id, false); } @@ -722,7 +732,7 @@ void SliderBase::stepPages(int pages) // slider with the mouse. // //.u Syntax -//.f void SliderBase::sliderMoved(double value, int _id) +//.f void SliderBase::sliderMoved(double value, int _id [, bool shift]) // //.u Parameters //.p double value -- new value diff --git a/muse2/muse/widgets/sliderbase.h b/muse2/muse/widgets/sliderbase.h index 56c7a586..abea5dd6 100644 --- a/muse2/muse/widgets/sliderbase.h +++ b/muse2/muse/widgets/sliderbase.h @@ -86,6 +86,7 @@ class SliderBase : public QWidget, public DoubleRange void sliderPressed(int id); void sliderReleased(int id); void sliderMoved(double value, int id); + void sliderMoved(double value, int id, bool shift); void sliderRightClicked(const QPoint &p, int id); public: diff --git a/muse2/share/locale/muse_cs.ts b/muse2/share/locale/muse_cs.ts index 400acd3f..844a236b 100644 --- a/muse2/share/locale/muse_cs.ts +++ b/muse2/share/locale/muse_cs.ts @@ -89,20 +89,31 @@ Název="%1" - + Bad timing Špatné načasování - + Timing source frequency is %1hz, which is below the recommended minimum: 500hz! This could lead to audible timing problems for MIDI. Please see the FAQ on http://muse-sequencer.org for remedies. -Also please check console output for any further error messages +Also please check console output for any further error messages. Kmitočet zdroje časování je %1hz, což je pod doporučeným minimem: 500hz! To by u MIDI mohlo vést až ke slyšitelným problémům s časováním. Podívejte se, prosím, na často kladené otázky na stránkách http://muse-sequencer.org na řešení. +Také se, prosím, podívejte na výstup konzole kvůli jakýmkoli dalším chybovým hláškám. + + + Timing source frequency is %1hz, which is below the recommended minimum: 500hz! +This could lead to audible timing problems for MIDI. +Please see the FAQ on http://muse-sequencer.org for remedies. +Also please check console output for any further error messages + + Kmitočet zdroje časování je %1hz, což je pod doporučeným minimem: 500hz! +To by u MIDI mohlo vést až ke slyšitelným problémům s časováním. +Podívejte se, prosím, na často kladené otázky na stránkách http://muse-sequencer.org na řešení. Také se, prosím, podívejte na výstup konzole kvůli jakýmkoli dalším chybovým hláškám @@ -114,18 +125,34 @@ Také se, prosím, podívejte na výstup konzole kvůli jakýmkoli dalším chyb O programu - Version 2 pre-alpha + Verze 2 + + + (C) Copyright 1999-2010 Werner Schweer and others. +See http://www.muse-sequencer.org for new versions and +more information. + +Published under the GNU Public License + (C) Autorské právo 1999-2010 Werner Schweer a další. +Podívejte se na stránky http://www.muse-sequencer.org na nové verze a +kvůli více informacím. + +Zveřejněno pod GNU Public License + + + + Version 2 Verze 2 - (C) Copyright 1999-2010 Werner Schweer and others. + (C) Copyright 1999-2012 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License - (C) Autorské právo 1999-2010 Werner Schweer a další. + (C) Autorské právo 1999-2012 Werner Schweer a další. Podívejte se na stránky http://www.muse-sequencer.org na nové verze a kvůli více informacím. @@ -4796,6 +4823,84 @@ zaměření na jim příslušné plátno Alt+C + + MidiAudioControlBase + + + Midi control + Ovládání MIDI + + + + Port: + Přípojka: + + + + Channel: + Kanál: + + + + Control type: + Typ ovládání: + + + + Control7 + Ovladač 7 + + + + Control14 + Ovladač 14 + + + + RPN + RPN + + + + NRPN + NRPN + + + + RPN14 + RPN 14 + + + + NRPN14 + NRPN 14 + + + + Pitch + Výška tónu + + + + Program + Program + + + + Hi: + Vysoký: + + + + Lo: + Nízký: + + + + Learn + Naučit se + + MidiFilterConfigBase @@ -6156,12 +6261,12 @@ mezi posláním "začátku" až po poslání prvních hodin. MusECore::Song - + Jack shutdown! Vypnutí JACK! - + Jack has detected a performance problem which has lead to MusE being disconnected. This could happen due to a number of reasons: @@ -6195,71 +6300,98 @@ Pro pokračování, prosím, přezkoušejte stav Jacka, zkuste Jack znovu spustit a klepněte na tlačítko "Spustit znovu". - - + + Automation: Automatizace: - + previous event Předchozí událost - + next event Další událost - - + + set event Nastavit událost - - + + add event Přidat událost - - + + erase event Vymazat událost - + erase range Vymazat rozsah - + clear automation Smazat automatizaci - + + Midi control + Ovládání MIDI + + + + Assign + Přiřadit + + + + Clear + Smazat + + + Clear all controller events? Smazat všechny události ovladače? - + &Ok &OK - + &Cancel Z&rušit - + + MusE: Tempo list + MusE: Seznam tempa + + + + External tempo changes were recorded. +Transfer them to master tempo list? + Byly nahrány vnější změny tempa. +Mají se převzít do hlavního seznamu tempa? + + + MusE - external script failed MusE: Vnější skript selhal - + MusE was unable to launch the script, error message: %1 MusE se skript nepodařilo spustit. Chybová zpráva: @@ -6985,98 +7117,98 @@ prosím, MusE znovu. Promiňte (pokoušíme se to opravit) MusEGui::AudioStrip - + panorama Vyvážení - + aux send level Úroveň poslání Aux - + Pan Vyvážení - + 1/2 channel 1/2 kanály - + Pre Před - + pre fader - post fader Předprolínač - poprolínač - + dB dB - + record Nahrávat - + mute Ztlumit - + record downmix Nahrávat smíchání - - + + solo mode Režim sóla - + off Vypnuto - + input routing Vstupní signálový tok - + output routing Výstupní signálový tok - + Off Vypnuto - + Read Číst - + Touch Dotknout se - + Write Zapsat - + automation type Typ automatizace @@ -7084,49 +7216,49 @@ prosím, MusE znovu. Promiňte (pokoušíme se to opravit) MusEGui::BigTime - + format display Zobrazení formátu - + bar Takt - + beat Doba - - + + tick Tik - + minute Minuta - + second Sekunda - - + + frame Snímek - + subframe Podsnímek - + MusE: Bigtime MusE: Velký ukazatel času @@ -8325,13 +8457,13 @@ Chcete použít na všechny existující stopy MIDI nyní? - + in Vstup - + out Výstup @@ -8363,184 +8495,184 @@ Chcete použít na všechny existující stopy MIDI nyní? Vytvořit zařízení Jack - - + + Port Number Číslo přípojky - + Enable gui Povolit rozhraní - + Enable reading Povolit čtení - + Enable writing Povolit zápis - + Port instrument Nástroj přípojky - + Midi device name. Click to edit (Jack) Název zařízení MIDI. Klepnout pro úpravu (Jack) - + Connections from Jack Midi outputs Spojení z výstupů MIDI Jack - + Connections to Jack Midi inputs Spojení do výstupů MIDI Jack - + Auto-connect these channels to new midi tracks Automaticky tyto kanály připojit do nových stop MIDI - + Auto-connect new midi tracks to these channels Automaticky připojit nové stopy MIDI do těchto kanálů - + Auto-connect new midi tracks to this channel Automaticky připojit nové stopy MIDI do tohoto kanálu - + Device state Stav zařízení - + Enable gui for device Povolit rozhraní pro zařízení - + Enable reading from device Povolit čtení ze zařízení - + Enable writing to device Povolit zápis na zařízení - + Name of the midi device associated with this port number. Click to edit Jack midi name. Název zařízení MIDI spojeného s tímto číslem přípojky. Klepněte pro změnu názvu MIDI Jack. - + Instrument connected to port Nástroj spojen s přípojkou - + Connections from Jack Midi output ports Spojení z výstupních přípojek MIDI Jack - + Connections to Jack Midi input ports Spojení k vstupním přípojkám MIDI Jack - + Auto-connect these channels, on this port, to new midi tracks. Automaticky připojit tyto kanály, na této přípojce, do nových stop MIDI. - + Connect new midi tracks to these channels, on this port. Připojit nové stopy MIDI do těchto kanálů, na této přípojce. - + Connect new midi tracks to this channel, on this port. Připojit nové stopy MIDI do tohoto kanálu, na této přípojce. - + State: result of opening the device Stav: Událost otevření zařízení - + Port Přípojka - + GUI Rozhraní - + I I - Vstup - + O O - Výstup - + Instrument Nástroj - + Device Name Název zařízení - + In routes Tok vstupního signálu - + Out routes Tok výstupního signálu - + Def in ch Výchozí vstupní kanál - + Def out ch Výchozí výstupní kanál - + State Stav - + <unknown> <neznámý> - - + + <none> <Žádný> @@ -9304,7 +9436,7 @@ Použít nastavení pro seřízení? - + Panic Nouzové zastavení @@ -9342,8 +9474,8 @@ Použít nastavení pro seřízení? - - + + &Save &Uložit @@ -9522,308 +9654,308 @@ Také můžete v nabídce Soubor vybrat příkaz Uložit. Smazat data automatizace - + Cascade Překrývat - + Tile Uspořádat jedno vedle druhého - + In rows V řádcích - + In columns Ve sloupcích - + Global Settings Celková nastavení - + Configure Shortcuts Nastavit klávesové zkratky - + Follow Song Sledovat píseň - + Don't Follow Song Nesledovat píseň - + Follow Page Sledovat píseň na stranách - + Follow Continuous Sledovat píseň stále - + Metronome Metronom - + Midi Sync Midi Sync - + Midi File Import/Export Zavedení/Vyvedení souboru MIDI - + Appearance Settings Nastavení vzhledu - + Midi Ports / Soft Synth Přípojky MIDI/Softwarové syntetizátory - + &Manual &Příručka - + &MusE Homepage Stránky &MusE - + &Report Bug... &Nahlásit chybu... - + &About MusE &O programu MusE - + Song Position Poloha písně - + Tempo Tempo - + Signature Taktové označení - + File Buttons Tlačítka pro soubor - + Undo/Redo Zpět/Znovu - + Transport Přesun - + &File &Soubor - + &View &Pohled - + &Midi &MIDI - + &Audio &Zvuk - + A&utomation A&utomatizace - + &Windows &Okna - + MusE Se&ttings Nas&tavení MusE - + &Help &Nápověda - + About &Qt O &Qt - + Cannot read template Nelze přečíst předlohu - + File open error Chyba při otevírání souboru - + File read error Chyba při čtení souboru - + Unknown File Format: %1 Neznámý formát souboru: %1 - - - + + + MusE: Song: %1 MusE: Píseň: %1 - + MusE: load project MusE: Nahrát projekt - + MusE: load template MusE: Nahrát předlohu - + MusE: Write File failed MusE: Zápis souboru se nezdařil - + The current Project contains unsaved data Save Current Project? Nynější projekt obsahuje neuložená data. Uložit nynější projekt? - - + + S&kip &Přeskočit - + &Cancel Z&rušit - + MusE: Save As MusE: Uložit jako - - + + Nothing to edit Není co upravovat - - - - - + + + + + MusE: Bounce to Track MusE: Odmíchat na stopu - + No wave tracks found Nebyly nalezeny žádné stopy Wave - - + + No audio output tracks found Nebyly nalezeny žádné zvukové výstupní stopy - + Select one audio output track, and one target wave track Vyberte jednu zvukovou výstupní stopu a jednu cílovou stopu Wave - + Select one target wave track Vyberte jednu cílovou stopu Wave - + Select one target wave track, and one audio output track Vyberte jednu cílovou stopu Wave a jednu zvukovou výstupní stopu - - + + MusE: Bounce to File MusE: Odmíchat do souboru - + Select one audio output track Vyberte jednu zvukovou výstupní stopu - + MusE: Bounce MusE: Odmíchat - + set left/right marker for bounce range Nastavit levou/pravou značku pro oblast odmíchání - + The current Project contains unsaved data Load overwrites current Project: Save Current Project? @@ -9832,11 +9964,31 @@ Nahrání přepíše nynější projekt: Uložit nynější projekt? - + &Abort &Zrušit + + + This will clear all automation data on + all audio tracks! +Proceed? + Toto smaže všechna data automatizace +u všech zvukových stop! +Pokračovat? + + + + This takes an automation snapshot of + all controllers on all audio tracks, + at the current position. +Proceed? + Toto udělá v nynější poloze snímek +automatizace všech ovladačů na všech +zvukových stopách. +Pokračovat? + MusE: Export Midi @@ -10098,8 +10250,13 @@ Files: Název části: %1 Soubory: + + + Remove selected + Odstranit vybrané + - + %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. @@ -10112,7 +10269,7 @@ Pravděpodobně má vybraná stopa nesprávný typ. - + %n part(s) could not be pasted. Likely the selected track is the wrong type. @@ -10125,32 +10282,32 @@ Pravděpodobně má vybraná stopa nesprávný typ. - + Cannot paste: multiple tracks selected Nelze vložit: vybráno více stop - + Cannot paste: no track selected Nelze vložit: nevybrána žádná stopa - + Can only paste to midi/drum track Vložení možné jen do stopy MIDI/Bicí - + Can only paste to wave track Vložení možné jen do stopy Wave - + Can only paste to midi or wave track Vložení možné jen do stopy MIDI nebo Wave - + Cannot paste: wrong data type Nelze vložit: Nesprávný datový typ @@ -10399,152 +10556,157 @@ Pravděpodobně má vybraná stopa nesprávný typ. MusEGui::PluginDialog - + MusE: select plugin MusE: Vybrat přídavný modul - + Type Typ - + Lib Lib - + Label Štítek - + Name Název - + AI Al - + AO AO - + CI Cl - + CO CO - + IP IP - + id ID - + Maker Výrobce - + Copyright Autorské právo - + Audio inputs Vstupy zvuku - + Audio outputs Výstupy zvuku - + Control inputs Vstupy ovládání - + Control outputs Výstupy ovládání - + In-place capable Schopen v místě - + ID number Číslo ID - + Ok OK - + Cancel Zrušit - + + Show plugs: + Ukázat přídavné moduly: + + + Mono and Stereo Mono a stereo - + Stereo Stereo - + Mono Mono - + Show All Ukázat vše - + Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.<br>Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack. Vyberte, které typy přídavných modulů mají být v seznamu viditelné.<br>Uvědomte si, že použití přídavných modulů monona stopy stereo není problém, dva budou použity souběžně.<br>Dejte si pozor na to, že alternativa 'vše' zahrnuje přídavné moduly, které v přihrádce s efekty nemusí být užitečné. - + Search in 'Label' and 'Name': Hledat ve 'Štítek' a 'Název': - + dssi synth syntetizátor dssi - + dssi effect efekt dssi - + ladspa ladspa @@ -10552,38 +10714,38 @@ Pravděpodobně má vybraná stopa nesprávný typ. MusEGui::PluginGui - + File Buttons Tlačítka pro soubor - + Load Preset Nahrát přednastavení - + Save Preset Uložit přednastavení - - + + bypass plugin Přeskočit přídavný modul pro tok signálu - + MusE: load preset MusE: Nahrát přednastavení - + Error reading preset. Might not be right type for this plugin Chyba při čtení přednastavení. Nemusí to být správný typ pro tento přídavný modul - + MusE: save preset MusE: Uložit přednastavení @@ -11019,7 +11181,7 @@ zvolený název není jedinečný MusEGui::Strip - + Remove track? Odstranit stopu? @@ -11027,143 +11189,168 @@ zvolený název není jedinečný MusEGui::TList - + <none> <Žádný> - + visible Viditelný - + no clef Žádný klíč - + Treble Houslový klíč - + Bass Basový klíč - + Grand Oba klíče - - + + off Vypnuto - + <unknown> <neznámý> - + MusE: bad trackname MusE: Špatný název stopy - + please choose a unique track name Vyberte, prosím, jedinečný název pro stopu - + Unused Devices Nepoužívaná zařízení - - + + Update drummap? Obnovit rozložení bicích? - + Do you want to use same port for all instruments in the drummap? Chcete pro všechny nástroje v rozložení bicích použít stejnou přípojku? - - + + &Yes &Ano - - + + &No &Ne - - + + show gui Ukázat uživatelské rozhraní - - + + show native gui Ukázat původní rozhraní - + + Midi control + Ovládání MIDI + + + + Assign + Přiřadit + + + + Clear + Smazat + + + Treble clef Houslový klíč - + Bass clef Basový klíč - + Grand Staff Oba klíče - + Viewable automation Viditelná automatizace - + + Internal + Vnitřní + + + + Synth + Syntetizátor + + + Delete Track Smazat stopu - + Track Comment Poznámka ke stopě - + Insert Track Vložit stopu - + Midi MIDI - + Drum Bicí - + Do you want to use same port and channel for all instruments in the drummap? Chcete pro všechny nástroje v rozložení bicích použít stejnou přípojku a týž kanál? @@ -12109,7 +12296,7 @@ Vytvořit jej? Vytvoření adresáře se nezdařilo - + File %1 exists. Overwrite? @@ -12118,12 +12305,12 @@ exists. Overwrite? existuje. Přepsat? - + MusE: write MusE: Zapsat - + Open File %1 failed: %2 @@ -12132,7 +12319,7 @@ failed: %2 se nepodařilo otevřít: %2 - + MusE: Open File MusE: Otevřít soubor @@ -13363,258 +13550,258 @@ Robert Jonsson Doba poklesu [ms] - - + + dB dB - + Dry Level [dB] Síla hlasitosti nezměněného signálu [dB] - + Wet Level [dB] Síla hlasitosti změněného signálu [dB] - + Preset: Přednastavení: - + AfterBurn AfterBurn - + AfterBurn (Long) AfterBurn (dlouhý) - + Ambience Atmosféra - + Ambience (Thick) Atmosféra (silná) - + Ambience (Thick) - HD Atmosféra (silná) - HD - + Cathedral Katedrála - + Cathedral - HD Katedrála - HD - + Drum Chamber Síň bicích - + Garage Garáž - + Garage (Bright) Garáž (jasná) - + Gymnasium Sportovní hala - + Gymnasium (Bright) Sportovní hala (jasná) - + Gymnasium (Bright) - HD Sportovní hala (jasná) - HD - + Hall (Small) Hala (malá) - + Hall (Medium) Hala (střední) - + Hall (Large) Hala (velká) - + Hall (Large) - HD Hala (velká) - HD - + Plate (Small) Deska (malá) - + Plate (Medium) Deska (střední) - + Plate (Large) Deska (velká) - + Plate (Large) - HD Deska (velká) - HD - + Pulse Chamber Síň rytmu - + Pulse Chamber (Reverse) Síň rytmu (obrácená) - + Resonator (96 ms) Rezonátor (96 ms) - + Resonator (152 ms) Rezonátor (152 ms) - + Resonator (208 ms) Rezonátor (208 ms) - + Room (Small) Pokoj (malý) - + Room (Medium) Pokoj (střední) - + Room (Large) Pokoj (velký) - + Room (Large) - HD Pokoj (velký) - HD - + Slap Chamber Síň plácnutí - + Slap Chamber - HD Síň plácnutí - HD - + Slap Chamber (Bright) Síň plácnutí (jasná) - + Slap Chamber (Bright) HD Síň plácnutí (jasná) HD - + Smooth Hall (Small) Klidná hala (malá) - + Smooth Hall (Medium) Klidná hala (střední) - + Smooth Hall (Large) Klidná hala (velká) - + Smooth Hall (Large) - HD Klidná hala (velká) - HD - + Vocal Plate Hlasitá deska - + Vocal Plate - HD Hlasitá deska - HD - + Warble Chamber Síň cvrlikání - + Warehoouse Skladiště - + Warehouse - HD Skladiště - HD - + Comb Filters Sdružené filtry - + Allpass Filters Všeprůchozí filtry - + Bandpass Filters Pásmové filtry - + Enhanced Stereo Rozšířené stereo diff --git a/muse2/share/locale/muse_de.ts b/muse2/share/locale/muse_de.ts index 1ade0db6..6347b66d 100644 --- a/muse2/share/locale/muse_de.ts +++ b/muse2/share/locale/muse_de.ts @@ -89,18 +89,34 @@ KanalMaske="%1" - + Bad timing - + Schlechter Taktgeber Timing source frequency is %1hz, which is below the recommended minimum: 500hz! This could lead to audible timing problems for MIDI. Please see the FAQ on http://muse-sequencer.org for remedies. +Also please check console output for any further error messages. + + Die Frequenz des Taktgebers ist %1Hz; das ist unter dem empfohlenen Minimum von 500Hz! +Dies könnte zu hörbaren Timingproblemen für MIDI führen. +Bitte sehen sie in den FAQ unter http://muse-sequencer.org nach Lösungen. +Bitte überprüfen sie außerdem die Konsolenmeldungen auf weitere Fehlermeldungen. + + + + Timing source frequency is %1hz, which is below the recommended minimum: 500hz! +This could lead to audible timing problems for MIDI. +Please see the FAQ on http://muse-sequencer.org for remedies. Also please check console output for any further error messages - + Die Frequenz des Taktgebers ist %1Hz; das ist unter dem empfohlenen Minimum von 500Hz! +Dies könnte zu hörbaren Timingproblemen für MIDI führen. +Bitte sehen sie in den FAQ unter http://muse-sequencer.org nach Lösungen. +Bitte überprüfen sie außerdem die Konsolenmeldungen auf weitere Fehlermeldungen. + @@ -111,18 +127,34 @@ Also please check console output for any further error messages Projektinformation - Version 2 pre-alpha - Version 2 pre-alpha + Version 2 pre-alpha - (C) Copyright 1999-2010 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License - (C) Copyright 1999-2010 Werner Schweer und andere. + (C) Copyright 1999-2010 Werner Schweer und andere. +Siehe http://www.muse-sequencer.org für neue Versionen und +mehr Informationen. + +Veröffentlicht unter der GNU Public License + + + + Version 2 + Version 2 + + + + (C) Copyright 1999-2012 Werner Schweer and others. +See http://www.muse-sequencer.org for new versions and +more information. + +Published under the GNU Public License + (C) Copyright 1999-2012 Werner Schweer und andere. Siehe http://www.muse-sequencer.org für neue Versionen und mehr Informationen. @@ -314,7 +346,7 @@ Veröffentlicht unter der GNU Public License May require restarting MusE for best results - + Könnte einen Neustart von MusE erfordern für optimale Ergebnisse @@ -630,7 +662,7 @@ Veröffentlicht unter der GNU Public License Samplerate - + Sample-Rate @@ -4303,33 +4335,34 @@ den Fokus zur jeweilgen Canvas zurückgeben Choose start song or template - + Wählen Sie ein Lied oder eine Vorlage für den Start Reset to default - + Auf Standardwerte zurücksetzen start with template - + mit Vorlage starten Start template or song: - + Mit Vorlage oder Lied starten: Read MIDI Ports configuration from file, or else automatically configure - + Lese MIDI-Port-Konfiguration von der Datei, +oder konfiguriere sonst automatisch Read MIDI Ports configuration - + Lese MIDI-Port-Konfiguration @@ -4812,6 +4845,84 @@ einen höheren Wert. Alt+C + + MidiAudioControlBase + + + Midi control + MIDI-Steuerung + + + + Port: + Port: + + + + Channel: + Channel: + + + + Control type: + Controller-Typ: + + + + Control7 + Controller7 + + + + Control14 + Controller14 + + + + RPN + RPN + + + + NRPN + NRPN + + + + RPN14 + RPN14 + + + + NRPN14 + NRPN14 + + + + Pitch + Pitch-Bend + + + + Program + Programm + + + + Hi: + + + + + Lo: + + + + + Learn + Lernen + + MidiFilterConfigBase @@ -5572,7 +5683,7 @@ bis zum Senden des ersten Clocks. Bank Select MSB. Ctrl-double-click on/off. - + Bank MSB. Ctrl+Doppelklick für an/aus. @@ -5587,7 +5698,7 @@ bis zum Senden des ersten Clocks. Bank Select LSB. Ctrl-double-click on/off. - + Bank LSB. Ctrl+Doppelklick für an/aus. @@ -5676,18 +5787,18 @@ bis zum Senden des ersten Clocks. Program. Ctrl-double-click on/off. - + Programm. Ctrl+Doppelklick für an/aus. Volume. Ctrl-double-click on/off. - + Lautstärke. Ctrl+Doppelklick für an/aus. Change stereo position. Ctrl-double-click on/off. - + Stereo-Position. Ctrl+Doppelklick für an/aus. @@ -6175,7 +6286,7 @@ bis zum Senden des ersten Clocks. MusECore::Song - + Jack shutdown! Jack heruntergefahren! @@ -6215,13 +6326,13 @@ versuchen, Jack neu zu starten und klicken dann auf den "Neustart"- Knopf. - - + + Automation: Automatisierung: - + previous event vorheriges Ereignis @@ -6232,34 +6343,49 @@ Knopf. - + set event setze Ereignis - - + + add event füge Ereignis hinzu - - + + erase event lösche Ereignis - + erase range - Lösche Bereich + lösche Bereich clear automation - Lösche Automatisierung + lösche Automatisierung - + + Midi control + MIDI-Steuerung + + + + Assign + Zuweisen + + + + Clear + Löschen + + + Clear all controller events? Alle Controller-Ereignisse löschen? @@ -6274,7 +6400,19 @@ Knopf. &Abbrechen - + + MusE: Tempo list + MusE: Tempo-Liste + + + + External tempo changes were recorded. +Transfer them to master tempo list? + Es wurden externe Tempoänderungen aufgezeichnet. +Sollen sie in die Master-Tempoliste übernommen werden? + + + MusE - external script failed MusE - externes Skript fehlgeschlagen @@ -7005,7 +7143,7 @@ Um Sie zu übernehmen, starten Sie MusE bitte neu. MusEGui::AudioStrip - + panorama Panorama @@ -7020,7 +7158,7 @@ Um Sie zu übernehmen, starten Sie MusE bitte neu. Pan - + 1/2 channel 1/2 Kanäle @@ -7040,7 +7178,7 @@ Um Sie zu übernehmen, starten Sie MusE bitte neu. dB - + record Aufnahme @@ -7104,7 +7242,7 @@ Um Sie zu übernehmen, starten Sie MusE bitte neu. MusEGui::BigTime - + format display Formatanzeige @@ -7217,7 +7355,7 @@ Um Sie zu übernehmen, starten Sie MusE bitte neu. ctrl-double-click on/off - + Ctrl+Doppelklick für an/aus @@ -8058,7 +8196,7 @@ click on part to mute/unmute MusE: Choose start template or song - + MusE: Lied oder eine Vorlage für den Start wählen @@ -8345,18 +8483,18 @@ Möchten Sie sie jetzt auf alle existierende MIDI-Spuren anwenden? - + in ein - - + + out aus - + Show first aliases Zeige erste Aliase @@ -8383,7 +8521,7 @@ Möchten Sie sie jetzt auf alle existierende MIDI-Spuren anwenden? Jack-Gerät erstellen - + Port Number Anschlussnummer @@ -8739,7 +8877,7 @@ Möchten Sie sie jetzt auf alle existierende MIDI-Spuren anwenden? ctrl-double-click on/off - + Ctrl+Doppelklick für an/aus @@ -9324,12 +9462,12 @@ Sync-Einstellungen anwenden? - + Panic Panik - + send note off to all midi channels Panik - "Note aus" Befehl an alle Midikanäle senden @@ -9362,13 +9500,13 @@ Sync-Einstellungen anwenden? - + &Save &Speichern - + Click this button to save the song you are editing. You will be prompted for a file name. You can also select the Save command from the File menu. @@ -9499,12 +9637,12 @@ Alternativ das Lied mit dem Befehl "Sichern" im Menü "Datei" Reset Instr. - Instr. zurücksetzen + Instrument zurücksetzen Init Instr. - Instr. initialisieren + Instrument initialisieren @@ -9542,7 +9680,7 @@ Alternativ das Lied mit dem Befehl "Sichern" im Menü "Datei" Automatisierungsdaten löschen - + Cascade Staffeln @@ -9857,6 +9995,26 @@ Aktuelles Projekt sichern? &Abort &Abbrechen + + + This will clear all automation data on + all audio tracks! +Proceed? + Dies wird alle Automatisierungsdaten +von allen Audiospuren löschen! +Fortsetzen? + + + + This takes an automation snapshot of + all controllers on all audio tracks, + at the current position. +Proceed? + Dies nimmt an der momentanen Position +einen Automatisierungs-Schnappschuss +aller Controller von allen Audiospuren auf. +Fortsetzen? + MusE: Export Midi @@ -10114,8 +10272,13 @@ Files: Part Name: %1 Dateien: + + + Remove selected + Löschen + - + %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. @@ -10368,7 +10531,7 @@ Wahrscheinlich hat die ausgewählte Spur den falschen Typ. &Pitch colors - Farbe nach &Tonhöhe + Farbe nach &Tonhöhe @@ -10409,14 +10572,14 @@ Wahrscheinlich hat die ausgewählte Spur den falschen Typ. MusEGui::PluginDialog - + MusE: select plugin MusE: PlugIn wählen Type - Typ + Typ @@ -10476,32 +10639,32 @@ Wahrscheinlich hat die ausgewählte Spur den falschen Typ. Audio inputs - + Audio-Eingänge Audio outputs - + Audio-Ausgänge Control inputs - + Steuerungs-Eingänge Control outputs - + Steuerungs-Ausgänge In-place capable - + In-Place-fähig ID number - + ID-Nummer @@ -10514,7 +10677,12 @@ Wahrscheinlich hat die ausgewählte Spur den falschen Typ. Abbrechen - + + Show plugs: + Zeige Plugins: + + + Mono and Stereo Mono und Stereo @@ -10536,22 +10704,22 @@ Wahrscheinlich hat die ausgewählte Spur den falschen Typ. Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.<br>Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack. - + Wählen Sie aus, welche Plugintypen in der Liste sichtbar sein sollen.<br>Es ist kein Problem, Mono-Plugins auf Stereospuren zu verwenden; dann werden zwei parallel verwendet.<br>Beachten Sie, dass die "Alle"-Alternative Plugings beinhaltet, die nicht in einem Effekteinschub nützlich sein könnten. dssi synth - + DSSI-Synth dssi effect - + DSSI-Effekt ladspa - + LADSPA @@ -10583,7 +10751,7 @@ Wahrscheinlich hat die ausgewählte Spur den falschen Typ. Signalfluss PlugIn überspringen - + MusE: load preset MusE: Vorlage laden @@ -11030,7 +11198,7 @@ Titel ist nicht einzigartig MusEGui::Strip - + Remove track? Spur entfernen? @@ -11038,7 +11206,7 @@ Titel ist nicht einzigartig MusEGui::TList - + <none> <kein> @@ -11090,29 +11258,29 @@ Titel ist nicht einzigartig - + Update drummap? Drumbelegung aktualisieren? - + Do you want to use same port for all instruments in the drummap? Möchten Sie für alle Instrumente der Drumbelegung den selben Anschluss verwenden? - + &Yes &Ja - - + + &No &Nein - + show gui GUI anzeigen @@ -11124,7 +11292,22 @@ Titel ist nicht einzigartig Native GUI anzeigen - + + Midi control + MIDI-Steuerung + + + + Assign + Zuweisen + + + + Clear + Löschen + + + Treble clef Violinschlüssel @@ -11143,6 +11326,16 @@ Titel ist nicht einzigartig Viewable automation Sichtbare Automatisierung + + + Internal + Intern + + + + Synth + Synth + Delete Track @@ -11174,7 +11367,7 @@ Titel ist nicht einzigartig Möchten Sie für alle Instrumente der Drumbelegung den selben Anschluss und Kanal verwenden? - + Unused Devices Ungenutzte Geräte @@ -12105,7 +12298,7 @@ Erzeugen? Verzeichnis erzeugen schlug fehl - + File %1 exists. Overwrite? @@ -13379,20 +13572,20 @@ Robert Jonsson Abschwellzeit [ms] - - + + dB dB - + Dry Level [dB] Lautstärke des unmodifizierten Signals (Dry Level) [dB] - + Wet Level [dB] Lautstärke des modifizierten diff --git a/muse2/share/locale/muse_en.ts b/muse2/share/locale/muse_en.ts index 88df5126..fbc16f29 100644 --- a/muse2/share/locale/muse_en.ts +++ b/muse2/share/locale/muse_en.ts @@ -89,16 +89,16 @@ - + Bad timing - + Timing source frequency is %1hz, which is below the recommended minimum: 500hz! This could lead to audible timing problems for MIDI. Please see the FAQ on http://muse-sequencer.org for remedies. -Also please check console output for any further error messages +Also please check console output for any further error messages. @@ -112,12 +112,12 @@ Also please check console output for any further error messages - Version 2 pre-alpha + Version 2 - (C) Copyright 1999-2010 Werner Schweer and others. + (C) Copyright 1999-2012 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. @@ -4723,6 +4723,84 @@ left button behave like the middle button in such areas. + + MidiAudioControlBase + + + Midi control + + + + + Port: + + + + + Channel: + + + + + Control type: + + + + + Control7 + + + + + Control14 + + + + + RPN + + + + + NRPN + + + + + RPN14 + + + + + NRPN14 + + + + + Pitch + + + + + Program + + + + + Hi: + + + + + Lo: + + + + + Learn + + + MidiFilterConfigBase @@ -6072,12 +6150,12 @@ Enabled inputs in the list will MusECore::Song - + Jack shutdown! - + Jack has detected a performance problem which has lead to MusE being disconnected. This could happen due to a number of reasons: @@ -6096,71 +6174,97 @@ click on the Restart button. - - + + Automation: - + previous event - + next event - - + + set event - - + + add event - - + + erase event - + erase range - + clear automation - + + Midi control + + + + + Assign + + + + + Clear + + + + Clear all controller events? - + &Ok - + &Cancel - + + MusE: Tempo list + + + + + External tempo changes were recorded. +Transfer them to master tempo list? + + + + MusE - external script failed - + MusE was unable to launch the script, error message: %1 @@ -6901,98 +7005,98 @@ To apply the changes, please restart MusE. Sorry. MusEGui::AudioStrip - + panorama - + aux send level - + Pan - + 1/2 channel - + Pre - + pre fader - post fader - + dB - + record - + mute - + record downmix - - + + solo mode - + off - + input routing - + output routing - + Off - + Read - + Touch - + Write - + automation type @@ -7000,49 +7104,49 @@ To apply the changes, please restart MusE. Sorry. MusEGui::BigTime - + format display - + bar - + beat - - + + tick - + minute - + second - - + + frame - + subframe - + MusE: Bigtime @@ -8225,13 +8329,13 @@ Do you want to apply to all existing midi tracks now? - + in - + out @@ -8263,184 +8367,184 @@ Do you want to apply to all existing midi tracks now? - - + + Port Number - + Enable gui - + Enable reading - + Enable writing - + Port instrument - + Midi device name. Click to edit (Jack) - + Connections from Jack Midi outputs - + Connections to Jack Midi inputs - + Auto-connect these channels to new midi tracks - + Auto-connect new midi tracks to these channels - + Auto-connect new midi tracks to this channel - + Device state - + Enable gui for device - + Enable reading from device - + Enable writing to device - + Name of the midi device associated with this port number. Click to edit Jack midi name. - + Instrument connected to port - + Connections from Jack Midi output ports - + Connections to Jack Midi input ports - + Auto-connect these channels, on this port, to new midi tracks. - + Connect new midi tracks to these channels, on this port. - + Connect new midi tracks to this channel, on this port. - + State: result of opening the device - + Port - + GUI - + I - + O - + Instrument - + Device Name - + In routes - + Out routes - + Def in ch - + Def out ch - + State - + <unknown> - - + + <none> @@ -9174,7 +9278,7 @@ Apply sync settings? - + Panic @@ -9212,8 +9316,8 @@ Apply sync settings? - - + + &Save @@ -9391,316 +9495,331 @@ You can also select the Save command from the File menu. - + Cascade - + Tile - + In rows - + In columns - + Global Settings - + Configure Shortcuts - + Follow Song - + Don't Follow Song - + Follow Page - + Follow Continuous - + Metronome - + Midi Sync - + Midi File Import/Export - + Appearance Settings - + Midi Ports / Soft Synth - + &Manual - + &MusE Homepage - + &Report Bug... - + &About MusE - + Song Position - + Tempo - + Signature - + File Buttons - + Undo/Redo - + Transport - + &File - + &View - + &Midi - + &Audio - + A&utomation - + &Windows - + MusE Se&ttings - + &Help - + About &Qt - + Cannot read template - + File open error - + File read error - + Unknown File Format: %1 - - - + + + MusE: Song: %1 - + MusE: load project - + MusE: load template - + MusE: Write File failed - + The current Project contains unsaved data Save Current Project? - - + + S&kip - + &Cancel - + MusE: Save As - - + + Nothing to edit - - - - - + + + + + MusE: Bounce to Track - + No wave tracks found - - + + No audio output tracks found - + Select one audio output track, and one target wave track - + Select one target wave track - + Select one target wave track, and one audio output track - - + + MusE: Bounce to File - + Select one audio output track - + MusE: Bounce - + set left/right marker for bounce range - + The current Project contains unsaved data Load overwrites current Project: Save Current Project? - + &Abort + + + This will clear all automation data on + all audio tracks! +Proceed? + + + + + This takes an automation snapshot of + all controllers on all audio tracks, + at the current position. +Proceed? + + MusE: Export Midi @@ -9953,8 +10072,13 @@ Do you still want to import it? Files: + + + Remove selected + + - + %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. @@ -9965,7 +10089,7 @@ Likely the selected track is the wrong type. - + %n part(s) could not be pasted. Likely the selected track is the wrong type. @@ -9976,32 +10100,32 @@ Likely the selected track is the wrong type. - + Cannot paste: multiple tracks selected - + Cannot paste: no track selected - + Can only paste to midi/drum track - + Can only paste to wave track - + Can only paste to midi or wave track - + Cannot paste: wrong data type @@ -10248,152 +10372,157 @@ Likely the selected track is the wrong type. MusEGui::PluginDialog - + MusE: select plugin - + Type - + Lib - + Label - + Name - + AI - + AO - + CI - + CO - + IP - + id - + Maker - + Copyright - + Audio inputs - + Audio outputs - + Control inputs - + Control outputs - + In-place capable - + ID number - + Ok - + Cancel - + + Show plugs: + + + + Mono and Stereo - + Stereo - + Mono - + Show All - + Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.<br>Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack. - + dssi synth - + dssi effect - + ladspa - + Search in 'Label' and 'Name': @@ -10401,38 +10530,38 @@ Likely the selected track is the wrong type. MusEGui::PluginGui - + File Buttons - + Load Preset - + Save Preset - - + + bypass plugin - + MusE: load preset - + Error reading preset. Might not be right type for this plugin - + MusE: save preset @@ -10867,7 +10996,7 @@ the selected title is not unique MusEGui::Strip - + Remove track? @@ -10875,143 +11004,168 @@ the selected title is not unique MusEGui::TList - + <none> - + visible - + no clef - + Treble - + Bass - + Grand - - + + off - + <unknown> - + MusE: bad trackname - + please choose a unique track name - + Unused Devices - - + + Update drummap? - + Do you want to use same port for all instruments in the drummap? - - + + &Yes - - + + &No - - + + show gui - - + + show native gui - + + Midi control + + + + + Assign + + + + + Clear + + + + Treble clef - + Bass clef - + Grand Staff - + Viewable automation - + + Internal + + + + + Synth + + + + Delete Track - + Track Comment - + Insert Track - + Midi - + Drum - + Do you want to use same port and channel for all instruments in the drummap? @@ -11965,26 +12119,26 @@ Create it? - + File %1 exists. Overwrite? - + Open File %1 failed: %2 - + MusE: write - + MusE: Open File @@ -13195,258 +13349,258 @@ Robert Jonsson - - + + dB - + Dry Level [dB] - + Wet Level [dB] - + Preset: - + AfterBurn - + AfterBurn (Long) - + Ambience - + Ambience (Thick) - + Ambience (Thick) - HD - + Cathedral - + Cathedral - HD - + Drum Chamber - + Garage - + Garage (Bright) - + Gymnasium - + Gymnasium (Bright) - + Gymnasium (Bright) - HD - + Hall (Small) - + Hall (Medium) - + Hall (Large) - + Hall (Large) - HD - + Plate (Small) - + Plate (Medium) - + Plate (Large) - + Plate (Large) - HD - + Pulse Chamber - + Pulse Chamber (Reverse) - + Resonator (96 ms) - + Resonator (152 ms) - + Resonator (208 ms) - + Room (Small) - + Room (Medium) - + Room (Large) - + Room (Large) - HD - + Slap Chamber - + Slap Chamber - HD - + Slap Chamber (Bright) - + Slap Chamber (Bright) HD - + Smooth Hall (Small) - + Smooth Hall (Medium) - + Smooth Hall (Large) - + Smooth Hall (Large) - HD - + Vocal Plate - + Vocal Plate - HD - + Warble Chamber - + Warehoouse - + Warehouse - HD - + Comb Filters - + Allpass Filters - + Bandpass Filters - + Enhanced Stereo diff --git a/muse2/share/locale/muse_es.ts b/muse2/share/locale/muse_es.ts index e0407626..197d8da6 100644 --- a/muse2/share/locale/muse_es.ts +++ b/muse2/share/locale/muse_es.ts @@ -89,7 +89,7 @@ channelMask="%1" - + Bad timing @@ -98,7 +98,7 @@ Timing source frequency is %1hz, which is below the recommended minimum: 500hz! This could lead to audible timing problems for MIDI. Please see the FAQ on http://muse-sequencer.org for remedies. -Also please check console output for any further error messages +Also please check console output for any further error messages. @@ -111,20 +111,34 @@ Also please check console output for any further error messages - Version 2 pre-alpha - MusE 2.0 pre-alfa + MusE 2.0 pre-alfa - (C) Copyright 1999-2010 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License - (C) Derechos de autor 1999-2010 Werner Schweer y otros. + (C) Derechos de autor 1999-2010 Werner Schweer y otros. Ver http://www.muse-sequencer.org por nuevas versiones y mas información. + + + + Version 2 + + + + + (C) Copyright 1999-2012 Werner Schweer and others. +See http://www.muse-sequencer.org for new versions and +more information. + +Published under the GNU Public License + (C) Derechos de autor 1999-2010 Werner Schweer y otros. +Ver http://www.muse-sequencer.org por nuevas versiones +y mas información. {1999-2012 ?} @@ -4786,6 +4800,84 @@ Ajusta la sensibilidad de los controles de audio y Alt+C + + MidiAudioControlBase + + + Midi control + + + + + Port: + + + + + Channel: + + + + + Control type: + + + + + Control7 + Control7 + + + + Control14 + Control14 + + + + RPN + RPN + + + + NRPN + NRPN + + + + RPN14 + RPN14 + + + + NRPN14 + NRPN14 + + + + Pitch + + + + + Program + Programa + + + + Hi: + + + + + Lo: + + + + + Learn + + + MidiFilterConfigBase @@ -6149,7 +6241,7 @@ Entradas habilitadas en la lista MusECore::Song - + Jack shutdown! Detener Jack @@ -6186,13 +6278,13 @@ Para comprobar el estado de Jack y reiniciar el servidor. haga clic en el botón Reiniciar. - - + + Automation: Automatización - + previous event evento anterior @@ -6203,24 +6295,24 @@ haga clic en el botón Reiniciar. - + set event - - + + add event agregar evento - - + + erase event borrar evento - + erase range borrar rango @@ -6230,7 +6322,22 @@ haga clic en el botón Reiniciar. limpiar automatizacion - + + Midi control + + + + + Assign + + + + + Clear + + + + Clear all controller events? Limpiar todos los eventos de control @@ -6245,7 +6352,18 @@ haga clic en el botón Reiniciar. &Cancelar - + + MusE: Tempo list + + + + + External tempo changes were recorded. +Transfer them to master tempo list? + + + + MusE - external script failed MusE - fallo de orden externa @@ -6967,7 +7085,7 @@ To apply the changes, please restart MusE. Sorry. MusEGui::AudioStrip - + panorama panorama @@ -6982,7 +7100,7 @@ To apply the changes, please restart MusE. Sorry. Paneo - + 1/2 channel Canales 1/2 @@ -7002,7 +7120,7 @@ To apply the changes, please restart MusE. Sorry. dB - + record grabar @@ -7066,7 +7184,7 @@ To apply the changes, please restart MusE. Sorry. MusEGui::BigTime - + format display Formato de vista @@ -8307,18 +8425,18 @@ Do you want to apply to all existing midi tracks now? - + in Entrada - - + + out Salida - + Show first aliases Mostrar primero los álias @@ -8345,7 +8463,7 @@ Do you want to apply to all existing midi tracks now? Crear conexión Jack - + Port Number Número de puerto @@ -9287,12 +9405,12 @@ Comprobar si Jack está en ejecución - + Panic Pánico - + send note off to all midi channels envia un apagado de nota (note off) a todos los canales midi @@ -9325,13 +9443,13 @@ Comprobar si Jack está en ejecución - + &Save &Guardar - + Click this button to save the song you are editing. You will be prompted for a file name. You can also select the Save command from the File menu. @@ -9505,7 +9623,7 @@ Puedes seleccionar también el comando Guardar del men de Archivo. Limpiar datos de automatización - + Cascade Cascada @@ -9820,6 +9938,21 @@ Guardar el proyecto actual? &Abort &Abortar + + + This will clear all automation data on + all audio tracks! +Proceed? + + + + + This takes an automation snapshot of + all controllers on all audio tracks, + at the current position. +Proceed? + + MusE: Export Midi @@ -10076,8 +10209,13 @@ Files: Nombre de región: %1 Archivos: + + + Remove selected + + - + %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. @@ -10369,7 +10507,7 @@ Probablemente la pista seleccionada es del tipo incorrecto. MusEGui::PluginDialog - + MusE: select plugin MusE: Selecciona el plugin @@ -10474,7 +10612,12 @@ Probablemente la pista seleccionada es del tipo incorrecto. Cancelar - + + Show plugs: + + + + Mono and Stereo Mono y estereo @@ -10543,7 +10686,7 @@ Probablemente la pista seleccionada es del tipo incorrecto. saltar plugin - + MusE: load preset MusE: Cargar plantilla @@ -10989,7 +11132,7 @@ El título seleccionado ya existe. MusEGui::Strip - + Remove track? @@ -10997,7 +11140,7 @@ El título seleccionado ya existe. MusEGui::TList - + <none> <ningúno> @@ -11054,29 +11197,29 @@ El título seleccionado ya existe. - + Update drummap? ¿Actualizár mapa de percusión? - + Do you want to use same port for all instruments in the drummap? ¿Desea utilizar el mismo puerto para todos los instrumentos en el mapa de percusión? - + &Yes Aceptar - - + + &No Cancelar - + show gui Ver interfáz @@ -11088,7 +11231,22 @@ El título seleccionado ya existe. Ver interfáz nativa - + + Midi control + + + + + Assign + + + + + Clear + + + + Treble clef Partitura de agudos @@ -11107,6 +11265,16 @@ El título seleccionado ya existe. Viewable automation Automatización visible + + + Internal + + + + + Synth + + Delete Track @@ -12068,7 +12236,7 @@ no existe. falló la creación de carpeta - + File %1 exists. Overwrite? @@ -13332,18 +13500,18 @@ Robert Jonsson Decay [ms] - - + + dB dB - + Dry Level [dB] Dry Level [dB] - + Wet Level [dB] Wet Level [dB] diff --git a/muse2/share/locale/muse_fr.ts b/muse2/share/locale/muse_fr.ts index 9eb6abbf..4c1f27bf 100644 --- a/muse2/share/locale/muse_fr.ts +++ b/muse2/share/locale/muse_fr.ts @@ -89,7 +89,7 @@ - + Bad timing @@ -98,7 +98,7 @@ Timing source frequency is %1hz, which is below the recommended minimum: 500hz! This could lead to audible timing problems for MIDI. Please see the FAQ on http://muse-sequencer.org for remedies. -Also please check console output for any further error messages +Also please check console output for any further error messages. @@ -112,12 +112,12 @@ Also please check console output for any further error messages - Version 2 pre-alpha + Version 2 - (C) Copyright 1999-2010 Werner Schweer and others. + (C) Copyright 1999-2012 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. @@ -590,7 +590,7 @@ Published under the GNU Public License off - + off @@ -603,7 +603,7 @@ Published under the GNU Public License off - + off @@ -782,7 +782,7 @@ Published under the GNU Public License &Cancel - + &Annuler @@ -2722,7 +2722,7 @@ Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t off - + off @@ -2737,7 +2737,7 @@ Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t &Cancel - + &Annuler @@ -2790,7 +2790,7 @@ Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t &Cancel - + &Annuler @@ -3231,7 +3231,7 @@ True range: Min: -8192 Max: 8191 (bias 0) off - + off @@ -3276,7 +3276,7 @@ Caution! Watch out for controllers such as off dont care - + off @@ -4728,6 +4728,84 @@ Distant + + MidiAudioControlBase + + + Midi control + + + + + Port: + + + + + Channel: + + + + + Control type: + + + + + Control7 + Control7 + + + + Control14 + Control8 + + + + RPN + RPN + + + + NRPN + NRPN + + + + RPN14 + + + + + NRPN14 + + + + + Pitch + Hauteur + + + + Program + + + + + Hi: + + + + + Lo: + + + + + Learn + + + MidiFilterConfigBase @@ -6077,7 +6155,7 @@ Enabled inputs in the list will MusECore::Song - + Jack shutdown! @@ -6101,13 +6179,13 @@ click on the Restart button. - - + + Automation: - + previous event @@ -6118,24 +6196,24 @@ click on the Restart button. - + set event - - + + add event - - + + erase event - + erase range @@ -6145,7 +6223,22 @@ click on the Restart button. - + + Midi control + + + + + Assign + + + + + Clear + + + + Clear all controller events? @@ -6157,10 +6250,21 @@ click on the Restart button. &Cancel + &Annuler + + + + MusE: Tempo list + + + + + External tempo changes were recorded. +Transfer them to master tempo list? - + MusE - external script failed @@ -6390,7 +6494,7 @@ Right-click to show GUI. Len - + Long @@ -6870,7 +6974,7 @@ To apply the changes, please restart MusE. Sorry. MusEGui::AudioStrip - + panorama @@ -6885,7 +6989,7 @@ To apply the changes, please restart MusE. Sorry. Pan - + 1/2 channel 1/2 canal @@ -6905,7 +7009,7 @@ To apply the changes, please restart MusE. Sorry. dB - + record @@ -6928,7 +7032,7 @@ To apply the changes, please restart MusE. Sorry. off - + off @@ -6969,7 +7073,7 @@ To apply the changes, please restart MusE. Sorry. MusEGui::BigTime - + format display @@ -7087,7 +7191,7 @@ To apply the changes, please restart MusE. Sorry. off - + off @@ -7339,7 +7443,7 @@ To apply the changes, please restart MusE. Sorry. Quantize - + Quantiser @@ -7439,7 +7543,7 @@ To apply the changes, please restart MusE. Sorry. Len - + Long @@ -8161,7 +8265,7 @@ polyphonique Len - + Long @@ -8208,18 +8312,18 @@ Do you want to apply to all existing midi tracks now? - + in - - + + out - + Show first aliases @@ -8246,7 +8350,7 @@ Do you want to apply to all existing midi tracks now? - + Port Number Numéro de port @@ -8596,7 +8700,7 @@ Do you want to apply to all existing midi tracks now? off - + off @@ -9150,7 +9254,7 @@ droit Record - + Enregistre @@ -9160,12 +9264,12 @@ Lecture (Play) - + Panic Panique! - + send note off to all midi channels envoyer ordre de relachement de note � tous les canaux midi @@ -9199,13 +9303,13 @@ midi - + &Save - + Click this button to save the song you are editing. You will be prompted for a file name. You can also select the Save command from the File menu. @@ -9378,7 +9482,7 @@ You can also select the Save command from the File menu. - + Cascade @@ -9605,7 +9709,7 @@ non encore sauvegardées. Enregistrer? &Cancel - + &Annuler @@ -9691,6 +9795,21 @@ avant d'en ouvrir un autre? &Abort &Annuler + + + This will clear all automation data on + all audio tracks! +Proceed? + + + + + This takes an automation snapshot of + all controllers on all audio tracks, + at the current position. +Proceed? + + MusE: Export Midi @@ -9831,7 +9950,7 @@ Do you still want to import it? Len - + Long @@ -9940,8 +10059,13 @@ différent Files: + + + Remove selected + + - + %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. @@ -10117,7 +10241,7 @@ sélectionnées Quantize - + Quantiser @@ -10228,7 +10352,7 @@ sélectionnées MusEGui::PluginDialog - + MusE: select plugin MusE: choisir plugin @@ -10333,7 +10457,12 @@ sélectionnées Annuler - + + Show plugs: + + + + Mono and Stereo @@ -10402,7 +10531,7 @@ sélectionnées Plugin de Direct (bypass) - + MusE: load preset MusE: charger réglages @@ -10847,7 +10976,7 @@ the selected title is not unique MusEGui::Strip - + Remove track? @@ -10855,7 +10984,7 @@ the selected title is not unique MusEGui::TList - + <none> @@ -10888,7 +11017,7 @@ the selected title is not unique off - + off @@ -10912,29 +11041,29 @@ the selected title is not unique - + Update drummap? - + Do you want to use same port for all instruments in the drummap? - + &Yes - - + + &No - + show gui montrer l'interface @@ -10946,7 +11075,22 @@ the selected title is not unique - + + Midi control + + + + + Assign + + + + + Clear + + + + Treble clef @@ -10965,6 +11109,16 @@ the selected title is not unique Viewable automation + + + Internal + + + + + Synth + + Delete Track @@ -10973,7 +11127,7 @@ the selected title is not unique Track Comment - + Commentaire pour la Piste @@ -10983,7 +11137,7 @@ the selected title is not unique Midi - + Midi @@ -11922,7 +12076,7 @@ Create it? échec de la création du répertoire - + File %1 exists. Overwrite? @@ -12261,7 +12415,7 @@ p, li { white-space: pre-wrap; } &Cancel - + &Annuler @@ -12493,7 +12647,7 @@ p, li { white-space: pre-wrap; } &Cancel - + &Annuler @@ -13165,18 +13319,18 @@ Robert Jonsson - - + + dB dB - + Dry Level [dB] - + Wet Level [dB] @@ -13997,7 +14151,7 @@ Robert Jonsson Quantize - + Quantiser diff --git a/muse2/share/locale/muse_pl.ts b/muse2/share/locale/muse_pl.ts index 8049f459..7b654e37 100644 --- a/muse2/share/locale/muse_pl.ts +++ b/muse2/share/locale/muse_pl.ts @@ -89,7 +89,7 @@ - + Bad timing @@ -98,7 +98,7 @@ Timing source frequency is %1hz, which is below the recommended minimum: 500hz! This could lead to audible timing problems for MIDI. Please see the FAQ on http://muse-sequencer.org for remedies. -Also please check console output for any further error messages +Also please check console output for any further error messages. @@ -112,12 +112,12 @@ Also please check console output for any further error messages - Version 2 pre-alpha + Version 2 - (C) Copyright 1999-2010 Werner Schweer and others. + (C) Copyright 1999-2012 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. @@ -519,7 +519,7 @@ Published under the GNU Public License Pitch - + Transpozycja @@ -2192,7 +2192,7 @@ Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t Pitch - + Transpozycja @@ -2539,7 +2539,7 @@ Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t Pan - + Panorama @@ -4727,6 +4727,84 @@ Adjusts responsiveness of audio controls and Alt+C + + MidiAudioControlBase + + + Midi control + + + + + Port: + + + + + Channel: + + + + + Control type: + + + + + Control7 + Kontroler7 + + + + Control14 + Kontroler14 + + + + RPN + RPN + + + + NRPN + NRPN + + + + RPN14 + + + + + NRPN14 + + + + + Pitch + Transpozycja + + + + Program + Program + + + + Hi: + + + + + Lo: + + + + + Learn + + + MidiFilterConfigBase @@ -6076,7 +6154,7 @@ Enabled inputs in the list will MusECore::Song - + Jack shutdown! @@ -6100,13 +6178,13 @@ click on the Restart button. - - + + Automation: - + previous event @@ -6117,24 +6195,24 @@ click on the Restart button. - + set event - - + + add event - - + + erase event - + erase range @@ -6144,7 +6222,22 @@ click on the Restart button. - + + Midi control + + + + + Assign + + + + + Clear + + + + Clear all controller events? @@ -6159,7 +6252,18 @@ click on the Restart button. - + + MusE: Tempo list + + + + + External tempo changes were recorded. +Transfer them to master tempo list? + + + + MusE - external script failed @@ -6389,7 +6493,7 @@ Right-click to show GUI. Len - + Długość @@ -6431,7 +6535,7 @@ Right-click to show GUI. Pitch - + Transpozycja @@ -6467,7 +6571,7 @@ Right-click to show GUI. R - + R @@ -6492,7 +6596,7 @@ Right-click to show GUI. Port - + Port @@ -6595,7 +6699,7 @@ Right-click to show GUI. Select - + Wybierz @@ -6869,7 +6973,7 @@ To apply the changes, please restart MusE. Sorry. MusEGui::AudioStrip - + panorama Panorama @@ -6881,10 +6985,10 @@ To apply the changes, please restart MusE. Sorry. Pan - + Panorama - + 1/2 channel kanał m/s @@ -6904,7 +7008,7 @@ To apply the changes, please restart MusE. Sorry. dB - + record @@ -6968,7 +7072,7 @@ To apply the changes, please restart MusE. Sorry. MusEGui::BigTime - + format display @@ -7092,7 +7196,7 @@ To apply the changes, please restart MusE. Sorry. Velocity - + Prędk. uderz. (vel) @@ -7288,7 +7392,7 @@ To apply the changes, please restart MusE. Sorry. Invert - + Odwróć @@ -7338,7 +7442,7 @@ To apply the changes, please restart MusE. Sorry. Quantize - + Kwantyzuj @@ -7438,7 +7542,7 @@ To apply the changes, please restart MusE. Sorry. Len - + Długość @@ -7453,7 +7557,7 @@ To apply the changes, please restart MusE. Sorry. Port - + Port @@ -7695,7 +7799,7 @@ Save Current Instrument? Pitch - + Transpozycja @@ -8153,7 +8257,7 @@ Kliknij na pojedynczy klocek aby go wyłączyć z odtwarzania. Len - + Długość @@ -8200,18 +8304,18 @@ Do you want to apply to all existing midi tracks now? - + in - - + + out - + Show first aliases @@ -8238,7 +8342,7 @@ Do you want to apply to all existing midi tracks now? - + Port Number Numer portu @@ -8356,7 +8460,7 @@ Do you want to apply to all existing midi tracks now? Port - + Port @@ -8639,7 +8743,7 @@ Do you want to apply to all existing midi tracks now? Pan - + Panorama @@ -8867,7 +8971,7 @@ Note: It may be impossible to rewind fast Port - + Port @@ -9149,19 +9253,19 @@ Apply sync settings? - + Panic Zatrzymaj wszystkie komunikaty midi! - + send note off to all midi channels Zatrzymaj komunikaty midi na wszystkich kanałach! &New - + &Nowy @@ -9188,13 +9292,13 @@ Apply sync settings? - + &Save &Zapisz - + Click this button to save the song you are editing. You will be prompted for a file name. You can also select the Save command from the File menu. @@ -9370,7 +9474,7 @@ Zgrywanie śladu (bounce) Wyczyść automatykę - + Cascade @@ -9683,6 +9787,21 @@ Zapisać otwarty utwór? &Abort &Anuluj + + + This will clear all automation data on + all audio tracks! +Proceed? + + + + + This takes an automation snapshot of + all controllers on all audio tracks, + at the current position. +Proceed? + + MusE: Export Midi @@ -9821,12 +9940,12 @@ Do you still want to import it? Len - + Długość Pitch - + Transpozycja @@ -9929,8 +10048,13 @@ Do you still want to import it? Files: + + + Remove selected + + - + %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. @@ -10105,7 +10229,7 @@ Likely the selected track is the wrong type. Quantize - + Kwantyzuj @@ -10216,7 +10340,7 @@ Likely the selected track is the wrong type. MusEGui::PluginDialog - + MusE: select plugin MuzA: wybierz wtyczkę @@ -10321,7 +10445,12 @@ Likely the selected track is the wrong type. Anuluj - + + Show plugs: + + + + Mono and Stereo @@ -10390,7 +10519,7 @@ Likely the selected track is the wrong type. omiń wtyczkę (bypass) - + MusE: load preset MuzA: załaduj ustawienie @@ -10835,7 +10964,7 @@ the selected title is not unique MusEGui::Strip - + Remove track? @@ -10843,7 +10972,7 @@ the selected title is not unique MusEGui::TList - + <none> @@ -10900,29 +11029,29 @@ the selected title is not unique - + Update drummap? Zaktualizować zestaw perkusyjny? - + Do you want to use same port for all instruments in the drummap? Czy chcesz używać jednego portu midi dla wszystkich instrumentów w zestawie perkusyjnym? - + &Yes &Tak - - + + &No &Nie - + show gui pokaż interfejs użytkownika @@ -10934,7 +11063,22 @@ the selected title is not unique - + + Midi control + + + + + Assign + + + + + Clear + + + + Treble clef @@ -10953,6 +11097,16 @@ the selected title is not unique Viewable automation + + + Internal + + + + + Synth + + Delete Track @@ -10961,7 +11115,7 @@ the selected title is not unique Track Comment - + Opis śladu @@ -11453,7 +11607,7 @@ the selected title is not unique Select - + Wybierz @@ -11635,7 +11789,7 @@ Missing data is muted Velocity - + Prędk. uderz. (vel) @@ -11899,7 +12053,7 @@ Create it? tworzenie katalogu nie powiodło się - + File %1 exists. Overwrite? @@ -12121,7 +12275,7 @@ If swing is -33, you get a 1:2-rhythm. Velocity - + Prędk. uderz. (vel) @@ -12260,7 +12414,7 @@ p, li { white-space: pre-wrap; } &OK - + &Akceptuj @@ -12780,7 +12934,7 @@ in current Invert - + Odwróć @@ -12826,7 +12980,7 @@ in current Pitch - + Transpozycja @@ -13152,18 +13306,18 @@ Robert Jonsson - - + + dB dB - + Dry Level [dB] - + Wet Level [dB] @@ -13984,7 +14138,7 @@ Robert Jonsson Quantize - + Kwantyzuj diff --git a/muse2/share/locale/muse_ru.ts b/muse2/share/locale/muse_ru.ts index b3421a79..19876ed8 100644 --- a/muse2/share/locale/muse_ru.ts +++ b/muse2/share/locale/muse_ru.ts @@ -79,7 +79,7 @@ Timing source frequency is %1hz, which is below the recommended minimum: 500hz! This could lead to audible timing problems for MIDI. Please see the FAQ on http://muse-sequencer.org for remedies. -Also please check console output for any further error messages +Also please check console output for any further error messages. @@ -92,7 +92,7 @@ Also please check console output for any further error messages Version 2 pre-alpha - Версия 2.0~rc2 (svn revision: 0) + Версия 2.0~rc2 (svn revision: 0) (C) Copyright 1999-2010 Werner Schweer and others. @@ -100,7 +100,7 @@ See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License - (C) Copyright 1999-2010 Werner Schweer и другие. + (C) Copyright 1999-2010 Werner Schweer и другие. На http://www.muse-sequencer.org смотрите новые версии и дополнительную информацию. @@ -114,6 +114,22 @@ Published under the GNU Public License Alt+K Alt+K + + Version 2 + + + + (C) Copyright 1999-2012 Werner Schweer and others. +See http://www.muse-sequencer.org for new versions and +more information. + +Published under the GNU Public License + (C) Copyright 1999-2010 Werner Schweer и другие. +На http://www.muse-sequencer.org смотрите новые версии и +дополнительную информацию. + +Опубликовано на условиях GNU Public License {1999-2012 ?} + AppearanceDialogBase @@ -3643,6 +3659,69 @@ Adjusts responsiveness of audio controls and Alt+C + + MidiAudioControlBase + + Midi control + + + + Port: + + + + Channel: + + + + Control type: + + + + Control7 + Контрл7 + + + Control14 + Контрл14 + + + RPN + RPN + + + NRPN + NRPN + + + RPN14 + RPN14 + + + NRPN14 + NRPN14 + + + Pitch + Высота тона + + + Program + Программа + + + Hi: + + + + Lo: + + + + Learn + + + MidiFilterConfigBase @@ -4720,6 +4799,27 @@ click on the Restart button. MusE не смог запустить скрипт, ошибка: %1 + + Midi control + + + + Assign + + + + Clear + + + + MusE: Tempo list + + + + External tempo changes were recorded. +Transfer them to master tempo list? + + MusEGui::Appearance @@ -7675,6 +7775,19 @@ Do you still want to import it? About &Qt + + This will clear all automation data on + all audio tracks! +Proceed? + + + + This takes an automation snapshot of + all controllers on all audio tracks, + at the current position. +Proceed? + + MusEGui::NoteInfo @@ -7821,6 +7934,10 @@ Likely the selected track is the wrong type. Cannot paste: wrong data type Вставка невозможна: неверный тип данных + + Remove selected + + MusEGui::PasteDialog @@ -8139,6 +8256,10 @@ Likely the selected track is the wrong type. ladspa + + Show plugs: + + MusEGui::PluginGui @@ -8622,6 +8743,26 @@ the selected title is not unique <unknown> <неизвестно> + + Midi control + + + + Assign + + + + Clear + + + + Internal + + + + Synth + + MusEGui::TempoSig diff --git a/muse2/share/locale/muse_sv_SE.ts b/muse2/share/locale/muse_sv_SE.ts index b8f1b25b..9c8808f3 100644 --- a/muse2/share/locale/muse_sv_SE.ts +++ b/muse2/share/locale/muse_sv_SE.ts @@ -73,15 +73,18 @@ Bad timing - + Dålig timing Timing source frequency is %1hz, which is below the recommended minimum: 500hz! This could lead to audible timing problems for MIDI. Please see the FAQ on http://muse-sequencer.org for remedies. -Also please check console output for any further error messages +Also please check console output for any further error messages. - + Frekvensen på tidkällan är %1hz vilket är under det rekommenderade minsta värdet: 500hz! +Det kan leda till hörbara timingproblem för MIDI. +See FAQ på http://muse-sequencer.org för lösningsförslag. +See också terminalutskrifter för eventuella fler felmeddelanden. @@ -91,24 +94,28 @@ Also please check console output for any further error messages Om - Version 2 pre-alpha - + &Keep On Rocking! + &Rocka på! - (C) Copyright 1999-2010 Werner Schweer and others. -See http://www.muse-sequencer.org for new versions and -more information. - -Published under the GNU Public License + Alt+K - &Keep On Rocking! - &Rocka på! + Version 2 + - Alt+K - + (C) Copyright 1999-2012 Werner Schweer and others. +See http://www.muse-sequencer.org for new versions and +more information. + +Published under the GNU Public License + (C) Copyright 1999-2012 Werner Schweer och andra. +Se http://www.muse-sequencer.org för nya versioner och +mer information. + +Publicerad under GNU Public License @@ -355,107 +362,107 @@ Published under the GNU Public License May require restarting MusE for best results - + Kan kräva omstart av MusE för bästa resultat ArrangerColumnsBase Configure arranger columns - + Konfigurera arrangerarkolumner Columns: - + Kolumner: Name: - Namn: + Namn: Controller type: - + Kontroltyp: Midi controller type - Midi kontrolltyp + Midi kontrolltyp Control7 - Kontroll7 + Kontroll7 Control14 - Kontroll14 + Kontroll14 RPN - + NRPN - + RPN14 - + NRPN14 - + Pitch - Tonhöjd + Tonhöjd Program - + H-Ctrl - + Midi controller number high byte - Midikontroll-nummer, hög byte + Midikontroll-nummer, hög byte L-Ctrl - + Midi controller number low byte - + Midikontrollnummer, låg byte * wild card - + affect CCs at - + påverkar CC vid begin of song - + början på sång current position - + nuvarande position &Add - &Lägg till + &Lägg till &Delete - &Ta bort + &Ta bort Done - + Färdig @@ -516,7 +523,7 @@ Published under the GNU Public License Samplerate - + Samplingshastighet @@ -1924,47 +1931,47 @@ Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t DuplicateTracksBase Duplicate tracks - + Duplicera spår Number of copies - + Antal kopior Copy all routes - + Kopiera alla kopplingar Default routing - + Förvald koppling No routes - + Inga kopplingar Copy parts - + Kopiera parter Copy standard controllers (vol, pan) - + Kopiera standardkontroller (vol, pan) Copy effects rack plugins - + Kopiera effektrack-instickseffekter Copy plugin controllers - + Kopiera instickseffekt-kontroller Ok - + Cancel - Avbryt + Avbryt @@ -2623,17 +2630,17 @@ Vanligtvis inställd på 127/127, eller genom att använda --- dont care - + ignorera * wild card - + off dont care - av + av @@ -3368,28 +3375,29 @@ fokus till respektive editor Choose start song or template - + Välj startsång eller mall Reset to default - + Återställ till förvalt start with template - + Starta med mall Start template or song: - + Startmall eller sång: Read MIDI Ports configuration from file, or else automatically configure - + Läs MIDI-port konfiguration från fil, +eller i annatfall, konfigurera automatiskt Read MIDI Ports configuration - + läs midi-portkonfiguration @@ -3643,6 +3651,69 @@ fokus till respektive editor + + MidiAudioControlBase + + Midi control + Midikontroll + + + Port: + + + + Channel: + Kanal: + + + Control type: + Kontrolltyp: + + + Control7 + Kontroll7 + + + Control14 + Kontroll14 + + + RPN + + + + NRPN + + + + RPN14 + + + + NRPN14 + + + + Pitch + Tonhöjd + + + Program + + + + Hi: + Hög: + + + Lo: + Låg: + + + Learn + Lär + + MidiControllerEditDialogBase @@ -4436,23 +4507,23 @@ program change-, pitchbend-, men inte SysEX- eller realtids-event på de valda k Bank Select MSB. Ctrl-double-click on/off. - + Bankval MSB. Ctrl-dubbelklick av/på. Bank Select LSB. Ctrl-double-click on/off. - + Bankval LSB. Ctrl-dubbelklick av/på. Program. Ctrl-double-click on/off. - + Program. Ctrl-dubbelklick av/på. Volume. Ctrl-double-click on/off. - + Volym. Ctrl-dubbelklick av/på. Change stereo position. Ctrl-double-click on/off. - + Byt stereo position. Ctrl-dubbelklick av/på. @@ -4873,6 +4944,28 @@ För att fortsätta, säkerställ att Jack är igång och klicka på Omstart kna %1 MusE kunde inte starta skript, felmeddelande: %1 + + Midi control + Midikontroll + + + Assign + Tilldela + + + Clear + Rensa + + + MusE: Tempo list + MusE: Tempolista + + + External tempo changes were recorded. +Transfer them to master tempo list? + Externa tempoändringar har spelats in. +För över dem till master-tempolistan? + MusEGui::Appearance @@ -5395,18 +5488,21 @@ Högerklick för at visa GUI. Configure &custom columns - + Konfigurera &anpassade kolumner Changed Settings - + Ändrade inställningar Unfortunately, the changed arranger column settings cannot be applied while MusE is running. To apply the changes, please restart MusE. Sorry. (we'll try to fix that) - + Tyvärr kan inte de ändrade kolumninställningarna +i arrangeraren appliceras när MusE kör. +För att aktivera starta om MusE. (Vi kommer att +försöka fixa det senare) @@ -5671,7 +5767,7 @@ To apply the changes, please restart MusE. Sorry. ctrl-double-click on/off - + kontroll dubbelklick av/på @@ -6316,7 +6412,7 @@ klicka på en part för att Tysta/Avtysta MusEGui::GlobalSettingsConfig MusE: Choose start template or song - + MusE: Välj startsång eller mall @@ -6964,7 +7060,7 @@ Vill du att gälla för alla befintliga MIDI-spår? ctrl-double-click on/off - + kontroll dubbelklick av/på @@ -7870,32 +7966,52 @@ Vill du importera den ändå? Song Position - + Sångposition Tempo - Tempo + Tempo Signature - Signatur + Signatur %n part(s) out of %1 could not be imported. Likely the selected track is the wrong type. - - + + %n parter av %1 kunde inte importeras. +Antagligen var det valda spåret av fel typ. %n part(s) could not be imported. Likely the selected track is the wrong type. - - + + %n parter kunde inte importeras. +Antagligen var det valda spåret av fel typ. + + This will clear all automation data on + all audio tracks! +Proceed? + Detta rensar all automationsdata på +alla ljudspår! +Fortsätt? + + + This takes an automation snapshot of + all controllers on all audio tracks, + at the current position. +Proceed? + Detta tar en ögonblicks bild av automationen +för alla kontrollrar på alla ljudspår vid den +nuvarande positionen. +Försätt? + MusEGui::NoteInfo @@ -7925,7 +8041,7 @@ Likely the selected track is the wrong type. delta/absolute mode - + delta/absolut-läge @@ -8027,19 +8143,25 @@ Filer: %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. - - + + %n parter av %1 kunde inte klistras in. +Antagligen var det valda spåret av fel typ. %n part(s) could not be pasted. Likely the selected track is the wrong type. - - + + %n parter kunde inte klistras in. +Antagligen var det valda spåret av fel typ. + + Remove selected + Ta bort valt + MusEGui::PasteDialog @@ -8055,8 +8177,8 @@ Likely the selected track is the wrong type. %n quarter(s) - - + + %1 kvartar @@ -8075,8 +8197,8 @@ Likely the selected track is the wrong type. %n quarter(s) - - + + %1 kvartar @@ -8328,47 +8450,51 @@ Likely the selected track is the wrong type. Type - Typ + Typ Audio inputs - + Ljudingång Audio outputs - + Ljudutgång Control inputs - + Kontrollingångar Control outputs - + Kontrollutgångar In-place capable - + Påplats-kapabel ID number - + ID-nummer Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.<br>Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack. - + Välj vilka typer av instickseffekter som skall vara synliga i listan.<br>Notera att användning av mono effekter på ett stereospår inte är något problem, två instanser kommer<br> att att användas parallellt. Var också medveten om att 'alla' alternativet inkluderar instickseffekter kanske inte går använda i ett effektrack. dssi synth - + dssi effect - + dssi-effekt ladspa - + + + + Show plugs: + Visa instickseffekter: @@ -8840,11 +8966,11 @@ Den valda titeln är inte unik MusEGui::SigToolbarWidget time signature at current position - time signature vid nuvarande position + tid-signatur vid nuvarande position Signature: - + Signatur @@ -8958,11 +9084,31 @@ Den valda titeln är inte unik off - av + av <unknown> - + <okänd> + + + Midi control + Midikontroll + + + Assign + Tilldela + + + Clear + Rensa + + + Internal + Intern + + + Synth + Synt @@ -8976,11 +9122,11 @@ Den valda titeln är inte unik MusEGui::TempoToolbarWidget tempo at current position - tempo vid nuvarande position + tempo vid nuvarande position Tempo: - + @@ -9062,27 +9208,27 @@ Den valda titeln är inte unik Undo/Redo tools - + Ångra/Gör om verktyg Panic - Panik + Panik Transport - Transport + Transport Song Position - + Sångposition Tempo - Tempo + Tempo Signature - Signatur + Signatur diff --git a/muse2/share/templates/MusE.cfg b/muse2/share/templates/MusE.cfg index 22ebbdea..550b44a8 100644 --- a/muse2/share/templates/MusE.cfg +++ b/muse2/share/templates/MusE.cfg @@ -12,7 +12,7 @@ 0 512 44100 - 64 + 256 20 1 384 diff --git a/muse2/synti/deicsonze/deicsonzegui.cpp b/muse2/synti/deicsonze/deicsonzegui.cpp index b4965e7b..52f53b86 100644 --- a/muse2/synti/deicsonze/deicsonzegui.cpp +++ b/muse2/synti/deicsonze/deicsonzegui.cpp @@ -4558,7 +4558,7 @@ void DeicsOnzeGui::updatePreset() { void DeicsOnzeGui::updateSelectPreset(int hbank, int lbank, int prog) { //QTreeWidgetItem* cat = categoryListView->currentItem(); //QTreeWidgetItem* sub = subcategoryListView->currentItem(); - QTreeWidgetItem* pre = presetListView->currentItem(); + //QTreeWidgetItem* pre = presetListView->currentItem(); //select category, subcategory, preset //category QList qlcat = @@ -4621,7 +4621,7 @@ void DeicsOnzeGui::updateSelectPreset(int hbank, int lbank, int prog) { presetListView->scrollToItem(qpre); updatePresetName(qpre->text(1), true); updateProg(prog, true); - pre=(QTreePreset*) qpre; + //pre=(QTreePreset*) qpre; setEnabledPreset(true); } else { diff --git a/muse2/synti/deicsonze/deicsonzeplugin.cpp b/muse2/synti/deicsonze/deicsonzeplugin.cpp index 442a61c5..fff03bc1 100644 --- a/muse2/synti/deicsonze/deicsonzeplugin.cpp +++ b/muse2/synti/deicsonze/deicsonzeplugin.cpp @@ -300,12 +300,12 @@ void DeicsOnzeGui::buildGuiReverb() { //for(int i = 0; i < plugI->plugin()->parameter(); i++) { for(int i = 0; i < (int)plugI->plugin()->controlInPorts(); i++) { ///double min, max, val; - float min, max, val; + float min, max; //, val; plugI->range(i, &min, &max); - val = _deicsOnze->getReverbParam(i); // FIXME FIXME Tim /* + val = _deicsOnze->getReverbParam(i); if(plugI->isBool(i)) addPluginCheckBox(i, plugI->getParameterName(i), val > 0.0, _reverbSuperWidget, grid, true); @@ -351,12 +351,12 @@ void DeicsOnzeGui::buildGuiChorus() { //for(int i = 0; i < plugI->plugin()->parameter(); i++) { for(int i = 0; i < (int)plugI->plugin()->controlInPorts(); i++) { ///double min, max, val; - float min, max, val; + float min, max; //, val; plugI->range(i, &min, &max); - val = _deicsOnze->getChorusParam(i); // FIXME FIXME Tim /* + val = _deicsOnze->getChorusParam(i); if(plugI->isBool(i)) addPluginCheckBox(i, plugI->getParameterName(i), val > 0.0, _chorusSuperWidget, grid, false); diff --git a/muse2/synti/simpledrums2/simpledrums.cpp b/muse2/synti/simpledrums2/simpledrums.cpp index 12e4fd18..4265c33c 100644 --- a/muse2/synti/simpledrums2/simpledrums.cpp +++ b/muse2/synti/simpledrums2/simpledrums.cpp @@ -664,10 +664,10 @@ const char* SimpleSynth::getPatchName(int /*index*/, int, int) const \return MidiPatch with patch info for host */ //--------------------------------------------------------- -const MidiPatch* SimpleSynth::getPatchInfo(int index, const MidiPatch* patch) const +const MidiPatch* SimpleSynth::getPatchInfo(int /*index*/, const MidiPatch* /*patch*/) const { SS_TRACE_IN - index = 0; patch = 0; + //index = 0; patch = 0; SS_TRACE_OUT return 0; } @@ -1433,7 +1433,9 @@ static void* loadSampleThread(void* p) // arg2 :sfi.frames is of type sf_count_t (== 64 bit) (long long) // this requires format %lld (twice 'l' in format string (arg1) // old code//printf("Resampling from %ld frames to %ld frames - srcration: %lf\n", sfi.frames, smp->frames, srcratio); - printf("Resampling from %lld frames to %ld frames - srcration: %lf\n", sfi.frames, smp->frames, srcratio); + //printf("Resampling from %lld frames to %ld frames - srcration: %lf\n", sfi.frames, smp->frames, srcratio); + // Changed by Tim. Just avoid the hassle for now. Need to determine 32/64 bit and provide two different printf lines. + printf("Resampling to %ld frames - srcration: %lf\n", smp->frames, srcratio); printf("Nr of new samples: %ld\n", smp->samples); } diff --git a/muse2/xpm/record_off.xpm b/muse2/xpm/record_off.xpm index 3f25c366..d4891609 100644 --- a/muse2/xpm/record_off.xpm +++ b/muse2/xpm/record_off.xpm @@ -1,150 +1,70 @@ /* XPM */ static const char * record_off_xpm[] = { -"15 15 132 2", -" c None", -". c #520002", -"+ c #420002", -"@ c #3F0002", -"# c #350002", -"$ c #3E0002", -"% c #360002", -"& c #370002", -"* c #590006", -"= c #490002", -"- c #3F0000", -"; c #450002", -"> c #470003", -", c #5A0006", -"' c #430003", -") c #3B0001", -"! c #450200", -"~ c #530006", -"{ c #470000", -"] c #520001", -"^ c #5C0200", -"/ c #760002", -"( c #980201", -"_ c #6E0006", -": c #660004", -"< c #630002", -"[ c #3B0000", -"} c #3B0200", -"| c #5A0002", -"1 c #4D0004", -"2 c #610600", -"3 c #740800", -"4 c #990200", -"5 c #B20201", -"6 c #740000", -"7 c #6E0200", -"8 c #680200", -"9 c #560200", -"0 c #390100", -"a c #460002", -"b c #400000", -"c c #590200", -"d c #830800", -"e c #A40B00", -"f c #A80400", -"g c #B00400", -"h c #960400", -"i c #6E0400", -"j c #630300", -"k c #450000", -"l c #330000", -"m c #700000", -"n c #AA0300", -"o c #A60300", -"p c #AA0200", -"q c #B40101", -"r c #AC0400", -"s c #8E0200", -"t c #750000", -"u c #590000", -"v c #4B0002", -"w c #680100", -"x c #330002", -"y c #9D0201", -"z c #A70200", -"A c #B00201", -"B c #AC0001", -"C c #B20101", -"D c #C00000", -"E c #AE0001", -"F c #8C0004", -"G c #530004", -"H c #3A0000", -"I c #540200", -"J c #400002", -"K c #560006", -"L c #670006", -"M c #7F0000", -"N c #A70400", -"O c #990300", -"P c #AF0100", -"Q c #8E0600", -"R c #920400", -"S c #9E0001", -"T c #5A0004", -"U c #430002", -"V c #340000", -"W c #510200", -"X c #6A0004", -"Y c #6C0200", -"Z c #9C0200", -"` c #B20200", -" . c #930300", -".. c #610000", -"+. c #4F0003", -"@. c #3F0003", -"#. c #390002", -"$. c #450003", -"%. c #640002", -"&. c #6A0200", -"*. c #6A0400", -"=. c #710001", -"-. c #810004", -";. c #900004", -">. c #650000", -",. c #540400", -"'. c #500200", -"). c #430000", -"!. c #3F0004", -"~. c #370001", -"{. c #350000", -"]. c #590300", -"^. c #570000", -"/. c #510004", -"(. c #5F0004", -"_. c #5D0002", -":. c #4F0200", -"<. c #3D0000", -"[. c #450004", -"}. c #4D0200", -"|. c #4C0002", -"1. c #440002", -"2. c #490003", -"3. c #380000", -"4. c #370000", -"5. c #3E0000", -"6. c #310000", -"7. c #390000", -"8. c #4A0006", -"9. c #440004", -"0. c #670100", -"a. c #5E0200", -" ", -" . + @ # $ % & ", -" * = - ; > , - ' ) ! ", -" ~ { ] ^ / ( _ : < [ } ", -" | 1 ] 2 3 4 5 6 7 8 9 - 0 ", -" a b c d e f 5 g h i j k l ", -" @ - m n o p q r s t u v [ w ", -" x > y z A B C D E F G = H I ", -" J K L M N O P Q R S T U V W ", -" # b X Y R Z ` .8 ..| +.@. ", -" #.$.%.&.*.=.-.;.>.,.'.).!. ", -" ~.{.].*.^./.(._.:.<.$ [. ", -" }.} - ).a |.1.2.{ 3.} ", -" 0 4.5.b 6.7.8.9. ", -" 0.}.a. "}; +"13 13 54 1", +" c None", +". c #790000", +"+ c #7A0000", +"@ c #780000", +"# c #770000", +"$ c #740000", +"% c #6B0000", +"& c #730000", +"* c #710000", +"= c #640000", +"- c #620000", +"; c #6D0000", +"> c #760000", +", c #6A0000", +"' c #5E0000", +") c #5A0000", +"! c #720000", +"~ c #610000", +"{ c #570000", +"] c #530000", +"^ c #5B0000", +"/ c #6F0000", +"( c #6E0000", +"_ c #680000", +": c #600000", +"< c #580000", +"[ c #4E0000", +"} c #4A0000", +"| c #510000", +"1 c #5C0000", +"2 c #560000", +"3 c #450000", +"4 c #460000", +"5 c #4C0000", +"6 c #520000", +"7 c #590000", +"8 c #550000", +"9 c #500000", +"0 c #4B0000", +"a c #440000", +"b c #3D0000", +"c c #410000", +"d c #490000", +"e c #3F0000", +"f c #380000", +"g c #360000", +"h c #3A0000", +"i c #3E0000", +"j c #3C0000", +"k c #330000", +"l c #2F0000", +"m c #310000", +"n c #300000", +"o c #2E0000", +" ..... ", +" +.@@@@@@. ", +" +.@.......# ", +" #........@$ ", +"%&.@.....@.*=", +"-;>.......$,'", +")=;$@...#!,~{", +"]^-,/!!*(_:<[", +"}|<'-===~12[3", +" 4562<7<890a ", +" bc3d}0}daef ", +" ghbieijfk ", +" lmmno "}; diff --git a/muse2/xpm/record_on.xpm b/muse2/xpm/record_on.xpm index 64a67cf4..b0408a34 100644 --- a/muse2/xpm/record_on.xpm +++ b/muse2/xpm/record_on.xpm @@ -1,160 +1,88 @@ /* XPM */ static const char * record_on_xpm[] = { -"15 15 142 2", -" c None", -". c #9B0500", -"+ c #900601", -"@ c #AF0205", -"# c #980401", -"$ c #AF0206", -"% c #A70205", -"& c #A80204", -"* c #A9040A", -"= c #990502", -"- c #A00400", -"; c #CC0004", -"> c #A30404", -", c #BD0212", -"' c #B20207", -") c #AC0201", -"! c #A30800", -"~ c #A70409", -"{ c #990500", -"] c #D50002", -"^ c #DB0400", -"/ c #E70004", -"( c #F20010", -"_ c #E30018", -": c #E00010", -"< c #C00202", -"[ c #AB0200", -"} c #AB0800", -"| c #A90401", -"1 c #A50406", -"2 c #B90201", -"3 c #DE1800", -"4 c #E62300", -"5 c #F10900", -"6 c #FF0016", -"7 c #E60000", -"8 c #E40800", -"9 c #E10900", -"0 c #BB0600", -"a c #BE0200", -"b c #B00200", -"c c #DA0400", -"d c #EB2500", -"e c #F33000", -"f c #F80D00", -"g c #FF0018", -"h c #FE0F00", -"i c #F11000", -"j c #E40F00", -"k c #DF0B00", -"l c #B30200", -"m c #A20200", -"n c #BB0304", -"o c #A00403", -"p c #E40000", -"q c #F90B00", -"r c #FF0F00", -"s c #F90006", -"t c #FB0012", -"u c #F40D00", -"v c #EE0800", -"w c #E70000", -"x c #DA0000", -"y c #D00004", -"z c #AC0500", -"A c #A40202", -"B c #B40207", -"C c #F40010", -"D c #FD0014", -"E c #FF0008", -"F c #F70009", -"G c #EE0010", -"H c #D6000F", -"I c #CF0009", -"J c #A80800", -"K c #BB0211", -"L c #E10018", -"M c #EA0000", -"N c #F10B00", -"O c #EE3000", -"P c #EF0F00", -"Q c #F5000D", -"R c #DA0010", -"S c #B10202", -"T c #A50200", -"U c #A70600", -"V c #9B0403", -"W c #970500", -"X c #E20010", -"Y c #E30800", -"Z c #EF1000", -"` c #F10800", -" . c #FF0009", -".. c #F01000", -"+. c #E10800", -"@. c #DE0000", -"#. c #DA0004", -"$. c #B80207", -"%. c #AF0207", -"&. c #EC0004", -"*. c #AB0204", -"=. c #B30207", -"-. c #E20800", -";. c #E20F00", -">. c #E50002", -",. c #EB000F", -"'. c #EF000D", -"). c #E00000", -"!. c #D70F00", -"~. c #B90500", -"{. c #B10200", -"]. c #AF020C", -"^. c #A90201", -"/. c #A70200", -"(. c #DA0B00", -"_. c #E20D00", -":. c #D90000", -"<. c #D5000F", -"[. c #DD0010", -"}. c #DC0004", -"|. c #D30400", -"1. c #AD0200", -"2. c #AE0204", -"3. c #B3020A", -"4. c #9A0600", -"5. c #AC0800", -"6. c #AF0200", -"7. c #C90000", -"8. c #CD0004", -"9. c #D10009", -"0. c #CA0004", -"a. c #B50207", -"b. c #B40200", -"c. c #9C0400", -"d. c #A90400", -"e. c #A30200", -"f. c #AA0200", -"g. c #B6020E", -"h. c #A90402", -"i. c #A50800", -"j. c #AB0600", -"k. c #B50302", -" ", -" . + @ # $ % & ", -" * = - ; > , - ' ) ! ", -" ~ { ] ^ / ( _ : < [ } ", -" | 1 2 3 4 5 6 7 8 9 0 - a ", -" > b c d e f g h i j k l m n ", -" o - p q r s t u v w x y [ z ", -" A B C 6 g D t E F G H I [ J ", -" o K L M r N E O P Q R S T U ", -" V W X Y Z ` ...+.@.#.$.%.&.", -" *.=.< -.;.>.,.'.).!.~.{.]. ", -" ^./.(._.:.<.[.}.|.1.2.3. ", -" 4.5.6.7.8.9.0.a.b.c.} ", -" d.e.6.b e.f.g.3. ", -" h.z i.j.k. "}; +"13 13 72 1", +" c None", +". c #FF0000", +"+ c #FE0000", +"@ c #FC0101", +"# c #F80202", +"$ c #EA0707", +"% c #F70303", +"& c #F30505", +"* c #E20A0A", +"= c #E00A0A", +"- c #EE0506", +"; c #FB0101", +"> c #E90707", +", c #DB0C0C", +"' c #D60E0E", +") c #E30909", +"! c #EF0505", +"~ c #F90202", +"{ c #FD0000", +"] c #F60303", +"^ c #EB0707", +"/ c #DF0A0A", +"( c #D10F0F", +"_ c #CA1111", +": c #D70E0D", +"< c #E10A0A", +"[ c #F00505", +"} c #F40404", +"| c #F60403", +"1 c #F30404", +"2 c #E70808", +"3 c #DE0B0B", +"4 c #D30E0E", +"5 c #C71313", +"6 c #C01515", +"7 c #CA1212", +"8 c #D30F0F", +"9 c #DA0D0D", +"0 c #E00B0B", +"a c #E40909", +"b c #E30A0A", +"c c #D80D0D", +"d c #D01010", +"e c #BA1717", +"f c #BB1717", +"g c #C41414", +"h c #CB1212", +"i c #D40F0F", +"j c #D30F10", +"k c #CE1010", +"l c #C91212", +"m c #C21515", +"n c #B91818", +"o c #AE1B1B", +"p c #B51A1A", +"q c #BF1616", +"r c #C31515", +"s c #C11515", +"t c #BE1616", +"u c #B81818", +"v c #B31A1A", +"w c #AA1D1D", +"x c #A51F1E", +"y c #AF1B1B", +"z c #B21A1A", +"A c #B11A1B", +"B c #A91D1D", +"C c #A31F1F", +"D c #9D2121", +"E c #A02020", +"F c #9F2121", +"G c #9C2121", +" .+++. ", +" ..++.++++ ", +" .++.......@ ", +" @+......+.# ", +"$%.+......+&*", +"=-;.......%>,", +"')!~{..+@]^/(", +"_:<$[}|1!2345", +"67890)ab3cd5e", +" fghd8ijklmn ", +" opeqmrstuvw ", +" xwyzzAoBC ", +" DEEFG "}; -- cgit v1.2.3