annotate plugin/plugins/SamplePlayer.cpp @ 5:31c4ed2d5da6

* Hook up SV file i/o. You can now save and load sessions. Some problems -- gain is not reloaded correctly for waveforms, reloaded panes are not properly reconnected to the panner, and no doubt plenty of others.
author Chris Cannam
date Tue, 17 Jan 2006 17:45:55 +0000
parents d86891498eef
children 39ae3dee27b9
rev   line source
Chris@0 1 /* -*- c-basic-offset: 4 -*- 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 }