|
|
|
@@ -84,12 +84,16 @@ static char s_sid[32] = {0};
|
|
|
|
|
* NOT re-read dialer_current() for the greeting/reply — that polluted number
|
|
|
|
|
* would 404 at the gateway. Capture the clean routed number here once. */
|
|
|
|
|
static char s_number[16] = {0};
|
|
|
|
|
/* Scene this call hints on (e.g. "SCENE_WARNING"), locked at pickup like the
|
|
|
|
|
* number. Empty for player-dialled calls. Drives the local SD hint clip. */
|
|
|
|
|
static char s_scene[40] = {0};
|
|
|
|
|
|
|
|
|
|
/* Incoming-call mode: an NPC "calls" the phone. conversation_arm_incoming()
|
|
|
|
|
* stores the caller's persona number and rings; when the handset is then picked
|
|
|
|
|
* up from IDLE, we skip the outgoing dialtone/dial/ringback and go straight to
|
|
|
|
|
* GREET with this number (the NPC speaks first). */
|
|
|
|
|
* stores the caller's persona number (and optional scene) and rings; when the
|
|
|
|
|
* handset is then picked up from IDLE, we skip the outgoing dialtone/dial/
|
|
|
|
|
* ringback and go straight to GREET with this number (the NPC speaks first). */
|
|
|
|
|
static char s_incoming_number[16] = {0};
|
|
|
|
|
static char s_incoming_scene[40] = {0};
|
|
|
|
|
static volatile bool s_incoming_armed = false;
|
|
|
|
|
|
|
|
|
|
/* Known numbers: ringback when dialed */
|
|
|
|
@@ -105,6 +109,20 @@ static bool is_known(const char *num)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* "SCENE_WARNING" → "warning": strip the SCENE_ prefix and lowercase, matching
|
|
|
|
|
* the SD pack filenames from tools/tts/generate_plip_sd_pack.py. */
|
|
|
|
|
static void scene_slug(const char *scene, char *out, size_t cap)
|
|
|
|
|
{
|
|
|
|
|
const char *p = scene;
|
|
|
|
|
if (strncmp(p, "SCENE_", 6) == 0) p += 6;
|
|
|
|
|
size_t i = 0;
|
|
|
|
|
for (; p[i] && i + 1 < cap; i++) {
|
|
|
|
|
char c = p[i];
|
|
|
|
|
out[i] = (c >= 'A' && c <= 'Z') ? (char)(c - 'A' + 'a') : c;
|
|
|
|
|
}
|
|
|
|
|
out[i] = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void go_idle(void)
|
|
|
|
|
{
|
|
|
|
|
tones_stop();
|
|
|
|
@@ -114,6 +132,8 @@ static void go_idle(void)
|
|
|
|
|
#if CONFIG_PLIP_DIAL_DTMF
|
|
|
|
|
dtmf_stop();
|
|
|
|
|
#endif
|
|
|
|
|
s_scene[0] = '\0'; /* clear scripted-call scene so a later dial-out call
|
|
|
|
|
* doesn't inherit it (CONNECTED would skip listening). */
|
|
|
|
|
s_state = STATE_IDLE;
|
|
|
|
|
ESP_LOGI(TAG, "-> IDLE");
|
|
|
|
|
}
|
|
|
|
@@ -134,6 +154,7 @@ static void enter_incoming_greet(void)
|
|
|
|
|
dialer_reset();
|
|
|
|
|
s_offhook = true;
|
|
|
|
|
snprintf(s_number, sizeof(s_number), "%s", s_incoming_number);
|
|
|
|
|
snprintf(s_scene, sizeof(s_scene), "%s", s_incoming_scene);
|
|
|
|
|
snprintf(s_sid, sizeof(s_sid), "%lld", (long long)esp_timer_get_time());
|
|
|
|
|
audio_pa_set(true);
|
|
|
|
|
s_state = STATE_GREET;
|
|
|
|
@@ -277,11 +298,37 @@ static void conv_task(void *arg)
|
|
|
|
|
ESP_LOGW(TAG, "no SD clip + gateway greeting failed — silent");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* Scripted offline call: chain this scene's hint from the SD pack
|
|
|
|
|
* right after the greeting (the audio queue plays them back-to-back).
|
|
|
|
|
* Fully local — no gateway, no model. */
|
|
|
|
|
if (s_scene[0] != '\0' && audio_ensure_sd()) {
|
|
|
|
|
char slug[40], hint[80];
|
|
|
|
|
scene_slug(s_scene, slug, sizeof(slug));
|
|
|
|
|
snprintf(hint, sizeof(hint),
|
|
|
|
|
"/sdcard/voice/hint_%s_l1_0.wav", slug);
|
|
|
|
|
FILE *hf = fopen(hint, "rb");
|
|
|
|
|
if (hf != NULL) {
|
|
|
|
|
fclose(hf);
|
|
|
|
|
audio_play_async(hint);
|
|
|
|
|
ESP_LOGI(TAG, "GREET: chained SD hint %s", hint);
|
|
|
|
|
} else {
|
|
|
|
|
ESP_LOGI(TAG, "GREET: no SD hint for scene %s (%s)",
|
|
|
|
|
s_scene, hint);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
s_state = STATE_CONNECTED;
|
|
|
|
|
ESP_LOGI(TAG, "-> CONNECTED");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case STATE_CONNECTED:
|
|
|
|
|
/* Scripted offline call (greeting + SD hint already queued): there's
|
|
|
|
|
* no live two-way conversation to run — just let the clips play out
|
|
|
|
|
* and wait for the player to hang up. Skips the gateway/model listen
|
|
|
|
|
* loop entirely. The global on-hook check returns us to IDLE. */
|
|
|
|
|
if (s_scene[0] != '\0') {
|
|
|
|
|
vTaskDelay(pdMS_TO_TICKS(REPLY_POLL_MS));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
#if CONFIG_PLIP_VOICE_REPLY
|
|
|
|
|
/*
|
|
|
|
|
* Stage 3 — LISTEN loop.
|
|
|
|
@@ -417,12 +464,14 @@ void conversation_on_hook_change(bool offhook)
|
|
|
|
|
s_hook_changed = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void conversation_arm_incoming(const char *number)
|
|
|
|
|
void conversation_arm_incoming(const char *number, const char *scene)
|
|
|
|
|
{
|
|
|
|
|
snprintf(s_incoming_number, sizeof(s_incoming_number), "%s",
|
|
|
|
|
(number && number[0]) ? number : "17");
|
|
|
|
|
snprintf(s_incoming_scene, sizeof(s_incoming_scene), "%s",
|
|
|
|
|
(scene && scene[0]) ? scene : "");
|
|
|
|
|
s_incoming_armed = true;
|
|
|
|
|
phone_ring_start();
|
|
|
|
|
ESP_LOGI(TAG, "incoming call armed (num=%s) — ringing until pickup",
|
|
|
|
|
s_incoming_number);
|
|
|
|
|
ESP_LOGI(TAG, "incoming call armed (num=%s scene=%s) — ringing until pickup",
|
|
|
|
|
s_incoming_number, s_incoming_scene[0] ? s_incoming_scene : "-");
|
|
|
|
|
}
|
|
|
|
|