annotate plugin/plugins/SamplePlayer.cpp @ 339:ba30f4a3e3be

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