4 bài trước dùng OscillatorNode làm nguồn — tiện cho học, nhưng thực tế thường cần phát
file nhạc thật hoặc bắt âm thanh từ micro. Bài này thêm 2 nguồn mới cùng cách đo độ to (loudness)
chuẩn hơn biên độ đỉnh đơn thuần.
1. decodeAudioData: Giải Mã File Thành AudioBuffer
Đọc file âm thanh (upload hoặc fetch) dưới dạng ArrayBuffer, rồi
audioContext.decodeAudioData(arrayBuffer) giải mã toàn bộ (MP3, WAV, OGG...) thành 1
AudioBuffer — PCM thô nằm sẵn trong bộ nhớ, sẵn sàng phát qua
AudioBufferSourceNode. Giống OscillatorNode, source node này cũng chỉ
start() được đúng 1 lần — phát lại phải tạo node mới (buffer gốc thì tái sử dụng thoải
mái).
decodeAudioData giải mã TOÀN BỘ file vào bộ nhớ cùng lúc
MediaElementSourceNode) là lựa chọn hợp lý hơn nhiều.
2. MediaElementSourceNode: Bọc Thẻ <audio> HTML5
audioContext.createMediaElementSource(audioElement) biến 1 thẻ
<audio>/<video> bình thường thành 1 node trong đồ thị Web Audio
— tận dụng luôn cơ chế streaming/buffer tăng dần sẵn có của trình duyệt (không cần chờ tải/giải mã
xong toàn bộ), trong khi vẫn áp được filter/analyser/hiệu ứng như mọi node khác đã học.
3. getUserMedia: Đầu Vào Micro
navigator.mediaDevices.getUserMedia({ audio: true }) xin quyền micro, trả về 1
MediaStream. audioContext.createMediaStreamSource(stream) biến stream đó
thành 1 AudioNode — nối tiếp vào AnalyserNode y hệt mọi nguồn khác đã học.
destination gây hú rít (feedback loop)
micSource.connect(audioContext.destination), âm thanh từ loa sẽ bị
chính micro thu lại, khuếch đại, phát ra loa, thu lại tiếp... tạo vòng lặp hú rít (audio feedback)
giống hệt micro để gần loa ngoài đời thật. Với ứng dụng chỉ cần phân tích mic (VU
meter, spectrum), chỉ nối micSource → analyser — dừng lại ở đó, không nối tiếp ra
destination.
stream.getTracks().forEach(track => track.stop()) giải phóng thiết bị mic hoàn toàn
— nếu quên, icon/đèn báo mic trên tab trình duyệt vẫn sáng, mic vẫn bị đọc dữ liệu liên tục dù ứng
dụng không còn cần nữa, vừa tốn tài nguyên vừa là mối lo về quyền riêng tư.
4. Đo Âm Lượng Bằng RMS (Root Mean Square)
Biên độ đỉnh (giá trị lớn nhất tức thời) không phản ánh đúng độ to cảm nhận — 1 tín hiệu có
thể đạt đỉnh cao trong tích tắc mà nghe không hề to. RMS lấy trung bình bình phương
biên độ trên cả cửa sổ mẫu rồi khai căn, phản ánh sát hơn nhiều năng lượng liên tục mà tai người cảm
nhận là "to" (với x_i là biên độ mẫu thứ i, N là số mẫu trong
cửa sổ đang xét):
$$\text{RMS} = \sqrt{\dfrac{1}{N}\sum_{i=1}^{N} x_i^2}$$
Sân chơi tương tác: Visualizer File Upload + Mic
Chọn nguồn: tải lên 1 file âm thanh để phát qua decodeAudioData, hoặc bật micro qua
getUserMedia. Cả 2 dùng chung 1 đường ống phân tích: waveform + thanh đo RMS bên dưới.
Nhật ký
Trắc nghiệm ôn tập
Câu 1: Vì sao decodeAudioData không phù hợp để phát ngay 1 bản nhạc dài vài phút?
Trắc nghiệm ôn tập
Câu 2: Vì sao phải luôn gọi stream.getTracks().forEach(t => t.stop()) sau khi dùng
xong micro?
Trắc nghiệm ôn tập
Câu 3: Vì sao RMS phản ánh độ to cảm nhận (loudness) chính xác hơn biên độ đỉnh (peak) đơn thuần?