Updated Wizard for X9 radios. (#4910)
[opentx.git] / radio / src / audio_arm.h
blob8cdd88ce2a8dd525362d7ec7bb1b5ce952acf819
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() { memset(this, 0, sizeof(AudioFragment)); };
179 class ToneContext {
180 public:
182 inline void clear() { memset(this, 0, sizeof(ToneContext)); };
183 bool isFree() const { return fragment.type == FRAGMENT_EMPTY; };
184 int mixBuffer(AudioBuffer *buffer, int volume, unsigned int fade);
186 void setFragment(uint16_t freq, uint16_t duration, uint16_t pause, uint8_t repeat, int8_t freqIncr, bool reset, uint8_t id=0)
188 fragment = AudioFragment(freq, duration, pause, repeat, freqIncr, reset, id);
191 private:
192 AudioFragment fragment;
194 struct {
195 float step;
196 float idx;
197 float volume;
198 uint16_t freq;
199 uint16_t duration;
200 uint16_t pause;
201 } state;
205 class WavContext {
206 public:
208 inline void clear() { fragment.clear(); };
210 int mixBuffer(AudioBuffer *buffer, int volume, unsigned int fade);
211 bool hasPromptId(uint8_t id) const { return fragment.id == id; };
213 void setFragment(const char * filename, uint8_t repeat, uint8_t id)
215 fragment = AudioFragment(filename, repeat, id);
218 void stop(uint8_t id)
220 if (fragment.id == id) {
221 fragment.clear();
225 private:
226 AudioFragment fragment;
228 struct {
229 FIL file;
230 uint8_t codec;
231 uint32_t freq;
232 uint32_t size;
233 uint8_t resampleRatio;
234 uint16_t readSize;
235 } state;
238 class MixedContext {
239 #if defined(CLI)
240 friend void printAudioVars();
241 #endif
242 public:
244 MixedContext()
246 clear();
249 void setFragment(const AudioFragment * frag)
251 if (frag) {
252 fragment = *frag;
256 inline void clear()
258 tone.clear(); // the biggest member of the uninon
261 bool isEmpty() const { return fragment.type == FRAGMENT_EMPTY; };
262 bool isTone() const { return fragment.type == FRAGMENT_TONE; };
263 bool isFile() const { return fragment.type == FRAGMENT_FILE; };
264 bool hasPromptId(uint8_t id) const { return fragment.id == id; };
266 int mixBuffer(AudioBuffer *buffer, int toneVolume, int wavVolume, unsigned int fade)
268 if (isTone()) return tone.mixBuffer(buffer, toneVolume, fade);
269 else if (isFile()) return wav.mixBuffer(buffer, wavVolume, fade);
270 return 0;
273 private:
274 union {
275 AudioFragment fragment; // a hack: fragment is used to access the fragment members of tone and wav
276 ToneContext tone;
277 WavContext wav;
282 class AudioBufferFifo {
283 #if defined(CLI)
284 friend void printAudioVars();
285 #endif
287 private:
288 volatile uint8_t readIdx;
289 volatile uint8_t writeIdx;
290 volatile bool bufferFull;
292 // readIdx == writeIdx -> buffer empty
293 // readIdx == writeIdx + 1 -> buffer full
295 inline uint8_t nextBufferIdx(uint8_t idx) const
297 return (idx >= AUDIO_BUFFER_COUNT-1 ? 0 : idx+1);
299 bool full() const
301 return bufferFull;
303 bool empty() const
305 return (readIdx == writeIdx) && !bufferFull;
307 uint8_t used() const
309 return bufferFull ? AUDIO_BUFFER_COUNT : writeIdx - readIdx;
312 public:
313 AudioBufferFifo() : readIdx(0), writeIdx(0), bufferFull(false)
315 memset(audioBuffers, 0, sizeof(audioBuffers));
318 // returns an empty buffer to be filled wit data and put back into FIFO with audioPushBuffer()
319 AudioBuffer * getEmptyBuffer() const
321 #if defined(AUDIO_DUAL_BUFFER)
322 AudioBuffer * buffer = &audioBuffers[writeIdx];
323 return buffer->state == AUDIO_BUFFER_FREE ? buffer : NULL;
324 #else
325 return full() ? NULL : &audioBuffers[writeIdx];
326 #endif
329 // puts filled buffer into FIFO
330 void audioPushBuffer()
332 audioDisableIrq();
333 #if defined(AUDIO_DUAL_BUFFER)
334 AudioBuffer * buffer = &audioBuffers[writeIdx];
335 buffer->state = AUDIO_BUFFER_FILLED;
336 #endif
337 writeIdx = nextBufferIdx(writeIdx);
338 bufferFull = (writeIdx == readIdx);
339 audioEnableIrq();
342 // returns a pointer to the audio buffer to be played
343 const AudioBuffer * getNextFilledBuffer()
345 #if defined(AUDIO_DUAL_BUFFER)
346 uint8_t idx = readIdx;
347 do {
348 AudioBuffer * buffer = &audioBuffers[idx];
349 if (buffer->state == AUDIO_BUFFER_FILLED) {
350 buffer->state = AUDIO_BUFFER_PLAYING;
351 readIdx = idx;
352 return buffer;
354 idx = nextBufferIdx(idx);
355 } while (idx != writeIdx); // this fixes a bug if all buffers are filled
356 return NULL;
357 #else
358 return empty() ? NULL : &audioBuffers[readIdx];
359 #endif
362 // frees the last played buffer
363 void freeNextFilledBuffer()
365 audioDisableIrq();
366 #if defined(AUDIO_DUAL_BUFFER)
367 if (audioBuffers[readIdx].state == AUDIO_BUFFER_PLAYING) {
368 audioBuffers[readIdx].state = AUDIO_BUFFER_FREE;
369 readIdx = nextBufferIdx(readIdx);
370 bufferFull = false;
372 #else
373 readIdx = nextBufferIdx(readIdx);
374 bufferFull = false;
375 #endif
376 audioEnableIrq();
379 bool filledAtleast(int noBuffers) const
381 #if defined(AUDIO_DUAL_BUFFER)
382 int count = 0;
383 for(int n= 0; n<AUDIO_BUFFER_COUNT; ++n) {
384 if (audioBuffers[n].state == AUDIO_BUFFER_FILLED) {
385 if (++count >= noBuffers) {
386 return true;
390 return false;
391 #else
392 return used() >= noBuffers;
393 #endif
398 class AudioFragmentFifo
400 #if defined(CLI)
401 friend void printAudioVars();
402 #endif
403 private:
404 volatile uint8_t ridx;
405 volatile uint8_t widx;
406 AudioFragment fragments[AUDIO_QUEUE_LENGTH];
408 uint8_t nextIdx(uint8_t idx) const
410 return (idx + 1) & (AUDIO_QUEUE_LENGTH - 1);
413 public:
414 AudioFragmentFifo() : ridx(0), widx(0), fragments() {};
416 bool hasPromptId(uint8_t id)
418 uint8_t i = ridx;
419 while (i != widx) {
420 AudioFragment & fragment = fragments[i];
421 if (fragment.id == id) return true;
422 i = nextIdx(i);
424 return false;
427 bool removePromptById(uint8_t id)
429 uint8_t i = ridx;
430 while (i != widx) {
431 AudioFragment & fragment = fragments[i];
432 if (fragment.id == id) fragment.clear();
433 i = nextIdx(i);
435 return false;
438 bool empty() const
440 return ridx == widx;
443 bool full() const
445 return ridx == nextIdx(widx);
448 void clear()
450 widx = ridx; // clean the queue
453 const AudioFragment * get()
455 if (!empty()) {
456 const AudioFragment * result = &fragments[ridx];
457 if (!fragments[ridx].repeat--) {
458 // repeat is done, move to the next fragment
459 ridx = nextIdx(ridx);
461 return result;
463 return 0;
466 void push(const AudioFragment & fragment)
468 if (!full()) {
469 // TRACE("fragment %d at %d", fragment.type, widx);
470 fragments[widx] = fragment;
471 widx = nextIdx(widx);
477 class AudioQueue {
479 #if defined(SIMU_AUDIO)
480 friend void fillAudioBuffer(void *, uint8_t *, int);
481 #endif
482 #if defined(CLI)
483 friend void printAudioVars();
484 #endif
485 public:
486 AudioQueue();
487 void start() { _started = true; };
488 void playTone(uint16_t freq, uint16_t len, uint16_t pause=0, uint8_t flags=0, int8_t freqIncr=0);
489 void playFile(const char *filename, uint8_t flags=0, uint8_t id=0);
490 void stopPlay(uint8_t id);
491 void stopAll();
492 void flush();
493 void pause(uint16_t tLen);
494 void stopSD();
495 bool isPlaying(uint8_t id);
496 bool isEmpty() const { return fragmentsFifo.empty(); };
497 void wakeup();
498 bool started() const { return _started; };
500 AudioBufferFifo buffersFifo;
502 private:
503 volatile bool _started;
504 MixedContext normalContext;
505 WavContext backgroundContext;
506 ToneContext priorityContext;
507 ToneContext varioContext;
508 AudioFragmentFifo fragmentsFifo;
511 extern uint8_t currentSpeakerVolume;
512 extern AudioQueue audioQueue;
514 enum {
515 // IDs for special functions [0:64]
516 // IDs for global functions [64:128]
517 ID_PLAY_PROMPT_BASE = 128,
518 ID_PLAY_FROM_SD_MANAGER = 255,
521 void codecsInit();
522 void audioEvent(unsigned int index);
523 void audioPlay(unsigned int index, uint8_t id=0);
524 void audioStart();
525 void audioTask(void * pdata);
527 #if defined(AUDIO) && defined(BUZZER)
528 #define AUDIO_BUZZER(a, b) do { a; b; } while(0)
529 #elif defined(AUDIO)
530 #define AUDIO_BUZZER(a, b) a
531 #else
532 #define AUDIO_BUZZER(a, b) b
533 #endif
535 #if defined(VOICE)
536 #define AUDIO_ERROR_MESSAGE(e) audioEvent(e)
537 #define AUDIO_TIMER_MINUTE(t) playDuration(t, 0, 0)
538 #else
539 #define AUDIO_ERROR_MESSAGE(e) audioEvent(AU_ERROR)
540 #define AUDIO_TIMER_MINUTE(t) audioDefevent(AU_WARNING1)
541 #endif
543 void audioKeyPress();
544 void audioKeyError();
545 void audioTrimPress(int value);
546 void audioTimerCountdown(uint8_t timer, int value);
548 #define AUDIO_KEY_PRESS() audioKeyPress()
549 #define AUDIO_KEY_ERROR() audioKeyError()
551 #define AUDIO_HELLO() audioPlay(AUDIO_HELLO)
552 #define AUDIO_BYE() audioPlay(AU_BYE, ID_PLAY_PROMPT_BASE + AU_BYE)
553 #define AUDIO_WARNING1() AUDIO_BUZZER(audioEvent(AU_WARNING1), beep(3))
554 #define AUDIO_WARNING2() AUDIO_BUZZER(audioEvent(AU_WARNING2), beep(2))
555 #define AUDIO_TX_BATTERY_LOW() AUDIO_BUZZER(audioEvent(AU_TX_BATTERY_LOW), beep(4))
556 #if defined(PCBSKY9X)
557 #define AUDIO_TX_MAH_HIGH() audioEvent(AU_TX_MAH_HIGH)
558 #define AUDIO_TX_TEMP_HIGH() audioEvent(AU_TX_TEMP_HIGH)
559 #endif
560 #define AUDIO_ERROR() AUDIO_BUZZER(audioEvent(AU_ERROR), beep(4))
561 #define AUDIO_TIMER_COUNTDOWN(idx, val) audioTimerCountdown(idx, val)
562 #define AUDIO_TIMER_ELAPSED(idx) AUDIO_BUZZER(audioEvent(AU_TIMER1_ELAPSED+idx), beep(3))
563 #define AUDIO_INACTIVITY() AUDIO_BUZZER(audioEvent(AU_INACTIVITY), beep(3))
564 #define AUDIO_MIX_WARNING(x) AUDIO_BUZZER(audioEvent(AU_MIX_WARNING_1+x-1), beep(1))
565 #define AUDIO_POT_MIDDLE(x) AUDIO_BUZZER(audioEvent(AU_STICK1_MIDDLE+x), beep(2))
566 #define AUDIO_TRIM_MIDDLE() AUDIO_BUZZER(audioEvent(AU_TRIM_MIDDLE), beep(2))
567 #define AUDIO_TRIM_MIN() AUDIO_BUZZER(audioEvent(AU_TRIM_MIN), beep(2))
568 #define AUDIO_TRIM_MAX() AUDIO_BUZZER(audioEvent(AU_TRIM_MAX), beep(2))
569 #define AUDIO_TRIM_PRESS(val) audioTrimPress(val)
570 #define AUDIO_PLAY(p) audioEvent(p)
571 #define AUDIO_VARIO(fq, t, p, f) audioQueue.playTone(fq, t, p, f)
572 #define AUDIO_RSSI_ORANGE() audioEvent(AU_RSSI_ORANGE)
573 #define AUDIO_RSSI_RED() audioEvent(AU_RSSI_RED)
574 #define AUDIO_SWR_RED() audioEvent(AU_SWR_RED)
575 #define AUDIO_TELEMETRY_LOST() audioEvent(AU_TELEMETRY_LOST)
576 #define AUDIO_TELEMETRY_BACK() audioEvent(AU_TELEMETRY_BACK)
577 #define AUDIO_TRAINER_LOST() audioEvent(AU_TRAINER_LOST)
578 #define AUDIO_TRAINER_BACK() audioEvent(AU_TRAINER_BACK)
580 #define AUDIO_HEARTBEAT()
582 enum AutomaticPromptsCategories {
583 SYSTEM_AUDIO_CATEGORY,
584 MODEL_AUDIO_CATEGORY,
585 PHASE_AUDIO_CATEGORY,
586 SWITCH_AUDIO_CATEGORY,
587 LOGICAL_SWITCH_AUDIO_CATEGORY,
590 enum AutomaticPromptsEvents {
591 AUDIO_EVENT_OFF,
592 AUDIO_EVENT_ON,
593 AUDIO_EVENT_MID,
596 void pushPrompt(uint16_t prompt, uint8_t id=0);
597 void pushUnit(uint8_t unit, uint8_t idx, uint8_t id);
598 void playModelName();
600 #define I18N_PLAY_FUNCTION(lng, x, ...) void lng ## _ ## x(__VA_ARGS__, uint8_t id)
601 #define PLAY_FUNCTION(x, ...) void x(__VA_ARGS__, uint8_t id)
602 #define PUSH_NUMBER_PROMPT(p) pushPrompt((p), id)
603 #define PUSH_UNIT_PROMPT(p, i) pushUnit((p), (i), id)
604 #define PLAY_NUMBER(n, u, a) playNumber((n), (u), (a), id)
605 #define PLAY_DURATION(d, att) playDuration((d), (att), id)
606 #define PLAY_DURATION_ATT , uint8_t flags
607 #define PLAY_TIME 1
608 #define IS_PLAY_TIME() (flags&PLAY_TIME)
609 #define IS_PLAYING(id) audioQueue.isPlaying((id))
610 #define PLAY_VALUE(v, id) playValue((v), (id))
611 #define PLAY_FILE(f, flags, id) audioQueue.playFile((f), (flags), (id))
612 #define STOP_PLAY(id) audioQueue.stopPlay((id))
613 #define AUDIO_RESET() audioQueue.stopAll()
614 #define AUDIO_FLUSH() audioQueue.flush()
616 #if defined(SDCARD)
617 extern tmr10ms_t timeAutomaticPromptsSilence;
618 void playModelEvent(uint8_t category, uint8_t index, event_t event=0);
619 #define PLAY_PHASE_OFF(phase) playModelEvent(PHASE_AUDIO_CATEGORY, phase, AUDIO_EVENT_OFF)
620 #define PLAY_PHASE_ON(phase) playModelEvent(PHASE_AUDIO_CATEGORY, phase, AUDIO_EVENT_ON)
621 #define PLAY_SWITCH_MOVED(sw) playModelEvent(SWITCH_AUDIO_CATEGORY, sw)
622 #define PLAY_LOGICAL_SWITCH_OFF(sw) playModelEvent(LOGICAL_SWITCH_AUDIO_CATEGORY, sw, AUDIO_EVENT_OFF)
623 #define PLAY_LOGICAL_SWITCH_ON(sw) playModelEvent(LOGICAL_SWITCH_AUDIO_CATEGORY, sw, AUDIO_EVENT_ON)
624 #define PLAY_MODEL_NAME() playModelName()
625 #define START_SILENCE_PERIOD() timeAutomaticPromptsSilence = get_tmr10ms()
626 #define IS_SILENCE_PERIOD_ELAPSED() (get_tmr10ms()-timeAutomaticPromptsSilence > 50)
627 #else
628 #define PLAY_PHASE_OFF(phase)
629 #define PLAY_PHASE_ON(phase)
630 #define PLAY_SWITCH_MOVED(sw)
631 #define PLAY_LOGICAL_SWITCH_OFF(sw)
632 #define PLAY_LOGICAL_SWITCH_ON(sw)
633 #define PLAY_MODEL_NAME()
634 #define START_SILENCE_PERIOD()
635 #endif
637 char * getAudioPath(char * path);
639 void referenceSystemAudioFiles();
640 void referenceModelAudioFiles();
642 bool isAudioFileReferenced(uint32_t i, char * filename/*at least AUDIO_FILENAME_MAXLEN+1 long*/);
644 #endif // _AUDIO_ARM_H_