void CachingReaderWorker::loadTrack(const TrackPointer& pTrack) { //qDebug() << m_group << "CachingReaderWorker::loadTrack() lock acquired for load."; // Emit that a new track is loading, stops the current track emit(trackLoading()); ReaderStatusUpdate status; status.status = TRACK_NOT_LOADED; QString filename = pTrack->getLocation(); if (filename.isEmpty() || !pTrack->exists()) { // Must unlock before emitting to avoid deadlock qDebug() << m_group << "CachingReaderWorker::loadTrack() load failed for\"" << filename << "\", unlocked reader lock"; m_pReaderStatusFIFO->writeBlocking(&status, 1); emit(trackLoadFailed( pTrack, QString("The file '%1' could not be found.").arg(filename))); return; } Mixxx::AudioSourceConfig audioSrcCfg; audioSrcCfg.setChannelCount(CachingReaderChunk::kChannels); m_pAudioSource = openAudioSourceForReading(pTrack, audioSrcCfg); if (m_pAudioSource.isNull()) { m_maxReadableFrameIndex = Mixxx::AudioSource::getMinFrameIndex(); // Must unlock before emitting to avoid deadlock qDebug() << m_group << "CachingReaderWorker::loadTrack() load failed for\"" << filename << "\", file invalid, unlocked reader lock"; m_pReaderStatusFIFO->writeBlocking(&status, 1); emit(trackLoadFailed( pTrack, QString("The file '%1' could not be loaded.").arg(filename))); return; } // Initially assume that the complete content offered by audio source // is available for reading. Later if read errors occur this value will // be decreased to avoid repeated reading of corrupt audio data. m_maxReadableFrameIndex = m_pAudioSource->getMaxFrameIndex(); status.maxReadableFrameIndex = m_maxReadableFrameIndex; status.status = TRACK_LOADED; m_pReaderStatusFIFO->writeBlocking(&status, 1); // Clear the chunks to read list. CachingReaderChunkReadRequest request; while (m_pChunkReadRequestFIFO->read(&request, 1) == 1) { qDebug() << "Skipping read request for " << request.chunk->getIndex(); status.status = CHUNK_READ_INVALID; status.chunk = request.chunk; m_pReaderStatusFIFO->writeBlocking(&status, 1); } // Emit that the track is loaded. const SINT sampleCount = CachingReaderChunk::frames2samples( m_pAudioSource->getFrameCount()); emit(trackLoaded(pTrack, m_pAudioSource->getSamplingRate(), sampleCount)); }
void CachingReaderWorker::loadTrack(const TrackPointer& pTrack) { //qDebug() << m_group << "CachingReaderWorker::loadTrack() lock acquired for load."; // Emit that a new track is loading, stops the current track emit(trackLoading()); ReaderStatusUpdate status; status.status = TRACK_NOT_LOADED; status.chunk = NULL; status.trackFrameCount = 0; QString filename = pTrack->getLocation(); if (filename.isEmpty() || !pTrack->exists()) { // Must unlock before emitting to avoid deadlock qDebug() << m_group << "CachingReaderWorker::loadTrack() load failed for\"" << filename << "\", unlocked reader lock"; m_pReaderStatusFIFO->writeBlocking(&status, 1); emit(trackLoadFailed( pTrack, QString("The file '%1' could not be found.").arg(filename))); return; } Mixxx::AudioSourceConfig audioSrcCfg; audioSrcCfg.channelCountHint = kChunkChannels; m_pAudioSource = openAudioSourceForReading(pTrack, audioSrcCfg); if (m_pAudioSource.isNull()) { // Must unlock before emitting to avoid deadlock qDebug() << m_group << "CachingReaderWorker::loadTrack() load failed for\"" << filename << "\", file invalid, unlocked reader lock"; m_pReaderStatusFIFO->writeBlocking(&status, 1); emit(trackLoadFailed( pTrack, QString("The file '%1' could not be loaded.").arg(filename))); return; } status.trackFrameCount = m_pAudioSource->getFrameCount(); status.status = TRACK_LOADED; m_pReaderStatusFIFO->writeBlocking(&status, 1); // Clear the chunks to read list. ChunkReadRequest request; while (m_pChunkReadRequestFIFO->read(&request, 1) == 1) { qDebug() << "Skipping read request for " << request.chunk->chunk_number; status.status = CHUNK_READ_INVALID; status.chunk = request.chunk; m_pReaderStatusFIFO->writeBlocking(&status, 1); } // Emit that the track is loaded. const SINT sampleCount = m_pAudioSource->getFrameCount() * kChunkChannels; emit(trackLoaded(pTrack, m_pAudioSource->getFrameRate(), sampleCount)); }
CachingReader::CachingReader(const char* group, ConfigObject<ConfigValue>* config) : m_pConfig(config), m_chunkReadRequestFIFO(1024), m_readerStatusFIFO(1024), m_readerStatus(INVALID), m_mruChunk(NULL), m_lruChunk(NULL), m_pRawMemoryBuffer(NULL), m_iTrackNumSamplesCallbackSafe(0) { int rawMemoryBufferLength = CachingReaderWorker::kSamplesPerChunk * maximumChunksInMemory; m_pRawMemoryBuffer = new CSAMPLE[rawMemoryBufferLength]; m_allocatedChunks.reserve(maximumChunksInMemory); CSAMPLE* bufferStart = m_pRawMemoryBuffer; // Divide up the allocated raw memory buffer into total_chunks // chunks. Initialize each chunk to hold nothing and add it to the free // list. for (int i=0; i < maximumChunksInMemory; i++) { Chunk* c = new Chunk; c->chunk_number = -1; c->length = 0; c->data = bufferStart; c->next_lru = NULL; c->prev_lru = NULL; m_chunks.push_back(c); m_freeChunks.push_back(c); bufferStart += CachingReaderWorker::kSamplesPerChunk; } m_pWorker = new CachingReaderWorker(group, &m_chunkReadRequestFIFO, &m_readerStatusFIFO); // Forward signals from worker connect(m_pWorker, SIGNAL(trackLoading()), this, SIGNAL(trackLoading()), Qt::DirectConnection); connect(m_pWorker, SIGNAL(trackLoaded(TrackPointer, int, int)), this, SIGNAL(trackLoaded(TrackPointer, int, int)), Qt::DirectConnection); connect(m_pWorker, SIGNAL(trackLoadFailed(TrackPointer, QString)), this, SIGNAL(trackLoadFailed(TrackPointer, QString)), Qt::DirectConnection); m_pWorker->start(QThread::HighPriority); }
CachingReader::CachingReader(QString group, UserSettingsPointer config) : m_pConfig(config), m_chunkReadRequestFIFO(1024), m_readerStatusFIFO(1024), m_readerStatus(INVALID), m_mruCachingReaderChunk(nullptr), m_lruCachingReaderChunk(nullptr), m_sampleBuffer(CachingReaderChunk::kSamples * maximumCachingReaderChunksInMemory), m_maxReadableFrameIndex(Mixxx::AudioSource::getMinFrameIndex()), m_worker(group, &m_chunkReadRequestFIFO, &m_readerStatusFIFO) { m_allocatedCachingReaderChunks.reserve(m_sampleBuffer.size()); CSAMPLE* bufferStart = m_sampleBuffer.data(); // Divide up the allocated raw memory buffer into total_chunks // chunks. Initialize each chunk to hold nothing and add it to the free // list. for (int i = 0; i < maximumCachingReaderChunksInMemory; ++i) { CachingReaderChunkForOwner* c = new CachingReaderChunkForOwner(bufferStart); m_chunks.push_back(c); m_freeChunks.push_back(c); bufferStart += CachingReaderChunk::kSamples; } // Forward signals from worker connect(&m_worker, SIGNAL(trackLoading()), this, SIGNAL(trackLoading()), Qt::DirectConnection); connect(&m_worker, SIGNAL(trackLoaded(TrackPointer, int, int)), this, SIGNAL(trackLoaded(TrackPointer, int, int)), Qt::DirectConnection); connect(&m_worker, SIGNAL(trackLoadFailed(TrackPointer, QString)), this, SIGNAL(trackLoadFailed(TrackPointer, QString)), Qt::DirectConnection); m_worker.start(QThread::HighPriority); }
void CachingReaderWorker::loadTrack(TrackPointer pTrack) { //qDebug() << m_pGroup << "CachingReaderWorker::loadTrack() lock acquired for load."; // Emit that a new track is loading, stops the current track emit(trackLoading()); ReaderStatusUpdate status; status.status = TRACK_LOADED; status.chunk = NULL; status.trackNumSamples = 0; if (m_pCurrentSoundSource != NULL) { delete m_pCurrentSoundSource; m_pCurrentSoundSource = NULL; } m_iTrackNumSamples = 0; QString filename = pTrack->getLocation(); if (filename.isEmpty() || !pTrack->exists()) { // Must unlock before emitting to avoid deadlock qDebug() << m_pGroup << "CachingReaderWorker::loadTrack() load failed for\"" << filename << "\", unlocked reader lock"; status.status = TRACK_NOT_LOADED; m_pReaderStatusFIFO->writeBlocking(&status, 1); emit(trackLoadFailed( pTrack, QString("The file '%1' could not be found.").arg(filename))); return; } m_pCurrentSoundSource = new SoundSourceProxy(pTrack); bool openSucceeded = (m_pCurrentSoundSource->open() == OK); //Open the song for reading unsigned int trackSampleRate = m_pCurrentSoundSource->getSampleRate(); m_iTrackNumSamples = status.trackNumSamples = m_pCurrentSoundSource->length(); if (!openSucceeded || m_iTrackNumSamples == 0 || trackSampleRate == 0) { // Must unlock before emitting to avoid deadlock qDebug() << m_pGroup << "CachingReaderWorker::loadTrack() load failed for\"" << filename << "\", file invalid, unlocked reader lock"; status.status = TRACK_NOT_LOADED; m_pReaderStatusFIFO->writeBlocking(&status, 1); emit(trackLoadFailed( pTrack, QString("The file '%1' could not be loaded.").arg(filename))); return; } m_pReaderStatusFIFO->writeBlocking(&status, 1); // Clear the chunks to read list. ChunkReadRequest request; while (m_pChunkReadRequestFIFO->read(&request, 1) == 1) { qDebug() << "Skipping read request for " << request.chunk->chunk_number; status.status = CHUNK_READ_INVALID; status.chunk = request.chunk; m_pReaderStatusFIFO->writeBlocking(&status, 1); } // Emit that the track is loaded. emit(trackLoaded(pTrack, trackSampleRate, m_iTrackNumSamples)); }
BaseTrackPlayer::BaseTrackPlayer(QObject* pParent, ConfigObject<ConfigValue>* pConfig, EngineMaster* pMixingEngine, EffectsManager* pEffectsManager, EngineChannel::ChannelOrientation defaultOrientation, QString group, bool defaultMaster, bool defaultHeadphones) : BasePlayer(pParent, group), m_pConfig(pConfig), m_pLoadedTrack(), m_pLowFilter(NULL), m_pMidFilter(NULL), m_pHighFilter(NULL), m_pLowFilterKill(NULL), m_pMidFilterKill(NULL), m_pHighFilterKill(NULL), m_replaygainPending(false) { m_pChannel = new EngineDeck(getGroup(), pConfig, pMixingEngine, pEffectsManager, defaultOrientation); EngineBuffer* pEngineBuffer = m_pChannel->getEngineBuffer(); pMixingEngine->addChannel(m_pChannel); // Set the routing option defaults for the master and headphone mixes. { ControlObject::set(ConfigKey(getGroup(), "master"), (double)defaultMaster); ControlObject::set(ConfigKey(getGroup(), "pfl"), (double)defaultHeadphones); } // Connect our signals and slots with the EngineBuffer's signals and // slots. This will let us know when the reader is done loading a track, and // let us request that the reader load a track. connect(this, SIGNAL(loadTrack(TrackPointer, bool)), pEngineBuffer, SLOT(slotLoadTrack(TrackPointer, bool))); connect(pEngineBuffer, SIGNAL(trackLoaded(TrackPointer)), this, SLOT(slotFinishLoading(TrackPointer))); connect(pEngineBuffer, SIGNAL(trackLoadFailed(TrackPointer, QString)), this, SLOT(slotLoadFailed(TrackPointer, QString))); connect(pEngineBuffer, SIGNAL(trackUnloaded(TrackPointer)), this, SLOT(slotUnloadTrack(TrackPointer))); // Get loop point control objects m_pLoopInPoint = new ControlObjectThread( getGroup(),"loop_start_position"); m_pLoopOutPoint = new ControlObjectThread( getGroup(),"loop_end_position"); // Duration of the current song, we create this one because nothing else does. m_pDuration = new ControlObject(ConfigKey(getGroup(), "duration")); // Waveform controls m_pWaveformZoom = new ControlPotmeter(ConfigKey(group, "waveform_zoom"), WaveformWidgetRenderer::s_waveformMinZoom, WaveformWidgetRenderer::s_waveformMaxZoom); m_pWaveformZoom->set(1.0); m_pWaveformZoom->setStepCount(WaveformWidgetRenderer::s_waveformMaxZoom - WaveformWidgetRenderer::s_waveformMinZoom); m_pWaveformZoom->setSmallStepCount(WaveformWidgetRenderer::s_waveformMaxZoom - WaveformWidgetRenderer::s_waveformMinZoom); m_pEndOfTrack = new ControlObject(ConfigKey(group, "end_of_track")); m_pEndOfTrack->set(0.); m_pPreGain = new ControlObjectSlave(ConfigKey(group, "pregain")); //BPM of the current song m_pBPM = new ControlObjectThread(group, "file_bpm"); m_pKey = new ControlObjectThread(group, "file_key"); m_pReplayGain = new ControlObjectThread(group, "replaygain"); m_pPlay = new ControlObjectThread(group, "play"); connect(m_pPlay, SIGNAL(valueChanged(double)), this, SLOT(slotPlayToggled(double))); }
BaseTrackPlayer::BaseTrackPlayer(QObject* pParent, ConfigObject<ConfigValue>* pConfig, EngineMaster* pMixingEngine, EngineChannel::ChannelOrientation defaultOrientation, QString group, bool defaultMaster, bool defaultHeadphones) : BasePlayer(pParent, group), m_pConfig(pConfig), m_pLoadedTrack() { // Need to strdup the string because EngineChannel will save the pointer, // but we might get deleted before the EngineChannel. TODO(XXX) // pSafeGroupName is leaked. It's like 5 bytes so whatever. const char* pSafeGroupName = strdup(getGroup().toAscii().constData()); m_pChannel = new EngineDeck(pSafeGroupName, pConfig, defaultOrientation); EngineBuffer* pEngineBuffer = m_pChannel->getEngineBuffer(); pMixingEngine->addChannel(m_pChannel); // Set the routing option defaults for the master and headphone mixes. { ControlObjectThreadMain* pMaster = new ControlObjectThreadMain( getGroup(), "master"); pMaster->slotSet(defaultMaster); delete pMaster; ControlObjectThreadMain* pHeadphones = new ControlObjectThreadMain( getGroup(), "pfl"); pHeadphones->slotSet(defaultHeadphones); delete pHeadphones; } // Connect our signals and slots with the EngineBuffer's signals and // slots. This will let us know when the reader is done loading a track, and // let us request that the reader load a track. connect(this, SIGNAL(loadTrack(TrackPointer, bool)), pEngineBuffer, SLOT(slotLoadTrack(TrackPointer, bool))); connect(pEngineBuffer, SIGNAL(trackLoaded(TrackPointer)), this, SLOT(slotFinishLoading(TrackPointer))); connect(pEngineBuffer, SIGNAL(trackLoadFailed(TrackPointer, QString)), this, SLOT(slotLoadFailed(TrackPointer, QString))); connect(pEngineBuffer, SIGNAL(trackUnloaded(TrackPointer)), this, SLOT(slotUnloadTrack(TrackPointer))); //Get cue point control object m_pCuePoint = new ControlObjectThreadMain( getGroup(),"cue_point"); // Get loop point control objects m_pLoopInPoint = new ControlObjectThreadMain( getGroup(),"loop_start_position"); m_pLoopOutPoint = new ControlObjectThreadMain( getGroup(),"loop_end_position"); //Playback position within the currently loaded track (in this player). m_pPlayPosition = new ControlObjectThreadMain( getGroup(), "playposition"); // Duration of the current song, we create this one because nothing else does. m_pDuration = new ControlObject(ConfigKey(getGroup(), "duration")); // Waveform controls m_pWaveformZoom = new ControlPotmeter(ConfigKey(group, "waveform_zoom"), WaveformWidgetRenderer::s_waveformMinZoom, WaveformWidgetRenderer::s_waveformMaxZoom); m_pWaveformZoom->set(1.0); m_pWaveformZoom->setStep(1.0); m_pWaveformZoom->setSmallStep(1.0); m_pEndOfTrack = new ControlObject(ConfigKey(group, "end_of_track")); m_pEndOfTrack->set(0.); //BPM of the current song m_pBPM = new ControlObjectThreadMain(group, "file_bpm"); m_pReplayGain = new ControlObjectThreadMain(group, "replaygain"); m_pPlay = new ControlObjectThreadMain(group, "play"); }
BaseTrackPlayerImpl::BaseTrackPlayerImpl(QObject* pParent, UserSettingsPointer pConfig, EngineMaster* pMixingEngine, EffectsManager* pEffectsManager, VisualsManager* pVisualsManager, EngineChannel::ChannelOrientation defaultOrientation, const QString& group, bool defaultMaster, bool defaultHeadphones) : BaseTrackPlayer(pParent, group), m_pConfig(pConfig), m_pEngineMaster(pMixingEngine), m_pLoadedTrack(), m_replaygainPending(false) { ChannelHandleAndGroup channelGroup = pMixingEngine->registerChannelGroup(group); m_pChannel = new EngineDeck(channelGroup, pConfig, pMixingEngine, pEffectsManager, defaultOrientation); m_pInputConfigured = std::make_unique<ControlProxy>(group, "input_configured", this); m_pPassthroughEnabled = std::make_unique<ControlProxy>(group, "passthrough", this); m_pPassthroughEnabled->connectValueChanged(this, &BaseTrackPlayerImpl::slotPassthroughEnabled); #ifdef __VINYLCONTROL__ m_pVinylControlEnabled = std::make_unique<ControlProxy>(group, "vinylcontrol_enabled", this); m_pVinylControlEnabled->connectValueChanged(this, &BaseTrackPlayerImpl::slotVinylControlEnabled); m_pVinylControlStatus = std::make_unique<ControlProxy>(group, "vinylcontrol_status", this); #endif EngineBuffer* pEngineBuffer = m_pChannel->getEngineBuffer(); pMixingEngine->addChannel(m_pChannel); // Set the routing option defaults for the master and headphone mixes. m_pChannel->setMaster(defaultMaster); m_pChannel->setPfl(defaultHeadphones); // Connect our signals and slots with the EngineBuffer's signals and // slots. This will let us know when the reader is done loading a track, and // let us request that the reader load a track. connect(pEngineBuffer, SIGNAL(trackLoaded(TrackPointer, TrackPointer)), this, SLOT(slotTrackLoaded(TrackPointer, TrackPointer))); connect(pEngineBuffer, SIGNAL(trackLoadFailed(TrackPointer, QString)), this, SLOT(slotLoadFailed(TrackPointer, QString))); // Get loop point control objects m_pLoopInPoint = std::make_unique<ControlProxy>( getGroup(), "loop_start_position", this); m_pLoopOutPoint = std::make_unique<ControlProxy>( getGroup(), "loop_end_position", this); // Duration of the current song, we create this one because nothing else does. m_pDuration = std::make_unique<ControlObject>( ConfigKey(getGroup(), "duration")); // Waveform controls // This acts somewhat like a ControlPotmeter, but the normal _up/_down methods // do not work properly with this CO. m_pWaveformZoom = std::make_unique<ControlObject>( ConfigKey(group, "waveform_zoom")); m_pWaveformZoom->connectValueChangeRequest( this, &BaseTrackPlayerImpl::slotWaveformZoomValueChangeRequest, Qt::DirectConnection); m_pWaveformZoom->set(1.0); m_pWaveformZoomUp = std::make_unique<ControlPushButton>( ConfigKey(group, "waveform_zoom_up")); connect(m_pWaveformZoomUp.get(), SIGNAL(valueChanged(double)), this, SLOT(slotWaveformZoomUp(double))); m_pWaveformZoomDown = std::make_unique<ControlPushButton>( ConfigKey(group, "waveform_zoom_down")); connect(m_pWaveformZoomDown.get(), SIGNAL(valueChanged(double)), this, SLOT(slotWaveformZoomDown(double))); m_pWaveformZoomSetDefault = std::make_unique<ControlPushButton>( ConfigKey(group, "waveform_zoom_set_default")); connect(m_pWaveformZoomSetDefault.get(), SIGNAL(valueChanged(double)), this, SLOT(slotWaveformZoomSetDefault(double))); m_pPreGain = std::make_unique<ControlProxy>(group, "pregain", this); // BPM of the current song m_pFileBPM = std::make_unique<ControlProxy>(group, "file_bpm", this); m_pKey = std::make_unique<ControlProxy>(group, "file_key", this); m_pReplayGain = std::make_unique<ControlProxy>(group, "replaygain", this); m_pPlay = std::make_unique<ControlProxy>(group, "play", this); m_pPlay->connectValueChanged(this, &BaseTrackPlayerImpl::slotPlayToggled); pVisualsManager->addDeck(group); }