/[svn]/linuxsampler/trunk/src/engines/common/VoiceBase.h
ViewVC logotype

Annotation of /linuxsampler/trunk/src/engines/common/VoiceBase.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2055 - (hide annotations) (download) (as text)
Sat Jan 30 10:30:02 2010 UTC (14 years, 2 months ago) by persson
File MIME type: text/x-c++hdr
File size: 13261 byte(s)
* sfz engine: added support for v2 multiple stage envelope generators
* sfz engine: added a fine-tuned v1 envelope generator instead of
  using the one from the gig engine

1 iliev 2012 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5     * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck *
6 persson 2055 * Copyright (C) 2005-2008 Christian Schoenebeck *
7     * Copyright (C) 2009-2010 Christian Schoenebeck and Grigor Iliev *
8 iliev 2012 * *
9     * This program is free software; you can redistribute it and/or modify *
10     * it under the terms of the GNU General Public License as published by *
11     * the Free Software Foundation; either version 2 of the License, or *
12     * (at your option) any later version. *
13     * *
14     * This program is distributed in the hope that it will be useful, *
15     * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17     * GNU General Public License for more details. *
18     * *
19     * You should have received a copy of the GNU General Public License *
20     * along with this program; if not, write to the Free Software *
21     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
22     * MA 02111-1307 USA *
23     ***************************************************************************/
24    
25     #ifndef __LS_VOICEBASE_H__
26     #define __LS_VOICEBASE_H__
27    
28 iliev 2015 #include "AbstractVoice.h"
29 iliev 2012
30     namespace LinuxSampler {
31    
32 iliev 2015 template <class EC /* Engine Channel */, class R /* Region */, class S /* Sample */, class D /* DiskThread */>
33     class VoiceBase : public AbstractVoice {
34 iliev 2012 public:
35 iliev 2015 D* pDiskThread; ///< Pointer to the disk thread, to be able to order a disk stream and later to delete the stream again
36     int RealSampleWordsLeftToRead; ///< Number of samples left to read, not including the silence added for the interpolator
37 iliev 2012
38 iliev 2015 VoiceBase() {
39     pRegion = NULL;
40     pDiskThread = NULL;
41     }
42     virtual ~VoiceBase() { }
43    
44 iliev 2012 virtual R* GetRegion() { return pRegion; }
45    
46 iliev 2015 virtual unsigned long GetSampleCacheSize() {
47     return pSample->GetCache().Size;
48     }
49    
50     /**
51     * Initializes and triggers the voice, a disk stream will be launched if
52     * needed.
53     *
54     * @param pEngineChannel - engine channel on which this voice was ordered
55     * @param itNoteOnEvent - event that caused triggering of this voice
56     * @param PitchBend - MIDI detune factor (-8192 ... +8191)
57     * @param pRegion - points to the region which provides sample wave(s) and articulation data
58     * @param VoiceType - type of this voice
59     * @param iKeyGroup - a value > 0 defines a key group in which this voice is member of
60     * @returns 0 on success, a value < 0 if the voice wasn't triggered
61     * (either due to an error or e.g. because no region is
62     * defined for the given key)
63     */
64     virtual int Trigger (
65     AbstractEngineChannel* pEngineChannel,
66     Pool<Event>::Iterator& itNoteOnEvent,
67     int PitchBend,
68     R* pRegion,
69     type_t VoiceType,
70     int iKeyGroup
71     ) {
72     this->pRegion = pRegion;
73     this->pSample = pRegion->pSample; // sample won't change until the voice is finished
74    
75     return AbstractVoice::Trigger (
76     pEngineChannel, itNoteOnEvent, PitchBend, VoiceType, iKeyGroup
77     );
78     }
79    
80     virtual int OrderNewStream() {
81     int res = pDiskThread->OrderNewStream (
82     &DiskStreamRef, pRegion, MaxRAMPos, !RAMLoop
83     );
84    
85     if (res < 0) {
86     dmsg(1,("Disk stream order failed!\n"));
87     KillImmediately();
88     return -1;
89     }
90    
91     return 0;
92     }
93    
94     /**
95     * Renders the audio data for this voice for the current audio fragment.
96     * The sample input data can either come from RAM (cached sample or sample
97     * part) or directly from disk. The output signal will be rendered by
98     * resampling / interpolation. If this voice is a disk streaming voice and
99     * the voice completely played back the cached RAM part of the sample, it
100     * will automatically switch to disk playback for the next RenderAudio()
101     * call.
102     *
103     * @param Samples - number of samples to be rendered in this audio fragment cycle
104     */
105     void Render(uint Samples) {
106     // select default values for synthesis mode bits
107     SYNTHESIS_MODE_SET_LOOP(SynthesisMode, false);
108    
109     switch (this->PlaybackState) {
110    
111     case Voice::playback_state_init:
112     this->PlaybackState = Voice::playback_state_ram; // we always start playback from RAM cache and switch then to disk if needed
113     // no break - continue with playback_state_ram
114    
115     case Voice::playback_state_ram: {
116     if (RAMLoop) SYNTHESIS_MODE_SET_LOOP(SynthesisMode, true); // enable looping
117    
118     // render current fragment
119     Synthesize(Samples, (sample_t*) pSample->GetCache().pStart, Delay);
120    
121     if (DiskVoice) {
122     // check if we reached the allowed limit of the sample RAM cache
123     if (finalSynthesisParameters.dPos > MaxRAMPos) {
124 iliev 2027 dmsg(5,("VoiceBase: switching to disk playback (Pos=%f)\n", finalSynthesisParameters.dPos));
125 iliev 2015 this->PlaybackState = Voice::playback_state_disk;
126     }
127     } else if (finalSynthesisParameters.dPos >= pSample->GetCache().Size / SmplInfo.FrameSize) {
128     this->PlaybackState = Voice::playback_state_end;
129     }
130     }
131     break;
132    
133     case Voice::playback_state_disk: {
134     if (!DiskStreamRef.pStream) {
135     // check if the disk thread created our ordered disk stream in the meantime
136     DiskStreamRef.pStream = pDiskThread->AskForCreatedStream(DiskStreamRef.OrderID);
137     if (!DiskStreamRef.pStream) {
138     std::cout << stderr << "Disk stream not available in time!" << std::endl << std::flush;
139     KillImmediately();
140     return;
141     }
142     DiskStreamRef.pStream->IncrementReadPos(SmplInfo.ChannelCount * (int(finalSynthesisParameters.dPos) - MaxRAMPos));
143     finalSynthesisParameters.dPos -= int(finalSynthesisParameters.dPos);
144     RealSampleWordsLeftToRead = -1; // -1 means no silence has been added yet
145     }
146    
147     const int sampleWordsLeftToRead = DiskStreamRef.pStream->GetReadSpace();
148    
149     // add silence sample at the end if we reached the end of the stream (for the interpolator)
150     if (DiskStreamRef.State == Stream::state_end) {
151     const int maxSampleWordsPerCycle = (GetEngine()->MaxSamplesPerCycle << CONFIG_MAX_PITCH) * SmplInfo.ChannelCount + 6; // +6 for the interpolator algorithm
152     if (sampleWordsLeftToRead <= maxSampleWordsPerCycle) {
153     // remember how many sample words there are before any silence has been added
154     if (RealSampleWordsLeftToRead < 0) RealSampleWordsLeftToRead = sampleWordsLeftToRead;
155     DiskStreamRef.pStream->WriteSilence(maxSampleWordsPerCycle - sampleWordsLeftToRead);
156     }
157     }
158    
159     sample_t* ptr = (sample_t*)DiskStreamRef.pStream->GetReadPtr(); // get the current read_ptr within the ringbuffer where we read the samples from
160    
161     // render current audio fragment
162     Synthesize(Samples, ptr, Delay);
163    
164     const int iPos = (int) finalSynthesisParameters.dPos;
165     const int readSampleWords = iPos * SmplInfo.ChannelCount; // amount of sample words actually been read
166     DiskStreamRef.pStream->IncrementReadPos(readSampleWords);
167     finalSynthesisParameters.dPos -= iPos; // just keep fractional part of playback position
168    
169     // change state of voice to 'end' if we really reached the end of the sample data
170     if (RealSampleWordsLeftToRead >= 0) {
171     RealSampleWordsLeftToRead -= readSampleWords;
172     if (RealSampleWordsLeftToRead <= 0) this->PlaybackState = Voice::playback_state_end;
173     }
174     }
175     break;
176    
177     case Voice::playback_state_end:
178 iliev 2027 std::cerr << "VoiceBase::Render(): entered with playback_state_end, this is a bug!\n" << std::flush;
179 iliev 2015 break;
180     }
181    
182     // Reset delay
183     Delay = 0;
184    
185     itTriggerEvent = Pool<Event>::Iterator();
186    
187     // If sample stream or release stage finished, kill the voice
188 persson 2055 if (PlaybackState == Voice::playback_state_end ||
189     pEG1->getSegmentType() == EG::segment_end) {
190     KillImmediately();
191     }
192 iliev 2015 }
193    
194     /**
195     * Immediately kill the voice. This method should not be used to kill
196     * a normal, active voice, because it doesn't take care of things like
197     * fading down the volume level to avoid clicks and regular processing
198     * until the kill event actually occured!
199     *
200     * If it's necessary to know when the voice's disk stream was actually
201     * deleted, then one can set the optional @a bRequestNotification
202     * parameter and this method will then return the handle of the disk
203     * stream (unique identifier) and one can use this handle to poll the
204     * disk thread if this stream has been deleted. In any case this method
205     * will return immediately and will not block until the stream actually
206     * was deleted.
207     *
208     * @param bRequestNotification - (optional) whether the disk thread shall
209     * provide a notification once it deleted
210     * the respective disk stream
211     * (default=false)
212     * @returns handle to the voice's disk stream or @c Stream::INVALID_HANDLE
213     * if the voice did not use a disk stream at all
214     * @see Kill()
215     */
216     Stream::Handle KillImmediately(bool bRequestNotification = false) {
217     Stream::Handle hStream = Stream::INVALID_HANDLE;
218     if (DiskVoice && DiskStreamRef.State != Stream::state_unused) {
219     pDiskThread->OrderDeletionOfStream(&DiskStreamRef, bRequestNotification);
220     hStream = DiskStreamRef.hStream;
221     }
222     Reset();
223     return hStream;
224     }
225    
226 iliev 2012 protected:
227 iliev 2015 S* pSample; ///< Pointer to the sample to be played back
228     R* pRegion; ///< Pointer to the articulation information of current region of this voice
229    
230     virtual unsigned long GetNoteOnTime(int MIDIKey) {
231     EC* pChannel = static_cast<EC*>(pEngineChannel);
232     return pChannel->pMIDIKeyInfo[MIDIKey].NoteOnTime;
233     }
234    
235     void GetFirstEventOnKey(uint8_t MIDIKey, RTList<Event>::Iterator& itEvent) {
236     EC* pChannel = static_cast<EC*>(pEngineChannel);
237     itEvent = pChannel->pMIDIKeyInfo[MIDIKey].pEvents->first();
238     }
239 iliev 2012 };
240     } // namespace LinuxSampler
241    
242     #endif /* __LS_VOICEBASE_H__ */
243    

  ViewVC Help
Powered by ViewVC