[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
+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;