/[svn]/linuxsampler/trunk/src/voice.cpp
ViewVC logotype

Annotation of /linuxsampler/trunk/src/voice.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 31 - (hide annotations) (download)
Sun Jan 18 20:31:31 2004 UTC (20 years, 4 months ago) by schoenebeck
File size: 12404 byte(s)
* Added JACK support: Audio rendering process is now callback based and
  independant of used audio output system. Interfaces to other audio output
  systems can be added by creating a class derived from abstract base class
  'AudioIO' and embedding the new class into linuxsampler.cpp.
* src/audiothread.cpp: applied patch from Vladimir Senkov which fixes
  hanging notes in conjunction with the sustain pedal

1 schoenebeck 9 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5     * Copyright (C) 2003 by Benno Senoner and Christian Schoenebeck *
6     * *
7     * This program is free software; you can redistribute it and/or modify *
8     * it under the terms of the GNU General Public License as published by *
9     * the Free Software Foundation; either version 2 of the License, or *
10     * (at your option) any later version. *
11     * *
12     * This program is distributed in the hope that it will be useful, *
13     * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15     * GNU General Public License for more details. *
16     * *
17     * You should have received a copy of the GNU General Public License *
18     * along with this program; if not, write to the Free Software *
19     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
20     * MA 02111-1307 USA *
21     ***************************************************************************/
22    
23     #include "voice.h"
24    
25     // FIXME: no support for layers (nor crossfades) yet
26    
27     DiskThread* Voice::pDiskThread = NULL;
28    
29     Voice::Voice(DiskThread* pDiskThread) {
30     Active = false;
31     Voice::pDiskThread = pDiskThread;
32     }
33    
34     Voice::~Voice() {
35     }
36    
37 schoenebeck 18 /**
38     * Initializes and triggers the voice, a disk stream will be launched if
39     * needed.
40     *
41     * @returns 0 on success, a value < 0 if something failed
42     */
43     int Voice::Trigger(int MIDIKey, uint8_t Velocity, gig::Instrument* Instrument) {
44 schoenebeck 15 Active = true;
45     this->MIDIKey = MIDIKey;
46     pRegion = Instrument->GetRegion(MIDIKey);
47     PlaybackState = playback_state_ram; // we always start playback from RAM cache and switch then to disk if needed
48     Pos = 0;
49     ReleaseVelocity = 127; // default release velocity value
50 schoenebeck 9
51     if (!pRegion) {
52     std::cerr << "Audio Thread: No Region defined for MIDI key " << MIDIKey << std::endl << std::flush;
53 schoenebeck 18 Kill();
54     return -1;
55 schoenebeck 9 }
56    
57     //TODO: current MIDI controller values are not taken into account yet
58     gig::DimensionRegion* pDimRgn = NULL;
59     for (int i = pRegion->Dimensions - 1; i >= 0; i--) { // Check if instrument has a velocity split
60     if (pRegion->pDimensionDefinitions[i].dimension == gig::dimension_velocity) {
61     uint DimValues[5] = {0,0,0,0,0};
62     DimValues[i] = Velocity;
63     pDimRgn = pRegion->GetDimensionRegionByValue(DimValues[4],DimValues[3],DimValues[2],DimValues[1],DimValues[0]);
64     break;
65     }
66     }
67     if (!pDimRgn) { // if there was no velocity split
68     pDimRgn = pRegion->GetDimensionRegionByValue(0,0,0,0,0);
69     }
70    
71     pSample = pDimRgn->pSample; // sample won't change until the voice is finished
72    
73     // Check if the sample needs disk streaming or is too short for that
74     long cachedsamples = pSample->GetCache().Size / pSample->FrameSize;
75     DiskVoice = cachedsamples < pSample->SamplesTotal;
76    
77 schoenebeck 26 if (DiskVoice) { // voice to be streamed from disk
78 schoenebeck 31 MaxRAMPos = cachedsamples - (MaxSamplesPerCycle << MAX_PITCH) / pSample->Channels;
79 schoenebeck 26
80     // check if there's a loop defined which completely fits into the cached (RAM) part of the sample
81     if (pSample->Loops && pSample->LoopEnd <= MaxRAMPos) {
82     RAMLoop = true;
83     LoopCyclesLeft = pSample->LoopPlayCount;
84     }
85     else RAMLoop = false;
86    
87     if (pDiskThread->OrderNewStream(&DiskStreamRef, pSample, MaxRAMPos, !RAMLoop) < 0) {
88 schoenebeck 18 dmsg(1,("Disk stream order failed!\n"));
89     Kill();
90     return -1;
91     }
92 schoenebeck 31 dmsg(4,("Disk voice launched (cached samples: %d, total Samples: %d, MaxRAMPos: %d, RAMLooping: %s)\n", cachedsamples, pSample->SamplesTotal, MaxRAMPos, (RAMLoop) ? "yes" : "no"));
93 schoenebeck 9 }
94 schoenebeck 26 else { // RAM only voice
95 schoenebeck 9 MaxRAMPos = cachedsamples;
96 schoenebeck 26 if (pSample->Loops) {
97     RAMLoop = true;
98     LoopCyclesLeft = pSample->LoopPlayCount;
99     }
100     else RAMLoop = false;
101     dmsg(4,("RAM only voice launched (Looping: %s)\n", (RAMLoop) ? "yes" : "no"));
102 schoenebeck 9 }
103    
104    
105 schoenebeck 30 // Pitch according to keyboard position (if 'PitchTrack' is set)
106     CurrentPitch = (pDimRgn->PitchTrack) ? pow(2, ((double) (MIDIKey - (int) pDimRgn->UnityNote) + (double) pDimRgn->FineTune / 100.0) / 12.0)
107     : pow(2, ((double) pDimRgn->FineTune / 100.0) / 12.0);
108    
109     Volume = pDimRgn->GetVelocityAttenuation(Velocity);
110    
111     EG1.Trigger(pDimRgn->EG1PreAttack, pDimRgn->EG1Attack, pDimRgn->EG1Release);
112    
113 schoenebeck 9 // ************************************************
114     // TODO: ARTICULATION DATA HANDLING IS MISSING HERE
115     // ************************************************
116 schoenebeck 18
117     return 0; // success
118 schoenebeck 9 }
119    
120 schoenebeck 26 /**
121     * Renders the audio data for this voice for the current audio fragment.
122     * The sample input data can either come from RAM (cached sample or sample
123     * part) or directly from disk. The output signal will be rendered by
124     * resampling / interpolation. If this voice is a disk streaming voice and
125     * the voice completely played back the cached RAM part of the sample, it
126     * will automatically switch to disk playback for the next RenderAudio()
127     * call.
128     */
129 schoenebeck 31 void Voice::Render(uint Samples) {
130 schoenebeck 9
131 schoenebeck 30 // Let all modulators throw their parameter changes for the current audio fragment
132     ModulationSystem::ResetDestinationParameter(ModulationSystem::destination_vca, this->Volume);
133 schoenebeck 31 EG1.Process(Samples);
134 schoenebeck 30
135    
136 schoenebeck 9 switch (this->PlaybackState) {
137    
138     case playback_state_ram: {
139 schoenebeck 31 if (RAMLoop) InterpolateAndLoop(Samples, (sample_t*) pSample->GetCache().pStart);
140     else Interpolate(Samples, (sample_t*) pSample->GetCache().pStart);
141 schoenebeck 9 if (DiskVoice) {
142     // check if we reached the allowed limit of the sample RAM cache
143     if (Pos > MaxRAMPos) {
144 schoenebeck 12 dmsg(5,("Voice: switching to disk playback (Pos=%f)\n", Pos));
145 schoenebeck 9 this->PlaybackState = playback_state_disk;
146     }
147     }
148     else if (Pos >= pSample->GetCache().Size / pSample->FrameSize) {
149     this->PlaybackState = playback_state_end;
150     }
151     }
152     break;
153    
154     case playback_state_disk: {
155     if (!DiskStreamRef.pStream) {
156     // check if the disk thread created our ordered disk stream in the meantime
157     DiskStreamRef.pStream = pDiskThread->AskForCreatedStream(DiskStreamRef.OrderID);
158     if (!DiskStreamRef.pStream) {
159     std::cout << stderr << "Disk stream not available in time!" << std::endl << std::flush;
160 schoenebeck 18 Kill();
161 schoenebeck 9 return;
162     }
163     DiskStreamRef.pStream->IncrementReadPos(pSample->Channels * (double_to_int(Pos) - MaxRAMPos));
164     Pos -= double_to_int(Pos);
165     }
166    
167     // add silence sample at the end if we reached the end of the stream (for the interpolator)
168 schoenebeck 31 if (DiskStreamRef.State == Stream::state_end && DiskStreamRef.pStream->GetReadSpace() < (MaxSamplesPerCycle << MAX_PITCH) / pSample->Channels) {
169     DiskStreamRef.pStream->WriteSilence((MaxSamplesPerCycle << MAX_PITCH) / pSample->Channels);
170 schoenebeck 9 this->PlaybackState = playback_state_end;
171     }
172    
173     sample_t* ptr = DiskStreamRef.pStream->GetReadPtr(); // get the current read_ptr within the ringbuffer where we read the samples from
174 schoenebeck 31 Interpolate(Samples, ptr);
175 schoenebeck 9 DiskStreamRef.pStream->IncrementReadPos(double_to_int(Pos) * pSample->Channels);
176     Pos -= double_to_int(Pos);
177     }
178     break;
179    
180     case playback_state_end:
181 schoenebeck 13 Kill(); // free voice
182 schoenebeck 9 break;
183     }
184 schoenebeck 30
185     // If release stage finished, let the voice be killed
186     if (EG1.GetStage() == EG_VCA::stage_end) this->PlaybackState = playback_state_end;
187 schoenebeck 9 }
188    
189 schoenebeck 26 /**
190     * Interpolates the input audio data (no loop).
191     *
192     * @param pSrc - pointer to input sample data
193     */
194 schoenebeck 31 void Voice::Interpolate(uint Samples, sample_t* pSrc) {
195 schoenebeck 30 int i = 0;
196 schoenebeck 9
197     // FIXME: assuming either mono or stereo
198     if (this->pSample->Channels == 2) { // Stereo Sample
199 schoenebeck 31 while (i < Samples) {
200     InterpolateOneStep_Stereo(pSrc, i, ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i]);
201 schoenebeck 9 }
202     }
203     else { // Mono Sample
204 schoenebeck 31 while (i < Samples) {
205     InterpolateOneStep_Mono(pSrc, i, ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i]);
206 schoenebeck 26 }
207     }
208     }
209 schoenebeck 18
210 schoenebeck 26 /**
211     * Interpolates the input audio data, this method honors looping.
212     *
213     * @param pSrc - pointer to input sample data
214     */
215 schoenebeck 31 void Voice::InterpolateAndLoop(uint Samples, sample_t* pSrc) {
216 schoenebeck 30 int i = 0;
217 schoenebeck 9
218 schoenebeck 26 // FIXME: assuming either mono or stereo
219     if (pSample->Channels == 2) { // Stereo Sample
220     if (pSample->LoopPlayCount) {
221     // render loop (loop count limited)
222 schoenebeck 31 while (i < Samples && LoopCyclesLeft) {
223     InterpolateOneStep_Stereo(pSrc, i, ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i]);
224 schoenebeck 26 if (Pos > pSample->LoopEnd) {
225     Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);;
226     LoopCyclesLeft--;
227     }
228     }
229     // render on without loop
230 schoenebeck 31 while (i < Samples) {
231     InterpolateOneStep_Stereo(pSrc, i, ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i]);
232 schoenebeck 26 }
233 schoenebeck 9 }
234 schoenebeck 26 else { // render loop (endless loop)
235 schoenebeck 31 while (i < Samples) {
236     InterpolateOneStep_Stereo(pSrc, i, ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i]);
237 schoenebeck 26 if (Pos > pSample->LoopEnd) {
238     Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);
239     }
240     }
241     }
242 schoenebeck 9 }
243 schoenebeck 26 else { // Mono Sample
244     if (pSample->LoopPlayCount) {
245     // render loop (loop count limited)
246 schoenebeck 31 while (i < Samples && LoopCyclesLeft) {
247     InterpolateOneStep_Mono(pSrc, i, ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i]);
248 schoenebeck 26 if (Pos > pSample->LoopEnd) {
249     Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);;
250     LoopCyclesLeft--;
251     }
252     }
253     // render on without loop
254 schoenebeck 31 while (i < Samples) {
255     InterpolateOneStep_Mono(pSrc, i, ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i]);
256 schoenebeck 26 }
257     }
258     else { // render loop (endless loop)
259 schoenebeck 31 while (i < Samples) {
260     InterpolateOneStep_Mono(pSrc, i, ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i]);
261 schoenebeck 26 if (Pos > pSample->LoopEnd) {
262     Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);;
263     }
264     }
265     }
266     }
267 schoenebeck 9 }
268    
269 schoenebeck 26 /**
270     * Immediately kill the voice.
271     */
272 schoenebeck 9 void Voice::Kill() {
273     if (DiskVoice && DiskStreamRef.State != Stream::state_unused) {
274     pDiskThread->OrderDeletionOfStream(&DiskStreamRef);
275     }
276 schoenebeck 18 DiskStreamRef.pStream = NULL;
277     DiskStreamRef.hStream = 0;
278     DiskStreamRef.State = Stream::state_unused;
279     DiskStreamRef.OrderID = 0;
280 schoenebeck 9 Active = false;
281     }
282 schoenebeck 30
283     /**
284     * Release the voice in an appropriate time range, the voice will go through
285     * it's release stage before it will be killed.
286     */
287     void Voice::Release() {
288     EG1.Release();
289     }

  ViewVC Help
Powered by ViewVC