Chris@0
|
1 /* -*- c-basic-offset: 4 -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 A waveform viewer and audio annotation editor.
|
Chris@0
|
5 Chris Cannam, Queen Mary University of London, 2005
|
Chris@0
|
6
|
Chris@0
|
7 This is experimental software. Not for distribution.
|
Chris@0
|
8 */
|
Chris@0
|
9
|
Chris@0
|
10 /*
|
Chris@0
|
11 Based on trivial_sampler from the DSSI distribution
|
Chris@0
|
12 (by Chris Cannam, public domain).
|
Chris@0
|
13 */
|
Chris@0
|
14
|
Chris@0
|
15 #include "SamplePlayer.h"
|
Chris@0
|
16
|
Chris@0
|
17 #include <dssi.h>
|
Chris@0
|
18 #include <cmath>
|
Chris@0
|
19
|
Chris@0
|
20 #include <QMutexLocker>
|
Chris@0
|
21 #include <QDir>
|
Chris@0
|
22 #include <QFileInfo>
|
Chris@0
|
23
|
Chris@0
|
24 #include <sndfile.h>
|
Chris@0
|
25 #include <samplerate.h>
|
Chris@0
|
26 #include <iostream>
|
Chris@0
|
27
|
Chris@0
|
28 const char *const
|
Chris@0
|
29 SamplePlayer::portNames[PortCount] =
|
Chris@0
|
30 {
|
Chris@0
|
31 "Output",
|
Chris@0
|
32 "Tuned (on/off)",
|
Chris@0
|
33 "Base Pitch (MIDI)",
|
Chris@0
|
34 "Sustain (on/off)",
|
Chris@0
|
35 "Release time (s)"
|
Chris@0
|
36 };
|
Chris@0
|
37
|
Chris@0
|
38 const LADSPA_PortDescriptor
|
Chris@0
|
39 SamplePlayer::ports[PortCount] =
|
Chris@0
|
40 {
|
Chris@0
|
41 LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO,
|
Chris@0
|
42 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
|
Chris@0
|
43 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
|
Chris@0
|
44 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
|
Chris@0
|
45 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL
|
Chris@0
|
46 };
|
Chris@0
|
47
|
Chris@0
|
48 const LADSPA_PortRangeHint
|
Chris@0
|
49 SamplePlayer::hints[PortCount] =
|
Chris@0
|
50 {
|
Chris@0
|
51 { 0, 0, 0 },
|
Chris@0
|
52 { LADSPA_HINT_DEFAULT_MAXIMUM | LADSPA_HINT_INTEGER |
|
Chris@0
|
53 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 1 },
|
Chris@0
|
54 { LADSPA_HINT_DEFAULT_MIDDLE | LADSPA_HINT_INTEGER |
|
Chris@0
|
55 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 120 },
|
Chris@0
|
56 { LADSPA_HINT_DEFAULT_MINIMUM | LADSPA_HINT_INTEGER |
|
Chris@0
|
57 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 1 },
|
Chris@0
|
58 { LADSPA_HINT_DEFAULT_MINIMUM | LADSPA_HINT_LOGARITHMIC |
|
Chris@0
|
59 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0.001, 2.0 }
|
Chris@0
|
60 };
|
Chris@0
|
61
|
Chris@0
|
62 const LADSPA_Properties
|
Chris@0
|
63 SamplePlayer::properties = LADSPA_PROPERTY_HARD_RT_CAPABLE;
|
Chris@0
|
64
|
Chris@0
|
65 const LADSPA_Descriptor
|
Chris@0
|
66 SamplePlayer::ladspaDescriptor =
|
Chris@0
|
67 {
|
Chris@0
|
68 0, // "Unique" ID
|
Chris@0
|
69 "sample_player", // Label
|
Chris@0
|
70 properties,
|
Chris@0
|
71 "Library Sample Player", // Name
|
Chris@0
|
72 "Chris Cannam", // Maker
|
Chris@0
|
73 "GPL", // Copyright
|
Chris@0
|
74 PortCount,
|
Chris@0
|
75 ports,
|
Chris@0
|
76 portNames,
|
Chris@0
|
77 hints,
|
Chris@0
|
78 0, // Implementation data
|
Chris@0
|
79 instantiate,
|
Chris@0
|
80 connectPort,
|
Chris@0
|
81 activate,
|
Chris@0
|
82 run,
|
Chris@0
|
83 0, // Run adding
|
Chris@0
|
84 0, // Set run adding gain
|
Chris@0
|
85 deactivate,
|
Chris@0
|
86 cleanup
|
Chris@0
|
87 };
|
Chris@0
|
88
|
Chris@0
|
89 const DSSI_Descriptor
|
Chris@0
|
90 SamplePlayer::dssiDescriptor =
|
Chris@0
|
91 {
|
Chris@0
|
92 2, // DSSI API version
|
Chris@0
|
93 &ladspaDescriptor,
|
Chris@0
|
94 0, // Configure
|
Chris@0
|
95 getProgram,
|
Chris@0
|
96 selectProgram,
|
Chris@0
|
97 getMidiController,
|
Chris@0
|
98 runSynth,
|
Chris@0
|
99 0, // Run synth adding
|
Chris@0
|
100 0, // Run multiple synths
|
Chris@0
|
101 0, // Run multiple synths adding
|
Chris@0
|
102 receiveHostDescriptor
|
Chris@0
|
103 };
|
Chris@0
|
104
|
Chris@0
|
105 const DSSI_Host_Descriptor *
|
Chris@0
|
106 SamplePlayer::hostDescriptor = 0;
|
Chris@0
|
107
|
Chris@0
|
108
|
Chris@0
|
109 const DSSI_Descriptor *
|
Chris@0
|
110 SamplePlayer::getDescriptor(unsigned long index)
|
Chris@0
|
111 {
|
Chris@0
|
112 if (index == 0) return &dssiDescriptor;
|
Chris@0
|
113 return 0;
|
Chris@0
|
114 }
|
Chris@0
|
115
|
Chris@0
|
116 SamplePlayer::SamplePlayer(int sampleRate) :
|
Chris@0
|
117 m_output(0),
|
Chris@0
|
118 m_retune(0),
|
Chris@0
|
119 m_basePitch(0),
|
Chris@0
|
120 m_sustain(0),
|
Chris@0
|
121 m_release(0),
|
Chris@0
|
122 m_sampleData(0),
|
Chris@0
|
123 m_sampleCount(0),
|
Chris@0
|
124 m_sampleRate(sampleRate),
|
Chris@0
|
125 m_sampleNo(0),
|
Chris@0
|
126 m_sampleSearchComplete(false),
|
Chris@0
|
127 m_pendingProgramChange(-1)
|
Chris@0
|
128 {
|
Chris@0
|
129 }
|
Chris@0
|
130
|
Chris@0
|
131 SamplePlayer::~SamplePlayer()
|
Chris@0
|
132 {
|
Chris@0
|
133 if (m_sampleData) free(m_sampleData);
|
Chris@0
|
134 }
|
Chris@0
|
135
|
Chris@0
|
136 LADSPA_Handle
|
Chris@0
|
137 SamplePlayer::instantiate(const LADSPA_Descriptor *, unsigned long rate)
|
Chris@0
|
138 {
|
Chris@0
|
139 if (!hostDescriptor || !hostDescriptor->request_non_rt_thread) {
|
Chris@0
|
140 std::cerr << "SamplePlayer::instantiate: Host does not provide request_non_rt_thread, not instantiating" << std::endl;
|
Chris@0
|
141 return 0;
|
Chris@0
|
142 }
|
Chris@0
|
143
|
Chris@0
|
144 SamplePlayer *player = new SamplePlayer(rate);
|
Chris@0
|
145
|
Chris@0
|
146 if (hostDescriptor->request_non_rt_thread(player, workThreadCallback)) {
|
Chris@0
|
147 std::cerr << "SamplePlayer::instantiate: Host rejected request_non_rt_thread call, not instantiating" << std::endl;
|
Chris@0
|
148 delete player;
|
Chris@0
|
149 return 0;
|
Chris@0
|
150 }
|
Chris@0
|
151
|
Chris@0
|
152 return player;
|
Chris@0
|
153 }
|
Chris@0
|
154
|
Chris@0
|
155 void
|
Chris@0
|
156 SamplePlayer::connectPort(LADSPA_Handle handle,
|
Chris@0
|
157 unsigned long port, LADSPA_Data *location)
|
Chris@0
|
158 {
|
Chris@0
|
159 SamplePlayer *player = (SamplePlayer *)handle;
|
Chris@0
|
160
|
Chris@0
|
161 float **ports[PortCount] = {
|
Chris@0
|
162 &player->m_output,
|
Chris@0
|
163 &player->m_retune,
|
Chris@0
|
164 &player->m_basePitch,
|
Chris@0
|
165 &player->m_sustain,
|
Chris@0
|
166 &player->m_release
|
Chris@0
|
167 };
|
Chris@0
|
168
|
Chris@0
|
169 *ports[port] = (float *)location;
|
Chris@0
|
170 }
|
Chris@0
|
171
|
Chris@0
|
172 void
|
Chris@0
|
173 SamplePlayer::activate(LADSPA_Handle handle)
|
Chris@0
|
174 {
|
Chris@0
|
175 SamplePlayer *player = (SamplePlayer *)handle;
|
Chris@0
|
176 QMutexLocker locker(&player->m_mutex);
|
Chris@0
|
177
|
Chris@0
|
178 player->m_sampleNo = 0;
|
Chris@0
|
179
|
Chris@0
|
180 for (size_t i = 0; i < Polyphony; ++i) {
|
Chris@0
|
181 player->m_ons[i] = -1;
|
Chris@0
|
182 player->m_offs[i] = -1;
|
Chris@0
|
183 player->m_velocities[i] = 0;
|
Chris@0
|
184 }
|
Chris@0
|
185 }
|
Chris@0
|
186
|
Chris@0
|
187 void
|
Chris@0
|
188 SamplePlayer::run(LADSPA_Handle handle, unsigned long samples)
|
Chris@0
|
189 {
|
Chris@0
|
190 runSynth(handle, samples, 0, 0);
|
Chris@0
|
191 }
|
Chris@0
|
192
|
Chris@0
|
193 void
|
Chris@0
|
194 SamplePlayer::deactivate(LADSPA_Handle handle)
|
Chris@0
|
195 {
|
Chris@0
|
196 activate(handle); // both functions just reset the plugin
|
Chris@0
|
197 }
|
Chris@0
|
198
|
Chris@0
|
199 void
|
Chris@0
|
200 SamplePlayer::cleanup(LADSPA_Handle handle)
|
Chris@0
|
201 {
|
Chris@0
|
202 delete (SamplePlayer *)handle;
|
Chris@0
|
203 }
|
Chris@0
|
204
|
Chris@0
|
205 const DSSI_Program_Descriptor *
|
Chris@0
|
206 SamplePlayer::getProgram(LADSPA_Handle handle, unsigned long program)
|
Chris@0
|
207 {
|
Chris@0
|
208 SamplePlayer *player = (SamplePlayer *)handle;
|
Chris@0
|
209
|
Chris@0
|
210 if (!player->m_sampleSearchComplete) {
|
Chris@0
|
211 QMutexLocker locker(&player->m_mutex);
|
Chris@0
|
212 if (!player->m_sampleSearchComplete) {
|
Chris@0
|
213 player->searchSamples();
|
Chris@0
|
214 }
|
Chris@0
|
215 }
|
Chris@0
|
216 if (program >= player->m_samples.size()) return 0;
|
Chris@0
|
217
|
Chris@0
|
218 static DSSI_Program_Descriptor descriptor;
|
Chris@0
|
219 static char name[60];
|
Chris@0
|
220
|
Chris@0
|
221 strncpy(name, player->m_samples[program].first.toLocal8Bit().data(), 60);
|
Chris@0
|
222 name[59] = '\0';
|
Chris@0
|
223
|
Chris@0
|
224 descriptor.Bank = 0;
|
Chris@0
|
225 descriptor.Program = program;
|
Chris@0
|
226 descriptor.Name = name;
|
Chris@0
|
227
|
Chris@0
|
228 return &descriptor;
|
Chris@0
|
229 }
|
Chris@0
|
230
|
Chris@0
|
231 void
|
Chris@0
|
232 SamplePlayer::selectProgram(LADSPA_Handle handle,
|
Chris@0
|
233 unsigned long,
|
Chris@0
|
234 unsigned long program)
|
Chris@0
|
235 {
|
Chris@0
|
236 SamplePlayer *player = (SamplePlayer *)handle;
|
Chris@0
|
237 player->m_pendingProgramChange = program;
|
Chris@0
|
238 }
|
Chris@0
|
239
|
Chris@0
|
240 int
|
Chris@0
|
241 SamplePlayer::getMidiController(LADSPA_Handle, unsigned long port)
|
Chris@0
|
242 {
|
Chris@0
|
243 int controllers[PortCount] = {
|
Chris@0
|
244 DSSI_NONE,
|
Chris@0
|
245 DSSI_CC(12),
|
Chris@0
|
246 DSSI_CC(13),
|
Chris@0
|
247 DSSI_CC(64),
|
Chris@0
|
248 DSSI_CC(72)
|
Chris@0
|
249 };
|
Chris@0
|
250
|
Chris@0
|
251 return controllers[port];
|
Chris@0
|
252 }
|
Chris@0
|
253
|
Chris@0
|
254 void
|
Chris@0
|
255 SamplePlayer::runSynth(LADSPA_Handle handle, unsigned long samples,
|
Chris@0
|
256 snd_seq_event_t *events, unsigned long eventCount)
|
Chris@0
|
257 {
|
Chris@0
|
258 SamplePlayer *player = (SamplePlayer *)handle;
|
Chris@0
|
259
|
Chris@0
|
260 player->runImpl(samples, events, eventCount);
|
Chris@0
|
261 }
|
Chris@0
|
262
|
Chris@0
|
263 void
|
Chris@0
|
264 SamplePlayer::receiveHostDescriptor(const DSSI_Host_Descriptor *descriptor)
|
Chris@0
|
265 {
|
Chris@0
|
266 hostDescriptor = descriptor;
|
Chris@0
|
267 }
|
Chris@0
|
268
|
Chris@0
|
269 void
|
Chris@0
|
270 SamplePlayer::workThreadCallback(LADSPA_Handle handle)
|
Chris@0
|
271 {
|
Chris@0
|
272 SamplePlayer *player = (SamplePlayer *)handle;
|
Chris@0
|
273
|
Chris@0
|
274 if (player->m_pendingProgramChange >= 0) {
|
Chris@0
|
275
|
Chris@0
|
276 std::cerr << "SamplePlayer::workThreadCallback: pending program change " << player->m_pendingProgramChange << std::endl;
|
Chris@0
|
277
|
Chris@0
|
278 player->m_mutex.lock();
|
Chris@0
|
279
|
Chris@0
|
280 int program = player->m_pendingProgramChange;
|
Chris@0
|
281 player->m_pendingProgramChange = -1;
|
Chris@0
|
282
|
Chris@0
|
283 if (!player->m_sampleSearchComplete) {
|
Chris@0
|
284 player->searchSamples();
|
Chris@0
|
285 }
|
Chris@0
|
286
|
Chris@0
|
287 if (program < int(player->m_samples.size())) {
|
Chris@0
|
288 QString path = player->m_samples[program].second;
|
Chris@0
|
289 QString programName = player->m_samples[program].first;
|
Chris@0
|
290 if (programName != player->m_program) {
|
Chris@0
|
291 player->m_program = programName;
|
Chris@0
|
292 player->m_mutex.unlock();
|
Chris@0
|
293 player->loadSampleData(path);
|
Chris@0
|
294 } else {
|
Chris@0
|
295 player->m_mutex.unlock();
|
Chris@0
|
296 }
|
Chris@0
|
297 }
|
Chris@0
|
298 }
|
Chris@0
|
299
|
Chris@0
|
300 if (!player->m_sampleSearchComplete) {
|
Chris@0
|
301
|
Chris@0
|
302 QMutexLocker locker(&player->m_mutex);
|
Chris@0
|
303
|
Chris@0
|
304 if (!player->m_sampleSearchComplete) {
|
Chris@0
|
305 player->searchSamples();
|
Chris@0
|
306 }
|
Chris@0
|
307 }
|
Chris@0
|
308 }
|
Chris@0
|
309
|
Chris@0
|
310 void
|
Chris@0
|
311 SamplePlayer::searchSamples()
|
Chris@0
|
312 {
|
Chris@0
|
313 if (m_sampleSearchComplete) return;
|
Chris@0
|
314
|
Chris@0
|
315 //!!!
|
Chris@0
|
316 // QString path = "/usr/share/hydrogen/data/drumkits/EasternHop-1";
|
Chris@0
|
317
|
Chris@0
|
318 std::cerr << "Current working directory is \"" << getcwd(0, 0) << "\"" << std::endl;
|
Chris@0
|
319
|
Chris@0
|
320 QString path = "samples";
|
Chris@0
|
321
|
Chris@0
|
322 std::cerr << "SamplePlayer::searchSamples: Path is \""
|
Chris@0
|
323 << path.toLocal8Bit().data() << "\"" << std::endl;
|
Chris@0
|
324
|
Chris@0
|
325 QDir dir(path, "*.wav");
|
Chris@0
|
326 for (unsigned int i = 0; i < dir.count(); ++i) {
|
Chris@0
|
327 QFileInfo file(dir.filePath(dir[i]));
|
Chris@0
|
328 m_samples.push_back(std::pair<QString, QString>
|
Chris@0
|
329 (file.baseName(), file.filePath()));
|
Chris@0
|
330 std::cerr << "Found: " << dir[i].toLocal8Bit().data() << std::endl;
|
Chris@0
|
331 }
|
Chris@0
|
332
|
Chris@0
|
333 m_sampleSearchComplete = true;
|
Chris@0
|
334 }
|
Chris@0
|
335
|
Chris@0
|
336 void
|
Chris@0
|
337 SamplePlayer::loadSampleData(QString path)
|
Chris@0
|
338 {
|
Chris@0
|
339 SF_INFO info;
|
Chris@0
|
340 SNDFILE *file;
|
Chris@0
|
341 size_t samples = 0;
|
Chris@0
|
342 float *tmpFrames, *tmpSamples, *tmpResamples, *tmpOld;
|
Chris@0
|
343 size_t i;
|
Chris@0
|
344
|
Chris@0
|
345 info.format = 0;
|
Chris@0
|
346 file = sf_open(path.toLocal8Bit().data(), SFM_READ, &info);
|
Chris@0
|
347 if (!file) {
|
Chris@0
|
348 std::cerr << "SamplePlayer::loadSampleData: Failed to open file "
|
Chris@0
|
349 << path.toLocal8Bit().data() << ": "
|
Chris@0
|
350 << sf_strerror(file) << std::endl;
|
Chris@0
|
351 return;
|
Chris@0
|
352 }
|
Chris@0
|
353
|
Chris@0
|
354 samples = info.frames;
|
Chris@0
|
355 tmpFrames = (float *)malloc(info.frames * info.channels * sizeof(float));
|
Chris@0
|
356 if (!tmpFrames) return;
|
Chris@0
|
357
|
Chris@0
|
358 sf_readf_float(file, tmpFrames, info.frames);
|
Chris@0
|
359 sf_close(file);
|
Chris@0
|
360
|
Chris@0
|
361 tmpResamples = 0;
|
Chris@0
|
362
|
Chris@0
|
363 if (info.samplerate != m_sampleRate) {
|
Chris@0
|
364
|
Chris@0
|
365 double ratio = (double)m_sampleRate / (double)info.samplerate;
|
Chris@0
|
366 size_t target = (size_t)(info.frames * ratio);
|
Chris@0
|
367 SRC_DATA data;
|
Chris@0
|
368
|
Chris@0
|
369 tmpResamples = (float *)malloc(target * info.channels * sizeof(float));
|
Chris@0
|
370 if (!tmpResamples) {
|
Chris@0
|
371 free(tmpFrames);
|
Chris@0
|
372 return;
|
Chris@0
|
373 }
|
Chris@0
|
374
|
Chris@0
|
375 memset(tmpResamples, 0, target * info.channels * sizeof(float));
|
Chris@0
|
376
|
Chris@0
|
377 data.data_in = tmpFrames;
|
Chris@0
|
378 data.data_out = tmpResamples;
|
Chris@0
|
379 data.input_frames = info.frames;
|
Chris@0
|
380 data.output_frames = target;
|
Chris@0
|
381 data.src_ratio = ratio;
|
Chris@0
|
382
|
Chris@0
|
383 if (!src_simple(&data, SRC_SINC_BEST_QUALITY, info.channels)) {
|
Chris@0
|
384 free(tmpFrames);
|
Chris@0
|
385 tmpFrames = tmpResamples;
|
Chris@0
|
386 samples = target;
|
Chris@0
|
387 } else {
|
Chris@0
|
388 free(tmpResamples);
|
Chris@0
|
389 }
|
Chris@0
|
390 }
|
Chris@0
|
391
|
Chris@0
|
392 /* add an extra sample for linear interpolation */
|
Chris@0
|
393 tmpSamples = (float *)malloc((samples + 1) * sizeof(float));
|
Chris@0
|
394 if (!tmpSamples) {
|
Chris@0
|
395 free(tmpFrames);
|
Chris@0
|
396 return;
|
Chris@0
|
397 }
|
Chris@0
|
398
|
Chris@0
|
399 for (i = 0; i < samples; ++i) {
|
Chris@0
|
400 int j;
|
Chris@0
|
401 tmpSamples[i] = 0.0f;
|
Chris@0
|
402 for (j = 0; j < info.channels; ++j) {
|
Chris@0
|
403 tmpSamples[i] += tmpFrames[i * info.channels + j];
|
Chris@0
|
404 }
|
Chris@0
|
405 }
|
Chris@0
|
406
|
Chris@0
|
407 free(tmpFrames);
|
Chris@0
|
408
|
Chris@0
|
409 /* add an extra sample for linear interpolation */
|
Chris@0
|
410 tmpSamples[samples] = 0.0f;
|
Chris@0
|
411
|
Chris@0
|
412 QMutexLocker locker(&m_mutex);
|
Chris@0
|
413
|
Chris@0
|
414 tmpOld = m_sampleData;
|
Chris@0
|
415 m_sampleData = tmpSamples;
|
Chris@0
|
416 m_sampleCount = samples;
|
Chris@0
|
417
|
Chris@0
|
418 for (i = 0; i < Polyphony; ++i) {
|
Chris@0
|
419 m_ons[i] = -1;
|
Chris@0
|
420 m_offs[i] = -1;
|
Chris@0
|
421 m_velocities[i] = 0;
|
Chris@0
|
422 }
|
Chris@0
|
423
|
Chris@0
|
424 if (tmpOld) free(tmpOld);
|
Chris@0
|
425
|
Chris@0
|
426 printf("%s: loaded %s (%ld samples from original %ld channels resampled from %ld frames at %ld Hz)\n", "sampler", path.toLocal8Bit().data(), (long)samples, (long)info.channels, (long)info.frames, (long)info.samplerate);
|
Chris@0
|
427 }
|
Chris@0
|
428
|
Chris@0
|
429 void
|
Chris@0
|
430 SamplePlayer::runImpl(unsigned long sampleCount,
|
Chris@0
|
431 snd_seq_event_t *events,
|
Chris@0
|
432 unsigned long eventCount)
|
Chris@0
|
433 {
|
Chris@0
|
434 unsigned long pos;
|
Chris@0
|
435 unsigned long count;
|
Chris@0
|
436 unsigned long event_pos;
|
Chris@0
|
437 int i;
|
Chris@0
|
438
|
Chris@0
|
439 memset(m_output, 0, sampleCount * sizeof(float));
|
Chris@0
|
440
|
Chris@0
|
441 if (!m_mutex.tryLock()) return;
|
Chris@0
|
442
|
Chris@0
|
443 if (!m_sampleData || !m_sampleCount) {
|
Chris@0
|
444 m_sampleNo += sampleCount;
|
Chris@0
|
445 m_mutex.unlock();
|
Chris@0
|
446 return;
|
Chris@0
|
447 }
|
Chris@0
|
448
|
Chris@0
|
449 for (pos = 0, event_pos = 0; pos < sampleCount; ) {
|
Chris@0
|
450
|
Chris@0
|
451 while (event_pos < eventCount
|
Chris@0
|
452 && pos >= events[event_pos].time.tick) {
|
Chris@0
|
453
|
Chris@0
|
454 if (events[event_pos].type == SND_SEQ_EVENT_NOTEON) {
|
Chris@0
|
455 snd_seq_ev_note_t n = events[event_pos].data.note;
|
Chris@0
|
456 if (n.velocity > 0) {
|
Chris@0
|
457 m_ons[n.note] =
|
Chris@0
|
458 m_sampleNo + events[event_pos].time.tick;
|
Chris@0
|
459 m_offs[n.note] = -1;
|
Chris@0
|
460 m_velocities[n.note] = n.velocity;
|
Chris@0
|
461 } else {
|
Chris@0
|
462 if (!m_sustain || (*m_sustain < 0.001)) {
|
Chris@0
|
463 m_offs[n.note] =
|
Chris@0
|
464 m_sampleNo + events[event_pos].time.tick;
|
Chris@0
|
465 }
|
Chris@0
|
466 }
|
Chris@0
|
467 } else if (events[event_pos].type == SND_SEQ_EVENT_NOTEOFF &&
|
Chris@0
|
468 (!m_sustain || (*m_sustain < 0.001))) {
|
Chris@0
|
469 snd_seq_ev_note_t n = events[event_pos].data.note;
|
Chris@0
|
470 m_offs[n.note] =
|
Chris@0
|
471 m_sampleNo + events[event_pos].time.tick;
|
Chris@0
|
472 }
|
Chris@0
|
473
|
Chris@0
|
474 ++event_pos;
|
Chris@0
|
475 }
|
Chris@0
|
476
|
Chris@0
|
477 count = sampleCount - pos;
|
Chris@0
|
478 if (event_pos < eventCount &&
|
Chris@0
|
479 events[event_pos].time.tick < sampleCount) {
|
Chris@0
|
480 count = events[event_pos].time.tick - pos;
|
Chris@0
|
481 }
|
Chris@0
|
482
|
Chris@0
|
483 for (i = 0; i < Polyphony; ++i) {
|
Chris@0
|
484 if (m_ons[i] >= 0) {
|
Chris@0
|
485 addSample(i, pos, count);
|
Chris@0
|
486 }
|
Chris@0
|
487 }
|
Chris@0
|
488
|
Chris@0
|
489 pos += count;
|
Chris@0
|
490 }
|
Chris@0
|
491
|
Chris@0
|
492 m_sampleNo += sampleCount;
|
Chris@0
|
493 m_mutex.unlock();
|
Chris@0
|
494 }
|
Chris@0
|
495
|
Chris@0
|
496 void
|
Chris@0
|
497 SamplePlayer::addSample(int n, unsigned long pos, unsigned long count)
|
Chris@0
|
498 {
|
Chris@0
|
499 float ratio = 1.0;
|
Chris@0
|
500 float gain = 1.0;
|
Chris@0
|
501 unsigned long i, s;
|
Chris@0
|
502
|
Chris@0
|
503 if (m_retune && *m_retune) {
|
Chris@0
|
504 if (m_basePitch && n != *m_basePitch) {
|
Chris@0
|
505 ratio = powf(1.059463094, n - *m_basePitch);
|
Chris@0
|
506 }
|
Chris@0
|
507 }
|
Chris@0
|
508
|
Chris@0
|
509 if (long(pos + m_sampleNo) < m_ons[n]) return;
|
Chris@0
|
510
|
Chris@0
|
511 gain = (float)m_velocities[n] / 127.0f;
|
Chris@0
|
512
|
Chris@0
|
513 for (i = 0, s = pos + m_sampleNo - m_ons[n];
|
Chris@0
|
514 i < count;
|
Chris@0
|
515 ++i, ++s) {
|
Chris@0
|
516
|
Chris@0
|
517 float lgain = gain;
|
Chris@0
|
518 float rs = s * ratio;
|
Chris@0
|
519 unsigned long rsi = lrintf(floor(rs));
|
Chris@0
|
520
|
Chris@0
|
521 if (rsi >= m_sampleCount) {
|
Chris@0
|
522 m_ons[n] = -1;
|
Chris@0
|
523 break;
|
Chris@0
|
524 }
|
Chris@0
|
525
|
Chris@0
|
526 if (m_offs[n] >= 0 &&
|
Chris@0
|
527 long(pos + i + m_sampleNo) > m_offs[n]) {
|
Chris@0
|
528
|
Chris@0
|
529 unsigned long dist =
|
Chris@0
|
530 pos + i + m_sampleNo - m_offs[n];
|
Chris@0
|
531
|
Chris@0
|
532 unsigned long releaseFrames = 200;
|
Chris@0
|
533 if (m_release) {
|
Chris@0
|
534 releaseFrames = long(*m_release * m_sampleRate + 0.0001);
|
Chris@0
|
535 }
|
Chris@0
|
536
|
Chris@0
|
537 if (dist > releaseFrames) {
|
Chris@0
|
538 m_ons[n] = -1;
|
Chris@0
|
539 break;
|
Chris@0
|
540 } else {
|
Chris@0
|
541 lgain = lgain * (float)(releaseFrames - dist) /
|
Chris@0
|
542 (float)releaseFrames;
|
Chris@0
|
543 }
|
Chris@0
|
544 }
|
Chris@0
|
545
|
Chris@0
|
546 float sample = m_sampleData[rsi] +
|
Chris@0
|
547 ((m_sampleData[rsi + 1] -
|
Chris@0
|
548 m_sampleData[rsi]) *
|
Chris@0
|
549 (rs - (float)rsi));
|
Chris@0
|
550
|
Chris@0
|
551 m_output[pos + i] += lgain * sample;
|
Chris@0
|
552 }
|
Chris@0
|
553 }
|