lbajardsilogic@0: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ lbajardsilogic@0: lbajardsilogic@0: /* lbajardsilogic@0: Sonic Visualiser lbajardsilogic@0: An audio file viewer and annotation editor. lbajardsilogic@0: Centre for Digital Music, Queen Mary, University of London. lbajardsilogic@0: lbajardsilogic@0: This program is free software; you can redistribute it and/or lbajardsilogic@0: modify it under the terms of the GNU General Public License as lbajardsilogic@0: published by the Free Software Foundation; either version 2 of the lbajardsilogic@0: License, or (at your option) any later version. See the file lbajardsilogic@0: COPYING included with this distribution for more information. lbajardsilogic@0: */ lbajardsilogic@0: lbajardsilogic@0: /* lbajardsilogic@0: Based on trivial_sampler from the DSSI distribution lbajardsilogic@0: (by Chris Cannam, public domain). lbajardsilogic@0: */ lbajardsilogic@0: lbajardsilogic@0: #include "SamplePlayer.h" lbajardsilogic@0: lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: lbajardsilogic@0: #include "system/System.h" lbajardsilogic@0: lbajardsilogic@0: const char *const lbajardsilogic@0: SamplePlayer::portNames[PortCount] = lbajardsilogic@0: { lbajardsilogic@0: "Output", lbajardsilogic@0: "Tuned (on/off)", lbajardsilogic@0: "Base Pitch (MIDI)", lbajardsilogic@0: "Tuning of A (Hz)", lbajardsilogic@0: "Sustain (on/off)", lbajardsilogic@0: "Release time (s)" lbajardsilogic@0: }; lbajardsilogic@0: lbajardsilogic@0: const LADSPA_PortDescriptor lbajardsilogic@0: SamplePlayer::ports[PortCount] = lbajardsilogic@0: { lbajardsilogic@0: LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, lbajardsilogic@0: LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, lbajardsilogic@0: LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, lbajardsilogic@0: LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, lbajardsilogic@0: LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL lbajardsilogic@0: }; lbajardsilogic@0: lbajardsilogic@0: const LADSPA_PortRangeHint lbajardsilogic@0: SamplePlayer::hints[PortCount] = lbajardsilogic@0: { lbajardsilogic@0: { 0, 0, 0 }, lbajardsilogic@0: { LADSPA_HINT_DEFAULT_MAXIMUM | LADSPA_HINT_INTEGER | lbajardsilogic@0: LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 1 }, lbajardsilogic@0: { LADSPA_HINT_DEFAULT_MIDDLE | LADSPA_HINT_INTEGER | lbajardsilogic@0: LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 120 }, lbajardsilogic@0: { LADSPA_HINT_DEFAULT_440 | LADSPA_HINT_LOGARITHMIC | lbajardsilogic@0: LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 400, 499 }, lbajardsilogic@0: { LADSPA_HINT_DEFAULT_MINIMUM | LADSPA_HINT_INTEGER | lbajardsilogic@0: LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 1 }, lbajardsilogic@0: { LADSPA_HINT_DEFAULT_MINIMUM | LADSPA_HINT_LOGARITHMIC | lbajardsilogic@0: LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0.001, 2.0 } lbajardsilogic@0: }; lbajardsilogic@0: lbajardsilogic@0: const LADSPA_Properties lbajardsilogic@0: SamplePlayer::properties = LADSPA_PROPERTY_HARD_RT_CAPABLE; lbajardsilogic@0: lbajardsilogic@0: const LADSPA_Descriptor lbajardsilogic@0: SamplePlayer::ladspaDescriptor = lbajardsilogic@0: { lbajardsilogic@0: 0, // "Unique" ID lbajardsilogic@0: "sample_player", // Label lbajardsilogic@0: properties, lbajardsilogic@0: "Library Sample Player", // Name lbajardsilogic@0: "Chris Cannam", // Maker lbajardsilogic@0: "GPL", // Copyright lbajardsilogic@0: PortCount, lbajardsilogic@0: ports, lbajardsilogic@0: portNames, lbajardsilogic@0: hints, lbajardsilogic@0: 0, // Implementation data lbajardsilogic@0: instantiate, lbajardsilogic@0: connectPort, lbajardsilogic@0: activate, lbajardsilogic@0: run, lbajardsilogic@0: 0, // Run adding lbajardsilogic@0: 0, // Set run adding gain lbajardsilogic@0: deactivate, lbajardsilogic@0: cleanup lbajardsilogic@0: }; lbajardsilogic@0: lbajardsilogic@0: const DSSI_Descriptor lbajardsilogic@0: SamplePlayer::dssiDescriptor = lbajardsilogic@0: { lbajardsilogic@0: 2, // DSSI API version lbajardsilogic@0: &ladspaDescriptor, lbajardsilogic@0: configure, lbajardsilogic@0: getProgram, lbajardsilogic@0: selectProgram, lbajardsilogic@0: getMidiController, lbajardsilogic@0: runSynth, lbajardsilogic@0: 0, // Run synth adding lbajardsilogic@0: 0, // Run multiple synths lbajardsilogic@0: 0, // Run multiple synths adding lbajardsilogic@0: receiveHostDescriptor lbajardsilogic@0: }; lbajardsilogic@0: lbajardsilogic@0: const DSSI_Host_Descriptor * lbajardsilogic@0: SamplePlayer::hostDescriptor = 0; lbajardsilogic@0: lbajardsilogic@0: lbajardsilogic@0: const DSSI_Descriptor * lbajardsilogic@0: SamplePlayer::getDescriptor(unsigned long index) lbajardsilogic@0: { lbajardsilogic@0: if (index == 0) return &dssiDescriptor; lbajardsilogic@0: return 0; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: SamplePlayer::SamplePlayer(int sampleRate) : lbajardsilogic@0: m_output(0), lbajardsilogic@0: m_retune(0), lbajardsilogic@0: m_basePitch(0), lbajardsilogic@0: m_concertA(0), lbajardsilogic@0: m_sustain(0), lbajardsilogic@0: m_release(0), lbajardsilogic@0: m_sampleData(0), lbajardsilogic@0: m_sampleCount(0), lbajardsilogic@0: m_sampleRate(sampleRate), lbajardsilogic@0: m_sampleNo(0), lbajardsilogic@0: m_sampleDir("samples"), lbajardsilogic@0: m_sampleSearchComplete(false), lbajardsilogic@0: m_pendingProgramChange(-1) lbajardsilogic@0: { lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: SamplePlayer::~SamplePlayer() lbajardsilogic@0: { lbajardsilogic@0: if (m_sampleData) free(m_sampleData); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: LADSPA_Handle lbajardsilogic@0: SamplePlayer::instantiate(const LADSPA_Descriptor *, unsigned long rate) lbajardsilogic@0: { lbajardsilogic@0: if (!hostDescriptor || !hostDescriptor->request_non_rt_thread) { lbajardsilogic@0: std::cerr << "SamplePlayer::instantiate: Host does not provide request_non_rt_thread, not instantiating" << std::endl; lbajardsilogic@0: return 0; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: SamplePlayer *player = new SamplePlayer(rate); lbajardsilogic@0: lbajardsilogic@0: if (hostDescriptor->request_non_rt_thread(player, workThreadCallback)) { lbajardsilogic@0: std::cerr << "SamplePlayer::instantiate: Host rejected request_non_rt_thread call, not instantiating" << std::endl; lbajardsilogic@0: delete player; lbajardsilogic@0: return 0; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: return player; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SamplePlayer::connectPort(LADSPA_Handle handle, lbajardsilogic@0: unsigned long port, LADSPA_Data *location) lbajardsilogic@0: { lbajardsilogic@0: SamplePlayer *player = (SamplePlayer *)handle; lbajardsilogic@0: lbajardsilogic@0: float **ports[PortCount] = { lbajardsilogic@0: &player->m_output, lbajardsilogic@0: &player->m_retune, lbajardsilogic@0: &player->m_basePitch, lbajardsilogic@0: &player->m_concertA, lbajardsilogic@0: &player->m_sustain, lbajardsilogic@0: &player->m_release lbajardsilogic@0: }; lbajardsilogic@0: lbajardsilogic@0: *ports[port] = (float *)location; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SamplePlayer::activate(LADSPA_Handle handle) lbajardsilogic@0: { lbajardsilogic@0: SamplePlayer *player = (SamplePlayer *)handle; lbajardsilogic@0: QMutexLocker locker(&player->m_mutex); lbajardsilogic@0: lbajardsilogic@0: player->m_sampleNo = 0; lbajardsilogic@0: lbajardsilogic@0: for (size_t i = 0; i < Polyphony; ++i) { lbajardsilogic@0: player->m_ons[i] = -1; lbajardsilogic@0: player->m_offs[i] = -1; lbajardsilogic@0: player->m_velocities[i] = 0; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SamplePlayer::run(LADSPA_Handle handle, unsigned long samples) lbajardsilogic@0: { lbajardsilogic@0: runSynth(handle, samples, 0, 0); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SamplePlayer::deactivate(LADSPA_Handle handle) lbajardsilogic@0: { lbajardsilogic@0: activate(handle); // both functions just reset the plugin lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SamplePlayer::cleanup(LADSPA_Handle handle) lbajardsilogic@0: { lbajardsilogic@0: delete (SamplePlayer *)handle; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: char * lbajardsilogic@0: SamplePlayer::configure(LADSPA_Handle handle, const char *key, const char *value) lbajardsilogic@0: { lbajardsilogic@0: if (key && !strcmp(key, "sampledir")) { lbajardsilogic@0: lbajardsilogic@0: SamplePlayer *player = (SamplePlayer *)handle; lbajardsilogic@0: lbajardsilogic@0: QMutexLocker locker(&player->m_mutex); lbajardsilogic@0: lbajardsilogic@0: if (QFileInfo(value).exists() && lbajardsilogic@0: QFileInfo(value).isDir()) { lbajardsilogic@0: lbajardsilogic@0: player->m_sampleDir = value; lbajardsilogic@0: lbajardsilogic@0: if (player->m_sampleSearchComplete) { lbajardsilogic@0: player->m_sampleSearchComplete = false; lbajardsilogic@0: player->searchSamples(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: return 0; lbajardsilogic@0: lbajardsilogic@0: } else { lbajardsilogic@0: char *buffer = (char *)malloc(strlen(value) + 80); lbajardsilogic@0: sprintf(buffer, "Sample directory \"%s\" does not exist, leaving unchanged", value); lbajardsilogic@0: return buffer; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: return strdup("Unknown configure key"); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: const DSSI_Program_Descriptor * lbajardsilogic@0: SamplePlayer::getProgram(LADSPA_Handle handle, unsigned long program) lbajardsilogic@0: { lbajardsilogic@0: SamplePlayer *player = (SamplePlayer *)handle; lbajardsilogic@0: lbajardsilogic@0: if (!player->m_sampleSearchComplete) { lbajardsilogic@0: QMutexLocker locker(&player->m_mutex); lbajardsilogic@0: if (!player->m_sampleSearchComplete) { lbajardsilogic@0: player->searchSamples(); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: if (program >= player->m_samples.size()) return 0; lbajardsilogic@0: lbajardsilogic@0: static DSSI_Program_Descriptor descriptor; lbajardsilogic@0: static char name[60]; lbajardsilogic@0: lbajardsilogic@0: strncpy(name, player->m_samples[program].first.toLocal8Bit().data(), 60); lbajardsilogic@0: name[59] = '\0'; lbajardsilogic@0: lbajardsilogic@0: descriptor.Bank = 0; lbajardsilogic@0: descriptor.Program = program; lbajardsilogic@0: descriptor.Name = name; lbajardsilogic@0: lbajardsilogic@0: return &descriptor; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SamplePlayer::selectProgram(LADSPA_Handle handle, lbajardsilogic@0: unsigned long, lbajardsilogic@0: unsigned long program) lbajardsilogic@0: { lbajardsilogic@0: SamplePlayer *player = (SamplePlayer *)handle; lbajardsilogic@0: player->m_pendingProgramChange = program; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: int lbajardsilogic@0: SamplePlayer::getMidiController(LADSPA_Handle, unsigned long port) lbajardsilogic@0: { lbajardsilogic@0: int controllers[PortCount] = { lbajardsilogic@0: DSSI_NONE, lbajardsilogic@0: DSSI_CC(12), lbajardsilogic@0: DSSI_CC(13), lbajardsilogic@0: DSSI_CC(64), lbajardsilogic@0: DSSI_CC(72) lbajardsilogic@0: }; lbajardsilogic@0: lbajardsilogic@0: return controllers[port]; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SamplePlayer::runSynth(LADSPA_Handle handle, unsigned long samples, lbajardsilogic@0: snd_seq_event_t *events, unsigned long eventCount) lbajardsilogic@0: { lbajardsilogic@0: SamplePlayer *player = (SamplePlayer *)handle; lbajardsilogic@0: lbajardsilogic@0: player->runImpl(samples, events, eventCount); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SamplePlayer::receiveHostDescriptor(const DSSI_Host_Descriptor *descriptor) lbajardsilogic@0: { lbajardsilogic@0: hostDescriptor = descriptor; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SamplePlayer::workThreadCallback(LADSPA_Handle handle) lbajardsilogic@0: { lbajardsilogic@0: SamplePlayer *player = (SamplePlayer *)handle; lbajardsilogic@0: lbajardsilogic@0: if (player->m_pendingProgramChange >= 0) { lbajardsilogic@0: lbajardsilogic@0: // std::cerr << "SamplePlayer::workThreadCallback: pending program change " << player->m_pendingProgramChange << std::endl; lbajardsilogic@0: lbajardsilogic@0: player->m_mutex.lock(); lbajardsilogic@0: lbajardsilogic@0: int program = player->m_pendingProgramChange; lbajardsilogic@0: player->m_pendingProgramChange = -1; lbajardsilogic@0: lbajardsilogic@0: if (!player->m_sampleSearchComplete) { lbajardsilogic@0: player->searchSamples(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (program < int(player->m_samples.size())) { lbajardsilogic@0: QString path = player->m_samples[program].second; lbajardsilogic@0: QString programName = player->m_samples[program].first; lbajardsilogic@0: if (programName != player->m_program) { lbajardsilogic@0: player->m_program = programName; lbajardsilogic@0: player->m_mutex.unlock(); lbajardsilogic@0: player->loadSampleData(path); lbajardsilogic@0: } else { lbajardsilogic@0: player->m_mutex.unlock(); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (!player->m_sampleSearchComplete) { lbajardsilogic@0: lbajardsilogic@0: QMutexLocker locker(&player->m_mutex); lbajardsilogic@0: lbajardsilogic@0: if (!player->m_sampleSearchComplete) { lbajardsilogic@0: player->searchSamples(); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SamplePlayer::searchSamples() lbajardsilogic@0: { lbajardsilogic@0: if (m_sampleSearchComplete) return; lbajardsilogic@0: lbajardsilogic@0: m_samples.clear(); lbajardsilogic@0: lbajardsilogic@0: std::cerr << "SamplePlayer::searchSamples: Directory is \"" lbajardsilogic@0: << m_sampleDir.toLocal8Bit().data() << "\"" << std::endl; lbajardsilogic@0: lbajardsilogic@0: QDir dir(m_sampleDir, "*.wav"); lbajardsilogic@0: lbajardsilogic@0: for (unsigned int i = 0; i < dir.count(); ++i) { lbajardsilogic@0: QFileInfo file(dir.filePath(dir[i])); lbajardsilogic@0: if (file.isReadable()) { lbajardsilogic@0: m_samples.push_back(std::pair lbajardsilogic@0: (file.baseName(), file.filePath())); lbajardsilogic@0: // std::cerr << "Found: " << dir[i].toLocal8Bit().data() << std::endl; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_sampleSearchComplete = true; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SamplePlayer::loadSampleData(QString path) lbajardsilogic@0: { lbajardsilogic@0: SF_INFO info; lbajardsilogic@0: SNDFILE *file; lbajardsilogic@0: size_t samples = 0; lbajardsilogic@0: float *tmpFrames, *tmpSamples, *tmpResamples, *tmpOld; lbajardsilogic@0: size_t i; lbajardsilogic@0: lbajardsilogic@0: info.format = 0; lbajardsilogic@0: file = sf_open(path.toLocal8Bit().data(), SFM_READ, &info); lbajardsilogic@0: if (!file) { lbajardsilogic@0: std::cerr << "SamplePlayer::loadSampleData: Failed to open file " lbajardsilogic@0: << path.toLocal8Bit().data() << ": " lbajardsilogic@0: << sf_strerror(file) << std::endl; lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: samples = info.frames; lbajardsilogic@0: tmpFrames = (float *)malloc(info.frames * info.channels * sizeof(float)); lbajardsilogic@0: if (!tmpFrames) return; lbajardsilogic@0: lbajardsilogic@0: sf_readf_float(file, tmpFrames, info.frames); lbajardsilogic@0: sf_close(file); lbajardsilogic@0: lbajardsilogic@0: tmpResamples = 0; lbajardsilogic@0: lbajardsilogic@0: if (info.samplerate != m_sampleRate) { lbajardsilogic@0: lbajardsilogic@0: double ratio = (double)m_sampleRate / (double)info.samplerate; lbajardsilogic@0: size_t target = (size_t)(info.frames * ratio); lbajardsilogic@0: SRC_DATA data; lbajardsilogic@0: lbajardsilogic@0: tmpResamples = (float *)malloc(target * info.channels * sizeof(float)); lbajardsilogic@0: if (!tmpResamples) { lbajardsilogic@0: free(tmpFrames); lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: memset(tmpResamples, 0, target * info.channels * sizeof(float)); lbajardsilogic@0: lbajardsilogic@0: data.data_in = tmpFrames; lbajardsilogic@0: data.data_out = tmpResamples; lbajardsilogic@0: data.input_frames = info.frames; lbajardsilogic@0: data.output_frames = target; lbajardsilogic@0: data.src_ratio = ratio; lbajardsilogic@0: lbajardsilogic@0: if (!src_simple(&data, SRC_SINC_BEST_QUALITY, info.channels)) { lbajardsilogic@0: free(tmpFrames); lbajardsilogic@0: tmpFrames = tmpResamples; lbajardsilogic@0: samples = target; lbajardsilogic@0: } else { lbajardsilogic@0: free(tmpResamples); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: /* add an extra sample for linear interpolation */ lbajardsilogic@0: tmpSamples = (float *)malloc((samples + 1) * sizeof(float)); lbajardsilogic@0: if (!tmpSamples) { lbajardsilogic@0: free(tmpFrames); lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: for (i = 0; i < samples; ++i) { lbajardsilogic@0: int j; lbajardsilogic@0: tmpSamples[i] = 0.0f; lbajardsilogic@0: for (j = 0; j < info.channels; ++j) { lbajardsilogic@0: tmpSamples[i] += tmpFrames[i * info.channels + j]; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: free(tmpFrames); lbajardsilogic@0: lbajardsilogic@0: /* add an extra sample for linear interpolation */ lbajardsilogic@0: tmpSamples[samples] = 0.0f; lbajardsilogic@0: lbajardsilogic@0: QMutexLocker locker(&m_mutex); lbajardsilogic@0: lbajardsilogic@0: tmpOld = m_sampleData; lbajardsilogic@0: m_sampleData = tmpSamples; lbajardsilogic@0: m_sampleCount = samples; lbajardsilogic@0: lbajardsilogic@0: for (i = 0; i < Polyphony; ++i) { lbajardsilogic@0: m_ons[i] = -1; lbajardsilogic@0: m_offs[i] = -1; lbajardsilogic@0: m_velocities[i] = 0; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (tmpOld) free(tmpOld); lbajardsilogic@0: lbajardsilogic@0: 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); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SamplePlayer::runImpl(unsigned long sampleCount, lbajardsilogic@0: snd_seq_event_t *events, lbajardsilogic@0: unsigned long eventCount) lbajardsilogic@0: { lbajardsilogic@0: unsigned long pos; lbajardsilogic@0: unsigned long count; lbajardsilogic@0: unsigned long event_pos; lbajardsilogic@0: int i; lbajardsilogic@0: lbajardsilogic@0: memset(m_output, 0, sampleCount * sizeof(float)); lbajardsilogic@0: lbajardsilogic@0: if (!m_mutex.tryLock()) return; lbajardsilogic@0: lbajardsilogic@0: if (!m_sampleData || !m_sampleCount) { lbajardsilogic@0: m_sampleNo += sampleCount; lbajardsilogic@0: m_mutex.unlock(); lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: for (pos = 0, event_pos = 0; pos < sampleCount; ) { lbajardsilogic@0: lbajardsilogic@0: while (event_pos < eventCount lbajardsilogic@0: && pos >= events[event_pos].time.tick) { lbajardsilogic@0: lbajardsilogic@0: if (events[event_pos].type == SND_SEQ_EVENT_NOTEON) { lbajardsilogic@0: snd_seq_ev_note_t n = events[event_pos].data.note; lbajardsilogic@0: if (n.velocity > 0) { lbajardsilogic@0: m_ons[n.note] = lbajardsilogic@0: m_sampleNo + events[event_pos].time.tick; lbajardsilogic@0: m_offs[n.note] = -1; lbajardsilogic@0: m_velocities[n.note] = n.velocity; lbajardsilogic@0: } else { lbajardsilogic@0: if (!m_sustain || (*m_sustain < 0.001)) { lbajardsilogic@0: m_offs[n.note] = lbajardsilogic@0: m_sampleNo + events[event_pos].time.tick; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } else if (events[event_pos].type == SND_SEQ_EVENT_NOTEOFF && lbajardsilogic@0: (!m_sustain || (*m_sustain < 0.001))) { lbajardsilogic@0: snd_seq_ev_note_t n = events[event_pos].data.note; lbajardsilogic@0: m_offs[n.note] = lbajardsilogic@0: m_sampleNo + events[event_pos].time.tick; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: ++event_pos; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: count = sampleCount - pos; lbajardsilogic@0: if (event_pos < eventCount && lbajardsilogic@0: events[event_pos].time.tick < sampleCount) { lbajardsilogic@0: count = events[event_pos].time.tick - pos; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: for (i = 0; i < Polyphony; ++i) { lbajardsilogic@0: if (m_ons[i] >= 0) { lbajardsilogic@0: addSample(i, pos, count); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: pos += count; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_sampleNo += sampleCount; lbajardsilogic@0: m_mutex.unlock(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SamplePlayer::addSample(int n, unsigned long pos, unsigned long count) lbajardsilogic@0: { lbajardsilogic@0: float ratio = 1.f; lbajardsilogic@0: float gain = 1.f; lbajardsilogic@0: unsigned long i, s; lbajardsilogic@0: lbajardsilogic@0: if (m_retune && *m_retune) { lbajardsilogic@0: if (m_concertA) { lbajardsilogic@0: ratio *= *m_concertA / 440.f; lbajardsilogic@0: } lbajardsilogic@0: if (m_basePitch && n != *m_basePitch) { lbajardsilogic@0: ratio *= powf(1.059463094f, n - *m_basePitch); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (long(pos + m_sampleNo) < m_ons[n]) return; lbajardsilogic@0: lbajardsilogic@0: gain = (float)m_velocities[n] / 127.0f; lbajardsilogic@0: lbajardsilogic@0: for (i = 0, s = pos + m_sampleNo - m_ons[n]; lbajardsilogic@0: i < count; lbajardsilogic@0: ++i, ++s) { lbajardsilogic@0: lbajardsilogic@0: float lgain = gain; lbajardsilogic@0: float rs = s * ratio; lbajardsilogic@0: unsigned long rsi = lrintf(floor(rs)); lbajardsilogic@0: lbajardsilogic@0: if (rsi >= m_sampleCount) { lbajardsilogic@0: m_ons[n] = -1; lbajardsilogic@0: break; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (m_offs[n] >= 0 && lbajardsilogic@0: long(pos + i + m_sampleNo) > m_offs[n]) { lbajardsilogic@0: lbajardsilogic@0: unsigned long dist = lbajardsilogic@0: pos + i + m_sampleNo - m_offs[n]; lbajardsilogic@0: lbajardsilogic@0: unsigned long releaseFrames = 200; lbajardsilogic@0: if (m_release) { lbajardsilogic@0: releaseFrames = long(*m_release * m_sampleRate + 0.0001); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (dist > releaseFrames) { lbajardsilogic@0: m_ons[n] = -1; lbajardsilogic@0: break; lbajardsilogic@0: } else { lbajardsilogic@0: lgain = lgain * (float)(releaseFrames - dist) / lbajardsilogic@0: (float)releaseFrames; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float sample = m_sampleData[rsi] + lbajardsilogic@0: ((m_sampleData[rsi + 1] - lbajardsilogic@0: m_sampleData[rsi]) * lbajardsilogic@0: (rs - (float)rsi)); lbajardsilogic@0: lbajardsilogic@0: m_output[pos + i] += lgain * sample; lbajardsilogic@0: } lbajardsilogic@0: }