mirror of
https://github.com/arduino/Arduino.git
synced 2024-11-29 10:24:12 +01:00
Some experiments about Audio buffering. DAC class added.
This commit is contained in:
parent
de30e38843
commit
5131431d96
@ -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<AudioClass *>(me);
|
||||
|
||||
//Serial1.print("INT(");
|
||||
audio->enqueue();
|
||||
|
||||
// Update running pointer
|
||||
audio->running = audio->dac->getCurrentQueuePointer();
|
||||
|
||||
audio->debug();
|
||||
//Serial1.print(")");
|
||||
}
|
||||
|
||||
AudioClass Audio(DAC);
|
||||
|
@ -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<const uint32_t*>(data), size/4) * 4; };
|
||||
virtual size_t write(const uint16_t *data, size_t size) { return write(reinterpret_cast<const uint32_t*>(data), size/2) * 2; };
|
||||
virtual size_t write(const int16_t *data, size_t size) { return write(reinterpret_cast<const uint32_t*>(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);
|
||||
|
||||
|
@ -1,3 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) 2012 by Cristian Maglie <c.maglie@bug.st>
|
||||
* 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 <DAC.h>
|
||||
|
||||
@ -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<uint32_t *>(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);
|
||||
|
@ -1,10 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2012 by Cristian Maglie <c.maglie@bug.st>
|
||||
* 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;
|
||||
|
Loading…
Reference in New Issue
Block a user