annotate plugin/plugins/SamplePlayer.cpp @ 537:3cc4b7cd2aa5

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