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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 2014 by iliev, Fri Oct 23 17:53:17 2009 UTC revision 2015 by iliev, Sun Oct 25 22:22:52 2009 UTC
# Line 25  Line 25 
25  #ifndef __LS_VOICEBASE_H__  #ifndef __LS_VOICEBASE_H__
26  #define __LS_VOICEBASE_H__  #define __LS_VOICEBASE_H__
27    
28  #include "Voice.h"  #include "AbstractVoice.h"
29    
30  namespace LinuxSampler {  namespace LinuxSampler {
31    
32      template <class R>      template <class EC /* Engine Channel */, class R /* Region */, class S /* Sample */, class D /* DiskThread */>
33      class VoiceBase : public Voice {      class VoiceBase : public AbstractVoice {
34          public:          public:
35              virtual R* GetRegion() = 0;              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    
38                VoiceBase() {
39                    pRegion      = NULL;
40                    pDiskThread  = NULL;
41                }
42                virtual ~VoiceBase() { }
43    
     template <class R, class S>  
     class VoiceBaseImpl : public VoiceBase<R> {  
         public:  
44              virtual R* GetRegion() { return pRegion; }              virtual R* GetRegion() { return pRegion; }
45    
46                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                                        dmsg(5,("Voice: switching to disk playback (Pos=%f)\n", finalSynthesisParameters.dPos));
125                                        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                            std::cerr << "gig::Voice::Render(): entered with playback_state_end, this is a bug!\n" << std::flush;
179                            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                    if (
189                        PlaybackState == Voice::playback_state_end ||
190                        EG1.getSegmentType() == gig::EGADSR::segment_end
191                    ) KillImmediately();
192                }
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          protected:          protected:
227              R* pRegion;     ///< Pointer to the articulation information of current region of this voice              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      };      };
240  } // namespace LinuxSampler  } // namespace LinuxSampler
241    

Legend:
Removed from v.2014  
changed lines
  Added in v.2015

  ViewVC Help
Powered by ViewVC