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

Contents of /linuxsampler/trunk/src/audiothread.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 10 - (show annotations) (download)
Tue Nov 11 23:30:47 2003 UTC (20 years, 5 months ago) by senoner
File size: 10511 byte(s)
* src/audiothread.cpp, src/audiothread.h: added Sustain Pedal support
  implemented by postponing note-offs and leting multiple voices play
  on the same MIDI key.
* added the RTELMemoryPool Class which is a fast RT-safe memory allocator
  and list manger
* src/linuxsampler.cpp: added a voice and stream counter debug message

1 /***************************************************************************
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 "audiothread.h"
24
25 AudioThread::AudioThread(AudioIO* pAudioIO, DiskThread* pDiskThread, gig::Instrument* pInstrument) : Thread(true, 1, 0) {
26 this->pAudioIO = pAudioIO;
27 this->pDiskThread = pDiskThread;
28 this->pInstrument = pInstrument;
29 pCommandQueue = new RingBuffer<command_t>(1024);
30 pVoices = new Voice*[MAX_AUDIO_VOICES];
31 // allocate the ActiveVoicePool (for each midi key there is a variable size linked list
32 // of pointers to Voice classes)
33 ActiveVoicePool=new RTELMemoryPool<Voice *>(MAX_AUDIO_VOICES);
34 for (uint i = 0; i < MAX_AUDIO_VOICES; i++) {
35 pVoices[i] = new Voice(pDiskThread);
36 }
37 for (uint i = 0; i < 128; i++) {
38 pActiveVoices[i] = new RTEList<Voice *>;
39 }
40
41 SustainedKeyPool=new RTELMemoryPool<sustained_key_t>(200);
42
43
44
45 pAudioSumBuffer = new float[pAudioIO->FragmentSize * pAudioIO->Channels];
46
47 // set all voice outputs to the AudioSumBuffer
48 for (int i = 0; i < MAX_AUDIO_VOICES; i++) {
49 pVoices[i]->SetOutput(pAudioSumBuffer, pAudioIO->FragmentSize * 2); //FIXME: assuming stereo
50 }
51
52 // cache initial samples points (for actually needed samples)
53 dmsg(("Caching initial samples..."));
54 gig::Region* pRgn = this->pInstrument->GetFirstRegion();
55 while (pRgn) {
56 if (!pRgn->GetSample()->GetCache().Size) {
57 //printf("C");
58 CacheInitialSamples(pRgn->GetSample());
59 }
60 for (uint i = 0; i < pRgn->DimensionRegions; i++) {
61 CacheInitialSamples(pRgn->pDimensionRegions[i]->pSample);
62 }
63
64 pRgn = this->pInstrument->GetNextRegion();
65 }
66
67 // sustain pedal value
68 PrevHoldCCValue=0;
69 SustainPedal=0;
70
71 dmsg(("OK\n"));
72 }
73
74 AudioThread::~AudioThread() {
75 if (pCommandQueue) delete pCommandQueue;
76 if (pVoices) {
77 for (uint i = 0; i < MAX_AUDIO_VOICES; i++) {
78 if (pVoices[i]) delete pVoices[i];
79 }
80 }
81 delete[] pVoices;
82 }
83
84 int AudioThread::Main() {
85 dmsg(("Audio thread running\n"));
86 //int fifofd=open("/tmp/fifo1",O_WRONLY);
87
88 while (true) {
89
90 // read and process commands from the queue
91 while (true) {
92 command_t command;
93 if (pCommandQueue->read(&command, 1) == 0) break;
94
95 switch (command.type) {
96 case command_type_note_on:
97 dmsg(("Audio Thread: Note on received\n"));
98 ActivateVoice(command.pitch, command.velocity);
99 break;
100 case command_type_note_off:
101 dmsg(("Audio Thread: Note off received\n"));
102 ReleaseVoice(command.pitch, command.velocity);
103 break;
104 case command_type_continuous_controller:
105 dmsg(("Audio Thread: MIDI CC received\n"));
106 ContinuousController(command.channel, command.number, command.value);
107 break;
108 }
109 }
110
111
112 // zero out the sum buffer
113 for (uint u = 0; u < pAudioIO->FragmentSize * pAudioIO->Channels; u++) {
114 pAudioSumBuffer[u] = 0.0;
115 }
116
117
118 // render audio from all active voices
119 int act_voices=0;
120 for (uint i = 0; i < MAX_AUDIO_VOICES; i++) {
121 if (pVoices[i]->IsActive()) {
122 pVoices[i]->RenderAudio();
123 act_voices++;
124 }
125 }
126 // write that to the disk thread class so that it can print it
127 // on the console for debugging purposes
128 ActiveVoiceCount=act_voices;
129
130
131 // check clipping in the audio sum, convert to sample_type
132 // (from 32bit to 16bit sample) and copy to output buffer
133 float sample_point;
134 for (uint u = 0; u < pAudioIO->FragmentSize * pAudioIO->Channels; u++) {
135 sample_point = this->pAudioSumBuffer[u] / 4; // FIXME division by 4 just for testing purposes (to give a bit of head room when mixing multiple voices together)
136 if (sample_point < -32768.0) sample_point = -32768.0;
137 if (sample_point > 32767.0) sample_point = 32767.0;
138 this->pAudioIO->pOutputBuffer[u] = (sample_t) sample_point;
139 }
140
141
142 // call audio driver to output sound
143 int res = this->pAudioIO->Output();
144 if (res < 0) exit(EXIT_FAILURE);
145
146 // FIXME remove because we use it only to write to a fifo to save the audio
147 //write(fifofd, pAudioIO->pOutputBuffer, pAudioIO->FragmentSize * pAudioIO->Channels * sizeof(short));
148
149 }
150 }
151
152 /// Will be called by the MIDIIn Thread to let the audio thread trigger a new voice.
153 void AudioThread::ProcessNoteOn(uint8_t Pitch, uint8_t Velocity) {
154 command_t cmd;
155 cmd.type = command_type_note_on;
156 cmd.pitch = Pitch;
157 cmd.velocity = Velocity;
158 this->pCommandQueue->write(&cmd, 1);
159 }
160
161 /// Will be called by the MIDIIn Thread to signal the audio thread to release a voice.
162 void AudioThread::ProcessNoteOff(uint8_t Pitch, uint8_t Velocity) {
163 command_t cmd;
164 cmd.type = command_type_note_off;
165 cmd.pitch = Pitch;
166 cmd.velocity = Velocity;
167 this->pCommandQueue->write(&cmd, 1);
168 }
169
170 // Will be called by the MIDIIn Thead to send MIDI continuos controller events
171 void AudioThread::ProcessContinuousController(uint8_t Channel, uint8_t Number, uint8_t Value) {
172 command_t cmd;
173 cmd.type = command_type_continuous_controller;
174 cmd.channel = Channel;
175 cmd.number = Number;
176 cmd.value = Value;
177 this->pCommandQueue->write(&cmd, 1);
178 }
179
180
181 void AudioThread::ActivateVoice(uint8_t MIDIKey, uint8_t Velocity) {
182 for (int i = 0; i < MAX_AUDIO_VOICES; i++) {
183 if (pVoices[i]->IsActive()) continue;
184 pVoices[i]->Trigger(MIDIKey, Velocity, this->pInstrument);
185 // add (append) a new voice to the corresponding MIDIKey active voices list
186 Voice **new_voice_ptr=ActiveVoicePool->alloc_append(pActiveVoices[MIDIKey]);
187 *new_voice_ptr=pVoices[i];
188 return;
189 }
190 std::cerr << "No free voice!" << std::endl << std::flush;
191 }
192
193 void AudioThread::ReleaseVoice(uint8_t MIDIKey, uint8_t Velocity) {
194
195
196 // get the first voice in the list of active voices on the MIDI Key
197 Voice** pVoicePtr = pActiveVoices[MIDIKey]->first();
198 Voice *pVoice=*pVoicePtr;
199
200 if (pVoice) {
201
202 // if sustain pedal is pressed postpone the Note-Off
203 if(SustainPedal) {
204 // alloc an element in the SustainedKeyPool and add the current midikey to it
205 sustained_key_t *key=SustainedKeyPool->alloc();
206 if(key == NULL) { /* FIXME */ printf("ERROR: SustainedKeyPool FULL ! exiting\n"); exit(0); }
207 key->midikey=MIDIKey;
208 key->velocity=Velocity;
209 return;
210 }
211
212 pVoice->Kill(); //TODO: for now we're rude and just kill the poor, poor voice immediately :), later we add a Release() method to the Voice class and call it here to let the voice go through it's release phase
213
214 // remove the voice from the list associated to this MIDI key
215 ActiveVoicePool->free(pVoicePtr);
216 }
217 else std::cerr << "Couldn't find active voice for note off command!" << std::endl << std::flush;
218 }
219
220 void AudioThread::ContinuousController(uint8_t Channel, uint8_t Number, uint8_t Value) {
221 //printf("AudioThread::ContinuousController c=%d n=%d v=%d\n",Channel, Number, Value);
222 if(Number == 64) {
223 if(Value >=64 && PrevHoldCCValue < 64) {
224 //printf("PEDAL DOWN\n");
225 SustainPedal=1;
226 }
227 if(Value < 64 && PrevHoldCCValue >=64) {
228 //printf("PEDAL UP\n");
229 SustainPedal=0;
230 sustained_key_t *key;
231 for(key = SustainedKeyPool->first(); key ; key=SustainedKeyPool->next() ) {
232 ReleaseVoice(key->midikey, key->velocity);
233 }
234 // empty the SustainedKeyPool (free all the elements)
235 SustainedKeyPool->empty();
236
237
238 }
239 PrevHoldCCValue=Value;
240 }
241
242 }
243
244
245 void AudioThread::CacheInitialSamples(gig::Sample* pSample) {
246 if (!pSample || pSample->GetCache().Size) return;
247 if (pSample->SamplesTotal <= NUM_RAM_PRELOAD_SAMPLES) {
248 // Sample is too short for disk streaming, so we load the whole
249 // sample into RAM and place 'pAudioIO->FragmentSize << MAX_PITCH'
250 // number of '0' samples (silence samples) behind the official buffer
251 // border, to allow the interpolator do it's work even at the end of
252 // the sample.
253 gig::buffer_t buf = pSample->LoadSampleDataWithNullSamplesExtension(pAudioIO->FragmentSize << MAX_PITCH);
254 dmsg(("Cached %d Bytes, %d silence bytes.\n", buf.Size, buf.NullExtensionSize));
255 }
256 else { // we only cache NUM_RAM_PRELOAD_SAMPLES and stream the other sample points from disk
257 pSample->LoadSampleData(NUM_RAM_PRELOAD_SAMPLES);
258 }
259
260 if (!pSample->GetCache().Size) std::cerr << "Unable to cache sample - maybe memory full!" << std::endl << std::flush;
261 }

  ViewVC Help
Powered by ViewVC