annotate plugin/plugins/SamplePlayer.cpp @ 661:a4faa1840384

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