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; }