annotate plugin/plugins/SamplePlayer.cpp @ 588:d04b8674b710

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