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