diff --git a/src/Makefile b/src/Makefile index 97022800e..1b5de53e3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -226,6 +226,13 @@ ifneq ($(SPLASH), NO) CPPDEFS += -DSPLASH endif +ifeq ($(BEEPER), SPEAKER) + CPPDEFS += -DBEEPSPKR + CPPSRC += audio.cpp +else + CPPSRC += beeper.cpp +endif + ifeq ($(PCB), STD) # STD PCB, so ... @@ -258,11 +265,6 @@ ifeq ($(PCB), STD) CPPDEFS += -DWS_HOW_HIGH endif endif - - # gruvin: If buzzer speaker replacment supported - ifeq ($(BEEPER), SPEAKER) - CPPDEFS += -DBEEPSPKR - endif # If buzzer modified (no interference with PPM jack) ifeq ($(BEEPER), BUZZER_MOD) @@ -293,9 +295,6 @@ else CPPSRC += frsky.cpp CPPDEFS += -DPCBV3 -DFRSKY -DFRSKY_HUB -DWS_HOW_HIGH - ifeq ($(PCB), V3) - CPPDEFS += -DBEEPSPKR - endif ifeq ($(LOGS), YES) CPPSRC += logs.cpp CPPDEFS += -DLOGS @@ -303,13 +302,6 @@ else endif ifeq ($(PCB), V4) CPPDEFS += -DPCBV4 - # Temporary hack to get stock beeper working for testing, etc ... make BEEPER=BUZZER_MOD - # TODO should not be needed / tested here in V4 - ifeq ($(BEEPER), BUZZER_MOD) - CPPDEFS += -DBUZZER_MOD - else - CPPDEFS += -DBEEPSPKR - endif endif ifeq ($(SOMO), YES) CPPSRC += somo14d.cpp diff --git a/src/audio.cpp b/src/audio.cpp new file mode 100644 index 000000000..d588e46a7 --- /dev/null +++ b/src/audio.cpp @@ -0,0 +1,601 @@ +/* + * Author - Rob Thomson + * + * 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() +{ + aqinit(); +} + +void audioQueue::aqinit() +{ + //make sure haptic off by default + + HAPTIC_OFF; + + //initialize all arrays + flushqueue(0); + + //set key vars to 0 to ensure no garbage + toneFreq = 0; + toneFreqEnd = 0; + toneRepeat = 0; + toneTimeLeft = 0; + queueState = 0; + toneRepeatCnt = 0; + inToneRepeat = 0; + hapticTick = 0; + +#ifdef FRSKY + frskySample = 0; +#endif + + heartbeatTimer = 0; + flushTemp(); + +} + +bool audioQueue::busy() +{ + if (toneTimeLeft > 0) { + return true; + } + else { + return false; + } +} + +bool audioQueue::freeslots(uint8_t slots) +{ + //find out if enough queue space exists to add the requested routine! + for (uint8_t i = AUDIO_QUEUE_LENGTH; i--;) { + slots--; + if (slots == 0) { + break; + } //only loop for the number of items indicated in slots + if (queueToneStart[i] != 0) { + return false; + break; + } + } + return true; +} + +void audioQueue::commit(uint8_t toneInterupt) +{ + + if (toneInterupt == 0) { + //queued events + if (freeslots(3)) { //do not insert into the queue if less than 3 spare slots! this stops sound collitions from occuring + for (uint8_t i = 0; i < AUDIO_QUEUE_LENGTH; i++) { + if (queueToneStart[i] == 0) { //add to the first entry that has a start tone of zero (no sound) + queueToneStart[i] = t_queueToneStart; + queueToneEnd[i] = t_queueToneEnd; + queueToneLength[i] = t_queueToneLength; + queueTonePause[i] = t_queueTonePause; + queueToneRepeat[i] = t_queueToneRepeat; + queueToneHaptic[i] = t_queueToneHaptic; + flushTemp(); + break; + } + } + } + } + else { + //interrupt events + queueToneStart[0] = t_queueToneStart; + queueToneEnd[0] = t_queueToneEnd; + queueToneLength[0] = t_queueToneLength; + queueTonePause[0] = t_queueTonePause; + queueToneRepeat[0] = t_queueToneRepeat; + queueToneHaptic[0] = t_queueToneHaptic; + + flushqueue(1); //purge queue on interrupt events to stop broken audio + flushTemp(); + } + +} + +void audioQueue::flushqueue(uint8_t startpos) +{ + for (; startpos < AUDIO_QUEUE_LENGTH; startpos++) { + queueToneStart[startpos] = 0; + queueToneEnd[startpos] = 0; + queueToneLength[startpos] = 0; + queueTonePause[startpos] = 0; + queueToneRepeat[startpos] = 0; + queueToneHaptic[startpos] = 0; + } +} + +//set all temporary buffers to default +void audioQueue::flushTemp() +{ + t_queueToneStart = 0; + t_queueToneEnd = 0; + t_queueToneLength = 0; + t_queueTonePause = 0; + t_queueToneRepeat = 0; + t_queueToneHaptic = 0; + rateOfChange = 0; + toneHaptic = 0; +} + +void audioQueue::restack() +{ + for (uint8_t i = 0; i < AUDIO_QUEUE_LENGTH - 1; i++) { + queueToneStart[i] = queueToneStart[i + 1]; + queueToneEnd[i] = queueToneEnd[i + 1]; + queueToneLength[i] = queueToneLength[i + 1]; + queueTonePause[i] = queueTonePause[i + 1]; + queueToneRepeat[i] = queueToneRepeat[i + 1]; + queueToneHaptic[i] = queueToneHaptic[i + 1]; + } + flushqueue(AUDIO_QUEUE_LENGTH - 1); //set the last entry to 0 as nothing in stack to add too! +} + +//heartbeat is responsibile for issueing the audio tones and general square waves +// it is essentially the life of the class. +void audioQueue::heartbeat() +{ + + uint8_t z; //direction calulations + uint8_t hTimer; //heartbeat timer + + if (queueState == 1) { + + if (g_eeGeneral.beeperVal > 0) { + //never do sounds if we are set to go quiet +#if 0 + switch (g_eeGeneral.speakerMode) { + + case 0: + //stock beeper. simply turn port on for x time! + if (toneTimeLeft > 0) { +#if defined (PCBV3) + TCCR0A &= ~(0b01< 0) { + toneCounter += toneFreq; + if ((toneCounter & 0x80) == 0x80) { +#if defined (PCBV3) + TCCR0A &= ~(0b01< 0) { + HAPTIC_ON; // haptic output 'high' + hapticTick++; + } + else { + HAPTIC_OFF; //haptic output low + hapticTick = 0; + } + } + else { + HAPTIC_OFF; // haptic output 'low' + } + + } + else { + PORTE &= ~(1 << OUT_E_BUZZER); // speaker output 'low' + HAPTIC_OFF; + } + + //step through array checking if we have any tones to play + //next heartbeat will play whatever we put in queue + if ((queueToneStart[0] > 0 || queueToneHaptic[0] == 1) && toneTimeLeft <= 0 + && queueState == 0) { + if (queueToneEnd[0] > 0 && queueToneEnd[0] != queueToneStart[0]) { + if (queueToneStart[0] > queueToneEnd[0]) { //tone going down + z = queueToneStart[0] - queueToneEnd[0]; + rateOfChange = -2; + } + else { //tone going up + z = queueToneEnd[0] - queueToneStart[0]; + rateOfChange = 2; + } + if (queueToneStart[0] > 0) { + toneFreq = queueToneStart[0] + g_eeGeneral.speakerPitch + BEEP_OFFSET; // add pitch compensator + } + else { + toneFreq = queueToneStart[0]; // done so hapticOnly option can work + } + toneFreqEnd = queueToneEnd[0] + g_eeGeneral.speakerPitch + BEEP_OFFSET; + toneTimeLeft = z; + tonePause = queueTonePause[0]; + toneRepeat = queueToneRepeat[0]; + toneHaptic = queueToneHaptic[0]; + } + else { + //simple tone handler + if (queueToneStart[0] > 0) { + toneFreq = (queueToneStart[0] + g_eeGeneral.speakerPitch) + BEEP_OFFSET; // add pitch compensator + } + else { + toneFreq = queueToneStart[0]; + } + rateOfChange = 0; + toneFreqEnd = 0; + toneTimeLeft = queueToneLength[0]; + tonePause = queueTonePause[0]; + toneRepeat = queueToneRepeat[0]; + toneHaptic = queueToneHaptic[0]; + } + queueState = 1; + if (toneRepeat != 0 && inToneRepeat == 0) { + inToneRepeat = 1; + toneRepeatCnt = toneRepeat; + } + + if (inToneRepeat == 1) { + toneRepeatCnt--; + if (toneRepeatCnt <= 0) { + inToneRepeat = 0; + restack(); + } + } + else { + restack(); + } + + } + + hTimer = AUDIO_QUEUE_HEARTBEAT_NORM; // default + if (g_eeGeneral.beeperVal == 2) { + //xshort + hTimer = AUDIO_QUEUE_HEARTBEAT_XSHORT; + } + else if (g_eeGeneral.beeperVal == 3) { + //short + hTimer = AUDIO_QUEUE_HEARTBEAT_SHORT; + } + else if (g_eeGeneral.beeperVal == 5) { + //long + hTimer = AUDIO_QUEUE_HEARTBEAT_LONG; + } + else if (g_eeGeneral.beeperVal == 6) { + //xlong + hTimer = AUDIO_QUEUE_HEARTBEAT_XLONG; + } + + heartbeatTimer++; + if (heartbeatTimer >= hTimer) { + + heartbeatTimer = 0; + + if (queueState == 1) { + if (toneTimeLeft > 0) { + toneFreq += rateOfChange; // -2, 0 or 2 + toneTimeLeft--; //time gets counted down + } + if (toneTimeLeft <= 0) { + if (tonePause-- <= 0) { + queueState = 0; + } + } + } + + } + +} + +void audioQueue::playNow(uint8_t tStart, uint8_t tLen, uint8_t tPause, + uint8_t tRepeat, uint8_t tHaptic, uint8_t tEnd) +{ + t_queueToneStart = tStart; + t_queueToneLength = tLen; + t_queueTonePause = tPause; + t_queueToneHaptic = tHaptic; + t_queueToneRepeat = tRepeat; + t_queueToneEnd = tEnd; + commit(1); +} + +void audioQueue::playASAP(uint8_t tStart, uint8_t tLen, uint8_t tPause, + uint8_t tRepeat, uint8_t tHaptic, uint8_t tEnd) +{ + t_queueToneStart = tStart; + t_queueToneLength = tLen; + t_queueTonePause = tPause; + t_queueToneHaptic = tHaptic; + t_queueToneRepeat = tRepeat; + t_queueToneEnd = tEnd; + commit(0); +} + +#ifdef FRSKY + +//this is done so the menu selections only plays tone once! +void audioQueue::frskyeventSample(uint8_t e) { + if(frskySample != e) { + aqinit(); //flush the queue + frskyevent(e); + frskySample = e; + } +} + +void audioQueue::frskyevent(uint8_t e) { + // example playASAP(tStart,tLen,tPause,tRepeat,tHaptic,tEnd); + switch(e) { + case AU_FRSKY_WARN1: + playASAP(BEEP_DEFAULT_FREQ+20,25,5,2,1); + break; + case AU_FRSKY_WARN2: + playASAP(BEEP_DEFAULT_FREQ+30,25,5,2,1); + break; + case AU_FRSKY_CHEEP: + playASAP(BEEP_DEFAULT_FREQ+30,20,2,2,1,BEEP_DEFAULT_FREQ+25); + break; + case AU_FRSKY_RING: + playASAP(BEEP_DEFAULT_FREQ+25,2,2,10,1); + playASAP(BEEP_DEFAULT_FREQ+25,2,10,1,1); + playASAP(BEEP_DEFAULT_FREQ+25,2,2,10,1); + break; + case AU_FRSKY_SCIFI: + playASAP(80,4,3,2,0,70); + playASAP(60,4,3,2,0,70); + playASAP(70,2,1,0,2); + break; + case AU_FRSKY_ROBOT: + playASAP(70,2,1,1,1); + playASAP(50,6,2,1,1); + playASAP(80,6,2,1,1); + break; + case AU_FRSKY_CHIRP: + + playASAP(BEEP_DEFAULT_FREQ+40,2,1,2,1); + playASAP(BEEP_DEFAULT_FREQ+54,2,1,3,1); + break; + case AU_FRSKY_TADA: + playASAP(50,10,5); + playASAP(90,10,5); + playASAP(110,6,4,2); + break; + case AU_FRSKY_CRICKET: + playASAP(80,1,10,3,1); + playASAP(80,1,20,1,1); + playASAP(80,1,10,3,1); + break; + case AU_FRSKY_SIREN: + playASAP(10,5,5,2,1,40); + break; + case AU_FRSKY_ALARMC: + playASAP(50,5,10,2,1); + playASAP(70,5,20,1,1); + playASAP(50,5,10,2,1); + playASAP(70,5,20,1,1); + break; + case AU_FRSKY_RATATA: + playASAP(BEEP_DEFAULT_FREQ+50,2,10,10,1); + break; + case AU_FRSKY_TICK: + playASAP(BEEP_DEFAULT_FREQ+50,2,50,2,1); + break; + case AU_FRSKY_HAPTIC1: + playASAP(0,2,10,1,1); + break; + case AU_FRSKY_HAPTIC2: + playASAP(0,2,10,2,1); + break; + case AU_FRSKY_HAPTIC3: + playASAP(0,2,10,3,1); + break; + default: + break; + } + +} +#endif + +void audioQueue::event(uint8_t e, uint8_t f) { + + uint8_t beepVal = g_eeGeneral.beeperVal; + + switch (e) { + //startup tune + // case 0: + case AU_TADA: + playASAP(50, 10, 5); + playASAP(90, 10, 5); + playASAP(110, 6, 4, 2); + break; + + //warning one + // case 1: + case AU_WARNING1: + playNow(BEEP_DEFAULT_FREQ, 25, 1, 0, 1); + break; + + //warning two + //case 2: + case AU_WARNING2: + playNow(BEEP_DEFAULT_FREQ, 34, 1, 0, 1); + break; + + //warning three + //case 3: + case AU_WARNING3: + playNow(BEEP_DEFAULT_FREQ, 15, 1, 0, 1); + break; + + //error + //case 4: + case AU_ERROR: + playNow(BEEP_DEFAULT_FREQ, 30, 1, 0, 1); + break; + + //keypad up (seems to be used when going left/right through system menu options. 0-100 scales etc) + //case 5: + case AU_KEYPAD_UP: + if (beepVal != BEEP_NOKEYS) { + playNow(BEEP_KEY_UP_FREQ, 2, 1); + } + break; + + //keypad down (seems to be used when going left/right through system menu options. 0-100 scales etc) + //case 6: + case AU_KEYPAD_DOWN: + if (beepVal != BEEP_NOKEYS) { + playNow(BEEP_KEY_DOWN_FREQ, 2, 1); + } + break; + + //trim sticks move + //case 7: + case AU_TRIM_MOVE: + //if(beepVal != BEEP_NOKEYS){ + playNow(f, 2, 1); + //} + break; + + //trim sticks center + //case 8: + case AU_TRIM_MIDDLE: + //if(beepVal != BEEP_NOKEYS){ + playNow(BEEP_DEFAULT_FREQ, 10, 2, 0, 1); + //} + break; + + //menu display (also used by a few generic beeps) + //case 9: + case AU_MENUS: + if (beepVal != BEEP_NOKEYS) { + playNow(BEEP_DEFAULT_FREQ, 2, 2, 0, 1); + } + break; + //pot/stick center + //case 10: + case AU_POT_STICK_MIDDLE: + playNow(BEEP_DEFAULT_FREQ + 50, 3, 1, 0, 1); + break; + + //mix warning 1 + //case 11: + case AU_MIX_WARNING_1: + playNow(BEEP_DEFAULT_FREQ + 50, 2, 1, 1, 1); + break; + + //mix warning 2 + //case 12: + case AU_MIX_WARNING_2: + playNow(BEEP_DEFAULT_FREQ + 52, 2, 1, 2, 1); + break; + + //mix warning 3 + //case 13: + case AU_MIX_WARNING_3: + playNow(BEEP_DEFAULT_FREQ + 54, 2, 1, 3, 1); + break; + + //time 30 seconds left + //case 14: + case AU_TIMER_30: + playNow(BEEP_DEFAULT_FREQ + 50, 5, 3, 3, 1); + break; + + //time 20 seconds left + //case 15: + case AU_TIMER_20: + playNow(BEEP_DEFAULT_FREQ + 50, 5, 3, 2, 1); + break; + + //time 10 seconds left + //case 16: + case AU_TIMER_10: + playNow(BEEP_DEFAULT_FREQ + 50, 5, 3, 1, 1); + break; + + //time <3 seconds left + //case 17: + case AU_TIMER_LT3: + playNow(BEEP_DEFAULT_FREQ, 20, 5, 1, 1); + break; + + //inactivity timer alert + //case 18: + case AU_INACTIVITY: + playASAP(70, 3, 2); + playASAP(50, 3, 5); + break; + + //low battery in tx + //case 19: + case AU_TX_BATTERY_LOW: + playASAP(60, 4, 3, 2, 1, 70); + playASAP(80, 4, 3, 2, 1, 70); + break; + + default: + break; + + } + +} + +void audioDefevent(uint8_t e) { + audio.event(e, BEEP_DEFAULT_FREQ); +} + diff --git a/src/audio.h b/src/audio.h new file mode 100644 index 000000000..c864e3057 --- /dev/null +++ b/src/audio.h @@ -0,0 +1,193 @@ +/* + * Author - Rob Thomson + * + * 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 + +//audio +#define AUDIO_QUEUE_LENGTH (8) //8 seems to suit most alerts +#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) + +#define AUDIO_QUEUE_HEARTBEAT_XSHORT (30) //speaker timing [Norm] +#define AUDIO_QUEUE_HEARTBEAT_SHORT (50) //speaker timing [Norm] +#define AUDIO_QUEUE_HEARTBEAT_NORM (77) //speaker timing [Norm] +#define AUDIO_QUEUE_HEARTBEAT_LONG (130) //speaker timing [Long] +#define AUDIO_QUEUE_HEARTBEAT_XLONG (200) //speaker timing [xLong] + + + +#define HAPTIC_ON PORTG |= (1<<2) +#define HAPTIC_OFF PORTG &= ~(1<<2) + +/* make sure the defines below always go in numeric order */ +#define AU_TADA (0) +#define AU_WARNING1 (1) +#define AU_WARNING2 (2) +#define AU_WARNING3 (3) +#define AU_ERROR (4) +#define AU_KEYPAD_UP (5) +#define AU_KEYPAD_DOWN (6) +#define AU_TRIM_MOVE (7) +#define AU_TRIM_MIDDLE (8) +#define AU_MENUS (9) +#define AU_POT_STICK_MIDDLE (10) +#define AU_MIX_WARNING_1 (11) +#define AU_MIX_WARNING_2 (12) +#define AU_MIX_WARNING_3 (13) +#define AU_TIMER_30 (14) +#define AU_TIMER_20 (15) +#define AU_TIMER_10 (16) +#define AU_TIMER_LT3 (17) +#define AU_INACTIVITY (18) +#define AU_TX_BATTERY_LOW (19) + +#ifdef FRSKY + +#define AU_FRSKY_WARN1 (0) +#define AU_FRSKY_WARN2 (1) +#define AU_FRSKY_CHEEP (2) +#define AU_FRSKY_RING (3) +#define AU_FRSKY_SCIFI (4) +#define AU_FRSKY_ROBOT (5) +#define AU_FRSKY_CHIRP (6) +#define AU_FRSKY_TADA (7) +#define AU_FRSKY_CRICKET (8) +#define AU_FRSKY_SIREN (9) +#define AU_FRSKY_ALARMC (10) +#define AU_FRSKY_RATATA (11) +#define AU_FRSKY_TICK (12) +#define AU_FRSKY_HAPTIC1 (13) +#define AU_FRSKY_HAPTIC2 (14) +#define AU_FRSKY_HAPTIC3 (15) +#endif + + + +#define BEEP_QUIET (0) +#define BEEP_NOKEYS (1) +#define BEEP_XSHORT (2) +#define BEEP_SHORT (3) +#define BEEP_NORMAL (4) +#define BEEP_LONG (5) +#define BEEP_XLONG (6) + + +struct audioQueue{ + + + //queue temporaries + uint8_t t_queueToneStart; + uint8_t t_queueToneEnd; + uint8_t t_queueToneLength; + uint8_t t_queueTonePause; + uint8_t t_queueToneRepeat; + uint8_t t_queueToneHaptic; + + //queue general vars + uint8_t toneFreq; + uint8_t toneFreqEnd; + uint8_t toneTimeLeft; + int8_t rateOfChange; + uint8_t tonePause; + uint8_t queueState; + uint8_t toneRepeat; + uint8_t toneRepeatCnt; + uint8_t inToneRepeat; + uint8_t toneHaptic; + uint8_t hapticTick; + uint8_t heartbeatTimer; + + +#ifdef FRSKY + uint8_t frskySample; +#endif + + //queue arrays + uint8_t queueToneStart[AUDIO_QUEUE_LENGTH]; + uint8_t queueToneEnd[AUDIO_QUEUE_LENGTH]; + uint8_t queueToneLength[AUDIO_QUEUE_LENGTH]; + uint8_t queueTonePause[AUDIO_QUEUE_LENGTH]; + uint8_t queueToneRepeat[AUDIO_QUEUE_LENGTH]; + uint8_t queueToneHaptic[AUDIO_QUEUE_LENGTH]; + + //beep length table +// uint8_t beepLenTable[10]; + + +public: + //constructor + audioQueue(); + + void aqinit() ; // To stop constructor being compiled twice + + //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 tStart,uint8_t tLen,uint8_t tPause,uint8_t tRepeat=0,uint8_t tHaptic=0,uint8_t tEnd=0); + void playASAP(uint8_t tStart,uint8_t tLen,uint8_t tPause,uint8_t tRepeat=0,uint8_t tHaptic=0,uint8_t tEnd=0); + bool busy(); + + void event(uint8_t e,uint8_t f=BEEP_DEFAULT_FREQ); + +#ifdef FRSKY + void frskyevent(uint8_t e); + void frskyeventSample(uint8_t e); +#endif + + void commit( uint8_t toneInterupt ); + + //set all temporary buffers to default + void flushTemp(); + void flushqueue( uint8_t startpos ) ; + + void restack(); + //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); +}; + +//wrapper function - dirty but results in a space saving!!! +extern audioQueue audio; +//void audioevent(uint8_t e,uint8_t f=BEEP_DEFAULT_FREQ); +void audioDefevent(uint8_t e); + +void audioError(); + +#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_3() audioDefevent(AU_MIX_WARNING_3) + + +#define AUDIO_HEARTBEAT() audio.heartbeat() + + +#endif // audio_h + diff --git a/src/beeper.cpp b/src/beeper.cpp new file mode 100644 index 000000000..a8cf7c723 --- /dev/null +++ b/src/beeper.cpp @@ -0,0 +1,45 @@ +/* + * Authors (alphabetical order) + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * + * gruvin9x is based on code named er9x by + * Author - Erez Raviv , which is in turn + * was based on 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" + +uint8_t g_beepCnt; +uint8_t beepAgain = 0; +uint8_t beepAgainOrig = 0; +uint8_t beepOn = false; + +// The various "beep" tone lengths +static prog_uint8_t APM beepTab[]= { + // 0 1 2 3 4 + 0, 1, 8, 30, 100, //silent + 1, 1, 8, 30, 100, //xShort + 1, 1, 8, 30, 100, //short + 1, 1, 8, 30, 100, //normal + 1, 1, 15, 50, 150, //long + 10, 10, 30, 50, 150, //xLong +}; + +void beep(uint8_t val) +{ + if (g_eeGeneral.beeperVal>0) { + _beep(pgm_read_byte(beepTab+5*g_eeGeneral.beeperVal-5+val)); + } +} diff --git a/src/beeper.h b/src/beeper.h new file mode 100644 index 000000000..8532a246e --- /dev/null +++ b/src/beeper.h @@ -0,0 +1,94 @@ +/* + * Authors (alphabetical order) + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * + * gruvin9x is based on code named er9x by + * Author - Erez Raviv , which is in turn + * was based on 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 BEEPER_H +#define BEEPER_H + +extern uint8_t g_beepCnt; +extern uint8_t g_beepVal[5]; +extern uint8_t beepAgain; +extern uint8_t beepAgainOrig; +extern uint8_t beepOn; + +extern void beep(uint8_t val); + +#define AUDIO_KEYPAD_UP() beep(0) +#define AUDIO_KEYPAD_DOWN() beep(0) +#define AUDIO_MENUS() beep(0) +#define AUDIO_TRIM() beep(1) +#define AUDIO_WARNING2() beep(2) +#define AUDIO_WARNING1() beep(3) +#define AUDIO_ERROR() beep(4) + +#define IS_AUDIO_BUSY() (g_beepCnt || beepAgain || beepOn) + +#define AUDIO_TIMER_30() { beepAgain=2; beep(2); } +#define AUDIO_TIMER_20() { beepAgain=1; beep(2); } +#define AUDIO_TIMER_10() beep(2) +#define AUDIO_TIMER_LT3() beep(2) +#define AUDIO_MINUTE_BEEP() beep(2) +#define AUDIO_INACTIVITY() beep(3) +#define AUDIO_MIX_WARNING_1() beep(1) +#define AUDIO_MIX_WARNING_3() beep(1) + +inline void AUDIO_HEARTBEAT() +{ + //cnt >/=0 + //beepon/off + //beepagain y/n + if(g_beepCnt) { + if(!beepAgainOrig) { + beepAgainOrig = g_beepCnt; + beepOn = true; + } + g_beepCnt--; + } + else + { + if(beepAgain && beepAgainOrig) { + beepOn = !beepOn; + g_beepCnt = beepOn ? beepAgainOrig : 8; + if(beepOn) beepAgain--; + } + else { + beepAgainOrig = 0; + beepOn = false; + warble = false; + } + } + + // G: use original external buzzer for beeps + if (beepOn) { + static bool warbleC; + warbleC = warble && !warbleC; + if (warbleC) + PORTE &= ~(1< @@ -20,6 +19,7 @@ * */ +#ifndef file_h #define file_h #include diff --git a/src/general_menus.cpp b/src/general_menus.cpp index 7c39e9afd..74313b1db 100644 --- a/src/general_menus.cpp +++ b/src/general_menus.cpp @@ -56,34 +56,65 @@ MenuFuncP_PROGMEM APM menuTabDiag[] = { menuProcDiagCalib }; +enum menuProcSetupItems { + ITEM_SETUP_BASE=17, +#ifdef SPLASH + ITEM_SETUP_SPLASH, +#endif +#ifdef BEEPSPKR + ITEM_SETUP_SPEAKER, +#endif +#ifdef HAPTIC + ITEM_SETUP_HAPTIC, +#endif + ITEM_SETUP_MAX +}; + void menuProcSetup(uint8_t event) { -#ifdef SPLASH -#define COUNT_ITEMS 19 -#else -#define COUNT_ITEMS 18 -#endif - #undef PARAM_OFS #define PARAM_OFS 17*FW - SIMPLE_MENU(STR_MENURADIOSETUP, menuTabDiag, e_Setup, COUNT_ITEMS+1); + SIMPLE_MENU(STR_MENURADIOSETUP, menuTabDiag, e_Setup, ITEM_SETUP_MAX+1); int8_t sub = m_posVert; // last 2 lines (radio mode) are non break-able - if(s_pgOfs==COUNT_ITEMS-7) s_pgOfs= sub<(COUNT_ITEMS-4) ? COUNT_ITEMS-8 : COUNT_ITEMS-6; + // TODO line to be checked + if(s_pgOfs==ITEM_SETUP_MAX-7) s_pgOfs= sub<(ITEM_SETUP_MAX-4) ? ITEM_SETUP_MAX-8 : ITEM_SETUP_MAX-6; uint8_t y = 1*FH; uint8_t subN = 1; if(s_pgOfs7*FH) return; }subN++; +#ifdef BEEPSPKR + if(s_pgOfs7*FH) return; + }subN++; +#endif + +#ifdef HAPTIC + if(s_pgOfs7*FH) return; + }subN++; +#endif + if(s_pgOfs 0) { f_close(&g_oLogFile); - beepWarn2(); + AUDIO_WARNING2(); g_logState = 0; } } diff --git a/src/main_views.cpp b/src/main_views.cpp index 80ec2be08..cf8a6f948 100644 --- a/src/main_views.cpp +++ b/src/main_views.cpp @@ -101,12 +101,14 @@ void menuMainView(uint8_t event) switch(event) { + /* TODO if timer2 is OFF, it's possible to use this timer2 as in er9x... case EVT_KEY_BREAK(KEY_MENU): if (view_base == e_timer2) { - // TODO Timer2_running = !Timer2_running; - beepKey(); + Timer2_running = !Timer2_running; + AUDIO_KEYPAD_UP(); } break; + */ case EVT_KEY_LONG(KEY_MENU):// go to last menu pushMenu(lastPopMenu()); killEvents(event); @@ -124,7 +126,7 @@ void menuMainView(uint8_t event) #endif g_eeGeneral.view = (view + (event == EVT_KEY_BREAK(KEY_RIGHT) ? ALTERNATE_VIEW : tabViews[view_base]*ALTERNATE_VIEW-ALTERNATE_VIEW)) % (tabViews[view_base]*ALTERNATE_VIEW); eeDirty(EE_GENERAL); - beepKey(); + AUDIO_KEYPAD_UP(); break; case EVT_KEY_LONG(KEY_RIGHT): pushMenu(menuProcModelSelect); @@ -138,7 +140,7 @@ void menuMainView(uint8_t event) g_eeGeneral.view = view+1; if(g_eeGeneral.view>=MAX_VIEWS) g_eeGeneral.view=0; eeDirty(EE_GENERAL); - beepKey(); + AUDIO_KEYPAD_UP(); break; case EVT_KEY_BREAK(KEY_DOWN): if(view>0) @@ -146,7 +148,7 @@ void menuMainView(uint8_t event) else g_eeGeneral.view = MAX_VIEWS-1; eeDirty(EE_GENERAL); - beepKey(); + AUDIO_KEYPAD_UP(); break; case EVT_KEY_LONG(KEY_UP): chainMenu(menuProcStatistic); @@ -176,7 +178,7 @@ void menuMainView(uint8_t event) else { resetTimer(0); } - beepKey(); + AUDIO_KEYPAD_UP(); break; case EVT_KEY_LONG(KEY_EXIT): resetTimer(0); @@ -184,7 +186,7 @@ void menuMainView(uint8_t event) #ifdef FRSKY resetTelemetry(); #endif - beepKey(); + AUDIO_KEYPAD_UP(); break; case EVT_ENTRY: killEvents(KEY_EXIT); diff --git a/src/menus.cpp b/src/menus.cpp index d6291724b..d685cd185 100644 --- a/src/menus.cpp +++ b/src/menus.cpp @@ -63,19 +63,12 @@ int16_t checkIncDec(uint8_t event, int16_t val, int16_t i_min, int16_t i_max, ui } if(event==EVT_KEY_FIRST(kpl) || event== EVT_KEY_REPT(kpl) || (s_editMode>0 && (event==EVT_KEY_FIRST(KEY_UP) || event== EVT_KEY_REPT(KEY_UP))) ) { newval++; -#if defined (BEEPSPKR) - beepKeySpkr(BEEP_KEY_UP_FREQ); -#else - beepKey(); -#endif + AUDIO_KEYPAD_UP(); kother=kmi; - }else if(event==EVT_KEY_FIRST(kmi) || event== EVT_KEY_REPT(kmi) || (s_editMode>0 && (event==EVT_KEY_FIRST(KEY_DOWN) || event== EVT_KEY_REPT(KEY_DOWN))) ) { + } + else if(event==EVT_KEY_FIRST(kmi) || event== EVT_KEY_REPT(kmi) || (s_editMode>0 && (event==EVT_KEY_FIRST(KEY_DOWN) || event== EVT_KEY_REPT(KEY_DOWN))) ) { newval--; -#if defined (BEEPSPKR) - beepKeySpkr(BEEP_KEY_DOWN_FREQ); -#else - beepKey(); -#endif + AUDIO_KEYPAD_DOWN(); kother=kpl; } if((kother != (uint8_t)-1) && keyState((EnumKeys)kother)){ @@ -98,33 +91,21 @@ int16_t checkIncDec(uint8_t event, int16_t val, int16_t i_min, int16_t i_max, ui { newval = i_max; killEvents(event); -#if defined (BEEPSPKR) - beepWarn2Spkr(BEEP_KEY_UP_FREQ); -#else - beepWarn2(); -#endif + AUDIO_WARNING2(); } if(newval < i_min) { newval = i_min; killEvents(event); -#if defined (BEEPSPKR) - beepWarn2Spkr(BEEP_KEY_DOWN_FREQ); -#else - beepWarn2(); -#endif + AUDIO_WARNING2(); } if(newval != val){ if(newval==0) { pauseEvents(event); // delay before auto-repeat continues -#if defined (BEEPSPKR) - if (newval>val) - beepWarn2Spkr(BEEP_KEY_UP_FREQ); + if (newval>val) // TODO check if without BEEPSPKR it's optimized! + AUDIO_KEYPAD_UP(); else - beepWarn2Spkr(BEEP_KEY_DOWN_FREQ); -#else - beepKey(); -#endif + AUDIO_KEYPAD_DOWN(); } eeDirty(i_flags & (EE_GENERAL|EE_MODEL)); checkIncDec_Ret = (newval > val ? 1 : -1); @@ -318,8 +299,9 @@ bool check(uint8_t event, uint8_t curr, MenuFuncP *menuTab, uint8_t menuTabSize, popMenu(); //beeps itself } else { - beepKey(); - minit();BLINK_SYNC; + AUDIO_MENUS(); + minit(); + BLINK_SYNC; } break; @@ -380,7 +362,7 @@ void popMenu() { if (g_menuStackPtr>0) { g_menuStackPtr = g_menuStackPtr-1; - beepKey(); + AUDIO_KEYPAD_UP(); m_posHorz = 0; m_posVert = g_menuPos[g_menuStackPtr]; (*g_menuStack[g_menuStackPtr])(EVT_ENTRY_UP); @@ -394,7 +376,7 @@ void chainMenu(MenuFuncP newMenu) { g_menuStack[g_menuStackPtr] = newMenu; (*newMenu)(EVT_ENTRY); - beepKey(); + AUDIO_MENUS(); } void pushMenu(MenuFuncP newMenu) @@ -408,7 +390,7 @@ void pushMenu(MenuFuncP newMenu) alert(STR_MENUSERROR); return; } - beepKey(); + AUDIO_MENUS(); g_menuStack[g_menuStackPtr] = newMenu; (*newMenu)(EVT_ENTRY); } diff --git a/src/model_menus.cpp b/src/model_menus.cpp index 6df068209..d9e8b304b 100644 --- a/src/model_menus.cpp +++ b/src/model_menus.cpp @@ -264,7 +264,7 @@ void menuProcModelSelect(uint8_t event) chainMenu(_event == EVT_KEY_FIRST(KEY_RIGHT) ? menuProcModel : menuTabModel[DIM(menuTabModel)-1]); return; } - beepWarn(); + AUDIO_WARNING1(); // TODO it was beepWarn(); break; case EVT_KEY_FIRST(KEY_UP): case EVT_KEY_FIRST(KEY_DOWN): @@ -279,7 +279,7 @@ void menuProcModelSelect(uint8_t event) m_posVert = eeFindEmptyModel(s_copySrcRow, _event==EVT_KEY_FIRST(KEY_DOWN)); if (m_posVert == (uint8_t)-1) { // no free room for duplicating the model - beepWarn(); + AUDIO_WARNING1(); m_posVert = oldSub; s_copyMode = 0; // TODO only this one? s_copyTgtOfs = 0; @@ -1926,7 +1926,7 @@ void menuProcTemplates(uint8_t event) applyTemplate(sub); if (sub==NUM_TEMPLATES) clearMixes(); - beepWarn1(); + AUDIO_WARNING1(); break; } diff --git a/src/myeeprom.h b/src/myeeprom.h index 570301690..fbadc00d7 100644 --- a/src/myeeprom.h +++ b/src/myeeprom.h @@ -95,6 +95,8 @@ PACK(typedef struct t_EEGeneral { uint8_t templateSetup; //RETA order according to chout_ar array int8_t PPM_Multiplier; FrSkyRSSIAlarm frskyRssiAlarms[2]; + uint8_t speakerPitch; + uint8_t hapticStrength; }) EEGeneral; // eeprom modelspec diff --git a/src/open9x.cpp b/src/open9x.cpp index be460dc97..89ff1f5de 100644 --- a/src/open9x.cpp +++ b/src/open9x.cpp @@ -56,7 +56,12 @@ uint8_t toneFreq = BEEP_DEFAULT_FREQ; uint8_t toneOn = false; #endif -bool warble = false; +bool warble = false; // TODO is it needed with BEEPSPKR? + +#ifdef BEEPSPKR +//new audio object +audioQueue audio; +#endif uint8_t heartbeat; @@ -651,9 +656,6 @@ void checkSwitches() } } -uint8_t g_beepCnt; -uint8_t g_beepVal[5]; - void message(const prog_char * s) { lcd_clear(); @@ -671,7 +673,7 @@ void alert(const prog_char * s, bool defaults) lcd_puts_P(64-LEN_PRESSANYKEY*FW/2, 7*FH, STR_PRESSANYKEY); refreshDisplay(); lcdSetRefVolt(defaults ? 25 : g_eeGeneral.contrast); - beepErr(); + AUDIO_ERROR(); clearKeyEvents(); while(1) { @@ -742,17 +744,17 @@ uint8_t checkTrim(uint8_t event) killEvents(event); warble = false; #if defined (BEEPSPKR) - beepWarn2Spkr(after); + audio.event(AU_TRIM_MOVE, after); #else - beepWarn2(); + AUDIO_WARNING2(); #endif } else { if (event & _MSK_KEY_REPT) warble = true; #if defined (BEEPSPKR) - beepTrimSpkr(after); + audio.event(AU_TRIM_MOVE, after); #else - beepWarn1(); + AUDIO_TRIM(); #endif } return 0; @@ -886,10 +888,6 @@ uint16_t g_vbat100mV = 0; volatile uint8_t tick10ms = 0; uint16_t g_LightOffCounter; -uint8_t beepAgain = 0; -uint8_t beepAgainOrig = 0; -uint8_t beepOn = false; - FORCEINLINE bool checkSlaveMode() { // no power -> only phone jack = slave mode @@ -899,7 +897,7 @@ FORCEINLINE bool checkSlaveMode() #else static bool lastSlaveMode = false; static uint8_t checkDelay = 0; - if (g_beepCnt || beepAgain || beepOn) { + if (IS_AUDIO_BUSY()) { checkDelay = 20; } else if (checkDelay) { @@ -1085,7 +1083,7 @@ void perOut(int16_t *chanOut, uint8_t phase) //===========BEEP CENTER================ anaCenter &= g_model.beepANACenter; - if(((bpanaCenter ^ anaCenter) & anaCenter)) beepWarn1(); + if(((bpanaCenter ^ anaCenter) & anaCenter)) AUDIO_WARNING1(); bpanaCenter = anaCenter; anas[MIX_MAX-1] = RESX; // MAX @@ -1571,22 +1569,22 @@ void perMain() if (last_tmr != s_timerVal[0]) { // beep only if seconds advance if (s_timerState[0] == TMR_RUNNING) { if (g_eeGeneral.preBeep && g_model.timer1.val) { // beep when 30, 15, 10, 5,4,3,2,1 seconds remaining - if(s_timerVal[0]==30) {beepAgain=2; beepWarn2();} //beep three times - if(s_timerVal[0]==20) {beepAgain=1; beepWarn2();} //beep two times - if(s_timerVal[0]==10) beepWarn2(); - if(s_timerVal[0]<= 3) beepWarn2(); + if(s_timerVal[0]==30) AUDIO_TIMER_30(); //beep three times + if(s_timerVal[0]==20) AUDIO_TIMER_20(); //beep two times + if(s_timerVal[0]==10) AUDIO_TIMER_10(); + if(s_timerVal[0]<= 3) AUDIO_TIMER_LT3(); if(g_eeGeneral.flashBeep && (s_timerVal[0]==30 || s_timerVal[0]==20 || s_timerVal[0]==10 || s_timerVal[0]<=3)) g_LightOffCounter = FLASH_DURATION; } if (g_eeGeneral.minuteBeep && (((g_model.timer1.val ? g_model.timer1.val-s_timerVal[0] : s_timerVal[0])%60)==0)) { // short beep every minute - beepWarn2(); + AUDIO_MINUTE_BEEP(); if(g_eeGeneral.flashBeep) g_LightOffCounter = FLASH_DURATION; } } else if(s_timerState[0] == TMR_BEEPING) { - beepWarn(); + AUDIO_WARNING1(); if(g_eeGeneral.flashBeep) g_LightOffCounter = FLASH_DURATION; } last_tmr = s_timerVal[0]; @@ -1628,7 +1626,7 @@ void perMain() inacCounter=0; } if(inacCounter>((uint32_t)g_eeGeneral.inactivityTimer*100*60)) - if((inacCounter&0x3F)==10) beepWarn(); + if((inacCounter&0x3F)==10) AUDIO_INACTIVITY(); } if (trimsCheckTimer > 0) @@ -1741,31 +1739,13 @@ Gruvin: static uint8_t s_batCheck; s_batCheck+=32; if(s_batCheck==0 && g_vbat100mV50) { - beepErr(); + AUDIO_ERROR(); if (g_eeGeneral.flashBeep) g_LightOffCounter = FLASH_DURATION; } } break; - - - case 3: - { - // The various "beep" tone lengths - static prog_uint8_t APM beepTab[]= { - // 0 1 2 3 4 - 0, 0, 0, 0, 0, //quiet - 0, 1, 8, 30, 100, //silent - 1, 1, 8, 30, 100, //normal - 1, 1, 15, 50, 150, //for motor - 10, 10, 30, 50, 150, //for motor - }; - memcpy_P(g_beepVal,beepTab+5*g_eeGeneral.beeperVal,5); - //g_beepVal = BEEP_VAL; - } - break; } - } int16_t g_ppmIns[8]; uint8_t ppmInState = 0; //0=unsync 1..8= wait for value i-1 @@ -1809,6 +1789,7 @@ ISR(TIMER0_COMP_vect, ISR_NOBLOCK) //10ms timer #endif { cli(); + #if defined (PCBV3) static uint8_t accuracyWarble = 4; // becasue 16M / 1024 / 100 = 156.25. So bump every 4. uint8_t bump = (!(accuracyWarble++ & 0x03)) ? 157 : 156; @@ -1824,30 +1805,17 @@ ISR(TIMER0_COMP_vect, ISR_NOBLOCK) //10ms timer OCR0 += bump; #endif #endif + sei(); + + AUDIO_HEARTBEAT(); #if defined (PCBSTD) && defined (BEEPSPKR) - // gruvin: Begin Tone Generator - static uint8_t toneCounter; - - if (toneOn) - { - toneCounter += toneFreq; - if ((toneCounter & 0x80) == 0x80) - PORTE |= (1</=0 - //beepon/off - //beepagain y/n - if(g_beepCnt) { - if(!beepAgainOrig) { - beepAgainOrig = g_beepCnt; - beepOn = true; - } - g_beepCnt--; - } - else - { - if(beepAgain && beepAgainOrig) { - beepOn = !beepOn; - g_beepCnt = beepOn ? beepAgainOrig : 8; - if(beepOn) beepAgain--; - } - else { - beepAgainOrig = 0; - beepOn = false; - warble = false; - } - } - -#if defined (PCBV3) && defined (BEEPSPKR) - // G: use timer0 WGM mode tone generator for beeps - if(beepOn) - { - static bool warbleC; - warbleC = warble && !warbleC; - if(warbleC) - TCCR0A &= ~(0b01<= 0: - print states + while 1: + # print index, states for language in languages: hex_file = hex @@ -36,15 +36,15 @@ def generate(hex, arg, options): print hex_file call(["make", "clean"]) call(make_args) - shutil.copyfile("open9x.hex", "../" + hex_file + ".hex") - - for i in range(index): - states[i] = False - states[index] = True + shutil.copyfile("open9x.hex", "../binaries/" + hex_file + ".hex") + try: index = states.index(False) except: - index = -1 + break + for i in range(index): + states[i] = False + states[index] = True # stock board generate("open9x-stock", "PCB=STD", options_stock)