7天从乐理小白到代码大神:Teoria.js音乐理论编程全指南

7天从乐理小白到代码大神:Teoria.js音乐理论编程全指南

【免费下载链接】teoria Javascript taught Music Theory 【免费下载链接】teoria 项目地址: https://gitcode.com/gh_mirrors/te/teoria

你还在为音乐理论与代码实现之间的鸿沟而苦恼吗?想让你的Web应用具备专业级音乐计算能力却不知从何下手?本文将带你从零掌握Teoria.js——这款轻量级却功能强大的JavaScript音乐理论库,7天内让你能够轻松实现音符计算、和弦构建、音阶生成等专业音乐功能。

读完本文你将获得:

  • 精通Teoria.js核心API的使用方法
  • 掌握音乐理论在代码中的实际应用
  • 学会构建和弦生成器、音阶分析器等实用音乐工具
  • 能够将音乐理论无缝集成到Web应用中
  • 获取10+个可直接复用的音乐编程代码模块

项目介绍:Teoria.js是什么?

Teoria.js是一个轻量级、高性能的JavaScript音乐理论库,同时支持爵士乐和古典音乐理论。它提供了直观的编程接口,使开发者能够轻松地在应用中实现复杂的音乐理论功能,如乐谱阅读器、音乐创作工具、MIDI播放器等。

该项目采用MIT许可证开源,代码托管于https://gitcode.com/gh_mirrors/te/teoria,目前已成为Web音乐应用开发的重要工具之一。

核心功能概览

Teoria.js提供了四大核心对象,覆盖了音乐理论的主要方面:

mermaid

安装与环境配置

Teoria.js支持Node.js和浏览器环境,安装方式简单直观:

NPM安装(推荐)

npm install teoria

浏览器直接引入

<script src="path/to/teoria.js"></script>

第一天:音符(Note)——音乐的基本单位

音符是音乐的基本构建块,Teoria.js的Note对象提供了全面的音符操作功能。让我们从最基础的音符创建开始:

创建音符的四种方式

Teoria.js支持多种音符表示法,满足不同场景需求:

// 1. 科学记谱法 (推荐)
const a4 = teoria.note('a4');       // A4 (标准音高,440Hz)
const eb3 = teoria.note('Eb3');     // 降E3

// 2. 赫尔姆霍茨记谱法
const c5 = teoria.note("c''");      // C5 (两个上标表示高两个八度)
const f2 = teoria.note("F,,");      // F2 (两个下标表示低两个八度)

// 3. 钢琴键编号 (A0为1)
const midi60 = teoria.note.fromKey(49); // 中央C (MIDI编号60)

// 4. 频率创建
const freqNote = teoria.note.fromFrequency(440); // {note: A4, cents: 0}

音符属性与方法

每个Note对象都提供了丰富的属性和方法,用于获取和转换音符信息:

const c4 = teoria.note('C4'); // 中央C

console.log(c4.name());          // "c" - 音符名称
console.log(c4.octave());       // 4 - 八度编号
console.log(c4.accidental());   // "" - 临时记号(升#/降b等)
console.log(c4.accidentalValue()); // 0 - 临时记号数值表示

console.log(c4.key());          // 60 - 钢琴键编号
console.log(c4.midi());         // 60 - MIDI编号
console.log(c4.fq());           // 261.6255653005986 - 频率(Hz)
console.log(c4.chroma());       // 0 - 音高等级(0-11,C为0)

// 记谱法转换
console.log(c4.scientific());   // "C4" - 科学记谱法
console.log(c4.helmholtz());    // "c'" - 赫尔姆霍茨记谱法

音符转换与计算

音符间的转换是音乐计算的基础,Note对象提供了便捷的转换方法:

const a4 = teoria.note('a4');

// 音程转换
const c5 = a4.interval('m3');   // A4上方小三度是C5
console.log(c5.scientific());   // "C5"

// 转位
const enharmonics = a4.enharmonics(); // 获取等音
console.log(enharmonics.map(n => n.scientific())); // ["G##4", "Bbb5"]

// 频率计算
const freq = a4.fq();           // 440Hz
const highA = teoria.note('a5');
console.log(highA.fq());        // 880Hz (正好是A4的两倍)

实战:音符频率计算器

结合上述知识,我们可以快速实现一个音符频率计算器:

<input type="text" id="noteInput" placeholder="输入音符如C4、Bb3">
<button onclick="calculateFrequency()">计算频率</button>
<div id="result"></div>

<script src="path/to/teoria.js"></script>
<script>
function calculateFrequency() {
  const noteName = document.getElementById('noteInput').value;
  try {
    const note = teoria.note(noteName);
    const frequency = note.fq().toFixed(2);
    const midiNumber = note.midi();
    const pianoKey = note.key();
    
    document.getElementById('result').innerHTML = `
      <p>音符: ${note.scientific()}</p>
      <p>频率: ${frequency} Hz</p>
      <p>MIDI编号: ${midiNumber}</p>
      <p>钢琴键位: ${pianoKey}</p>
    `;
  } catch (e) {
    document.getElementById('result').textContent = '无效的音符输入';
  }
}
</script>

第二天:音程(Interval)——音符间的距离

音程是音乐中两个音符之间的距离,是构建和弦和音阶的基础。Teoria.js的Interval对象提供了全面的音程计算功能。

音程的表示与创建

音程由性质和度数组成,Teoria.js支持多种音程表示方法:

// 从字符串创建音程
const majorThird = teoria.interval('M3');   // 大三度
const perfectFifth = teoria.interval('P5'); // 纯五度
const minorSeventh = teoria.interval('m7'); // 小七度

// 两个音符之间的音程
const fromNote = teoria.note('C4');
const toNote = teoria.note('E4');
const interval = teoria.interval(fromNote, toNote); // 计算C4到E4的音程
console.log(interval.toString()); // "M3" (大三度)

// 从音程创建音符
const c4 = teoria.note('C4');
const e4 = c4.interval('M3'); // C4上方大三度是E4

音程属性与计算

Interval对象提供了丰富的属性和方法,用于音程分析和转换:

const interval = teoria.interval('m3'); // 小三度

console.log(interval.toString());   // "m3" - 音程字符串表示
console.log(interval.number());     // 3 - 音程度数
console.log(interval.quality());    // "minor" - 音程性质
console.log(interval.semitones());  // 3 - 半音数量
console.log(interval.direction());  // "up" - 方向(上行)

// 音程转位
const inverted = interval.invert();
console.log(inverted.toString());   // "M6" (小三度转位为大六度)

// 音程比较
const m3 = teoria.interval('m3');
const M3 = teoria.interval('M3');
console.log(m3.smaller(M3));        // true (小三度比大三度小)

音程的数学计算

音程可以进行加减运算,这在复杂音乐理论计算中非常有用:

const m3 = teoria.interval('m3');  // 小三度(3半音)
const P4 = teoria.interval('P4');  // 纯四度(5半音)

// 音程相加
const sum = m3.add(P4);
console.log(sum.toString());       // "m6" (小三度+纯四度=小六度)
console.log(sum.semitones());      // 8 (3+5=8半音)

// 音程比较
console.log(m3.equal(teoria.interval('m3')));  // true
console.log(m3.greater(P4));                   // false

实战:音程计算器

下面实现一个功能完整的音程计算器,支持音程识别和构建:

<div>
  <label>音符1: </label>
  <input type="text" id="note1" value="C4">
  
  <label>音符2: </label>
  <input type="text" id="note2" value="E4">
  
  <button onclick="calculateInterval()">计算音程</button>
</div>

<div>
  <label>根音: </label>
  <input type="text" id="rootNote" value="C4">
  
  <label>音程: </label>
  <select id="intervalSelect">
    <option value="M3">大三度(M3)</option>
    <option value="m3">小三度(m3)</option>
    <option value="P5">纯五度(P5)</option>
    <option value="M7">大七度(M7)</option>
  </select>
  
  <button onclick="buildNoteFromInterval()">构建音符</button>
</div>

<div id="result"></div>

<script src="path/to/teoria.js"></script>
<script>
function calculateInterval() {
  try {
    const note1 = teoria.note(document.getElementById('note1').value);
    const note2 = teoria.note(document.getElementById('note2').value);
    const interval = teoria.interval(note1, note2);
    
    document.getElementById('result').innerHTML += `
      <p>音程: ${interval.toString()} (${interval.quality()} ${interval.number()})</p>
      <p>半音数: ${interval.semitones()}</p>
      <p>转位音程: ${interval.invert().toString()}</p>
    `;
  } catch (e) {
    alert('无效的音符输入');
  }
}

function buildNoteFromInterval() {
  try {
    const root = teoria.note(document.getElementById('rootNote').value);
    const interval = document.getElementById('intervalSelect').value;
    const targetNote = root.interval(interval);
    
    document.getElementById('result').innerHTML += `
      <p>${root.scientific()} + ${interval} = ${targetNote.scientific()}</p>
    `;
  } catch (e) {
    alert('无效的输入');
  }
}
</script>

第三天:和弦(Chord)——和谐的音符组合

和弦是音乐的和声基础,Teoria.js的Chord对象提供了强大的和弦构建与分析能力,支持从简单三和弦到复杂爵士和弦的全面功能。

和弦的创建方法

Teoria.js支持多种和弦创建方式,满足不同使用场景:

// 1. 从音符和和弦类型创建
const cMajor = teoria.note('C4').chord();         // C大三和弦(默认)
const cMinor = teoria.note('C4').chord('m');      // C小三和弦
const c7 = teoria.note('C4').chord('7');          // C属七和弦
const cmaj7 = teoria.note('C4').chord('maj7');    // C大七和弦

// 2. 从和弦名称直接创建
const gMinor7 = teoria.chord('Gm7');              // Gm7和弦
const abDominant9 = teoria.chord('Ab9');          // Ab9和弦
const fSharp11 = teoria.chord('F#11');            // F#11和弦

// 3. 复杂爵士和弦
const advancedChord = teoria.chord('C#m9b5');     // C#m9b5和弦
const jazzChord = teoria.chord('Bb#11b9');        // 高级爵士和弦

和弦属性与方法

Chord对象提供了全面的和弦分析功能:

const cmaj7 = teoria.chord('Cmaj7'); // C大七和弦

// 获取和弦音符
const notes = cmaj7.notes();
console.log(notes.map(n => n.scientific())); // ["C4", "E4", "G4", "B4"]

// 简化表示
console.log(cmaj7.simple()); // ["c", "e", "g", "b"]

// 和弦属性
console.log(cmaj7.name);     // "Cmaj7" - 和弦名称
console.log(cmaj7.root.name()); // "c" - 根音
console.log(cmaj7.quality()); // "major" - 和弦性质
console.log(cmaj7.chordType()); // "tetrad" - 和弦类型(四音和弦)

// 和弦转位
const firstInversion = cmaj7.voicing(['M3', 'P5', 'M7', 'P1']);
console.log(firstInversion.notes().map(n => n.scientific())); 
// ["E4", "G4", "B4", "C5"] (第一转位,E为低音)

和弦进行与和声分析

Teoria.js可以轻松实现和弦进行和和声分析,是音乐理论教学和实践的强大工具:

// 创建和弦进行
const progression = [
  teoria.chord('C'),    // C大三和弦
  teoria.chord('Am'),   // A小三和弦
  teoria.chord('F'),    // F大三和弦
  teoria.chord('G7')    // G属七和弦
];

// 分析和弦进行
progression.forEach((chord, i) => {
  console.log(`和弦 ${i+1}: ${chord.name}`);
  console.log(`  性质: ${chord.quality()}`);
  console.log(`  类型: ${chord.chordType()}`);
  console.log(`  音符: ${chord.simple().join(', ')}`);
});

// 和弦关系
const cMajor = teoria.chord('C');
console.log(cMajor.dominant().name); // "G" (C的属和弦是G)
console.log(cMajor.subdominant().name); // "F" (C的下属和弦是F)
console.log(cMajor.parallel().name); // "Cm" (C的平行小调是Cm)

实战:和弦可视化工具

下面实现一个和弦可视化工具,可以显示和弦组成音及钢琴键盘位置:

<div>
  <input type="text" id="chordInput" value="Cmaj7">
  <button onclick="visualizeChord()">可视化和弦</button>
</div>
<div id="chordNotes"></div>
<div id="pianoKeyboard" style="display: flex; margin-top: 20px;"></div>

<script src="path/to/teoria.js"></script>
<script>
function visualizeChord() {
  try {
    const chordName = document.getElementById('chordInput').value;
    const chord = teoria.chord(chordName);
    const notes = chord.notes();
    
    // 显示和弦音符
    document.getElementById('chordNotes').innerHTML = `
      <h3>${chordName} 组成音</h3>
      <p>${notes.map(n => `${n.scientific()} (${n.fq().toFixed(1)}Hz)`).join(', ')}</p>
      <p>和弦性质: ${chord.quality()}, 类型: ${chord.chordType()}</p>
    `;
    
    // 生成钢琴键盘
    generatePianoKeyboard(notes);
  } catch (e) {
    alert('无效的和弦输入');
  }
}

function generatePianoKeyboard(highlightNotes) {
  const keyboard = document.getElementById('pianoKeyboard');
  keyboard.innerHTML = '';
  
  // 生成从C4到B5的钢琴键盘
  const startNote = teoria.note('C4');
  const highlightKeys = new Set(highlightNotes.map(n => n.key()));
  
  for (let i = 0; i < 24; i++) {
    const note = startNote.interval(teoria.interval(i + 'm2'));
    const key = note.key();
    const isBlack = ['c#', 'd#', 'f#', 'g#', 'a#'].includes(note.toString(true));
    
    const keyDiv = document.createElement('div');
    keyDiv.style.width = isBlack ? '30px' : '50px';
    keyDiv.style.height = isBlack ? '100px' : '200px';
    keyDiv.style.backgroundColor = highlightKeys.has(key) ? 
      (isBlack ? '#666' : '#ffaaaa') : (isBlack ? '#000' : '#fff');
    keyDiv.style.border = '1px solid #333';
    keyDiv.style.position = isBlack ? 'absolute' : 'relative';
    keyDiv.style.marginLeft = isBlack ? '-15px' : '0';
    keyDiv.title = note.scientific();
    
    keyboard.appendChild(keyDiv);
  }
}
</script>

第四天:音阶(Scale)——音乐的调色板

音阶是音乐的基础框架,Teoria.js的Scale对象提供了全面的音阶生成和分析功能,支持从简单的大调音阶到复杂的爵士音阶。

音阶的创建与表示

Teoria.js内置了多种常见音阶,同时支持自定义音阶:

// 1. 从根音和音阶名称创建
const cMajor = teoria.note('C4').scale('major');         // C大调音阶
const aMinor = teoria.note('A4').scale('minor');         // A小调音阶
const cMajorPentatonic = teoria.note('C4').scale('majorpentatonic'); // C大调五声音阶

// 2. 获取所有已知音阶
console.log(teoria.Scale.KNOWN_SCALES); 
// 输出所有支持的音阶名称

// 3. 七声音阶(调式)
const modes = [
  'ionian', 'dorian', 'phrygian', 'lydian', 
  'mixolydian', 'aeolian', 'locrian'
];

modes.forEach(mode => {
  const scale = teoria.note('C4').scale(mode);
  console.log(`${mode}: ${scale.simple().join(', ')}`);
});

// 4. 自定义音阶
const customScale = new teoria.Scale(teoria.note('C4'), ['P1', 'M2', 'm3', 'A4', 'P5']);

音阶属性与方法

Scale对象提供了丰富的属性和方法,用于音阶分析和转换:

const cMajor = teoria.note('C4').scale('major');

// 音阶基本信息
console.log(cMajor.name);       // "ionian" - 音阶名称(大调即伊奥尼亚调式)
console.log(cMajor.tonic.scientific()); // "C4" - 主音
console.log(cMajor.type());     // "heptatonic" - 音阶类型(七声音阶)

// 获取音阶音符
const notes = cMajor.notes();
console.log(notes.map(n => n.scientific())); 
// ["C4", "D4", "E4", "F4", "G4", "A4", "B4"]

// 简化表示
console.log(cMajor.simple());   // ["c", "d", "e", "f", "g", "a", "b"]

// 获取特定度数的音
console.log(cMajor.get(1).scientific()); // "C4" - 第一音(主音)
console.log(cMajor.get('third').scientific()); // "E4" - 第三音(三音)
console.log(cMajor.get('seventh').scientific()); // "B4" - 第七音(七音)

音阶转调与模态转换

音阶可以轻松转调和转换为不同调式,这在音乐创作和分析中非常有用:

// 原始音阶
const cMajor = teoria.note('C4').scale('major');
console.log(cMajor.simple()); // ["c", "d", "e", "f", "g", "a", "b"]

// 转调 - 移高大二度
const dMajor = cMajor.interval('M2');
console.log(dMajor.simple()); // ["d", "e", "f#", "g", "a", "b", "c#"]

// 调式转换 - 同主音不同调式
const cDorian = teoria.note('C4').scale('dorian');
console.log(cDorian.simple()); // ["c", "d", "eb", "f", "g", "a", "bb"]

// 关系大小调
const aMinor = cMajor.interval('m3'); // C大调的关系小调是A小调
console.log(aMinor.simple()); // ["a", "b", "c", "d", "e", "f", "g"]

实战:音阶训练工具

下面实现一个音阶训练工具,帮助音乐学习者熟悉各种音阶:

<div>
  <select id="rootNote">
    <option value="C">C</option>
    <option value="D">D</option>
    <option value="E">E</option>
    <!-- 其他音符选项 -->
  </select>
  
  <select id="scaleType">
    <option value="major">大调</option>
    <option value="minor">小调</option>
    <option value="majorpentatonic">大调五声音阶</option>
    <option value="minorpentatonic">小调五声音阶</option>
    <option value="dorian">多里安调式</option>
    <!-- 其他音阶选项 -->
  </select>
  
  <button onclick="generateScale()">生成音阶</button>
</div>

<div id="scaleDisplay"></div>
<div id="earTraining"></div>

<script src="path/to/teoria.js"></script>
<script>
function generateScale() {
  const root = document.getElementById('rootNote').value;
  const scaleType = document.getElementById('scaleType').value;
  
  try {
    const scale = teoria.note(root + '4').scale(scaleType);
    const notes = scale.notes();
    
    // 显示音阶
    let scaleHtml = `<h3>${root} ${getScaleName(scaleType)}音阶</h3>`;
    scaleHtml += '<div style="display: flex; gap: 10px; margin: 20px 0;">';
    
    notes.forEach((note, index) => {
      scaleHtml += `
        <div style="text-align: center;">
          <div style="width: 50px; height: 50px; border: 1px solid #333; 
                      display: flex; align-items: center; justify-content: center;
                      background: ${index === 0 ? '#ffd700' : '#fff'}">
            ${note.scientific()}
          </div>
          <div>${index + 1}度</div>
        </div>
      `;
    });
    
    scaleHtml += '</div>';
    scaleHtml += `<p>音程结构: ${scale.scale.join(', ')}</p>`;
    scaleHtml += `<p>音阶类型: ${scale.type()}</p>`;
    
    document.getElementById('scaleDisplay').innerHTML = scaleHtml;
    
    // 生成听力训练
    generateEarTraining(scale);
    
  } catch (e) {
    alert('无法生成音阶: ' + e.message);
  }
}

function getScaleName(scaleType) {
  const names = {
    'major': '大调',
    'minor': '小调',
    'majorpentatonic': '大调五声音阶',
    'minorpentatonic': '小调五声音阶',
    'dorian': '多里安调式',
    // 其他音阶名称映射
  };
  return names[scaleType] || scaleType;
}

function generateEarTraining(scale) {
  // 简单的听力训练生成逻辑
  const degrees = ['第一', '第二', '第三', '第四', '第五', '第六', '第七'];
  const randomDegree = Math.floor(Math.random() * scale.notes().length);
  
  document.getElementById('earTraining').innerHTML = `
    <h4>听力训练</h4>
    <p>问题: 音阶中的${degrees[randomDegree]}音是什么?</p>
    <button onclick="showAnswer(${randomDegree}, '${scale.tonic.scientific()}', '${scale.name}')">
      显示答案
    </button>
    <div id="answer"></div>
  `;
}

function showAnswer(degree, tonic, scaleName) {
  const scale = teoria.note(tonic).scale(scaleName);
  const note = scale.get(degree + 1);
  document.getElementById('answer').textContent = 
    `答案: ${note.scientific()} (${note.name().toUpperCase()}${note.accidental()})`;
}
</script>

第五天:高级应用——Teoria.js实战技巧

掌握了Teoria.js的基本功能后,让我们探索一些高级应用技巧,将音乐理论与编程更紧密地结合起来。

音乐理论与数学的结合

音乐本质上是数学的一种表现形式,Teoria.js可以帮助我们揭示音乐中的数学规律:

// 计算十二平均律的频率比
function calculateTemperamentRatios() {
  const ratios = [];
  for (let i = 0; i < 12; i++) {
    // 十二平均律: 频率比 = 2^(i/12)
    const ratio = Math.pow(2, i / 12);
    ratios.push({
      semitone: i,
      ratio: ratio.toFixed(4),
      interval: teoria.interval(teoria.note('C4'), teoria.note('C4').interval(`${i}m2`)).toString()
    });
  }
  return ratios;
}

// 显示频率比表格
console.table(calculateTemperamentRatios());

// 计算和弦的和谐度(基于泛音列相似度)
function chordConsonance(chord) {
  const notes = chord.notes();
  const rootFreq = notes[0].fq();
  let consonance = 0;
  
  // 基于泛音列计算和谐度
  notes.forEach(note => {
    const ratio = note.fq() / rootFreq;
    // 简化的和谐度计算: 接近简单整数比的音程更和谐
    const simplified = simplifyRatio(ratio);
    consonance += 1 / (simplified.numerator + simplified.denominator);
  });
  
  return consonance / notes.length; // 平均和谐度
}

// 简化频率比为最简分数
function simplifyRatio(ratio) {
  // 实现简化分数的逻辑
  // ...
}

即兴演奏辅助系统

Teoria.js可以构建强大的即兴演奏辅助系统,帮助音乐家在演奏时做出更好的和声选择:

// 构建即兴演奏辅助系统
class ImprovisationAssistant {
  constructor(key, style = 'jazz') {
    this.key = key; // 调式中心
    this.style = style; // 音乐风格
    this.chordProgressions = this.loadProgressions();
  }
  
  // 加载常见和弦进行
  loadProgressions() {
    return {
      jazz: [
        'ii-V-I', 'I-vi-ii-V', 'blues', 'rhythm changes'
      ],
      pop: [
        'I-V-vi-IV', 'I-IV-V', 'vi-IV-I-V', 'I-vi-iv-V'
      ]
      // 其他风格...
    };
  }
  
  // 生成和弦进行
  generateProgression(type, length = 4) {
    if (type === 'ii-V-I') {
      const ii = this.key.interval('m2').scale('dorian').chord('m7');
      const V = this.key.interval('P5').scale('mixolydian').chord('7');
      const I = this.key.scale('major').chord('maj7');
      return [ii, V, I];
    }
    // 实现其他和弦进行...
  }
  
  // 获取和弦的可用音阶
  getChordScales(chord) {
    const quality = chord.quality();
    const root = chord.root;
    
    switch(quality) {
      case 'major':
        return [root.scale('ionian'), root.scale('lydian')];
      case 'minor':
        return [root.scale('aeolian'), root.scale('dorian'), root.scale('phrygian')];
      case 'dominant':
        return [root.scale('mixolydian'), root.scale('altered')];
      // 其他和弦类型...
    }
  }
  
  // 推荐即兴音符
  suggestNotes(chord, position = 'upbeat') {
    const scales = this.getChordScales(chord);
    const chordTones = chord.notes().map(n => n.chroma());
    const availableNotes = [];
    
    // 收集可用音符
    scales.forEach(scale => {
      scale.notes().forEach(note => {
        if (!availableNotes.some(n => n.chroma() === note.chroma())) {
          availableNotes.push({
            note: note,
            isChordTone: chordTones.includes(note.chroma()),
            scale: scale.name
          });
        }
      });
    });
    
    // 根据位置推荐不同音符(强拍推荐和弦音,弱拍推荐经过音)
    return availableNotes.filter(n => position === 'downbeat' ? n.isChordTone : true);
  }
}

// 使用示例
const assistant = new ImprovisationAssistant(teoria.note('C4'), 'jazz');
const progression = assistant.generateProgression('ii-V-I');
console.log('推荐和弦进行:', progression.map(c => c.name));

// 为每个和弦推荐即兴音符
progression.forEach(chord => {
  console.log(`\n${chord.name} 推荐音符:`);
  const notes = assistant.suggestNotes(chord);
  notes.forEach(n => {
    console.log(`${n.note.scientific()} (${n.scale}${n.isChordTone ? ', 和弦音' : ''})`);
  });
});

MIDI与Web Audio集成

Teoria.js可以与Web MIDI API和Web Audio API无缝集成,创建完整的音乐应用:

// Web Audio API与Teoria.js集成
class WebAudioSynth {
  constructor() {
    this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
    this.gainNode = this.audioContext.createGain();
    this.gainNode.connect(this.audioContext.destination);
    this.oscillators = {};
  }
  
  // 播放音符
  playNote(note, duration = 1, velocity = 0.7) {
    const now = this.audioContext.currentTime;
    const oscillator = this.audioContext.createOscillator();
    const gain = this.audioContext.createGain();
    
    // 设置振荡器
    oscillator.type = 'sine';
    oscillator.frequency.value = note.fq(); // 使用Teoria.js获取频率
    
    // 设置音量包络
    gain.gain.setValueAtTime(0, now);
    gain.gain.linearRampToValueAtTime(velocity, now + 0.01);
    gain.gain.linearRampToValueAtTime(velocity * 0.8, now + duration * 0.5);
    gain.gain.linearRampToValueAtTime(0, now + duration);
    
    // 连接并开始播放
    oscillator.connect(gain);
    gain.connect(this.gainNode);
    oscillator.start(now);
    
    // 存储振荡器引用以便停止
    const id = note.scientific();
    this.oscillators[id] = oscillator;
    
    // 自动停止
    setTimeout(() => {
      oscillator.stop(now + 0.05);
      delete this.oscillators[id];
    }, duration * 1000);
    
    return oscillator;
  }
  
  // 停止所有音符
  stopAllNotes() {
    Object.values(this.oscillators).forEach(osc => {
      osc.stop(this.audioContext.currentTime + 0.01);
    });
    this.oscillators = {};
  }
  
  // 播放和弦
  playChord(chord, duration = 1, velocity = 0.7) {
    chord.notes().forEach(note => {
      this.playNote(note, duration, velocity);
    });
  }
  
  // 播放音阶
  playScale(scale, duration = 0.5, velocity = 0.7, direction = 'up') {
    let notes = scale.notes();
    if (direction === 'down') notes = notes.reverse();
    
    notes.forEach((note, index) => {
      setTimeout(() => {
        this.playNote(note, duration, velocity);
      }, index * duration * 1000);
    });
  }
}

// 使用示例
const synth = new WebAudioSynth();

// 播放C大调音阶
document.getElementById('playScale').addEventListener('click', () => {
  const scale = teoria.note('C4').scale('major');
  synth.playScale(scale, 0.3);
});

// 播放Cmaj7和弦
document.getElementById('playChord').addEventListener('click', () => {
  const chord = teoria.chord('Cmaj7');
  synth.playChord(chord, 2);
});

// 钢琴键盘交互
document.querySelectorAll('.piano-key').forEach(key => {
  key.addEventListener('mousedown', () => {
    const noteName = key.dataset.note;
    const note = teoria.note(noteName);
    synth.playNote(note, 0.5);
  });
});

第六天:项目实战——构建音乐理论工具包

现在我们将综合运用Teoria.js的各项功能,构建几个实用的音乐理论工具。

工具1:和弦进行生成器

这个工具可以生成常见风格的和弦进行,并提供详细的和声分析:

<div class="chord-progression-generator">
  <h3>和弦进行生成器</h3>
  
  <div class="controls">
    <div>
      <label>调式中心: </label>
      <select id="keySelector">
        <!-- 生成所有12个调式选项 -->
      </select>
    </div>
    
    <div>
      <label>音乐风格: </label>
      <select id="styleSelector">
        <option value="pop">流行</option>
        <option value="rock">摇滚</option>
        <option value="jazz">爵士</option>
        <option value="blues">布鲁斯</option>
        <option value="classical">古典</option>
      </select>
    </div>
    
    <div>
      <label>长度: </label>
      <select id="lengthSelector">
        <option value="4">4小节</option>
        <option value="8">8小节</option>
        <option value="12">12小节</option>
        <option value="16">16小节</option>
      </select>
    </div>
    
    <button id="generateBtn">生成和弦进行</button>
  </div>
  
  <div id="progressionDisplay"></div>
  <div id="analysisDisplay"></div>
  <button id="playProgressionBtn">播放和弦进行</button>
</div>

<script src="path/to/teoria.js"></script>
<script>
// 生成调式选择器选项
const keySelector = document.getElementById('keySelector');
['C', 'C#', 'D', 'Eb', 'E', 'F', 'F#', 'G', 'Ab', 'A', 'Bb', 'B'].forEach(key => {
  const option = document.createElement('option');
  option.value = key;
  option.textContent = key;
  keySelector.appendChild(option);
});

// 和弦进行生成器类
class ChordProgressionGenerator {
  constructor() {
    this.synth = new WebAudioSynth(); // 使用前面定义的WebAudioSynth
    this.progression = [];
    
    // 初始化事件监听
    document.getElementById('generateBtn').addEventListener('click', () => this.generate());
    document.getElementById('playProgressionBtn').addEventListener('click', () => this.play());
  }
  
  // 生成和弦进行
  generate() {
    const key = document.getElementById('keySelector').value;
    const style = document.getElementById('styleSelector').value;
    const length = parseInt(document.getElementById('lengthSelector').value);
    
    // 根据风格和长度生成不同的和弦进行
    this.progression = this.createProgression(key, style, length);
    
    // 显示和弦进行
    this.displayProgression();
    
    // 分析和弦进行
    this.analyzeProgression();
  }
  
  // 创建和弦进行
  createProgression(key, style, length) {
    const rootNote = teoria.note(key + '4');
    const progression = [];
    
    switch(style) {
      case 'pop':
        // 流行音乐常见和弦进行
        const popPatterns = [
          ['I', 'V', 'vi', 'IV'], // 最流行的和弦进行
          ['I', 'IV', 'V', 'V'],
          ['vi', 'IV', 'I', 'V'],
          ['I', 'vi', 'iv', 'V']
        ];
        
        const pattern = popPatterns[Math.floor(Math.random() * popPatterns.length)];
        while (progression.length < length) {
          pattern.forEach(roman => {
            if (progression.length < length) {
              progression.push(this.getChordFromRoman(rootNote, roman));
            }
          });
        }
        break;
        
      case 'blues':
        // 12小节布鲁斯进行
        progression.push(
          this.getChordFromRoman(rootNote, 'I'),
          this.getChordFromRoman(rootNote, 'I'),
          this.getChordFromRoman(rootNote, 'I'),
          this.getChordFromRoman(rootNote, 'I'),
          
          this.getChordFromRoman(rootNote, 'IV'),
          this.getChordFromRoman(rootNote, 'IV'),
          this.getChordFromRoman(rootNote, 'I'),
          this.getChordFromRoman(rootNote, 'I'),
          
          this.getChordFromRoman(rootNote, 'V'),
          this.getChordFromRoman(rootNote, 'IV'),
          this.getChordFromRoman(rootNote, 'I'),
          this.getChordFromRoman(rootNote, 'V')
        );
        break;
        
      // 其他风格的和弦进行生成逻辑...
    }
    
    return progression;
  }
  
  // 根据罗马数字标记获取和弦
  getChordFromRoman(rootNote, roman) {
    const key = rootNote.scale('major');
    let chord;
    
    switch(roman) {
      case 'I':  chord = key.get(1).chord('maj7'); break;
      case 'ii': chord = key.get(2).chord('m7'); break;
      case 'iii': chord = key.get(3).chord('m7'); break;
      case 'IV': chord = key.get(4).chord('maj7'); break;
      case 'V':  chord = key.get(5).chord('7'); break;
      case 'vi': chord = key.get(6).chord('m7'); break;
      case 'vii': chord = key.get(7).chord('dim7'); break;
      // 更多和弦类型...
    }
    
    return chord;
  }
  
  // 显示和弦进行
  displayProgression() {
    const display = document.getElementById('progressionDisplay');
    display.innerHTML = '<h4>和弦进行</h4><div class="chord-grid">';
    
    this.progression.forEach((chord, index) => {
      const chordDiv = document.createElement('div');
      chordDiv.className = 'chord-box';
      chordDiv.innerHTML = `
        <div class="chord-name">${chord.name}</div>
        <div class="chord-notes">${chord.simple().join(', ')}</div>
        <div class="chord-degrees">${index + 1}</div>
      `;
      chordDiv.addEventListener('click', () => this.synth.playChord(chord, 1));
      display.appendChild(chordDiv);
    });
    
    display.innerHTML += '</div>';
  }
  
  // 分析和弦进行
  analyzeProgression() {
    const analysis = document.getElementById('analysisDisplay');
    analysis.innerHTML = '<h4>和声分析</h4>';
    
    // 调式分析
    const key = this.progression[0].root.scale('major');
    analysis.innerHTML += `<p>调式: ${key.tonic.scientific()}大调</p>`;
    
    // 和弦功能分析
    analysis.innerHTML += '<h5>和弦功能分析</h5><ul>';
    this.progression.forEach((chord, index) => {
      const functionName = this.determineChordFunction(chord, key);
      analysis.innerHTML += `<li>第${index + 1}小节: ${chord.name} (${functionName})</li>`;
    });
    analysis.innerHTML += '</ul>';
    
    // 和声节奏分析
    analysis.innerHTML += '<h5>和声节奏</h5>';
    analysis.innerHTML += `<p>平均和弦时长: ${4 / this.progression.length}小节</p>`;
  }
  
  // 确定和弦功能
  determineChordFunction(chord, key) {
    const root = chord.root;
    const interval = teoria.interval(key.tonic, root).toString();
    
    const functions = {
      'P1': '主和弦(I)',
      'M2': '上主和弦(ii)',
      'M3': '中音和弦(iii)',
      'P4': '下属和弦(IV)',
      'P5': '属和弦(V)',
      'M6': '下中音和弦(vi)',
      'm7': '导音和弦(vii°)'
      // 更多和弦功能...
    };
    
    return functions[interval] || '未确定功能';
  }
  
  // 播放和弦进行
  playProgression() {
    this.progression.forEach((chord, index) => {
      setTimeout(() => {
        this.synth.playChord(chord, 0.8);
      }, index * 1000);
    });
  }
}

// 初始化生成器
const generator = new ChordProgressionGenerator();
</script>

工具2:音乐理论考试系统

这个工具可以帮助音乐学习者测试和巩固音乐理论知识:

<div class="music-theory-quiz">
  <h3>音乐理论考试系统</h3>
  
  <div class="quiz-controls">
    <select id="questionType">
      <option value="interval">音程识别</option>
      <option value="chord">和弦识别</option>
      <option value="scale">音阶识别</option>
      <option value="note">音符识别</option>
    </select>
    
    <select id="difficulty">
      <option value="easy">简单</option>
      <option value="medium">中等</option>
      <option value="hard">困难</option>
    </select>
    
    <button id="startQuizBtn">开始考试</button>
  </div>
  
  <div id="quizContainer"></div>
  <div id="scoreContainer"></div>
</div>

<script src="path/to/teoria.js"></script>
<script>
// 音乐理论考试系统
class MusicTheoryQuiz {
  constructor() {
    this.score = 0;
    this.questions = [];
    this.currentQuestion = 0;
    
    document.getElementById('startQuizBtn').addEventListener('click', () => this.startQuiz());
  }
  
  // 开始考试
  startQuiz() {
    this.score = 0;
    this.currentQuestion = 0;
    const questionType = document.getElementById('questionType').value;
    const difficulty = document.getElementById('difficulty').value;
    
    // 生成题目
    this.generateQuestions(questionType, difficulty, 10);
    
    // 显示第一题
    this.displayQuestion();
    
    // 重置分数显示
    document.getElementById('scoreContainer').innerHTML = 
      `<p>得分: ${this.score}/${this.questions.length}</p>`;
  }
  
  // 生成题目
  generateQuestions(type, difficulty, count) {
    this.questions = [];
    
    for (let i = 0; i < count; i++) {
      switch(type) {
        case 'interval':
          this.questions.push(this.generateIntervalQuestion(difficulty));
          break;
        case 'chord':
          this.questions.push(this.generateChordQuestion(difficulty));
          break;
        case 'scale':
          this.questions.push(this.generateScaleQuestion(difficulty));
          break;
        case 'note':
          this.questions.push(this.generateNoteQuestion(difficulty));
          break;
      }
    }
  }
  
  // 生成音程题目
  generateIntervalQuestion(difficulty) {
    // 根据难度选择不同范围的音程
    const intervals = difficulty === 'easy' ? 
      ['M2', 'm2', 'M3', 'm3', 'P4', 'P5', 'M6', 'm6', 'M7', 'm7', 'P8'] :
      difficulty === 'medium' ?
      ['M2', 'm2', 'M3', 'm3', 'P4', 'A4', 'd5', 'P5', 'M6', 'm6', 'M7', 'm7', 'P8'] :
      ['M2', 'm2', 'A2', 'd2', 'M3', 'm3', 'A3', 'd3', 'P4', 'A4', 'd5', 'P5', 'A5', 'd5', 'M6', 'm6', 'A6', 'd6', 'M7', 'm7', 'A7', 'd7', 'P8'];
    
    // 随机选择音程和根音
    const intervalName = intervals[Math.floor(Math.random() * intervals.length)];
    const interval = teoria.interval(intervalName);
    const rootNote = teoria.note.fromKey(36 + Math.floor(Math.random() * 48)); // 随机根音
    const targetNote = rootNote.interval(interval);
    
    // 创建选项
    const options = [intervalName];
    // 添加干扰项
    while (options.length < 4) {
      const distractor = intervals[Math.floor(Math.random() * intervals.length)];
      if (!options.includes(distractor)) options.push(distractor);
    }
    // 打乱选项顺序
    this.shuffleArray(options);
    
    return {
      type: 'interval',
      question: `识别${rootNote.scientific()}到${targetNote.scientific()}的音程`,
      correctAnswer: intervalName,
      options: options,
      audio: () => {
        // 播放音程的函数
        setTimeout(() => this.synth.playNote(rootNote, 0.5), 100);
        setTimeout(() => this.synth.playNote(targetNote, 0.5), 600);
      }
    };
  }
  
  // 生成和弦题目
  generateChordQuestion(difficulty) {
    // 实现和弦题目的生成逻辑
    // ...
  }
  
  // 生成音阶题目
  generateScaleQuestion(difficulty) {
    // 实现音阶题目的生成逻辑
    // ...
  }
  
  // 生成音符题目
  generateNoteQuestion(difficulty) {
    // 实现音符题目的生成逻辑
    // ...
  }
  
  // 显示当前题目
  displayQuestion() {
    const container = document.getElementById('quizContainer');
    const question = this.questions[this.currentQuestion];
    
    container.innerHTML = `
      <div class="question-header">
        <h4>问题 ${this.currentQuestion + 1}/${this.questions.length}</h4>
        <p>${question.question}</p>
      </div>
      <div class="question-options">
        ${question.options.map(option => `
          <div class="quiz-option" data-correct="${option === question.correctAnswer}">
            ${option}
          </div>
        `).join('')}
      </div>
    `;
    
    // 如果有音频播放函数,添加播放按钮
    if (question.audio) {
      const audioBtn = document.createElement('button');
      audioBtn.className = 'play-audio-btn';
      audioBtn.textContent = '播放音频';
      audioBtn.addEventListener('click', question.audio);
      container.appendChild(audioBtn);
    }
    
    // 添加选项点击事件
    document.querySelectorAll('.quiz-option').forEach(option => {
      option.addEventListener('click', () => {
        this.checkAnswer(option.dataset.correct === 'true');
      });
    });
  }
  
  // 检查答案
  checkAnswer(isCorrect) {
    if (isCorrect) {
      this.score++;
      document.getElementById('scoreContainer').innerHTML = 
        `<p>得分: ${this.score}/${this.questions.length}</p>`;
      alert('正确!');
    } else {
      alert(`错误,正确答案是: ${this.questions[this.currentQuestion].correctAnswer}`);
    }
    
    // 下一题或结束
    this.currentQuestion++;
    if (this.currentQuestion < this.questions.length) {
      this.displayQuestion();
    } else {
      this.endQuiz();
    }
  }
  
  // 结束考试
  endQuiz() {
    const container = document.getElementById('quizContainer');
    const scorePercentage = (this.score / this.questions.length) * 100;
    
    container.innerHTML = `
      <div class="quiz-results">
        <h3>考试结束!</h3>
        <p>你的得分: ${this.score}/${this.questions.length} (${scorePercentage.toFixed(1)}%)</p>
        <p>${this.getFeedback(scorePercentage)}</p>
        <button id="restartQuizBtn">再试一次</button>
      </div>
    `;
    
    document.getElementById('restartQuizBtn').addEventListener('click', () => this.startQuiz());
  }
  
  // 获取反馈
  getFeedback(score) {
    if (score >= 90) return '优秀!你的音乐理论知识非常扎实。';
    if (score >= 75) return '良好!继续努力。';
    if (score >= 60) return '及格。还有提升空间。';
    return '需要加强练习。建议复习相关音乐理论知识。';
  }
  
  // 打乱数组顺序
  shuffleArray(array) {
    for (let i = array.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [array[i], array[j]] = [array[j], array[i]];
    }
    return array;
  }
}

// 初始化考试系统
const quizSystem = new MusicTheoryQuiz();
</script>

第七天:项目部署与优化

经过六天的学习,我们已经掌握了Teoria.js的核心功能和高级应用。最后一天,我们将学习如何将音乐应用部署到生产环境并进行优化。

项目构建与打包

使用Webpack打包Teoria.js应用:

// webpack.config.js
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  },
  devtool: 'source-map',
  devServer: {
    contentBase: './dist',
    port: 8080
  }
};

性能优化策略

优化Teoria.js应用性能的关键技巧:

// 1. 缓存常用计算结果
class CachedMusicCalculator {
  constructor() {
    this.chordCache = new Map();
    this.scaleCache = new Map();
    this.intervalCache = new Map();
  }
  
  // 缓存和弦计算
  getChord(root, type) {
    const key = `${root.scientific()}-${type}`;
    if (this.chordCache.has(key)) {
      return this.chordCache.get(key);
    }
    
    const chord = root.chord(type);
    this.chordCache.set(key, chord);
    
    // 限制缓存大小,防止内存泄漏
    if (this.chordCache.size > 1000) {
      const oldestKey = this.chordCache.keys().next().value;
      this.chordCache.delete(oldestKey);
    }
    
    return chord;
  }
  
  // 缓存音阶计算
  getScale(root, type) {
    const key = `${root.scientific()}-${type}`;
    if (this.scaleCache.has(key)) {
      return this.scaleCache.get(key);
    }
    
    const scale = root.scale(type);
    this.scaleCache.set(key, scale);
    
    if (this.scaleCache.size > 1000) {
      const oldestKey = this.scaleCache.keys().next().value;
      this.scaleCache.delete(oldestKey);
    }
    
    return scale;
  }
  
  // 缓存音程计算
  getInterval(fromNote, toNote) {
    const key = `${fromNote.scientific()}-${toNote.scientific()}`;
    if (this.intervalCache.has(key)) {
      return this.intervalCache.get(key);
    }
    
    const interval = teoria.interval(fromNote, toNote);
    this.intervalCache.set(key, interval);
    
    if (this.intervalCache.size > 10000) {
      const oldestKey = this.intervalCache.keys().next().value;
      this.intervalCache.delete(oldestKey);
    }
    
    return interval;
  }
}

// 使用缓存计算器
const musicCalc = new CachedMusicCalculator();
// 第一次计算会缓存结果
const cMajor = musicCalc.getScale(teoria.note('C4'), 'major');
// 第二次计算会直接返回缓存结果
const cMajorAgain = musicCalc.getScale(teoria.note('C4'), 'major');

移动端优化与响应式设计

为确保音乐应用在移动设备上有良好体验,需要进行响应式设计优化:

/* 响应式和弦网格 */
.chord-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 10px;
  margin-top: 20px;
}

.chord-box {
  background: #f5f5f5;
  border: 1px solid #ddd;
  border-radius: 8px;
  padding: 10px;
  text-align: center;
  cursor: pointer;
  transition: all 0.2s;
}

.chord-box:hover {
  background: #e0e0e0;
  transform: translateY(-2px);
}

/* 响应式测验布局 */
.quiz-option {
  background: #f5f5f5;
  border: 1px solid #ddd;
  border-radius: 8px;
  padding: 15px;
  margin: 5px 0;
  cursor: pointer;
}

@media (max-width: 768px) {
  .controls {
    flex-direction: column;
    gap: 10px;
  }
  
  .chord-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  
  .quiz-option {
    padding: 12px;
    font-size: 0.9em;
  }
}

@media (max-width: 480px) {
  .chord-grid {
    grid-template-columns: 1fr;
  }
  
  h3 {
    font-size: 1.2em;
  }
  
  .question-header {
    font-size: 0.9em;
  }
}

总结与后续学习

通过这7天的学习,你已经掌握了Teoria.js的核心功能和实际应用技巧。从基本的音符创建到复杂的和弦进行生成,从音乐理论分析到Web Audio集成,你现在拥有了构建专业音乐应用的能力。

知识回顾

  • 第1天:学习了Note对象的创建和基本操作,能够表示和转换各种音符
  • 第2天:掌握了Interval对象,理解音程的性质和计算方法
  • 第3天:深入学习Chord对象,能够创建和分析各种和弦
  • 第4天:探索Scale对象,生成和转换各种音阶和调式
  • 第5-6天:综合运用Teoria.js构建实际项目,包括和弦生成器和音乐理论考试系统
  • 第7天:学习项目优化和部署技巧,确保应用性能和兼容性

后续学习路径

  1. 深入音乐理论:进一步学习和声学、对位法等高级音乐理论
  2. MIDI应用开发:探索Web MIDI API,实现与外部MIDI设备的交互
  3. 音频可视化:结合Canvas或WebGL实现音乐可视化效果
  4. AI音乐创作:将Teoria.js与机器学习结合,开发音乐创作AI
  5. 音乐教育应用:构建互动式音乐理论学习工具

实用资源推荐

  • Teoria.js官方文档:深入了解每个API的详细用法
  • 音乐理论基础:《音乐理论教程》(Tonal Harmony)
  • Web Audio API文档:MDN Web Audio API指南
  • 音乐编程社区:Web Audio API论坛和Stack Overflow音乐编程板块

现在,是时候将你的音乐创意和编程技能结合起来,开发出令人惊叹的音乐应用了!无论你是想构建音乐教育工具、创作辅助软件还是交互式音乐体验,Teoria.js都将是你不可或缺的强大工具。

祝你在音乐编程的道路上越走越远!

如果你觉得本教程对你有帮助,请点赞、收藏并关注作者,获取更多音乐编程相关内容。下期预告:《使用TensorFlow.js和Teoria.js构建AI作曲助手》

【免费下载链接】teoria Javascript taught Music Theory 【免费下载链接】teoria 项目地址: https://gitcode.com/gh_mirrors/te/teoria

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值