From f79e2aba4fa47387517dae6200c62d2c6986a810 Mon Sep 17 00:00:00 2001 From: "bsongis@gmail.com" Date: Sat, 15 Jun 2013 16:18:08 +0000 Subject: [PATCH] [sky9x/Taranis] Audio mixer! --- src/Makefile | 2 +- src/audio_arm.cpp | 684 +++++++++++++-------------- src/audio_arm.h | 163 +++++-- src/audio_avr.cpp | 14 +- src/gui/menu_general.cpp | 14 +- src/maths.cpp | 18 +- src/opentx.cpp | 13 +- src/targets/sky9x/audio_driver.cpp | 64 +-- src/targets/sky9x/audio_driver.h | 33 +- src/targets/stock/board_stock.h | 4 +- src/targets/taranis/audio_driver.cpp | 61 ++- src/targets/taranis/audio_driver.h | 30 +- 12 files changed, 524 insertions(+), 576 deletions(-) diff --git a/src/Makefile b/src/Makefile index 9e35e832e..3cff14e54 100644 --- a/src/Makefile +++ b/src/Makefile @@ -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. diff --git a/src/audio_arm.cpp b/src/audio_arm.cpp index 1f1a40a59..89fd48787 100644 --- a/src/audio_arm.cpp +++ b/src/audio_arm.cpp @@ -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 #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= 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= 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= 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(AUDIO_BUFFER_DURATION, fragment.tone.duration); + for (unsigned int i=0; idata[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(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; idata[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; diff --git a/src/audio_arm.h b/src/audio_arm.h index 3fd48df00..9f2bc9de0 100644 --- a/src/audio_arm.h +++ b/src/audio_arm.h @@ -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 diff --git a/src/audio_avr.cpp b/src/audio_avr.cpp index 54d7e8f2b..f57816d38 100644 --- a/src/audio_avr.cpp +++ b/src/audio_avr.cpp @@ -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: { diff --git a/src/gui/menu_general.cpp b/src/gui/menu_general.cpp index f8c4c6ae9..6e7d8bad1 100644 --- a/src/gui/menu_general.cpp +++ b/src/gui/menu_general.cpp @@ -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 } diff --git a/src/maths.cpp b/src/maths.cpp index 12c5dd010..aeab39576 100644 --- a/src/maths.cpp +++ b/src/maths.cpp @@ -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 diff --git a/src/opentx.cpp b/src/opentx.cpp index 6e617e1a6..885a8f0c4 100644 --- a/src/opentx.cpp +++ b/src/opentx.cpp @@ -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(); diff --git a/src/targets/sky9x/audio_driver.cpp b/src/targets/sky9x/audio_driver.cpp index a69e4f0a4..d7c5dd323 100644 --- a/src/targets/sky9x/audio_driver.cpp +++ b/src/targets/sky9x/audio_driver.cpp @@ -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(); diff --git a/src/targets/sky9x/audio_driver.h b/src/targets/sky9x/audio_driver.h index 349100983..72e67fa21 100644 --- a/src/targets/sky9x/audio_driver.h +++ b/src/targets/sky9x/audio_driver.h @@ -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 diff --git a/src/targets/stock/board_stock.h b/src/targets/stock/board_stock.h index 9a849d59f..e13b52604 100644 --- a/src/targets/stock/board_stock.h +++ b/src/targets/stock/board_stock.h @@ -78,8 +78,8 @@ #define RESUME_PPMIN_INTERRUPT() TIMSK3 |= (1<CR1 &= ~TIM_CR1_CEN ; - TIM6->CNT = 0 ; - TIM6->ARR = limit(2, timer, 65535) ; - TIM6->CR1 |= TIM_CR1_CEN ; - } + TIM6->CR1 &= ~TIM_CR1_CEN ; + TIM6->CNT = 0 ; + TIM6->ARR = limit(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; diff --git a/src/targets/taranis/audio_driver.h b/src/targets/taranis/audio_driver.h index 9fdfde393..0a79ea75d 100644 --- a/src/targets/taranis/audio_driver.h +++ b/src/targets/taranis/audio_driver.h @@ -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] =