首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >用Java MIDI实现音乐播放:生日快乐歌演奏程序详解

用Java MIDI实现音乐播放:生日快乐歌演奏程序详解

作者头像
红目香薰
发布2025-12-16 14:51:20
发布2025-12-16 14:51:20
1430
举报
文章被收录于专栏:CSDNToQQCodeCSDNToQQCode

前言

在计算机音乐领域,MIDI(Musical Instrument Digital Interface,音乐数字接口)是一种重要的技术标准,它允许电子乐器、计算机和其他设备相互连接并通信。Java平台通过其javax.sound.midi包提供了强大的MIDI处理能力,使开发者能够在应用程序中创建、编辑和播放音乐。

本文将详细介绍一个基于Java MIDI技术的音乐播放应用,该应用能够演奏经典的"生日快乐歌"。通过这个简单而实用的例子,我们将探索Java MIDI编程的基础知识,包括音符表示、音乐序列创建、音符添加以及音乐播放等核心概念。无论你是音乐爱好者还是Java编程学习者,这个项目都能帮助你理解如何将音乐与编程结合起来。

演绎效果

https://live.csdn.net/v/487674

技术说明

Java MIDI API概述

Java Sound API中的MIDI部分提供了一套完整的接口和类,用于处理MIDI数据和控制MIDI设备。在我们的应用中,主要使用了以下几个核心组件:

  1. Sequencer:MIDI序列播放器,负责按照指定的速度和顺序播放MIDI事件。
  2. Sequence:包含音乐数据的容器,由多个Track组成。
  3. Track:音轨,包含按时间顺序排列的MIDI事件。
  4. MidiEvent:MIDI事件,表示在特定时间点发生的MIDI消息。
  5. ShortMessage:最常见的MIDI消息类型,用于表示音符开始、音符结束等事件。
音符表示方法

在MIDI标准中,音符由数字表示。中央C(C4)的MIDI值为60,每升高一个半音,值增加1。我们的程序中定义了从C4到C5的常量:

代码语言:javascript
复制
private static final int C4 = 60;  // 中央C
private static final int D4 = 62;  // D音
private static final int E4 = 64;  // E音
private static final int F4 = 65;  // F音
private static final int G4 = 67;  // G音
private static final int A4 = 69;  // A音
private static final int B4 = 71;  // B音
private static final int C5 = 72;  // 高八度C
音符时值定义

音乐中的节奏由不同时值的音符组成。在我们的程序中,定义了几种常见的音符时值:

代码语言:javascript
复制
private static final double QUARTER = 1.0;       // 四分音符
private static final double HALF = 2.0;          // 二分音符
private static final double EIGHTH = 0.5;        // 八分音符
private static final double SIXTEENTH = 0.25;    // 十六分音符
private static final double DOTTED_QUARTER = 1.5; // 附点四分音符
private static final double WHOLE = 4.0;         // 全音符

这些值表示相对时长,例如一个二分音符的时长是四分音符的两倍。

核心代码解析

1. 初始化MIDI系统
代码语言:javascript
复制
Sequencer sequencer = MidiSystem.getSequencer();
sequencer.open();
            
Sequence sequence = new Sequence(Sequence.PPQ, 4);
            
Track track = sequence.createTrack();

这段代码获取MIDI序列器,创建一个新的序列,并在序列中创建一个音轨。Sequence.PPQ表示使用每四分音符的脉冲数(Pulses Per Quarter Note)作为时间单位,值4表示每个四分音符包含4个脉冲。

2. 添加音符的方法
代码语言:javascript
复制
private static int addNote(Track track, int note, double duration, int startTick, int ticksPerBeat, int velocity) {
    try {
        int noteDuration = (int)(duration * ticksPerBeat);
        
        ShortMessage noteOn = new ShortMessage();
        noteOn.setMessage(ShortMessage.NOTE_ON, 0, note, velocity);
        track.add(new MidiEvent(noteOn, startTick));
        
        ShortMessage noteOff = new ShortMessage();
        noteOff.setMessage(ShortMessage.NOTE_OFF, 0, note, 0);
        track.add(new MidiEvent(noteOff, startTick + noteDuration));
        
        return startTick + noteDuration;
    } catch (Exception e) {
        System.out.println("Error adding note: " + e.getMessage());
        return startTick;
    }
}

这个方法是程序的核心,它负责向音轨中添加音符。每个音符由两个MIDI事件组成:NOTE_ON(开始播放)和NOTE_OFF(停止播放)。方法参数包括:

  • track:要添加音符的音轨
  • note:音符的MIDI值
  • duration:音符的持续时间(以音符时值表示)
  • startTick:音符开始的时间点
  • ticksPerBeat:每拍的脉冲数
  • velocity:音符的力度(音量)

方法返回下一个音符应该开始的时间点。

3. 生日快乐歌的音符序列
代码语言:javascript
复制
time = addNote(track, C4, QUARTER, time, ticksPerBeat, 100);
time = addNote(track, C4, QUARTER, time, ticksPerBeat, 100);
time = addNote(track, D4, HALF, time, ticksPerBeat, 100);
time = addNote(track, C4, HALF, time, ticksPerBeat, 100);
time = addNote(track, F4, HALF, time, ticksPerBeat, 100);
time = addNote(track, E4, WHOLE, time, ticksPerBeat, 100);
// ... 更多音符

这段代码按顺序添加生日快乐歌的所有音符。每次调用addNote方法后,time变量会更新为下一个音符的开始时间,确保音符按顺序播放而不重叠。

4. 播放音乐
代码语言:javascript
复制
sequencer.setSequence(sequence);
sequencer.setTempoInBPM(TEMPO);
sequencer.start();

while (sequencer.isRunning()) {
    Thread.sleep(1000);
}

sequencer.close();

这段代码设置序列器的序列和速度(每分钟拍数),然后开始播放。程序会在一个循环中等待,直到音乐播放完毕,然后关闭序列器释放资源。

总结

这个Java MIDI音乐播放应用展示了如何使用Java的声音API创建和播放音乐。通过分析,我们可以将程序分为以下几个核心模块:

1. 音符定义模块

定义了音符的音高(C4、D4等)和时值(四分音符、二分音符等),为音乐创作提供了基础元素。

2. MIDI初始化模块

负责设置MIDI系统,创建序列器、序列和音轨,为音乐播放做准备。

3. 音符处理模块

包含添加音符的核心方法,处理音符的开始和结束事件,控制音符的时长和力度。

4. 音乐序列模块

按照特定的顺序和节奏组织音符,形成完整的音乐作品(本例中是生日快乐歌)。

5. 播放控制模块

负责启动音乐播放,监控播放状态,并在播放结束后释放资源。

通过这个简单而实用的例子,我们不仅学习了Java MIDI编程的基础知识,还了解了如何将音乐理论与编程实践结合起来。这个应用可以进一步扩展,例如添加更多乐器音色、实现和弦播放、创建图形用户界面等,为音乐创作和教育提供更丰富的功能。

完整代码

代码语言:javascript
复制
import javax.sound.midi.*;

public class Demo3 {
    private static final int C4 = 60;
    private static final int D4 = 62;
    private static final int E4 = 64;
    private static final int F4 = 65;
    private static final int G4 = 67;
    private static final int A4 = 69;
    private static final int B4 = 71;
    private static final int C5 = 72;
   
    private static final double QUARTER = 1.0;
    private static final double HALF = 2.0;
    private static final double EIGHTH = 0.5;
    private static final double SIXTEENTH = 0.25;
    private static final double DOTTED_QUARTER = 1.5;
    private static final double WHOLE = 4.0;
    
    private static final int TEMPO = 100;
    
    public static void main(String[] args) {
        
        try {
            Sequencer sequencer = MidiSystem.getSequencer();
            sequencer.open();
            
            Sequence sequence = new Sequence(Sequence.PPQ, 4);
            
            Track track = sequence.createTrack();
            
            track.add(createEvent(ShortMessage.PROGRAM_CHANGE, 1, 0, 0, 0));
            
            int ticksPerBeat = sequence.getResolution();
            
            int time = 0;
            
            time = addNote(track, C4, QUARTER, time, ticksPerBeat, 100);
            time = addNote(track, C4, QUARTER, time, ticksPerBeat, 100);
            time = addNote(track, D4, HALF, time, ticksPerBeat, 100);
            time = addNote(track, C4, HALF, time, ticksPerBeat, 100);
            time = addNote(track, F4, HALF, time, ticksPerBeat, 100);
            time = addNote(track, E4, WHOLE, time, ticksPerBeat, 100);
            
            time = addNote(track, C4, QUARTER, time, ticksPerBeat, 100);
            time = addNote(track, C4, QUARTER, time, ticksPerBeat, 100);
            time = addNote(track, D4, HALF, time, ticksPerBeat, 100);
            time = addNote(track, C4, HALF, time, ticksPerBeat, 100);
            time = addNote(track, G4, HALF, time, ticksPerBeat, 100);
            time = addNote(track, F4, WHOLE, time, ticksPerBeat, 100);
            
            time = addNote(track, C4, QUARTER, time, ticksPerBeat, 100);
            time = addNote(track, C4, QUARTER, time, ticksPerBeat, 100);
            time = addNote(track, C5, HALF, time, ticksPerBeat, 100);
            time = addNote(track, A4, HALF, time, ticksPerBeat, 100);
            time = addNote(track, F4, HALF, time, ticksPerBeat, 100);
            time = addNote(track, E4, HALF, time, ticksPerBeat, 100);
            time = addNote(track, D4, WHOLE, time, ticksPerBeat, 100);
            
            time = addNote(track, B4, QUARTER, time, ticksPerBeat, 100);
            time = addNote(track, B4, QUARTER, time, ticksPerBeat, 100);
            time = addNote(track, A4, HALF, time, ticksPerBeat, 100);
            time = addNote(track, F4, HALF, time, ticksPerBeat, 100);
            time = addNote(track, G4, HALF, time, ticksPerBeat, 100);
            time = addNote(track, F4, WHOLE, time, ticksPerBeat, 100);
            
            sequencer.setSequence(sequence);
            sequencer.setTempoInBPM(TEMPO);
            sequencer.start();
            
            
            while (sequencer.isRunning()) {
                Thread.sleep(1000);
            }
            
            sequencer.close();
            
        } catch (Exception e) {
            System.out.println("Error playing music: " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    private static int addNote(Track track, int note, double duration, int startTick, int ticksPerBeat, int velocity) {
        try {
            int noteDuration = (int)(duration * ticksPerBeat);
            
            ShortMessage noteOn = new ShortMessage();
            noteOn.setMessage(ShortMessage.NOTE_ON, 0, note, velocity);
            track.add(new MidiEvent(noteOn, startTick));
            
            ShortMessage noteOff = new ShortMessage();
            noteOff.setMessage(ShortMessage.NOTE_OFF, 0, note, 0);
            track.add(new MidiEvent(noteOff, startTick + noteDuration));
            
            return startTick + noteDuration;
        } catch (Exception e) {
            System.out.println("Error adding note: " + e.getMessage());
            return startTick;
        }
    }
    
    private static MidiEvent createEvent(int command, int channel, int data1, int data2, long tick) {
        try {
            ShortMessage message = new ShortMessage();
            message.setMessage(command, channel, data1, data2);
            return new MidiEvent(message, tick);
        } catch (Exception e) {
            return null;
        }
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-08-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
    • 演绎效果
    • 技术说明
      • Java MIDI API概述
      • 音符表示方法
      • 音符时值定义
    • 核心代码解析
      • 1. 初始化MIDI系统
      • 2. 添加音符的方法
      • 3. 生日快乐歌的音符序列
      • 4. 播放音乐
    • 总结
      • 1. 音符定义模块
      • 2. MIDI初始化模块
      • 3. 音符处理模块
      • 4. 音乐序列模块
      • 5. 播放控制模块
    • 完整代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档