5 bài trước, âm thanh luôn phát ra "ở giữa" — không có khái niệm trái/phải hay gần/xa. Bài này thêm
chiều không gian: từ cân bằng trái-phải đơn giản (StereoPannerNode) tới định vị 3D đầy đủ
mô phỏng cách tai người thật nghe hướng âm thanh (PannerNode + HRTF).
1. StereoPannerNode: Cân Bằng Trái/Phải Đơn Giản
audioContext.createStereoPanner() chỉ có đúng 1 tham số: pan, nhận giá trị
từ -1 (hoàn toàn trái) tới 1 (hoàn toàn phải), 0 là chính giữa.
Đây là dạng "không gian" đơn giản nhất — không mô phỏng khoảng cách, không mô phỏng chiều sâu, chỉ đơn
thuần chia tỉ lệ âm lượng giữa 2 kênh loa.
StereoPannerNode nhẹ hơn nhiều về mặt tính toán và code ngắn gọn hơn hẳn. Chỉ cần
PannerNode khi thực sự cần mô phỏng nguồn âm tồn tại ở 1 VỊ TRÍ trong không gian 3D
(game, trải nghiệm VR/AR, mô phỏng môi trường thật).
2. PannerNode & Mô Hình HRTF: Không Gian 3D Thật Sự
audioContext.createPanner() định vị nguồn âm bằng toạ độ 3 chiều
(positionX/Y/Z) trong không gian, kết hợp với vị trí và hướng của
audioContext.listener (đại diện cho "tai" người nghe) để tính ra âm thanh cuối cùng.
Thuộc tính panningModel quyết định thuật toán tính toán:
-
'equalpower'(mặc định) — chỉ điều chỉnh âm lượng tương đối giữa 2 tai theo góc, tương tự StereoPannerNode nhưng có thêm khái niệm khoảng cách. -
'HRTF'(Head-Related Transfer Function) — áp bộ lọc được đo thực nghiệm trên hình dạng đầu/tai người mẫu, mô phỏng cả độ trễ thời gian đến 2 tai (ITD) lẫn khác biệt cường độ giữa 2 tai (ILD) — giúp não bộ định vị được cả hướng trước/sau/trên/dưới, không chỉ trái/phải.
3. Vị Trí & Hướng: Nguồn Âm và Listener
Cả nguồn âm (PannerNode) và người nghe (audioContext.listener) đều có toạ độ
vị trí (positionX/Y/Z) và hướng nhìn (orientationX/Y/Z) — đây đều là
AudioParam (từ Bài 1), có thể lập lịch mượt bằng
setValueAtTime/linearRampToValueAtTime giống mọi tham số khác đã học:
const panner = audioContext.createPanner();
panner.panningModel = 'HRTF';
panner.positionX.setValueAtTime(3, audioContext.currentTime); // 3 mét sang phải
panner.positionZ.setValueAtTime(-2, audioContext.currentTime); // 2 mét phía trước
// Vị trí/hướng người nghe — mặc định (0,0,0), nhìn theo trục -Z
const listener = audioContext.listener;
listener.positionX.setValueAtTime(0, audioContext.currentTime);
listener.forwardX.setValueAtTime(0, audioContext.currentTime);
listener.forwardZ.setValueAtTime(-1, audioContext.currentTime);
4. Mô Hình Suy Giảm Theo Khoảng Cách
PannerNode còn tự động giảm âm lượng theo khoảng cách qua 3 tham số:
refDistance (khoảng cách "chuẩn" chưa suy giảm), maxDistance, và
rolloffFactor (tốc độ suy giảm). Với distanceModel = 'inverse' (mặc định,
giống vật lý thật của sóng âm cầu):
$$\text{gain} = \dfrac{\text{refDistance}}{\text{refDistance} + \text{rolloff} \times (\text{distance} - \text{refDistance})}$$
positionX/Y/Z không có đơn vị vật lý bắt buộc — bạn tự quyết định 1 đơn vị "khoảng
cách" nghĩa là gì trong ứng dụng của mình. Nếu refDistance mặc định (1) không khớp với
thang đo bạn đang dùng (vd toạ độ game tính bằng hàng trăm đơn vị pixel), âm lượng sẽ suy giảm gần
như về 0 ngay ở khoảng cách rất gần — luôn chỉnh refDistance/
maxDistance khớp với đơn vị toạ độ thực tế của ứng dụng.
5. So Sánh StereoPannerNode vs PannerNode
| Tiêu chí | StereoPannerNode | PannerNode |
|---|---|---|
| Số chiều không gian | 1 (trái ↔ phải) | 3 (x, y, z đầy đủ) |
| Mô phỏng khoảng cách? | Không | Có (distance model) |
| Mô hình HRTF? | Không hỗ trợ | Có (panningModel: 'HRTF') |
| Chi phí tính toán | Rất thấp | Cao hơn (đặc biệt với HRTF) |
Sân chơi tương tác: Spatial Audio Playground
Phát 1 âm liên tục, chọn kiểu định vị, kéo slider hoặc bấm "Tự động xoay" để nguồn âm di chuyển quanh người nghe — nghe (đeo tai nghe để cảm nhận rõ nhất với chế độ HRTF) và quan sát trực quan vị trí trên canvas.
Kiểu định vị
Nhật ký
Trắc nghiệm ôn tập
Câu 1: Khác biệt cơ bản nhất giữa StereoPannerNode và PannerNode là gì?
Trắc nghiệm ôn tập
Câu 2: Mô hình HRTF giúp ích gì mà panningModel: 'equalpower' (mặc định) không làm
được?
Trắc nghiệm ôn tập
Câu 3: Vì sao PannerNode cần biết cả vị trí của audioContext.listener
chứ không chỉ vị trí của chính nguồn âm?