From cf7528ee8a9499d1dd68e30543103b10dd99dde0 Mon Sep 17 00:00:00 2001 From: Steve Lascos Date: Sun, 11 Feb 2018 13:02:32 -0500 Subject: [PATCH] Switched some 16-bit function offsets from bytes to words --- src/LibBasicFunctions.cpp | 6 +- src/LibBasicFunctions.h | 180 ++++++++++-------------------------- src/LibMemoryManagement.cpp | 14 +-- src/LibMemoryManagement.h | 18 ++-- 4 files changed, 67 insertions(+), 151 deletions(-) diff --git a/src/LibBasicFunctions.cpp b/src/LibBasicFunctions.cpp index 74ec2c3..9fc0c71 100644 --- a/src/LibBasicFunctions.cpp +++ b/src/LibBasicFunctions.cpp @@ -144,7 +144,7 @@ audio_block_t* AudioDelay::getBlock(size_t index) return ret; } -bool AudioDelay::getSamples(audio_block_t *dest, size_t offset, size_t numSamples) +bool AudioDelay::getSamples(audio_block_t *dest, size_t offsetSamples, size_t numSamples) { if (!dest) { Serial.println("getSamples(): dest is invalid"); @@ -152,7 +152,7 @@ bool AudioDelay::getSamples(audio_block_t *dest, size_t offset, size_t numSample } if (m_type == (MemType::MEM_INTERNAL)) { - QueuePosition position = calcQueuePosition(offset); + QueuePosition position = calcQueuePosition(offsetSamples); size_t index = position.index; audio_block_t *currentQueue0 = m_ringBuffer->at(m_ringBuffer->get_index_from_back(index)); @@ -195,7 +195,7 @@ bool AudioDelay::getSamples(audio_block_t *dest, size_t offset, size_t numSample // EXTERNAL Memory if (numSamples*sizeof(int16_t) <= m_slot->size() ) { int currentPositionBytes = (int)m_slot->getWritePosition() - (int)(AUDIO_BLOCK_SAMPLES*sizeof(int16_t)); - size_t offsetBytes = offset * sizeof(int16_t); + size_t offsetBytes = offsetSamples * sizeof(int16_t); if ((int)offsetBytes <= currentPositionBytes) { m_slot->setReadPosition(currentPositionBytes - offsetBytes); diff --git a/src/LibBasicFunctions.h b/src/LibBasicFunctions.h index 32872cc..830d5e6 100644 --- a/src/LibBasicFunctions.h +++ b/src/LibBasicFunctions.h @@ -131,16 +131,19 @@ public: /// @details when using INTERNAL memory, only supported size is AUDIO_BLOCK_SAMPLES. When using /// EXTERNAL, a size smaller than AUDIO_BLOCK_SAMPLES can be requested. /// @param dest pointer to the target audio block to write the samples to. - /// @param offset data will start being transferred offset samples from the start of the audio buffer + /// @param offsetSamples data will start being transferred offset samples from the start of the audio buffer /// @param numSamples default value is AUDIO_BLOCK_SAMPLES, so typically you don't have to specify this parameter. /// @returns true on success, false on error. - bool getSamples(audio_block_t *dest, size_t offset, size_t numSamples = AUDIO_BLOCK_SAMPLES); + bool getSamples(audio_block_t *dest, size_t offsetSamples, size_t numSamples = AUDIO_BLOCK_SAMPLES); /// When using EXTERNAL memory, this function can return a pointer to the underlying ExtMemSlot object associated /// with the buffer. /// @returns pointer to the underlying ExtMemSlot. ExtMemSlot *getSlot() const { return m_slot; } + /// Ween using INTERNAL memory, thsi function can return a pointer to the underlying RingBuffer that contains + /// audio_block_t * pointers. + /// @returns pointer to the underlying RingBuffer RingBuffer *getRingBuffer() const { return m_ringBuffer; } private: @@ -168,8 +171,19 @@ private: class IirBiQuadFilter { public: IirBiQuadFilter() = delete; + /// Construct a Biquad filter with specified number of stages, coefficients and scaling. + /// @details See CMSIS-DSP documentation for more details + /// @param numStages number of biquad stages. Each stage has 5 coefficients. + /// @param coeffs pointer to an array of Q31 fixed-point coefficients (range -1 to +0.999...) + /// @param coeffShift coeffs are multiplied by 2^coeffShift to support coefficient range scaling IirBiQuadFilter(unsigned numStages, const int32_t *coeffs, int coeffShift = 0); virtual ~IirBiQuadFilter(); + + /// Process the data using the configured IIR filter + /// @details output and input can be the same pointer if in-place modification is desired + /// @param output pointer to where the output results will be written + /// @param input pointer to where the input data will be read from + /// @param numSampmles number of samples to process bool process(int16_t *output, int16_t *input, size_t numSamples); private: const unsigned NUM_STAGES; @@ -180,30 +194,58 @@ private: int32_t *m_state = nullptr; }; - +/**************************************************************************//** + * A High-precision version of IirBiQuadFilter often necessary for complex, multistage + * filters. This class uses CMSIS-DSP biquads with 64-bit internal precision instead + * of 32-bit. + *****************************************************************************/ class IirBiQuadFilterHQ { public: IirBiQuadFilterHQ() = delete; + /// Construct a Biquad filter with specified number of stages, coefficients and scaling. + /// @details See CMSIS-DSP documentation for more details + /// @param numStages number of biquad stages. Each stage has 5 coefficients. + /// @param coeffs pointer to an array of Q31 fixed-point coefficients (range -1 to +0.999...) + /// @param coeffShift coeffs are multiplied by 2^coeffShift to support coefficient range scaling IirBiQuadFilterHQ(unsigned numStages, const int32_t *coeffs, int coeffShift = 0); virtual ~IirBiQuadFilterHQ(); + + /// Process the data using the configured IIR filter + /// @details output and input can be the same pointer if in-place modification is desired + /// @param output pointer to where the output results will be written + /// @param input pointer to where the input data will be read from + /// @param numSampmles number of samples to process bool process(int16_t *output, int16_t *input, size_t numSamples); private: - - const unsigned NUM_STAGES; int32_t *m_coeffs = nullptr; // ARM DSP Math library filter instance arm_biquad_cas_df1_32x64_ins_q31 m_iirCfg; int64_t *m_state = nullptr; - }; +/**************************************************************************//** + * A single-precision floating-point biquad using CMSIS-DSP hardware instructions. + * @details Use this when IirBiQuadFilterHQ is insufficient, since that version + * is still faster with 64-bit fixed-point arithmetic. + *****************************************************************************/ class IirBiQuadFilterFloat { public: IirBiQuadFilterFloat() = delete; + + /// Construct a Biquad filter with specified number of stages and coefficients + /// @details See CMSIS-DSP documentation for more details + /// @param numStages number of biquad stages. Each stage has 5 coefficients. + /// @param coeffs pointer to an array of Q31 fixed-point coefficients (range -1 to +0.999...) IirBiQuadFilterFloat(unsigned numStages, const float *coeffs); virtual ~IirBiQuadFilterFloat(); + + /// Process the data using the configured IIR filter + /// @details output and input can be the same pointer if in-place modification is desired + /// @param output pointer to where the output results will be written + /// @param input pointer to where the input data will be read from + /// @param numberSampmles number of samples to process bool process(float *output, float *input, size_t numSamples); private: const unsigned NUM_STAGES; @@ -215,132 +257,6 @@ private: }; -///**************************************************************************//** -// * Customer RingBuffer with random access -// *****************************************************************************/ -//template -//class RingBuffer { -//public: -// RingBuffer() = delete; -// -// /// Construct a RingBuffer of specified max size -// /// @param maxSize number of entries in ring buffer -// RingBuffer(const size_t maxSize) : m_maxSize(maxSize) { -// m_buffer = new T[maxSize](); -// } -// virtual ~RingBuffer(){ -// if (m_buffer) delete [] m_buffer; -// } -// -// /// Add an element to the back of the queue -// /// @param element element to add to queue -// /// returns 0 if success, otherwise error -// int push_back(T element) { -// -// //Serial.println(String("RingBuffer::push_back...") + m_head + String(":") + m_tail + String(":") + m_size); -// if ( (m_head == m_tail) && (m_size > 0) ) { -// // overflow -// Serial.println("RingBuffer::push_back: overflow"); -// return -1; -// } -// -// m_buffer[m_head] = element; -// if (m_head < (m_maxSize-1) ) { -// m_head++; -// } else { -// m_head = 0; -// } -// m_size++; -// -// return 0; -// } -// -// /// Remove the element at teh front of the queue -// /// @returns 0 if success, otherwise error -// int pop_front() { -// -// if (m_size == 0) { -// // buffer is empty -// //Serial.println("RingBuffer::pop_front: buffer is empty\n"); -// return -1; -// } -// if (m_tail < m_maxSize-1) { -// m_tail++; -// } else { -// m_tail = 0; -// } -// m_size--; -// //Serial.println(String("RingBuffer::pop_front: ") + m_head + String(":") + m_tail + String(":") + m_size); -// return 0; -// } -// -// /// Get the element at the front of the queue -// /// @returns element at front of queue -// T front() const { -// return m_buffer[m_tail]; -// } -// -// /// get the element at the back of the queue -// /// @returns element at the back of the queue -// T back() const { -// return m_buffer[m_head-1]; -// } -// -// /// Get a previously pushed elememt -// /// @param offset zero is last pushed, 1 is second last, etc. -// /// @returns the absolute index corresponding to the requested offset. -// size_t get_index_from_back(size_t offset = 0) const { -// // the target at m_head - 1 - offset or m_maxSize + m_head -1 - offset; -// size_t idx = (m_maxSize + m_head -1 - offset); -// -// if ( idx >= m_maxSize) { -// idx -= m_maxSize; -// } -// -// return idx; -// } -// -// /// get the current size of the queue -// /// @returns size of the queue -// size_t size() const { -// return m_size; -// } -// -// /// get the maximum size the queue can hold -// /// @returns maximum size of the queue -// size_t max_size() const { -// return m_maxSize; -// } -// -// /// get the element at the specified absolute index -// /// @param index element to retrieve from absolute queue position -// /// @returns the request element -// T& operator[] (size_t index) { -// return m_buffer[index]; -// } -// -// /// get the element at the specified absolute index -// /// @param index element to retrieve from absolute queue position -// /// @returns the request element -// T at(size_t index) const { -// return m_buffer[index]; -// } -// -// /// DEBUG: Prints the status of the Ringbuffer. NOte using this much printing will usually cause audio glitches -// void print() const { -// for (int idx=0; idxdata, HEX); -// } -// } -//private: -// size_t m_head=0; ///< back of the queue -// size_t m_tail=0; ///< front of the queue -// size_t m_size=0; ///< current size of the qeueu -// T *m_buffer = nullptr; ///< pointer to the allocated buffer array -// const size_t m_maxSize; ///< maximum size of the queue -//}; - } diff --git a/src/LibMemoryManagement.cpp b/src/LibMemoryManagement.cpp index 07c8c02..ae1fb0d 100644 --- a/src/LibMemoryManagement.cpp +++ b/src/LibMemoryManagement.cpp @@ -44,13 +44,13 @@ bool ExtMemSlot::setWritePosition(size_t offsetBytes) } else { return false; } } -bool ExtMemSlot::write16(size_t offsetBytes, int16_t *dest, size_t numWords) +bool ExtMemSlot::write16(size_t offsetWords, int16_t *src, size_t numWords) { if (!m_valid) { return false; } - size_t writeStart = m_start + offsetBytes; // 2x because int16 is two bytes per data + size_t writeStart = m_start + sizeof(int16_t)*offsetWords; // 2x because int16 is two bytes per data size_t numBytes = sizeof(int16_t)*numWords; if ((writeStart + numBytes-1) <= m_end) { - m_spi->write16(writeStart, reinterpret_cast(dest), numWords); // cast audio data to uint + m_spi->write16(writeStart, reinterpret_cast(src), numWords); // cast audio data to uint return true; } else { // this would go past the end of the memory slot, do not perform the write @@ -68,10 +68,10 @@ bool ExtMemSlot::setReadPosition(size_t offsetBytes) } } -bool ExtMemSlot::zero16(size_t offsetBytes, size_t numWords) +bool ExtMemSlot::zero16(size_t offsetWords, size_t numWords) { if (!m_valid) { return false; } - size_t writeStart = m_start + offsetBytes; + size_t writeStart = m_start + sizeof(int16_t)*offsetWords; size_t numBytes = sizeof(int16_t)*numWords; if ((writeStart + numBytes-1) <= m_end) { m_spi->zero16(writeStart, numWords); // cast audio data to uint @@ -82,10 +82,10 @@ bool ExtMemSlot::zero16(size_t offsetBytes, size_t numWords) } } -bool ExtMemSlot::read16(int16_t *dest, size_t offsetBytes, size_t numWords) +bool ExtMemSlot::read16(size_t offsetWords, int16_t *dest, size_t numWords) { if (!dest) return false; // invalid destination - size_t readOffset = m_start + offsetBytes; + size_t readOffset = m_start + sizeof(int16_t)*offsetWords; size_t numBytes = sizeof(int16_t)*numWords; if ((readOffset + numBytes-1) <= m_end) { diff --git a/src/LibMemoryManagement.h b/src/LibMemoryManagement.h index 8ce5074..574b7d9 100644 --- a/src/LibMemoryManagement.h +++ b/src/LibMemoryManagement.h @@ -81,24 +81,24 @@ public: size_t getReadPosition() const { return m_currentRdPosition-m_start; } /// Write a block of 16-bit data to the memory at the specified offset - /// @param offsetBytes offset in bytes from start of slot - /// @param dataPtr pointer to start of block of 16-bit data + /// @param offsetWords offset in 16-bit words from start of slot + /// @param src pointer to start of block of 16-bit data /// @param numWords number of 16-bit words to transfer /// @returns true on success, else false on error - bool write16(size_t offsetBytes, int16_t *dest, size_t numWords); + bool write16(size_t offsetWords, int16_t *src, size_t numWords); /// Write a block of zeros (16-bit) to the memory at the specified offset - /// @param offsetBytes offset in bytes from start of slot + /// @param offsetWords offset in 16-bit words from start of slot /// @param numWords number of 16-bit words to transfer /// @returns true on success, else false on error - bool zero16(size_t offsetBytes, size_t numWords); + bool zero16(size_t offsetWords, size_t numWords); /// Read a block of 16-bit data from the memory at the specified location + /// @param offsetWords offset in 16-bit words from start of slot /// @param dest pointer to destination for the read data - /// @param offsetBytes offset in bytes from start of slot /// @param numWords number of 16-bit words to transfer /// @returns true on success, else false on error - bool read16(int16_t *dest, size_t offsetBytes, size_t numWords); + bool read16(size_t offsetWords, int16_t *dest, size_t numWords); /// Read the next in memory during circular operation /// @returns the next 16-bit data word in memory @@ -188,7 +188,7 @@ public: /// @param slot a pointer to the global slot object to which memory will be allocated /// @param delayMilliseconds request the amount of memory based on required time for audio samples, rather than number of bytes. /// @param mem specify which external memory to allocate from - /// @param dmaBufferSize When > 0, DMA mode is used with the specified DMA buffer size + /// @param useDma when true, DMA is used for SPI port, else transfers block until complete /// @returns true on success, otherwise false on error bool requestMemory(ExtMemSlot *slot, float delayMilliseconds, BAGuitar::MemSelect mem = BAGuitar::MemSelect::MEM0, bool useDma = false); @@ -196,7 +196,7 @@ public: /// @param slot a pointer to the global slot object to which memory will be allocated /// @param sizeBytes request the amount of memory in bytes to request /// @param mem specify which external memory to allocate from - /// @param dmaBufferSize When > 0, DMA mode is used with the specified DMA buffer size + /// @param useDma when true, DMA is used for SPI port, else transfers block until complete /// @returns true on success, otherwise false on error bool requestMemory(ExtMemSlot *slot, size_t sizeBytes, BAGuitar::MemSelect mem = BAGuitar::MemSelect::MEM0, bool useDma = false);