/[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 3725 - (show annotations) (download) (as text)
Thu Jan 23 18:45:00 2020 UTC (4 years, 2 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 14226 byte(s)
* Fixed playing noise at the end of a disk stream under certain
  conditions.

* Bumped version (2.1.1.svn42).

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

  ViewVC Help
Powered by ViewVC