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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3054 - (show annotations) (download) (as text)
Thu Dec 15 12:47:45 2016 UTC (7 years, 4 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 13776 byte(s)
* Fixed numerous compiler warnings.
* Bumped version (2.0.0.svn32).

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

  ViewVC Help
Powered by ViewVC