annotate plugin/plugins/SamplePlayer.cpp @ 631:3a5ee4b6c9ad

* Complete the overhaul of CSV file import; now you can pick the purpose for each column in the file, and SV should do the rest. The most significant practical improvement here is that we can now handle files in which time and duration do not necessarily appear in known columns.
author Chris Cannam
date Mon, 19 Jul 2010 17:08:56 +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 }