EngineBuffer* EngineControl::pickSyncTarget() { EngineMaster* pMaster = getEngineMaster(); if (!pMaster) { return NULL; } QString group = getGroup(); EngineBuffer* pFirstNonplayingDeck = NULL; for (int i = 0; i < m_numDecks.get(); ++i) { QString deckGroup = PlayerManager::groupForDeck(i); if (deckGroup == group) { continue; } EngineChannel* pChannel = pMaster->getChannel(deckGroup); // Only consider channels that have a track loaded and are in the master // mix. if (pChannel && pChannel->isActive() && pChannel->isMaster()) { EngineBuffer* pBuffer = pChannel->getEngineBuffer(); if (pBuffer && pBuffer->getBpm() > 0) { // If the deck is playing then go with it immediately. if (fabs(pBuffer->getSpeed()) > 0) { return pBuffer; } // Otherwise hold out for a deck that might be playing but // remember the first deck that matched our criteria. if (pFirstNonplayingDeck == NULL) { pFirstNonplayingDeck = pBuffer; } } } } // No playing decks have a BPM. Go with the first deck that was stopped but // had a BPM. return pFirstNonplayingDeck; }
EngineBuffer* EngineControl::pickSyncTarget() { EngineMaster* pMaster = getEngineMaster(); if (!pMaster) { return NULL; } EngineSync* pEngineSync = pMaster->getEngineSync(); if (pEngineSync == NULL) { return NULL; } // TODO(rryan): Remove. This is a linear search over groups in // EngineMaster. We should pass the EngineChannel into EngineControl. EngineChannel* pThisChannel = pMaster->getChannel(getGroup()); EngineChannel* pChannel = pEngineSync->pickNonSyncSyncTarget(pThisChannel); return pChannel ? pChannel->getEngineBuffer() : NULL; }
int main(int argc, char **argv) { // initialize the stack trace mechanism with our binary file StackTraceInit(argv[0], -1); main_pid = getpid(); main_thread = pthread_self(); // setting signal handler for catching SIGINT (thus e.g. <CTRL><C>) signal(SIGINT, signal_handler); // register signal handler for all unusual signals // (we will print the stack trace and exit) struct sigaction sact; sigemptyset(&sact.sa_mask); sact.sa_flags = 0; sact.sa_handler = signal_handler; sigaction(SIGSEGV, &sact, NULL); sigaction(SIGBUS, &sact, NULL); sigaction(SIGILL, &sact, NULL); sigaction(SIGFPE, &sact, NULL); sigaction(SIGUSR1, &sact, NULL); sigaction(SIGUSR2, &sact, NULL); lscp_addr = htonl(LSCP_ADDR); lscp_port = htons(LSCP_PORT); // parse and assign command line options parse_options(argc, argv); dmsg(1,("LinuxSampler %s\n", VERSION)); dmsg(1,("Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck\n")); dmsg(1,("Copyright (C) 2005-2007 Christian Schoenebeck\n")); if (tune) { // detect and print system / CPU specific features Features::detect(); dmsg(1,("Detected features: %s\n", Features::featuresAsString().c_str())); // prevent slow denormal FPU modes Features::enableDenormalsAreZeroMode(); } // create LinuxSampler instance dmsg(1,("Creating Sampler...")); pSampler = new Sampler; dmsg(1,("OK\n")); dmsg(1,("Registered sampler engines: %s\n", EngineFactory::AvailableEngineTypesAsString().c_str())); dmsg(1,("Registered MIDI input drivers: %s\n", MidiInputDeviceFactory::AvailableDriversAsString().c_str())); dmsg(1,("Registered audio output drivers: %s\n", AudioOutputDeviceFactory::AvailableDriversAsString().c_str())); dmsg(1,("Registered instrument editors: %s\n", InstrumentEditorFactory::AvailableEditorsAsString().c_str())); // start LSCP network server struct in_addr addr; addr.s_addr = lscp_addr; dmsg(1,("Starting LSCP network server (%s:%d)...", inet_ntoa(addr), ntohs(lscp_port))); pLSCPServer = new LSCPServer(pSampler, lscp_addr, lscp_port); pLSCPServer->StartThread(); pLSCPServer->WaitUntilInitialized(); dmsg(1,("OK\n")); if (profile) { dmsg(1,("Calibrating profiler...")); LinuxSampler::gig::Profiler::Calibrate(); LinuxSampler::gig::Profiler::Reset(); LinuxSampler::gig::Profiler::enable(); dmsg(1,("OK\n")); } printf("LinuxSampler initialization completed. :-)\n\n"); std::list<LSCPEvent::event_t> rtEvents; rtEvents.push_back(LSCPEvent::event_voice_count); rtEvents.push_back(LSCPEvent::event_stream_count); rtEvents.push_back(LSCPEvent::event_buffer_fill); rtEvents.push_back(LSCPEvent::event_total_voice_count); while (true) { if (bPrintStatistics) { const std::set<Engine*>& engines = EngineFactory::EngineInstances(); std::set<Engine*>::iterator itEngine = engines.begin(); for (int i = 0; itEngine != engines.end(); itEngine++, i++) { Engine* pEngine = *itEngine; printf("Engine %d) Voices: %3.3d (Max: %3.3d) Streams: %3.3d (Max: %3.3d)\n", i, pEngine->VoiceCount(), pEngine->VoiceCountMax(), pEngine->DiskStreamCount(), pEngine->DiskStreamCountMax() ); fflush(stdout); } } sleep(1); if (profile) { unsigned int samplingFreq = 48000; //FIXME: hardcoded for now unsigned int bv = LinuxSampler::gig::Profiler::GetBogoVoices(samplingFreq); if (bv != 0) { printf(" BogoVoices: %i \r", bv); fflush(stdout); } } if (LSCPServer::EventSubscribers(rtEvents)) { LSCPServer::LockRTNotify(); std::map<uint,SamplerChannel*> channels = pSampler->GetSamplerChannels(); std::map<uint,SamplerChannel*>::iterator iter = channels.begin(); for (; iter != channels.end(); iter++) { SamplerChannel* pSamplerChannel = iter->second; EngineChannel* pEngineChannel = pSamplerChannel->GetEngineChannel(); if (!pEngineChannel) continue; Engine* pEngine = pEngineChannel->GetEngine(); if (!pEngine) continue; pSampler->fireVoiceCountChanged(iter->first, pEngineChannel->GetVoiceCount()); pSampler->fireStreamCountChanged(iter->first, pEngineChannel->GetDiskStreamCount()); pSampler->fireBufferFillChanged(iter->first, pEngine->DiskStreamBufferFillPercentage()); pSampler->fireTotalVoiceCountChanged(pSampler->GetVoiceCount()); } LSCPServer::UnlockRTNotify(); } } return EXIT_SUCCESS; }
void EngineMaster::processChannels(unsigned int* busChannelConnectionFlags, unsigned int* headphoneOutput, int iBufferSize) { ScopedTimer timer("EngineMaster::processChannels"); QList<ChannelInfo*>::iterator it = m_channels.begin(); QList<ChannelInfo*>::iterator master_it = NULL; // Clear talkover compressor for the next round of gain calculation. m_pTalkoverDucking->clearKeys(); // Find the Sync Master and process it first then process all the slaves // (and skip the master). EngineChannel* pMasterChannel = m_pMasterSync->getMaster(); if (pMasterChannel != NULL) { for (unsigned int channel_number = 0; it != m_channels.end(); ++it, ++channel_number) { ChannelInfo* pChannelInfo = *it; EngineChannel* pChannel = pChannelInfo->m_pChannel; if (!pChannel || !pChannel->isActive()) { continue; } if (pMasterChannel == pChannel) { master_it = it; // Proceed with the processing as below. bool needsProcessing = false; if (pChannel->isMaster()) { busChannelConnectionFlags[pChannel->getOrientation()] |= (1 << channel_number); needsProcessing = true; } // If the channel is enabled for previewing in headphones, copy it // over to the headphone buffer if (pChannel->isPFL()) { *headphoneOutput |= (1 << channel_number); needsProcessing = true; } // Process the buffer if necessary, which it damn well better be if (needsProcessing) { pChannel->process(pChannelInfo->m_pBuffer, iBufferSize); if (m_pTalkoverDucking->getMode() != EngineTalkoverDucking::OFF && pChannel->isTalkover()) { m_pTalkoverDucking->processKey(pChannelInfo->m_pBuffer, iBufferSize); } } break; } } } it = m_channels.begin(); for (unsigned int channel_number = 0; it != m_channels.end(); ++it, ++channel_number) { ChannelInfo* pChannelInfo = *it; EngineChannel* pChannel = pChannelInfo->m_pChannel; // Skip the master since we already processed it. if (it == master_it) { continue; } // Skip inactive channels. if (!pChannel || !pChannel->isActive()) { continue; } bool needsProcessing = false; if (pChannel->isMaster()) { busChannelConnectionFlags[pChannel->getOrientation()] |= (1 << channel_number); needsProcessing = true; } // If the channel is enabled for previewing in headphones, copy it // over to the headphone buffer if (pChannel->isPFL()) { *headphoneOutput |= (1 << channel_number); needsProcessing = true; } // Process the buffer if necessary if (needsProcessing) { pChannel->process(pChannelInfo->m_pBuffer, iBufferSize); if (m_pTalkoverDucking->getMode() != EngineTalkoverDucking::OFF && pChannel->isTalkover()) { m_pTalkoverDucking->processKey(pChannelInfo->m_pBuffer, iBufferSize); } } } }
void EngineMaster::processChannels(int iBufferSize) { m_activeBusChannels[EngineChannel::LEFT].clear(); m_activeBusChannels[EngineChannel::CENTER].clear(); m_activeBusChannels[EngineChannel::RIGHT].clear(); m_activeHeadphoneChannels.clear(); m_activeTalkoverChannels.clear(); m_activeChannels.clear(); ScopedTimer timer("EngineMaster::processChannels"); EngineChannel* pMasterChannel = m_pMasterSync->getMaster(); // Reserve the first place for the master channel which // should be processed first m_activeChannels.append(NULL); int activeChannelsStartIndex = 1; // Nothing at 0 yet for (int i = 0; i < m_channels.size(); ++i) { ChannelInfo* pChannelInfo = m_channels[i]; EngineChannel* pChannel = pChannelInfo->m_pChannel; // Skip inactive channels. if (!pChannel || !pChannel->isActive()) { continue; } if (pChannel->isTalkoverEnabled()) { // talkover is an exclusive channel // once talkover is enabled it is not used in // xFader-Mix m_activeTalkoverChannels.append(pChannelInfo); // Check if we need to fade out the master channel GainCache& gainCache = m_channelMasterGainCache[i]; if (gainCache.m_gain) { gainCache.m_fadeout = true; m_activeBusChannels[pChannel->getOrientation()].append(pChannelInfo); } } else { // Check if we need to fade out the channel GainCache& gainCache = m_channelTalkoverGainCache[i]; if (gainCache.m_gain) { gainCache.m_fadeout = true; m_activeTalkoverChannels.append(pChannelInfo); } if (pChannel->isMasterEnabled() && !pChannelInfo->m_pMuteControl->toBool()) { // the xFader-Mix m_activeBusChannels[pChannel->getOrientation()].append(pChannelInfo); } else { // Check if we need to fade out the channel GainCache& gainCache = m_channelMasterGainCache[i]; if (gainCache.m_gain) { gainCache.m_fadeout = true; m_activeBusChannels[pChannel->getOrientation()].append(pChannelInfo); } } } // If the channel is enabled for previewing in headphones, copy it // over to the headphone buffer if (pChannel->isPflEnabled()) { m_activeHeadphoneChannels.append(pChannelInfo); } else { // Check if we need to fade out the channel GainCache& gainCache = m_channelHeadphoneGainCache[i]; if (gainCache.m_gain) { m_channelHeadphoneGainCache[i].m_fadeout = true; m_activeHeadphoneChannels.append(pChannelInfo); } } // If necessary, add the channel to the list of buffers to process. if (pChannel == pMasterChannel) { // If this is the sync master, it should be processed first. m_activeChannels.replace(0, pChannelInfo); activeChannelsStartIndex = 0; } else { m_activeChannels.append(pChannelInfo); } } // Now that the list is built and ordered, do the processing. for (int i = activeChannelsStartIndex; i < m_activeChannels.size(); ++i) { ChannelInfo* pChannelInfo = m_activeChannels[i]; EngineChannel* pChannel = pChannelInfo->m_pChannel; pChannel->process(pChannelInfo->m_pBuffer, iBufferSize); } // After all the engines have been processed, trigger post-processing // which ensures that all channels are updating certain values at the // same point in time. This prevents sync from failing depending on // if the sync target was processed before or after the sync origin. for (int i = activeChannelsStartIndex; i < m_activeChannels.size(); ++i) { m_activeChannels[i]->m_pChannel->postProcess(iBufferSize); } }
Pool<Voice>::Iterator Engine::LaunchVoice ( LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent, int iLayer, bool ReleaseTriggerVoice, bool VoiceStealing, bool HandleKeyGroupConflicts ) { EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel); int MIDIKey = itNoteOnEvent->Param.Note.Key; //EngineChannel::MidiKey* pKey = &pChannel->pMIDIKeyInfo[MIDIKey]; ::gig::Region* pRegion = pChannel->pInstrument->GetRegion(MIDIKey); // if nothing defined for this key if (!pRegion) return Pool<Voice>::Iterator(); // nothing to do int iKeyGroup = pRegion->KeyGroup; // only need to send a group event from the first voice in a layered region, // as all layers in a region always belongs to the same key group if (HandleKeyGroupConflicts && iLayer == 0) pChannel->HandleKeyGroupConflicts(iKeyGroup, itNoteOnEvent); Voice::type_t VoiceType = Voice::type_normal; // get current dimension values to select the right dimension region //TODO: for stolen voices this dimension region selection block is processed twice, this should be changed //FIXME: controller values for selecting the dimension region here are currently not sample accurate uint DimValues[8] = { 0 }; for (int i = pRegion->Dimensions - 1; i >= 0; i--) { switch (pRegion->pDimensionDefinitions[i].dimension) { case ::gig::dimension_samplechannel: DimValues[i] = 0; //TODO: we currently ignore this dimension break; case ::gig::dimension_layer: DimValues[i] = iLayer; break; case ::gig::dimension_velocity: DimValues[i] = itNoteOnEvent->Param.Note.Velocity; break; case ::gig::dimension_channelaftertouch: DimValues[i] = pChannel->ControllerTable[128]; break; case ::gig::dimension_releasetrigger: VoiceType = (ReleaseTriggerVoice) ? Voice::type_release_trigger : (!iLayer) ? Voice::type_release_trigger_required : Voice::type_normal; DimValues[i] = (uint) ReleaseTriggerVoice; break; case ::gig::dimension_keyboard: DimValues[i] = (uint) (pChannel->CurrentKeyDimension * pRegion->pDimensionDefinitions[i].zones); break; case ::gig::dimension_roundrobin: DimValues[i] = uint(*pChannel->pMIDIKeyInfo[MIDIKey].pRoundRobinIndex % pRegion->pDimensionDefinitions[i].zones); // RoundRobinIndex is incremented for each note on in this Region break; case ::gig::dimension_roundrobinkeyboard: DimValues[i] = uint(pChannel->RoundRobinIndex % pRegion->pDimensionDefinitions[i].zones); // RoundRobinIndex is incremented for each note on break; case ::gig::dimension_random: DimValues[i] = uint(Random() * pRegion->pDimensionDefinitions[i].zones); break; case ::gig::dimension_smartmidi: DimValues[i] = 0; break; case ::gig::dimension_modwheel: DimValues[i] = pChannel->ControllerTable[1]; break; case ::gig::dimension_breath: DimValues[i] = pChannel->ControllerTable[2]; break; case ::gig::dimension_foot: DimValues[i] = pChannel->ControllerTable[4]; break; case ::gig::dimension_portamentotime: DimValues[i] = pChannel->ControllerTable[5]; break; case ::gig::dimension_effect1: DimValues[i] = pChannel->ControllerTable[12]; break; case ::gig::dimension_effect2: DimValues[i] = pChannel->ControllerTable[13]; break; case ::gig::dimension_genpurpose1: DimValues[i] = pChannel->ControllerTable[16]; break; case ::gig::dimension_genpurpose2: DimValues[i] = pChannel->ControllerTable[17]; break; case ::gig::dimension_genpurpose3: DimValues[i] = pChannel->ControllerTable[18]; break; case ::gig::dimension_genpurpose4: DimValues[i] = pChannel->ControllerTable[19]; break; case ::gig::dimension_sustainpedal: DimValues[i] = pChannel->ControllerTable[64]; break; case ::gig::dimension_portamento: DimValues[i] = pChannel->ControllerTable[65]; break; case ::gig::dimension_sostenutopedal: DimValues[i] = pChannel->ControllerTable[66]; break; case ::gig::dimension_softpedal: DimValues[i] = pChannel->ControllerTable[67]; break; case ::gig::dimension_genpurpose5: DimValues[i] = pChannel->ControllerTable[80]; break; case ::gig::dimension_genpurpose6: DimValues[i] = pChannel->ControllerTable[81]; break; case ::gig::dimension_genpurpose7: DimValues[i] = pChannel->ControllerTable[82]; break; case ::gig::dimension_genpurpose8: DimValues[i] = pChannel->ControllerTable[83]; break; case ::gig::dimension_effect1depth: DimValues[i] = pChannel->ControllerTable[91]; break; case ::gig::dimension_effect2depth: DimValues[i] = pChannel->ControllerTable[92]; break; case ::gig::dimension_effect3depth: DimValues[i] = pChannel->ControllerTable[93]; break; case ::gig::dimension_effect4depth: DimValues[i] = pChannel->ControllerTable[94]; break; case ::gig::dimension_effect5depth: DimValues[i] = pChannel->ControllerTable[95]; break; case ::gig::dimension_none: std::cerr << "gig::Engine::LaunchVoice() Error: dimension=none\n" << std::flush; break; default: std::cerr << "gig::Engine::LaunchVoice() Error: Unknown dimension\n" << std::flush; } } // return if this is a release triggered voice and there is no // releasetrigger dimension (could happen if an instrument // change has occured between note on and off) if (ReleaseTriggerVoice && !(VoiceType & Voice::type_release_trigger)) return Pool<Voice>::Iterator(); NoteIterator itNote = GetNotePool()->fromID(itNoteOnEvent->Param.Note.ID); ::gig::DimensionRegion* pDimRgn; if (!itNote->Format.Gig.DimMask) { // normal case ... pDimRgn = pRegion->GetDimensionRegionByValue(DimValues); } else { // some dimension zones were overridden (i.e. by instrument script) ... dmsg(3,("trigger with dim mask=%d val=%d\n", itNote->Format.Gig.DimMask, itNote->Format.Gig.DimBits)); int index = pRegion->GetDimensionRegionIndexByValue(DimValues); index &= ~itNote->Format.Gig.DimMask; index |= itNote->Format.Gig.DimBits & itNote->Format.Gig.DimMask; pDimRgn = pRegion->pDimensionRegions[index & 255]; } if (!pDimRgn) return Pool<Voice>::Iterator(); // error (could not resolve dimension region) // no need to continue if sample is silent if (!pDimRgn->pSample || !pDimRgn->pSample->SamplesTotal) return Pool<Voice>::Iterator(); dmsg(2,("sample -> \"%s\"\n", pDimRgn->pSample->pInfo->Name.c_str())); // allocate a new voice for the key Pool<Voice>::Iterator itNewVoice = GetVoicePool()->allocAppend(); int res = InitNewVoice ( pChannel, pDimRgn, itNoteOnEvent, VoiceType, iLayer, iKeyGroup, ReleaseTriggerVoice, VoiceStealing, itNewVoice ); if (!res) return itNewVoice; return Pool<Voice>::Iterator(); // no free voice or error }
void EngineMaster::process(const CSAMPLE *, const CSAMPLE *pOut, const int iBufferSize) { static bool haveSetName = false; if (!haveSetName) { QThread::currentThread()->setObjectName("Engine"); haveSetName = true; } ScopedTimer t("EngineMaster::process"); CSAMPLE **pOutput = (CSAMPLE**)pOut; Q_UNUSED(pOutput); // Prepare each channel for output // Bitvector of enabled channels const unsigned int maxChannels = 32; unsigned int masterOutput = 0; unsigned int headphoneOutput = 0; // Compute headphone mix // Head phone left/right mix float cf_val = head_mix->get(); float chead_gain = 0.5*(-cf_val+1.); float cmaster_gain = 0.5*(cf_val+1.); // qDebug() << "head val " << cf_val << ", head " << chead_gain // << ", master " << cmaster_gain; Timer timer("EngineMaster::process channels"); QList<ChannelInfo*>::iterator it = m_channels.begin(); for (unsigned int channel_number = 0; it != m_channels.end(); ++it, ++channel_number) { ChannelInfo* pChannelInfo = *it; EngineChannel* pChannel = pChannelInfo->m_pChannel; if (!pChannel->isActive()) { continue; } bool needsProcessing = false; if (pChannel->isMaster()) { masterOutput |= (1 << channel_number); needsProcessing = true; } // If the channel is enabled for previewing in headphones, copy it // over to the headphone buffer if (pChannel->isPFL()) { headphoneOutput |= (1 << channel_number); needsProcessing = true; } // Process the buffer if necessary if (needsProcessing) { pChannel->process(NULL, pChannelInfo->m_pBuffer, iBufferSize); } } timer.elapsed(true); // Mix all the enabled headphone channels together. m_headphoneGain.setGain(chead_gain); mixChannels(headphoneOutput, maxChannels, m_pHead, iBufferSize, &m_headphoneGain); // Calculate the crossfader gains for left and right side of the crossfader float c1_gain, c2_gain; EngineXfader::getXfadeGains(c1_gain, c2_gain, crossfader->get(), xFaderCurve->get(), xFaderCalibration->get(), xFaderMode->get()==MIXXX_XFADER_CONSTPWR, xFaderReverse->get()==1.0); // Now set the gains for overall volume and the left, center, right gains. m_masterGain.setGains(m_pMasterVolume->get(), c1_gain, 1.0, c2_gain); // Perform the master mix mixChannels(masterOutput, maxChannels, m_pMaster, iBufferSize, &m_masterGain); #ifdef __LADSPA__ // LADPSA master effects ladspa->process(m_pMaster, m_pMaster, iBufferSize); #endif // Clipping clipping->process(m_pMaster, m_pMaster, iBufferSize); // Balance values float balright = 1.; float balleft = 1.; float bal = m_pBalance->get(); if (bal>0.) balleft -= bal; else if (bal<0.) balright += bal; // Perform balancing on main out SampleUtil::applyAlternatingGain(m_pMaster, balleft, balright, iBufferSize); // Update VU meter (it does not return anything). Needs to be here so that // master balance is reflected in the VU meter. if (vumeter != NULL) vumeter->process(m_pMaster, m_pMaster, iBufferSize); //Submit master samples to the side chain to do shoutcasting, recording, //etc. (cpu intensive non-realtime tasks) if (m_pSideChain != NULL) { m_pSideChain->writeSamples(m_pMaster, iBufferSize); } // Add master to headphone with appropriate gain SampleUtil::addWithGain(m_pHead, m_pMaster, cmaster_gain, iBufferSize); // Head volume and clipping SampleUtil::applyGain(m_pHead, m_pHeadVolume->get(), iBufferSize); head_clipping->process(m_pHead, m_pHead, iBufferSize); //Master/headphones interleaving is now done in //SoundManager::requestBuffer() - Albert Nov 18/07 // We're close to the end of the callback. Wake up the engine worker // scheduler so that it runs the workers. m_pWorkerScheduler->runWorkers(); }