// BLOG — 2026-08-28

Từ một câu prompt tới một video nhạc hoàn chỉnh: máy làm được mấy bước?

Bài mổ xẻ hành trình từ prompt tới video nhạc hoàn chỉnh, tập trung vào trích xuất nhịp, xếp lớp hiệu ứng và những trade-off kỹ thuật thực tế.

Từ một câu prompt tới một video nhạc hoàn chỉnh: máy làm được mấy bước?

Bài này mở đầu chuỗi mổ xẻ kỹ thuật tại zeodavu.com/blog. Mình ghi lại hành trình thật của một bài nhạc AI — từ một dòng mô tả tới một file video có hình, có sóng âm bám nhịp. Không có recipe để sao chép; chỉ có cấu trúc, số liệu đo được, và vấn đề gặp phải.


Câu hỏi đặt ra ban đầu: nếu chỉ gõ một câu mô tả, máy tự làm được bao nhiêu phần trước khi mình phải tham gia?

Kết quả thực tế: gần như toàn bộ phần sinh nội dung. Phần quyết định thì vẫn do mình. Bài này mổ xẻ việc phân chia đó.


Cấu trúc hành trình

Một bài nhạc AI trong setup này đi qua sáu tầng nối tiếp:

  1. 1.Tầng ý tưởng — mô tả dạng text: thể loại, tempo, tông, tâm trạng. Ví dụ "dark trap Nhật: đàn shamisen + bass 808, 70–90 BPM".
  2. 2.Tầng sinh nhạc — máy dựng phần nền (tiết tấu, bass, giai điệu). Hai con đường: trên mây (nhanh, có giọng hát, tốn phí theo tháng) và tại nhà (miễn phí, riêng tư, cần GPU).
  3. 3.Tầng giọng hát — tạo giọng từ mô tả, hoặc chuyển chất giọng nguồn thành giọng mong muốn.
  4. 4.Tầng trộn — ghép giọng với nền, cân độ lớn, thêm hiệu ứng.
  5. 5.Tầng hình ảnh — dựng hình bám nhịp: đập beat, chuyển màu, tạo layer khói/lỗi hình.
  6. 6.Tầng xuất — render ra file video hoàn chỉnh.

Phần sinh nội dung làm được ở mức "có thể chấp nhận", nhưng phần quyết định (chọn đúng tông, đúng nhịp, đúng cảm xúc) vẫn phải làm thủ công từng bài.


Trích xuất tín hiệu: lấy "nhịp" từ âm thanh như thế nào

Phần hình bám nhịp không hoạt động kiểu "ảnh chạy nền". Nó cần một tín hiệu nhịp rút ra từ chính âm thanh. Đây là bước kỹ thuật quan trọng nhất.

Onset detection bằng spectral flux

So sánh phổ năng lượng theo thời gian, nơi có bước nhảy đột ngột thì đánh dấu một onset. Với kick (dải trầm) thì lọc band trước khi tính, để tránh nhiễu từ phần âm cao. Minh hoạ (không phải source thật):

# pseudo-code minh hoạ — không phải nguồn triển khai
# Để lại mức khái niệm về onset detection bằng spectral flux
def detect_onsets(signal, sr, low_cut=20, high_cut=150):
    # Lọc band trầm cho kick
    x = bandpass(signal, sr, low_cut, high_cut)

    # STFT: phổ theo thời gian
    frames = stft(x, hop=512, win=2048)
    mag = abs(frames)

    # Spectral flux: tổng mức tăng năng lượng giữa 2 frame liên tiếp
    flux = sum(relu(mag[t] - mag[t-1]) for t in range(1, len(mag)))

    # Ngưỡng động: đánh dấu nơi flux vượt bối cảnh cục bộ
    onsets = []
    for i, f in enumerate(flux):
        thr = np.percentile(flux[max(0, i-25):i+25], 90)
        if f > thr and is_local_peak(flux, i):
            onsets.append(frames_to_time(i, sr, hop=512))
    return onsets
python

Energy envelope — trigger cho từng layer

Thay vì khớp thủ công từng khoảnh khắc, dùng đường bao cường độ làm "trigger": mỗi khi vượt ngưỡng, layer tương ứng nhảy.

# pseudo-code minh hoạ — không phải nguồn triển khai
def envelope(signal, sr, window=0.05):
    # RMS trên cửa sổ 50ms -> đường bao cường độ mượt theo thời gian
    hop = int(sr * window)
    return [sqrt(mean(signal[i:i+hop]**2)) for i in range(0, len(signal), hop)]

# Trigger: layer "nổ" khi envelope vượt ngưỡng cục bộ
def trigger_layers(env, layers_thresholds):
    fired = []
    for i, e in enumerate(env):
        for name, thr in layers_thresholds.items():
            if e > thr and not fired_since(name, i):
                fired.append((i, name))   # đưa vào hàng đợi render
    return fired
python

Beat grid — đồng bộ theo tempo cố định

Khi biết BPM (ở đây dải 70–90), lưới chia đều theo beat; mọi thứ bám lưới này để không lệch nhịp.

# pseudo-code minh hoạ — không phải nguồn triển khai
def build_beat_grid(bpm, duration_s):
    beat_s        = 60.0 / bpm
    grid          = [i * beat_s for i in range(int(duration_s / beat_s))]
    # Map sample-accurate: chiếu thời gian âm thanh lên khung hình
    return [(t, t * fps) for t in grid]
python

Con số độ chính xác: phần lệch nhịp lộ ra rõ khi sai tầm vài phần trăm giây (1–2 frame ở 30fps). Vì vậy toàn bộ đường bao và lưới được tính theo sample-accurate ở tần số lấy mẫu âm thanh, rồi mới chiếu lên khung hình — không khớp theo kinh nghiệm.


Lớp hiệu ứng hình ảnh bám nhịp

Hình ảnh là nhiều lớp xếp chồng, mỗi lớp phản ứng với một phần của tín hiệu đã trích xuất ở trên. Có lớp trigger theo onset (nổi lên ở kick), có lớp theo envelope trung bình (trải mờ suốt bài), có lớp theo beat grid (nhịp nhàng đều).

Ví dụ cách phân loại theo dải tần & thời gian:

| Loại layer | Nguồn trigger | Hành vi |

|---|---|---|

| Kick-dependent | onset ở band trầm (20–150Hz) | nháy/pulse mỗi kick |

| Mid-dependent | envelope năng lượng trung bình | co giãn liên tục theo nhạc |

| Grid-dependent | beat grid cố định | nhịp nhàng đều, ổn định |

| Ambient | envelope dài hạn | trải nền, chuyển động chậm |

Bật tắt từng lớp độc lập, xếp chồng theo thứ tự. Với bản dark-trap Nhật, bảng màu chốt là đen + đỏ thẫm + ánh vàng, và phản ứng nhịp theo 2 pha: chậm ở intro, dồn dập ở drop. Cuối cùng phủ một lớp màu (color grading) + grain + viền tối để video không bị "quá sạch/giả".

# pseudo-code minh hoạ — không phải nguồn triển khai
# Pipeline xếp chồng layer rồi mới phủ phi hiệu ứng cuối
layers = [
    ("background",   Source.VIDEO,  needs=Signal.ENVELOPE_LONG),
    ("particles",    Source.PARAM,  needs=Signal.ONSET_KICK),
    ("fog",          Source.GRADIENT, needs=Signal.ENVELOPE_AVG),
    ("glitch",       Source.VIDEO,  needs=Signal.ONSET_DROP),
    ("ring",         Source.SHAPE,  needs=Signal.ENVELOPE_LOW),
("grade",        Source.COLOR,   needs=Signal.CONSTANT),
    ("grain",        Source.NOISE,  needs=Signal.CONSTANT),
]
for layer in layers:
    frame = composite(frame, layer, t, signal_map[t])
python

Vấn đề gặp phải

1. Sinh nhạc trên mây: nhanh, nhưng giảm quyền kiểm soát

Chạy trên mây cho kết quả nhanh, có giọng hát ngay. Nhưng tempo và tông gần như do máy tự chọn — mình không chỉnh được. Muốn đúng nhịp/tông thì phải chuyển sang chạy tại nhà, nơi mỗi tham số chỉnh được.

Trade-off: tốc độ ⇄ quyền kiểm soát. Đo được sự chênh này ở mức minutes (cloud) so với vài phút/bài (local) — nhưng local trả lại toàn quyền về BPM, tông, và chỉnh lại được.

2. Giọng hát: đổi được "chất", không sửa được "sai"

Model giọng hát tạo được giọng ở mức khá. Nhưng nếu giọng nguồn hát lệch nhịp, bước đổi chất giọng không sửa — nó chỉ biến đổi, vẫn giữ nguyên lỗi. Đầu vào lệch thì đầu ra lệch. Chất lượng phụ thuộc nguồn nhiều hơn là phần xử lý.

3. Dựng hình bám nhịp: dễ ở mức tương đối, khó ở độ chính xác

Làm video "gần đúng theo nhạc" thì nhanh. Làm cho hình chạm đúng từng kick, khói lắng đúng pha, lớp lỗi hình nổ đúng drop — là nơi mình nhầm nhiều nhất. Sai vài phần trăm giây là nghe/mắt ra ngay "lệch nhịp". Đây chính là lý do phải dùng onset/envelope (mục trên) thay vì khớp theo bàn tay.


Thông số kỹ thuật đầu ra

Để tránh phải đổi lại sau, mình cố định output ngay từ đầu ở mức dùng cho social/web:

| Thông số | Giá trị |

|---|---|

| Độ phân giải | 1080p (1920×1080) / tùy chọn 2160p |

| Tốc độ khung hình | 30 fps (bám lưới 1–2 frame), tùy chọn 60 |

| Bitrate video | ~8–12 Mb/s (đủ sắc, không nặng) |

| Codec | mã nguồn mở, web-friendly |

| Âm thanh | 44.1 kHz / 16-bit, giữ khoảng dynamic |

| Độ lớn chuẩn | trung tính, không spike — dùng làm nền hợp |

Chi phí tiền mặt gần như bằng 0; chi phí thực là điện + thời gian GPU. Chạy trên card 16GB, vẫn để dành phần GPU cho việc khác.


Kết luận

Bài này cố ý không nêu tên lệnh, tên file, tham số, hay prompt chính xác. Phần đáng kể nằm ở cấu trúc, không phải công cụ. Các code block ở trên là minh hoạ khái niệm (pseudo-code mình viết để dễ hiểu cơ chế), không phải nguồn triển khai thật — với bất kỳ bài mổ xẻ nào thì thứ đáng giữ là tư duy cấu trúc, không phải dòng code.

Cấu trúc chốt lại thành vài điểm:

  • Trích xuất tín hiệu (onset / envelope / beat grid) là nền tảng để hình bám nhịp chính xác — không khớp thủ công.
  • Tách phần máy làm (sinh nhạc, trộn, dựng hình — lặp lại, rẻ) khỏi phần quyết định (chọn tông, nhịp, cảm xúc).
  • Ưu tiên quyền kiểm soát khi cần đúng, tốc độ khi không cần.
  • Cố định output specs sớm để khỏi đổi lại.
  • Giữ phần sinh nội dung trên phần mềm mở để lặp lại và tự động hoá được.

Toàn bộ số liệu và thành phẩm mình ghi lại ở zeodavu.com/blog và khu music.


Một phần của chuỗi mổ xẻ kỹ thuật tại zeodavu.com/blog. Nội dung là bài học mở, không phải blueprint để sao chép — thứ đáng giữ là cấu trúc và trải nghiệm, không phải danh sách công cụ.

Nếu thấy hữu ích, theo dõi [zeodavu.com](/blog) — mỗi bài là một thứ mình thật sự dùng, vấp, và sửa lại.