annotate plugin/plugins/SamplePlayer.cpp @ 1394:9ef1cc26024c

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