[sky9x/Taranis] Audio mixer!

This commit is contained in:
bsongis@gmail.com
2013-06-15 16:18:08 +00:00
parent 46ae2e434a
commit f79e2aba4f
12 changed files with 524 additions and 576 deletions
+1 -1
View File
@@ -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
View File
@@ -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(&currentContext.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
View File
@@ -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
View File
@@ -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:
{
+7 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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();
+23 -41
View File
@@ -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();
+1 -32
View File
@@ -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
+2 -2
View File
@@ -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
+35 -26
View File
@@ -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;
+1 -29
View File
@@ -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] =