Companion: Russian UI (#7180)
[opentx.git] / radio / src / audio.h
blobac356863a8c0ffdf80d33018f12a6b853cb366d4
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(PCBX12S)
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(AUDIO_DUAL_BUFFER)
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(PCBX12S)
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 #if defined(AUDIO_DUAL_BUFFER)
122 uint8_t state;
123 #endif
126 extern AudioBuffer audioBuffers[AUDIO_BUFFER_COUNT];
128 enum FragmentTypes {
129 FRAGMENT_EMPTY,
130 FRAGMENT_TONE,
131 FRAGMENT_FILE,
134 struct Tone {
135 uint16_t freq;
136 uint16_t duration;
137 uint16_t pause;
138 int8_t freqIncr;
139 uint8_t reset;
140 Tone() {};
141 Tone(uint16_t freq, uint16_t duration, uint16_t pause, int8_t freqIncr, bool reset):
142 freq(freq),
143 duration(duration),
144 pause(pause),
145 freqIncr(freqIncr),
146 reset(reset)
150 struct AudioFragment {
151 uint8_t type;
152 uint8_t id;
153 uint8_t repeat;
154 union {
155 Tone tone;
156 char file[AUDIO_FILENAME_MAXLEN+1];
159 AudioFragment() { clear(); };
161 AudioFragment(uint16_t freq, uint16_t duration, uint16_t pause, uint8_t repeat, int8_t freqIncr, bool reset, uint8_t id=0):
162 type(FRAGMENT_TONE),
163 id(id),
164 repeat(repeat),
165 tone(freq, duration, pause, freqIncr, reset)
168 AudioFragment(const char * filename, uint8_t repeat, uint8_t id=0):
169 type(FRAGMENT_FILE),
170 id(id),
171 repeat(repeat)
173 strcpy(file, filename);
176 void clear()
178 memset(reinterpret_cast<void*>(this), 0, sizeof(AudioFragment));
182 class ToneContext {
183 public:
185 inline void clear()
187 memset(reinterpret_cast<void*>(this), 0, sizeof(ToneContext));
190 bool isFree() const
192 return fragment.type == FRAGMENT_EMPTY;
195 int mixBuffer(AudioBuffer *buffer, int volume, unsigned int fade);
197 void setFragment(uint16_t freq, uint16_t duration, uint16_t pause, uint8_t repeat, int8_t freqIncr, bool reset, uint8_t id=0)
199 fragment = AudioFragment(freq, duration, pause, repeat, freqIncr, reset, id);
202 private:
203 AudioFragment fragment;
205 struct {
206 float step;
207 float idx;
208 float volume;
209 uint16_t freq;
210 uint16_t duration;
211 uint16_t pause;
212 } state;
216 class WavContext {
217 public:
219 inline void clear() { fragment.clear(); };
221 int mixBuffer(AudioBuffer *buffer, int volume, unsigned int fade);
222 bool hasPromptId(uint8_t id) const { return fragment.id == id; };
224 void setFragment(const char * filename, uint8_t repeat, uint8_t id)
226 fragment = AudioFragment(filename, repeat, id);
229 void stop(uint8_t id)
231 if (fragment.id == id) {
232 fragment.clear();
236 private:
237 AudioFragment fragment;
239 struct {
240 FIL file;
241 uint8_t codec;
242 uint32_t freq;
243 uint32_t size;
244 uint8_t resampleRatio;
245 uint16_t readSize;
246 } state;
249 class MixedContext {
250 #if defined(CLI)
251 friend void printAudioVars();
252 #endif
253 public:
255 MixedContext()
257 clear();
260 void setFragment(const AudioFragment * frag)
262 if (frag) {
263 fragment = *frag;
267 inline void clear()
269 tone.clear(); // the biggest member of the uninon
272 bool isEmpty() const { return fragment.type == FRAGMENT_EMPTY; };
273 bool isTone() const { return fragment.type == FRAGMENT_TONE; };
274 bool isFile() const { return fragment.type == FRAGMENT_FILE; };
275 bool hasPromptId(uint8_t id) const { return fragment.id == id; };
277 int mixBuffer(AudioBuffer *buffer, int toneVolume, int wavVolume, unsigned int fade)
279 if (isTone())
280 return tone.mixBuffer(buffer, toneVolume, fade);
281 else if (isFile())
282 return wav.mixBuffer(buffer, wavVolume, fade);
283 return 0;
286 private:
287 union {
288 AudioFragment fragment; // a hack: fragment is used to access the fragment members of tone and wav
289 ToneContext tone;
290 WavContext wav;
295 class AudioBufferFifo {
296 #if defined(CLI)
297 friend void printAudioVars();
298 #endif
300 private:
301 volatile uint8_t readIdx;
302 volatile uint8_t writeIdx;
303 volatile bool bufferFull;
305 // readIdx == writeIdx -> buffer empty
306 // readIdx == writeIdx + 1 -> buffer full
308 inline uint8_t nextBufferIdx(uint8_t idx) const
310 return (idx >= AUDIO_BUFFER_COUNT-1 ? 0 : idx+1);
313 bool full() const
315 return bufferFull;
318 bool empty() const
320 return (readIdx == writeIdx) && !bufferFull;
323 uint8_t used() const
325 return bufferFull ? AUDIO_BUFFER_COUNT : writeIdx - readIdx;
328 public:
329 AudioBufferFifo() : readIdx(0), writeIdx(0), bufferFull(false)
331 memset(audioBuffers, 0, sizeof(audioBuffers));
334 // returns an empty buffer to be filled wit data and put back into FIFO with audioPushBuffer()
335 AudioBuffer * getEmptyBuffer() const
337 #if defined(AUDIO_DUAL_BUFFER)
338 AudioBuffer * buffer = &audioBuffers[writeIdx];
339 return buffer->state == AUDIO_BUFFER_FREE ? buffer : NULL;
340 #else
341 return full() ? NULL : &audioBuffers[writeIdx];
342 #endif
345 // puts filled buffer into FIFO
346 void audioPushBuffer()
348 audioDisableIrq();
349 #if defined(AUDIO_DUAL_BUFFER)
350 AudioBuffer * buffer = &audioBuffers[writeIdx];
351 buffer->state = AUDIO_BUFFER_FILLED;
352 #endif
353 writeIdx = nextBufferIdx(writeIdx);
354 bufferFull = (writeIdx == readIdx);
355 audioEnableIrq();
358 // returns a pointer to the audio buffer to be played
359 const AudioBuffer * getNextFilledBuffer()
361 #if defined(AUDIO_DUAL_BUFFER)
362 uint8_t idx = readIdx;
363 do {
364 AudioBuffer * buffer = &audioBuffers[idx];
365 if (buffer->state == AUDIO_BUFFER_FILLED) {
366 buffer->state = AUDIO_BUFFER_PLAYING;
367 readIdx = idx;
368 return buffer;
370 idx = nextBufferIdx(idx);
371 } while (idx != writeIdx); // this fixes a bug if all buffers are filled
372 return NULL;
373 #else
374 return empty() ? NULL : &audioBuffers[readIdx];
375 #endif
378 // frees the last played buffer
379 void freeNextFilledBuffer()
381 audioDisableIrq();
382 #if defined(AUDIO_DUAL_BUFFER)
383 if (audioBuffers[readIdx].state == AUDIO_BUFFER_PLAYING) {
384 audioBuffers[readIdx].state = AUDIO_BUFFER_FREE;
385 readIdx = nextBufferIdx(readIdx);
386 bufferFull = false;
388 #else
389 readIdx = nextBufferIdx(readIdx);
390 bufferFull = false;
391 #endif
392 audioEnableIrq();
395 bool filledAtleast(int noBuffers) const
397 #if defined(AUDIO_DUAL_BUFFER)
398 int count = 0;
399 for(int n= 0; n<AUDIO_BUFFER_COUNT; ++n) {
400 if (audioBuffers[n].state == AUDIO_BUFFER_FILLED) {
401 if (++count >= noBuffers) {
402 return true;
406 return false;
407 #else
408 return used() >= noBuffers;
409 #endif
414 class AudioFragmentFifo
416 #if defined(CLI)
417 friend void printAudioVars();
418 #endif
419 private:
420 volatile uint8_t ridx;
421 volatile uint8_t widx;
422 AudioFragment fragments[AUDIO_QUEUE_LENGTH];
424 uint8_t nextIdx(uint8_t idx) const
426 return (idx + 1) & (AUDIO_QUEUE_LENGTH - 1);
429 public:
430 AudioFragmentFifo() : ridx(0), widx(0), fragments() {};
432 bool hasPromptId(uint8_t id)
434 uint8_t i = ridx;
435 while (i != widx) {
436 AudioFragment & fragment = fragments[i];
437 if (fragment.id == id) return true;
438 i = nextIdx(i);
440 return false;
443 bool removePromptById(uint8_t id)
445 uint8_t i = ridx;
446 while (i != widx) {
447 AudioFragment & fragment = fragments[i];
448 if (fragment.id == id) fragment.clear();
449 i = nextIdx(i);
451 return false;
454 bool empty() const
456 return ridx == widx;
459 bool full() const
461 return ridx == nextIdx(widx);
464 void clear()
466 widx = ridx; // clean the queue
469 const AudioFragment * get()
471 if (!empty()) {
472 const AudioFragment * result = &fragments[ridx];
473 if (!fragments[ridx].repeat--) {
474 // repeat is done, move to the next fragment
475 ridx = nextIdx(ridx);
477 return result;
479 return 0;
482 void push(const AudioFragment & fragment)
484 if (!full()) {
485 // TRACE("fragment %d at %d", fragment.type, widx);
486 fragments[widx] = fragment;
487 widx = nextIdx(widx);
493 class AudioQueue {
495 #if defined(SIMU_AUDIO)
496 friend void fillAudioBuffer(void *, uint8_t *, int);
497 #endif
498 #if defined(CLI)
499 friend void printAudioVars();
500 #endif
501 public:
502 AudioQueue();
503 void start() { _started = true; };
504 void playTone(uint16_t freq, uint16_t len, uint16_t pause=0, uint8_t flags=0, int8_t freqIncr=0);
505 void playFile(const char *filename, uint8_t flags=0, uint8_t id=0);
506 void stopPlay(uint8_t id);
507 void stopAll();
508 void flush();
509 void pause(uint16_t tLen);
510 void stopSD();
511 bool isPlaying(uint8_t id);
512 bool isEmpty() const { return fragmentsFifo.empty(); };
513 void wakeup();
514 bool started() const { return _started; };
516 AudioBufferFifo buffersFifo;
518 private:
519 volatile bool _started;
520 MixedContext normalContext;
521 WavContext backgroundContext;
522 ToneContext priorityContext;
523 ToneContext varioContext;
524 AudioFragmentFifo fragmentsFifo;
527 extern uint8_t currentSpeakerVolume;
528 extern AudioQueue audioQueue;
530 enum {
531 // IDs for special functions [0:64]
532 // IDs for global functions [64:128]
533 ID_PLAY_PROMPT_BASE = 128,
534 ID_PLAY_FROM_SD_MANAGER = 255,
537 void codecsInit();
538 void audioEvent(unsigned int index);
539 void audioPlay(unsigned int index, uint8_t id=0);
540 void audioStart();
541 void audioTask(void * pdata);
543 #if defined(AUDIO) && defined(BUZZER)
544 #define AUDIO_BUZZER(a, b) do { a; b; } while(0)
545 #elif defined(AUDIO)
546 #define AUDIO_BUZZER(a, b) a
547 #else
548 #define AUDIO_BUZZER(a, b) b
549 #endif
551 #define AUDIO_ERROR_MESSAGE(e) audioEvent(e)
552 #define AUDIO_TIMER_MINUTE(t) playDuration(t, 0, 0)
554 void audioKeyPress();
555 void audioKeyError();
556 void audioTrimPress(int value);
557 void audioTimerCountdown(uint8_t timer, int value);
559 #define AUDIO_KEY_PRESS() audioKeyPress()
560 #define AUDIO_KEY_ERROR() audioKeyError()
562 #define AUDIO_HELLO() audioPlay(AUDIO_HELLO)
563 #define AUDIO_BYE() audioPlay(AU_BYE, ID_PLAY_PROMPT_BASE + AU_BYE)
564 #define AUDIO_WARNING1() AUDIO_BUZZER(audioEvent(AU_WARNING1), beep(3))
565 #define AUDIO_WARNING2() AUDIO_BUZZER(audioEvent(AU_WARNING2), beep(2))
566 #define AUDIO_TX_BATTERY_LOW() AUDIO_BUZZER(audioEvent(AU_TX_BATTERY_LOW), beep(4))
567 #if defined(PCBSKY9X)
568 #define AUDIO_TX_MAH_HIGH() audioEvent(AU_TX_MAH_HIGH)
569 #define AUDIO_TX_TEMP_HIGH() audioEvent(AU_TX_TEMP_HIGH)
570 #endif
571 #define AUDIO_ERROR() AUDIO_BUZZER(audioEvent(AU_ERROR), beep(4))
572 #define AUDIO_TIMER_COUNTDOWN(idx, val) audioTimerCountdown(idx, val)
573 #define AUDIO_TIMER_ELAPSED(idx) AUDIO_BUZZER(audioEvent(AU_TIMER1_ELAPSED+idx), beep(3))
574 #define AUDIO_INACTIVITY() AUDIO_BUZZER(audioEvent(AU_INACTIVITY), beep(3))
575 #define AUDIO_MIX_WARNING(x) AUDIO_BUZZER(audioEvent(AU_MIX_WARNING_1+x-1), beep(1))
576 #define AUDIO_POT_MIDDLE(x) AUDIO_BUZZER(audioEvent(AU_STICK1_MIDDLE+x), beep(2))
577 #define AUDIO_TRIM_MIDDLE() AUDIO_BUZZER(audioEvent(AU_TRIM_MIDDLE), beep(2))
578 #define AUDIO_TRIM_MIN() AUDIO_BUZZER(audioEvent(AU_TRIM_MIN), beep(2))
579 #define AUDIO_TRIM_MAX() AUDIO_BUZZER(audioEvent(AU_TRIM_MAX), beep(2))
580 #define AUDIO_TRIM_PRESS(val) audioTrimPress(val)
581 #define AUDIO_PLAY(p) audioEvent(p)
582 #define AUDIO_VARIO(fq, t, p, f) audioQueue.playTone(fq, t, p, f)
583 #define AUDIO_RSSI_ORANGE() audioEvent(AU_RSSI_ORANGE)
584 #define AUDIO_RSSI_RED() audioEvent(AU_RSSI_RED)
585 #define AUDIO_RAS_RED() audioEvent(AU_RAS_RED)
586 #define AUDIO_TELEMETRY_LOST() audioEvent(AU_TELEMETRY_LOST)
587 #define AUDIO_TELEMETRY_BACK() audioEvent(AU_TELEMETRY_BACK)
588 #define AUDIO_TRAINER_LOST() audioEvent(AU_TRAINER_LOST)
589 #define AUDIO_TRAINER_BACK() audioEvent(AU_TRAINER_BACK)
591 enum AutomaticPromptsCategories {
592 SYSTEM_AUDIO_CATEGORY,
593 MODEL_AUDIO_CATEGORY,
594 PHASE_AUDIO_CATEGORY,
595 SWITCH_AUDIO_CATEGORY,
596 LOGICAL_SWITCH_AUDIO_CATEGORY,
599 enum AutomaticPromptsEvents {
600 AUDIO_EVENT_OFF,
601 AUDIO_EVENT_ON,
602 AUDIO_EVENT_MID,
605 void pushPrompt(uint16_t prompt, uint8_t id=0);
606 void pushUnit(uint8_t unit, uint8_t idx, uint8_t id);
607 void playModelName();
609 #define I18N_PLAY_FUNCTION(lng, x, ...) void lng ## _ ## x(__VA_ARGS__, uint8_t id)
610 #define PLAY_FUNCTION(x, ...) void x(__VA_ARGS__, uint8_t id)
611 #define PUSH_NUMBER_PROMPT(p) pushPrompt((p), id)
612 #define PUSH_UNIT_PROMPT(p, i) pushUnit((p), (i), id)
613 #define PLAY_NUMBER(n, u, a) playNumber((n), (u), (a), id)
614 #define PLAY_DURATION(d, att) playDuration((d), (att), id)
615 #define PLAY_DURATION_ATT , uint8_t flags
616 #define PLAY_TIME 1
617 #define IS_PLAY_TIME() (flags&PLAY_TIME)
618 #define IS_PLAYING(id) audioQueue.isPlaying((id))
619 #define PLAY_VALUE(v, id) playValue((v), (id))
620 #define PLAY_FILE(f, flags, id) audioQueue.playFile((f), (flags), (id))
621 #define STOP_PLAY(id) audioQueue.stopPlay((id))
622 #define AUDIO_RESET() audioQueue.stopAll()
623 #define AUDIO_FLUSH() audioQueue.flush()
625 #if defined(SDCARD)
626 extern tmr10ms_t timeAutomaticPromptsSilence;
627 void playModelEvent(uint8_t category, uint8_t index, event_t event=0);
628 #define PLAY_PHASE_OFF(phase) playModelEvent(PHASE_AUDIO_CATEGORY, phase, AUDIO_EVENT_OFF)
629 #define PLAY_PHASE_ON(phase) playModelEvent(PHASE_AUDIO_CATEGORY, phase, AUDIO_EVENT_ON)
630 #define PLAY_SWITCH_MOVED(sw) playModelEvent(SWITCH_AUDIO_CATEGORY, sw)
631 #define PLAY_LOGICAL_SWITCH_OFF(sw) playModelEvent(LOGICAL_SWITCH_AUDIO_CATEGORY, sw, AUDIO_EVENT_OFF)
632 #define PLAY_LOGICAL_SWITCH_ON(sw) playModelEvent(LOGICAL_SWITCH_AUDIO_CATEGORY, sw, AUDIO_EVENT_ON)
633 #define PLAY_MODEL_NAME() playModelName()
634 #define START_SILENCE_PERIOD() timeAutomaticPromptsSilence = get_tmr10ms()
635 #define IS_SILENCE_PERIOD_ELAPSED() (get_tmr10ms()-timeAutomaticPromptsSilence > 50)
636 #else
637 #define PLAY_PHASE_OFF(phase)
638 #define PLAY_PHASE_ON(phase)
639 #define PLAY_SWITCH_MOVED(sw)
640 #define PLAY_LOGICAL_SWITCH_OFF(sw)
641 #define PLAY_LOGICAL_SWITCH_ON(sw)
642 #define PLAY_MODEL_NAME()
643 #define START_SILENCE_PERIOD()
644 #endif
646 char * getAudioPath(char * path);
648 void referenceSystemAudioFiles();
649 void referenceModelAudioFiles();
651 bool isAudioFileReferenced(uint32_t i, char * filename/*at least AUDIO_FILENAME_MAXLEN+1 long*/);
653 #endif // _AUDIO_ARM_H_