Skip to content

Commit db2dc88

Browse files
committed
Fix HDMI audio sample rate detection for non-48kHz sources
Query TC358743 HDMI receiver for detected audio sample rate before initializing ALSA capture device. This fixes distortion issues when HDMI sources send 44.1kHz audio (e.g., Armbian SBC) instead of 48kHz. Previously, the code always requested 48kHz from ALSA, but in I2S slave mode, the RV1106 I2S controller receives whatever clock rate the TC358743 master provides. This caused a sample rate mismatch where ALSA thought it was 48kHz but hardware was actually running at 44.1kHz, resulting in incorrect SpeexDSP resampling and audio distortion. Changes: - Add V4L2 ioctl to query TC358743's audio_sampling_rate control - Use detected rate when configuring ALSA (falls back to 48kHz if unavailable) - SpeexDSP resampler now gets correct input rate (44.1k, 48k, etc.) - Supports all HDMI audio sample rates: 32k, 44.1k, 48k, 88.2k, 96k, etc.
1 parent 818a2ca commit db2dc88

File tree

1 file changed

+73
-5
lines changed

1 file changed

+73
-5
lines changed

internal/audio/c/audio.c

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,19 @@
3030
#include <signal.h>
3131
#include <pthread.h>
3232
#include <stdatomic.h>
33+
#include <fcntl.h>
34+
#include <sys/ioctl.h>
35+
#include <linux/videodev2.h>
3336

3437
// ARM NEON SIMD optimizations (Cortex-A7 accelerates buffer operations, with scalar fallback)
3538
#include <arm_neon.h>
3639

40+
// TC358743 V4L2 control IDs for audio
41+
#ifndef V4L2_CID_USER_TC35874X_BASE
42+
#define V4L2_CID_USER_TC35874X_BASE (V4L2_CID_USER_BASE + 0x10a0)
43+
#endif
44+
#define TC35874X_CID_AUDIO_SAMPLING_RATE (V4L2_CID_USER_TC35874X_BASE + 0)
45+
3746
// RV1106 (Cortex-A7) has 64-byte cache lines
3847
#define CACHE_LINE_SIZE 64
3948
#define SIMD_ALIGN __attribute__((aligned(16)))
@@ -209,6 +218,54 @@ static volatile sig_atomic_t playback_initialized = 0;
209218

210219
// ALSA UTILITY FUNCTIONS
211220

221+
/**
222+
* Query TC358743 HDMI receiver for detected audio sample rate
223+
* Reads the hardware-detected sample rate from V4L2 control
224+
* @return detected sample rate (44100, 48000, etc.) or 0 if detection fails
225+
*/
226+
static unsigned int get_hdmi_audio_sample_rate(void) {
227+
// TC358743 is a V4L2 subdevice at /dev/v4l-subdev2
228+
int fd = open("/dev/v4l-subdev2", O_RDWR);
229+
if (fd < 0) {
230+
fprintf(stderr, "WARNING: Could not open /dev/v4l-subdev2 to query HDMI audio sample rate: %s\n", strerror(errno));
231+
fflush(stderr);
232+
return 0;
233+
}
234+
235+
// Use extended controls API for custom V4L2 controls
236+
struct v4l2_ext_control ext_ctrl = {0};
237+
ext_ctrl.id = TC35874X_CID_AUDIO_SAMPLING_RATE;
238+
239+
struct v4l2_ext_controls ext_ctrls = {0};
240+
ext_ctrls.ctrl_class = V4L2_CTRL_CLASS_USER;
241+
ext_ctrls.count = 1;
242+
ext_ctrls.controls = &ext_ctrl;
243+
244+
if (ioctl(fd, VIDIOC_G_EXT_CTRLS, &ext_ctrls) == -1) {
245+
fprintf(stderr, "WARNING: Could not query TC358743 audio sample rate control: %s (errno=%d)\n", strerror(errno), errno);
246+
fflush(stderr);
247+
close(fd);
248+
return 0;
249+
}
250+
251+
close(fd);
252+
253+
unsigned int detected_rate = (unsigned int)ext_ctrl.value;
254+
fprintf(stdout, "DEBUG: TC358743 control read returned: %u Hz (error_idx=%u)\n", detected_rate, ext_ctrls.error_idx);
255+
fflush(stdout);
256+
257+
if (detected_rate == 0) {
258+
fprintf(stdout, "INFO: TC358743 reports 0 Hz (no signal or rate not detected yet)\n");
259+
fflush(stdout);
260+
return 0; // No signal or rate not detected
261+
}
262+
263+
fprintf(stdout, "INFO: TC358743 detected HDMI audio sample rate: %u Hz\n", detected_rate);
264+
fflush(stdout);
265+
266+
return detected_rate;
267+
}
268+
212269
/**
213270
* Open ALSA device with exponential backoff retry
214271
* @return 0 on success, negative error code on failure
@@ -365,12 +422,13 @@ static int handle_alsa_error(snd_pcm_t *handle, snd_pcm_t **valid_handle,
365422
* @param handle ALSA PCM handle
366423
* @param device_name Device name for logging
367424
* @param num_channels Number of channels (1=mono, 2=stereo)
425+
* @param preferred_rate Preferred sample rate (0 = use default 48kHz)
368426
* @param actual_rate_out Pointer to store the actual hardware-negotiated rate
369427
* @param actual_frame_size_out Pointer to store the actual frame size at hardware rate
370428
* @return 0 on success, negative error code on failure
371429
*/
372430
static int configure_alsa_device(snd_pcm_t *handle, const char *device_name, uint8_t num_channels,
373-
unsigned int *actual_rate_out, uint16_t *actual_frame_size_out) {
431+
unsigned int preferred_rate, unsigned int *actual_rate_out, uint16_t *actual_frame_size_out) {
374432
snd_pcm_hw_params_t *params;
375433
snd_pcm_sw_params_t *sw_params;
376434
int err;
@@ -410,8 +468,8 @@ static int configure_alsa_device(snd_pcm_t *handle, const char *device_name, uin
410468
return err;
411469
}
412470

413-
// Try to set 48kHz first (preferred), then let hardware negotiate
414-
unsigned int requested_rate = opus_sample_rate;
471+
// Use preferred rate if specified, otherwise default to 48kHz
472+
unsigned int requested_rate = (preferred_rate > 0) ? preferred_rate : opus_sample_rate;
415473
err = snd_pcm_hw_params_set_rate_near(handle, params, &requested_rate, 0);
416474
if (err < 0) return err;
417475

@@ -540,9 +598,19 @@ int jetkvm_audio_capture_init() {
540598
return ERR_ALSA_OPEN_FAILED;
541599
}
542600

601+
// Query TC358743 for detected HDMI audio sample rate
602+
unsigned int preferred_rate = get_hdmi_audio_sample_rate();
603+
if (preferred_rate > 0) {
604+
fprintf(stdout, "INFO: Using TC358743 detected sample rate: %u Hz\n", preferred_rate);
605+
} else {
606+
fprintf(stdout, "INFO: TC358743 sample rate not detected, using default 48kHz\n");
607+
preferred_rate = 0; // Will default to 48kHz
608+
}
609+
fflush(stdout);
610+
543611
unsigned int actual_rate = 0;
544612
uint16_t actual_frame_size_with_flag = 0;
545-
err = configure_alsa_device(pcm_capture_handle, "capture", capture_channels, &actual_rate, &actual_frame_size_with_flag);
613+
err = configure_alsa_device(pcm_capture_handle, "capture", capture_channels, preferred_rate, &actual_rate, &actual_frame_size_with_flag);
546614
if (err < 0) {
547615
snd_pcm_t *handle = pcm_capture_handle;
548616
pcm_capture_handle = NULL;
@@ -819,7 +887,7 @@ int jetkvm_audio_playback_init() {
819887

820888
unsigned int actual_rate = 0;
821889
uint16_t actual_frame_size = 0;
822-
err = configure_alsa_device(pcm_playback_handle, "playback", playback_channels, &actual_rate, &actual_frame_size);
890+
err = configure_alsa_device(pcm_playback_handle, "playback", playback_channels, 0, &actual_rate, &actual_frame_size);
823891
if (err < 0) {
824892
snd_pcm_t *handle = pcm_playback_handle;
825893
pcm_playback_handle = NULL;

0 commit comments

Comments
 (0)