javax.sound音频API全面解析:从基础使用到高级应用

2024-06-21 李腾 133 次阅读 0 次点赞
本文全面介绍Java平台提供的javax.sound音频处理API,详细解析sampled和midi两个核心子包的功能与使用方法。通过四个完整的实战示例:WAV文件播放、实时音频生成、MIDI音乐播放与创作、音频录制功能,帮助开发者快速掌握Java音频编程技术。每个示例都包含详细的代码注释和异常处理,适合不同层次的Java开发者学习和参考。

javax.sound 包是 Java 平台提供的用于音频处理的 API,主要包含两个子包:

1、javax.sound.sampled - 处理采样音频(如 WAV、AIFF 等格式)

2、javax.sound.midi - 处理 MIDI 音乐数据

核心类概览

javax.sound.sampled 包

1、AudioSystem - 音频系统的入口点

2、AudioFormat - 描述音频数据格式

3、AudioInputStream - 音频数据输入流

4、Clip - 用于播放短音频片段

5、SourceDataLine - 用于实时音频播放

javax.sound.midi 包

1、Sequencer - MIDI 序列器

2、Synthesizer - MIDI 合成器

3、Sequence - MIDI 序列

4、MidiEvent - MIDI 事件

示例代码

1. 播放音频文件 (WAV)

import javax.sound.sampled.*;
import java.io.File;
import java.io.IOException;

public class AudioPlayer {
    
    public static void playAudioFile(String filePath) {
        try {
            // 获取音频输入流
            AudioInputStream audioStream = AudioSystem.getAudioInputStream(new File(filePath));
            
            // 获取音频格式
            AudioFormat format = audioStream.getFormat();
            
            // 创建数据行信息
            DataLine.Info info = new DataLine.Info(Clip.class, format);
            
            // 检查系统是否支持该格式
            if (!AudioSystem.isLineSupported(info)) {
                System.out.println("不支持的音频格式");
                return;
            }
            
            // 获取并打开音频剪辑
            Clip audioClip = (Clip) AudioSystem.getLine(info);
            audioClip.open(audioStream);
            
            // 播放音频
            audioClip.start();
            
            // 等待播放完成
            while (audioClip.isRunning()) {
                Thread.sleep(100);
            }
            
            // 关闭资源
            audioClip.close();
            audioStream.close();
            
        } catch (UnsupportedAudioFileException | IOException | 
                 LineUnavailableException | InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        playAudioFile("example.wav");
    }
}

2. 实时音频播放

import javax.sound.sampled.*;
import java.io.ByteArrayInputStream;
import java.io.IOException;

public class ToneGenerator {
    
    public static void generateTone(int frequency, int duration) {
        try {
            // 音频参数
            float sampleRate = 44100;
            int sampleSizeInBits = 16;
            int channels = 1;
            boolean signed = true;
            boolean bigEndian = false;
            
            // 创建音频格式
            AudioFormat format = new AudioFormat(sampleRate, sampleSizeInBits, 
                                               channels, signed, bigEndian);
            
            // 创建数据行
            DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
            SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info);
            line.open(format);
            line.start();
            
            // 生成正弦波数据
            byte[] buffer = generateSineWave(frequency, duration, sampleRate);
            
            // 播放音频
            line.write(buffer, 0, buffer.length);
            
            // 等待播放完成并清理
            line.drain();
            line.close();
            
        } catch (LineUnavailableException e) {
            e.printStackTrace();
        }
    }
    
    private static byte[] generateSineWave(int frequency, int duration, float sampleRate) {
        int numSamples = (int) (duration * sampleRate / 1000);
        byte[] output = new byte[numSamples * 2]; // 16-bit = 2 bytes per sample
        
        for (int i = 0; i < numSamples; i++) {
            double angle = 2.0 * Math.PI * i / (sampleRate / frequency);
            short sample = (short) (Short.MAX_VALUE * Math.sin(angle));
            
            // 将16位样本转换为字节
            output[2 * i] = (byte) (sample & 0xFF);
            output[2 * i + 1] = (byte) ((sample >> 8) & 0xFF);
        }
        
        return output;
    }
    
    public static void main(String[] args) {
        // 播放 440Hz (A4) 音调,持续 2 秒
        generateTone(440, 2000);
    }
}

3. MIDI 音乐播放

import javax.sound.midi.*;
import java.io.File;
import java.io.IOException;

public class MidiPlayer {
    
    public static void playMidiFile(String filePath) {
        try {
            // 获取 MIDI 序列
            Sequence sequence = MidiSystem.getSequence(new File(filePath));
            
            // 获取序列器
            Sequencer sequencer = MidiSystem.getSequencer();
            sequencer.open();
            sequencer.setSequence(sequence);
            
            // 播放
            sequencer.start();
            
            // 等待播放完成
            while (sequencer.isRunning()) {
                Thread.sleep(100);
            }
            
            // 关闭序列器
            sequencer.close();
            
        } catch (InvalidMidiDataException | IOException | 
                 MidiUnavailableException | InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    public static void createAndPlayMidi() {
        try {
            // 创建新的序列
            Sequence sequence = new Sequence(Sequence.PPQ, 24);
            Track track = sequence.createTrack();
            
            // 设置音色(程序改变事件)
            ShortMessage programChange = new ShortMessage();
            programChange.setMessage(ShortMessage.PROGRAM_CHANGE, 0, 0, 0); // 钢琴音色
            track.add(new MidiEvent(programChange, 0));
            
            // 添加音符事件(C大调音阶)
            int[] notes = {60, 62, 64, 65, 67, 69, 71, 72}; // C4 到 C5
            long tick = 0;
            
            for (int note : notes) {
                // 音符开始
                ShortMessage noteOn = new ShortMessage();
                noteOn.setMessage(ShortMessage.NOTE_ON, 0, note, 93);
                track.add(new MidiEvent(noteOn, tick));
                
                // 音符结束
                ShortMessage noteOff = new ShortMessage();
                noteOff.setMessage(ShortMessage.NOTE_OFF, 0, note, 0);
                track.add(new MidiEvent(noteOff, tick + 48));
                
                tick += 48;
            }
            
            // 播放序列
            Sequencer sequencer = MidiSystem.getSequencer();
            sequencer.open();
            sequencer.setSequence(sequence);
            sequencer.start();
            
            // 等待播放完成
            while (sequencer.isRunning()) {
                Thread.sleep(100);
            }
            
            sequencer.close();
            
        } catch (InvalidMidiDataException | MidiUnavailableException | 
                 InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        // 播放 MIDI 文件
        // playMidiFile("example.mid");
        
        // 创建并播放简单的 MIDI 序列
        createAndPlayMidi();
    }
}

4. 音频录制

import javax.sound.sampled.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class AudioRecorder {
    
    private static final int RECORD_TIME = 5000; // 录制5秒
    private AudioFormat format;
    private TargetDataLine targetLine;
    
    public AudioRecorder() {
        // 设置音频格式
        format = new AudioFormat(44100, 16, 1, true, false);
    }
    
    public void startRecording() {
        try {
            // 获取目标数据行
            DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
            targetLine = (TargetDataLine) AudioSystem.getLine(info);
            targetLine.open(format);
            targetLine.start();
            
            System.out.println("开始录制...");
            
            // 创建线程进行录制
            Thread recordingThread = new Thread(() -> {
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                
                long startTime = System.currentTimeMillis();
                
                while (System.currentTimeMillis() - startTime < RECORD_TIME) {
                    int bytesRead = targetLine.read(buffer, 0, buffer.length);
                    if (bytesRead > 0) {
                        out.write(buffer, 0, bytesRead);
                    }
                }
                
                // 停止录制
                targetLine.stop();
                targetLine.close();
                
                System.out.println("录制完成");
                
                // 可以在这里保存或处理录制的音频数据
                byte[] audioData = out.toByteArray();
                System.out.println("录制数据大小: " + audioData.length + " 字节");
                
            });
            
            recordingThread.start();
            
        } catch (LineUnavailableException e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        AudioRecorder recorder = new AudioRecorder();
        recorder.startRecording();
    }
}

使用注意事项

1、异常处理:所有音频操作都需要处理可能的异常

2、资源管理:使用后务必关闭音频流和数据线

3、格式兼容性:检查系统是否支持特定的音频格式

4、性能考虑:实时音频处理需要注意缓冲区大小和延迟

最后更新于4月前
本文由人工编写,AI优化,转载请注明原文地址: Java音频处理完全指南:javax.sound使用详解与实战代码

评论 (0)

发表评论

昵称:加载中...

暂无评论,快来发表第一条评论吧!