annotate plugin/plugins/SamplePlayer.cpp @ 97:22494cc28c9f

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