double AudioTransportSource::getCurrentPosition() const
{
    if (sampleRate > 0.0)
        return getNextReadPosition() / sampleRate;

    return 0.0;
}
//==============================================================================
void LoopingAudioSource::setNextReadPosition (int64 newPosition)
{
    const ScopedLock sl (loopPosLock);
    
    if (isLoopingBetweenTimes 
        && getNextReadPosition() > loopStartSample
        && getNextReadPosition() < loopEndSample)
    {
        const int64 numLoopSamples = loopEndSample - loopStartSample;
        
        if (newPosition > loopEndSample)
            newPosition = loopStartSample + ((newPosition - loopEndSample) % numLoopSamples);
        else if (newPosition < loopStartSample)
            newPosition = loopEndSample - ((loopStartSample - newPosition) % numLoopSamples);
    }
    
    input->setNextReadPosition (newPosition);
}
//==============================================================================
void LoopingAudioSource::setLoopTimes (double startTime, double endTime)
{
    jassert (endTime > startTime); // end time has to be after start!
    
    {
        const ScopedLock sl (loopPosLock);
        
        loopStartTime = startTime;
        loopEndTime = endTime;
        
        loopStartSample = (int64) (startTime * currentSampleRate);
        loopEndSample = (int64) (endTime * currentSampleRate);
    }

    // need to update read position based on new limits
    setNextReadPosition (getNextReadPosition());
}
void LoopingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
{
    if (info.numSamples > 0)
    {
        if (isLoopingBetweenTimes)
        {
            const ScopedLock sl (loopPosLock);

            const int64 newStart = getNextReadPosition();
            int64 newEnd = loopStartSample + ((newStart + info.numSamples) % loopEndSample);
            
            if (newStart > loopEndSample)
                newEnd = newStart + info.numSamples;
            
            if (newEnd > newStart)
            {
                input->getNextAudioBlock (info);
            }
            else
            {
                const int64 numEndSamps = loopEndSample - newStart;
                const int64 numStartSamps = newEnd - loopStartSample;
                
                tempInfo.startSample = 0;
                tempInfo.numSamples = (int) numEndSamps;
                input->getNextAudioBlock (tempInfo);
                
                tempInfo.startSample = (int) numEndSamps;
                tempInfo.numSamples = (int) numStartSamps;
                input->setNextReadPosition (loopStartSample);
                input->getNextAudioBlock (tempInfo);

                for (int i = 0; i < info.buffer->getNumChannels(); ++i)
                    info.buffer->copyFrom (i, info.startSample, 
                                           tempBuffer,
                                           i, 0, info.numSamples);
            }
        }
        else
        {
            input->getNextAudioBlock (info);
        }
    }    
}
void LoopMachine::getNextAudioBlockFixedBpm(const AudioSourceChannelInfo& bufferToFill) {
    auto& transport = audioEngine.getTransport();
    bool mainTransportPlaying = transport.isPlaying();
    if (fixedBpmTransport.isPlaying() != mainTransportPlaying) {
        if (mainTransportPlaying)
            fixedBpmTransport.play();
        else
            fixedBpmTransport.stop();
    }
    
    fixedBpmTransport.updateTransport(bufferToFill.numSamples);
    
    bufferToFill.clearActiveBufferRegion();
    
    if (fixedBpmTransport.isPlaying()) {
        float frameStartTicks = fixedBpmTransport.getFrameStartTicks();
        float frameEndTicks = fixedBpmTransport.getFrameEndTicks();
        
        float nextTick = (float) ((int)frameStartTicks + 1);
        float fadeLengthTicks = fixedBpmTransport.millisToTicks(FADE_TIME_MS);
        
        float fadeStartTicks = nextTick - fadeLengthTicks;
        float fadeEndTicks = nextTick;
        
        if (frameStartTicks < fadeStartTicks && frameEndTicks >= fadeStartTicks)
            drainRingBuffer();
//        std::cout << "MPD: CPP: LoopMachine::getNextAudioBlock: reality check! " << ((int)nextTick/4) << std::endl;
        for (int groupIx = 0; groupIx < groupIxToLoopInfo.size(); groupIx++) {
            
            int state = audioState[groupIx];
            int prevState = prevAudioState[groupIx];
            
            if (state == LOOP_INACTIVE && prevState == LOOP_INACTIVE) {
                // we were doing nothing last period, and we're still doing nothing: do nothing
            } else if (state == LOOP_INACTIVE && prevState != LOOP_INACTIVE) {
                // for this loop group, we are fading out: going from an active loop to silence.
                processFadeOut(groupIx, prevState, frameStartTicks, frameEndTicks, fadeStartTicks, fadeEndTicks, bufferToFill);
            } else if (!wasPlaying || (state != LOOP_INACTIVE && prevState == LOOP_INACTIVE)) {
                // for this loop group, we are fading in: going from silence to signal.
				setReaderPos(groupIx, state, fadeStartTicks, frameStartTicks);
                processFadeIn(groupIx, state, frameStartTicks, frameEndTicks, fadeStartTicks, fadeEndTicks, bufferToFill);
            } else if (prevState != state) {
                // for this loop group, the loop being played has switched: do a crossfade
                processFadeOut(groupIx, prevState, frameStartTicks, frameEndTicks, fadeStartTicks, fadeEndTicks, bufferToFill);
				setReaderPos(groupIx, state, fadeStartTicks, frameStartTicks);
                processFadeIn(groupIx, state, frameStartTicks, frameEndTicks, fadeStartTicks, fadeEndTicks, bufferToFill);
            } else {
                // we're playing the same thing as in the last period.
				if (!wasPlaying) {
                    auto src = (*groupIxToLoopInfo[groupIx])[state];
                    src->reader->setNextReadPosition(0);
                }
               processBlock(groupIx, state, 0, bufferToFill.numSamples, bufferToFill);
            }
        }
        
        if (frameStartTicks < fadeEndTicks && frameEndTicks >= fadeEndTicks) {
			bool changes = false;
			for (int groupIx = 0; groupIx < groupIxToLoopInfo.size(); groupIx++) {
				int state = audioState[groupIx];
				
				if (state != LOOP_INACTIVE) {
					auto type = (*groupIxToLoopInfo[groupIx])[state]->type;
					auto src = (*groupIxToLoopInfo[groupIx])[state]->reader;
					
					if (type == LoopType::ONE_SHOT && audioState[groupIx] != LOOP_INACTIVE && src != nullptr && src->getNextReadPosition() >= src->getTotalLength()) {
						// (groupIx, prevState) is done playing.
						// now we need to plop a message in the ring buffer
						
						audioState[groupIx] = LOOP_INACTIVE;
						userState[groupIx] = LOOP_INACTIVE;
						int ix = ++endReserveIx & RINGBUF_SIZE_M1; // == ++reserveIx % RINGBUF_SIZE
						endringbuf[ix][0] = groupIx;
						endringbuf[ix][1] = state;
						endCommitIx++;
						changes = true;
						src->setNextReadPosition(0);
//						std::cout << "MPD: handling messaages audio thread" << std::endl;
					}
				}
			}
			if (changes)
				sendChangeMessage();
			
            std::memcpy(prevAudioState, audioState, sizeof(audioState));
            wasPlaying = true;
        }
        
//        bufferToFill.buffer->applyGain(0, 0, bufferToFill.numSamples, 0.5);
//        bufferToFill.buffer->applyGain(1, 0, bufferToFill.numSamples, 0.5);
        
//        wasPlaying = true;
    }
    
    if (wasPlaying && !fixedBpmTransport.isPlaying())
        wasPlaying = false;
}