|
发表于 2024-7-19 04:36:31
|
显示全部楼层
好的鱼油们,准备好了吗?让我们来完成这个16步鼓机的创作吧,但要先上好安全帽,确保不会因为太嗨而头撞天花板!
### 1. HTML 部分
先来写基础的HTML结构。我们已经定义了一个drum-machine的div,还有三个按钮和一个滑块,来控制播放、停止以及速度(BPM)。
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>16步鼓机</title>
- <style>
- /* 放入之前给出的CSS样式代码 */
- </style>
- </head>
- <body>
- <div class="drum-machine">
- <h1>16步鼓机</h1>
- <div class="grid-container">
- <div class="labels">
- <div>踩镲</div>
- <div>军鼓</div>
- <div>底鼓</div>
- </div>
- <div class="grid-wrapper">
- <div class="beat-numbers">
- <!-- 生成节拍编号 -->
- <script>
- for (let i = 1; i <= 16; i++) {
- document.write(`<div class="beat-number">${i}</div>`);
- }
- </script>
- </div>
- <div id="grid">
- <!-- 生成网格 -->
- <script>
- for (let i = 0; i < 3; i++) {
- for (let j = 0; j < 16; j++) {
- document.write('<div class="cell"></div>');
- }
- }
- </script>
- </div>
- </div>
- </div>
- <div class="controls">
- <button id="playButton">播放</button>
- <button id="stopButton">停止</button>
- <label>
- <input type="range" id="tempoSlider" min="60" max="200" value="140">
- <span id="tempoValue">140 BPM</span>
- </label>
- </div>
- </div>
- <script>
- // 放置下面的JavaScript代码
- </script>
- </body>
- </html>
复制代码
### 2. JavaScript 部分
现在进入真正的音频处理部分——用Web Audio API来生成鼓声!我们得让我们的鼓“踩”起来非常逼真。
- // AudioContext用于创建和管理音频内容
- const audioContext = new (window.AudioContext || window.webkitAudioContext)();
- const grid = document.getElementById('grid');
- const playButton = document.getElementById('playButton');
- const stopButton = document.getElementById('stopButton');
- const tempoSlider = document.getElementById('tempoSlider');
- const tempoValue = document.getElementById('tempoValue');
- // 各种鼓声数组
- const drumSounds = ["hihat", "snare", "kick"];
- const pattern = [
- new Array(16).fill(false), // 踩镲
- new Array(16).fill(false), // 军鼓
- new Array(16).fill(false), // 底鼓
- ];
- let isPlaying = false;
- let currentBeat = 0;
- let tempo = 140;
- tempoSlider.addEventListener('input', () => {
- tempo = parseInt(tempoSlider.value);
- tempoValue.textContent = `${tempo} BPM`;
- });
- playButton.addEventListener('click', () => {
- if (!isPlaying) {
- isPlaying = true;
- playPattern();
- }
- });
- stopButton.addEventListener('click', () => {
- isPlaying = false;
- currentBeat = 0;
- stopPattern();
- });
- grid.addEventListener('click', (e) => {
- if (e.target.classList.contains('cell')) {
- const row = Math.floor(Array.from(grid.children).indexOf(e.target) / 16);
- const col = Array.from(grid.children).indexOf(e.target) % 16;
- pattern[row][col] = !pattern[row][col];
- e.target.classList.toggle('active');
- }
- });
- function playPattern() {
- if (!isPlaying) return;
-
- const interval = (60 / tempo) / 4 * 1000; // 计算每个节拍的间隔时间
- pattern.forEach((row, rowIndex) => {
- if (row[currentBeat]) {
- playDrumSound(drumSounds[rowIndex]);
- }
- });
- highlightCurrentBeat(currentBeat);
- currentBeat = (currentBeat + 1) % 16;
- setTimeout(playPattern, interval); // 循环播放
- }
- function stopPattern() {
- clearTimeout(playPattern);
- Array.from(grid.children).forEach(cell => cell.classList.remove('playing'));
- }
- function highlightCurrentBeat(beat) {
- Array.from(grid.children).forEach((cell, index) => {
- if (index % 16 == beat) {
- cell.classList.add('playing');
- } else {
- cell.classList.remove('playing');
- }
- });
- }
- function createNoiseBuffer() {
- const bufferSize = audioContext.sampleRate * 2;
- const buffer = audioContext.createBuffer(1, bufferSize, audioContext.sampleRate);
- const output = buffer.getChannelData(0);
- for (let i = 0; i < bufferSize; i++) {
- output[i] = Math.random() * 2 - 1;
- }
- return buffer;
- }
- const noiseBuffer = createNoiseBuffer();
- function playDrumSound(type) {
- const gainNode = audioContext.createGain();
- gainNode.connect(audioContext.destination);
- switch (type) {
- case 'hihat':
- playHiHat(gainNode);
- break;
- case 'snare':
- playSnare(gainNode);
- break;
- case 'kick':
- playKick(gainNode);
- break;
- }
- }
- function playHiHat(gainNode) {
- const hihatSource = audioContext.createBufferSource();
- hihatSource.buffer = noiseBuffer;
- const hihatFilter = audioContext.createBiquadFilter();
- hihatFilter.type = 'highpass';
- hihatFilter.frequency.value = 7000;
- hihatSource.connect(hihatFilter);
- hihatFilter.connect(gainNode);
- gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
- gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.1);
- hihatSource.start();
- hihatSource.stop(audioContext.currentTime + 0.1);
- }
- function playSnare(gainNode) {
- const snareNoise = audioContext.createBufferSource();
- snareNoise.buffer = noiseBuffer;
- const snareFilter = audioContext.createBiquadFilter();
- snareFilter.type = 'highpass';
- snareFilter.frequency.value = 1000;
- const snareEnvelope = audioContext.createGain();
- snareEnvelope.gain.setValueAtTime(1, audioContext.currentTime);
- snareEnvelope.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.2);
- snareNoise.connect(snareFilter);
- snareFilter.connect(snareEnvelope);
- snareEnvelope.connect(gainNode);
- snareNoise.start();
- snareNoise.stop(audioContext.currentTime + 0.2);
- }
- function playKick(gainNode) {
- const osc = audioContext.createOscillator();
- osc.type = 'sine';
- osc.frequency.setValueAtTime(150, audioContext.currentTime);
- osc.frequency.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.5);
-
- gainNode.gain.setValueAtTime(1, audioContext.currentTime);
- gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5);
-
- osc.connect(gainNode);
- osc.start();
- osc.stop(audioContext.currentTime + 0.5);
- }
复制代码
### 总结
就这样,我们的16步鼓机已经准备好让你叩击出节奏了!你可以通过点击网格激活或禁用某个节拍,调整速度,并使用播放和停止按钮来控制音乐。这是一台小小的节拍机器,但足以引爆派对!去享受创造音乐的快乐吧,别忘了拿好你的DJ帽子!
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 |
|