[SKY9X] Audio fix - Tested on 9XR-PRO (#4088)
[opentx.git] / radio / src / audio_arm.h
blob88b8cfad3087311555c56e4a32a91648c94a9803
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #ifndef _AUDIO_ARM_H_
22 #define _AUDIO_ARM_H_
24 #include <stddef.h>
25 #include "ff.h"
28 Implements a bit field, number of bits is set by the template,
29 each bit can be modified and read by the provided methods.
31 template <unsigned int NUM_BITS> class BitField {
32 private:
33 uint8_t bits[(NUM_BITS+7)/8];
34 public:
35 BitField()
37 reset();
40 void reset()
42 memset(bits, 0, sizeof(bits));
45 void setBit(unsigned int bitNo)
47 if (bitNo >= NUM_BITS) return;
48 bits[bitNo >> 3] = bits[bitNo >> 3] | (1 << (bitNo & 0x07));
51 bool getBit(unsigned int bitNo) const
53 // assert(bitNo < NUM_BITS);
54 if (bitNo >= NUM_BITS) return false;
55 return bits[bitNo >> 3] & (1 << (bitNo & 0x07));
58 unsigned int getSize() const
60 return NUM_BITS;
64 #define INDEX_LOGICAL_SWITCH_AUDIO_FILE(index, event) (2*(index)+(event))
65 #define INDEX_PHASE_AUDIO_FILE(index, event) (2*(index)+(event))
68 #define AUDIO_FILENAME_MAXLEN (42) // max length (example: /SOUNDS/fr/123456789012/1234567890-off.wav)
69 #define AUDIO_QUEUE_LENGTH (16) // must be a power of 2!
71 #define AUDIO_SAMPLE_RATE (32000)
72 #define AUDIO_BUFFER_DURATION (10)
73 #define AUDIO_BUFFER_SIZE (AUDIO_SAMPLE_RATE*AUDIO_BUFFER_DURATION/1000)
75 #if defined(SIMU) && defined(SIMU_AUDIO)
76 #define AUDIO_BUFFER_COUNT (10) // simulator needs more buffers for smooth audio
77 #elif defined(PCBHORUS)
78 #define AUDIO_BUFFER_COUNT (2) // smaller than Taranis since there is also a buffer on the ADC chip
79 #else
80 #define AUDIO_BUFFER_COUNT (3)
81 #endif
83 #define BEEP_MIN_FREQ (150)
84 #define BEEP_MAX_FREQ (15000)
85 #define BEEP_DEFAULT_FREQ (2250)
86 #define BEEP_KEY_UP_FREQ (BEEP_DEFAULT_FREQ+150)
87 #define BEEP_KEY_DOWN_FREQ (BEEP_DEFAULT_FREQ-150)
89 #if defined(PCBSKY9X)
90 enum AudioBufferState
92 AUDIO_BUFFER_FREE,
93 AUDIO_BUFFER_FILLED,
94 AUDIO_BUFFER_PLAYING
96 #endif
98 #if defined(SIMU)
99 typedef uint16_t audio_data_t;
100 #define AUDIO_DATA_SILENCE 0x8000
101 #define AUDIO_DATA_MIN 0
102 #define AUDIO_DATA_MAX 0xffff
103 #define AUDIO_BITS_PER_SAMPLE 16
104 #elif defined(PCBHORUS)
105 typedef int16_t audio_data_t;
106 #define AUDIO_DATA_SILENCE 0
107 #define AUDIO_DATA_MIN INT16_MIN
108 #define AUDIO_DATA_MAX INT16_MAX
109 #define AUDIO_BITS_PER_SAMPLE 16
110 #else
111 typedef uint16_t audio_data_t;
112 #define AUDIO_DATA_SILENCE (0x8000 >> 4)
113 #define AUDIO_DATA_MIN 0
114 #define AUDIO_DATA_MAX 0x0fff
115 #define AUDIO_BITS_PER_SAMPLE 12
116 #endif
118 struct AudioBuffer {
119 audio_data_t data[AUDIO_BUFFER_SIZE];
120 uint16_t size;
121 uint8_t state;
124 extern AudioBuffer audioBuffers[AUDIO_BUFFER_COUNT];
126 enum FragmentTypes {
127 FRAGMENT_EMPTY,
128 FRAGMENT_TONE,
129 FRAGMENT_FILE,
132 struct Tone {
133 uint16_t freq;
134 uint16_t duration;
135 uint16_t pause;
136 int8_t freqIncr;
137 uint8_t reset;
138 Tone() {};
139 Tone(uint16_t freq, uint16_t duration, uint16_t pause, int8_t freqIncr, bool reset):
140 freq(freq),
141 duration(duration),
142 pause(pause),
143 freqIncr(freqIncr),
144 reset(reset)
149 struct AudioFragment {
150 uint8_t type;
151 uint8_t id;
152 uint8_t repeat;
153 union {
154 Tone tone;
155 char file[AUDIO_FILENAME_MAXLEN+1];
158 AudioFragment() { clear(); };
160 AudioFragment(uint16_t freq, uint16_t duration, uint16_t pause, uint8_t repeat, int8_t freqIncr, bool reset, uint8_t id=0):
161 type(FRAGMENT_TONE),
162 id(id),
163 repeat(repeat),
164 tone(freq, duration, pause, freqIncr, reset)
167 AudioFragment(const char * filename, uint8_t repeat, uint8_t id=0):
168 type(FRAGMENT_FILE),
169 id(id),
170 repeat(repeat)
172 strcpy(file, filename);
175 void clear() { memset(this, 0, sizeof(AudioFragment)); };
178 class ToneContext {
179 public:
181 inline void clear() { memset(this, 0, sizeof(ToneContext)); };
182 bool isFree() const { return fragment.type == FRAGMENT_EMPTY; };
183 int mixBuffer(AudioBuffer *buffer, int volume, unsigned int fade);
185 void setFragment(uint16_t freq, uint16_t duration, uint16_t pause, uint8_t repeat, int8_t freqIncr, bool reset, uint8_t id=0)
187 fragment = AudioFragment(freq, duration, pause, repeat, freqIncr, reset, id);
190 private:
191 AudioFragment fragment;
193 struct {
194 double step;
195 double idx;
196 float volume;
197 uint16_t freq;
198 uint16_t duration;
199 uint16_t pause;
200 } state;
204 class WavContext {
205 public:
207 inline void clear() { fragment.clear(); };
209 int mixBuffer(AudioBuffer *buffer, int volume, unsigned int fade);
210 bool hasId(uint8_t id) const { return fragment.id == id; };
212 void setFragment(const char * filename, uint8_t repeat, uint8_t id)
214 fragment = AudioFragment(filename, repeat, id);
217 void stop(uint8_t id)
219 if (fragment.id == id) {
220 fragment.clear();
224 private:
225 AudioFragment fragment;
227 struct {
228 FIL file;
229 uint8_t codec;
230 uint32_t freq;
231 uint32_t size;
232 uint8_t resampleRatio;
233 uint16_t readSize;
234 } state;
237 class MixedContext {
238 #if defined(CLI)
239 friend void printAudioVars();
240 #endif
241 public:
243 MixedContext()
245 clear();
248 void setFragment(const AudioFragment * frag)
250 if (frag) {
251 fragment = *frag;
255 inline void clear()
257 tone.clear(); // the biggest member of the uninon
260 bool isEmpty() const { return fragment.type == FRAGMENT_EMPTY; };
261 bool isTone() const { return fragment.type == FRAGMENT_TONE; };
262 bool isFile() const { return fragment.type == FRAGMENT_FILE; };
263 bool hasId(uint8_t id) const { return fragment.id == id; };
265 int mixBuffer(AudioBuffer *buffer, int toneVolume, int wavVolume, unsigned int fade)
267 if (isTone()) return tone.mixBuffer(buffer, toneVolume, fade);
268 else if (isFile()) return wav.mixBuffer(buffer, wavVolume, fade);
269 return 0;
272 private:
273 union {
274 AudioFragment fragment; // a hack: fragment is used to access the fragment members of tone and wav
275 ToneContext tone;
276 WavContext wav;
281 class AudioBufferFifo {
282 #if defined(CLI)
283 friend void printAudioVars();
284 #endif
286 private:
287 volatile uint8_t readIdx;
288 volatile uint8_t writeIdx;
289 volatile bool bufferFull;
291 // readIdx == writeIdx -> buffer empty
292 // readIdx == writeIdx + 1 -> buffer full
294 inline uint8_t nextBufferIdx(uint8_t idx) const
296 return (idx >= AUDIO_BUFFER_COUNT-1 ? 0 : idx+1);
298 bool full() const
300 return bufferFull;
302 bool empty() const
304 return (readIdx == writeIdx) && !bufferFull;
306 uint8_t used() const
308 return bufferFull ? AUDIO_BUFFER_COUNT : writeIdx - readIdx;
311 public:
312 AudioBufferFifo() : readIdx(0), writeIdx(0), bufferFull(false)
314 memset(audioBuffers, 0, sizeof(audioBuffers));
317 // returns an empty buffer to be filled wit data and put back into FIFO with audioPushBuffer()
318 AudioBuffer * getEmptyBuffer() const
320 #if defined(PCBSKY9X)
321 AudioBuffer * buffer = &audioBuffers[writeIdx];
322 return buffer->state == AUDIO_BUFFER_FREE ? buffer : NULL;
323 #else
324 return full() ? NULL : &audioBuffers[writeIdx];
325 #endif
328 // puts filled buffer into FIFO
329 void audioPushBuffer()
331 audioDisableIrq();
332 #if defined(PCBSKY9X)
333 AudioBuffer * buffer = &audioBuffers[writeIdx];
334 buffer->state = AUDIO_BUFFER_FILLED;
335 #endif
336 writeIdx = nextBufferIdx(writeIdx);
337 bufferFull = (writeIdx == readIdx);
338 audioEnableIrq();
341 // returns a pointer to the audio buffer to be played
342 const AudioBuffer * getNextFilledBuffer()
344 #if defined(PCBSKY9X)
345 uint8_t idx = readIdx;
346 do {
347 AudioBuffer * buffer = &audioBuffers[idx];
348 if (buffer->state == AUDIO_BUFFER_FILLED) {
349 buffer->state = AUDIO_BUFFER_PLAYING;
350 readIdx = idx;
351 return buffer;
353 idx = nextBufferIdx(idx);
354 } while (idx != writeIdx); // this fixes a bug if all buffers are filled
355 return NULL;
356 #else
357 return empty() ? NULL : &audioBuffers[readIdx];
358 #endif
361 // frees the last played buffer
362 void freeNextFilledBuffer()
364 audioDisableIrq();
365 #if defined(PCBSKY9X)
366 if (audioBuffers[readIdx].state == AUDIO_BUFFER_PLAYING) {
367 audioBuffers[readIdx].state = AUDIO_BUFFER_FREE;
368 readIdx = nextBufferIdx(readIdx);
369 bufferFull = false;
371 #else
372 readIdx = nextBufferIdx(readIdx);
373 bufferFull = false;
374 #endif
375 audioEnableIrq();
378 bool filledAtleast(int noBuffers) const
380 #if defined(PCBSKY9X)
381 int count = 0;
382 for(int n= 0; n<AUDIO_BUFFER_COUNT; ++n) {
383 if (audioBuffers[n].state == AUDIO_BUFFER_FILLED) {
384 if (++count >= noBuffers) {
385 return true;
389 return false;
390 #else
391 return used() >= noBuffers;
392 #endif
397 class AudioFragmentFifo
399 #if defined(CLI)
400 friend void printAudioVars();
401 #endif
402 private:
403 volatile uint8_t ridx;
404 volatile uint8_t widx;
405 AudioFragment fragments[AUDIO_QUEUE_LENGTH];
407 uint8_t nextIdx(uint8_t idx) const
409 return (idx + 1) & (AUDIO_QUEUE_LENGTH - 1);
412 public:
413 AudioFragmentFifo() : ridx(0), widx(0), fragments() {};
415 bool hasId(uint8_t id)
417 uint8_t i = ridx;
418 while (i != widx) {
419 AudioFragment & fragment = fragments[i];
420 if (fragment.id == id) return true;
421 i = nextIdx(i);
423 return false;
426 bool empty() const
428 return ridx == widx;
431 bool full() const
433 return ridx == nextIdx(widx);
436 void clear()
438 widx = ridx; // clean the queue
441 const AudioFragment * get()
443 if (!empty()) {
444 const AudioFragment * result = &fragments[ridx];
445 if (!fragments[ridx].repeat--) {
446 // repeat is done, move to the next fragment
447 ridx = nextIdx(ridx);
449 return result;
451 return 0;
454 void push(const AudioFragment & fragment)
456 if (!full()) {
457 // TRACE("fragment %d at %d", fragment.type, widx);
458 fragments[widx] = fragment;
459 widx = nextIdx(widx);
465 class AudioQueue {
467 #if defined(SIMU_AUDIO)
468 friend void fillAudioBuffer(void *, uint8_t *, int);
469 #endif
470 #if defined(CLI)
471 friend void printAudioVars();
472 #endif
473 public:
474 AudioQueue();
475 void start() { _started = true; };
476 void playTone(uint16_t freq, uint16_t len, uint16_t pause=0, uint8_t flags=0, int8_t freqIncr=0);
477 void playFile(const char *filename, uint8_t flags=0, uint8_t id=0);
478 void stopPlay(uint8_t id);
479 void stopAll();
480 void flush();
481 void pause(uint16_t tLen);
482 void stopSD();
483 bool isPlaying(uint8_t id);
484 bool isEmpty() const { return fragmentsFifo.empty(); };
485 void wakeup();
486 bool started() const { return _started; };
488 AudioBufferFifo buffersFifo;
490 private:
491 volatile bool _started;
492 MixedContext normalContext;
493 WavContext backgroundContext;
494 ToneContext priorityContext;
495 ToneContext varioContext;
496 AudioFragmentFifo fragmentsFifo;
499 extern uint8_t currentSpeakerVolume;
500 extern AudioQueue audioQueue;
502 enum {
503 ID_PLAY_FROM_SD_MANAGER = 254,
504 ID_PLAY_BYE = 255
507 void codecsInit();
508 void audioEvent(unsigned int index);
509 void audioPlay(unsigned int index, uint8_t id=0);
510 void audioStart();
512 #if defined(AUDIO) && defined(BUZZER)
513 #define AUDIO_BUZZER(a, b) do { a; b; } while(0)
514 #elif defined(AUDIO)
515 #define AUDIO_BUZZER(a, b) a
516 #else
517 #define AUDIO_BUZZER(a, b) b
518 #endif
520 #if defined(VOICE)
521 #define AUDIO_ERROR_MESSAGE(e) audioEvent(e)
522 #define AUDIO_TIMER_MINUTE(t) playDuration(t, 0, 0)
523 #else
524 #define AUDIO_ERROR_MESSAGE(e) audioEvent(AU_ERROR)
525 #define AUDIO_TIMER_MINUTE(t) audioDefevent(AU_WARNING1)
526 #endif
528 void audioKeyPress();
529 void audioKeyError();
530 void audioTrimPress(int value);
531 void audioTimerCountdown(uint8_t timer, int value);
533 #define AUDIO_KEY_PRESS() audioKeyPress()
534 #define AUDIO_KEY_ERROR() audioKeyError()
536 #define AUDIO_HELLO() audioPlay(AUDIO_HELLO)
537 #define AUDIO_BYE() audioPlay(AU_BYE, ID_PLAY_BYE)
538 #define AUDIO_WARNING1() AUDIO_BUZZER(audioEvent(AU_WARNING1), beep(3))
539 #define AUDIO_WARNING2() AUDIO_BUZZER(audioEvent(AU_WARNING2), beep(2))
540 #define AUDIO_TX_BATTERY_LOW() AUDIO_BUZZER(audioEvent(AU_TX_BATTERY_LOW), beep(4))
541 #if defined(PCBSKY9X)
542 #define AUDIO_TX_MAH_HIGH() audioEvent(AU_TX_MAH_HIGH)
543 #define AUDIO_TX_TEMP_HIGH() audioEvent(AU_TX_TEMP_HIGH)
544 #endif
545 #define AUDIO_ERROR() AUDIO_BUZZER(audioEvent(AU_ERROR), beep(4))
546 #define AUDIO_TIMER_COUNTDOWN(idx, val) audioTimerCountdown(idx, val)
547 #define AUDIO_TIMER_ELAPSED(idx) AUDIO_BUZZER(audioEvent(AU_TIMER1_ELAPSED+idx), beep(3))
548 #define AUDIO_INACTIVITY() AUDIO_BUZZER(audioEvent(AU_INACTIVITY), beep(3))
549 #define AUDIO_MIX_WARNING(x) AUDIO_BUZZER(audioEvent(AU_MIX_WARNING_1+x-1), beep(1))
550 #define AUDIO_POT_MIDDLE(x) AUDIO_BUZZER(audioEvent(AU_STICK1_MIDDLE+x), beep(2))
551 #define AUDIO_TRIM_MIDDLE() AUDIO_BUZZER(audioEvent(AU_TRIM_MIDDLE), beep(2))
552 #define AUDIO_TRIM_MIN() AUDIO_BUZZER(audioEvent(AU_TRIM_MIN), beep(2))
553 #define AUDIO_TRIM_MAX() AUDIO_BUZZER(audioEvent(AU_TRIM_MAX), beep(2))
554 #define AUDIO_TRIM_PRESS(val) audioTrimPress(val)
555 #define AUDIO_PLAY(p) audioEvent(p)
556 #define AUDIO_VARIO(fq, t, p, f) audioQueue.playTone(fq, t, p, f)
557 #define AUDIO_RSSI_ORANGE() audioEvent(AU_RSSI_ORANGE)
558 #define AUDIO_RSSI_RED() audioEvent(AU_RSSI_RED)
559 #define AUDIO_SWR_RED() audioEvent(AU_SWR_RED)
560 #define AUDIO_TELEMETRY_LOST() audioEvent(AU_TELEMETRY_LOST)
561 #define AUDIO_TELEMETRY_BACK() audioEvent(AU_TELEMETRY_BACK)
562 #define AUDIO_TRAINER_LOST() audioEvent(AU_TRAINER_LOST)
563 #define AUDIO_TRAINER_BACK() audioEvent(AU_TRAINER_BACK)
565 #define AUDIO_HEARTBEAT()
567 enum AutomaticPromptsCategories {
568 SYSTEM_AUDIO_CATEGORY,
569 MODEL_AUDIO_CATEGORY,
570 PHASE_AUDIO_CATEGORY,
571 SWITCH_AUDIO_CATEGORY,
572 LOGICAL_SWITCH_AUDIO_CATEGORY,
575 enum AutomaticPromptsEvents {
576 AUDIO_EVENT_OFF,
577 AUDIO_EVENT_ON,
578 AUDIO_EVENT_MID,
581 void pushPrompt(uint16_t prompt, uint8_t id=0);
582 void pushUnit(uint8_t unit, uint8_t idx, uint8_t id);
583 void playModelName();
585 #define I18N_PLAY_FUNCTION(lng, x, ...) void lng ## _ ## x(__VA_ARGS__, uint8_t id)
586 #define PLAY_FUNCTION(x, ...) void x(__VA_ARGS__, uint8_t id)
587 #define PUSH_NUMBER_PROMPT(p) pushPrompt((p), id)
588 #define PUSH_UNIT_PROMPT(p, i) pushUnit((p), (i), id)
589 #define PLAY_NUMBER(n, u, a) playNumber((n), (u), (a), id)
590 #define PLAY_DURATION(d, att) playDuration((d), (att), id)
591 #define PLAY_DURATION_ATT , uint8_t flags
592 #define PLAY_TIME 1
593 #define IS_PLAY_TIME() (flags&PLAY_TIME)
594 #define IS_PLAYING(id) audioQueue.isPlaying((id))
595 #define PLAY_VALUE(v, id) playValue((v), (id))
596 #define PLAY_FILE(f, flags, id) audioQueue.playFile((f), (flags), (id))
597 #define STOP_PLAY(id) audioQueue.stopPlay((id))
598 #define AUDIO_RESET() audioQueue.stopAll()
599 #define AUDIO_FLUSH() audioQueue.flush()
601 #if defined(SDCARD)
602 extern tmr10ms_t timeAutomaticPromptsSilence;
603 void playModelEvent(uint8_t category, uint8_t index, event_t event=0);
604 #define PLAY_PHASE_OFF(phase) playModelEvent(PHASE_AUDIO_CATEGORY, phase, AUDIO_EVENT_OFF)
605 #define PLAY_PHASE_ON(phase) playModelEvent(PHASE_AUDIO_CATEGORY, phase, AUDIO_EVENT_ON)
606 #define PLAY_SWITCH_MOVED(sw) playModelEvent(SWITCH_AUDIO_CATEGORY, sw)
607 #define PLAY_LOGICAL_SWITCH_OFF(sw) playModelEvent(LOGICAL_SWITCH_AUDIO_CATEGORY, sw, AUDIO_EVENT_OFF)
608 #define PLAY_LOGICAL_SWITCH_ON(sw) playModelEvent(LOGICAL_SWITCH_AUDIO_CATEGORY, sw, AUDIO_EVENT_ON)
609 #define PLAY_MODEL_NAME() playModelName()
610 #define START_SILENCE_PERIOD() timeAutomaticPromptsSilence = get_tmr10ms()
611 #define IS_SILENCE_PERIOD_ELAPSED() (get_tmr10ms()-timeAutomaticPromptsSilence > 50)
612 #else
613 #define PLAY_PHASE_OFF(phase)
614 #define PLAY_PHASE_ON(phase)
615 #define PLAY_SWITCH_MOVED(sw)
616 #define PLAY_LOGICAL_SWITCH_OFF(sw)
617 #define PLAY_LOGICAL_SWITCH_ON(sw)
618 #define PLAY_MODEL_NAME()
619 #define START_SILENCE_PERIOD()
620 #endif
622 char * getAudioPath(char * path);
624 void referenceSystemAudioFiles();
625 void referenceModelAudioFiles();
627 bool isAudioFileReferenced(uint32_t i, char * filename/*at least AUDIO_FILENAME_MAXLEN+1 long*/);
629 #endif // _AUDIO_ARM_H_