[sky9x/Taranis] Audio mixer!
This commit is contained in:
+1
-1
@@ -1252,7 +1252,7 @@ OBJS = $(TMP:.s=.o)
|
||||
@echo
|
||||
@echo $(MSG_COMPILING) $@
|
||||
$(CC) $(ARMCPPFLAGS) $< -o allsrc.o
|
||||
$(CC) $(OBJS) allsrc.o -mcpu=cortex-m3 -mthumb -nostartfiles -T$(LDSCRIPT) -Wl,-Map=$(TARGET).map,--cref,--no-warn-mismatch -o $@
|
||||
$(CC) $(OBJS) allsrc.o -mcpu=cortex-m3 -mthumb -nostartfiles -lm -T$(LDSCRIPT) -Wl,-Map=$(TARGET).map,--cref,--no-warn-mismatch -o $@
|
||||
endif
|
||||
|
||||
# Target: clean project.
|
||||
|
||||
+317
-367
@@ -35,22 +35,7 @@
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
// Must NOT be in flash, PDC needs a RAM source.
|
||||
// Amplitude reduced to 30% to allow for voice volume
|
||||
uint16_t Sine_values[] =
|
||||
{
|
||||
2048,2085,2123,2160,2197,2233,2268,2303,2336,2369,
|
||||
2400,2430,2458,2485,2510,2533,2554,2573,2590,2605,
|
||||
2618,2629,2637,2643,2646,2648,2646,2643,2637,2629,
|
||||
2618,2605,2590,2573,2554,2533,2510,2485,2458,2430,
|
||||
2400,2369,2336,2303,2268,2233,2197,2160,2123,2085,
|
||||
2048,2010,1972,1935,1898,1862,1826,1792,1758,1726,
|
||||
1695,1665,1637,1610,1585,1562,1541,1522,1505,1490,
|
||||
1477,1466,1458,1452,1448,1448,1448,1452,1458,1466,
|
||||
1477,1490,1505,1522,1541,1562,1585,1610,1637,1665,
|
||||
1695,1726,1758,1792,1826,1862,1898,1935,1972,2010
|
||||
};
|
||||
#include <math.h>
|
||||
|
||||
#if defined(SDCARD)
|
||||
const char * audioFilenames[] = {
|
||||
@@ -221,8 +206,8 @@ bool isAudioFileAvailable(uint32_t i, char * filename)
|
||||
#define isAudioFileAvailable(i, f) false
|
||||
#endif
|
||||
|
||||
uint16_t alawTable[256];
|
||||
uint16_t ulawTable[256];
|
||||
int16_t alawTable[256];
|
||||
int16_t ulawTable[256];
|
||||
|
||||
#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
|
||||
#define QUANT_MASK (0xf) /* Quantization field mask. */
|
||||
@@ -265,8 +250,8 @@ static short ulaw2linear(unsigned char u_val)
|
||||
void codecsInit()
|
||||
{
|
||||
for (uint32_t i=0; i<256; i++) {
|
||||
alawTable[i] = (0x8000 + alaw2linear(i)) >> 4;
|
||||
ulawTable[i] = (0x8000 + ulaw2linear(i)) >> 4;
|
||||
alawTable[i] = alaw2linear(i);
|
||||
ulawTable[i] = ulaw2linear(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,19 +260,13 @@ AudioQueue audioQueue;
|
||||
AudioQueue::AudioQueue()
|
||||
{
|
||||
memset(this, 0, sizeof(AudioQueue));
|
||||
prioIdx = -1;
|
||||
}
|
||||
|
||||
void AudioQueue::start()
|
||||
{
|
||||
state = AUDIO_SLEEPING;
|
||||
CoSetFlag(audioFlag);
|
||||
state = 1;
|
||||
}
|
||||
|
||||
void audioTimerHandle(void)
|
||||
{
|
||||
CoSetFlag(audioFlag);
|
||||
}
|
||||
|
||||
#define CODEC_ID_PCM_S16LE 1
|
||||
#define CODEC_ID_PCM_ALAW 6
|
||||
@@ -297,9 +276,11 @@ void audioTimerHandle(void)
|
||||
void audioTask(void* pdata)
|
||||
{
|
||||
while (!audioQueue.started()) {
|
||||
CoWaitForSingleFlag(audioFlag, 0);
|
||||
CoTickDelay(1);
|
||||
}
|
||||
|
||||
setSampleRate(AUDIO_SAMPLE_RATE);
|
||||
|
||||
#if defined(SDCARD)
|
||||
if (!unexpectedShutdown) {
|
||||
codecsInit();
|
||||
@@ -309,262 +290,243 @@ void audioTask(void* pdata)
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
|
||||
CoWaitForSingleFlag(audioFlag, 0);
|
||||
|
||||
audioQueue.wakeup();
|
||||
|
||||
CoTickDelay(2/*4ms*/);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#define WAV_BUFFER_SIZE 512
|
||||
uint16_t wavSamplesArray[3*WAV_BUFFER_SIZE];
|
||||
uint16_t * wavSamplesBuffer = &wavSamplesArray[0];
|
||||
uint16_t * nextAudioData = NULL;
|
||||
uint16_t nextAudioSize = 0;
|
||||
void AudioQueue::pushBuffer(AudioBuffer *buffer)
|
||||
{
|
||||
buffer->state = AUDIO_BUFFER_FILLED;
|
||||
|
||||
if (dacQueue(buffer))
|
||||
buffer->state = AUDIO_BUFFER_PLAYING;
|
||||
|
||||
bufferWIdx = nextBufferIdx(bufferWIdx);
|
||||
}
|
||||
|
||||
void mix(uint16_t * result, int sample, unsigned int fade)
|
||||
{
|
||||
*result = limit(0, *result + ((sample >> fade) >> 4), 4095);
|
||||
}
|
||||
|
||||
#if defined(SDCARD) && !defined(SIMU)
|
||||
|
||||
#define RIFF_CHUNK_SIZE 12
|
||||
uint8_t wavBuffer[AUDIO_BUFFER_SIZE*2];
|
||||
|
||||
void AudioQueue::sdWakeup(AudioContext & context)
|
||||
int AudioQueue::mixWav(AudioContext &context, AudioBuffer *buffer, unsigned int weight)
|
||||
{
|
||||
if (!nextAudioData) {
|
||||
AudioFragment & fragment = context.fragment;
|
||||
FRESULT result = FR_OK;
|
||||
UINT read = 0;
|
||||
if (fragment.file[1]) {
|
||||
result = f_open(&context.wavFile, fragment.file, FA_OPEN_EXISTING | FA_READ);
|
||||
fragment.file[1] = 0;
|
||||
if (result == FR_OK) {
|
||||
result = f_read(&context.wavFile, (uint8_t *)wavSamplesBuffer, RIFF_CHUNK_SIZE+8, &read);
|
||||
if (result == FR_OK && read == RIFF_CHUNK_SIZE+8 && !memcmp(wavSamplesBuffer, "RIFF", 4) && !memcmp(wavSamplesBuffer+4/*short*/, "WAVEfmt ", 8)) {
|
||||
uint32_t size = *((uint32_t *)(wavSamplesBuffer+8/*short*/));
|
||||
result = (size < 256 ? f_read(&context.wavFile, (uint8_t *)wavSamplesBuffer, size+8, &read) : FR_DENIED);
|
||||
if (result == FR_OK && read == size+8) {
|
||||
context.pcmCodec = wavSamplesBuffer[0];
|
||||
context.pcmFreq = wavSamplesBuffer[2];
|
||||
uint32_t *wavSamplesPtr = (uint32_t *)(wavSamplesBuffer + size/2);
|
||||
uint32_t size = wavSamplesPtr[1];
|
||||
while (result == FR_OK && memcmp(wavSamplesPtr, "data", 4) != 0) {
|
||||
result = (size < 256 ? f_read(&context.wavFile, (uint8_t *)wavSamplesBuffer, size+8, &read) : FR_DENIED);
|
||||
if (read != size+8) result = FR_DENIED;
|
||||
wavSamplesPtr = (uint32_t *)(wavSamplesBuffer + size/2);
|
||||
size = wavSamplesPtr[1];
|
||||
}
|
||||
context.wavSize = size;
|
||||
FRESULT result = FR_OK;
|
||||
UINT read = 0;
|
||||
AudioFragment & fragment = context.fragment;
|
||||
|
||||
if (fragment.file[1]) {
|
||||
result = f_open(&context.state.wav.file, fragment.file, FA_OPEN_EXISTING | FA_READ);
|
||||
fragment.file[1] = 0;
|
||||
if (result == FR_OK) {
|
||||
result = f_read(&context.state.wav.file, wavBuffer, RIFF_CHUNK_SIZE+8, &read);
|
||||
if (result == FR_OK && read == RIFF_CHUNK_SIZE+8 && !memcmp(wavBuffer, "RIFF", 4) && !memcmp(wavBuffer+8, "WAVEfmt ", 8)) {
|
||||
uint32_t size = *((uint32_t *)(wavBuffer+16));
|
||||
result = (size < 256 ? f_read(&context.state.wav.file, wavBuffer, size+8, &read) : FR_DENIED);
|
||||
if (result == FR_OK && read == size+8) {
|
||||
context.state.wav.codec = ((uint16_t *)wavBuffer)[0];
|
||||
context.state.wav.freq = ((uint16_t *)wavBuffer)[2];
|
||||
uint32_t *wavSamplesPtr = (uint32_t *)(wavBuffer + size);
|
||||
uint32_t size = wavSamplesPtr[1];
|
||||
if (context.state.wav.freq != 0 && context.state.wav.freq * (AUDIO_SAMPLE_RATE / context.state.wav.freq) == AUDIO_SAMPLE_RATE) {
|
||||
context.state.wav.resampleRatio = (AUDIO_SAMPLE_RATE / context.state.wav.freq);
|
||||
context.state.wav.readSize = (context.state.wav.codec == CODEC_ID_PCM_S16LE ? 2*AUDIO_BUFFER_SIZE : AUDIO_BUFFER_SIZE) / context.state.wav.resampleRatio;
|
||||
}
|
||||
else {
|
||||
result = FR_DENIED;
|
||||
}
|
||||
while (result == FR_OK && memcmp(wavSamplesPtr, "data", 4) != 0) {
|
||||
result = (size < 256 ? f_read(&context.state.wav.file, wavBuffer, size+8, &read) : FR_DENIED);
|
||||
if (read != size+8) result = FR_DENIED;
|
||||
wavSamplesPtr = (uint32_t *)(wavBuffer + size);
|
||||
size = wavSamplesPtr[1];
|
||||
}
|
||||
context.state.wav.size = size;
|
||||
}
|
||||
else {
|
||||
result = FR_DENIED;
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = FR_DENIED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
read = 0;
|
||||
uint16_t bufsize = (context.pcmCodec == CODEC_ID_PCM_S16LE ? WAV_BUFFER_SIZE * sizeof(uint16_t) : WAV_BUFFER_SIZE);
|
||||
read = 0;
|
||||
if (result == FR_OK) {
|
||||
result = f_read(&context.state.wav.file, wavBuffer, context.state.wav.readSize, &read);
|
||||
if (result == FR_OK) {
|
||||
result = f_read(&context.wavFile, (uint8_t *)wavSamplesBuffer, bufsize, &read);
|
||||
}
|
||||
|
||||
if (result != FR_OK) {
|
||||
dacStop();
|
||||
memset(&fragment, 0, sizeof(fragment));
|
||||
f_close(&context.wavFile);
|
||||
if (context.pcmFreq) {
|
||||
CoSetTmrCnt(audioTimer, (WAV_BUFFER_SIZE * 1000) / context.pcmFreq, 0);
|
||||
CoStartTmr(audioTimer);
|
||||
CoClearFlag(audioFlag);
|
||||
if (read > context.state.wav.size) {
|
||||
read = context.state.wav.size;
|
||||
}
|
||||
else {
|
||||
CoSetFlag(audioFlag);
|
||||
}
|
||||
state = AUDIO_RESUMING;
|
||||
}
|
||||
else {
|
||||
if (read > context.wavSize)
|
||||
read = context.wavSize;
|
||||
context.wavSize -= read;
|
||||
context.state.wav.size -= read;
|
||||
|
||||
if (read != bufsize) {
|
||||
memset(&fragment, 0, sizeof(fragment));
|
||||
f_close(&context.wavFile);
|
||||
if (read != context.state.wav.readSize) {
|
||||
f_close(&context.state.wav.file);
|
||||
fragment.clear();
|
||||
}
|
||||
|
||||
setFrequency(context.pcmFreq);
|
||||
|
||||
if (context.pcmCodec == CODEC_ID_PCM_S16LE) {
|
||||
uint16_t * samples = buffer->data;
|
||||
if (context.state.wav.codec == CODEC_ID_PCM_S16LE) {
|
||||
read /= 2;
|
||||
uint32_t i = 0;
|
||||
for (; i < read; i++)
|
||||
wavSamplesBuffer[i] = ((uint16_t) 0x8000 + ((int16_t) (wavSamplesBuffer[i]))) >> 4;
|
||||
for (; i < WAV_BUFFER_SIZE; i++)
|
||||
wavSamplesBuffer[i] = 0x8000;
|
||||
for (uint32_t i=0; i<read; i++) {
|
||||
for (uint8_t j=0; j<context.state.wav.resampleRatio; j++)
|
||||
mix(samples++, ((int16_t *)wavBuffer)[i], weight);
|
||||
}
|
||||
}
|
||||
else if (context.pcmCodec == CODEC_ID_PCM_ALAW) {
|
||||
int32_t i;
|
||||
for (i = read - 1; i >= 0; i--)
|
||||
wavSamplesBuffer[i] = alawTable[((uint8_t *) wavSamplesBuffer)[i]];
|
||||
for (i = read; i < WAV_BUFFER_SIZE; i++)
|
||||
wavSamplesBuffer[i] = 0x8000;
|
||||
else if (context.state.wav.codec == CODEC_ID_PCM_ALAW) {
|
||||
for (uint32_t i=0; i<read; i++)
|
||||
for (uint8_t j=0; j<context.state.wav.resampleRatio; j++)
|
||||
mix(samples++, alawTable[wavBuffer[i]], weight);
|
||||
}
|
||||
else if (context.pcmCodec == CODEC_ID_PCM_MULAW) {
|
||||
int32_t i;
|
||||
for (i = read - 1; i >= 0; i--)
|
||||
wavSamplesBuffer[i] = ulawTable[((uint8_t *) wavSamplesBuffer)[i]];
|
||||
for (i = read; i < WAV_BUFFER_SIZE; i++)
|
||||
wavSamplesBuffer[i] = 0x8000;
|
||||
else if (context.state.wav.codec == CODEC_ID_PCM_MULAW) {
|
||||
for (uint32_t i=0; i<read; i++)
|
||||
for (uint8_t j=0; j<context.state.wav.resampleRatio; j++)
|
||||
mix(samples++, ulawTable[wavBuffer[i]], weight);
|
||||
}
|
||||
|
||||
read /= 2;
|
||||
|
||||
if (read) {
|
||||
state = AUDIO_PLAYING_WAV;
|
||||
if (dacQueue(wavSamplesBuffer, read)) {
|
||||
CoSetFlag(audioFlag);
|
||||
}
|
||||
else {
|
||||
nextAudioSize = read;
|
||||
nextAudioData = wavSamplesBuffer;
|
||||
#if defined(PCBSKY9X)
|
||||
dacStart();
|
||||
#endif
|
||||
CoSetTmrCnt(audioTimer, (WAV_BUFFER_SIZE * 500) / context.pcmFreq, 0);
|
||||
CoStartTmr(audioTimer);
|
||||
}
|
||||
wavSamplesBuffer += WAV_BUFFER_SIZE;
|
||||
if (wavSamplesBuffer >= wavSamplesArray + 3 * WAV_BUFFER_SIZE)
|
||||
wavSamplesBuffer = wavSamplesArray;
|
||||
}
|
||||
else {
|
||||
state = AUDIO_RESUMING;
|
||||
CoSetFlag(audioFlag);
|
||||
}
|
||||
return samples - buffer->data;
|
||||
}
|
||||
}
|
||||
else {
|
||||
CoSetTmrCnt(audioTimer, 5/*10ms*/, 0);
|
||||
CoStartTmr(audioTimer);
|
||||
}
|
||||
|
||||
fragment.clear();
|
||||
return -result;
|
||||
}
|
||||
#else
|
||||
int AudioQueue::mixWav(AudioContext &context, AudioBuffer *buffer, unsigned int weight)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int AudioQueue::mixTone(AudioContext &context, AudioBuffer *buffer, unsigned int weight)
|
||||
{
|
||||
AudioFragment & fragment = context.fragment;
|
||||
unsigned int duration = 0;
|
||||
int result = 0;
|
||||
|
||||
if (fragment.tone.duration > 0) {
|
||||
result = AUDIO_BUFFER_SIZE;
|
||||
if (fragment.tone.freq && context.state.tone.freq!=fragment.tone.freq && (!fragment.tone.freqIncr || abs(context.state.tone.freq-fragment.tone.freq) > 100)) {
|
||||
int periods = DIM(context.state.tone.points) / ((AUDIO_SAMPLE_RATE / fragment.tone.freq) + 1);
|
||||
context.state.tone.count = (periods * AUDIO_SAMPLE_RATE) / fragment.tone.freq;
|
||||
if (context.state.tone.idx >= context.state.tone.count) context.state.tone.idx = 0;
|
||||
double t = (M_PI * 2 * periods) / context.state.tone.count;
|
||||
for (unsigned int i=0; i<context.state.tone.count; i++)
|
||||
context.state.tone.points[i] = sin(t*i) * 16000;
|
||||
}
|
||||
|
||||
if (fragment.tone.freqIncr)
|
||||
fragment.tone.freq += AUDIO_BUFFER_DURATION * fragment.tone.freqIncr;
|
||||
else
|
||||
fragment.tone.freq = 0;
|
||||
|
||||
duration = min<unsigned int>(AUDIO_BUFFER_DURATION, fragment.tone.duration);
|
||||
for (unsigned int i=0; i<duration*(AUDIO_BUFFER_SIZE/AUDIO_BUFFER_DURATION); i++) {
|
||||
mix(&buffer->data[i], context.state.tone.points[context.state.tone.idx], weight);
|
||||
context.state.tone.idx = context.state.tone.idx + 1;
|
||||
if (context.state.tone.idx >= context.state.tone.count) context.state.tone.idx = 0;
|
||||
}
|
||||
|
||||
fragment.tone.duration -= duration;
|
||||
if (fragment.tone.duration > 0)
|
||||
return AUDIO_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
if (fragment.tone.pause > 0) {
|
||||
result = AUDIO_BUFFER_SIZE;
|
||||
fragment.tone.pause -= min<unsigned int>(AUDIO_BUFFER_DURATION-duration, fragment.tone.pause);
|
||||
if (fragment.tone.pause > 0)
|
||||
return AUDIO_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
fragment.clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
int AudioQueue::mixAudioContext(AudioContext &context, AudioBuffer *buffer, unsigned int weight)
|
||||
{
|
||||
int result;
|
||||
AudioFragment & fragment = context.fragment;
|
||||
|
||||
if (fragment.type == FRAGMENT_FILE) {
|
||||
result = mixWav(context, buffer, weight);
|
||||
}
|
||||
else if (fragment.type == FRAGMENT_TONE) {
|
||||
result = mixTone(context, buffer, weight);
|
||||
}
|
||||
else {
|
||||
result = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void AudioQueue::wakeup()
|
||||
{
|
||||
AudioFragment & fragment = currentContext.fragment;
|
||||
int result;
|
||||
AudioBuffer *buffer = getEmptyBuffer();
|
||||
if (buffer) {
|
||||
unsigned int weight = 0;
|
||||
int size = 0;
|
||||
|
||||
if (prioIdx >= 0) {
|
||||
memset(&fragment, 0, sizeof(fragment));
|
||||
}
|
||||
|
||||
#if defined(SDCARD) && !defined(SIMU)
|
||||
if (fragment.file[0]) {
|
||||
sdWakeup(currentContext);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (fragment.duration > 0) {
|
||||
if (state != AUDIO_PLAYING_TONE) {
|
||||
state = AUDIO_PLAYING_TONE;
|
||||
dacFill(Sine_values, 50/*100 samples*/);
|
||||
// write silence in the buffer
|
||||
for (uint32_t i=0; i<AUDIO_BUFFER_SIZE; i++) {
|
||||
buffer->data[i] = 0x8000 >> 4; /* silence */
|
||||
}
|
||||
|
||||
setFrequency(fragment.freq * 6100 / 4);
|
||||
// mix the foreground context
|
||||
result = mixAudioContext(foregroundContext, buffer, weight);
|
||||
if (result > 0) {
|
||||
size = max(size, result);
|
||||
weight += 1;
|
||||
}
|
||||
|
||||
if (fragment.freqIncr) {
|
||||
CoSetTmrCnt(audioTimer, 2, 0);
|
||||
fragment.freq += fragment.freqIncr;
|
||||
fragment.duration--;
|
||||
// mix the normal context
|
||||
result = mixAudioContext(currentContext, buffer, weight);
|
||||
if (result > 0) {
|
||||
size = max(size, result);
|
||||
weight += 1;
|
||||
}
|
||||
else {
|
||||
CoSetTmrCnt(audioTimer, fragment.duration*2, 0);
|
||||
fragment.duration = 0;
|
||||
CoEnterMutexSection(audioMutex);
|
||||
if (ridx != widx) {
|
||||
currentContext.clear();
|
||||
currentContext.fragment = fragments[ridx];
|
||||
if (!fragments[ridx].repeat--) {
|
||||
ridx = (ridx + 1) % AUDIO_QUEUE_LENGTH;
|
||||
}
|
||||
}
|
||||
CoLeaveMutexSection(audioMutex);
|
||||
}
|
||||
dacStart();
|
||||
CoClearFlag(audioFlag);
|
||||
CoStartTmr(audioTimer);
|
||||
}
|
||||
else if (fragment.pause > 0) {
|
||||
state = AUDIO_PLAYING_TONE;
|
||||
CoSetTmrCnt(audioTimer, fragment.pause*2, 0);
|
||||
fragment.pause = 0;
|
||||
dacStop();
|
||||
CoClearFlag(audioFlag);
|
||||
CoStartTmr(audioTimer);
|
||||
}
|
||||
#if defined(SDCARD) && !defined(SIMU)
|
||||
else if (ridx == widx && prioIdx < 0 && backgroundContext.fragment.file[0]) {
|
||||
|
||||
// mix the background context
|
||||
if (!isFunctionActive(FUNC_BACKGND_MUSIC_PAUSE)) {
|
||||
sdWakeup(backgroundContext);
|
||||
}
|
||||
else {
|
||||
CoSetTmrCnt(audioTimer, 5/*10ms*/, 0);
|
||||
CoStartTmr(audioTimer);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (ridx == widx && prioIdx < 0 && backgroundContext.fragment.duration > 0) {
|
||||
if (state != AUDIO_PLAYING_TONE) {
|
||||
state = AUDIO_PLAYING_TONE;
|
||||
dacFill(Sine_values, 50/*100 samples*/);
|
||||
}
|
||||
CoSetTmrCnt(audioTimer, backgroundContext.fragment.duration*2, 0);
|
||||
backgroundContext.fragment.duration = 0;
|
||||
setFrequency(backgroundContext.fragment.freq * 6100 / 4);
|
||||
dacStart();
|
||||
CoClearFlag(audioFlag);
|
||||
CoStartTmr(audioTimer);
|
||||
}
|
||||
else {
|
||||
CoEnterMutexSection(audioMutex);
|
||||
|
||||
if (ridx != widx) {
|
||||
if (prioIdx >= 0) {
|
||||
ridx = prioIdx;
|
||||
prioIdx = -1;
|
||||
dacStop();
|
||||
CoClearFlag(audioFlag);
|
||||
nextAudioData = NULL;
|
||||
if (state == AUDIO_PLAYING_WAV) {
|
||||
state = AUDIO_RESUMING;
|
||||
CoSetTmrCnt(audioTimer, 1/*2ms*/ + (WAV_BUFFER_SIZE * 1000) / getFrequency(), 0);
|
||||
CoStartTmr(audioTimer);
|
||||
}
|
||||
else if (state == AUDIO_PLAYING_TONE) {
|
||||
state = AUDIO_RESUMING;
|
||||
CoSetTmrCnt(audioTimer, 1/*2ms*/, 0);
|
||||
CoStartTmr(audioTimer);
|
||||
}
|
||||
else {
|
||||
CoSetFlag(audioFlag);
|
||||
}
|
||||
}
|
||||
else {
|
||||
CoSetFlag(audioFlag);
|
||||
}
|
||||
|
||||
memcpy(&fragment, &fragments[ridx], sizeof(fragment));
|
||||
if (!fragments[ridx].repeat--) {
|
||||
ridx = (ridx + 1) % AUDIO_QUEUE_LENGTH;
|
||||
result = mixAudioContext(backgroundContext, buffer, weight);
|
||||
if (result > 0) {
|
||||
size = max(size, result);
|
||||
}
|
||||
}
|
||||
else {
|
||||
memset(&fragment, 0, sizeof(fragment));
|
||||
state = AUDIO_SLEEPING;
|
||||
dacStop();
|
||||
}
|
||||
|
||||
CoLeaveMutexSection(audioMutex);
|
||||
// push the buffer if needed
|
||||
if (size > 0) {
|
||||
buffer->size = size;
|
||||
pushBuffer(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline uint8_t getToneLength(uint8_t tLen)
|
||||
inline unsigned int getToneLength(uint16_t len)
|
||||
{
|
||||
uint8_t result = tLen; // default
|
||||
unsigned int result = len; // default
|
||||
if (g_eeGeneral.beepLength < 0) {
|
||||
result /= (1-g_eeGeneral.beepLength);
|
||||
}
|
||||
@@ -574,9 +536,9 @@ inline uint8_t getToneLength(uint8_t tLen)
|
||||
return result;
|
||||
}
|
||||
|
||||
void AudioQueue::pause(uint8_t tLen)
|
||||
void AudioQueue::pause(uint16_t len)
|
||||
{
|
||||
play(0, 0, tLen); // a pause
|
||||
play(0, 0, len);
|
||||
}
|
||||
|
||||
bool AudioQueue::isPlaying(uint8_t id)
|
||||
@@ -594,44 +556,49 @@ bool AudioQueue::isPlaying(uint8_t id)
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef SIMU
|
||||
extern OS_MutexID audioMutex;
|
||||
#endif
|
||||
|
||||
void AudioQueue::play(uint16_t tFreq, uint8_t tLen, uint8_t tPause, uint8_t tFlags, int8_t tFreqIncr)
|
||||
void AudioQueue::play(uint16_t freq, uint16_t len, uint16_t pause, uint8_t flags, int8_t freqIncr)
|
||||
{
|
||||
CoEnterMutexSection(audioMutex);
|
||||
|
||||
if (tFlags & PLAY_BACKGROUND) {
|
||||
backgroundContext.fragment.freq = tFreq;
|
||||
backgroundContext.fragment.duration = tLen;
|
||||
backgroundContext.fragment.pause = tPause;
|
||||
if (!busy()) {
|
||||
state = AUDIO_RESUMING;
|
||||
CoSetFlag(audioFlag);
|
||||
}
|
||||
if (freq && freq < BEEP_MIN_FREQ)
|
||||
freq = BEEP_MIN_FREQ;
|
||||
|
||||
if (flags & PLAY_BACKGROUND) {
|
||||
AudioFragment & fragment = backgroundContext.fragment;
|
||||
backgroundContext.clear();
|
||||
fragment.type = FRAGMENT_TONE;
|
||||
fragment.tone.freq = freq;
|
||||
fragment.tone.duration = len;
|
||||
fragment.tone.pause = pause;
|
||||
}
|
||||
else {
|
||||
if (tFreq > 0) { //we dont add pitch if zero as this is a pause only event
|
||||
tFreq += g_eeGeneral.speakerPitch + BEEP_OFFSET; // add pitch compensator
|
||||
}
|
||||
tLen = getToneLength(tLen);
|
||||
freq += g_eeGeneral.speakerPitch * 15;
|
||||
len = getToneLength(len);
|
||||
|
||||
uint8_t next_widx = (widx + 1) % AUDIO_QUEUE_LENGTH;
|
||||
if (next_widx != ridx) {
|
||||
AudioFragment & fragment = fragments[widx];
|
||||
memset(&fragment, 0, sizeof(fragment));
|
||||
fragment.freq = tFreq;
|
||||
fragment.duration = tLen;
|
||||
fragment.pause = tPause;
|
||||
fragment.repeat = tFlags & 0x0f;
|
||||
fragment.freqIncr = tFreqIncr;
|
||||
if (tFlags & PLAY_NOW)
|
||||
prioIdx = widx;
|
||||
widx = next_widx;
|
||||
if (!busy()) {
|
||||
state = AUDIO_RESUMING;
|
||||
CoSetFlag(audioFlag);
|
||||
if (flags & PLAY_NOW) {
|
||||
AudioFragment & fragment = foregroundContext.fragment;
|
||||
if (fragment.type == FRAGMENT_EMPTY) {
|
||||
foregroundContext.clear();
|
||||
fragment.type = FRAGMENT_TONE;
|
||||
fragment.repeat = flags & 0x0f;
|
||||
fragment.tone.freq = freq;
|
||||
fragment.tone.duration = len;
|
||||
fragment.tone.pause = pause;
|
||||
fragment.tone.freqIncr = freqIncr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint8_t next_widx = (widx + 1) % AUDIO_QUEUE_LENGTH;
|
||||
if (next_widx != ridx) {
|
||||
AudioFragment & fragment = fragments[widx];
|
||||
fragment.clear();
|
||||
fragment.type = FRAGMENT_TONE;
|
||||
fragment.repeat = flags & 0x0f;
|
||||
fragment.tone.freq = freq;
|
||||
fragment.tone.duration = len;
|
||||
fragment.tone.pause = pause;
|
||||
fragment.tone.freqIncr = freqIncr;
|
||||
widx = next_widx;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -653,29 +620,32 @@ void AudioQueue::playFile(const char *filename, uint8_t flags, uint8_t id)
|
||||
CoEnterMutexSection(audioMutex);
|
||||
|
||||
if (flags & PLAY_BACKGROUND) {
|
||||
memset(&backgroundContext.fragment, 0, sizeof(AudioFragment));
|
||||
strcpy(backgroundContext.fragment.file, filename);
|
||||
backgroundContext.fragment.id = id;
|
||||
if (!busy()) {
|
||||
state = AUDIO_RESUMING;
|
||||
CoSetFlag(audioFlag);
|
||||
backgroundContext.clear();
|
||||
AudioFragment & fragment = backgroundContext.fragment;
|
||||
fragment.type = FRAGMENT_FILE;
|
||||
strcpy(fragment.file, filename);
|
||||
fragment.id = id;
|
||||
}
|
||||
else if (flags & PLAY_NOW) {
|
||||
AudioFragment & fragment = foregroundContext.fragment;
|
||||
if (fragment.type == FRAGMENT_EMPTY) {
|
||||
foregroundContext.clear();
|
||||
fragment.type = FRAGMENT_FILE;
|
||||
strcpy(fragment.file, filename);
|
||||
fragment.repeat = flags & 0x0f;
|
||||
fragment.id = id;
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint8_t next_widx = (widx + 1) % AUDIO_QUEUE_LENGTH;
|
||||
if (next_widx != ridx) {
|
||||
AudioFragment & fragment = fragments[widx];
|
||||
memset(&fragment, 0, sizeof(fragment));
|
||||
fragment.clear();
|
||||
fragment.type = FRAGMENT_FILE;
|
||||
strcpy(fragment.file, filename);
|
||||
fragment.repeat = flags & 0x0f;
|
||||
fragment.id = id;
|
||||
if (flags & PLAY_NOW)
|
||||
prioIdx = widx;
|
||||
widx = next_widx;
|
||||
if (!busy()) {
|
||||
state = AUDIO_RESUMING;
|
||||
CoSetFlag(audioFlag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -691,7 +661,8 @@ void AudioQueue::stopPlay(uint8_t id)
|
||||
|
||||
// For the moment it's only needed to stop the background music
|
||||
if (backgroundContext.fragment.id == id) {
|
||||
memset(&backgroundContext.fragment, 0, sizeof(AudioFragment));
|
||||
backgroundContext.fragment.type = FRAGMENT_EMPTY;
|
||||
backgroundContext.fragment.id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -708,12 +679,13 @@ void AudioQueue::reset()
|
||||
{
|
||||
CoEnterMutexSection(audioMutex);
|
||||
widx = ridx; // clean the queue
|
||||
memset(¤tContext.fragment, 0, sizeof(AudioFragment));
|
||||
memset(&backgroundContext.fragment, 0, sizeof(AudioFragment));
|
||||
foregroundContext.clear();
|
||||
currentContext.clear();
|
||||
backgroundContext.clear();
|
||||
CoLeaveMutexSection(audioMutex);
|
||||
}
|
||||
|
||||
void audioEvent(uint8_t e, uint8_t f)
|
||||
void audioEvent(uint8_t e, uint16_t f)
|
||||
{
|
||||
#if defined(SDCARD)
|
||||
char filename[AUDIO_FILENAME_MAXLEN+1];
|
||||
@@ -740,29 +712,23 @@ void audioEvent(uint8_t e, uint8_t f)
|
||||
switch (e) {
|
||||
// inactivity timer alert
|
||||
case AU_INACTIVITY:
|
||||
audioQueue.play(140, 20, 4, 2|PLAY_NOW);
|
||||
audioQueue.play(2250, 80, 20, PLAY_REPEAT(2));
|
||||
break;
|
||||
// low battery in tx
|
||||
case AU_TX_BATTERY_LOW:
|
||||
if (!audioQueue.busy()) {
|
||||
audioQueue.play(120, 40, 6, 2, 1);
|
||||
audioQueue.play(160, 40, 6, 2, -1);
|
||||
}
|
||||
audioQueue.play(1950, 160, 20, PLAY_REPEAT(2), 1);
|
||||
audioQueue.play(2550, 160, 20, PLAY_REPEAT(2), -1);
|
||||
break;
|
||||
#if defined(PCBSKY9X)
|
||||
case AU_TX_MAH_HIGH:
|
||||
if (!audioQueue.busy()) {
|
||||
// TODO Rob something better here?
|
||||
audioQueue.play(120, 40, 6, 2, 1);
|
||||
audioQueue.play(160, 40, 6, 2, -1);
|
||||
}
|
||||
// TODO Rob something better here?
|
||||
audioQueue.play(1950, 160, 20, PLAY_REPEAT(2), 1);
|
||||
audioQueue.play(2550, 160, 20, PLAY_REPEAT(2), -1);
|
||||
break;
|
||||
case AU_TX_TEMP_HIGH:
|
||||
if (!audioQueue.busy()) {
|
||||
// TODO Rob something better here?
|
||||
audioQueue.play(120, 40, 6, 2, 1);
|
||||
audioQueue.play(160, 40, 6, 2, -1);
|
||||
}
|
||||
// TODO Rob something better here?
|
||||
audioQueue.play(1950, 160, 20, PLAY_REPEAT(2), 1);
|
||||
audioQueue.play(2550, 160, 20, PLAY_REPEAT(2), -1);
|
||||
break;
|
||||
#endif
|
||||
#if defined(VOICE)
|
||||
@@ -770,173 +736,157 @@ void audioEvent(uint8_t e, uint8_t f)
|
||||
case AU_SWITCH_ALERT:
|
||||
#endif
|
||||
case AU_ERROR:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ, 50, 2, PLAY_NOW);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ, 200, 20, PLAY_NOW);
|
||||
break;
|
||||
// keypad up (seems to be used when going left/right through system menu options. 0-100 scales etc)
|
||||
case AU_KEYPAD_UP:
|
||||
audioQueue.play(BEEP_KEY_UP_FREQ, 20, 2, PLAY_NOW);
|
||||
audioQueue.play(BEEP_KEY_UP_FREQ, 80, 20, PLAY_NOW);
|
||||
break;
|
||||
// keypad down (seems to be used when going left/right through system menu options. 0-100 scales etc)
|
||||
case AU_KEYPAD_DOWN:
|
||||
audioQueue.play(BEEP_KEY_DOWN_FREQ, 20, 2, PLAY_NOW);
|
||||
audioQueue.play(BEEP_KEY_DOWN_FREQ, 80, 20, PLAY_NOW);
|
||||
break;
|
||||
// menu display (also used by a few generic beeps)
|
||||
case AU_MENUS:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ, 20, 4, PLAY_NOW);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ, 80, 20, PLAY_NOW);
|
||||
break;
|
||||
// trim move
|
||||
case AU_TRIM_MOVE:
|
||||
audioQueue.play(f, 12, 2, PLAY_NOW);
|
||||
audioQueue.play(f, 40, 20, PLAY_NOW);
|
||||
break;
|
||||
// trim center
|
||||
case AU_TRIM_MIDDLE:
|
||||
audioQueue.play(f, 20, 4, PLAY_NOW);
|
||||
audioQueue.play(f, 80, 20, PLAY_NOW);
|
||||
break;
|
||||
// trim center
|
||||
case AU_TRIM_END:
|
||||
audioQueue.play(f, 20, 4, PLAY_NOW);
|
||||
audioQueue.play(f, 80, 20, PLAY_NOW);
|
||||
break;
|
||||
// warning one
|
||||
case AU_WARNING1:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ, 20, 2, PLAY_NOW);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ, 80, 20, PLAY_NOW);
|
||||
break;
|
||||
// warning two
|
||||
case AU_WARNING2:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ, 40, 2, PLAY_NOW);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ, 160, 20, PLAY_NOW);
|
||||
break;
|
||||
// warning three
|
||||
case AU_WARNING3:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ, 50, 2, PLAY_NOW);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ, 200, 20, PLAY_NOW);
|
||||
break;
|
||||
// pot/stick center
|
||||
case AU_POT_STICK_MIDDLE:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+100, 20, 2, PLAY_NOW);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+1500, 80, 20, PLAY_NOW);
|
||||
break;
|
||||
// mix warning 1
|
||||
case AU_MIX_WARNING_1:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+96, 12, 0);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+1440, 48, 32);
|
||||
break;
|
||||
// mix warning 2
|
||||
case AU_MIX_WARNING_2:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+104, 12, 8, PLAY_REPEAT(1));
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+1560, 48, 32, PLAY_REPEAT(1));
|
||||
break;
|
||||
// mix warning 3
|
||||
case AU_MIX_WARNING_3:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+112, 12, 8, PLAY_REPEAT(2));
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+1680, 48, 32, PLAY_REPEAT(2));
|
||||
break;
|
||||
// time <= 10 seconds left
|
||||
case AU_TIMER_LT10:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+10, 30, 6, PLAY_NOW);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+150, 120, 20, PLAY_NOW);
|
||||
break;
|
||||
// time 20 seconds left
|
||||
case AU_TIMER_20:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ, 30, 6, PLAY_REPEAT(1)|PLAY_NOW);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ, 120, 20, PLAY_REPEAT(1)|PLAY_NOW);
|
||||
break;
|
||||
// time 30 seconds left
|
||||
case AU_TIMER_30:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ, 30, 6, PLAY_REPEAT(2)|PLAY_NOW);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ, 120, 20, PLAY_REPEAT(2)|PLAY_NOW);
|
||||
break;
|
||||
#if defined(PCBTARANIS)
|
||||
case AU_A1_ORANGE:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+40, 50, 6, PLAY_NOW);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+600, 200, 20, PLAY_NOW);
|
||||
break;
|
||||
case AU_A1_RED:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+40, 50, 6, PLAY_REPEAT(1)|PLAY_NOW);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+600, 200, 20, PLAY_REPEAT(1)|PLAY_NOW);
|
||||
break;
|
||||
case AU_A2_ORANGE:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+100, 50, 6, PLAY_NOW);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+1500, 200, 20, PLAY_NOW);
|
||||
break;
|
||||
case AU_A2_RED:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+100, 50, 6, PLAY_REPEAT(1)|PLAY_NOW);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+1500, 200, 20, PLAY_REPEAT(1)|PLAY_NOW);
|
||||
break;
|
||||
case AU_RSSI_ORANGE:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+120, 200, 6, PLAY_NOW);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+1500, 800, 20, PLAY_NOW);
|
||||
break;
|
||||
case AU_RSSI_RED:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+120, 200, 6, PLAY_REPEAT(1)|PLAY_NOW);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+1800, 800, 20, PLAY_REPEAT(1)|PLAY_NOW);
|
||||
break;
|
||||
case AU_SWR_RED:
|
||||
// siren
|
||||
audioQueue.play(20, 40, 10, 2, 1);
|
||||
audioQueue.pause(200);
|
||||
// TODO
|
||||
audioQueue.play(450, 160, 40, PLAY_REPEAT(2), 1);
|
||||
break;
|
||||
#endif
|
||||
case AU_FRSKY_BEEP1:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ, 15, 2);
|
||||
audioQueue.pause(200);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ, 60, 20);
|
||||
break;
|
||||
case AU_FRSKY_BEEP2:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ, 30, 2);
|
||||
audioQueue.pause(200);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ, 120, 20);
|
||||
break;
|
||||
case AU_FRSKY_BEEP3:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ, 50, 2);
|
||||
audioQueue.pause(200);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ, 200, 20);
|
||||
break;
|
||||
case AU_FRSKY_WARN1:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+40, 30, 10, 2);
|
||||
audioQueue.pause(200);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+600, 120, 40, PLAY_REPEAT(2));
|
||||
break;
|
||||
case AU_FRSKY_WARN2:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+60,30,10,2);
|
||||
audioQueue.pause(200);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+900, 120, 40, PLAY_REPEAT(2));
|
||||
break;
|
||||
case AU_FRSKY_CHEEP:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+60,20,4,2,2);
|
||||
audioQueue.pause(200);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+900, 80, 20, PLAY_REPEAT(2), 2);
|
||||
break;
|
||||
case AU_FRSKY_RING:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+50,10,4,10);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+50,10,20,1);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+50,10,4,10);
|
||||
audioQueue.pause(200);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+750, 40, 20, PLAY_REPEAT(10));
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+750, 40, 80, PLAY_REPEAT(1));
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+750, 40, 20, PLAY_REPEAT(10));
|
||||
break;
|
||||
case AU_FRSKY_SCIFI:
|
||||
audioQueue.play(160,20,6,2,-1);
|
||||
audioQueue.play(120,20,6,2,1);
|
||||
audioQueue.play(140,20,2,0);
|
||||
audioQueue.pause(200);
|
||||
audioQueue.play(2550, 80, 20, PLAY_REPEAT(2), -1);
|
||||
audioQueue.play(1950, 80, 20, PLAY_REPEAT(2), 1);
|
||||
audioQueue.play(2250, 80, 20, 0);
|
||||
break;
|
||||
case AU_FRSKY_ROBOT:
|
||||
audioQueue.play(140,10,2,1);
|
||||
audioQueue.play(100,30,4,1);
|
||||
audioQueue.play(160,30,4,1);
|
||||
audioQueue.pause(200);
|
||||
audioQueue.play(2250, 40, 20, PLAY_REPEAT(1));
|
||||
audioQueue.play(1650, 120, 20, PLAY_REPEAT(1));
|
||||
audioQueue.play(2550, 120, 20, PLAY_REPEAT(1));
|
||||
break;
|
||||
case AU_FRSKY_CHIRP:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+80,10,2,2);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+108,10,2,3);
|
||||
audioQueue.pause(200);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+1200, 40, 20, PLAY_REPEAT(2));
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+1620, 40, 20, PLAY_REPEAT(3));
|
||||
break;
|
||||
case AU_FRSKY_TADA:
|
||||
audioQueue.play(100,20,10);
|
||||
audioQueue.play(180,20,10);
|
||||
audioQueue.play(220,16,8,2);
|
||||
audioQueue.pause(200);
|
||||
audioQueue.play(1650, 80, 40);
|
||||
audioQueue.play(2850, 80, 40);
|
||||
audioQueue.play(3450, 64, 36, PLAY_REPEAT(2));
|
||||
break;
|
||||
case AU_FRSKY_CRICKET:
|
||||
audioQueue.play(160,10,20,3);
|
||||
audioQueue.play(160,10,40,1);
|
||||
audioQueue.play(160,10,20,3);
|
||||
audioQueue.pause(200);
|
||||
audioQueue.play(2550, 40, 80, PLAY_REPEAT(3));
|
||||
audioQueue.play(2550, 40, 160, PLAY_REPEAT(1));
|
||||
audioQueue.play(2550, 40, 80, PLAY_REPEAT(3));
|
||||
break;
|
||||
case AU_FRSKY_SIREN:
|
||||
audioQueue.play(20,40,10,2,1);
|
||||
audioQueue.pause(200);
|
||||
audioQueue.play(450, 160, 40, PLAY_REPEAT(2), 2);
|
||||
break;
|
||||
case AU_FRSKY_ALARMC:
|
||||
audioQueue.play(100,8,20,2);
|
||||
audioQueue.play(140,16,40,1);
|
||||
audioQueue.play(100,16,20,2);
|
||||
audioQueue.play(140,8,40,1);
|
||||
audioQueue.pause(200);
|
||||
audioQueue.play(1650, 32, 68, PLAY_REPEAT(2));
|
||||
audioQueue.play(2250, 64, 156, PLAY_REPEAT(1));
|
||||
audioQueue.play(1650, 64, 76, PLAY_REPEAT(2));
|
||||
audioQueue.play(2250, 32, 168, PLAY_REPEAT(1));
|
||||
break;
|
||||
case AU_FRSKY_RATATA:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+100,10,20,10);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+1500, 40, 80, PLAY_REPEAT(10));
|
||||
break;
|
||||
case AU_FRSKY_TICK:
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+100,10,100,2);
|
||||
audioQueue.pause(200);
|
||||
audioQueue.play(BEEP_DEFAULT_FREQ+1500, 40, 400, PLAY_REPEAT(2));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
+114
-49
@@ -41,53 +41,87 @@
|
||||
|
||||
#define AUDIO_FILENAME_MAXLEN (40)
|
||||
#define AUDIO_QUEUE_LENGTH (20)
|
||||
#define BEEP_DEFAULT_FREQ (140)
|
||||
#define BEEP_OFFSET (10)
|
||||
#define BEEP_KEY_UP_FREQ (BEEP_DEFAULT_FREQ+10)
|
||||
#define BEEP_KEY_DOWN_FREQ (BEEP_DEFAULT_FREQ-10)
|
||||
|
||||
extern uint16_t Sine_values[];
|
||||
#define AUDIO_SAMPLE_RATE (32000)
|
||||
#define AUDIO_BUFFER_DURATION (10)
|
||||
#define AUDIO_BUFFER_SIZE (AUDIO_SAMPLE_RATE*AUDIO_BUFFER_DURATION/1000)
|
||||
#define AUDIO_BUFFER_COUNT (3)
|
||||
|
||||
class ToneFragment {
|
||||
public:
|
||||
uint16_t freq;
|
||||
uint8_t duration;
|
||||
uint8_t pause;
|
||||
uint8_t repeat;
|
||||
int8_t freqIncr;
|
||||
uint8_t id;
|
||||
#define BEEP_MIN_FREQ (440)
|
||||
#define BEEP_DEFAULT_FREQ (2250)
|
||||
#define BEEP_KEY_UP_FREQ (BEEP_DEFAULT_FREQ+150)
|
||||
#define BEEP_KEY_DOWN_FREQ (BEEP_DEFAULT_FREQ-150)
|
||||
|
||||
#define AUDIO_BUFFER_FREE (0)
|
||||
#define AUDIO_BUFFER_FILLED (1)
|
||||
#define AUDIO_BUFFER_PLAYING (2)
|
||||
|
||||
struct AudioBuffer {
|
||||
uint16_t data[AUDIO_BUFFER_SIZE];
|
||||
uint16_t size;
|
||||
uint8_t state;
|
||||
};
|
||||
|
||||
class AudioFragment : public ToneFragment {
|
||||
public:
|
||||
#define FRAGMENT_EMPTY 0
|
||||
#define FRAGMENT_TONE 1
|
||||
#define FRAGMENT_FILE 2
|
||||
|
||||
struct AudioFragment {
|
||||
uint8_t type;
|
||||
uint8_t id;
|
||||
uint8_t repeat;
|
||||
union {
|
||||
struct {
|
||||
uint16_t freq;
|
||||
uint16_t duration;
|
||||
uint16_t pause;
|
||||
int8_t freqIncr;
|
||||
} tone;
|
||||
|
||||
char file[AUDIO_FILENAME_MAXLEN+1];
|
||||
};
|
||||
|
||||
void clear()
|
||||
{
|
||||
memset(this, 0, sizeof(AudioFragment));
|
||||
}
|
||||
};
|
||||
|
||||
extern "C" void DAC_IRQHandler();
|
||||
|
||||
#define AUDIO_NOT_STARTED 0
|
||||
#define AUDIO_SLEEPING 1
|
||||
#define AUDIO_RESUMING 2
|
||||
#define AUDIO_PLAYING_TONE 3
|
||||
#define AUDIO_PLAYING_WAV 4
|
||||
|
||||
extern bool playingBackground;
|
||||
|
||||
class AudioContext {
|
||||
public:
|
||||
AudioFragment fragment;
|
||||
|
||||
union {
|
||||
#if defined(SDCARD)
|
||||
FIL wavFile;
|
||||
struct {
|
||||
FIL file;
|
||||
uint8_t codec;
|
||||
uint32_t freq;
|
||||
uint32_t size;
|
||||
uint8_t resampleRatio;
|
||||
uint16_t readSize;
|
||||
} wav;
|
||||
#endif
|
||||
uint8_t pcmCodec;
|
||||
uint32_t pcmFreq;
|
||||
uint32_t wavSize;
|
||||
|
||||
struct {
|
||||
uint16_t points[100];
|
||||
uint8_t count;
|
||||
uint8_t idx;
|
||||
uint16_t freq;
|
||||
} tone;
|
||||
} state;
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
fragment.clear();
|
||||
}
|
||||
};
|
||||
|
||||
bool dacQueue(AudioBuffer *buffer);
|
||||
|
||||
class AudioQueue {
|
||||
|
||||
friend void audioTask(void* pdata);
|
||||
friend void DAC_IRQHandler();
|
||||
|
||||
public:
|
||||
|
||||
@@ -95,13 +129,13 @@ class AudioQueue {
|
||||
|
||||
void start();
|
||||
|
||||
void play(uint16_t tFreq, uint8_t tLen, uint8_t tPause, uint8_t tFlags=0, int8_t tFreqIncr=0);
|
||||
void play(uint16_t freq, uint16_t len, uint16_t pause=0, uint8_t flags=0, int8_t freqIncr=0);
|
||||
|
||||
void playFile(const char *filename, uint8_t flags=0, uint8_t id=0);
|
||||
|
||||
void stopPlay(uint8_t id);
|
||||
|
||||
void pause(uint8_t tLen);
|
||||
void pause(uint16_t tLen);
|
||||
|
||||
void stopSD();
|
||||
|
||||
@@ -109,16 +143,7 @@ class AudioQueue {
|
||||
|
||||
bool started()
|
||||
{
|
||||
return (state != AUDIO_NOT_STARTED);
|
||||
}
|
||||
|
||||
bool busy()
|
||||
{
|
||||
#ifdef SIMU
|
||||
return false;
|
||||
#else
|
||||
return (state != AUDIO_SLEEPING);
|
||||
#endif
|
||||
return state;
|
||||
}
|
||||
|
||||
bool empty()
|
||||
@@ -128,27 +153,69 @@ class AudioQueue {
|
||||
|
||||
void reset();
|
||||
|
||||
inline AudioBuffer * getNextFilledBuffer()
|
||||
{
|
||||
if (buffers[bufferRIdx].state == AUDIO_BUFFER_PLAYING) {
|
||||
buffers[bufferRIdx].state = AUDIO_BUFFER_FREE;
|
||||
bufferRIdx = nextBufferIdx(bufferRIdx);
|
||||
}
|
||||
|
||||
uint8_t idx = bufferRIdx;
|
||||
while (idx != bufferWIdx) {
|
||||
AudioBuffer * buffer = &buffers[idx];
|
||||
if (buffer->state == AUDIO_BUFFER_FILLED) {
|
||||
buffer->state = AUDIO_BUFFER_PLAYING;
|
||||
return buffer;
|
||||
}
|
||||
idx = nextBufferIdx(idx);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void wakeup();
|
||||
|
||||
void sdWakeup(AudioContext & context);
|
||||
int mixAudioContext(AudioContext &context, AudioBuffer *buffer, unsigned int weight);
|
||||
int mixTone(AudioContext &context, AudioBuffer *buffer, unsigned int weight);
|
||||
int mixWav(AudioContext &context, AudioBuffer *buffer, unsigned int weight);
|
||||
|
||||
volatile uint8_t state;
|
||||
volatile bool state;
|
||||
uint8_t ridx;
|
||||
uint8_t widx;
|
||||
int8_t prioIdx;
|
||||
|
||||
AudioFragment fragments[AUDIO_QUEUE_LENGTH];
|
||||
|
||||
AudioContext currentContext;
|
||||
AudioContext backgroundContext; // for background music / vario
|
||||
AudioContext currentContext;
|
||||
AudioContext foregroundContext; // for important beeps
|
||||
|
||||
AudioBuffer buffers[AUDIO_BUFFER_COUNT];
|
||||
uint8_t bufferRIdx;
|
||||
uint8_t bufferWIdx;
|
||||
|
||||
inline uint8_t nextBufferIdx(uint8_t idx)
|
||||
{
|
||||
return (idx == AUDIO_BUFFER_COUNT-1 ? 0 : idx+1);
|
||||
}
|
||||
|
||||
inline AudioBuffer * getEmptyBuffer()
|
||||
{
|
||||
AudioBuffer * buffer = &buffers[bufferWIdx];
|
||||
if (buffer->state == AUDIO_BUFFER_FREE)
|
||||
return buffer;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void pushBuffer(AudioBuffer *buffer);
|
||||
};
|
||||
|
||||
extern AudioQueue audioQueue;
|
||||
|
||||
void codecsInit();
|
||||
void audioEvent(uint8_t e, uint8_t f=BEEP_DEFAULT_FREQ);
|
||||
void audioEvent(uint8_t e, uint16_t f=BEEP_DEFAULT_FREQ);
|
||||
void audioStart();
|
||||
|
||||
#if defined(AUDIO) && defined(BUZZER)
|
||||
@@ -240,8 +307,6 @@ extern void refreshModelAudioFiles();
|
||||
extern bool isAudioFileAvailable(uint32_t i, char * filename);
|
||||
|
||||
// TODO not public, everything could be inside a function in audio_arm.cpp
|
||||
extern OS_TCID audioTimer;
|
||||
extern OS_FlagID audioFlag;
|
||||
extern OS_MutexID audioMutex;
|
||||
|
||||
#endif
|
||||
|
||||
+4
-10
@@ -131,10 +131,9 @@ void audioQueue::play(uint8_t tFreq, uint8_t tLen, uint8_t tPause, uint8_t tFlag
|
||||
int8_t tFreqIncr = (tFlags >> 6);
|
||||
if (tFreqIncr == 3) tFreqIncr = -1;
|
||||
|
||||
if (tFreq > 0) { //we dont add pitch if zero as this is a pause only event
|
||||
tFreq += g_eeGeneral.speakerPitch + BEEP_OFFSET; // add pitch compensator
|
||||
}
|
||||
tFreq += g_eeGeneral.speakerPitch + BEEP_OFFSET; // add pitch compensator
|
||||
tLen = getToneLength(tLen);
|
||||
|
||||
if ((tFlags & PLAY_NOW) || (!busy() && empty())) {
|
||||
toneFreq = tFreq;
|
||||
toneTimeLeft = tLen;
|
||||
@@ -177,12 +176,14 @@ void audioQueue::event(uint8_t e, uint8_t f)
|
||||
if (e < AU_FRSKY_FIRST || empty()) {
|
||||
// TODO when VOICE enable some cases here are not needed!
|
||||
switch (e) {
|
||||
#if !defined(VOICE)
|
||||
case AU_TX_BATTERY_LOW:
|
||||
if (empty()) {
|
||||
play(60, 20, 3, PLAY_REPEAT(2)|PLAY_INCREMENT(1));
|
||||
play(80, 20, 3, PLAY_REPEAT(2)|PLAY_INCREMENT(-1));
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case AU_TRIM_MOVE:
|
||||
play(f, 6, 1, PLAY_NOW);
|
||||
break;
|
||||
@@ -193,43 +194,36 @@ void audioQueue::event(uint8_t e, uint8_t f)
|
||||
play(BEEP_DEFAULT_FREQ+25, 5, 2, PLAY_REPEAT(10));
|
||||
play(BEEP_DEFAULT_FREQ+25, 5, 10, PLAY_REPEAT(1));
|
||||
play(BEEP_DEFAULT_FREQ+25, 5, 2, PLAY_REPEAT(10));
|
||||
pause(200);
|
||||
break;
|
||||
case AU_FRSKY_SCIFI:
|
||||
play(80, 10, 3, PLAY_REPEAT(2)|PLAY_INCREMENT(-1));
|
||||
play(60, 10, 3, PLAY_REPEAT(2)|PLAY_INCREMENT(1));
|
||||
play(70, 10, 1);
|
||||
pause(200);
|
||||
break;
|
||||
case AU_FRSKY_ROBOT:
|
||||
play(70, 5, 1, PLAY_REPEAT(1));
|
||||
play(50, 15, 2, PLAY_REPEAT(1));
|
||||
play(80, 15, 2, PLAY_REPEAT(1));
|
||||
pause(200);
|
||||
break;
|
||||
case AU_FRSKY_CHIRP:
|
||||
play(BEEP_DEFAULT_FREQ+40, 5, 1, PLAY_REPEAT(2));
|
||||
play(BEEP_DEFAULT_FREQ+54, 5, 1, PLAY_REPEAT(3));
|
||||
pause(200);
|
||||
break;
|
||||
case AU_FRSKY_TADA:
|
||||
play(50, 5, 5);
|
||||
play(90, 5, 5);
|
||||
play(110, 3, 4, PLAY_REPEAT(2));
|
||||
pause(200);
|
||||
break;
|
||||
case AU_FRSKY_CRICKET:
|
||||
play(80, 5, 10, PLAY_REPEAT(3));
|
||||
play(80, 5, 20, PLAY_REPEAT(1));
|
||||
play(80, 5, 10, PLAY_REPEAT(3));
|
||||
pause(200);
|
||||
break;
|
||||
case AU_FRSKY_ALARMC:
|
||||
play(50, 4, 10, PLAY_REPEAT(2));
|
||||
play(70, 8, 20, PLAY_REPEAT(1));
|
||||
play(50, 8, 10, PLAY_REPEAT(2));
|
||||
play(70, 4, 20, PLAY_REPEAT(1));
|
||||
pause(200);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
|
||||
@@ -96,13 +96,13 @@ const MenuFuncP_PROGMEM menuTabDiag[] PROGMEM = {
|
||||
};
|
||||
|
||||
#if LCD_W >= 212
|
||||
#define RADIO_SETUP_2ND_COLUMN (LCD_W-10*FW-MENUS_SCROLLBAR_WIDTH)
|
||||
#define RADIO_SETUP_DATE_COLUMN RADIO_SETUP_2ND_COLUMN + 4*FWNUM
|
||||
#define RADIO_SETUP_TIME_COLUMN RADIO_SETUP_2ND_COLUMN + 2*FWNUM
|
||||
#define RADIO_SETUP_2ND_COLUMN (LCD_W-10*FW-MENUS_SCROLLBAR_WIDTH)
|
||||
#define RADIO_SETUP_DATE_COLUMN RADIO_SETUP_2ND_COLUMN + 4*FWNUM
|
||||
#define RADIO_SETUP_TIME_COLUMN RADIO_SETUP_2ND_COLUMN + 2*FWNUM
|
||||
#else
|
||||
#define RADIO_SETUP_2ND_COLUMN (LCD_W-6*FW-3-MENUS_SCROLLBAR_WIDTH)
|
||||
#define RADIO_SETUP_TIME_COLUMN (FW*15+9)
|
||||
#define RADIO_SETUP_DATE_COLUMN (FW*15+5)
|
||||
#define RADIO_SETUP_2ND_COLUMN (LCD_W-6*FW-3-MENUS_SCROLLBAR_WIDTH)
|
||||
#define RADIO_SETUP_TIME_COLUMN (FW*15+9)
|
||||
#define RADIO_SETUP_DATE_COLUMN (FW*15+5)
|
||||
#endif
|
||||
|
||||
#if !defined(CPUM64)
|
||||
@@ -656,7 +656,7 @@ void onSdManagerMenu(const char *result)
|
||||
f_getcwd(lfn, SD_SCREEN_FILE_LENGTH);
|
||||
strcat(lfn, "/");
|
||||
strcat(lfn, reusableBuffer.sdmanager.lines[index]);
|
||||
audioQueue.playFile(lfn, PLAY_NOW);
|
||||
audioQueue.playFile(lfn, PLAY_BACKGROUND, 255);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+16
-2
@@ -143,8 +143,21 @@ void varioWakeup()
|
||||
}
|
||||
|
||||
if (verticalSpeed < 0 || (int16_t)(s_varioTmr-tmr10ms) < 0) {
|
||||
uint8_t SoundVarioBeepTime;
|
||||
uint8_t SoundVarioBeepFreq;
|
||||
#if defined(CPUARM)
|
||||
uint16_t SoundVarioBeepTime;
|
||||
uint16_t SoundVarioBeepFreq;
|
||||
if (verticalSpeed > 0) {
|
||||
SoundVarioBeepTime = 320 - (verticalSpeed / 5);
|
||||
SoundVarioBeepFreq = (verticalSpeed * 4 + 8000) >> 3;
|
||||
}
|
||||
else {
|
||||
SoundVarioBeepTime = 80;
|
||||
SoundVarioBeepFreq = (verticalSpeed * 3 + 8000) >> 3;
|
||||
}
|
||||
s_varioTmr = tmr10ms + (SoundVarioBeepTime/5);
|
||||
#else
|
||||
uint8_t SoundVarioBeepTime;
|
||||
uint8_t SoundVarioBeepFreq;
|
||||
if (verticalSpeed > 0) {
|
||||
SoundVarioBeepTime = (8000 - verticalSpeed * 5) / 100;
|
||||
SoundVarioBeepFreq = (verticalSpeed * 4 + 8000) >> 7;
|
||||
@@ -154,6 +167,7 @@ void varioWakeup()
|
||||
SoundVarioBeepFreq = (verticalSpeed * 3 + 8000) >> 7;
|
||||
}
|
||||
s_varioTmr = tmr10ms + (SoundVarioBeepTime/2);
|
||||
#endif
|
||||
AUDIO_VARIO(SoundVarioBeepFreq, SoundVarioBeepTime);
|
||||
}
|
||||
#endif
|
||||
|
||||
+3
-10
@@ -62,9 +62,6 @@ OS_TID debugTaskId;
|
||||
OS_STK debugStack[DEBUG_STACK_SIZE];
|
||||
#endif
|
||||
|
||||
OS_TCID audioTimer;
|
||||
OS_FlagID audioFlag;
|
||||
|
||||
OS_MutexID audioMutex;
|
||||
OS_MutexID mixerMutex;
|
||||
|
||||
@@ -126,7 +123,7 @@ audioQueue audio;
|
||||
#endif
|
||||
|
||||
#if defined(DSM2)
|
||||
// TODO port to ARM Bryan's code
|
||||
// TODO move elsewhere
|
||||
bool s_bind_mode = false;
|
||||
bool s_rangecheck_mode = false;
|
||||
uint8_t s_bind_allowed = 255;
|
||||
@@ -1882,8 +1879,8 @@ uint8_t checkTrim(uint8_t event)
|
||||
if (after < TRIM_MIN)
|
||||
after = TRIM_MIN;
|
||||
#if defined(CPUARM)
|
||||
after >>= 1;
|
||||
after += 120;
|
||||
after <<= 3;
|
||||
after += 120*16;
|
||||
#else
|
||||
after >>= 2;
|
||||
after += 60;
|
||||
@@ -4342,7 +4339,6 @@ void menusTask(void * pdata)
|
||||
pwrOff(); // Only turn power off if necessary
|
||||
}
|
||||
|
||||
extern void audioTimerHandle(void);
|
||||
extern void audioTask(void* pdata);
|
||||
|
||||
#endif
|
||||
@@ -4449,9 +4445,6 @@ int main(void)
|
||||
|
||||
mixerTaskId = CoCreateTask(mixerTask, NULL, 5, &mixerStack[MIXER_STACK_SIZE-1], MIXER_STACK_SIZE);
|
||||
menusTaskId = CoCreateTask(menusTask, NULL, 10, &menusStack[MENUS_STACK_SIZE-1], MENUS_STACK_SIZE);
|
||||
|
||||
audioFlag = CoCreateFlag(true, false); // Auto-reset, start FALSE
|
||||
audioTimer = CoCreateTmr(TMR_TYPE_ONE_SHOT, 1000/(1000/CFG_SYSTICK_FREQ), 1000/(1000/CFG_SYSTICK_FREQ), audioTimerHandle);
|
||||
audioTaskId = CoCreateTask(audioTask, NULL, 7, &audioStack[AUDIO_STACK_SIZE-1], AUDIO_STACK_SIZE);
|
||||
|
||||
audioMutex = CoCreateMutex();
|
||||
|
||||
@@ -36,33 +36,22 @@
|
||||
|
||||
#include "../../opentx.h"
|
||||
|
||||
uint32_t currentFrequency = 0;
|
||||
|
||||
uint32_t getFrequency()
|
||||
void setSampleRate(uint32_t frequency)
|
||||
{
|
||||
return currentFrequency;
|
||||
}
|
||||
register Tc *ptc ;
|
||||
register uint32_t timer ;
|
||||
|
||||
void setFrequency(uint32_t frequency)
|
||||
{
|
||||
if (currentFrequency != frequency) {
|
||||
currentFrequency = frequency;
|
||||
timer = Master_frequency / (8 * frequency) ; // MCK/8 and 100 000 Hz
|
||||
if (timer > 65535)
|
||||
timer = 65535 ;
|
||||
if (timer < 2)
|
||||
timer = 2 ;
|
||||
|
||||
register Tc *ptc ;
|
||||
register uint32_t timer ;
|
||||
|
||||
timer = Master_frequency / (8 * frequency) ; // MCK/8 and 100 000 Hz
|
||||
if (timer > 65535)
|
||||
timer = 65535 ;
|
||||
if (timer < 2)
|
||||
timer = 2 ;
|
||||
|
||||
ptc = TC0 ; // Tc block 0 (TC0-2)
|
||||
ptc->TC_CHANNEL[1].TC_CCR = TC_CCR0_CLKDIS ; // Stop clock
|
||||
ptc->TC_CHANNEL[1].TC_RC = timer ; // 100 000 Hz
|
||||
ptc->TC_CHANNEL[1].TC_RA = timer >> 1 ;
|
||||
ptc->TC_CHANNEL[1].TC_CCR = 5 ; // Enable clock and trigger it (may only need trigger)
|
||||
}
|
||||
ptc = TC0 ; // Tc block 0 (TC0-2)
|
||||
ptc->TC_CHANNEL[1].TC_CCR = TC_CCR0_CLKDIS ; // Stop clock
|
||||
ptc->TC_CHANNEL[1].TC_RC = timer ; // 100 000 Hz
|
||||
ptc->TC_CHANNEL[1].TC_RA = timer >> 1 ;
|
||||
ptc->TC_CHANNEL[1].TC_CCR = 5 ; // Enable clock and trigger it (may only need trigger)
|
||||
}
|
||||
|
||||
// Start TIMER1 at 100000Hz, used for DACC trigger
|
||||
@@ -108,29 +97,22 @@ void dacInit()
|
||||
|
||||
dacptr->DACC_CDR = 2048 ; // Half amplitude
|
||||
|
||||
// Data for PDC must NOT be in flash, PDC needs a RAM source.
|
||||
dacFill(Sine_values, 50/*100 samples*/);
|
||||
dacptr->DACC_PTCR = DACC_PTCR_TXTEN ;
|
||||
|
||||
NVIC_EnableIRQ(DACC_IRQn) ;
|
||||
}
|
||||
|
||||
bool dacQueue(AudioBuffer *buffer)
|
||||
{
|
||||
dacStart();
|
||||
return false;
|
||||
}
|
||||
|
||||
extern "C" void DAC_IRQHandler()
|
||||
{
|
||||
// Data for PDC must NOT be in flash, PDC needs a RAM source.
|
||||
if (audioQueue.state == AUDIO_PLAYING_WAV) {
|
||||
if (nextAudioData) {
|
||||
DACC->DACC_TNPR = CONVERT_PTR(nextAudioData);
|
||||
DACC->DACC_TNCR = nextAudioSize;
|
||||
nextAudioData = NULL;
|
||||
}
|
||||
else {
|
||||
dacStop();
|
||||
}
|
||||
}
|
||||
else if (audioQueue.state == AUDIO_PLAYING_TONE){
|
||||
DACC->DACC_TNPR = CONVERT_PTR(Sine_values);
|
||||
DACC->DACC_TNCR = 50 ; // words, 100 16 bit values
|
||||
AudioBuffer *nextBuffer = audioQueue.getNextFilledBuffer();
|
||||
if (nextBuffer) {
|
||||
DACC->DACC_TNPR = CONVERT_PTR(nextBuffer->data);
|
||||
DACC->DACC_TNCR = nextBuffer->size/2;
|
||||
}
|
||||
else {
|
||||
dacStop();
|
||||
|
||||
@@ -40,11 +40,7 @@
|
||||
void audioInit( void ) ;
|
||||
void audioEnd( void ) ;
|
||||
|
||||
void setFrequency( uint32_t frequency ) ;
|
||||
uint32_t getFrequency();
|
||||
|
||||
extern uint16_t *nextAudioData;
|
||||
extern uint16_t nextAudioSize;
|
||||
void setSampleRate( uint32_t frequency ) ;
|
||||
|
||||
inline void dacStart()
|
||||
{
|
||||
@@ -61,31 +57,4 @@ inline void dacStop()
|
||||
#define VOLUME_LEVEL_DEF 12
|
||||
void setVolume(uint8_t volume);
|
||||
|
||||
inline void dacFill(uint16_t *data, uint16_t size)
|
||||
{
|
||||
register Dacc *dacptr = DACC;
|
||||
dacptr->DACC_TPR = CONVERT_PTR(data);
|
||||
dacptr->DACC_TNPR = CONVERT_PTR(data);
|
||||
dacptr->DACC_TCR = size; // words
|
||||
dacptr->DACC_TNCR = size; // words
|
||||
}
|
||||
|
||||
inline uint16_t dacQueue(uint16_t *data, uint16_t size)
|
||||
{
|
||||
register Dacc *dacptr = DACC;
|
||||
if (dacptr->DACC_ISR & DACC_ISR_TXBUFE) {
|
||||
dacptr->DACC_TPR = CONVERT_PTR(data);
|
||||
dacptr->DACC_TCR = size;
|
||||
return size;
|
||||
}
|
||||
else if (dacptr->DACC_TNCR == 0) {
|
||||
dacptr->DACC_TNPR = CONVERT_PTR(data);
|
||||
dacptr->DACC_TNCR = size;
|
||||
return size;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -78,8 +78,8 @@
|
||||
#define RESUME_PPMIN_INTERRUPT() TIMSK3 |= (1<<ICIE3)
|
||||
#define TIMER_AUDIO_VECT TIMER4_OVF_vect
|
||||
#define SET_TIMER_AUDIO_CTRL() TCCR4B = (0b111 << CS20)
|
||||
#define PAUSE_AUDIO_INTERRUPT() TIMSK4 &= ~(1<<OCIE4A)
|
||||
#define RESUME_AUDIO_INTERRUPT() TIMSK4 |= (1<<OCIE4A)
|
||||
#define PAUSE_AUDIO_INTERRUPT() TIMSK4 &= ~(1<<TOIE4)
|
||||
#define RESUME_AUDIO_INTERRUPT() TIMSK4 |= (1<<TOIE4)
|
||||
#else
|
||||
#define TIMER_16KHZ_VECT TIMER0_OVF_vect
|
||||
#define COUNTER_16KHZ TCNT0
|
||||
|
||||
@@ -37,25 +37,15 @@
|
||||
#include "../../opentx.h"
|
||||
|
||||
bool dacIdle = true;
|
||||
uint32_t currentFrequency = 0;
|
||||
|
||||
uint32_t getFrequency()
|
||||
void setSampleRate(uint32_t frequency)
|
||||
{
|
||||
return currentFrequency;
|
||||
}
|
||||
register uint32_t timer = (PERI1_FREQUENCY * TIMER_MULT_APB1) / frequency - 1 ; // MCK/8 and 100 000 Hz
|
||||
|
||||
void setFrequency(uint32_t frequency)
|
||||
{
|
||||
if (currentFrequency != frequency) {
|
||||
currentFrequency = frequency;
|
||||
|
||||
register uint32_t timer = (PERI1_FREQUENCY * TIMER_MULT_APB1) / frequency - 1 ; // MCK/8 and 100 000 Hz
|
||||
|
||||
TIM6->CR1 &= ~TIM_CR1_CEN ;
|
||||
TIM6->CNT = 0 ;
|
||||
TIM6->ARR = limit<uint32_t>(2, timer, 65535) ;
|
||||
TIM6->CR1 |= TIM_CR1_CEN ;
|
||||
}
|
||||
TIM6->CR1 &= ~TIM_CR1_CEN ;
|
||||
TIM6->CNT = 0 ;
|
||||
TIM6->ARR = limit<uint32_t>(2, timer, 65535) ;
|
||||
TIM6->CR1 |= TIM_CR1_CEN ;
|
||||
}
|
||||
|
||||
// Start TIMER6 at 100000Hz, used for DAC trigger
|
||||
@@ -87,24 +77,42 @@ void dacInit()
|
||||
DMA1_Stream5->CR &= ~DMA_SxCR_EN ; // Disable DMA
|
||||
DMA1->HIFCR = DMA_HIFCR_CTCIF5 | DMA_HIFCR_CHTIF5 | DMA_HIFCR_CTEIF5 | DMA_HIFCR_CDMEIF5 | DMA_HIFCR_CFEIF5 ; // Write ones to clear bits
|
||||
DMA1_Stream5->CR = DMA_SxCR_CHSEL_0 | DMA_SxCR_CHSEL_1 | DMA_SxCR_CHSEL_2 | DMA_SxCR_PL_0 |
|
||||
DMA_SxCR_MSIZE_0 | DMA_SxCR_PSIZE_0 | DMA_SxCR_MINC | DMA_SxCR_DIR_0 | DMA_SxCR_CIRC ;
|
||||
DMA_SxCR_MSIZE_0 | DMA_SxCR_PSIZE_0 | DMA_SxCR_MINC | DMA_SxCR_DIR_0 | DMA_SxCR_CIRC ;
|
||||
DMA1_Stream5->PAR = CONVERT_PTR(&DAC->DHR12R1);
|
||||
DMA1_Stream5->M0AR = CONVERT_PTR(Sine_values);
|
||||
// DMA1_Stream5->M0AR = CONVERT_PTR(Sine_values);
|
||||
DMA1_Stream5->FCR = 0x05 ; //DMA_SxFCR_DMDIS | DMA_SxFCR_FTH_0 ;
|
||||
DMA1_Stream5->NDTR = 100 ;
|
||||
// DMA1_Stream5->NDTR = 100 ;
|
||||
|
||||
DAC->DHR12R1 = 2010 ;
|
||||
DAC->SR = DAC_SR_DMAUDR1 ; // Write 1 to clear flag
|
||||
DAC->CR = DAC_CR_TEN1 | DAC_CR_EN1 ; // Enable DAC
|
||||
NVIC_SetPriority( DMA1_Stream5_IRQn, 2 ) ; // High priority interrupt
|
||||
NVIC_EnableIRQ(TIM6_DAC_IRQn) ;
|
||||
NVIC_SetPriority(DMA1_Stream5_IRQn, 2) ; // High priority interrupt
|
||||
NVIC_EnableIRQ(TIM6_DAC_IRQn) ; // TODO needed?
|
||||
NVIC_EnableIRQ(DMA1_Stream5_IRQn) ;
|
||||
}
|
||||
|
||||
bool dacQueue(AudioBuffer *buffer)
|
||||
{
|
||||
if (dacIdle) {
|
||||
dacIdle = false;
|
||||
DMA1_Stream5->CR &= ~DMA_SxCR_EN ; // Disable DMA channel
|
||||
DMA1->HIFCR = DMA_HIFCR_CTCIF5 | DMA_HIFCR_CHTIF5 | DMA_HIFCR_CTEIF5 | DMA_HIFCR_CDMEIF5 | DMA_HIFCR_CFEIF5 ; // Write ones to clear bits
|
||||
DMA1_Stream5->M0AR = CONVERT_PTR(buffer->data);
|
||||
DMA1_Stream5->NDTR = buffer->size;
|
||||
DMA1_Stream5->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE ; // Enable DMA channel and interrupt
|
||||
DAC->SR = DAC_SR_DMAUDR1 ; // Write 1 to clear flag
|
||||
DAC->CR |= DAC_CR_EN1 | DAC_CR_DMAEN1 ; // Enable DAC
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Sound routines
|
||||
void audioInit()
|
||||
{
|
||||
dacInit() ;
|
||||
dacInit();
|
||||
}
|
||||
|
||||
void audioEnd()
|
||||
@@ -129,13 +137,14 @@ extern "C" void DMA1_Stream5_IRQHandler()
|
||||
DMA1_Stream5->CR &= ~DMA_SxCR_TCIE ; // Stop interrupt
|
||||
DMA1->HIFCR = DMA_HIFCR_CTCIF5 | DMA_HIFCR_CHTIF5 | DMA_HIFCR_CTEIF5 | DMA_HIFCR_CDMEIF5 | DMA_HIFCR_CFEIF5 ; // Write ones to clear flags
|
||||
DMA1_Stream5->CR &= ~DMA_SxCR_EN ; // Disable DMA channel
|
||||
if (nextAudioData) {
|
||||
DMA1_Stream5->M0AR = CONVERT_PTR(nextAudioData);
|
||||
DMA1_Stream5->NDTR = nextAudioSize*2;
|
||||
|
||||
AudioBuffer *nextBuffer = audioQueue.getNextFilledBuffer();
|
||||
if (nextBuffer) {
|
||||
DMA1_Stream5->M0AR = CONVERT_PTR(nextBuffer->data);
|
||||
DMA1_Stream5->NDTR = nextBuffer->size;
|
||||
DMA1->HIFCR = DMA_HIFCR_CTCIF5 | DMA_HIFCR_CHTIF5 | DMA_HIFCR_CTEIF5 | DMA_HIFCR_CDMEIF5 | DMA_HIFCR_CFEIF5 ; // Write ones to clear bits
|
||||
DMA1_Stream5->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE ; // Enable DMA channel
|
||||
DAC->SR = DAC_SR_DMAUDR1; // Write 1 to clear flag
|
||||
nextAudioData = NULL;
|
||||
}
|
||||
else {
|
||||
dacIdle = true;
|
||||
|
||||
@@ -40,12 +40,9 @@
|
||||
extern void audioInit( void ) ;
|
||||
extern void audioEnd( void ) ;
|
||||
|
||||
extern void setFrequency( uint32_t frequency ) ;
|
||||
extern uint32_t getFrequency();
|
||||
extern void setSampleRate(uint32_t frequency);
|
||||
|
||||
extern bool dacIdle;
|
||||
extern uint16_t *nextAudioData;
|
||||
extern uint16_t nextAudioSize;
|
||||
|
||||
inline void dacStart()
|
||||
{
|
||||
@@ -60,31 +57,6 @@ inline void dacStop()
|
||||
DMA1_Stream5->CR &= ~DMA_SxCR_CIRC ;
|
||||
}
|
||||
|
||||
inline void dacFill(uint16_t *data, uint16_t size)
|
||||
{
|
||||
DMA1_Stream5->CR &= ~DMA_SxCR_EN; // Disable DMA channel
|
||||
DMA1_Stream5->M0AR = CONVERT_PTR(data);
|
||||
DMA1_Stream5->NDTR = size*2;
|
||||
}
|
||||
|
||||
inline uint16_t dacQueue(uint16_t *data, uint16_t size)
|
||||
{
|
||||
if (dacIdle) {
|
||||
dacIdle = false;
|
||||
DMA1_Stream5->CR &= ~DMA_SxCR_EN ; // Disable DMA channel
|
||||
DMA1->HIFCR = DMA_HIFCR_CTCIF5 | DMA_HIFCR_CHTIF5 | DMA_HIFCR_CTEIF5 | DMA_HIFCR_CDMEIF5 | DMA_HIFCR_CFEIF5 ; // Write ones to clear bits
|
||||
DMA1_Stream5->M0AR = CONVERT_PTR(data);
|
||||
DMA1_Stream5->NDTR = size*2;
|
||||
DMA1_Stream5->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE ; // Enable DMA channel and interrupt
|
||||
DAC->SR = DAC_SR_DMAUDR1 ; // Write 1 to clear flag
|
||||
DAC->CR |= DAC_CR_EN1 | DAC_CR_DMAEN1 ; // Enable DAC
|
||||
return size;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define VOLUME_LEVEL_MAX 23
|
||||
#define VOLUME_LEVEL_DEF 12
|
||||
static const int8_t volumeScale[VOLUME_LEVEL_MAX+1] =
|
||||
|
||||
Reference in New Issue
Block a user