diff options
Diffstat (limited to 'muse2/doc/html/single/developer_docs')
-rw-r--r-- | muse2/doc/html/single/developer_docs/developer_docs.css | 34 | ||||
-rw-r--r-- | muse2/doc/html/single/developer_docs/developer_docs.html | 1745 | ||||
-rw-r--r-- | muse2/doc/html/single/developer_docs/img1.png | bin | 0 -> 853 bytes | |||
-rw-r--r-- | muse2/doc/html/single/developer_docs/img2.png | bin | 0 -> 374 bytes | |||
-rw-r--r-- | muse2/doc/html/single/developer_docs/img3.png | bin | 0 -> 476 bytes | |||
-rw-r--r-- | muse2/doc/html/single/developer_docs/index.html | 1745 |
6 files changed, 3524 insertions, 0 deletions
diff --git a/muse2/doc/html/single/developer_docs/developer_docs.css b/muse2/doc/html/single/developer_docs/developer_docs.css new file mode 100644 index 00000000..d3fede21 --- /dev/null +++ b/muse2/doc/html/single/developer_docs/developer_docs.css @@ -0,0 +1,34 @@ +/* Century Schoolbook font is very similar to Computer Modern Math: cmmi */ +.MATH { font-family: "Century Schoolbook", serif; } +.MATH I { font-family: "Century Schoolbook", serif; font-style: italic } +.BOLDMATH { font-family: "Century Schoolbook", serif; font-weight: bold } + +/* implement both fixed-size and relative sizes */ +SMALL.XTINY { font-size : xx-small } +SMALL.TINY { font-size : x-small } +SMALL.SCRIPTSIZE { font-size : smaller } +SMALL.FOOTNOTESIZE { font-size : small } +SMALL.SMALL { } +BIG.LARGE { } +BIG.XLARGE { font-size : large } +BIG.XXLARGE { font-size : x-large } +BIG.HUGE { font-size : larger } +BIG.XHUGE { font-size : xx-large } + +/* heading styles */ +H1 { } +H2 { } +H3 { } +H4 { } +H5 { } + +/* mathematics styles */ +DIV.displaymath { } /* math displays */ +TD.eqno { } /* equation-number cells */ + + +/* document-specific styles come next */ +DIV.navigation { } +SPAN.textit { font-style: italic } +SPAN.arabic { } +SPAN.textbf { font-weight: bold } diff --git a/muse2/doc/html/single/developer_docs/developer_docs.html b/muse2/doc/html/single/developer_docs/developer_docs.html new file mode 100644 index 00000000..9cf45e45 --- /dev/null +++ b/muse2/doc/html/single/developer_docs/developer_docs.html @@ -0,0 +1,1745 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> + +<!--Converted with LaTeX2HTML 2008 (1.71) +original version by: Nikos Drakos, CBLU, University of Leeds +* revised and updated by: Marcus Hennecke, Ross Moore, Herb Swan +* with significant contributions from: + Jens Lippmann, Marek Rouchal, Martin Wilck and others --> +<HTML> +<HEAD> +<TITLE>MusE Documentation</TITLE> +<META NAME="description" CONTENT="MusE Documentation"> +<META NAME="keywords" CONTENT="developer_docs"> +<META NAME="resource-type" CONTENT="document"> +<META NAME="distribution" CONTENT="global"> + +<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8"> +<META NAME="Generator" CONTENT="LaTeX2HTML v2008"> +<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css"> + +<LINK REL="STYLESHEET" HREF="developer_docs.css"> + +</HEAD> + +<BODY > +<BR> + +<H2><A NAME="SECTION00100000000000000000"> +Contents</A> +</H2> +<!--Table of Contents--> + +<UL CLASS="TofC"> +<LI><A NAME="tex2html22" + HREF="developer_docs.html#SECTION00200000000000000000">Internals - how it works</A> +<UL> +<LI><A NAME="tex2html23" + HREF="developer_docs.html#SECTION00210000000000000000">User interface programming</A> +<LI><A NAME="tex2html24" + HREF="developer_docs.html#SECTION00220000000000000000">Configuration</A> +<LI><A NAME="tex2html25" + HREF="developer_docs.html#SECTION00230000000000000000">User controls and automation</A> +<UL> +<LI><A NAME="tex2html26" + HREF="developer_docs.html#SECTION00231000000000000000">Handling user input</A> +</UL> +</UL> +<BR> +<LI><A NAME="tex2html27" + HREF="developer_docs.html#SECTION00300000000000000000">Design decisions</A> +<UL> +<LI><A NAME="tex2html28" + HREF="developer_docs.html#SECTION00310000000000000000">Automation</A> +</UL> +<BR> +<LI><A NAME="tex2html29" + HREF="developer_docs.html#SECTION00400000000000000000">Feature requests</A> +<UL> +<LI><A NAME="tex2html30" + HREF="developer_docs.html#SECTION00410000000000000000">Per-Part automation and more on automation</A> +<LI><A NAME="tex2html31" + HREF="developer_docs.html#SECTION00420000000000000000">Pre-Rendering tracks</A> +<UL> +<LI><A NAME="tex2html32" + HREF="developer_docs.html#SECTION00421000000000000000">The feature</A> +<LI><A NAME="tex2html33" + HREF="developer_docs.html#SECTION00422000000000000000">Use cases</A> +<LI><A NAME="tex2html34" + HREF="developer_docs.html#SECTION00423000000000000000">Possible scenarios</A> +<LI><A NAME="tex2html35" + HREF="developer_docs.html#SECTION00424000000000000000">Extensions</A> +</UL> +<LI><A NAME="tex2html36" + HREF="developer_docs.html#SECTION00430000000000000000">Slotted editors</A> +<LI><A NAME="tex2html37" + HREF="developer_docs.html#SECTION00440000000000000000">Controller master values</A> +<LI><A NAME="tex2html38" + HREF="developer_docs.html#SECTION00450000000000000000">Enabled-indicator while recording</A> +<LI><A NAME="tex2html39" + HREF="developer_docs.html#SECTION00460000000000000000">Linear automation editing</A> +<LI><A NAME="tex2html40" + HREF="developer_docs.html#SECTION00470000000000000000">Symbolic names for MIDI ports</A> +</UL></UL> +<!--End of Table of Contents--> +<H1><A NAME="SECTION00200000000000000000"> +Internals - how it works</A> +</H1> +This chapter explains how MusE is built internally, and is meant +to be an aid for developers wanting to quickly start up with MusE. +For details on <SPAN CLASS="textit">why</SPAN> stuff is done please refer to the following +chapter. + +<P> + +<H1><A NAME="SECTION00210000000000000000"> +User interface programming</A> +</H1> +We use the QT Toolkit for GUI- and other programming. The <SPAN CLASS="textit">QT-Assistant</SPAN> +is an important tool for getting help. Almost everything can be looked +up there. + +<P> +GUIs can be either be hardcoded (see <TT>fterfirsta`a=95 _ `a=58 : + `a>64 + `a<91 aaa + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>rranger.cpp</TT> for an example) +or can be created using <SPAN CLASS="textit">QT-Designer</SPAN> (see the dialogs under +<TT>fterfirstw`w=95 _ `w=58 : + `w>64 + `w<91 www + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>idgets/function_dialogs/</TT> for mostly cleanly-written examples). +Don't forget to add your <TT>fterfirstc`c=95 _ `c=58 : + `c>64 + `c<91 ccc + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>pp</TT>, <TT>fterfirsth`h=95 _ `h=58 : + `h>64 + `h<91 hhh + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>></TT> and <TT>fterfirstu`u=95 _ `u=58 : + `u>64 + `u<91 uuu + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>i</TT> files to the +corresponding sections in the <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>MakeLists.txt</TT>! + +<P> +Additionally, MusE offers some custom widgets, like menu title items etc. +Following, there will be a small, unordered list about custom widgets: + +<UL> +<LI><TT>fterfirstM`M=95 _ `M=58 : + `M>64 + `M<91 MMM + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>usEGui::MenuTitleItem</TT>: Provides a title-bar in a <TT>fterfirstQ`Q=95 _ `Q=58 : + `Q>64 + `Q<91 QQQ + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>Menu</TT>. +<BR> +Usage: <TT>fterfirsts`s=95 _ `s=58 : + `s>64 + `s<91 sss + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>omeMenu->addAction(new MusEGui::MenuTitleItem(tr("fnord"), someMenu));</TT> +<BR> +Defined in <TT>fterfirstw`w=95 _ `w=58 : + `w>64 + `w<91 www + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>idgets/menutitleitem.h</TT>. +</LI> +<LI><TT>fterfirstM`M=95 _ `M=58 : + `M>64 + `M<91 MMM + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>usEGui::PopupMenu</TT>: Provides a <TT>fterfirstQ`Q=95 _ `Q=58 : + `Q>64 + `Q<91 QQQ + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>Menu</TT>-like menu which + can stay open after the user checks a checkable action. +<BR> +Usage: just create a <TT>fterfirstn`n=95 _ `n=58 : + `n>64 + `n<91 nnn + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ew PopupMenu( true|false )</TT> instead of + a <TT>fterfirstn`n=95 _ `n=58 : + `n>64 + `n<91 nnn + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ew QMenu()</TT>. (<TT>fterfirstt`t=95 _ `t=58 : + `t>64 + `t<91 ttt + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>rue</TT> means 'stay open') +<BR> +Defined in <TT>fterfirstw`w=95 _ `w=58 : + `w>64 + `w<91 www + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>idgets/popupmenu.h</TT>. +</LI> +</UL> + +<P> + +<H1><A NAME="SECTION00220000000000000000"></A> <A NAME="portconfig_sucks"></A> +<BR> +Configuration +</H1> +Configuration is a bit pesky in MusE in its current state. If you get +confused by reading this chapter, that's a sign of a sane mind. + +<P> +There are three kinds of configuration items: + +<UL> +<LI>(1) Global configuration, like coloring schemes, plugin categories, MDI-ness settings +</LI> +<LI>(2) Per-Song configuration, like whether to show or hide certain track types in the arranger +</LI> +<LI>(3) Something in between, like MIDI port settings etc. They obviously actually are + global configuration issues (or ought to be), but also obviously must be stored + in the song file for portability. (This problem could possibly be solved by + the feature proposal in <A HREF="#symbolic_ports">3.7</A>. +</LI> +</UL> + +<P> + +<H4><A NAME="SECTION00220010000000000000"> +Reading configuration</A> +</H4> +<TT>fterfirstv`v=95 _ `v=58 : + `v>64 + `v<91 vvv + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>oid MusECore::readConfiguration(Xml&, bool, bool)</TT> in +<TT>fterfirstc`c=95 _ `c=58 : + `c>64 + `c<91 ccc + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>onf.cpp</TT> is the central point +of reading configuration. It is called when MusE is started first +(by <TT>fterfirstb`b=95 _ `b=58 : + `b>64 + `b<91 bbb + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ool MusECore::readConfiguration()</TT>), and also when a +song is loaded. +<BR>It can be instructed whether to read MIDI ports (3), global configuration +and MIDI ports (1+3). Per-Song configuration is always read (2). + +<P> +When adding new configuration items and thus altering <TT>fterfirstr`r=95 _ `r=58 : + `r>64 + `r<91 rrr + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>eadConfiguration()</TT>, +you must take care to place your item into the correct section. The code is +divided into the following sections: + +<UL> +<LI>Global and/or per-song configuration (3) +</LI> +<LI>Global configuration (1) +</LI> +<LI>Code for skipping obsolete entries +</LI> +</UL> + +<P> +The sections are divided by comments (they contain <TT>--</TT>, so just +search for them). Please do not just remove code for reading obsolete entries, +but always add an appropriate entry to the 'skipping' section in order to +prevent error messages when reading old configs. + +<P> + +<H4><A NAME="SECTION00220020000000000000"> +Writing configuration</A> +</H4> +Global configuration is written using the +<TT>fterfirstM`M=95 _ `M=58 : + `M>64 + `M<91 MMM + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>usEGui::MusE::writeGlobalConfiguration()</TT> functions, while +per-song-config is written by <TT>fterfirstM`M=95 _ `M=58 : + `M>64 + `M<91 MMM + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>usEGui::MusE::writeConfiguration()</TT> +(notice the missing <TT>fterfirstG`G=95 _ `G=58 : + `G>64 + `G<91 GGG + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>lobal</TT>; both implemented in <TT>fterfirstc`c=95 _ `c=58 : + `c>64 + `c<91 ccc + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>onf.cpp</TT>). + +<P> +<TT>fterfirstw`w=95 _ `w=58 : + `w>64 + `w<91 www + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>riteConfiguration</TT> is actually just a subset of the code in +<TT>fterfirstw`w=95 _ `w=58 : + `w>64 + `w<91 www + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>riteGlobalConfiguration</TT>. <SPAN CLASS="textbf">Duplicate code!</SPAN> +<P> + +<H4><A NAME="SECTION00220030000000000000"> +Song state</A> +</H4> +Additionally to per-song configuration, there is the song's state. +This contains "the song", that is all tracks, parts and note events, +together with information about the currently opened windows, their +position, size, settings and so on. Adding new items here is actually +pretty painless: Configuration is read and written using +<TT>fterfirstM`M=95 _ `M=58 : + `M>64 + `M<91 MMM + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>usECore::Song::read</TT> and <TT>fterfirst:`:=95 _ `:=58 : + `:>64 + `:<91 ::: + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>:write</TT>, both implemented in +<TT>fterfirsts`s=95 _ `s=58 : + `s>64 + `s<91 sss + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ongfile.cpp</TT>. There are no caveats. + +<P> + +<H4><A NAME="SECTION00220040000000000000"> +How to add new items</A> +</H4> +When adding global configuration items, then add them into the second +block ("global configuration") in <TT>fterfirstr`r=95 _ `r=58 : + `r>64 + `r<91 rrr + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>eadConfiguration</TT> and into +<TT>fterfirstw`w=95 _ `w=58 : + `w>64 + `w<91 www + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>riteGlobalConfiguration</TT>. + +<P> +When adding just-per-song items, better don't bother to touch the +"configuration" code and just add it to the song's state (there might +be rare exceptions). + +<P> +When adding global configuration items, make sure you add them into the +correct section of <TT>fterfirstr`r=95 _ `r=58 : + `r>64 + `r<91 rrr + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>eadConfiguration</TT>, and into <TT>fterfirstw`w=95 _ `w=58 : + `w>64 + `w<91 www + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>riteGlobalConfiguration</TT>. + +<P> + +<H1><A NAME="SECTION00230000000000000000"> +User controls and automation</A> +</H1> + +<H2><A NAME="SECTION00231000000000000000"> +Handling user input</A> +</H2> + +<H3><A NAME="SECTION00231100000000000000"> +Plugins and synthesizers</A> +</H3> + +<H4><A NAME="SECTION00231110000000000000"> +Overview</A> +</H4> +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 <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginGui</TT>, <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginIBase</TT> +(in <TT>fterfirstp`p=95 _ `p=58 : + `p>64 + `p<91 ppp + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>lugin.h</TT>) and <TT>fterfirstO`O=95 _ `O=58 : + `O>64 + `O<91 OOO + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>scIF</TT> (in <TT>fterfirsto`o=95 _ `o=58 : + `o>64 + `o<91 ooo + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>sc.h</TT>). + +<P> +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. Then MusE will update the plugin's parameter +value, and also record the new value. When appropriate, the controller +is enabled again. + +<P> + +<H4><A NAME="SECTION00231120000000000000"> +Processing the input, recording</A> +</H4> +Upon operating a slider, <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginIBase::setParam</TT> is called, +which usually writes the control change into the ringbuffer +<TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::_controlFifo</TT>. (<TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::apply()</TT>, +<TT>fterfirstD`D=95 _ `D=58 : + `D>64 + `D<91 DDD + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ssiSynthIF::getData()</TT> will read this ringbuffer and +do the processing accordingly). Furthermore, <TT>fterfirstA`A=95 _ `A=58 : + `A>64 + `A<91 AAA + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>udioTrack::recordAutomation</TT> +is called, which either directly modifies the controller lists or writes +the change into a "to be recorded"-list (<TT>fterfirstA`A=95 _ `A=58 : + `A>64 + `A<91 AAA + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>udioTrack::_recEvents</TT>) +(depending on whether the song is stopped or played). + +<P> +The <TT>fterfirstA`A=95 _ `A=58 : + `A>64 + `A<91 AAA + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>udioTrack::_recEvents</TT> list consists of <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlRecVal</TT> +items (see <TT>fterfirstc`c=95 _ `c=58 : + `c>64 + `c<91 ccc + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trl.h</TT>), which hold the following data: + +<UL> +<LI>the frame where the change occurred +</LI> +<LI>the value +</LI> +<LI>the type, which can be <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>RVT_START</TT>, <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>RVT_VAL</TT> or <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>RVT_STOP</TT>. + <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>RVT_VAL</TT> are written by every <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>udioTrack::recordAutomation</TT> + call, <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>RVT_START</TT> and <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>RVT_STOP</TT> are generated by + <TT>fterfirstA`A=95 _ `A=58 : + `A>64 + `A<91 AAA + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>udioTrack::startAutoRecord</TT> and <TT>fterfirsts`s=95 _ `s=58 : + `s>64 + `s<91 sss + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>topAutoRecord</TT>, respectively. +</LI> +<LI>and the id of the controller which is affected +</LI> +</UL> +It is processed when the song is stopped. The call path for this is: +<TT>fterfirstS`S=95 _ `S=58 : + `S>64 + `S<91 SSS + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ong::stopRolling</TT> calls <TT>fterfirstS`S=95 _ `S=58 : + `S>64 + `S<91 SSS + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ong::processAutomationEvents</TT> +calls <TT>fterfirstA`A=95 _ `A=58 : + `A>64 + `A<91 AAA + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>udioTrack::processAutomationEvents</TT>. +This function removes the old events from the track's controller list +and replaces them with the new events from <TT>fterfirst_`_=95 _ `_=58 : + `_>64 + `_<91 ___ + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>recEvents</TT>. In +<TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>UTO_WRITE</TT> mode, just all controller events within the recorded +range are erased; in <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>UTO_TOUCH</TT> mode, the <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>RVT_START</TT> +and <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>RVT_STOP</TT> types of the <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlRecVal</TT> events are used +to determine the range(s) which should be wiped. + +<P> + +<H4><A NAME="SECTION00231130000000000000"> +How it's stored</A> +</H4> +Automation data is kept in <TT>fterfirstA`A=95 _ `A=58 : + `A>64 + `A<91 AAA + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>udioTrack::_controller</TT>, which is a <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlListList</TT>, that is, a list of <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlList</TT>s, that is, a list of lists of +controller-objects which hold the control points of the automation graph. +The <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlList</TT> also stores whether the list is meant discrete +(a new control point results in a value-jump) or continuous (a new control +point results in the value slowly sloping to the new value). +Furthermore, it stores a <TT>fterfirst_`_=95 _ `_=58 : + `_>64 + `_<91 ___ + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>curVal</TT> (accessed by <TT>fterfirstc`c=95 _ `c=58 : + `c>64 + `c<91 ccc + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>urVal()</TT>), +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. + +<P> +<TT>fterfirstA`A=95 _ `A=58 : + `A>64 + `A<91 AAA + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>udioTrack::addController</TT> and <TT>fterfirstr`r=95 _ `r=58 : + `r>64 + `r<91 rrr + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>emoveController</TT> are used to add/remove whole controller types; the most important functions which +access <TT>fterfirst_`_=95 _ `_=58 : + `_>64 + `_<91 ___ + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>controller</TT> are: + +<UL> +<LI><TT>fterfirstp`p=95 _ `p=58 : + `p>64 + `p<91 ppp + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>rocessAutomationEvents</TT>, <TT>fterfirstr`r=95 _ `r=58 : + `r>64 + `r<91 rrr + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ecordAutomation</TT>, + <TT>fterfirsts`s=95 _ `s=58 : + `s>64 + `s<91 sss + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>tartAutoRecord</TT>, <TT>fterfirsts`s=95 _ `s=58 : + `s>64 + `s<91 sss + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>topAutoRecord</TT>: see above. +</LI> +<LI><TT>fterfirsts`s=95 _ `s=58 : + `s>64 + `s<91 sss + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>eekPrevACEvent</TT>, <TT>fterfirsts`s=95 _ `s=58 : + `s>64 + `s<91 sss + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>eekNextACEvent</TT>, <TT>fterfirste`e=95 _ `e=58 : + `e>64 + `e<91 eee + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>raseACEvent</TT>, + <TT>fterfirste`e=95 _ `e=58 : + `e>64 + `e<91 eee + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>raseRangeACEvents</TT>, <TT>fterfirsta`a=95 _ `a=58 : + `a>64 + `a<91 aaa + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ddACEvent</TT>, <TT>fterfirstc`c=95 _ `c=58 : + `c>64 + `c<91 ccc + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>hangeACEvent</TT>, + which do the obvious +</LI> +<LI><TT>fterfirstp`p=95 _ `p=58 : + `p>64 + `p<91 ppp + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginCtrlVal</TT>, <TT>fterfirsts`s=95 _ `s=58 : + `s>64 + `s<91 sss + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>etPluginCtrlVal</TT>: the first + returns the current value according to the <TT>fterfirst_`_=95 _ `_=58 : + `_>64 + `_<91 ___ + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>controller</TT> + list, the second only sets the <TT>fterfirstc`c=95 _ `c=58 : + `c>64 + `c<91 ccc + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>urVal</TT>, but does not + insert any events. +</LI> +</UL> + +<P> +Whenever a <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlList</TT> has been manipulated, +<TT>fterfirstM`M=95 _ `M=58 : + `M>64 + `M<91 MMM + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>usEGlobal::song->controllerChange(Track*)</TT> shall be called, +which emits the <TT>fterfirstM`M=95 _ `M=58 : + `M>64 + `M<91 MMM + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>usEGlobal::song->controllerChanged(Track*)</TT> +signal in order to inform any parts of MusE about the change (currently, +only the arranger's part canvas utilizes this). + +<P> + +<H4><A NAME="SECTION00231140000000000000"> +Enabling and disabling controllers</A> +</H4> +Disabling the controller is both dependent from the current automation +mode and from whether the GUI is native or not. +In <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>UTO_WRITE</TT> 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. + +<P> +In <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>UTO_TOUCH</TT> (and currently (r1492) <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>UTO_READ</TT>, 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, +however they only affect the recorded automation until the last toggle +of the checkbox. (Example: start the song, toggle the checkbox, toggle +it again, wait 10 seconds, stop the song. This will NOT overwrite the +last 10 seconds of automation data, but everything between the first +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 <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>UTO_WRITE</TT> mode. + +<P> +The responsible functions are: <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::oscControl</TT> and +<TT>fterfirstD`D=95 _ `D=58 : + `D>64 + `D<91 DDD + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ssiSynthIF::oscControl</TT> for handling native GUIs, +<TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::ctrlPressed</TT> and <TT>fterfirstc`c=95 _ `c=58 : + `c>64 + `c<91 ccc + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlReleased</TT> for MusE +default GUIs and <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::guiParamPressed</TT>, +<TT>fterfirstg`g=95 _ `g=58 : + `g>64 + `g<91 ggg + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>uiParamReleased</TT>, <TT>fterfirstg`g=95 _ `g=58 : + `g>64 + `g<91 ggg + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>uiSliderPressed</TT> and +<TT>fterfirstg`g=95 _ `g=58 : + `g>64 + `g<91 ggg + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>uiSliderReleased</TT> for MusE GUIs read from a UI file; +<TT>fterfirstg`g=95 _ `g=58 : + `g>64 + `g<91 ggg + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>uiSlider*</TT> obviously handle sliders, while <TT>fterfirstg`g=95 _ `g=58 : + `g>64 + `g<91 ggg + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>uiParam*</TT> +handle everything else which is not a slider. They call +<TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::enableController</TT> to enable/disable it. + +<P> +Furthermore, on every song stop or seek, <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::enableAllControllers</TT> +is called, which re-enables all controllers again. The call paths for +this are: + +<UL> +<LI>For stop: <TT>fterfirstS`S=95 _ `S=58 : + `S>64 + `S<91 SSS + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ong::stopRolling</TT> calls + <TT>fterfirstS`S=95 _ `S=58 : + `S>64 + `S<91 SSS + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ong::processAutomationEvents</TT> calls + <TT>fterfirstS`S=95 _ `S=58 : + `S>64 + `S<91 SSS + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ong::clearRecAutomation</TT> calls + <TT>fterfirstT`T=95 _ `T=58 : + `T>64 + `T<91 TTT + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>rack::clearRecAutomation</TT> calls + <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::enableAllControllers</TT> +</LI> +<LI>For seek: <TT>fterfirstA`A=95 _ `A=58 : + `A>64 + `A<91 AAA + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>udio::seek</TT> sends a message ("<TT>fterfirstG`G=95 _ `G=58 : + `G>64 + `G<91 GGG + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>></TT>") to + <TT>fterfirstS`S=95 _ `S=58 : + `S>64 + `S<91 SSS + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ong::seqSignal</TT> which calls + <TT>fterfirstS`S=95 _ `S=58 : + `S>64 + `S<91 SSS + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ong::clearRecAutomation</TT> which calls + <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::enableAllControllers</TT> +</LI> +</UL> + +<P> + +<H1><A NAME="SECTION00300000000000000000"> +Design decisions</A> +</H1> + +<H1><A NAME="SECTION00310000000000000000"> +Automation</A> +</H1> +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 <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::_controlFifo</TT>. Automation data is kept +in <TT>fterfirstA`A=95 _ `A=58 : + `A>64 + `A<91 AAA + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>udioTrack::_controller</TT>, which is a <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlListList</TT>, +that is, a list of <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlList</TT>s, that is, a list of lists of +controller-objects which hold the control points of the automation graph. +The <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlList</TT> 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). + +<P> +While <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::_controlFifo</TT> 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 <SPAN CLASS="textit">driver</SPAN> period. +This might lead to noticeable jumps in value. + +<P> +This could possibly be solved in two ways: + +<H4><A NAME="SECTION00310010000000000000"> +Maintaining a slave control list</A> +</H4> +This approach would maintain a fully redundant slave control list, +similar to <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::_controlFifo</TT>. 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 <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::_controlFifo</TT>, +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). + +<P> + +<H4><A NAME="SECTION00310020000000000000"> +Holding iterators</A> +</H4> +We also could hold a list of iterators of the single <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlList</TT>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 +song), and we must iterate through a whole list of iterators. + +<P> + +<H4><A NAME="SECTION00310030000000000000"> +Just use the current data access functions</A> +</H4> +By just using the current functions for accessing automation data, +we might get a quick-and-dirty solution, which however wastes way too +much CPU ressources. This is because on <SPAN CLASS="textit">every single frame</SPAN>, we +need to do a binary search on multiple controller lists. + +<P> + +<H1><A NAME="SECTION00400000000000000000"> +Feature requests</A> +</H1> + +<H1><A NAME="SECTION00410000000000000000"> +Per-Part automation and more on automation</A> +</H1> Automation shall be undo-able. Automation shall reside in parts which +are exchangeable, clonable etc (like the MIDI- and Wave-Parts). +Global per-synth/per-audiotrack automation shall also be available, but +this can also be implemented as special case of part automation (one +long part). + +<P> + +<H1><A NAME="SECTION00420000000000000000"> +Pre-Rendering tracks</A> +</H1> + +<H2><A NAME="SECTION00421000000000000000"> +The feature</A> +</H2> +All tracks shall be able to be "pre-renderable". Pre-rendering shall +be "layered". Pre-rendering shall act like a transparent audio cache: +Audio data is (redundantly) stored, wasting memory in order to save CPU. + +<P> +That is: Each track owns one or more wave-recordings of the length of +the song. If the user calls "pre-render" on a track, then this track +is played quasi-solo (see below), and the raw audio data is recorded +and stored in the "layer 0" wave recording. If the user has any effects +set up to be applied, then each effect is applied on a different layer +(creating layer 1, layer 2 etc). + +<P> +This means, that also MIDI and drum tracks can have effects (which +usually only operate on audio, but we HAVE audio data because of this +prerendering). + +<P> +Furthermore, MusE by default does not send MIDI events to the synthesizers +but instead just plays back the last layer of the prerecording (for +MIDI tracks), or does not pipe the audio data through the whole plugin +chain (causing cpu usage), but instead just plays back the last layer. +The hearable result shall be the same. + +<P> +Once the user changes any parameter (automation data or plugins for +wave tracks, MIDI events or effect plugin stuff for MIDI tracks), +then MusE shall generate the sound for this particular track in the +"old" way (send MIDI data to synthes, or pipe audio data through plugins). +(So that the user will not even notice that MusE actually pre-renderered +stuff.) Either MusE automatically records this while playback (if possible) +or prompts the user to accordingly set up his cabling and then record +it. Or (temporarily) disables prerecording for this track, falling back +to the plain old way of generating sound. + +<P> +<SPAN CLASS="textit">Quasi-solo</SPAN> means: For wave tracks, just solo the track. For MIDI +tracks, mute all tracks which are not on the same synth (channel?), +and mute all <SPAN CLASS="textit">note</SPAN> events which are not on the quasi-soloed track. +This causes MusE to still play any controller events from different +tracks, because they might have effects on the quasi-soloed track. (You +can have notes on channel 1 on one track and controller stuff on channel +1 on another track; then you would need quasi-solo to get proper results.) + +<P> + +<H2><A NAME="SECTION00422000000000000000"> +Use cases</A> +</H2> + +<H4><A NAME="SECTION00422010000000000000"> +Saving CPU</A> +</H4> +On slow systems, this is neccessary for songs with lots of, or demanding +(or both) soft synthes / plugins. Even if the synth or plugin is so +demanding that your system is not able to produce sound in real-time, +then with this feature you'll be able to use the synth (this will make +editing pretty laggish, because for a change you need to re-render at +least a part before you can listen to it, but better than being unable +to use the synth at all!) + +<P> + +<H4><A NAME="SECTION00422020000000000000"> +Exporting as audio project</A> +</H4> +Using pre-rendering on all tracks, you easily can export your project +as multi-track audio file (for use with Ardour or similar DAWs). +Just take the last layer of each track, and write the raw audio data +into the file, and you're done. (Maybe we are even able to write down +the raw-raw layer0 audio data plus information about used plugins and +settings etc..?) + +<P> + +<H4><A NAME="SECTION00422030000000000000"> +Mobile audio workstations</A> +</H4> +You might want to work a bit on your audio projects on your notebook +while you're not at home, not having access to your hardware synthesizers. +Using this feature, you could have pre-recorded the stuff in your studio +before, and now can at least fiddle around with the non-hw-synth-dependent +parts of your song, while still having your <SPAN CLASS="textit">full</SPAN> song with you. + +<P> + +<H4><A NAME="SECTION00422040000000000000"> +Applying effects on MIDI tracks</A> +</H4> +If you have many physical audio inputs, you might already be able to +apply effect chains on MIDI tracks, by wiring the synthes' audio +outputs to your soundcard's inputs, and applying the effects on +dedicated input tracks you have to create. This requires you to have +expensive hardware, and is pretty complicated, because you need one +additional track per MIDI synth. + +<P> +This feature allows you to apply effects on single MIDI tracks, and not +only on full MIDI synthes, and doesn't require you to be have that +many physical audio inputs (you need to manually replug your synthes, +however). + +<P> + +<H2><A NAME="SECTION00423000000000000000"> +Possible scenarios</A> +</H2> + +<H4><A NAME="SECTION00423010000000000000"> +Setting it up</A> +</H4> +Create a wave track, MusE will allow you to set or unset prerendering +for every plugin in the plugin rack (recording the actual track is +useless because it would be a plain copy). +Create a MIDI track, MusE will ask you on which physical audio input +your synth is connected. Setting up multiple synthes on one physical +audio in is allowed, see below. + +<P> + +<H4><A NAME="SECTION00423020000000000000"> +Pre-rendering stuff</A> +</H4> +When the user presses the "pre-render" button, all tracks which have +been changed since their last pre-rendering will be re-rendered. +If you have multiple hardware synthes set up as they were connected +to one physical audio input port, MusE will prompt you to first plug +the proper cable in. + +<P> + +<H4><A NAME="SECTION00423030000000000000"> +Making changes</A> +</H4> +Change a note in a MIDI part, move or delete a part or change automation +parameters. MusE will temporarily disable the pre-rendered information +and instead generate the sound via sending out MIDI events, piping stuff +through effect chains or similar. If you play back the whole song, or +if you manually trigger a re-rendering of a track via the context menu, +MusE will play back the stuff, record it again and re-enable the +pre-rendered information. + +<P> + +<H2><A NAME="SECTION00424000000000000000"> +Extensions</A> +</H2> + +<H4><A NAME="SECTION00424010000000000000"> +Automatic discovery of physical audio connections</A> +</H4> +The user plugs all (or only some) synthes' audio outs into the available +audio inputs, then runs automatic discovery. This will send MIDI events +to each synthesizer, and look on which audio in there's activity. Then +it will assume that the synthesizer is connected to that particular +audio in. Audio ins which show activity before any MIDI events were +sent are not considered, as they're probably connected to microphones +or other noise-generating non-synthes. + +<P> + +<H4><A NAME="SECTION00424020000000000000"> +Audio export</A> +</H4> +As described in the Use cases, MusE can allow you to export your song +in some multitrack audio format. + +<P> + +<H4><A NAME="SECTION00424030000000000000"> +Cheap/Faked changes</A> +</H4> +For expensive or unavailable synthes, changing the Volume midi controller, +the Pan controller or similar "easy" controllers will not trigger a +complete re-rendering, but instead "fake" the change, by changing +the volume data directly on the recorded wave. This might require some +learning and might even get pretty complicated. + +<P> + +<H4><A NAME="SECTION00424040000000000000"> +Intelligent re-recording</A> +</H4> +For tiny changes, MusE shall only re-render the relevant part. If you +change some MIDI notes, then begin re-recording shortly before the +changes, and end re-recording as soon as the recorded stuff doesn't +differ to much from the stuff coming from the synth. Then properly +blend the old recording with the updated part. + +<P> + +<H1><A NAME="SECTION00430000000000000000"> +Slotted editors</A> +</H1> +Currently, MusE has the pianoroll editor, drum editor, score editor, +then the controller editor which is inside the pianoroll/drum editor. +All these editors have a very similar concept: the "time axis" is +vertical and (almost) linear, they handle parts, and events are +manipulated similarly. + +<P> +A unified editor shall be created which allows you to combine different +kinds of editors in one window, properly aligned against each other. +These "different kinds of editors" shall be handled as "slots"; one +unified editor window consists of: + +<UL> +<LI>A menu bar, containing stuff suitable for the complete window, + which might include window name, MDI-ness etc. +</LI> +<LI>A toolbar which contains controls suitable for every single slot. +</LI> +<LI>A container with one or more slots; the slots can be scrolled in + y-direction if there are multipe slots. +</LI> +<LI>A time-scrollbar with zoom +</LI> +</UL> + +<P> +Each slot contains the following: + +<UL> +<LI>A menu button, button box or control panel for setting up this + particular slot. This could contain "note head colors", "show + a transposing instrument" etc for score edit slots, "event + rectangle color", "grid size" and "snap to grid" for pianoroll/ + drum editors. +</LI> +<LI>The actual canvas +</LI> +<LI>A y-direction scroll bar, possibly with zoom control (for + pianoroll editor) +</LI> +</UL> + +<P> +The main window does not show its scroll bar if there is only one slot, +because the slot's scrollbar is sufficient then. + +<P> +Slots can be added, destroyed, moved around, maybe even merged (if the +slot types allow it); basically, you can compare them with the staves +in the score editor. + +<P> +The slots shall align against each other, that is, if a score editor +slot displays a key change with lots of accidentials, then all other +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. + +<P> + +<H1><A NAME="SECTION00440000000000000000"> +Controller master values</A> +</H1> +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, <!-- MATH + $\textrm{value} * \textrm{bias} ++ textrm{bias}$ + --> +<SPAN CLASS="MATH"><IMG + WIDTH="180" HEIGHT="30" ALIGN="MIDDLE" BORDER="0" + SRC="img1.png" + ALT="$\textrm{value} * \textrm{bias} ++ textrm{bias}$"></SPAN> 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 <!-- MATH + $\textrm{bias}=2$ + --> +<SPAN CLASS="MATH"><IMG + WIDTH="61" HEIGHT="15" ALIGN="BOTTOM" BORDER="0" + SRC="img2.png" + ALT="$\textrm{bias}=2$"></SPAN> and <!-- MATH + $\textrm{gain}=0.5$ + --> +<SPAN CLASS="MATH"><IMG + WIDTH="75" HEIGHT="30" ALIGN="MIDDLE" BORDER="0" + SRC="img3.png" + ALT="$\textrm{gain}=0.5$"></SPAN>, 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. + +<P> + +<H1><A NAME="SECTION00450000000000000000"> +Enabled-indicator while recording</A> +</H1> +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. + +<P> +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 <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>UTO_TOUCH</TT> mode. + +<P> + +<H1><A NAME="SECTION00460000000000000000"> +Linear automation editing</A> +</H1> +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"). + +<P> +Maybe make this behaviour default for discrete controllers? + +<P> + +<H1><A NAME="SECTION00470000000000000000"></A> <A NAME="symbolic_ports"></A> +<BR> +Symbolic names for MIDI ports +</H1> +MIDI ports shall have a user-defined symbolic name (like "Korg" or "Yamaha DX 7"). +The mapping between these symbolic names and the hardware port (like +"ALSA midi out port") is stored in the global configuration. + +<P> +Song files only specify the symbolic names as the ports associated with +their tracks. No information about physical devices/port names, but only +symbolic names are stored in the song file. + +<P> +This resolves the issues mentioned in <A HREF="#portconfig_sucks">1.2</A>, and also +allows the user to share his pieces with other people: They would only +have to set up that symbolic-to-hardware mapping once (collisions are +unlikely, because an equal symbolic name should usually mean the same +device) and are happy, instead of having to re-map <SPAN CLASS="textit">every</SPAN> port +for <SPAN CLASS="textit">every</SPAN> song. + +<P> +<BR><HR> + +</BODY> +</HTML> diff --git a/muse2/doc/html/single/developer_docs/img1.png b/muse2/doc/html/single/developer_docs/img1.png Binary files differnew file mode 100644 index 00000000..85b2087d --- /dev/null +++ b/muse2/doc/html/single/developer_docs/img1.png diff --git a/muse2/doc/html/single/developer_docs/img2.png b/muse2/doc/html/single/developer_docs/img2.png Binary files differnew file mode 100644 index 00000000..b5a0cc4b --- /dev/null +++ b/muse2/doc/html/single/developer_docs/img2.png diff --git a/muse2/doc/html/single/developer_docs/img3.png b/muse2/doc/html/single/developer_docs/img3.png Binary files differnew file mode 100644 index 00000000..ac4a584a --- /dev/null +++ b/muse2/doc/html/single/developer_docs/img3.png diff --git a/muse2/doc/html/single/developer_docs/index.html b/muse2/doc/html/single/developer_docs/index.html new file mode 100644 index 00000000..9cf45e45 --- /dev/null +++ b/muse2/doc/html/single/developer_docs/index.html @@ -0,0 +1,1745 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> + +<!--Converted with LaTeX2HTML 2008 (1.71) +original version by: Nikos Drakos, CBLU, University of Leeds +* revised and updated by: Marcus Hennecke, Ross Moore, Herb Swan +* with significant contributions from: + Jens Lippmann, Marek Rouchal, Martin Wilck and others --> +<HTML> +<HEAD> +<TITLE>MusE Documentation</TITLE> +<META NAME="description" CONTENT="MusE Documentation"> +<META NAME="keywords" CONTENT="developer_docs"> +<META NAME="resource-type" CONTENT="document"> +<META NAME="distribution" CONTENT="global"> + +<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8"> +<META NAME="Generator" CONTENT="LaTeX2HTML v2008"> +<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css"> + +<LINK REL="STYLESHEET" HREF="developer_docs.css"> + +</HEAD> + +<BODY > +<BR> + +<H2><A NAME="SECTION00100000000000000000"> +Contents</A> +</H2> +<!--Table of Contents--> + +<UL CLASS="TofC"> +<LI><A NAME="tex2html22" + HREF="developer_docs.html#SECTION00200000000000000000">Internals - how it works</A> +<UL> +<LI><A NAME="tex2html23" + HREF="developer_docs.html#SECTION00210000000000000000">User interface programming</A> +<LI><A NAME="tex2html24" + HREF="developer_docs.html#SECTION00220000000000000000">Configuration</A> +<LI><A NAME="tex2html25" + HREF="developer_docs.html#SECTION00230000000000000000">User controls and automation</A> +<UL> +<LI><A NAME="tex2html26" + HREF="developer_docs.html#SECTION00231000000000000000">Handling user input</A> +</UL> +</UL> +<BR> +<LI><A NAME="tex2html27" + HREF="developer_docs.html#SECTION00300000000000000000">Design decisions</A> +<UL> +<LI><A NAME="tex2html28" + HREF="developer_docs.html#SECTION00310000000000000000">Automation</A> +</UL> +<BR> +<LI><A NAME="tex2html29" + HREF="developer_docs.html#SECTION00400000000000000000">Feature requests</A> +<UL> +<LI><A NAME="tex2html30" + HREF="developer_docs.html#SECTION00410000000000000000">Per-Part automation and more on automation</A> +<LI><A NAME="tex2html31" + HREF="developer_docs.html#SECTION00420000000000000000">Pre-Rendering tracks</A> +<UL> +<LI><A NAME="tex2html32" + HREF="developer_docs.html#SECTION00421000000000000000">The feature</A> +<LI><A NAME="tex2html33" + HREF="developer_docs.html#SECTION00422000000000000000">Use cases</A> +<LI><A NAME="tex2html34" + HREF="developer_docs.html#SECTION00423000000000000000">Possible scenarios</A> +<LI><A NAME="tex2html35" + HREF="developer_docs.html#SECTION00424000000000000000">Extensions</A> +</UL> +<LI><A NAME="tex2html36" + HREF="developer_docs.html#SECTION00430000000000000000">Slotted editors</A> +<LI><A NAME="tex2html37" + HREF="developer_docs.html#SECTION00440000000000000000">Controller master values</A> +<LI><A NAME="tex2html38" + HREF="developer_docs.html#SECTION00450000000000000000">Enabled-indicator while recording</A> +<LI><A NAME="tex2html39" + HREF="developer_docs.html#SECTION00460000000000000000">Linear automation editing</A> +<LI><A NAME="tex2html40" + HREF="developer_docs.html#SECTION00470000000000000000">Symbolic names for MIDI ports</A> +</UL></UL> +<!--End of Table of Contents--> +<H1><A NAME="SECTION00200000000000000000"> +Internals - how it works</A> +</H1> +This chapter explains how MusE is built internally, and is meant +to be an aid for developers wanting to quickly start up with MusE. +For details on <SPAN CLASS="textit">why</SPAN> stuff is done please refer to the following +chapter. + +<P> + +<H1><A NAME="SECTION00210000000000000000"> +User interface programming</A> +</H1> +We use the QT Toolkit for GUI- and other programming. The <SPAN CLASS="textit">QT-Assistant</SPAN> +is an important tool for getting help. Almost everything can be looked +up there. + +<P> +GUIs can be either be hardcoded (see <TT>fterfirsta`a=95 _ `a=58 : + `a>64 + `a<91 aaa + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>rranger.cpp</TT> for an example) +or can be created using <SPAN CLASS="textit">QT-Designer</SPAN> (see the dialogs under +<TT>fterfirstw`w=95 _ `w=58 : + `w>64 + `w<91 www + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>idgets/function_dialogs/</TT> for mostly cleanly-written examples). +Don't forget to add your <TT>fterfirstc`c=95 _ `c=58 : + `c>64 + `c<91 ccc + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>pp</TT>, <TT>fterfirsth`h=95 _ `h=58 : + `h>64 + `h<91 hhh + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>></TT> and <TT>fterfirstu`u=95 _ `u=58 : + `u>64 + `u<91 uuu + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>i</TT> files to the +corresponding sections in the <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>MakeLists.txt</TT>! + +<P> +Additionally, MusE offers some custom widgets, like menu title items etc. +Following, there will be a small, unordered list about custom widgets: + +<UL> +<LI><TT>fterfirstM`M=95 _ `M=58 : + `M>64 + `M<91 MMM + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>usEGui::MenuTitleItem</TT>: Provides a title-bar in a <TT>fterfirstQ`Q=95 _ `Q=58 : + `Q>64 + `Q<91 QQQ + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>Menu</TT>. +<BR> +Usage: <TT>fterfirsts`s=95 _ `s=58 : + `s>64 + `s<91 sss + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>omeMenu->addAction(new MusEGui::MenuTitleItem(tr("fnord"), someMenu));</TT> +<BR> +Defined in <TT>fterfirstw`w=95 _ `w=58 : + `w>64 + `w<91 www + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>idgets/menutitleitem.h</TT>. +</LI> +<LI><TT>fterfirstM`M=95 _ `M=58 : + `M>64 + `M<91 MMM + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>usEGui::PopupMenu</TT>: Provides a <TT>fterfirstQ`Q=95 _ `Q=58 : + `Q>64 + `Q<91 QQQ + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>Menu</TT>-like menu which + can stay open after the user checks a checkable action. +<BR> +Usage: just create a <TT>fterfirstn`n=95 _ `n=58 : + `n>64 + `n<91 nnn + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ew PopupMenu( true|false )</TT> instead of + a <TT>fterfirstn`n=95 _ `n=58 : + `n>64 + `n<91 nnn + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ew QMenu()</TT>. (<TT>fterfirstt`t=95 _ `t=58 : + `t>64 + `t<91 ttt + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>rue</TT> means 'stay open') +<BR> +Defined in <TT>fterfirstw`w=95 _ `w=58 : + `w>64 + `w<91 www + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>idgets/popupmenu.h</TT>. +</LI> +</UL> + +<P> + +<H1><A NAME="SECTION00220000000000000000"></A> <A NAME="portconfig_sucks"></A> +<BR> +Configuration +</H1> +Configuration is a bit pesky in MusE in its current state. If you get +confused by reading this chapter, that's a sign of a sane mind. + +<P> +There are three kinds of configuration items: + +<UL> +<LI>(1) Global configuration, like coloring schemes, plugin categories, MDI-ness settings +</LI> +<LI>(2) Per-Song configuration, like whether to show or hide certain track types in the arranger +</LI> +<LI>(3) Something in between, like MIDI port settings etc. They obviously actually are + global configuration issues (or ought to be), but also obviously must be stored + in the song file for portability. (This problem could possibly be solved by + the feature proposal in <A HREF="#symbolic_ports">3.7</A>. +</LI> +</UL> + +<P> + +<H4><A NAME="SECTION00220010000000000000"> +Reading configuration</A> +</H4> +<TT>fterfirstv`v=95 _ `v=58 : + `v>64 + `v<91 vvv + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>oid MusECore::readConfiguration(Xml&, bool, bool)</TT> in +<TT>fterfirstc`c=95 _ `c=58 : + `c>64 + `c<91 ccc + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>onf.cpp</TT> is the central point +of reading configuration. It is called when MusE is started first +(by <TT>fterfirstb`b=95 _ `b=58 : + `b>64 + `b<91 bbb + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ool MusECore::readConfiguration()</TT>), and also when a +song is loaded. +<BR>It can be instructed whether to read MIDI ports (3), global configuration +and MIDI ports (1+3). Per-Song configuration is always read (2). + +<P> +When adding new configuration items and thus altering <TT>fterfirstr`r=95 _ `r=58 : + `r>64 + `r<91 rrr + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>eadConfiguration()</TT>, +you must take care to place your item into the correct section. The code is +divided into the following sections: + +<UL> +<LI>Global and/or per-song configuration (3) +</LI> +<LI>Global configuration (1) +</LI> +<LI>Code for skipping obsolete entries +</LI> +</UL> + +<P> +The sections are divided by comments (they contain <TT>--</TT>, so just +search for them). Please do not just remove code for reading obsolete entries, +but always add an appropriate entry to the 'skipping' section in order to +prevent error messages when reading old configs. + +<P> + +<H4><A NAME="SECTION00220020000000000000"> +Writing configuration</A> +</H4> +Global configuration is written using the +<TT>fterfirstM`M=95 _ `M=58 : + `M>64 + `M<91 MMM + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>usEGui::MusE::writeGlobalConfiguration()</TT> functions, while +per-song-config is written by <TT>fterfirstM`M=95 _ `M=58 : + `M>64 + `M<91 MMM + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>usEGui::MusE::writeConfiguration()</TT> +(notice the missing <TT>fterfirstG`G=95 _ `G=58 : + `G>64 + `G<91 GGG + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>lobal</TT>; both implemented in <TT>fterfirstc`c=95 _ `c=58 : + `c>64 + `c<91 ccc + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>onf.cpp</TT>). + +<P> +<TT>fterfirstw`w=95 _ `w=58 : + `w>64 + `w<91 www + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>riteConfiguration</TT> is actually just a subset of the code in +<TT>fterfirstw`w=95 _ `w=58 : + `w>64 + `w<91 www + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>riteGlobalConfiguration</TT>. <SPAN CLASS="textbf">Duplicate code!</SPAN> +<P> + +<H4><A NAME="SECTION00220030000000000000"> +Song state</A> +</H4> +Additionally to per-song configuration, there is the song's state. +This contains "the song", that is all tracks, parts and note events, +together with information about the currently opened windows, their +position, size, settings and so on. Adding new items here is actually +pretty painless: Configuration is read and written using +<TT>fterfirstM`M=95 _ `M=58 : + `M>64 + `M<91 MMM + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>usECore::Song::read</TT> and <TT>fterfirst:`:=95 _ `:=58 : + `:>64 + `:<91 ::: + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>:write</TT>, both implemented in +<TT>fterfirsts`s=95 _ `s=58 : + `s>64 + `s<91 sss + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ongfile.cpp</TT>. There are no caveats. + +<P> + +<H4><A NAME="SECTION00220040000000000000"> +How to add new items</A> +</H4> +When adding global configuration items, then add them into the second +block ("global configuration") in <TT>fterfirstr`r=95 _ `r=58 : + `r>64 + `r<91 rrr + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>eadConfiguration</TT> and into +<TT>fterfirstw`w=95 _ `w=58 : + `w>64 + `w<91 www + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>riteGlobalConfiguration</TT>. + +<P> +When adding just-per-song items, better don't bother to touch the +"configuration" code and just add it to the song's state (there might +be rare exceptions). + +<P> +When adding global configuration items, make sure you add them into the +correct section of <TT>fterfirstr`r=95 _ `r=58 : + `r>64 + `r<91 rrr + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>eadConfiguration</TT>, and into <TT>fterfirstw`w=95 _ `w=58 : + `w>64 + `w<91 www + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>riteGlobalConfiguration</TT>. + +<P> + +<H1><A NAME="SECTION00230000000000000000"> +User controls and automation</A> +</H1> + +<H2><A NAME="SECTION00231000000000000000"> +Handling user input</A> +</H2> + +<H3><A NAME="SECTION00231100000000000000"> +Plugins and synthesizers</A> +</H3> + +<H4><A NAME="SECTION00231110000000000000"> +Overview</A> +</H4> +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 <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginGui</TT>, <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginIBase</TT> +(in <TT>fterfirstp`p=95 _ `p=58 : + `p>64 + `p<91 ppp + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>lugin.h</TT>) and <TT>fterfirstO`O=95 _ `O=58 : + `O>64 + `O<91 OOO + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>scIF</TT> (in <TT>fterfirsto`o=95 _ `o=58 : + `o>64 + `o<91 ooo + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>sc.h</TT>). + +<P> +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. Then MusE will update the plugin's parameter +value, and also record the new value. When appropriate, the controller +is enabled again. + +<P> + +<H4><A NAME="SECTION00231120000000000000"> +Processing the input, recording</A> +</H4> +Upon operating a slider, <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginIBase::setParam</TT> is called, +which usually writes the control change into the ringbuffer +<TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::_controlFifo</TT>. (<TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::apply()</TT>, +<TT>fterfirstD`D=95 _ `D=58 : + `D>64 + `D<91 DDD + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ssiSynthIF::getData()</TT> will read this ringbuffer and +do the processing accordingly). Furthermore, <TT>fterfirstA`A=95 _ `A=58 : + `A>64 + `A<91 AAA + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>udioTrack::recordAutomation</TT> +is called, which either directly modifies the controller lists or writes +the change into a "to be recorded"-list (<TT>fterfirstA`A=95 _ `A=58 : + `A>64 + `A<91 AAA + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>udioTrack::_recEvents</TT>) +(depending on whether the song is stopped or played). + +<P> +The <TT>fterfirstA`A=95 _ `A=58 : + `A>64 + `A<91 AAA + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>udioTrack::_recEvents</TT> list consists of <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlRecVal</TT> +items (see <TT>fterfirstc`c=95 _ `c=58 : + `c>64 + `c<91 ccc + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trl.h</TT>), which hold the following data: + +<UL> +<LI>the frame where the change occurred +</LI> +<LI>the value +</LI> +<LI>the type, which can be <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>RVT_START</TT>, <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>RVT_VAL</TT> or <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>RVT_STOP</TT>. + <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>RVT_VAL</TT> are written by every <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>udioTrack::recordAutomation</TT> + call, <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>RVT_START</TT> and <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>RVT_STOP</TT> are generated by + <TT>fterfirstA`A=95 _ `A=58 : + `A>64 + `A<91 AAA + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>udioTrack::startAutoRecord</TT> and <TT>fterfirsts`s=95 _ `s=58 : + `s>64 + `s<91 sss + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>topAutoRecord</TT>, respectively. +</LI> +<LI>and the id of the controller which is affected +</LI> +</UL> +It is processed when the song is stopped. The call path for this is: +<TT>fterfirstS`S=95 _ `S=58 : + `S>64 + `S<91 SSS + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ong::stopRolling</TT> calls <TT>fterfirstS`S=95 _ `S=58 : + `S>64 + `S<91 SSS + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ong::processAutomationEvents</TT> +calls <TT>fterfirstA`A=95 _ `A=58 : + `A>64 + `A<91 AAA + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>udioTrack::processAutomationEvents</TT>. +This function removes the old events from the track's controller list +and replaces them with the new events from <TT>fterfirst_`_=95 _ `_=58 : + `_>64 + `_<91 ___ + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>recEvents</TT>. In +<TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>UTO_WRITE</TT> mode, just all controller events within the recorded +range are erased; in <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>UTO_TOUCH</TT> mode, the <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>RVT_START</TT> +and <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>RVT_STOP</TT> types of the <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlRecVal</TT> events are used +to determine the range(s) which should be wiped. + +<P> + +<H4><A NAME="SECTION00231130000000000000"> +How it's stored</A> +</H4> +Automation data is kept in <TT>fterfirstA`A=95 _ `A=58 : + `A>64 + `A<91 AAA + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>udioTrack::_controller</TT>, which is a <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlListList</TT>, that is, a list of <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlList</TT>s, that is, a list of lists of +controller-objects which hold the control points of the automation graph. +The <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlList</TT> also stores whether the list is meant discrete +(a new control point results in a value-jump) or continuous (a new control +point results in the value slowly sloping to the new value). +Furthermore, it stores a <TT>fterfirst_`_=95 _ `_=58 : + `_>64 + `_<91 ___ + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>curVal</TT> (accessed by <TT>fterfirstc`c=95 _ `c=58 : + `c>64 + `c<91 ccc + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>urVal()</TT>), +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. + +<P> +<TT>fterfirstA`A=95 _ `A=58 : + `A>64 + `A<91 AAA + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>udioTrack::addController</TT> and <TT>fterfirstr`r=95 _ `r=58 : + `r>64 + `r<91 rrr + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>emoveController</TT> are used to add/remove whole controller types; the most important functions which +access <TT>fterfirst_`_=95 _ `_=58 : + `_>64 + `_<91 ___ + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>controller</TT> are: + +<UL> +<LI><TT>fterfirstp`p=95 _ `p=58 : + `p>64 + `p<91 ppp + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>rocessAutomationEvents</TT>, <TT>fterfirstr`r=95 _ `r=58 : + `r>64 + `r<91 rrr + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ecordAutomation</TT>, + <TT>fterfirsts`s=95 _ `s=58 : + `s>64 + `s<91 sss + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>tartAutoRecord</TT>, <TT>fterfirsts`s=95 _ `s=58 : + `s>64 + `s<91 sss + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>topAutoRecord</TT>: see above. +</LI> +<LI><TT>fterfirsts`s=95 _ `s=58 : + `s>64 + `s<91 sss + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>eekPrevACEvent</TT>, <TT>fterfirsts`s=95 _ `s=58 : + `s>64 + `s<91 sss + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>eekNextACEvent</TT>, <TT>fterfirste`e=95 _ `e=58 : + `e>64 + `e<91 eee + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>raseACEvent</TT>, + <TT>fterfirste`e=95 _ `e=58 : + `e>64 + `e<91 eee + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>raseRangeACEvents</TT>, <TT>fterfirsta`a=95 _ `a=58 : + `a>64 + `a<91 aaa + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ddACEvent</TT>, <TT>fterfirstc`c=95 _ `c=58 : + `c>64 + `c<91 ccc + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>hangeACEvent</TT>, + which do the obvious +</LI> +<LI><TT>fterfirstp`p=95 _ `p=58 : + `p>64 + `p<91 ppp + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginCtrlVal</TT>, <TT>fterfirsts`s=95 _ `s=58 : + `s>64 + `s<91 sss + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>etPluginCtrlVal</TT>: the first + returns the current value according to the <TT>fterfirst_`_=95 _ `_=58 : + `_>64 + `_<91 ___ + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>controller</TT> + list, the second only sets the <TT>fterfirstc`c=95 _ `c=58 : + `c>64 + `c<91 ccc + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>urVal</TT>, but does not + insert any events. +</LI> +</UL> + +<P> +Whenever a <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlList</TT> has been manipulated, +<TT>fterfirstM`M=95 _ `M=58 : + `M>64 + `M<91 MMM + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>usEGlobal::song->controllerChange(Track*)</TT> shall be called, +which emits the <TT>fterfirstM`M=95 _ `M=58 : + `M>64 + `M<91 MMM + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>usEGlobal::song->controllerChanged(Track*)</TT> +signal in order to inform any parts of MusE about the change (currently, +only the arranger's part canvas utilizes this). + +<P> + +<H4><A NAME="SECTION00231140000000000000"> +Enabling and disabling controllers</A> +</H4> +Disabling the controller is both dependent from the current automation +mode and from whether the GUI is native or not. +In <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>UTO_WRITE</TT> 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. + +<P> +In <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>UTO_TOUCH</TT> (and currently (r1492) <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>UTO_READ</TT>, 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, +however they only affect the recorded automation until the last toggle +of the checkbox. (Example: start the song, toggle the checkbox, toggle +it again, wait 10 seconds, stop the song. This will NOT overwrite the +last 10 seconds of automation data, but everything between the first +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 <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>UTO_WRITE</TT> mode. + +<P> +The responsible functions are: <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::oscControl</TT> and +<TT>fterfirstD`D=95 _ `D=58 : + `D>64 + `D<91 DDD + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ssiSynthIF::oscControl</TT> for handling native GUIs, +<TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::ctrlPressed</TT> and <TT>fterfirstc`c=95 _ `c=58 : + `c>64 + `c<91 ccc + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlReleased</TT> for MusE +default GUIs and <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::guiParamPressed</TT>, +<TT>fterfirstg`g=95 _ `g=58 : + `g>64 + `g<91 ggg + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>uiParamReleased</TT>, <TT>fterfirstg`g=95 _ `g=58 : + `g>64 + `g<91 ggg + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>uiSliderPressed</TT> and +<TT>fterfirstg`g=95 _ `g=58 : + `g>64 + `g<91 ggg + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>uiSliderReleased</TT> for MusE GUIs read from a UI file; +<TT>fterfirstg`g=95 _ `g=58 : + `g>64 + `g<91 ggg + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>uiSlider*</TT> obviously handle sliders, while <TT>fterfirstg`g=95 _ `g=58 : + `g>64 + `g<91 ggg + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>uiParam*</TT> +handle everything else which is not a slider. They call +<TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::enableController</TT> to enable/disable it. + +<P> +Furthermore, on every song stop or seek, <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::enableAllControllers</TT> +is called, which re-enables all controllers again. The call paths for +this are: + +<UL> +<LI>For stop: <TT>fterfirstS`S=95 _ `S=58 : + `S>64 + `S<91 SSS + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ong::stopRolling</TT> calls + <TT>fterfirstS`S=95 _ `S=58 : + `S>64 + `S<91 SSS + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ong::processAutomationEvents</TT> calls + <TT>fterfirstS`S=95 _ `S=58 : + `S>64 + `S<91 SSS + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ong::clearRecAutomation</TT> calls + <TT>fterfirstT`T=95 _ `T=58 : + `T>64 + `T<91 TTT + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>rack::clearRecAutomation</TT> calls + <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::enableAllControllers</TT> +</LI> +<LI>For seek: <TT>fterfirstA`A=95 _ `A=58 : + `A>64 + `A<91 AAA + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>udio::seek</TT> sends a message ("<TT>fterfirstG`G=95 _ `G=58 : + `G>64 + `G<91 GGG + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>></TT>") to + <TT>fterfirstS`S=95 _ `S=58 : + `S>64 + `S<91 SSS + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ong::seqSignal</TT> which calls + <TT>fterfirstS`S=95 _ `S=58 : + `S>64 + `S<91 SSS + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>ong::clearRecAutomation</TT> which calls + <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::enableAllControllers</TT> +</LI> +</UL> + +<P> + +<H1><A NAME="SECTION00300000000000000000"> +Design decisions</A> +</H1> + +<H1><A NAME="SECTION00310000000000000000"> +Automation</A> +</H1> +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 <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::_controlFifo</TT>. Automation data is kept +in <TT>fterfirstA`A=95 _ `A=58 : + `A>64 + `A<91 AAA + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>udioTrack::_controller</TT>, which is a <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlListList</TT>, +that is, a list of <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlList</TT>s, that is, a list of lists of +controller-objects which hold the control points of the automation graph. +The <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlList</TT> 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). + +<P> +While <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::_controlFifo</TT> 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 <SPAN CLASS="textit">driver</SPAN> period. +This might lead to noticeable jumps in value. + +<P> +This could possibly be solved in two ways: + +<H4><A NAME="SECTION00310010000000000000"> +Maintaining a slave control list</A> +</H4> +This approach would maintain a fully redundant slave control list, +similar to <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::_controlFifo</TT>. 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 <TT>fterfirstP`P=95 _ `P=58 : + `P>64 + `P<91 PPP + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>luginI::_controlFifo</TT>, +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). + +<P> + +<H4><A NAME="SECTION00310020000000000000"> +Holding iterators</A> +</H4> +We also could hold a list of iterators of the single <TT>fterfirstC`C=95 _ `C=58 : + `C>64 + `C<91 CCC + + <`<=95 _ `<=58 : + `<>64 + `<<91 <<< + + c@amelhyph<269>>trlList</TT>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 +song), and we must iterate through a whole list of iterators. + +<P> + +<H4><A NAME="SECTION00310030000000000000"> +Just use the current data access functions</A> +</H4> +By just using the current functions for accessing automation data, +we might get a quick-and-dirty solution, which however wastes way too +much CPU ressources. This is because on <SPAN CLASS="textit">every single frame</SPAN>, we +need to do a binary search on multiple controller lists. + +<P> + +<H1><A NAME="SECTION00400000000000000000"> +Feature requests</A> +</H1> + +<H1><A NAME="SECTION00410000000000000000"> +Per-Part automation and more on automation</A> +</H1> Automation shall be undo-able. Automation shall reside in parts which +are exchangeable, clonable etc (like the MIDI- and Wave-Parts). +Global per-synth/per-audiotrack automation shall also be available, but +this can also be implemented as special case of part automation (one +long part). + +<P> + +<H1><A NAME="SECTION00420000000000000000"> +Pre-Rendering tracks</A> +</H1> + +<H2><A NAME="SECTION00421000000000000000"> +The feature</A> +</H2> +All tracks shall be able to be "pre-renderable". Pre-rendering shall +be "layered". Pre-rendering shall act like a transparent audio cache: +Audio data is (redundantly) stored, wasting memory in order to save CPU. + +<P> +That is: Each track owns one or more wave-recordings of the length of +the song. If the user calls "pre-render" on a track, then this track +is played quasi-solo (see below), and the raw audio data is recorded +and stored in the "layer 0" wave recording. If the user has any effects +set up to be applied, then each effect is applied on a different layer +(creating layer 1, layer 2 etc). + +<P> +This means, that also MIDI and drum tracks can have effects (which +usually only operate on audio, but we HAVE audio data because of this +prerendering). + +<P> +Furthermore, MusE by default does not send MIDI events to the synthesizers +but instead just plays back the last layer of the prerecording (for +MIDI tracks), or does not pipe the audio data through the whole plugin +chain (causing cpu usage), but instead just plays back the last layer. +The hearable result shall be the same. + +<P> +Once the user changes any parameter (automation data or plugins for +wave tracks, MIDI events or effect plugin stuff for MIDI tracks), +then MusE shall generate the sound for this particular track in the +"old" way (send MIDI data to synthes, or pipe audio data through plugins). +(So that the user will not even notice that MusE actually pre-renderered +stuff.) Either MusE automatically records this while playback (if possible) +or prompts the user to accordingly set up his cabling and then record +it. Or (temporarily) disables prerecording for this track, falling back +to the plain old way of generating sound. + +<P> +<SPAN CLASS="textit">Quasi-solo</SPAN> means: For wave tracks, just solo the track. For MIDI +tracks, mute all tracks which are not on the same synth (channel?), +and mute all <SPAN CLASS="textit">note</SPAN> events which are not on the quasi-soloed track. +This causes MusE to still play any controller events from different +tracks, because they might have effects on the quasi-soloed track. (You +can have notes on channel 1 on one track and controller stuff on channel +1 on another track; then you would need quasi-solo to get proper results.) + +<P> + +<H2><A NAME="SECTION00422000000000000000"> +Use cases</A> +</H2> + +<H4><A NAME="SECTION00422010000000000000"> +Saving CPU</A> +</H4> +On slow systems, this is neccessary for songs with lots of, or demanding +(or both) soft synthes / plugins. Even if the synth or plugin is so +demanding that your system is not able to produce sound in real-time, +then with this feature you'll be able to use the synth (this will make +editing pretty laggish, because for a change you need to re-render at +least a part before you can listen to it, but better than being unable +to use the synth at all!) + +<P> + +<H4><A NAME="SECTION00422020000000000000"> +Exporting as audio project</A> +</H4> +Using pre-rendering on all tracks, you easily can export your project +as multi-track audio file (for use with Ardour or similar DAWs). +Just take the last layer of each track, and write the raw audio data +into the file, and you're done. (Maybe we are even able to write down +the raw-raw layer0 audio data plus information about used plugins and +settings etc..?) + +<P> + +<H4><A NAME="SECTION00422030000000000000"> +Mobile audio workstations</A> +</H4> +You might want to work a bit on your audio projects on your notebook +while you're not at home, not having access to your hardware synthesizers. +Using this feature, you could have pre-recorded the stuff in your studio +before, and now can at least fiddle around with the non-hw-synth-dependent +parts of your song, while still having your <SPAN CLASS="textit">full</SPAN> song with you. + +<P> + +<H4><A NAME="SECTION00422040000000000000"> +Applying effects on MIDI tracks</A> +</H4> +If you have many physical audio inputs, you might already be able to +apply effect chains on MIDI tracks, by wiring the synthes' audio +outputs to your soundcard's inputs, and applying the effects on +dedicated input tracks you have to create. This requires you to have +expensive hardware, and is pretty complicated, because you need one +additional track per MIDI synth. + +<P> +This feature allows you to apply effects on single MIDI tracks, and not +only on full MIDI synthes, and doesn't require you to be have that +many physical audio inputs (you need to manually replug your synthes, +however). + +<P> + +<H2><A NAME="SECTION00423000000000000000"> +Possible scenarios</A> +</H2> + +<H4><A NAME="SECTION00423010000000000000"> +Setting it up</A> +</H4> +Create a wave track, MusE will allow you to set or unset prerendering +for every plugin in the plugin rack (recording the actual track is +useless because it would be a plain copy). +Create a MIDI track, MusE will ask you on which physical audio input +your synth is connected. Setting up multiple synthes on one physical +audio in is allowed, see below. + +<P> + +<H4><A NAME="SECTION00423020000000000000"> +Pre-rendering stuff</A> +</H4> +When the user presses the "pre-render" button, all tracks which have +been changed since their last pre-rendering will be re-rendered. +If you have multiple hardware synthes set up as they were connected +to one physical audio input port, MusE will prompt you to first plug +the proper cable in. + +<P> + +<H4><A NAME="SECTION00423030000000000000"> +Making changes</A> +</H4> +Change a note in a MIDI part, move or delete a part or change automation +parameters. MusE will temporarily disable the pre-rendered information +and instead generate the sound via sending out MIDI events, piping stuff +through effect chains or similar. If you play back the whole song, or +if you manually trigger a re-rendering of a track via the context menu, +MusE will play back the stuff, record it again and re-enable the +pre-rendered information. + +<P> + +<H2><A NAME="SECTION00424000000000000000"> +Extensions</A> +</H2> + +<H4><A NAME="SECTION00424010000000000000"> +Automatic discovery of physical audio connections</A> +</H4> +The user plugs all (or only some) synthes' audio outs into the available +audio inputs, then runs automatic discovery. This will send MIDI events +to each synthesizer, and look on which audio in there's activity. Then +it will assume that the synthesizer is connected to that particular +audio in. Audio ins which show activity before any MIDI events were +sent are not considered, as they're probably connected to microphones +or other noise-generating non-synthes. + +<P> + +<H4><A NAME="SECTION00424020000000000000"> +Audio export</A> +</H4> +As described in the Use cases, MusE can allow you to export your song +in some multitrack audio format. + +<P> + +<H4><A NAME="SECTION00424030000000000000"> +Cheap/Faked changes</A> +</H4> +For expensive or unavailable synthes, changing the Volume midi controller, +the Pan controller or similar "easy" controllers will not trigger a +complete re-rendering, but instead "fake" the change, by changing +the volume data directly on the recorded wave. This might require some +learning and might even get pretty complicated. + +<P> + +<H4><A NAME="SECTION00424040000000000000"> +Intelligent re-recording</A> +</H4> +For tiny changes, MusE shall only re-render the relevant part. If you +change some MIDI notes, then begin re-recording shortly before the +changes, and end re-recording as soon as the recorded stuff doesn't +differ to much from the stuff coming from the synth. Then properly +blend the old recording with the updated part. + +<P> + +<H1><A NAME="SECTION00430000000000000000"> +Slotted editors</A> +</H1> +Currently, MusE has the pianoroll editor, drum editor, score editor, +then the controller editor which is inside the pianoroll/drum editor. +All these editors have a very similar concept: the "time axis" is +vertical and (almost) linear, they handle parts, and events are +manipulated similarly. + +<P> +A unified editor shall be created which allows you to combine different +kinds of editors in one window, properly aligned against each other. +These "different kinds of editors" shall be handled as "slots"; one +unified editor window consists of: + +<UL> +<LI>A menu bar, containing stuff suitable for the complete window, + which might include window name, MDI-ness etc. +</LI> +<LI>A toolbar which contains controls suitable for every single slot. +</LI> +<LI>A container with one or more slots; the slots can be scrolled in + y-direction if there are multipe slots. +</LI> +<LI>A time-scrollbar with zoom +</LI> +</UL> + +<P> +Each slot contains the following: + +<UL> +<LI>A menu button, button box or control panel for setting up this + particular slot. This could contain "note head colors", "show + a transposing instrument" etc for score edit slots, "event + rectangle color", "grid size" and "snap to grid" for pianoroll/ + drum editors. +</LI> +<LI>The actual canvas +</LI> +<LI>A y-direction scroll bar, possibly with zoom control (for + pianoroll editor) +</LI> +</UL> + +<P> +The main window does not show its scroll bar if there is only one slot, +because the slot's scrollbar is sufficient then. + +<P> +Slots can be added, destroyed, moved around, maybe even merged (if the +slot types allow it); basically, you can compare them with the staves +in the score editor. + +<P> +The slots shall align against each other, that is, if a score editor +slot displays a key change with lots of accidentials, then all other +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. + +<P> + +<H1><A NAME="SECTION00440000000000000000"> +Controller master values</A> +</H1> +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, <!-- MATH + $\textrm{value} * \textrm{bias} ++ textrm{bias}$ + --> +<SPAN CLASS="MATH"><IMG + WIDTH="180" HEIGHT="30" ALIGN="MIDDLE" BORDER="0" + SRC="img1.png" + ALT="$\textrm{value} * \textrm{bias} ++ textrm{bias}$"></SPAN> 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 <!-- MATH + $\textrm{bias}=2$ + --> +<SPAN CLASS="MATH"><IMG + WIDTH="61" HEIGHT="15" ALIGN="BOTTOM" BORDER="0" + SRC="img2.png" + ALT="$\textrm{bias}=2$"></SPAN> and <!-- MATH + $\textrm{gain}=0.5$ + --> +<SPAN CLASS="MATH"><IMG + WIDTH="75" HEIGHT="30" ALIGN="MIDDLE" BORDER="0" + SRC="img3.png" + ALT="$\textrm{gain}=0.5$"></SPAN>, 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. + +<P> + +<H1><A NAME="SECTION00450000000000000000"> +Enabled-indicator while recording</A> +</H1> +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. + +<P> +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 <TT>fterfirstA`A=95 _ `A=58 : A + <`<=95 _ `<=58 : < + u@nderscorehyph<271>>UTO_TOUCH</TT> mode. + +<P> + +<H1><A NAME="SECTION00460000000000000000"> +Linear automation editing</A> +</H1> +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"). + +<P> +Maybe make this behaviour default for discrete controllers? + +<P> + +<H1><A NAME="SECTION00470000000000000000"></A> <A NAME="symbolic_ports"></A> +<BR> +Symbolic names for MIDI ports +</H1> +MIDI ports shall have a user-defined symbolic name (like "Korg" or "Yamaha DX 7"). +The mapping between these symbolic names and the hardware port (like +"ALSA midi out port") is stored in the global configuration. + +<P> +Song files only specify the symbolic names as the ports associated with +their tracks. No information about physical devices/port names, but only +symbolic names are stored in the song file. + +<P> +This resolves the issues mentioned in <A HREF="#portconfig_sucks">1.2</A>, and also +allows the user to share his pieces with other people: They would only +have to set up that symbolic-to-hardware mapping once (collisions are +unlikely, because an equal symbolic name should usually mean the same +device) and are happy, instead of having to re-map <SPAN CLASS="textit">every</SPAN> port +for <SPAN CLASS="textit">every</SPAN> song. + +<P> +<BR><HR> + +</BODY> +</HTML> |