annotate plugin/plugins/SamplePlayer.cpp @ 628:001db550bd48

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