Mọi thứ trong Web Audio API bắt đầu từ 1 đối tượng duy nhất: AudioContext. Bài này xây nền tảng bắt buộc trước khi chạm vào synth, filter, hay spectrum: hiểu đúng mô hình đồ thị node, vòng đời của context, và vì sao trình duyệt không cho bạn phát âm thanh ngay khi trang vừa tải xong.

1. AudioContext Là Gì?

AudioContext là "nhạc trưởng" quản lý toàn bộ hệ thống âm thanh của trang: nó giữ tần số lấy mẫu (sample rate — thường 44100 hoặc 48000 Hz, cố định suốt vòng đời context), 1 đồng hồ thời gian riêng (currentTime, tính bằng giây, chạy độc lập với Date.now()), và là nơi mọi AudioNode được tạo ra từ đó (audioContext.createOscillator(), .createGain()...).

2. Đồ Thị Node: Nguồn → Xử Lý → Đích

Âm thanh không "phát" bằng 1 lệnh đơn — nó chảy qua 1 chuỗi node được nối bằng connect(), giống hệt dây cắm trên bàn mixer:

Vai trò Ví dụ Node Nhiệm vụ
Nguồn (source) OscillatorNode, AudioBufferSourceNode Tạo ra tín hiệu âm thanh ban đầu.
Xử lý (processing) GainNode, BiquadFilterNode Biến đổi tín hiệu đi qua (âm lượng, lọc tần số...).
Đích (destination) audioContext.destination Điểm cuối — thường là loa của thiết bị.

osc.connect(gain).connect(audioContext.destination) nối 3 node này thành 1 đường ống — đây chính xác là "đồ thị" (graph) trong tên gọi Web Audio API.

🕳️ Cạm bẫy thường gặp: source node chỉ start() được ĐÚNG 1 LẦN
OscillatorNodeAudioBufferSourceNode giống 1 que diêm — dùng 1 lần rồi bỏ. Gọi .start() lần thứ 2 trên cùng 1 node sẽ ném lỗi InvalidStateError. Muốn phát lại, bạn phải tạo 1 node hoàn toàn mới — đây là lý do mọi ví dụ synth thực tế đều tạo oscillator mới cho mỗi nốt nhạc, dù tái sử dụng cùng 1 GainNode/AudioContext phía sau nó.

3. Vòng Đời Context: suspendedrunningclosed

AudioContext luôn khởi tạo ở trạng thái suspended (tạm dừng) — không tự động running. Gọi audioContext.resume() để bắt đầu xử lý audio thật sự. close() giải phóng tài nguyên vĩnh viễn (không thể resume lại context đã đóng, phải tạo context mới).

💡 Mẹo: chỉ tạo AudioContext bên trong 1 sự kiện người dùng
Vì autoplay policy (mục 4) yêu cầu cử chỉ người dùng để context chạy được, thói quen tốt nhất là trì hoãn việc tạo new AudioContext() tới đúng lúc người dùng bấm nút "Phát" lần đầu — thay vì tạo sẵn khi trang load rồi phải gọi resume() riêng.

4. Autoplay Policy: Vì Sao Cần Cử Chỉ Người Dùng?

Trình duyệt hiện đại chặn mọi trang tự động phát âm thanh khi vừa tải xong — lý do đơn giản: quá nhiều website/quảng cáo từng lạm dụng điều này gây phiền. AudioContext chỉ chuyển sang running khi được tạo/resume bên trong 1 tương tác thực của người dùng (click, tap, phím) — gọi resume() từ 1 setTimeout hay khi trang vừa load sẽ không có tác dụng.

🔬 Đào sâu: AudioParam lên lịch theo audio-clock, không phải JS event loop
Giá trị như gainNode.gain hay oscillator.frequencyAudioParam — có thể gán tức thời (.value = 440), nhưng cũng có thể lên lịch chính xác bằng setValueAtTime(440, audioContext.currentTime + 0.5). Vì audio xử lý trên luồng riêng ở tần số lấy mẫu cố định, lịch này chính xác tới từng mẫu — hoàn toàn không bị ảnh hưởng bởi độ trễ/jitter của vòng lặp sự kiện JS chính, khác hẳn setTimeout vốn chỉ đảm bảo "tối thiểu bấy nhiêu mili-giây" chứ không chính xác tuyệt đối.

Sân chơi tương tác: Phát 1 Tone Bằng OscillatorNode

Bấm "Phát" để tạo AudioContext (đúng lúc user gesture), nối OscillatorNode → GainNode → destination, và nghe âm thanh thật. Trong lúc đang phát, kéo tần số/âm lượng để thấy AudioParam cập nhật tức thì trên cùng 1 node — không cần tạo node mới. Bấm "Dừng" rồi "Phát" lại để thấy log giải thích vì sao phải tạo oscillator mới.

🔊 Sân chơi tương tác: AudioContext & Node Graph

Điều khiển

Nhật ký

audio-context-graph-live.js

Trắc nghiệm ôn tập

Câu 1: Vì sao gọi oscillator.start() lần thứ 2 trên cùng 1 OscillatorNode sẽ ném lỗi?

Trắc nghiệm ôn tập

Câu 2: Vì sao không thể tạo AudioContext và phát nhạc ngay khi trang vừa tải xong?

Trắc nghiệm ôn tập

Câu 3: Điều gì khiến lên lịch bằng AudioParam.setValueAtTime(value, audioContext.currentTime + delay) chính xác hơn setTimeout?

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

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

Bài 2: Oscillator & Synthesis Quay lại Lộ trình Series Web Audio API