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