/* * LibBasicFunctions.cpp * * Created on: Dec 23, 2017 * Author: slascos */ #include "Audio.h" #include "LibBasicFunctions.h" namespace BAGuitar { size_t calcAudioSamples(float milliseconds) { return (size_t)((milliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f); } QueuePosition calcQueuePosition(size_t numSamples) { QueuePosition queuePosition; queuePosition.index = (int)(numSamples / AUDIO_BLOCK_SAMPLES); queuePosition.offset = numSamples % AUDIO_BLOCK_SAMPLES; return queuePosition; } QueuePosition calcQueuePosition(float milliseconds) { size_t numSamples = (int)((milliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f); return calcQueuePosition(numSamples); } size_t calcOffset(QueuePosition position) { return (position.index*AUDIO_BLOCK_SAMPLES) + position.offset; } AudioDelay::AudioDelay(size_t maxSamples) : m_slot(nullptr) { m_type = MemType::MEM_INTERNAL; // INTERNAL memory consisting of audio_block_t data structures. QueuePosition pos = calcQueuePosition(maxSamples); m_ringBuffer = new RingBuffer(pos.index+2); // If the delay is in queue x, we need to overflow into x+1, thus x+2 total buffers. } AudioDelay::AudioDelay(float maxDelayTimeMs) : AudioDelay(calcAudioSamples(maxDelayTimeMs)) { } AudioDelay::AudioDelay(ExtMemSlot *slot) : m_slot(slot) { m_type = MemType::MEM_EXTERNAL; } AudioDelay::~AudioDelay() { if (m_ringBuffer) delete m_ringBuffer; } audio_block_t* AudioDelay::addBlock(audio_block_t *block) { if (m_type == MemType::MEM_INTERNAL) { // INTERNAL memory audio_block_t *blockToRelease = nullptr; // purposefully don't check if block is valid, the ringBuffer can support nullptrs if ( m_ringBuffer->size() >= m_ringBuffer->max_size() ) { // pop before adding blockToRelease = m_ringBuffer->front(); m_ringBuffer->pop_front(); } // add the new buffer m_ringBuffer->push_back(block); return blockToRelease; } else { // EXTERNAL memory //m_slot->writeAdvance16(block->data, AUDIO_BLOCK_SAMPLES); // Audio is stored in reverse in block so we need to write it backwards to external memory // to maintain temporal coherency. int16_t *srcPtr = block->data + AUDIO_BLOCK_SAMPLES - 1; for (int i=0; iwriteAdvance16(*srcPtr); srcPtr--; } return block; } } audio_block_t* AudioDelay::getBlock(size_t index) { if (m_type == MemType::MEM_INTERNAL) { return m_ringBuffer->at(m_ringBuffer->get_index_from_back(index)); } else { return nullptr; } } bool AudioDelay::getSamples(audio_block_t *dest, size_t offset, size_t numSamples) { if (!dest) return false; if (m_type == MemType::MEM_INTERNAL) { QueuePosition position = calcQueuePosition(offset); size_t index = position.index; if (position.offset == 0) { // single transfer audio_block_t *currentQueue = m_ringBuffer->at(m_ringBuffer->get_index_from_back(index)); memcpy(static_cast(dest->data), static_cast(currentQueue->data), numSamples * sizeof(int16_t)); return true; } // Otherwise we need to break the transfer into two memcpy because it will go across two source queues. // Audio is stored in reverse order. That means the first sample (in time) goes in the last location in the audio block. int16_t *destStart = dest->data; audio_block_t *currentQueue; int16_t *srcStart; // Break the transfer into two. Copy the 'older' data first then the 'newer' data with respect to current time. currentQueue = m_ringBuffer->at(m_ringBuffer->get_index_from_back(index+1)); // The latest buffer is at the back. We need index+1 counting from the back. srcStart = (currentQueue->data + AUDIO_BLOCK_SAMPLES - position.offset); size_t numData = position.offset; memcpy(static_cast(destStart), static_cast(srcStart), numData * sizeof(int16_t)); currentQueue = m_ringBuffer->at(m_ringBuffer->get_index_from_back(index)); // now grab the queue where the 'first' data sample was destStart += numData; // we already wrote numData so advance by this much. srcStart = (currentQueue->data); numData = AUDIO_BLOCK_SAMPLES - numData; memcpy(static_cast(destStart), static_cast(srcStart), numData * sizeof(int16_t)); return true; } else { // EXTERNAL Memory if (numSamples <= m_slot->size() ) { int currentPosition = (int)m_slot->getWritePosition() - (int)AUDIO_BLOCK_SAMPLES; if ((int)offset <= currentPosition) { m_slot->setReadPosition(currentPosition - offset); } else { // It's going to wrap around to the end of the slot int readPosition = (int)m_slot->size() + currentPosition - offset; m_slot->setReadPosition((size_t)readPosition); } // write the data to the destination block in reverse int16_t *destPtr = dest->data + AUDIO_BLOCK_SAMPLES-1; for (int i=0; ireadAdvance16(); } return true; } else { // numSampmles is > than total slot size return false; } } } }