Files
uv-k1-k5v3-firmware-custom/App/audio.c
T
2026-03-31 02:57:20 +02:00

528 lines
13 KiB
C

/* Copyright 2025 muzkr https://github.com/muzkr
* Copyright 2023 Dual Tachyon
* https://github.com/DualTachyon
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef ENABLE_FMRADIO
#include "app/fm.h"
#endif
#include "audio.h"
#ifdef ENABLE_FMRADIO
#include "driver/bk1080.h"
#endif
#include "driver/bk4819.h"
#include "driver/gpio.h"
#include "driver/system.h"
#include "driver/systick.h"
#include "driver/voice.h"
#include "driver/py25q16.h"
#include "functions.h"
#include "misc.h"
#include "settings.h"
#include "ui/ui.h"
BEEP_Type_t gBeepToPlay = BEEP_NONE;
static void AUDIO_PlayBeepPulse(void) {
BK4819_ExitTxMute();
SYSTEM_DelayMs(60);
BK4819_EnterTxMute();
SYSTEM_DelayMs(20);
}
void AUDIO_PlayBeep(BEEP_Type_t Beep)
{
if (Beep != BEEP_880HZ_60MS_DOUBLE_BEEP &&
Beep != BEEP_500HZ_60MS_DOUBLE_BEEP &&
Beep != BEEP_440HZ_500MS &&
#ifdef ENABLE_DTMF_CALLING
Beep != BEEP_880HZ_200MS &&
Beep != BEEP_880HZ_500MS &&
#endif
#ifdef ENABLE_FEAT_F4HWN
Beep != BEEP_400HZ_30MS &&
Beep != BEEP_500HZ_30MS &&
Beep != BEEP_600HZ_30MS &&
#endif
!gEeprom.BEEP_CONTROL)
return;
if (gCurrentFunction == FUNCTION_RECEIVE)
return;
if (gCurrentFunction == FUNCTION_MONITOR)
return;
#ifdef ENABLE_FMRADIO
if (gFmRadioMode)
BK1080_Mute(true);
#endif
AUDIO_AudioPathOff();
if (gCurrentFunction == FUNCTION_POWER_SAVE && gRxIdleMode)
BK4819_RX_TurnOn();
SYSTEM_DelayMs(20);
uint16_t ToneConfig = BK4819_ReadRegister(BK4819_REG_71);
uint16_t ToneFrequency;
switch (Beep)
{
default:
case BEEP_NONE:
ToneFrequency = 220;
break;
case BEEP_1KHZ_60MS_OPTIONAL:
ToneFrequency = 1000;
break;
case BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL:
case BEEP_500HZ_60MS_DOUBLE_BEEP:
ToneFrequency = 500;
break;
case BEEP_440HZ_500MS:
ToneFrequency = 440;
break;
case BEEP_880HZ_60MS_DOUBLE_BEEP:
#ifndef ENABLE_FEAT_F4HWN
case BEEP_880HZ_200MS:
case BEEP_880HZ_500MS:
#endif
ToneFrequency = 880;
break;
#ifdef ENABLE_FEAT_F4HWN
case BEEP_400HZ_30MS:
ToneFrequency = 400;
break;
case BEEP_500HZ_30MS:
ToneFrequency = 500;
break;
case BEEP_600HZ_30MS:
ToneFrequency = 600;
break;
#endif
}
if(Beep == BEEP_400HZ_30MS || Beep == BEEP_500HZ_30MS || Beep == BEEP_600HZ_30MS)
{
BK4819_WriteRegister(BK4819_REG_70, BK4819_REG_70_ENABLE_TONE1 | ((1 & 0x7f) << BK4819_REG_70_SHIFT_TONE1_TUNING_GAIN));
}
BK4819_PlayTone(ToneFrequency, true);
SYSTEM_DelayMs(2);
AUDIO_AudioPathOn();
SYSTEM_DelayMs(60);
uint16_t Duration;
switch (Beep)
{
case BEEP_880HZ_60MS_DOUBLE_BEEP:
AUDIO_PlayBeepPulse();
[[fallthrough]];
case BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL:
case BEEP_500HZ_60MS_DOUBLE_BEEP:
AUDIO_PlayBeepPulse();
[[fallthrough]];
case BEEP_1KHZ_60MS_OPTIONAL:
BK4819_ExitTxMute();
Duration = 60;
break;
#ifdef ENABLE_FEAT_F4HWN
case BEEP_400HZ_30MS:
case BEEP_500HZ_30MS:
case BEEP_600HZ_30MS:
BK4819_ExitTxMute();
Duration = 30;
break;
#endif
case BEEP_440HZ_500MS:
#ifndef ENABLE_FEAT_F4HWN
case BEEP_880HZ_200MS:
BK4819_ExitTxMute();
Duration = 200;
break;
case BEEP_880HZ_500MS:
#endif
default:
BK4819_ExitTxMute();
Duration = 500;
break;
}
SYSTEM_DelayMs(Duration);
BK4819_EnterTxMute();
SYSTEM_DelayMs(20);
AUDIO_AudioPathOff();
SYSTEM_DelayMs(5);
BK4819_TurnsOffTones_TurnsOnRX();
SYSTEM_DelayMs(5);
BK4819_WriteRegister(BK4819_REG_71, ToneConfig);
#ifdef ENABLE_FMRADIO
const bool isFmRadio = gFmRadioMode;
if (isFmRadio)
SYSTEM_DelayMs(10);
#endif
if (gEnableSpeaker)
AUDIO_AudioPathOn();
#ifdef ENABLE_FMRADIO
if (isFmRadio)
BK1080_Mute(false);
#endif
if (gCurrentFunction == FUNCTION_POWER_SAVE && gRxIdleMode)
BK4819_Sleep();
#ifdef ENABLE_VOX
gVoxResumeCountdown = 80;
#endif
}
#ifdef ENABLE_VOICE
uint16_t gVoiceBuf[VOICE_BUF_CAP][VOICE_BUF_LEN];
uint8_t gVoiceBufReadIndex = 0;
uint8_t gVoiceBufWriteIndex = 0;
uint8_t gVoiceBufLen = 0;
VOICE_ID_t gVoiceID[8];
uint8_t gVoiceReadIndex;
uint8_t gVoiceWriteIndex;
volatile uint16_t gCountdownToPlayNextVoice_10ms;
volatile bool gFlagPlayQueuedVoice;
VOICE_ID_t gAnotherVoiceID = VOICE_ID_INVALID;
static const uint16_t VOICE_SAMPLES[256] =
{
0x06a8, 0x06b8, 0x0688, 0x0698, 0x06e8, 0x06f8, 0x06c8, 0x06d8, //
0x0628, 0x0638, 0x0608, 0x0618, 0x0668, 0x0678, 0x0648, 0x0658, //
0x0754, 0x075c, 0x0744, 0x074c, 0x0774, 0x077c, 0x0764, 0x076c, //
0x0714, 0x071c, 0x0704, 0x070c, 0x0734, 0x073c, 0x0724, 0x072c, //
0x02a0, 0x02e0, 0x0220, 0x0260, 0x03a0, 0x03e0, 0x0320, 0x0360, //
0x00a0, 0x00e0, 0x0020, 0x0060, 0x01a0, 0x01e0, 0x0120, 0x0160, //
0x0550, 0x0570, 0x0510, 0x0530, 0x05d0, 0x05f0, 0x0590, 0x05b0, //
0x0450, 0x0470, 0x0410, 0x0430, 0x04d0, 0x04f0, 0x0490, 0x04b0, //
0x07ea, 0x07eb, 0x07e8, 0x07e9, 0x07ee, 0x07ef, 0x07ec, 0x07ed, //
0x07e2, 0x07e3, 0x07e0, 0x07e1, 0x07e6, 0x07e7, 0x07e4, 0x07e5, //
0x07fa, 0x07fb, 0x07f8, 0x07f9, 0x07fe, 0x07ff, 0x07fc, 0x07fd, //
0x07f2, 0x07f3, 0x07f0, 0x07f1, 0x07f6, 0x07f7, 0x07f4, 0x07f5, //
0x07aa, 0x07ae, 0x07a2, 0x07a6, 0x07ba, 0x07be, 0x07b2, 0x07b6, //
0x078a, 0x078e, 0x0782, 0x0786, 0x079a, 0x079e, 0x0792, 0x0796, //
0x07d5, 0x07d7, 0x07d1, 0x07d3, 0x07dd, 0x07df, 0x07d9, 0x07db, //
0x07c5, 0x07c7, 0x07c1, 0x07c3, 0x07cd, 0x07cf, 0x07c9, 0x07cb, //
0x0958, 0x0948, 0x0978, 0x0968, 0x0918, 0x0908, 0x0938, 0x0928, //
0x09d8, 0x09c8, 0x09f8, 0x09e8, 0x0998, 0x0988, 0x09b8, 0x09a8, //
0x08ac, 0x08a4, 0x08bc, 0x08b4, 0x088c, 0x0884, 0x089c, 0x0894, //
0x08ec, 0x08e4, 0x08fc, 0x08f4, 0x08cc, 0x08c4, 0x08dc, 0x08d4, //
0x0d60, 0x0d20, 0x0de0, 0x0da0, 0x0c60, 0x0c20, 0x0ce0, 0x0ca0, //
0x0f60, 0x0f20, 0x0fe0, 0x0fa0, 0x0e60, 0x0e20, 0x0ee0, 0x0ea0, //
0x0ab0, 0x0a90, 0x0af0, 0x0ad0, 0x0a30, 0x0a10, 0x0a70, 0x0a50, //
0x0bb0, 0x0b90, 0x0bf0, 0x0bd0, 0x0b30, 0x0b10, 0x0b70, 0x0b50, //
0x0815, 0x0814, 0x0817, 0x0816, 0x0811, 0x0810, 0x0813, 0x0812, //
0x081d, 0x081c, 0x081f, 0x081e, 0x0819, 0x0818, 0x081b, 0x081a, //
0x0805, 0x0804, 0x0807, 0x0806, 0x0801, 0x0800, 0x0803, 0x0802, //
0x080d, 0x080c, 0x080f, 0x080e, 0x0809, 0x0808, 0x080b, 0x080a, //
0x0856, 0x0852, 0x085e, 0x085a, 0x0846, 0x0842, 0x084e, 0x084a, //
0x0876, 0x0872, 0x087e, 0x087a, 0x0866, 0x0862, 0x086e, 0x086a, //
0x082b, 0x0829, 0x082f, 0x082d, 0x0823, 0x0821, 0x0827, 0x0825, //
0x083b, 0x0839, 0x083f, 0x083d, 0x0833, 0x0831, 0x0837, 0x0835 //
};
static struct
{
uint32_t Addr;
uint32_t Size;
} VoiceClipState = {0};
static bool LoadVoiceClip(uint8_t VoiceID)
{
if (VoiceID >= VOICE_ID_END)
{
return false;
}
uint32_t Addr = gEeprom.VOICE_PROMPT == VOICE_PROMPT_CHINESE ? 0x14c000 : 0x14c800;
struct
{
uint32_t Offset;
uint32_t Size;
} Info;
PY25Q16_ReadBuffer(Addr + 8 * VoiceID, &Info, 8);
if (Info.Offset > 0x0b0000 || Info.Size > 0x019000)
{
return false;
}
VoiceClipState.Addr = 0x14d000 + Info.Offset;
VoiceClipState.Size = Info.Size;
return true;
}
static inline uint32_t CalcDelay(uint32_t Size)
{
return 2 * Size; // in ms!!
}
static void LoadVoiceSamples()
{
if (0 == VoiceClipState.Addr || 0 == VoiceClipState.Size)
{
return;
}
if (gVoiceBufLen >= VOICE_BUF_CAP)
{
return;
}
extern uint8_t **gFrameBuffer;
uint8_t *Buf = (uint8_t *)gFrameBuffer;
PY25Q16_ReadBuffer(VoiceClipState.Addr, Buf, VOICE_BUF_LEN);
VoiceClipState.Addr += VOICE_BUF_LEN;
VoiceClipState.Size -= VOICE_BUF_LEN;
for (uint32_t i = 0; i < VOICE_BUF_LEN; i++)
{
gVoiceBuf[gVoiceBufWriteIndex][i] = VOICE_SAMPLES[Buf[i]];
}
VOICE_BUF_ForwardWriteIndex();
gVoiceBufLen++;
}
void AUDIO_PlaySingleVoice(bool bFlag)
{
uint8_t VoiceID;
uint8_t Delay;
VoiceID = gVoiceID[0];
if (gEeprom.VOICE_PROMPT != VOICE_PROMPT_OFF && gVoiceWriteIndex > 0)
{
if (gEeprom.VOICE_PROMPT == VOICE_PROMPT_CHINESE)
{ // Chinese
if (VoiceID >= ARRAY_SIZE(VoiceClipLengthChinese))
goto Bailout;
Delay = VoiceClipLengthChinese[VoiceID];
VoiceID += VOICE_ID_CHI_BASE;
}
else
{ // English
if (VoiceID >= ARRAY_SIZE(VoiceClipLengthEnglish))
goto Bailout;
Delay = VoiceClipLengthEnglish[VoiceID];
VoiceID += VOICE_ID_ENG_BASE;
}
if (FUNCTION_IsRx()) // 1of11
BK4819_SetAF(BK4819_AF_MUTE);
#ifdef ENABLE_FMRADIO
if (gFmRadioMode)
BK1080_Mute(true);
#endif
AUDIO_AudioPathOn();
#ifdef ENABLE_VOX
gVoxResumeCountdown = 2000;
#endif
SYSTEM_DelayMs(5);
AUDIO_PlayVoice(VoiceID);
if (gVoiceWriteIndex == 1)
Delay += 3;
if (bFlag)
{
SYSTEM_DelayMs(Delay * 10);
if (FUNCTION_IsRx()) // 1of11
RADIO_SetModulation(gRxVfo->Modulation);
#ifdef ENABLE_FMRADIO
if (gFmRadioMode)
BK1080_Mute(false);
#endif
if (!gEnableSpeaker)
AUDIO_AudioPathOff();
gVoiceWriteIndex = 0;
gVoiceReadIndex = 0;
#ifdef ENABLE_VOX
gVoxResumeCountdown = 80;
#endif
return;
}
gVoiceReadIndex = 1;
gCountdownToPlayNextVoice_10ms = Delay;
gFlagPlayQueuedVoice = false;
return;
}
Bailout:
gVoiceReadIndex = 0;
gVoiceWriteIndex = 0;
}
void AUDIO_SetVoiceID(uint8_t Index, VOICE_ID_t VoiceID)
{
if (Index >= ARRAY_SIZE(gVoiceID))
return;
if (Index == 0)
{
gVoiceWriteIndex = 0;
gVoiceReadIndex = 0;
}
gVoiceID[Index] = VoiceID;
gVoiceWriteIndex++;
}
uint8_t AUDIO_SetDigitVoice(uint8_t Index, uint16_t Value)
{
uint16_t Remainder;
uint8_t Result;
uint8_t Count;
if (Index == 0)
{
gVoiceWriteIndex = 0;
gVoiceReadIndex = 0;
}
Count = 0;
Result = Value / 1000U;
Remainder = Value % 1000U;
if (Remainder < 100U)
{
if (Remainder < 10U)
goto Skip;
}
else
{
Result = Remainder / 100U;
gVoiceID[gVoiceWriteIndex++] = (VOICE_ID_t)Result;
Count++;
Remainder -= Result * 100U;
}
Result = Remainder / 10U;
gVoiceID[gVoiceWriteIndex++] = (VOICE_ID_t)Result;
Count++;
Remainder -= Result * 10U;
Skip:
gVoiceID[gVoiceWriteIndex++] = (VOICE_ID_t)Remainder;
return Count + 1U;
}
void AUDIO_PlayQueuedVoice(void)
{
uint8_t VoiceID;
uint8_t Delay;
bool Skip;
Skip = false;
if (gVoiceReadIndex != gVoiceWriteIndex && gEeprom.VOICE_PROMPT != VOICE_PROMPT_OFF)
{
VoiceID = gVoiceID[gVoiceReadIndex];
if (gEeprom.VOICE_PROMPT == VOICE_PROMPT_CHINESE)
{
if (VoiceID < ARRAY_SIZE(VoiceClipLengthChinese))
{
Delay = VoiceClipLengthChinese[VoiceID];
VoiceID += VOICE_ID_CHI_BASE;
}
else
Skip = true;
}
else
{
if (VoiceID < ARRAY_SIZE(VoiceClipLengthEnglish))
{
Delay = VoiceClipLengthEnglish[VoiceID];
VoiceID += VOICE_ID_ENG_BASE;
}
else
Skip = true;
}
gVoiceReadIndex++;
if (!Skip)
{
if (gVoiceReadIndex == gVoiceWriteIndex)
Delay += 3;
AUDIO_PlayVoice(VoiceID);
gCountdownToPlayNextVoice_10ms = Delay;
gFlagPlayQueuedVoice = false;
#ifdef ENABLE_VOX
gVoxResumeCountdown = 2000;
#endif
return;
}
}
if (FUNCTION_IsRx())
{
RADIO_SetModulation(gRxVfo->Modulation); // 1of11
}
#ifdef ENABLE_FMRADIO
if (gFmRadioMode)
BK1080_Mute(false);
#endif
if (!gEnableSpeaker)
AUDIO_AudioPathOff();
#ifdef ENABLE_VOX
gVoxResumeCountdown = 80;
#endif
gVoiceWriteIndex = 0;
gVoiceReadIndex = 0;
}
#endif