comparison plugin/plugins/SamplePlayer.cpp @ 0:da6937383da8

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