feat(plip): tones module — France Télécom dial/busy/ringback

This commit is contained in:
2026-06-14 21:53:50 +02:00
parent 39f4cd2313
commit ba248e92fe
3 changed files with 136 additions and 0 deletions
+4
View File
@@ -7,6 +7,9 @@ idf_component_register(
"cmd_exec.c"
"hook_client.c"
"phone.c"
"tones.c"
"dialer.c"
"conversation.c"
INCLUDE_DIRS "."
PRIV_REQUIRES
driver
@@ -14,6 +17,7 @@ idf_component_register(
esp_http_client
esp_http_server
esp_netif
esp_timer
esp_wifi
json
nvs_flash
+125
View File
@@ -0,0 +1,125 @@
/*
* tones.c — France Télécom telephone tones (dial / busy / ringback).
*
* Generates 440 Hz sinusoid with on/off cadence written directly to the
* I2S speaker handle (stereo — ES8388 requires L+R pairs).
*
* Cadences (all at 440 Hz):
* DIAL : continuous
* BUSY : 0.5 s on / 0.5 s off (1 s cycle)
* RINGBACK : 1.5 s on / 3.5 s off (5 s cycle)
*/
#include "tones.h"
#include "audio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2s_std.h"
#include "esp_log.h"
#include <math.h>
#include <string.h>
#include <stdbool.h>
#define TAG "tones"
typedef enum { TONE_NONE, TONE_DIAL, TONE_BUSY, TONE_RINGBACK } tone_mode_t;
static volatile tone_mode_t s_mode = TONE_NONE;
static TaskHandle_t s_task = NULL;
#define SR 16000 /* sample rate (must match audio_init) */
#define FRAME 320 /* 20 ms @ 16 kHz */
/*
* Fill `n` stereo frames (L=R = 440 Hz sine) into buf.
* buf must hold n * 2 int16_t (stereo interleaved).
* ph: running phase accumulator (mono sample index).
*/
static void fill_440_stereo(int16_t *buf, int n, int *ph)
{
for (int i = 0; i < n; i++) {
int16_t v = (int16_t)(8000.0f * sinf(2.0f * (float)M_PI * 440.0f * (*ph) / SR));
buf[i * 2] = v; /* L */
buf[i * 2 + 1] = v; /* R */
(*ph)++;
}
}
static void tone_task(void *a)
{
(void)a;
/* Stereo buffer: FRAME mono samples × 2 channels × 2 bytes = FRAME*4 bytes */
int16_t buf[FRAME * 2];
int ph = 0;
int64_t t = 0; /* ms elapsed in current tone state */
for (;;) {
tone_mode_t m = s_mode;
if (m == TONE_NONE) {
vTaskDelay(pdMS_TO_TICKS(20));
t = 0;
ph = 0;
continue;
}
bool on = true;
if (m == TONE_BUSY) {
on = (t % 1000) < 500; /* 500ms on / 500ms off */
} else if (m == TONE_RINGBACK) {
on = (t % 5000) < 1500; /* 1.5s on / 3.5s off */
}
/* TONE_DIAL: always on */
if (on) {
fill_440_stereo(buf, FRAME, &ph);
} else {
memset(buf, 0, sizeof(buf));
/* Keep phase accumulator advancing so pitch is smooth on resumption */
ph += FRAME;
}
size_t written = 0;
i2s_channel_write(audio_spk_handle(), buf, sizeof(buf), &written, pdMS_TO_TICKS(500));
t += 20; /* each frame = 20 ms */
}
}
static void ensure_task(void)
{
if (!s_task) {
xTaskCreate(tone_task, "tones", 3072, NULL, 5, &s_task);
ESP_LOGI(TAG, "tone_task created");
}
}
void tones_dialtone_start(void)
{
ESP_LOGI(TAG, "dial tone start");
audio_pa_set(true);
ensure_task();
s_mode = TONE_DIAL;
}
void tones_busy_start(void)
{
ESP_LOGI(TAG, "busy tone start");
audio_pa_set(true);
ensure_task();
s_mode = TONE_BUSY;
}
void tones_ringback_start(void)
{
ESP_LOGI(TAG, "ringback tone start");
audio_pa_set(true);
ensure_task();
s_mode = TONE_RINGBACK;
}
void tones_stop(void)
{
ESP_LOGI(TAG, "tones stop");
s_mode = TONE_NONE;
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include "esp_err.h"
void tones_dialtone_start(void); /* 440 Hz continuous */
void tones_busy_start(void); /* 440 Hz 0.5s on/off */
void tones_ringback_start(void); /* 440 Hz 1.5s/3.5s */
void tones_stop(void);