diff --git a/src/Makefile b/src/Makefile index a5caa9945..76c5967ad 100644 --- a/src/Makefile +++ b/src/Makefile @@ -292,19 +292,18 @@ ifeq ($(PCB), ARM) # ersky9x/ff.c ersky9x/diskio_sam3s.c ersky9x/Media.c ersky9x/ccsbcs.c ersky9x/sdcard.c ersky9x/MEDSdcard.c EEPROMSRC = eeprom_arm.cpp PULSESSRC = pulses_arm.cpp - CPPSRC += audio.cpp - CPPSRC += ersky9x/sound.cpp + CPPSRC += ersky9x/audio.cpp ersky9x/sound.cpp haptic.cpp endif ifeq ($(PCB), V4) # V4 PCB, so ... OPT = 2 CPPDEFS += -DPCBV4 -DAUDIO -DHAPTIC -DROTARY_ENCODERS - EXTRAINCDIRS += gruvin9x + EXTRAINCDIRS += gruvin9x stock BOARDSRC += board_gruvin9x.cpp EEPROMSRC = eeprom_avr.cpp PULSESSRC = pulses_avr.cpp - CPPSRC += audio.cpp + CPPSRC += stock/audio.cpp haptic.cpp CPPSRC += gruvin9x/gtime.cpp CPPSRC += gruvin9x/rtc.cpp CPPSRC += gruvin9x/ff.cpp @@ -336,6 +335,7 @@ endif ifeq ($(PCB), STD) # STD PCB, so ... OPT = s + EXTRAINCDIRS += stock CPPDEFS += -DPCBSTD BOARDSRC = board_stock.cpp EEPROMSRC = eeprom_avr.cpp @@ -343,13 +343,14 @@ ifeq ($(PCB), STD) ifeq ($(AUDIO), YES) CPPDEFS += -DAUDIO - CPPSRC += audio.cpp + CPPSRC += stock/audio.cpp else CPPSRC += beeper.cpp endif ifeq ($(HAPTIC), YES) CPPDEFS += -DHAPTIC + CPPSRC += haptic.cpp endif endif diff --git a/src/audio.cpp b/src/audio.cpp deleted file mode 100644 index 9b6e8c790..000000000 --- a/src/audio.cpp +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Authors (alphabetical order) - * - Bertrand Songis - * - Bryan J. Rentoul (Gruvin) - * - Cameron Weeks - * - Erez Raviv - * - Jean-Pierre Parisy - * - Karl Szmutny - * - Michael Blandford - * - Michal Hlavinka - * - Pat Mackenzie - * - Philip Moss - * - Rob Thomson - * - Romolo Manfredini - * - Thomas Husterer - * - * open9x is based on code named - * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, - * er9x by Erez Raviv: http://code.google.com/p/er9x/, - * and the original (and ongoing) project by - * Thomas Husterer, th9x: http://code.google.com/p/th9x/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include "open9x.h" - -audioQueue::audioQueue() -{ - toneTimeLeft = 0; - tonePause = 0; - - t_queueRidx = 0; - t_queueWidx = 0; - -#ifdef HAPTIC - hapticTick = 0; - hapticSpinUpTime = 0; -#endif -} - -#if 0 -bool audioQueue::freeslots(uint8_t slots) -{ - return AUDIO_QUEUE_LENGTH - ((t_queueWidx + AUDIO_QUEUE_LENGTH - t_queueRidx) % AUDIO_QUEUE_LENGTH) >= slots; -} -#endif - -// heartbeat is responsibile for issueing the audio tones and general square waves -// it is essentially the life of the class. -// it is called every 10ms -void audioQueue::heartbeat() -{ -#if defined(SIMU) - return; -#endif - -#if defined(PCBARM) - if (toneTimeLeft) { - - if (queueTone(toneFreq * 61 / 2, toneTimeLeft * 10, - toneFreqIncr * 61 / 2)) { - toneTimeLeft = 0; //time gets counted down - } - - -#if defined(HAPTIC) - - if (toneHaptic) { - hapticOn((g_eeGeneral.hapticStrength * 2) * 10); - hapticSpinUpTime = HAPTIC_SPINUP; //min time haptic runs for. Might be worth a config option in system prefs to allow user to set to suit motor? - } - -#endif - } - else { - -#if defined(HAPTIC) - if(hapticSpinUpTime > 0){ - hapticSpinUpTime--; - } else { - hapticOff(); - } -#endif - - if (tonePause) { - if (queueTone(0, tonePause * 10, 0)) { - tonePause = 0; //time gets counted down - } - } - else { - if (t_queueRidx != t_queueWidx) { - toneFreq = queueToneFreq[t_queueRidx]; - toneTimeLeft = queueToneLength[t_queueRidx]; - toneFreqIncr = queueToneFreqIncr[t_queueRidx]; - tonePause = queueTonePause[t_queueRidx]; - toneHaptic = queueToneHaptic[t_queueRidx]; - hapticTick = 0; - if (!queueToneRepeat[t_queueRidx]--) { - t_queueRidx = (t_queueRidx + 1) % AUDIO_QUEUE_LENGTH; - } - } - } - } -#else - if (toneTimeLeft > 0) { -#if defined(PCBV4) - if (toneFreq) { - OCR0A = (5000 / toneFreq); // sticking with old values approx 20(abs. min) to 90, 60 being the default tone(?). - SPEAKER_ON; - } -#endif - toneTimeLeft--; //time gets counted down - toneFreq += toneFreqIncr; - -#if defined(HAPTIC) - if (toneHaptic){ - if (hapticTick-- > 0) { - HAPTIC_ON; // haptic output 'high' - } - else { - HAPTIC_OFF; // haptic output 'low' - hapticTick = g_eeGeneral.hapticStrength; - } - } -#endif - } - else { - SPEAKER_OFF; - HAPTIC_OFF; - - if (tonePause > 0) { - tonePause--; - } - else if (t_queueRidx != t_queueWidx) { - toneFreq = queueToneFreq[t_queueRidx]; - toneTimeLeft = queueToneLength[t_queueRidx]; - toneFreqIncr = queueToneFreqIncr[t_queueRidx]; - tonePause = queueTonePause[t_queueRidx]; -#if defined(HAPTIC) - toneHaptic = queueToneHaptic[t_queueRidx]; -#endif - if (!queueToneRepeat[t_queueRidx]--) { - t_queueRidx = (t_queueRidx + 1) % AUDIO_QUEUE_LENGTH; - } - } - } -#endif -} - -inline uint8_t audioQueue::getToneLength(uint8_t tLen) -{ - uint8_t result = tLen; // default - if (g_eeGeneral.beeperLength < 0) { - result /= (1-g_eeGeneral.beeperLength); - } - if (g_eeGeneral.beeperLength > 0) { - result *= (1+g_eeGeneral.beeperLength); - } - return result; -} - -bool s_beeper; -#if defined(HAPTIC) -bool s_haptic; -#endif - -void audioQueue::playNow(uint8_t tFreq, uint8_t tLen, uint8_t tPause, - uint8_t tRepeat, uint8_t tHaptic, int8_t tFreqIncr) -{ - toneFreq = ((s_beeper && tFreq) ? tFreq + g_eeGeneral.speakerPitch + BEEP_OFFSET : 0); // add pitch compensator - toneTimeLeft = getToneLength(tLen); - tonePause = tPause; -#if defined(HAPTIC) - toneHaptic = s_haptic ? tHaptic : 0; -#endif - toneFreqIncr = tFreqIncr; - t_queueWidx = t_queueRidx; - - if (tRepeat) { - playASAP(tFreq, tLen, tPause, tRepeat-1, tHaptic, tFreqIncr); - } -} - -void audioQueue::playASAP(uint8_t tFreq, uint8_t tLen, uint8_t tPause, - uint8_t tRepeat, uint8_t tHaptic, int8_t tFreqIncr) -{ - uint8_t next_queueWidx = (t_queueWidx + 1) % AUDIO_QUEUE_LENGTH; - if (next_queueWidx != t_queueRidx) { - queueToneFreq[t_queueWidx] = ((s_beeper && tFreq) ? tFreq + g_eeGeneral.speakerPitch + BEEP_OFFSET : 0); // add pitch compensator - queueToneLength[t_queueWidx] = getToneLength(tLen); - queueTonePause[t_queueWidx] = tPause; -#if defined(HAPTIC) - queueToneHaptic[t_queueWidx] = s_haptic ? tHaptic : 0; -#endif - queueToneRepeat[t_queueWidx] = tRepeat; - queueToneFreqIncr[t_queueWidx] = tFreqIncr; - t_queueWidx = next_queueWidx; - } -} - -void audioQueue::event(uint8_t e, uint8_t f) -{ - s_beeper = (g_eeGeneral.beeperMode>0 || (g_eeGeneral.beeperMode==0 && e>=AU_WARNING1) || (g_eeGeneral.beeperMode>=-1 && e<=AU_ERROR)); -#if defined(HAPTIC) - s_haptic = (g_eeGeneral.hapticMode>0 || (g_eeGeneral.hapticMode==0 && e>=AU_WARNING1) || (g_eeGeneral.hapticMode>=-1 && e<=AU_ERROR)); -#endif - if (g_eeGeneral.flashBeep && (e <= AU_ERROR || e >= AU_WARNING1)) g_LightOffCounter = FLASH_DURATION; // we got an event do we need to flash the display ? - if (e < AU_FRSKY_FIRST || empty()) { - switch (e) { - // inactivity timer alert - case AU_INACTIVITY: - playNow(70, 10, 2, 2); - break; - // low battery in tx - case AU_TX_BATTERY_LOW: - if (empty()) { - playASAP(60, 20, 3, 2, 0, 1); - playASAP(80, 20, 3, 2, 1, -1); - } - break; - // error - case AU_ERROR: - playNow(BEEP_DEFAULT_FREQ, 40, 1, 0, 1); - break; - // keypad up (seems to be used when going left/right through system menu options. 0-100 scales etc) - case AU_KEYPAD_UP: - playNow(BEEP_KEY_UP_FREQ, 10, 1, 0, 1); - break; - // keypad down (seems to be used when going left/right through system menu options. 0-100 scales etc) - case AU_KEYPAD_DOWN: - playNow(BEEP_KEY_DOWN_FREQ, 10, 1, 0, 1); - break; - // menu display (also used by a few generic beeps) - case AU_MENUS: - playNow(BEEP_DEFAULT_FREQ, 10, 2, 0, 1); - break; - // trim move - case AU_TRIM_MOVE: - playNow(f, 6, 1); - break; - // trim center - case AU_TRIM_MIDDLE: - playNow(BEEP_DEFAULT_FREQ, 10, 2, 0, 1); - break; - // warning one - case AU_WARNING1: - playNow(BEEP_DEFAULT_FREQ, 10, 1, 0, 1); - break; - // warning two - case AU_WARNING2: - playNow(BEEP_DEFAULT_FREQ, 20, 1, 0, 1); - break; - // warning three - case AU_WARNING3: - playNow(BEEP_DEFAULT_FREQ, 30, 1, 0, 1); - break; - // startup tune - case AU_TADA: - playASAP(50, 10, 5); - playASAP(90, 10, 5); - playASAP(110, 5, 4, 2); - break; - // pot/stick center - case AU_POT_STICK_MIDDLE: - playNow(BEEP_DEFAULT_FREQ + 50, 10, 1, 0, 0); - break; - // mix warning 1 - case AU_MIX_WARNING_1: - playNow(BEEP_DEFAULT_FREQ + 50, 6, 0, 0, 1); - break; - // mix warning 2 - case AU_MIX_WARNING_2: - playNow(BEEP_DEFAULT_FREQ + 52, 6, 0, 0, 1); - break; - // mix warning 3 - case AU_MIX_WARNING_3: - playNow(BEEP_DEFAULT_FREQ + 54, 6, 0, 0, 1); - break; - // time 30 seconds left - case AU_TIMER_30: - playNow(BEEP_DEFAULT_FREQ + 50, 15, 3, 2, 1); - break; - // time 20 seconds left - case AU_TIMER_20: - playNow(BEEP_DEFAULT_FREQ + 50, 15, 3, 1, 1); - break; - // time 10 seconds left - case AU_TIMER_10: - playNow(BEEP_DEFAULT_FREQ + 50, 15, 3, 0, 1); - break; - // time <3 seconds left - case AU_TIMER_LT3: - playNow(BEEP_DEFAULT_FREQ, 20, 25, 1, 1); - break; - case AU_FRSKY_WARN1: - playASAP(BEEP_DEFAULT_FREQ+20,15,5,2,1); - break; - case AU_FRSKY_WARN2: - playASAP(BEEP_DEFAULT_FREQ+30,15,5,2,1); - break; - case AU_FRSKY_CHEEP: - playASAP(BEEP_DEFAULT_FREQ+30,10,2,2,1,2); - break; - case AU_FRSKY_RING: - playASAP(BEEP_DEFAULT_FREQ+25,5,2,10,1); - playASAP(BEEP_DEFAULT_FREQ+25,5,10,1,1); - playASAP(BEEP_DEFAULT_FREQ+25,5,2,10,1); - break; - case AU_FRSKY_SCIFI: - playASAP(80,10,3,2,0,-1); - playASAP(60,10,3,2,0,1); - playASAP(70,10,1,0,2); - break; - case AU_FRSKY_ROBOT: - playASAP(70,5,1,1,1); - playASAP(50,15,2,1,1); - playASAP(80,15,2,1,1); - break; - case AU_FRSKY_CHIRP: - playASAP(BEEP_DEFAULT_FREQ+40,5,1,2,1); - playASAP(BEEP_DEFAULT_FREQ+54,5,1,3,1); - break; - case AU_FRSKY_TADA: - playASAP(50,5,5); - playASAP(90,5,5); - playASAP(110,3,4,2); - break; - case AU_FRSKY_CRICKET: - playASAP(80,5,10,3,1); - playASAP(80,5,20,1,1); - playASAP(80,5,10,3,1); - break; - case AU_FRSKY_SIREN: - playASAP(10,20,5,2,1,1); - break; - case AU_FRSKY_ALARMC: - playASAP(50,4,10,2,1); - playASAP(70,8,20,1,1); - playASAP(50,8,10,2,1); - playASAP(70,4,20,1,1); - break; - case AU_FRSKY_RATATA: - playASAP(BEEP_DEFAULT_FREQ+50,5,10,10,1); - break; - case AU_FRSKY_TICK: - playASAP(BEEP_DEFAULT_FREQ+50,5,50,2,1); - break; -#ifdef HAPTIC - case AU_FRSKY_HAPTIC1: - playASAP(0,30,10,0,1); - playASAP(0,10,50,0,1); - break; - case AU_FRSKY_HAPTIC2: - playASAP(0,30,10,0,1); - playASAP(0,10,10,0,1); - playASAP(0,10,30,0,1); - break; - case AU_FRSKY_HAPTIC3: - playASAP(0,30,10,0,1); - playASAP(0,10,10,2,1); - break; -#endif - default: - break; - } - } -} - -void audioDefevent(uint8_t e) -{ - audio.event(e, BEEP_DEFAULT_FREQ); -} diff --git a/src/beeper.cpp b/src/beeper.cpp index a817de706..1048c5440 100644 --- a/src/beeper.cpp +++ b/src/beeper.cpp @@ -39,9 +39,6 @@ uint8_t beepAgainOrig = 0; uint8_t beepOn = false; bool warble = false; bool warbleC; -#if defined(HAPTIC) -uint8_t hapticTick = 0; -#endif // The various "beep" tone lengths static const pm_uint8_t beepTab[] PROGMEM = { @@ -55,6 +52,10 @@ static const pm_uint8_t beepTab[] PROGMEM = { void beep(uint8_t val) { +#if defined(HAPTIC) + haptic.event(val==0 ? 0 : AU_TIMER_10+beepAgain); +#endif + if(g_eeGeneral.flashBeep && val > 1) g_LightOffCounter = FLASH_DURATION; if (g_eeGeneral.beeperMode>0 || (g_eeGeneral.beeperMode==0 && val!=0) || (g_eeGeneral.beeperMode==-1 && val>=3)) { diff --git a/src/beeper.h b/src/beeper.h index 4b0ce2142..cf18a8cba 100644 --- a/src/beeper.h +++ b/src/beeper.h @@ -111,19 +111,9 @@ FORCEINLINE void AUDIO_HEARTBEAT() BUZZER_OFF; else BUZZER_ON; -#if defined(HAPTIC) - if (hapticTick-- > 0) { - HAPTIC_ON; // haptic output 'high' - } - else { - HAPTIC_OFF; // haptic output 'low' - hapticTick = g_eeGeneral.hapticStrength; - } -#endif } else { BUZZER_OFF; - HAPTIC_OFF; } } #endif diff --git a/src/board_ersky9x.h b/src/board_ersky9x.h index a455998ed..c2bb0003d 100644 --- a/src/board_ersky9x.h +++ b/src/board_ersky9x.h @@ -40,6 +40,8 @@ #include "ersky9x/AT91SAM3S4.h" #endif +#include "ersky9x/sound.h" + #define PIN_ENABLE 0x001 #define PIN_PERIPHERAL 0x000 #define PIN_INPUT 0x002 diff --git a/src/ersky9x/audio.cpp b/src/ersky9x/audio.cpp new file mode 100644 index 000000000..4767b90ce --- /dev/null +++ b/src/ersky9x/audio.cpp @@ -0,0 +1,320 @@ +/* + * Authors (alphabetical order) + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * open9x is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "open9x.h" + +audioQueue::audioQueue() +{ + toneTimeLeft = 0; + tonePause = 0; + + t_queueRidx = 0; + t_queueWidx = 0; +} + + +#define QUEUE_TONE(tf,ttl,tfi) queueTone(tf * 61 / 2, ttl * 10, tfi * 61 / 2) + + +// heartbeat is responsibile for issueing the audio tones and general square waves +// it is essentially the life of the class. +// it is called every 10ms +void audioQueue::heartbeat() +{ +#if defined(SIMU) + return; +#endif + + if (toneTimeLeft == 0) { + if (tonePause == 0) { + if (t_queueRidx != t_queueWidx) { + toneFreq = queueToneFreq[t_queueRidx]; + toneTimeLeft = queueToneLength[t_queueRidx]; + toneFreqIncr = queueToneFreqIncr[t_queueRidx]; + tonePause = queueTonePause[t_queueRidx]; + if ((toneFreq == 0) || (toneTimeLeft == 0)) { + //SPEAKER_OFF; + } + if (!queueToneRepeat[t_queueRidx]--) { + t_queueRidx = (t_queueRidx + 1) % AUDIO_QUEUE_LENGTH; + } + } + else { + if ((tone2Freq == 0) & (tone2TimeLeft == 0)) { + //SPEAKER_OFF; + } + } + } + } + + if ((toneFreq > 0) & (toneTimeLeft > 0)) { + QUEUE_TONE(toneFreq, toneTimeLeft, toneFreqIncr); + } + else if (((tone2Freq > 0) & (tone2TimeLeft > 0)) & (tonePause == 0)) { + //second flow tone here, priority on 1st, pause of 1st not allow to start second + QUEUE_TONE(tone2Freq, tone2TimeLeft, 0); + } + + if (toneTimeLeft > 0) { + toneTimeLeft--; //time gets counted down + toneFreq += toneFreqIncr; + } + else { + if (tonePause > 0) { + // SPEAKER_OFF; + tonePause--; //time gets counted down + } + } + + if (tone2TimeLeft > 0) { + tone2TimeLeft--; //time gets counted down + } + else { + if (toneTimeLeft == 0) { + //SPEAKER_OFF; + } + if (tone2Pause > 0) { + tone2Pause--; //time gets counted down + } + } +} + +inline uint8_t audioQueue::getToneLength(uint8_t tLen) +{ + uint8_t result = tLen; // default + if (g_eeGeneral.beeperLength < 0) { + result /= (1-g_eeGeneral.beeperLength); + } + if (g_eeGeneral.beeperLength > 0) { + result *= (1+g_eeGeneral.beeperLength); + } + return result; +} + +void audioQueue::play(uint8_t tFreq, uint8_t tLen, uint8_t tPause, + uint8_t tFlags, int8_t tFreqIncr) +{ + if (tFlags & PLAY_SOUND_VARIO) { + tone2Freq = tFreq; + tone2TimeLeft = tLen; + tone2Pause = tPause; + } + else { + tFreq += g_eeGeneral.speakerPitch + BEEP_OFFSET; // add pitch compensator + tLen = getToneLength(tLen); + if (tFlags & PLAY_NOW || (!busy() && empty())) { + toneFreq = tFreq; + toneTimeLeft = tLen; + tonePause = tPause; + toneFreqIncr = tFreqIncr; + t_queueWidx = t_queueRidx; + } + else { + tFlags++; + } + + tFlags &= 0x0f; + if (tFlags) { + uint8_t next_queueWidx = (t_queueWidx + 1) % AUDIO_QUEUE_LENGTH; + if (next_queueWidx != t_queueRidx) { + queueToneFreq[t_queueWidx] = tFreq; + queueToneLength[t_queueWidx] = tLen; + queueTonePause[t_queueWidx] = tPause; + queueToneRepeat[t_queueWidx] = tFlags - 1; + queueToneFreqIncr[t_queueWidx] = tFreqIncr; + t_queueWidx = next_queueWidx; + } + } + } +} + +void audioQueue::event(uint8_t e, uint8_t f) +{ + if (g_eeGeneral.flashBeep && (e <= AU_ERROR || e >= AU_WARNING1)) + g_LightOffCounter = FLASH_DURATION; + + if (g_eeGeneral.beeperMode>0 || (g_eeGeneral.beeperMode==0 && e>=AU_WARNING1) || (g_eeGeneral.beeperMode>=-1 && e<=AU_ERROR)) { + if (e < AU_FRSKY_FIRST || empty()) { + switch (e) { + // inactivity timer alert + case AU_INACTIVITY: + play(70, 10, 2, 2|PLAY_NOW); + break; + // low battery in tx + case AU_TX_BATTERY_LOW: + if (empty()) { + play(60, 20, 3, 2, 1); + play(80, 20, 3, 2, -1); + } + break; + // error + case AU_ERROR: + play(BEEP_DEFAULT_FREQ, 40, 1, 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: + play(BEEP_KEY_UP_FREQ, 10, 1, 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: + play(BEEP_KEY_DOWN_FREQ, 10, 1, PLAY_NOW); + break; + // menu display (also used by a few generic beeps) + case AU_MENUS: + play(BEEP_DEFAULT_FREQ, 10, 2, PLAY_NOW); + break; + // trim move + case AU_TRIM_MOVE: + play(f, 6, 1, PLAY_NOW); + break; + // trim center + case AU_TRIM_MIDDLE: + play(BEEP_DEFAULT_FREQ, 10, 2, PLAY_NOW); + break; + // warning one + case AU_WARNING1: + play(BEEP_DEFAULT_FREQ, 10, 1, PLAY_NOW); + break; + // warning two + case AU_WARNING2: + play(BEEP_DEFAULT_FREQ, 20, 1, PLAY_NOW); + break; + // warning three + case AU_WARNING3: + play(BEEP_DEFAULT_FREQ, 30, 1, PLAY_NOW); + break; + // startup tune + case AU_TADA: + play(50, 10, 5); + play(90, 10, 5); + play(110, 5, 4, 2); + break; + // pot/stick center + case AU_POT_STICK_MIDDLE: + play(BEEP_DEFAULT_FREQ + 50, 10, 1, PLAY_NOW); + break; + // mix warning 1 + case AU_MIX_WARNING_1: + play(BEEP_DEFAULT_FREQ + 50, 6, 0, PLAY_NOW); + break; + // mix warning 2 + case AU_MIX_WARNING_2: + play(BEEP_DEFAULT_FREQ + 52, 6, 0, PLAY_NOW); + break; + // mix warning 3 + case AU_MIX_WARNING_3: + play(BEEP_DEFAULT_FREQ + 54, 6, 0, PLAY_NOW); + break; + // time 30 seconds left + case AU_TIMER_30: + play(BEEP_DEFAULT_FREQ + 50, 15, 3, 2|PLAY_NOW); + break; + // time 20 seconds left + case AU_TIMER_20: + play(BEEP_DEFAULT_FREQ + 50, 15, 3, 1|PLAY_NOW); + break; + // time 10 seconds left + case AU_TIMER_10: + play(BEEP_DEFAULT_FREQ + 50, 15, 3, PLAY_NOW); + break; + // time <3 seconds left + case AU_TIMER_LT3: + play(BEEP_DEFAULT_FREQ + 50, 15, 3, PLAY_NOW); + break; + case AU_FRSKY_WARN1: + play(BEEP_DEFAULT_FREQ+20,15,5,2); + break; + case AU_FRSKY_WARN2: + play(BEEP_DEFAULT_FREQ+30,15,5,2); + break; + case AU_FRSKY_CHEEP: + play(BEEP_DEFAULT_FREQ+30,10,2,2,2); + break; + case AU_FRSKY_RING: + play(BEEP_DEFAULT_FREQ+25,5,2,10); + play(BEEP_DEFAULT_FREQ+25,5,10,1); + play(BEEP_DEFAULT_FREQ+25,5,2,10); + break; + case AU_FRSKY_SCIFI: + play(80,10,3,2,-1); + play(60,10,3,2,1); + play(70,10,1,0); + break; + case AU_FRSKY_ROBOT: + play(70,5,1,1); + play(50,15,2,1); + play(80,15,2,1); + break; + case AU_FRSKY_CHIRP: + play(BEEP_DEFAULT_FREQ+40,5,1,2); + play(BEEP_DEFAULT_FREQ+54,5,1,3); + break; + case AU_FRSKY_TADA: + play(50,5,5); + play(90,5,5); + play(110,3,4,2); + break; + case AU_FRSKY_CRICKET: + play(80,5,10,3); + play(80,5,20,1); + play(80,5,10,3); + break; + case AU_FRSKY_SIREN: + play(10,20,5,2,1); + break; + case AU_FRSKY_ALARMC: + play(50,4,10,2); + play(70,8,20,1); + play(50,8,10,2); + play(70,4,20,1); + break; + case AU_FRSKY_RATATA: + play(BEEP_DEFAULT_FREQ+50,5,10,10); + break; + case AU_FRSKY_TICK: + play(BEEP_DEFAULT_FREQ+50,5,50,2); + break; + default: + break; + } + } + } +} + +void audioDefevent(uint8_t e) +{ +#ifdef HAPTIC + haptic.event(e); //do this before audio to help sync timings +#endif + audio.event(e, BEEP_DEFAULT_FREQ); +} diff --git a/src/ersky9x/audio.h b/src/ersky9x/audio.h new file mode 100644 index 000000000..abc95fe15 --- /dev/null +++ b/src/ersky9x/audio.h @@ -0,0 +1,127 @@ +/* + * Authors (alphabetical order) + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * open9x is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + + +#ifndef audio_h +#define audio_h + +#define AUDIO_QUEUE_LENGTH (20) +#define BEEP_DEFAULT_FREQ (70) +#define BEEP_OFFSET (10) +#define BEEP_KEY_UP_FREQ (BEEP_DEFAULT_FREQ+5) +#define BEEP_KEY_DOWN_FREQ (BEEP_DEFAULT_FREQ-5) + +class audioQueue +{ + public: + + audioQueue(); + + + void play(uint8_t tFreq, uint8_t tLen, uint8_t tPause, uint8_t tFlags=0, int8_t tFreqIncr=0); + + inline bool busy() { + return (toneTimeLeft > 0); + } + + void event(uint8_t e, uint8_t f=BEEP_DEFAULT_FREQ); + + // heartbeat is responsibile for issueing the audio tones and general square waves + // it is essentially the life of the class. + void heartbeat(); + + // bool freeslots(uint8_t slots); + + inline bool empty() { + return (t_queueRidx == t_queueWidx); + } + + protected: + inline uint8_t getToneLength(uint8_t tLen); + + private: + uint8_t t_queueRidx; + uint8_t t_queueWidx; + + uint8_t toneFreq; + int8_t toneFreqIncr; + uint8_t toneTimeLeft; + uint8_t tonePause; + + // vario + uint8_t tone2Freq; + uint8_t tone2TimeLeft; + uint8_t tone2Pause; + + // queue arrays + uint8_t queueToneFreq[AUDIO_QUEUE_LENGTH]; + int8_t queueToneFreqIncr[AUDIO_QUEUE_LENGTH]; + uint8_t queueToneLength[AUDIO_QUEUE_LENGTH]; + uint8_t queueTonePause[AUDIO_QUEUE_LENGTH]; + uint8_t queueToneRepeat[AUDIO_QUEUE_LENGTH]; + +}; + +//wrapper function - dirty but results in a space saving!!! +extern audioQueue audio; + +void audioDefevent(uint8_t e); + +#define AUDIO_KEYPAD_UP() audioDefevent(AU_KEYPAD_UP) +#define AUDIO_KEYPAD_DOWN() audioDefevent(AU_KEYPAD_DOWN) +#define AUDIO_MENUS() audioDefevent(AU_MENUS) +#define AUDIO_WARNING1() audioDefevent(AU_WARNING1) +#define AUDIO_WARNING2() audioDefevent(AU_WARNING2) +#define AUDIO_ERROR() audioDefevent(AU_ERROR) + + +#define IS_AUDIO_BUSY() audio.busy() + +#define AUDIO_TIMER_30() audioDefevent(AU_TIMER_30) +#define AUDIO_TIMER_20() audioDefevent(AU_TIMER_20) +#define AUDIO_TIMER_10() audioDefevent(AU_TIMER_10) +#define AUDIO_TIMER_LT3() audioDefevent(AU_TIMER_LT3) +#define AUDIO_MINUTE_BEEP() audioDefevent(AU_WARNING1) +#define AUDIO_INACTIVITY() audioDefevent(AU_INACTIVITY) +#define AUDIO_MIX_WARNING_1() audioDefevent(AU_MIX_WARNING_1) +#define AUDIO_MIX_WARNING_2() audioDefevent(AU_MIX_WARNING_2) +#define AUDIO_MIX_WARNING_3() audioDefevent(AU_MIX_WARNING_3) +#define AUDIO_POT_STICK_MIDDLE() audioDefevent(AU_POT_STICK_MIDDLE) +#define AUDIO_VARIO_UP() audioDefevent(AU_KEYPAD_UP) +#define AUDIO_VARIO_DOWN() audioDefevent(AU_KEYPAD_DOWN) +#define AUDIO_TRIM_MIDDLE() audioDefevent(AU_TRIM_MIDDLE) +#define AUDIO_TELEMETRY_ALARM() audioDefevent(AU_WARNING1) + +#define AUDIO_HEARTBEAT() audio.heartbeat() + +#endif diff --git a/src/frsky.cpp b/src/frsky.cpp index 6fd10d605..7ef2ea4d7 100644 --- a/src/frsky.cpp +++ b/src/frsky.cpp @@ -641,11 +641,45 @@ void check_frsky() #if defined(FRSKY_HUB) || defined(WS_HOW_HIGH) static uint16_t s_varioTmr = 0; - if (isFunctionActive(FUNC_VARIO)) { #if defined(AUDIO) - uint8_t warble = 0; -#endif + int16_t verticalSpeed = 0; + //vertical speed in 0.01m/s now + //not baro vario only has additional resolution + //A1/A2 need conversion there + //GPS source need precision to be set, my gps gives 0.1m resolution + /*if (g_model.varioSource == BARO_SOURCE_BARO)*/ + verticalSpeed = limit((int16_t)(-VARIO_SPEED_LIMIT*100), (int16_t)frskyHubData.varioSpeed, (int16_t)(+VARIO_SPEED_LIMIT*100)); + /*else + verticalSpeed = limit((int16_t)-VARIO_SPEED_LIMIT, (int16_t)(frskyHubData.varioSpeed), (int16_t)+VARIO_SPEED_LIMIT)*100;*/ + + uint8_t SoundAltBeepNextFreq = 0; + uint8_t SoundAltBeepNextTime = 0; + static uint8_t SoundAltBeepFreq = 0; + static uint8_t SoundAltBeepTime = 0; + if ((verticalSpeed < g_model.varioSpeedUpMin*VARIO_SPEED_LIMIT_MUL) && (verticalSpeed > (255 - g_model.varioSpeedDownMin)*(-VARIO_SPEED_LIMIT_MUL))) { //check thresholds here in cm/s + SoundAltBeepNextFreq = (0); + SoundAltBeepNextTime = (0); + } + else { + SoundAltBeepNextFreq = (verticalSpeed * 10 + 16000) >> 8; + SoundAltBeepNextTime = (1600 - verticalSpeed) / 100; + if (verticalSpeed > 0) { + if ((int16_t)(g_tmr10ms - s_varioTmr) > (int16_t)((int16_t)SoundAltBeepTime*2)) { + s_varioTmr = g_tmr10ms; + SoundAltBeepTime = SoundAltBeepNextTime; + SoundAltBeepFreq = SoundAltBeepNextFreq; + audio.play(SoundAltBeepFreq, SoundAltBeepTime, 0, PLAY_SOUND_VARIO); + } + } + else { + // negative vertical speed gives sound without pauses + SoundAltBeepTime = SoundAltBeepNextTime; + SoundAltBeepFreq = SoundAltBeepNextFreq; + audio.play(SoundAltBeepFreq, 1, 0, PLAY_SOUND_VARIO); + } + } +#else int8_t verticalSpeed = limit((int16_t)-100, (int16_t)(frskyHubData.varioSpeed/10), (int16_t)+100); uint16_t interval; @@ -666,6 +700,7 @@ void check_frsky() else AUDIO_VARIO_UP(); } +#endif } #endif } diff --git a/src/general_menus.cpp b/src/general_menus.cpp index ec339e851..07e6b40dc 100644 --- a/src/general_menus.cpp +++ b/src/general_menus.cpp @@ -96,6 +96,7 @@ enum menuProcSetupItems { #endif #if defined(HAPTIC) ITEM_SETUP_HAPTIC_MODE, + ITEM_SETUP_HAPTIC_LENGTH, ITEM_SETUP_HAPTIC_STRENGTH, #endif #if defined(PCBARM) @@ -217,6 +218,10 @@ void menuProcSetup(uint8_t event) g_eeGeneral.hapticMode = selectMenuItem(y, STR_HAPTICMODE, STR_VBEEPMODE, g_eeGeneral.hapticMode, -2, 1, attr, event); break; + case ITEM_SETUP_HAPTIC_LENGTH: + g_eeGeneral.hapticLength = selectMenuItem(y, STR_HAPTICLENGTH, STR_VBEEPLEN, g_eeGeneral.hapticLength, -2, 2, attr, event); + break; + case ITEM_SETUP_HAPTIC_STRENGTH: lcd_putsLeft( y, STR_HAPTICSTRENGTH); lcd_outdezAtt(GENERAL_PARAM_OFS, y, g_eeGeneral.hapticStrength, attr|LEFT); diff --git a/src/haptic.cpp b/src/haptic.cpp new file mode 100644 index 000000000..2eacfe533 --- /dev/null +++ b/src/haptic.cpp @@ -0,0 +1,127 @@ +/* + * Authors (alphabetical order) + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * open9x is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "open9x.h" + +hapticQueue::hapticQueue() +{ + buzzTimeLeft = 0; + buzzPause = 0; + + t_queueRidx = 0; + t_queueWidx = 0; + + hapticTick = 0; +} + +void hapticQueue::heartbeat() +{ +#if defined(SIMU) + return; +#endif + + if (buzzTimeLeft > 0) { + buzzTimeLeft--; // time gets counted down +#if defined(PCBARM) + hapticOn(g_eeGeneral.hapticStrength * 20); +#else + if (hapticTick-- > 0) { + HAPTIC_ON; // haptic output 'high' + } + else { + HAPTIC_OFF; // haptic output 'high' + hapticTick = g_eeGeneral.hapticStrength; + } +#endif + } + else { + HAPTIC_OFF; // haptic output 'high' + if (buzzPause > 0) { + buzzPause--; + } + else if (t_queueRidx != t_queueWidx) { + buzzTimeLeft = queueHapticLength[t_queueRidx]; + buzzPause = queueHapticPause[t_queueRidx]; + if (!queueHapticRepeat[t_queueRidx]--) { + t_queueRidx = (t_queueRidx + 1) % HAPTIC_QUEUE_LENGTH; + } + } + } +} + +void hapticQueue::play(uint8_t tLen, uint8_t tPause, uint8_t tFlags) +{ + tLen = getHapticLength(tLen); + + if (tFlags & PLAY_NOW || (!busy() && empty())) { + buzzTimeLeft = tLen; + buzzPause = tPause; + t_queueWidx = t_queueRidx; + } + else { + tFlags += 1; + } + + tFlags &= 0x0f; + if (tFlags) { + uint8_t next_queueWidx = (t_queueWidx + 1) % HAPTIC_QUEUE_LENGTH; + if (next_queueWidx != t_queueRidx) { + queueHapticLength[t_queueWidx] = tLen; + queueHapticPause[t_queueWidx] = tPause; + queueHapticRepeat[t_queueWidx] = tFlags-1; + t_queueWidx = next_queueWidx; + } + } +} + +void hapticQueue::event(uint8_t e) +{ + if (g_eeGeneral.hapticMode>0 || (g_eeGeneral.hapticMode==0 && e>=AU_WARNING1) || (g_eeGeneral.hapticMode>=-1 && e<=AU_ERROR)) { + switch (e) { + case 0: // very little buzz for keys / trims + play(5, 0, PLAY_NOW); + break; + case AU_TIMER_20: // two buzz + play(10,2,1); + break; + case AU_TIMER_30: // three buzz + play(10,2,2); + break; + default: + play(10,2,0); + break; + } + } +} + +hapticQueue haptic; diff --git a/src/haptic.h b/src/haptic.h new file mode 100644 index 000000000..1e2ac9cd4 --- /dev/null +++ b/src/haptic.h @@ -0,0 +1,90 @@ +/* + * Authors (alphabetical order) + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * open9x is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef haptic_h +#define haptic_h + +#define HAPTIC_QUEUE_LENGTH 3 + +class hapticQueue +{ + public: + + hapticQueue(); + + // only difference between these two functions is that one does the + // interupt queue (Now) and the other queues for playing ASAP. + void play(uint8_t tLen, uint8_t tPause, uint8_t tRepeat=0); + + inline bool busy() { return (buzzTimeLeft > 0); } + + void event(uint8_t e); + + // heartbeat is responsibile for issueing the haptic buzzs and general square waves + // it is essentially the life of the class. + void heartbeat(); + + // bool freeslots(uint8_t slots); + + inline bool empty() { + return (t_queueRidx == t_queueWidx); + } + + protected: + inline uint8_t getHapticLength(uint8_t tLen) { + return ((g_eeGeneral.hapticLength * 2) + tLen) * 2; + } + + private: + uint8_t t_queueRidx; + uint8_t t_queueWidx; + + uint8_t buzzTimeLeft; + uint8_t buzzPause; + + uint8_t hapticTick; + + // queue arrays + uint8_t queueHapticLength[HAPTIC_QUEUE_LENGTH]; + uint8_t queueHapticPause[HAPTIC_QUEUE_LENGTH]; + uint8_t queueHapticRepeat[HAPTIC_QUEUE_LENGTH]; +}; + +//wrapper function - dirty but results in a space saving!!! +extern hapticQueue haptic; + +#define IS_HAPTIC_BUSY() haptic.busy() +#define HAPTIC_HEARTBEAT() haptic.heartbeat() + + +#endif // haptic_h diff --git a/src/model_menus.cpp b/src/model_menus.cpp index 8696610fc..2d48a1357 100644 --- a/src/model_menus.cpp +++ b/src/model_menus.cpp @@ -2102,6 +2102,12 @@ void menuProcFunctionSwitches(uint8_t event) break; #endif } +#if defined(HAPTIC) + else if (sd->func == FUNC_HAPTIC) { + val_max = 3; + lcd_outdezAtt(21*FW, y, val_displayed, attr); + } +#endif #if defined(SOMO) else if (sd->func == FUNC_PLAY_SOMO) { lcd_outdezAtt(21*FW, y, val_displayed, attr); diff --git a/src/myeeprom.h b/src/myeeprom.h index 5cab40ba2..3adce1ee3 100644 --- a/src/myeeprom.h +++ b/src/myeeprom.h @@ -286,6 +286,9 @@ enum Functions { FUNC_TRAINER_AIL, FUNC_INSTANT_TRIM, FUNC_PLAY_SOUND, +#ifdef HAPTIC + FUNC_HAPTIC, +#endif #ifdef SOMO FUNC_PLAY_SOMO, #endif diff --git a/src/open9x.cpp b/src/open9x.cpp index d888110f9..9c09d3041 100644 --- a/src/open9x.cpp +++ b/src/open9x.cpp @@ -1429,6 +1429,13 @@ void evalFunctions() beep(3); #endif } + +#if defined(HAPTIC) + if (sd->func == FUNC_HAPTIC) { + haptic.event(sd->param); + } +#endif + active_functions |= mask; } else { @@ -2085,6 +2092,7 @@ void perMain() } #if defined(PCBARM) + HAPTIC_HEARTBEAT(); AUDIO_HEARTBEAT(); // the queue processing #endif @@ -2156,6 +2164,10 @@ ISR(TIMER0_COMP_vect, ISR_NOBLOCK) //10ms timer AUDIO_HEARTBEAT(); +#ifdef HAPTIC + HAPTIC_HEARTBEAT(); +#endif + #ifdef DEBUG // Record start time from TCNT1 to record excution time cli(); diff --git a/src/open9x.h b/src/open9x.h index acbdd905f..68aab5746 100644 --- a/src/open9x.h +++ b/src/open9x.h @@ -621,7 +621,9 @@ void read_9_adc(void ) ; #define BUZZER_OFF PORTE &= ~(1 << OUT_E_BUZZER) #if defined(HAPTIC) -#if defined(PCBV4) +#if defined(PCBARM) +#define HAPTIC_OFF hapticOff() +#elif defined(PCBV4) #define HAPTIC_ON PORTD &= ~(1 << INP_D_HAPTIC) #define HAPTIC_OFF PORTD |= (1 << INP_D_HAPTIC) #else @@ -821,6 +823,48 @@ extern uint16_t jeti_keys; #include "mavlink.h" #endif +// REPEAT uses 0x01 to 0x0f +#define PLAY_NOW 0x10 +#define PLAY_SOUND_VARIO 0x20 + +/* make sure the defines below always go in numeric order */ +enum AUDIO_SOUNDS { + AU_INACTIVITY, + AU_TX_BATTERY_LOW, + AU_ERROR, + AU_KEYPAD_UP, + AU_KEYPAD_DOWN, + AU_MENUS, + AU_TRIM_MOVE, + AU_WARNING1, + AU_WARNING2, + AU_WARNING3, + AU_TRIM_MIDDLE, + AU_TADA, + AU_POT_STICK_MIDDLE, + AU_MIX_WARNING_1, + AU_MIX_WARNING_2, + AU_MIX_WARNING_3, + AU_TIMER_30, + AU_TIMER_20, + AU_TIMER_10, + AU_TIMER_LT3, + AU_FRSKY_FIRST, + AU_FRSKY_WARN1 = AU_FRSKY_FIRST, + AU_FRSKY_WARN2, + AU_FRSKY_CHEEP, + AU_FRSKY_RING, + AU_FRSKY_SCIFI, + AU_FRSKY_ROBOT, + AU_FRSKY_CHIRP, + AU_FRSKY_TADA, + AU_FRSKY_CRICKET, + AU_FRSKY_SIREN, + AU_FRSKY_ALARMC, + AU_FRSKY_RATATA, + AU_FRSKY_TICK, + AU_FRSKY_LAST, +}; #if defined(AUDIO) //audio settungs are external to keep out clutter! // TODO english learning for me... what does mean "keep out clutter"? @@ -829,6 +873,10 @@ extern uint16_t jeti_keys; #include "beeper.h" #endif +#if defined(HAPTIC) +#include "haptic.h" +#endif + #if defined(SDCARD) #include "sdcard.h" #endif diff --git a/src/stock/audio.cpp b/src/stock/audio.cpp new file mode 100644 index 000000000..2ff64a8e4 --- /dev/null +++ b/src/stock/audio.cpp @@ -0,0 +1,307 @@ +/* + * Authors (alphabetical order) + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * open9x is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "open9x.h" + +audioQueue::audioQueue() +{ + toneTimeLeft = 0; + tonePause = 0; + + t_queueRidx = 0; + t_queueWidx = 0; +} + + +// heartbeat is responsibile for issueing the audio tones and general square waves +// it is essentially the life of the class. +// it is called every 10ms +void audioQueue::heartbeat() +{ +#if defined(SIMU) + return; +#endif + + if (toneTimeLeft > 0) { +#if defined(PCBV4) + if (toneFreq) { + OCR0A = (5000 / toneFreq); // sticking with old values approx 20(abs. min) to 90, 60 being the default tone(?). + SPEAKER_ON; + } +#endif + toneTimeLeft--; //time gets counted down + toneFreq += toneFreqIncr; + } + else { + if (tonePause > 0) { + SPEAKER_OFF; + tonePause--; //time gets counted down + } + else if (t_queueRidx != t_queueWidx) { + toneFreq = queueToneFreq[t_queueRidx]; + toneTimeLeft = queueToneLength[t_queueRidx]; + toneFreqIncr = queueToneFreqIncr[t_queueRidx]; + tonePause = queueTonePause[t_queueRidx]; + if (!queueToneRepeat[t_queueRidx]--) { + t_queueRidx = (t_queueRidx + 1) % AUDIO_QUEUE_LENGTH; + } + } + else { + if (tone2TimeLeft > 0) { +#if defined(PCBV4) + if (tone2Freq) { + OCR0A = (5000 / tone2Freq); // sticking with old values approx 20(abs. min) to 90, 60 being the default tone(?). + SPEAKER_ON; + } +#else + toneFreq = tone2Freq; +#endif + tone2TimeLeft--; //time gets counted down + } + else { + SPEAKER_OFF; + if (tone2Pause > 0) { + tone2Pause--; //time gets counted down + } + } + } + } +} + +inline uint8_t audioQueue::getToneLength(uint8_t tLen) +{ + uint8_t result = tLen; // default + if (g_eeGeneral.beeperLength < 0) { + result /= (1-g_eeGeneral.beeperLength); + } + if (g_eeGeneral.beeperLength > 0) { + result *= (1+g_eeGeneral.beeperLength); + } + return result; +} + +void audioQueue::play(uint8_t tFreq, uint8_t tLen, uint8_t tPause, + uint8_t tFlags, int8_t tFreqIncr) +{ + if (tFlags & PLAY_SOUND_VARIO) { + tone2Freq = tFreq; + tone2TimeLeft = tLen; + tone2Pause = tPause; + } + else { + tFreq += g_eeGeneral.speakerPitch + BEEP_OFFSET; // add pitch compensator + tLen = getToneLength(tLen); + if (tFlags & PLAY_NOW || (!busy() && empty())) { + toneFreq = tFreq; + toneTimeLeft = tLen; + tonePause = tPause; + toneFreqIncr = tFreqIncr; + t_queueWidx = t_queueRidx; + } + else { + tFlags++; + } + + tFlags &= 0x0f; + if (tFlags) { + uint8_t next_queueWidx = (t_queueWidx + 1) % AUDIO_QUEUE_LENGTH; + if (next_queueWidx != t_queueRidx) { + queueToneFreq[t_queueWidx] = tFreq; + queueToneLength[t_queueWidx] = tLen; + queueTonePause[t_queueWidx] = tPause; + queueToneRepeat[t_queueWidx] = tFlags - 1; + queueToneFreqIncr[t_queueWidx] = tFreqIncr; + t_queueWidx = next_queueWidx; + } + } + } +} + +void audioQueue::event(uint8_t e, uint8_t f) +{ + if (g_eeGeneral.flashBeep && (e <= AU_ERROR || e >= AU_WARNING1)) + g_LightOffCounter = FLASH_DURATION; + + if (g_eeGeneral.beeperMode>0 || (g_eeGeneral.beeperMode==0 && e>=AU_WARNING1) || (g_eeGeneral.beeperMode>=-1 && e<=AU_ERROR)) { + if (e < AU_FRSKY_FIRST || empty()) { + switch (e) { + // inactivity timer alert + case AU_INACTIVITY: + play(70, 10, 2, 2|PLAY_NOW); + break; + // low battery in tx + case AU_TX_BATTERY_LOW: + if (empty()) { + play(60, 20, 3, 2, 1); + play(80, 20, 3, 2, -1); + } + break; + // error + case AU_ERROR: + play(BEEP_DEFAULT_FREQ, 40, 1, 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: + play(BEEP_KEY_UP_FREQ, 10, 1, 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: + play(BEEP_KEY_DOWN_FREQ, 10, 1, PLAY_NOW); + break; + // menu display (also used by a few generic beeps) + case AU_MENUS: + play(BEEP_DEFAULT_FREQ, 10, 2, PLAY_NOW); + break; + // trim move + case AU_TRIM_MOVE: + play(f, 6, 1, PLAY_NOW); + break; + // trim center + case AU_TRIM_MIDDLE: + play(BEEP_DEFAULT_FREQ, 10, 2, PLAY_NOW); + break; + // warning one + case AU_WARNING1: + play(BEEP_DEFAULT_FREQ, 10, 1, PLAY_NOW); + break; + // warning two + case AU_WARNING2: + play(BEEP_DEFAULT_FREQ, 20, 1, PLAY_NOW); + break; + // warning three + case AU_WARNING3: + play(BEEP_DEFAULT_FREQ, 30, 1, PLAY_NOW); + break; + // startup tune + case AU_TADA: + play(50, 10, 5); + play(90, 10, 5); + play(110, 5, 4, 2); + break; + // pot/stick center + case AU_POT_STICK_MIDDLE: + play(BEEP_DEFAULT_FREQ + 50, 10, 1, PLAY_NOW); + break; + // mix warning 1 + case AU_MIX_WARNING_1: + play(BEEP_DEFAULT_FREQ + 50, 6, 0, PLAY_NOW); + break; + // mix warning 2 + case AU_MIX_WARNING_2: + play(BEEP_DEFAULT_FREQ + 52, 6, 0, PLAY_NOW); + break; + // mix warning 3 + case AU_MIX_WARNING_3: + play(BEEP_DEFAULT_FREQ + 54, 6, 0, PLAY_NOW); + break; + // time 30 seconds left + case AU_TIMER_30: + play(BEEP_DEFAULT_FREQ + 50, 15, 3, 2|PLAY_NOW); + break; + // time 20 seconds left + case AU_TIMER_20: + play(BEEP_DEFAULT_FREQ + 50, 15, 3, 1|PLAY_NOW); + break; + // time 10 seconds left + case AU_TIMER_10: + // time <3 seconds left + case AU_TIMER_LT3: + play(BEEP_DEFAULT_FREQ + 50, 15, 3, PLAY_NOW); + break; + case AU_FRSKY_WARN1: + play(BEEP_DEFAULT_FREQ+20,15,5,2); + break; + case AU_FRSKY_WARN2: + play(BEEP_DEFAULT_FREQ+30,15,5,2); + break; + case AU_FRSKY_CHEEP: + play(BEEP_DEFAULT_FREQ+30,10,2,2,2); + break; + case AU_FRSKY_RING: + play(BEEP_DEFAULT_FREQ+25,5,2,10); + play(BEEP_DEFAULT_FREQ+25,5,10,1); + play(BEEP_DEFAULT_FREQ+25,5,2,10); + break; + case AU_FRSKY_SCIFI: + play(80,10,3,2,-1); + play(60,10,3,2,1); + play(70,10,1,0); + break; + case AU_FRSKY_ROBOT: + play(70,5,1,1); + play(50,15,2,1); + play(80,15,2,1); + break; + case AU_FRSKY_CHIRP: + play(BEEP_DEFAULT_FREQ+40,5,1,2); + play(BEEP_DEFAULT_FREQ+54,5,1,3); + break; + case AU_FRSKY_TADA: + play(50,5,5); + play(90,5,5); + play(110,3,4,2); + break; + case AU_FRSKY_CRICKET: + play(80,5,10,3); + play(80,5,20,1); + play(80,5,10,3); + break; + case AU_FRSKY_SIREN: + play(10,20,5,2,1); + break; + case AU_FRSKY_ALARMC: + play(50,4,10,2); + play(70,8,20,1); + play(50,8,10,2); + play(70,4,20,1); + break; + case AU_FRSKY_RATATA: + play(BEEP_DEFAULT_FREQ+50,5,10,10); + break; + case AU_FRSKY_TICK: + play(BEEP_DEFAULT_FREQ+50,5,50,2); + break; + default: + break; + } + } + } +} + +void audioDefevent(uint8_t e) +{ +#ifdef HAPTIC + haptic.event(e); //do this before audio to help sync timings +#endif + audio.event(e); +} diff --git a/src/audio.h b/src/stock/audio.h similarity index 60% rename from src/audio.h rename to src/stock/audio.h index 56116a4a6..c82ffc1ce 100644 --- a/src/audio.h +++ b/src/stock/audio.h @@ -31,9 +31,6 @@ * */ -#ifndef audio_h -#define audio_h - #if defined(PCBARM) #include "ersky9x/sound.h" #endif @@ -50,93 +47,18 @@ #define BEEP_KEY_UP_FREQ (BEEP_DEFAULT_FREQ+5) #define BEEP_KEY_DOWN_FREQ (BEEP_DEFAULT_FREQ-5) -/* make sure the defines below always go in numeric order */ -enum AUDIO_SOUNDS { - AU_INACTIVITY, - AU_TX_BATTERY_LOW, - AU_ERROR, - AU_KEYPAD_UP, - AU_KEYPAD_DOWN, - AU_MENUS, - AU_TRIM_MOVE, - AU_WARNING1, - AU_WARNING2, - AU_WARNING3, - AU_TRIM_MIDDLE, - AU_TADA, - AU_POT_STICK_MIDDLE, - AU_MIX_WARNING_1, - AU_MIX_WARNING_2, - AU_MIX_WARNING_3, - AU_TIMER_30, - AU_TIMER_20, - AU_TIMER_10, - AU_TIMER_LT3, - AU_FRSKY_FIRST, - AU_FRSKY_WARN1 = AU_FRSKY_FIRST, - AU_FRSKY_WARN2, - AU_FRSKY_CHEEP, - AU_FRSKY_RING, - AU_FRSKY_SCIFI, - AU_FRSKY_ROBOT, - AU_FRSKY_CHIRP, - AU_FRSKY_TADA, - AU_FRSKY_CRICKET, - AU_FRSKY_SIREN, - AU_FRSKY_ALARMC, - AU_FRSKY_RATATA, - AU_FRSKY_TICK, -#ifdef HAPTIC - AU_FRSKY_HAPTIC1, - AU_FRSKY_HAPTIC2, - AU_FRSKY_HAPTIC3, -#endif - AU_FRSKY_LAST, -}; - class audioQueue { public: audioQueue(); - // only difference between these two functions is that one does the - // interupt queue (Now) and the other queues for playing ASAP. - void playNow(uint8_t tFreq, uint8_t tLen, uint8_t tPause, uint8_t tRepeat=0, uint8_t tHaptic=0, int8_t tFreqIncr=0); - - void playASAP(uint8_t tFreq, uint8_t tLen, uint8_t tPause, uint8_t tRepeat=0, uint8_t tHaptic=0, int8_t tFreqIncr=0); + void play(uint8_t tFreq, uint8_t tLen, uint8_t tPause, uint8_t tFlags=0, int8_t tFreqIncr=0); inline bool busy() { return (toneTimeLeft > 0); } void event(uint8_t e, uint8_t f=BEEP_DEFAULT_FREQ); -#if defined(ISER9X) - inline void driver() { - if (toneTimeLeft > 0) { - switch (g_eeGeneral.speakerMode) { - case 0: - // stock beeper. simply turn port on for x time! - if (toneTimeLeft > 0){ - PORTE |= (1< 0) { toneCounter += toneFreq; @@ -146,7 +68,6 @@ class audioQueue BUZZER_OFF; } } -#endif // heartbeat is responsibile for issueing the audio tones and general square waves // it is essentially the life of the class. @@ -170,6 +91,11 @@ class audioQueue uint8_t toneTimeLeft; uint8_t tonePause; + // vario has less priority + uint8_t tone2Freq; + uint8_t tone2TimeLeft; + uint8_t tone2Pause; + // queue arrays uint8_t queueToneFreq[AUDIO_QUEUE_LENGTH]; int8_t queueToneFreqIncr[AUDIO_QUEUE_LENGTH]; @@ -177,35 +103,21 @@ class audioQueue uint8_t queueTonePause[AUDIO_QUEUE_LENGTH]; uint8_t queueToneRepeat[AUDIO_QUEUE_LENGTH]; -#ifdef HAPTIC - uint8_t toneHaptic; - uint8_t hapticTick; - uint8_t queueToneHaptic[AUDIO_QUEUE_LENGTH]; - uint8_t hapticSpinUpTime; -#endif - -#if defined(PCBSTD) uint8_t toneCounter; -#endif + }; -//wrapper function - dirty but results in a space saving!!! + extern audioQueue audio; void audioDefevent(uint8_t e); -#define HAPTIC_SPINUP (10); - #define AUDIO_KEYPAD_UP() audioDefevent(AU_KEYPAD_UP) #define AUDIO_KEYPAD_DOWN() audioDefevent(AU_KEYPAD_DOWN) #define AUDIO_MENUS() audioDefevent(AU_MENUS) #define AUDIO_WARNING1() audioDefevent(AU_WARNING1) #define AUDIO_WARNING2() audioDefevent(AU_WARNING2) #define AUDIO_ERROR() audioDefevent(AU_ERROR) - - -#define IS_AUDIO_BUSY() audio.busy() - #define AUDIO_TIMER_30() audioDefevent(AU_TIMER_30) #define AUDIO_TIMER_20() audioDefevent(AU_TIMER_20) #define AUDIO_TIMER_10() audioDefevent(AU_TIMER_10) @@ -221,10 +133,7 @@ void audioDefevent(uint8_t e); #define AUDIO_TRIM_MIDDLE() audioDefevent(AU_TRIM_MIDDLE) #define AUDIO_TELEMETRY_ALARM() audioDefevent(AU_WARNING1) -#if defined(PCBSTD) #define AUDIO_DRIVER() audio.driver() -#endif - #define AUDIO_HEARTBEAT() audio.heartbeat() +#define IS_AUDIO_BUSY() audio.busy() -#endif // audio_h diff --git a/src/translations/en.h b/src/translations/en.h index 7db7a0081..e92c7739c 100644 --- a/src/translations/en.h +++ b/src/translations/en.h @@ -99,12 +99,17 @@ #else #define TR_SOUND "Beep\0 " #endif +#ifdef HAPTIC +#define TR_HAPTIC "Haptic\0 " +#else +#define TR_HAPTIC +#endif #ifdef DEBUG #define TR_TEST "Test\0 " #else #define TR_TEST #endif -#define TR_VFSWFUNC "Security \0 ""Trainer \0 ""Instant Trim " TR_SOUND TR_SOMO "Reset\0 " TR_VVARIO TR_SDCLOGGS TR_TEST +#define TR_VFSWFUNC "Security \0 ""Trainer \0 ""Instant Trim " TR_SOUND TR_HAPTIC TR_SOMO "Reset\0 " TR_VVARIO TR_SDCLOGGS TR_TEST #define LEN_VFSWRESET "\006" #define TR_VFSWRESET "Timer1""Timer2""All ""Telem." diff --git a/src/translations/fr.h b/src/translations/fr.h index 059c920f4..26b6e8d55 100644 --- a/src/translations/fr.h +++ b/src/translations/fr.h @@ -99,12 +99,17 @@ #else #define TR_SOUND "Beep\0 " #endif +#ifdef HAPTIC +#define TR_HAPTIC "Haptic\0 " +#else +#define TR_HAPTIC +#endif #ifdef DEBUG #define TR_TEST "Test\0 " #else #define TR_TEST #endif -#define TR_VFSWFUNC "Securite \0 ""Ecolage \0 ""Trim instant " TR_SOUND TR_SOMO "Reset\0 " TR_VVARIO TR_SDCLOGGS TR_TEST +#define TR_VFSWFUNC "Securite \0 ""Ecolage \0 ""Trim instant " TR_SOUND TR_HAPTIC TR_SOMO "Reset\0 " TR_VVARIO TR_SDCLOGGS TR_TEST #define LEN_VFSWRESET "\006" #define TR_VFSWRESET "Timer1""Timer2""All ""Telem." diff --git a/src/translations/se.h b/src/translations/se.h index 651370dbc..1d24da160 100644 --- a/src/translations/se.h +++ b/src/translations/se.h @@ -99,12 +99,17 @@ #else #define TR_SOUND "Pip \0 " #endif +#ifdef HAPTIC +#define TR_HAPTIC "Haptic\0 " +#else +#define TR_HAPTIC +#endif #ifdef DEBUG #define TR_TEST "Test\0 " #else #define TR_TEST #endif -#define TR_VFSWFUNC "S\201kerhet \0 ""Trainer \0 ""Instant Trim " TR_SOUND TR_SOMO "Reset\0 " TR_VVARIO TR_SDCLOGGS TR_TEST +#define TR_VFSWFUNC "S\201kerhet \0 ""Trainer \0 ""Instant Trim " TR_SOUND TR_HAPTIC TR_SOMO "Reset\0 " TR_VVARIO TR_SDCLOGGS TR_TEST #define LEN_VFSWRESET "\006" #define TR_VFSWRESET "Timer1""Timer2""All ""Telem."