Esempio n. 1
0
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));
}
Esempio n. 2
0
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));
}
Esempio n. 3
0
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);
}
Esempio n. 4
0
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);
}
Esempio n. 5
0
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));
}
Esempio n. 6
0
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)));
}
Esempio n. 7
0
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");
}
Esempio n. 8
0
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);
}