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.
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
{
33 uint8_t bits
[(NUM_BITS
+7)/8];
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
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
80 #define AUDIO_BUFFER_COUNT (3)
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)
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
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
119 audio_data_t data
[AUDIO_BUFFER_SIZE
];
121 #if defined(AUDIO_DUAL_BUFFER)
126 extern AudioBuffer audioBuffers
[AUDIO_BUFFER_COUNT
];
141 Tone(uint16_t freq
, uint16_t duration
, uint16_t pause
, int8_t freqIncr
, bool reset
):
150 struct AudioFragment
{
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):
165 tone(freq
, duration
, pause
, freqIncr
, reset
)
168 AudioFragment(const char * filename
, uint8_t repeat
, uint8_t id
=0):
173 strcpy(file
, filename
);
176 void clear() { memset(this, 0, sizeof(AudioFragment
)); };
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
);
192 AudioFragment fragment
;
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
) {
226 AudioFragment fragment
;
233 uint8_t resampleRatio
;
240 friend void printAudioVars();
249 void setFragment(const AudioFragment
* frag
)
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
);
275 AudioFragment fragment
; // a hack: fragment is used to access the fragment members of tone and wav
282 class AudioBufferFifo
{
284 friend void printAudioVars();
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);
305 return (readIdx
== writeIdx
) && !bufferFull
;
309 return bufferFull
? AUDIO_BUFFER_COUNT
: writeIdx
- readIdx
;
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
;
325 return full() ? NULL
: &audioBuffers
[writeIdx
];
329 // puts filled buffer into FIFO
330 void audioPushBuffer()
333 #if defined(AUDIO_DUAL_BUFFER)
334 AudioBuffer
* buffer
= &audioBuffers
[writeIdx
];
335 buffer
->state
= AUDIO_BUFFER_FILLED
;
337 writeIdx
= nextBufferIdx(writeIdx
);
338 bufferFull
= (writeIdx
== readIdx
);
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
;
348 AudioBuffer
* buffer
= &audioBuffers
[idx
];
349 if (buffer
->state
== AUDIO_BUFFER_FILLED
) {
350 buffer
->state
= AUDIO_BUFFER_PLAYING
;
354 idx
= nextBufferIdx(idx
);
355 } while (idx
!= writeIdx
); // this fixes a bug if all buffers are filled
358 return empty() ? NULL
: &audioBuffers
[readIdx
];
362 // frees the last played buffer
363 void freeNextFilledBuffer()
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
);
373 readIdx
= nextBufferIdx(readIdx
);
379 bool filledAtleast(int noBuffers
) const
381 #if defined(AUDIO_DUAL_BUFFER)
383 for(int n
= 0; n
<AUDIO_BUFFER_COUNT
; ++n
) {
384 if (audioBuffers
[n
].state
== AUDIO_BUFFER_FILLED
) {
385 if (++count
>= noBuffers
) {
392 return used() >= noBuffers
;
398 class AudioFragmentFifo
401 friend void printAudioVars();
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);
414 AudioFragmentFifo() : ridx(0), widx(0), fragments() {};
416 bool hasPromptId(uint8_t id
)
420 AudioFragment
& fragment
= fragments
[i
];
421 if (fragment
.id
== id
) return true;
427 bool removePromptById(uint8_t id
)
431 AudioFragment
& fragment
= fragments
[i
];
432 if (fragment
.id
== id
) fragment
.clear();
445 return ridx
== nextIdx(widx
);
450 widx
= ridx
; // clean the queue
453 const AudioFragment
* get()
456 const AudioFragment
* result
= &fragments
[ridx
];
457 if (!fragments
[ridx
].repeat
--) {
458 // repeat is done, move to the next fragment
459 ridx
= nextIdx(ridx
);
466 void push(const AudioFragment
& fragment
)
469 // TRACE("fragment %d at %d", fragment.type, widx);
470 fragments
[widx
] = fragment
;
471 widx
= nextIdx(widx
);
479 #if defined(SIMU_AUDIO)
480 friend void fillAudioBuffer(void *, uint8_t *, int);
483 friend void printAudioVars();
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
);
493 void pause(uint16_t tLen
);
495 bool isPlaying(uint8_t id
);
496 bool isEmpty() const { return fragmentsFifo
.empty(); };
498 bool started() const { return _started
; };
500 AudioBufferFifo buffersFifo
;
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
;
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,
522 void audioEvent(unsigned int index
);
523 void audioPlay(unsigned int index
, uint8_t id
=0);
525 void audioTask(void * pdata
);
527 #if defined(AUDIO) && defined(BUZZER)
528 #define AUDIO_BUZZER(a, b) do { a; b; } while(0)
530 #define AUDIO_BUZZER(a, b) a
532 #define AUDIO_BUZZER(a, b) b
536 #define AUDIO_ERROR_MESSAGE(e) audioEvent(e)
537 #define AUDIO_TIMER_MINUTE(t) playDuration(t, 0, 0)
539 #define AUDIO_ERROR_MESSAGE(e) audioEvent(AU_ERROR)
540 #define AUDIO_TIMER_MINUTE(t) audioDefevent(AU_WARNING1)
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)
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
{
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
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()
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)
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()
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_