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:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user