3 bài trước tạo và xử lý âm thanh; bài này quan sát nó. AnalyserNode không tạo ra hay biến đổi âm thanh — nó là 1 "điểm dò" (tap) đọc dữ liệu tín hiệu đang chạy qua, cho phép vẽ waveform và spectrum tần số thời gian thực — nền tảng của mọi visualizer nhạc.

1. Biến Đổi Fourier (FFT): Trực Giác Cơ Bản

Mọi tín hiệu âm thanh phức tạp — dù là hợp âm, giọng nói, hay tiếng trống — về mặt toán học đều có thể phân tích thành tổng của nhiều sóng sine đơn giản ở các tần số, biên độ, và pha khác nhau. Biến đổi Fourier Nhanh (FFT — Fast Fourier Transform) là thuật toán tách 1 đoạn tín hiệu theo thời gian thành đúng những thành phần tần số đó — biến 1 dạng sóng rối rắm thành 1 biểu đồ rõ ràng: "tần số này có bao nhiêu năng lượng".

2. Thiết Lập AnalyserNode: fftSize Và Độ Phân Giải

audioContext.createAnalyser() tạo 1 node đặc biệt — kết nối vào giữa đồ thị (như 1 điểm rẽ nhánh) để đọc dữ liệu mà không ảnh hưởng âm thanh thật sự phát ra. analyser.fftSize (phải là luỹ thừa của 2, từ 32 tới 32768) quyết định độ phân giải: FFT càng lớn, càng nhiều tần số phân biệt được, nhưng cũng chậm cập nhật hơn theo thời gian.

ℹ️ Vì sao frequencyBinCount chỉ bằng đúng 1 nửa fftSize?
Theo định lý Nyquist, tần số cao nhất có thể biểu diễn chính xác từ 1 tín hiệu lấy mẫu là đúng 1 nửa sample rate (VD sampleRate 48000 Hz → tối đa 24000 Hz). Nửa còn lại của kết quả FFT thô là ảnh đối xứng (mirror), không mang thêm thông tin mới — nên Web Audio chỉ trả về analyser.frequencyBinCount = fftSize / 2 giá trị thực sự hữu ích.
🕳️ Cạm bẫy thường gặp: quên nối analyser tiếp tới destination
AnalyserNode chỉ là 1 điểm dò dữ liệu — nó KHÔNG tự động phát tiếp âm thanh ra loa. Nếu chỉ source.connect(analyser) mà quên analyser.connect(destination), visualizer vẫn vẽ được (vì đọc dữ liệu không cần phát ra loa) nhưng người dùng sẽ không nghe thấy gì cả — 1 lỗi rất dễ bỏ sót vì visualizer trông vẫn "chạy đúng".

3. getByteFrequencyData()getByteTimeDomainData()

Phương thức Trả về gì Dùng để vẽ
getByteFrequencyData(array) Năng lượng (0–255) tại mỗi dải tần số (frequency bin) Spectrum analyzer (thanh cột theo tần số)
getByteTimeDomainData(array) Biên độ mẫu thô (0–255, tâm ở 128) theo thời gian Waveform / oscilloscope (dạng sóng thô)

Cả 2 đều cần gọi lại mỗi frame trong vòng lặp requestAnimationFrame — dữ liệu chỉ phản ánh đúng khoảnh khắc hiện tại, không tự động cập nhật nếu không đọc lại.

🔬 Đào sâu: smoothingTimeConstant làm mượt dữ liệu giữa các frame
analyser.smoothingTimeConstant (0–1, mặc định 0.8) trộn kết quả FFT khung hình hiện tại với khung hình ngay trước đó theo tỉ lệ đó — giá trị càng cao, thanh spectrum càng mượt mà/ít giật, nhưng phản ứng càng chậm hơn với thay đổi thực tế của âm thanh. Giá trị 0 cho dữ liệu thô, giật nhưng phản ứng tức thời.
💡 Mẹo: luôn dùng requestAnimationFrame, không phải setInterval
requestAnimationFrame tự động đồng bộ với tần số refresh màn hình và tự tạm dừng khi tab ẩn (tab không hiển thị) — tiết kiệm CPU/pin đáng kể so với setInterval chạy đều đặn dù người dùng không nhìn thấy gì.

Sân chơi tương tác: Spectrum Analyzer + Waveform

Bấm "Phát" và kéo tần số/dạng sóng — quan sát waveform (nửa trên) phản ánh đúng hình dạng sóng bạn chọn, và spectrum (nửa dưới) hiện rõ tần số cơ bản cùng các họa âm bậc cao đã học ở Bài 2.

📊 Sân chơi tương tác: Spectrum Analyzer + Waveform
AnalyserNode

Nhật ký

audio-analyser-fft-live.js

Trắc nghiệm ôn tập

Câu 1: Vì sao analyser.frequencyBinCount chỉ bằng đúng 1 nửa fftSize?

Trắc nghiệm ôn tập

Câu 2: smoothingTimeConstant càng cao thì điều gì xảy ra?

Trắc nghiệm ôn tập

Câu 3: Nếu chỉ source.connect(analyser) mà quên analyser.connect(destination), điều gì xảy ra?

📖 Tài liệu tham khảo / References

Bài viết liên quan trong series

Bài 5: Phát & Xử Lý File/Mic Bài 3: Gain, Filter & Hiệu Ứng Quay lại Lộ trình Series Web Audio API