diff --git a/hardware/arduino/sam/libraries/Audio/Audio.cpp b/hardware/arduino/sam/libraries/Audio/Audio.cpp index cee8d77c0..bd8d99186 100644 --- a/hardware/arduino/sam/libraries/Audio/Audio.cpp +++ b/hardware/arduino/sam/libraries/Audio/Audio.cpp @@ -10,31 +10,126 @@ #include "Audio.h" -void AudioClass::begin(uint32_t sampleRate) { - const uint32_t T = VARIANT_MCK / sampleRate; - dac->begin(T); +class LockDAC { +public: + LockDAC(DACClass *_dac) : dac(_dac) + { dac->disableInterrupts(); }; + ~LockDAC() { dac->enableInterrupts(); }; + DACClass *dac; +}; - currentBuffer = 0; +void AudioClass::begin(uint32_t sampleRate, uint32_t msPreBuffer) { + // Allocate a buffer to keep msPreBuffer milliseconds of audio + bufferSize = msPreBuffer * sampleRate / 1000; + if (bufferSize < 2048) + bufferSize = 2048; + buffer = (uint32_t *) malloc(bufferSize * sizeof(uint32_t)); + last = buffer + bufferSize; + + // Buffering starts from the beginning + running = last; + current = buffer; + next = buffer; + + // Run DAC + dac->begin(VARIANT_MCK / sampleRate); + dac->setOnTransmitEnd_CB(onTransmitEnd, this); } void AudioClass::end() { dac->end(); + free(buffer); } -uint32_t *AudioClass::cook(const uint32_t *buffer, size_t size) { - if (currentBuffer == 0) { - // Use buffer0 - for (int i = 0; i < size; i++) - buffer0[i] = buffer[i] | 0x10000000; - currentBuffer = 1; - return buffer0; +size_t AudioClass::write(const uint32_t *data, size_t size) { + LockDAC lock(dac); + //Serial1.print("WRI("); + const uint32_t TAG = 0x10000000; + int i; + for (i=0; i < size && next != running; i++) { + *next = data[i] | TAG; + next++; + + // Wrap around circular buffer + if (next == last) + next = buffer; + } + debug(); + if (dac->canQueue()) { + enqueue(); + debug(); + } + //Serial1.print(")"); + return i; +} + +void AudioClass::enqueue() { + if (!dac->canQueue()) + // DMA queue full + return; + + if (current == next) + // No data to enqueue + return; + + // If wrapping happened + if (next < current) { + + uint32_t size = last - current; + + if (size < 1024) { + // enqueue the last part of the circular buffer + dac->queueBuffer(current, size); + current = buffer; + next = buffer; + } else { + // Enqueue only a block of 512 + dac->queueBuffer(current, 512); + current += 512; + } + return; + } + + bool aboutToWrap = (last - next) < 512; + uint32_t size = next - current; + + // If buffered data is less than 512 bytes + if (size < 512) { + + // Enqueue all + dac->queueBuffer(current, size); + + if (aboutToWrap) + next = buffer; + current = next; + } else { - // Use buffer1 - for (int i = 0; i < size; i++) - buffer1[i] = buffer[i] | 0x10000000; - currentBuffer = 0; - return buffer1; + + if (aboutToWrap && size < 1024) { + // Enqueue all + dac->queueBuffer(current, size); + next = buffer; + current = buffer; + } else { + // Enqueue only a block of 512 + dac->queueBuffer(current, 512); + current += 512; + } + } } +void AudioClass::onTransmitEnd(void *me) { + AudioClass *audio = reinterpret_cast(me); + + //Serial1.print("INT("); + audio->enqueue(); + + // Update running pointer + audio->running = audio->dac->getCurrentQueuePointer(); + + audio->debug(); + //Serial1.print(")"); +} + AudioClass Audio(DAC); diff --git a/hardware/arduino/sam/libraries/Audio/Audio.h b/hardware/arduino/sam/libraries/Audio/Audio.h index 4eedd5717..93ae0e780 100644 --- a/hardware/arduino/sam/libraries/Audio/Audio.h +++ b/hardware/arduino/sam/libraries/Audio/Audio.h @@ -18,19 +18,33 @@ class AudioClass : public Print { public: AudioClass(DACClass &_dac) : dac(&_dac) { }; - void begin(uint32_t sampleRate); + void begin(uint32_t sampleRate, uint32_t msPreBuffer); void end(); - virtual size_t write(uint8_t c) { /* not implemented */ }; - virtual size_t write(const uint8_t *buffer, size_t size) { return write((uint32_t*) buffer, size/4) * 4; }; - virtual size_t write(const uint16_t *buffer, size_t size) { return write((uint32_t*) buffer, size/2) * 2; }; - virtual size_t write(const int16_t *buffer, size_t size) { return write((uint32_t*) buffer, size/2) * 2; }; - virtual size_t write(const uint32_t *buffer, size_t size) { return dac->queueBuffer(cook(buffer,size), size); }; + virtual size_t write(uint8_t c) { /* not implemented */ }; + virtual size_t write(const uint8_t *data, size_t size) { return write(reinterpret_cast(data), size/4) * 4; }; + virtual size_t write(const uint16_t *data, size_t size) { return write(reinterpret_cast(data), size/2) * 2; }; + virtual size_t write(const int16_t *data, size_t size) { return write(reinterpret_cast(data), size/2) * 2; }; + virtual size_t write(const uint32_t *data, size_t size); + void debug() { +// Serial1.print(running-buffer, DEC); +// Serial1.print(" "); +// Serial1.print(current-buffer, DEC); +// Serial1.print(" "); +// Serial1.print(next-buffer, DEC); +// Serial1.print(" "); +// Serial1.println(last-buffer, DEC); + } private: - uint32_t buffer0[1024]; - uint32_t buffer1[1024]; - int currentBuffer; + void enqueue(); + static void onTransmitEnd(void *me); + uint32_t bufferSize; + uint32_t *buffer; + uint32_t *last; + uint32_t * volatile running; + uint32_t * volatile current; + uint32_t * volatile next; uint32_t *cook(const uint32_t *buffer, size_t size); diff --git a/hardware/arduino/sam/libraries/Audio/DAC.cpp b/hardware/arduino/sam/libraries/Audio/DAC.cpp index ae9056db0..766d7e4c4 100644 --- a/hardware/arduino/sam/libraries/Audio/DAC.cpp +++ b/hardware/arduino/sam/libraries/Audio/DAC.cpp @@ -1,3 +1,12 @@ +/* + * Copyright (c) 2012 by Cristian Maglie + * DAC library for Arduino Due. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either the GNU General Public License version 2 + * or the GNU Lesser General Public License version 2.1, both as + * published by the Free Software Foundation. + */ #include @@ -108,21 +117,25 @@ size_t DACClass::queueBuffer(const uint32_t *buffer, size_t size) { return 0; } -void DACClass::setOnQueueFree_CB(OnQueueFree_CB _cb) { +uint32_t *DACClass::getCurrentQueuePointer() { + return reinterpret_cast(dac->DACC_TPR); +} + +void DACClass::setOnTransmitEnd_CB(OnTransmitEnd_CB _cb, void *_data) { cb = _cb; - if (cb) { - dacc_enable_interrupt(dac, DACC_IER_ENDTX); - } else { + cbData = _data; + if (!cb) dacc_disable_interrupt(dac, DACC_IDR_ENDTX); - } } void DACClass::onService() { uint32_t sr = dac->DACC_ISR; - if (sr & DACC_ISR_ENDTX) { +// if (sr & DACC_ISR_ENDTX) { + // There is a free slot, enqueue data dacc_disable_interrupt(dac, DACC_IDR_ENDTX); - if (cb) cb(); - } + if (cb) + cb(cbData); +// } } DACClass DAC(DACC_INTERFACE, DACC_INTERFACE_ID, DACC_ISR_ID); diff --git a/hardware/arduino/sam/libraries/Audio/DAC.h b/hardware/arduino/sam/libraries/Audio/DAC.h index 925fb2164..d49658415 100644 --- a/hardware/arduino/sam/libraries/Audio/DAC.h +++ b/hardware/arduino/sam/libraries/Audio/DAC.h @@ -1,10 +1,19 @@ +/* + * Copyright (c) 2012 by Cristian Maglie + * DAC library for Arduino Due. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either the GNU General Public License version 2 + * or the GNU Lesser General Public License version 2.1, both as + * published by the Free Software Foundation. + */ #ifndef DAC_INCLUDED #define DAC_INCLUDED #include "Arduino.h" -typedef void (*OnQueueFree_CB)(void); +typedef void (*OnTransmitEnd_CB)(void *data); class DACClass { @@ -15,14 +24,19 @@ public: void end(); bool canQueue(); size_t queueBuffer(const uint32_t *buffer, size_t size); - void setOnQueueFree_CB(OnQueueFree_CB _cb); + uint32_t *getCurrentQueuePointer(); + void setOnTransmitEnd_CB(OnTransmitEnd_CB _cb, void *data); void onService(); + void enableInterrupts() { NVIC_EnableIRQ(isrId); }; + void disableInterrupts() { NVIC_DisableIRQ(isrId); }; + private: Dacc *dac; uint32_t dacId; IRQn_Type isrId; - OnQueueFree_CB cb; + OnTransmitEnd_CB cb; + void *cbData; }; extern DACClass DAC;