|
7 | 7 | #include "freertos/task.h" |
8 | 8 | #include "freertos/timers.h" |
9 | 9 |
|
| 10 | +#include <cmath> |
10 | 11 | #include <cstring> |
11 | 12 | #include <driver/gpio.h> |
12 | 13 | #include <esp_sleep.h> |
@@ -859,6 +860,119 @@ void TLoRaPagerBoard::stopVibrator() |
859 | 860 | log_d("[stopVibrator] Power disabled, function completed"); |
860 | 861 | } |
861 | 862 |
|
| 863 | +void TLoRaPagerBoard::playMessageTone() |
| 864 | +{ |
| 865 | +#ifndef USING_AUDIO_CODEC |
| 866 | + return; |
| 867 | +#else |
| 868 | + if (!(devices_probe & HW_CODEC_ONLINE)) |
| 869 | + { |
| 870 | + return; |
| 871 | + } |
| 872 | + |
| 873 | + static bool s_playing = false; |
| 874 | + static uint32_t s_last_play_ms = 0; |
| 875 | + |
| 876 | + if (s_playing) |
| 877 | + { |
| 878 | + return; |
| 879 | + } |
| 880 | + |
| 881 | + const uint32_t now = millis(); |
| 882 | + if ((now - s_last_play_ms) < 240) |
| 883 | + { |
| 884 | + return; |
| 885 | + } |
| 886 | + |
| 887 | + s_playing = true; |
| 888 | + s_last_play_ms = now; |
| 889 | + |
| 890 | + struct ToneStep |
| 891 | + { |
| 892 | + uint16_t freq_hz; |
| 893 | + uint16_t duration_ms; |
| 894 | + }; |
| 895 | + |
| 896 | + static constexpr ToneStep kTone[] = { |
| 897 | + {1319, 55}, |
| 898 | + {1568, 55}, |
| 899 | + {1976, 90}, |
| 900 | + }; |
| 901 | + static constexpr uint16_t kGapMs = 18; |
| 902 | + static constexpr uint32_t kSampleRate = 16000; |
| 903 | + static constexpr uint8_t kChannels = 2; |
| 904 | + static constexpr size_t kFramesPerChunk = 128; |
| 905 | + static constexpr float kAmplitude = 0.16f; |
| 906 | + static constexpr float kTwoPi = 6.28318530718f; |
| 907 | + |
| 908 | + int prev_volume = codec.getVolume(); |
| 909 | + if (prev_volume < 0 || prev_volume > 100) |
| 910 | + { |
| 911 | + prev_volume = 50; |
| 912 | + } |
| 913 | + bool prev_out_mute = codec.getOutMute(); |
| 914 | + |
| 915 | + bool opened = (codec.open(16, kChannels, kSampleRate) == 0); |
| 916 | + if (!opened) |
| 917 | + { |
| 918 | + s_playing = false; |
| 919 | + return; |
| 920 | + } |
| 921 | + |
| 922 | + codec.setOutMute(false); |
| 923 | + codec.setVolume(static_cast<uint8_t>(prev_volume < 45 ? 45 : prev_volume)); |
| 924 | + |
| 925 | + int16_t pcm[kFramesPerChunk * kChannels]; |
| 926 | + auto write_silence = [&](uint32_t ms) |
| 927 | + { |
| 928 | + uint32_t remaining = (kSampleRate * ms) / 1000; |
| 929 | + while (remaining > 0) |
| 930 | + { |
| 931 | + size_t frames = remaining > kFramesPerChunk ? kFramesPerChunk : static_cast<size_t>(remaining); |
| 932 | + memset(pcm, 0, frames * kChannels * sizeof(int16_t)); |
| 933 | + codec.write(reinterpret_cast<uint8_t*>(pcm), frames * kChannels * sizeof(int16_t)); |
| 934 | + remaining -= static_cast<uint32_t>(frames); |
| 935 | + } |
| 936 | + }; |
| 937 | + |
| 938 | + for (size_t i = 0; i < (sizeof(kTone) / sizeof(kTone[0])); ++i) |
| 939 | + { |
| 940 | + const ToneStep& step = kTone[i]; |
| 941 | + uint32_t remaining = (kSampleRate * step.duration_ms) / 1000; |
| 942 | + float phase = 0.0f; |
| 943 | + const float phase_step = (kTwoPi * static_cast<float>(step.freq_hz)) / static_cast<float>(kSampleRate); |
| 944 | + |
| 945 | + while (remaining > 0) |
| 946 | + { |
| 947 | + size_t frames = remaining > kFramesPerChunk ? kFramesPerChunk : static_cast<size_t>(remaining); |
| 948 | + for (size_t n = 0; n < frames; ++n) |
| 949 | + { |
| 950 | + int16_t sample = static_cast<int16_t>(sinf(phase) * (32767.0f * kAmplitude)); |
| 951 | + pcm[n * 2] = sample; |
| 952 | + pcm[n * 2 + 1] = sample; |
| 953 | + phase += phase_step; |
| 954 | + if (phase >= kTwoPi) |
| 955 | + { |
| 956 | + phase -= kTwoPi; |
| 957 | + } |
| 958 | + } |
| 959 | + codec.write(reinterpret_cast<uint8_t*>(pcm), frames * kChannels * sizeof(int16_t)); |
| 960 | + remaining -= static_cast<uint32_t>(frames); |
| 961 | + } |
| 962 | + |
| 963 | + if (i + 1 < (sizeof(kTone) / sizeof(kTone[0]))) |
| 964 | + { |
| 965 | + write_silence(kGapMs); |
| 966 | + } |
| 967 | + } |
| 968 | + |
| 969 | + codec.setVolume(static_cast<uint8_t>(prev_volume)); |
| 970 | + codec.setOutMute(prev_out_mute); |
| 971 | + codec.close(); |
| 972 | + s_playing = false; |
| 973 | +#endif |
| 974 | +} |
| 975 | + |
862 | 976 | void TLoRaPagerBoard::setHapticEffects(uint8_t effects) |
863 | 977 | { |
864 | 978 | if (effects > 127) effects = 127; |
|
0 commit comments