annotate plugin/plugins/SamplePlayer.cpp @ 51:c2913298cf94

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