Release 4.2.0 tarball

This commit is contained in:
Gary Scavone
2013-09-29 23:39:37 +02:00
committed by Stephen Sinclair
parent fe20fe92a2
commit de344668dd
347 changed files with 16972 additions and 8538 deletions

View File

@@ -9,118 +9,199 @@
<a class="qindex" href="index.html">Home</a> &nbsp; <a class="qindex" href="information.html">Information</a> &nbsp; <a class="qindex" href="classes.html">Classes</a> &nbsp; <a class="qindex" href="download.html">Download</a> &nbsp; <a class="qindex" href="usage.html">Usage</a> &nbsp; <a class="qindex" href="maillist.html">Mail List</a> &nbsp; <a class="qindex" href="system.html">Requirements</a> &nbsp; <a class="qindex" href="links.html">Links</a> &nbsp; <a class="qindex" href="tutorial.html">Tutorial</a></CENTER>
<HR>
<!-- Generated by Doxygen 1.3.4 -->
<h1><a class="anchor" name="polyvoices">Voice Management</a></h1>The previous tutorial chapters were concerned only with monophonic ToolKit instrument playback and control. At this point, it should be relatively clear that one can instantiate multiple instruments and perhaps sum together their sounds or even direct their sounds to separate output channels. It is less clear how one might go about controlling a group of instruments. The <a class="el" href="classVoicer.html">Voicer</a> class is designed to serve just this purpose.<p>
The STK <a class="el" href="classVoicer.html">Voicer</a> class is a relatively simple voice manager. The user can dynamically add and delete instruments from its "control", with the option of controlling specific instruments via unique note tags and/or grouping sets of instruments via a "channel" number. All sounding instrument outputs are summed and returned via the <code>tick()</code> function. The <a class="el" href="classVoicer.html">Voicer</a> class responds to noteOn, noteOff, setFrequency, pitchBend, and controlChange messages, automatically assigning incoming messages to the voices in its control. When all voices are sounding and a new noteOn is encountered, the <a class="el" href="classVoicer.html">Voicer</a> interrupts the oldest sounding voice. The user is responsible for creating and deleting all instrument instances.<p>
<h1><a class="anchor" name="polyvoices">Voice Management</a></h1>The previous tutorial chapters were concerned only with monophonic ToolKit instrument playback and control. At this point, it should be relatively clear that one can instantiate multiple instruments and perhaps sum together their outputs or even direct their outputs to separate channels. It is less clear how one might go about controlling a group of instruments. The <a class="el" href="classVoicer.html">Voicer</a> class is designed to serve just this purpose.<p>
The STK <a class="el" href="classVoicer.html">Voicer</a> class is a relatively simple voice manager. The user can dynamically add and delete instruments to/from its "control", with the option of controlling specific instruments via unique note tags and/or grouping sets of instruments via a "channel" number. All sounding instrument outputs are summed and returned via the <code>tick()</code> function. The <a class="el" href="classVoicer.html">Voicer</a> class responds to noteOn, noteOff, setFrequency, pitchBend, and controlChange messages, automatically assigning incoming messages to the voices in its control. When all voices are sounding and a new noteOn is encountered, the <a class="el" href="classVoicer.html">Voicer</a> interrupts the oldest sounding voice. The user is responsible for creating and deleting all instrument instances.<p>
In the following example, we modify the <code>controlbee.cpp</code> program to make use of three <a class="el" href="classBeeThree.html">BeeThree</a> instruments, all controlled using a <a class="el" href="classVoicer.html">Voicer</a>.<p>
<div class="fragment"><pre><span class="comment">// threebees.cpp</span>
<div class="fragment"><pre><span class="comment">// threebees.cpp STK tutorial program</span>
<span class="preprocessor">#include "BeeThree.h"</span>
<span class="preprocessor">#include "RtWvOut.h"</span>
<span class="preprocessor">#include "RtAudio.h"</span>
<span class="preprocessor">#include "Messager.h"</span>
<span class="preprocessor">#include "Voicer.h"</span>
<span class="preprocessor">#include "SKINI.msg"</span>
<span class="preprocessor">#include &lt;algorithm&gt;</span>
<span class="preprocessor">#if !defined(__OS_WINDOWS__) // Windoze bogosity for VC++ 6.0</span>
<span class="preprocessor"></span> <span class="keyword">using</span> std::min;
<span class="preprocessor">#endif</span>
<span class="preprocessor"></span>
<span class="comment">// The TickData structure holds all the class instances and data that</span>
<span class="comment">// are shared by the various processing functions.</span>
<span class="keyword">struct </span>TickData {
<a class="code" href="classVoicer.html">Voicer</a> voicer;
<a class="code" href="classMessager.html">Messager</a> messager;
<a class="code" href="structSkini_1_1Message.html">Skini::Message</a> message;
<span class="keywordtype">int</span> counter;
<span class="keywordtype">bool</span> haveMessage;
<span class="keywordtype">bool</span> done;
<span class="comment">// Default constructor.</span>
TickData()
: counter(0), haveMessage(false), done( false ) {}
};
<span class="preprocessor">#define DELTA_CONTROL_TICKS 64 // default sample frames between control input checks</span>
<span class="preprocessor"></span>
<span class="comment">// The processMessage() function encapsulates the handling of control</span>
<span class="comment">// messages. It can be easily relocated within a program structure</span>
<span class="comment">// depending on the desired scheduling scheme.</span>
<span class="keywordtype">void</span> processMessage( TickData* data )
{
<span class="keyword">register</span> StkFloat value1 = data-&gt;message.floatValues[0];
<span class="keyword">register</span> StkFloat value2 = data-&gt;message.floatValues[1];
<span class="keywordflow">switch</span>( data-&gt;message.type ) {
<span class="keywordflow">case</span> __SK_Exit_:
data-&gt;done = <span class="keyword">true</span>;
<span class="keywordflow">return</span>;
<span class="keywordflow">case</span> __SK_NoteOn_:
<span class="keywordflow">if</span> ( value2 == 0.0 ) <span class="comment">// velocity is zero ... really a NoteOff</span>
data-&gt;voicer.noteOff( value1, 64.0 );
<span class="keywordflow">else</span> { <span class="comment">// a NoteOn</span>
data-&gt;voicer.noteOn( value1, value2 );
}
<span class="keywordflow">break</span>;
<span class="keywordflow">case</span> __SK_NoteOff_:
data-&gt;voicer.noteOff( value1, value2 );
<span class="keywordflow">break</span>;
<span class="keywordflow">case</span> __SK_ControlChange_:
data-&gt;voicer.controlChange( (<span class="keywordtype">int</span>) value1, value2 );
<span class="keywordflow">break</span>;
<span class="keywordflow">case</span> __SK_AfterTouch_:
data-&gt;voicer.controlChange( 128, value1 );
<span class="keywordflow">case</span> __SK_PitchChange_:
data-&gt;voicer.setFrequency( value1 );
<span class="keywordflow">break</span>;
<span class="keywordflow">case</span> __SK_PitchBend_:
data-&gt;voicer.pitchBend( value1 );
} <span class="comment">// end of switch</span>
data-&gt;haveMessage = <span class="keyword">false</span>;
<span class="keywordflow">return</span>;
}
<span class="comment">// This tick() function handles sample computation and scheduling of</span>
<span class="comment">// control updates. It will be called automatically when the system</span>
<span class="comment">// needs a new buffer of audio samples.</span>
<span class="keywordtype">int</span> tick(<span class="keywordtype">char</span> *buffer, <span class="keywordtype">int</span> bufferSize, <span class="keywordtype">void</span> *dataPointer)
{
TickData *data = (TickData *) dataPointer;
<span class="keyword">register</span> StkFloat *samples = (StkFloat *) buffer;
<span class="keywordtype">int</span> counter, nTicks = bufferSize;
<span class="keywordflow">while</span> ( nTicks &gt; 0 &amp;&amp; !data-&gt;done ) {
<span class="keywordflow">if</span> ( !data-&gt;haveMessage ) {
data-&gt;messager.popMessage( data-&gt;message );
<span class="keywordflow">if</span> ( data-&gt;message.type &gt; 0 ) {
data-&gt;counter = (<span class="keywordtype">long</span>) (data-&gt;message.time * <a class="code" href="classStk.html#e0">Stk::sampleRate</a>());
data-&gt;haveMessage = <span class="keyword">true</span>;
}
<span class="keywordflow">else</span>
data-&gt;counter = DELTA_CONTROL_TICKS;
}
counter = min( nTicks, data-&gt;counter );
data-&gt;counter -= counter;
<span class="keywordflow">for</span> ( <span class="keywordtype">int</span> i=0; i&lt;counter; i++ ) {
*samples++ = data-&gt;voicer.tick();
nTicks--;
}
<span class="keywordflow">if</span> ( nTicks == 0 ) <span class="keywordflow">break</span>;
<span class="comment">// Process control messages.</span>
<span class="keywordflow">if</span> ( data-&gt;haveMessage ) processMessage( data );
}
<span class="keywordflow">return</span> 0;
}
<span class="keywordtype">int</span> main()
{
<span class="comment">// Set the global sample rate before creating class instances.</span>
<span class="comment">// Set the global sample rate and rawwave path before creating class instances.</span>
<a class="code" href="classStk.html#e1">Stk::setSampleRate</a>( 44100.0 );
<a class="code" href="classStk.html#e3">Stk::setRawwavePath</a>( <span class="stringliteral">"../../rawwaves/"</span> );
<span class="keywordtype">int</span> i;
<a class="code" href="classRtWvOut.html">RtWvOut</a> *output = 0;
<a class="code" href="classMessager.html">Messager</a> *messager = 0;
<a class="code" href="classVoicer.html">Voicer</a> *voicer = 0;
<span class="keywordtype">bool</span> done = FALSE;
TickData data;
RtAudio *dac = 0;
<a class="code" href="classInstrmnt.html">Instrmnt</a> *instrument[3];
<span class="keywordflow">for</span> ( i=0; i&lt;3; i++ ) instrument[i] = 0;
<span class="comment">// Figure out how many bytes in an StkFloat and setup the RtAudio object.</span>
RtAudioFormat format = ( <span class="keyword">sizeof</span>(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32;
<span class="keywordtype">int</span> bufferSize = RT_BUFFER_SIZE;
<span class="keywordflow">try</span> {
dac = <span class="keyword">new</span> RtAudio(0, 1, 0, 0, format, (<span class="keywordtype">int</span>)Stk::sampleRate(), &amp;bufferSize, 4);
}
<span class="keywordflow">catch</span> (<a class="code" href="classRtError.html">RtError</a>&amp; error) {
error.printMessage();
<span class="keywordflow">goto</span> cleanup;
}
<span class="keywordflow">try</span> {
<span class="comment">// Define and load the BeeThree instruments</span>
<span class="keywordflow">for</span> ( i=0; i&lt;3; i++ )
instrument[i] = <span class="keyword">new</span> <a class="code" href="classBeeThree.html">BeeThree</a>();
<span class="comment">// Define and open the default realtime output device for one-channel playback</span>
output = <span class="keyword">new</span> <a class="code" href="classRtWvOut.html">RtWvOut</a>(1);
}
<span class="keywordflow">catch</span> (<a class="code" href="classStkError.html">StkError</a> &amp;) {
<span class="keywordflow">goto</span> cleanup;
}
<span class="comment">// "Add" the instruments to the voicer.</span>
<span class="keywordflow">for</span> ( i=0; i&lt;3; i++ )
data.voicer.addInstrument( instrument[i] );
<span class="keywordflow">if</span> ( data.messager.startStdInput() == <span class="keyword">false</span> )
<span class="keywordflow">goto</span> cleanup;
<span class="keywordflow">try</span> {
<span class="comment">// Create a Messager instance to read from a redirected SKINI scorefile.</span>
messager = <span class="keyword">new</span> <a class="code" href="classMessager.html">Messager</a>();
dac-&gt;<a class="code" href="classRtAudio.html#a6">setStreamCallback</a>(&amp;tick, (<span class="keywordtype">void</span> *)&amp;data);
dac-&gt;<a class="code" href="classRtAudio.html#a13">startStream</a>();
}
<span class="keywordflow">catch</span> (<a class="code" href="classStkError.html">StkError</a> &amp;) {
<span class="keywordflow">catch</span> (<a class="code" href="classRtError.html">RtError</a> &amp;error) {
error.printMessage();
<span class="keywordflow">goto</span> cleanup;
}
<span class="comment">// Instantiate the voicer for a maximum of three voices.</span>
voicer = <span class="keyword">new</span> <a class="code" href="classVoicer.html">Voicer</a>( 3 );
<span class="keywordflow">for</span> ( i=0; i&lt;3; i++ )
voicer-&gt;<a class="code" href="classVoicer.html#a2">addInstrument</a>( instrument[i] );
<span class="comment">// Play the instrument until the end of the scorefile.</span>
<span class="keywordtype">int</span> nTicks, type;
MY_FLOAT byte2, byte3;
<span class="keywordflow">while</span> (!done) {
<span class="comment">// Look for new messages and return a delta time (in samples).</span>
type = messager-&gt;<a class="code" href="classMessager.html#a2">nextMessage</a>();
<span class="keywordflow">if</span> (type &lt; 0)
done = TRUE;
nTicks = messager-&gt;<a class="code" href="classMessager.html#a4">getDelta</a>();
<span class="keywordflow">try</span> {
<span class="keywordflow">for</span> ( i=0; i&lt;nTicks; i++ )
output-&gt;<a class="code" href="classRtWvOut.html#a6">tick</a>( voicer-&gt;<a class="code" href="classVoicer.html#a14">tick</a>() );
}
<span class="keywordflow">catch</span> (<a class="code" href="classStkError.html">StkError</a> &amp;) {
<span class="keywordflow">goto</span> cleanup;
}
<span class="keywordflow">if</span> ( type &gt; 0 ) {
<span class="comment">// Process the new control message.</span>
byte2 = messager-&gt;<a class="code" href="classMessager.html#a6">getByteTwo</a>();
byte3 = messager-&gt;<a class="code" href="classMessager.html#a7">getByteThree</a>();
<span class="keywordflow">switch</span>(type) {
<span class="keywordflow">case</span> __SK_NoteOn_:
voicer-&gt;<a class="code" href="classVoicer.html#a4">noteOn</a>( byte2, byte3 );
<span class="keywordflow">break</span>;
<span class="keywordflow">case</span> __SK_NoteOff_:
voicer-&gt;<a class="code" href="classVoicer.html#a5">noteOff</a>( byte2, byte3 );
<span class="keywordflow">break</span>;
<span class="keywordflow">case</span> __SK_ControlChange_:
voicer-&gt;<a class="code" href="classVoicer.html#a11">controlChange</a>( (<span class="keywordtype">int</span>) byte2, byte3 );
<span class="keywordflow">break</span>;
<span class="keywordflow">case</span> __SK_AfterTouch_:
voicer-&gt;<a class="code" href="classVoicer.html#a11">controlChange</a>( 128, byte2 );
<span class="keywordflow">break</span>;
}
}
<span class="comment">// Block waiting until callback signals done.</span>
<span class="keywordflow">while</span> ( !data.done )
<a class="code" href="classStk.html#e7">Stk::sleep</a>( 100 );
<span class="comment">// Shut down the callback and output stream.</span>
<span class="keywordflow">try</span> {
dac-&gt;<a class="code" href="classRtAudio.html#a7">cancelStreamCallback</a>();
dac-&gt;<a class="code" href="classRtAudio.html#a12">closeStream</a>();
}
<span class="keywordflow">catch</span> (<a class="code" href="classRtError.html">RtError</a> &amp;error) {
error.printMessage();
}
cleanup:
<span class="keywordflow">for</span> ( i=0; i&lt;3; i++ ) <span class="keyword">delete</span> instrument[i];
<span class="keyword">delete</span> output;
<span class="keyword">delete</span> messager;
<span class="keyword">delete</span> voicer;
<span class="keyword">delete</span> dac;
<span class="keywordflow">return</span> 0;
}
</pre></div><p>
Assuming the program is compiled as <code>threebees</code>, the three-voice <a class="el" href="classSKINI.html">SKINI</a> scorefile <a href="tutorial/bachfugue.ski"><code>bachfugue.ski</code></a> (also located in the <code>scores</code> directory with the examples) could be redirected to the program as:<p>
<div class="fragment"><pre>threebees &lt; bachfugue.ski
We have written this program to accept control messages from <code>STDIN</code>. Assuming the program is compiled as <code>threebees</code>, the three-voice SKINI scorefile <a href="tutorial/bachfugue.ski"><code>bachfugue.ski</code></a> (located in the <code>scores</code> directory with the examples) can be redirected to the program as:<p>
<div class="fragment"><pre>threebees &lt; scores/bachfugue.ski
</pre></div><p>
For more fun, surf to <a href="http://kern.humdrum.net/">Kern Scores</a> for a huge assortment of other scorefiles which can be downloaded in the <a class="el" href="classSKINI.html">SKINI</a> format.<p>
Another easy extension would be to use the <code>STK_MIDI</code> constructor argument to the <a class="el" href="classMessager.html">Messager</a> class and then play the instruments via a MIDI keyboard.<p>
For more fun, surf to <a href="http://kern.humdrum.net/">Kern Scores</a> for a huge assortment of other scorefiles which can be downloaded in the SKINI format.<p>
Another easy extension would be to add the <code>Messager::startMidiInput()</code> function to the program and then play the instruments via a MIDI keyboard.<p>
[<a href="tutorial.html">Main tutorial page</a>] <HR>
<table>
<tr><td><A HREF="http://www-ccrma.stanford.edu/software/stk/"><I>The Synthesis ToolKit in C++ (STK)</I></A></td></tr>
<tr><td><A HREF="http://ccrma.stanford.edu/software/stk/"><I>The Synthesis ToolKit in C++ (STK)</I></A></td></tr>
<tr><td>&copy;1995-2004 Perry R. Cook and Gary P. Scavone. All Rights Reserved.</td></tr>
</table>