aes67_tx: mono sum; the source always delivers stereo

The pull callback's contract said "stream channels", but the player always
delivers stereo, so a 1-channel stream went out as L,R,L,R at half speed.
Sources now always deliver AES67_TX_SRC_CHANNELS (2); TX maps to the stream:
2 ch as is, 1 ch = (L+R)/2 in 64 bit with mono_sum (can't clip), else L.
Checked: mono packets 156 B, 1000/s, 48 samples each, SDP L24/48000/1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-25 23:26:48 +10:00
parent faf427c897
commit c9e06da47c
4 changed files with 22 additions and 5 deletions
+17 -2
View File
@@ -106,6 +106,7 @@ typedef struct {
uint8_t ttl, dscp, pt;
uint32_t ssrc, clk_offset;
int channels, rate, bytes; // bytes per sample: 3 (L24) or 2 (L16)
bool mono_sum; // 1-channel stream carries (L + R) / 2, else L
int frames; // samples per channel per packet
} tx_cfg_t;
@@ -127,6 +128,7 @@ static bool load(tx_cfg_t *c)
c->ssrc = (uint32_t)NUM("ssrc");
c->clk_offset = (uint32_t)NUM("clk_offset");
c->channels = (int)NUM("channels");
c->mono_sum = cJSON_IsTrue(cJSON_GetObjectItemCaseSensitive(a, "mono_sum"));
c->rate = (int)NUM("rate");
c->bytes = strcmp(cJSON_GetObjectItemCaseSensitive(a, "encoding")->valuestring, "L16") == 0 ? 2 : 3;
c->frames = (int)lround(NUM("ptime") * c->rate / 1000.0); // 0.333 ms -> 16 at 48 kHz
@@ -168,6 +170,15 @@ static int64_t samples_to_ns(int64_t s, int rate)
// 1 kHz tone, phase from the PTP sample index, so every sender's tone lines up.
// One period is rate / 1000 samples (48 at 48 kHz, 96 at 96 kHz): precomputed table.
// Stereo source -> 1-channel stream, in place. The sum is in 64 bit, so it can't clip; identical
// L/R comes out at the same level.
static void to_mono(int32_t *buf, int frames, bool sum)
{
for (int i = 0; i < frames; i++) {
buf[i] = sum ? (int32_t)(((int64_t)buf[2 * i] + buf[2 * i + 1]) / 2) : buf[2 * i];
}
}
static void tone(int32_t *buf, int frames, int channels, int rate, int64_t s0)
{
static int32_t table[96000 / TONE_HZ];
@@ -202,7 +213,7 @@ static void tx_task(void *arg)
esp_timer_handle_t timer = NULL;
const esp_timer_create_args_t targs = { .callback = timer_cb, .name = "aes67_tx" };
esp_timer_create(&targs, &timer);
static int32_t pcm[MAX_FRAMES * MAX_CH];
static int32_t pcm[MAX_FRAMES * (MAX_CH > AES67_TX_SRC_CHANNELS ? MAX_CH : AES67_TX_SRC_CHANNELS)];
static uint8_t pkt[RTP_HDR + MAX_FRAMES * MAX_CH * 3];
uint16_t seq = (uint16_t)esp_random();
int64_t next = 0; // sample index of the next packet; 0 = resync needed
@@ -250,9 +261,13 @@ static void tx_task(void *arg)
if (read) {
size_t got = read(pcm, c.frames);
if (got < (size_t)c.frames) {
memset(pcm + got * c.channels, 0, (c.frames - got) * c.channels * sizeof(int32_t));
memset(pcm + got * AES67_TX_SRC_CHANNELS, 0,
(c.frames - got) * AES67_TX_SRC_CHANNELS * sizeof(int32_t));
s_underruns++;
}
if (c.channels == 1) {
to_mono(pcm, c.frames, c.mono_sum);
}
} else {
tone(pcm, c.frames, c.channels, c.rate, next);
}