Skip to content

Commit b6050ea

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 b6050ea

File tree

1 file changed

+61
-5
lines changed

1 file changed

+61
-5
lines changed

internal/audio/c/audio.c

Lines changed: 61 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,42 @@ 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+
int fd = open("/dev/video0", O_RDWR);
228+
if (fd < 0) {
229+
fprintf(stderr, "WARNING: Could not open /dev/video0 to query HDMI audio sample rate: %s\n", strerror(errno));
230+
fflush(stderr);
231+
return 0;
232+
}
233+
234+
struct v4l2_control control = {0};
235+
control.id = TC35874X_CID_AUDIO_SAMPLING_RATE;
236+
237+
if (ioctl(fd, VIDIOC_G_CTRL, &control) == -1) {
238+
fprintf(stderr, "WARNING: Could not query TC358743 audio sample rate control: %s\n", strerror(errno));
239+
fflush(stderr);
240+
close(fd);
241+
return 0;
242+
}
243+
244+
close(fd);
245+
246+
unsigned int detected_rate = (unsigned int)control.value;
247+
if (detected_rate == 0) {
248+
return 0; // No signal or rate not detected
249+
}
250+
251+
fprintf(stdout, "INFO: TC358743 detected HDMI audio sample rate: %u Hz\n", detected_rate);
252+
fflush(stdout);
253+
254+
return detected_rate;
255+
}
256+
212257
/**
213258
* Open ALSA device with exponential backoff retry
214259
* @return 0 on success, negative error code on failure
@@ -365,12 +410,13 @@ static int handle_alsa_error(snd_pcm_t *handle, snd_pcm_t **valid_handle,
365410
* @param handle ALSA PCM handle
366411
* @param device_name Device name for logging
367412
* @param num_channels Number of channels (1=mono, 2=stereo)
413+
* @param preferred_rate Preferred sample rate (0 = use default 48kHz)
368414
* @param actual_rate_out Pointer to store the actual hardware-negotiated rate
369415
* @param actual_frame_size_out Pointer to store the actual frame size at hardware rate
370416
* @return 0 on success, negative error code on failure
371417
*/
372418
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) {
419+
unsigned int preferred_rate, unsigned int *actual_rate_out, uint16_t *actual_frame_size_out) {
374420
snd_pcm_hw_params_t *params;
375421
snd_pcm_sw_params_t *sw_params;
376422
int err;
@@ -410,8 +456,8 @@ static int configure_alsa_device(snd_pcm_t *handle, const char *device_name, uin
410456
return err;
411457
}
412458

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

@@ -540,9 +586,19 @@ int jetkvm_audio_capture_init() {
540586
return ERR_ALSA_OPEN_FAILED;
541587
}
542588

589+
// Query TC358743 for detected HDMI audio sample rate
590+
unsigned int preferred_rate = get_hdmi_audio_sample_rate();
591+
if (preferred_rate > 0) {
592+
fprintf(stdout, "INFO: Using TC358743 detected sample rate: %u Hz\n", preferred_rate);
593+
} else {
594+
fprintf(stdout, "INFO: TC358743 sample rate not detected, using default 48kHz\n");
595+
preferred_rate = 0; // Will default to 48kHz
596+
}
597+
fflush(stdout);
598+
543599
unsigned int actual_rate = 0;
544600
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);
601+
err = configure_alsa_device(pcm_capture_handle, "capture", capture_channels, preferred_rate, &actual_rate, &actual_frame_size_with_flag);
546602
if (err < 0) {
547603
snd_pcm_t *handle = pcm_capture_handle;
548604
pcm_capture_handle = NULL;
@@ -819,7 +875,7 @@ int jetkvm_audio_playback_init() {
819875

820876
unsigned int actual_rate = 0;
821877
uint16_t actual_frame_size = 0;
822-
err = configure_alsa_device(pcm_playback_handle, "playback", playback_channels, &actual_rate, &actual_frame_size);
878+
err = configure_alsa_device(pcm_playback_handle, "playback", playback_channels, 0, &actual_rate, &actual_frame_size);
823879
if (err < 0) {
824880
snd_pcm_t *handle = pcm_playback_handle;
825881
pcm_playback_handle = NULL;

0 commit comments

Comments
 (0)