Android Studio Java实现多线程AudioTrack:可启停及动态调参
嘿,我来帮你搞定这个AudioTrack的需求!针对你要的多轨独立播放、可随时停止、实时修改频率这些核心点,我整理了一套实用的Java实现方案,直接在Android Studio里就能用。
核心设计思路
要满足你的需求,关键要做到这几点:
- 每个音频轨道对应一个独立的
AudioTrack实例和播放线程,互不干扰,也不会阻塞主线程 - 用线程安全的变量控制播放状态和参数(比如频率),保证主线程修改后能实时生效
- 妥善管理
AudioTrack的生命周期,避免内存泄漏和资源浪费
完整代码实现
1. 音频轨道播放器类(TrackPlayer)
这个类封装了单个轨道的播放逻辑,包含线程控制、参数修改和资源释放:
import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioTrack; public class TrackPlayer { // 基础音频参数,可根据需求调整 private static final int SAMPLE_RATE = 44100; private static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_OUT_MONO; private static final int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT; private static final int BUFFER_SIZE = AudioTrack.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT); // 线程安全的播放控制和参数变量 private volatile boolean isPlaying = false; private volatile double frequency = 440.0; // 默认A4音 private volatile float volume = 1.0f; private AudioTrack audioTrack; private Thread playbackThread; public TrackPlayer() { // 初始化AudioTrack audioTrack = new AudioTrack( AudioManager.STREAM_MUSIC, SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT, BUFFER_SIZE, AudioTrack.MODE_STREAM ); } // 开始播放 public void start() { if (isPlaying) return; isPlaying = true; playbackThread = new Thread(this::playLoop); playbackThread.start(); } // 停止播放并释放资源 public void stop() { isPlaying = false; if (playbackThread != null) { try { playbackThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } playbackThread = null; } if (audioTrack != null) { if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { audioTrack.stop(); } audioTrack.release(); audioTrack = null; } } // 修改频率(实时生效) public void setFrequency(double newFreq) { this.frequency = newFreq; } // 修改音量(实时生效) public void setVolume(float newVolume) { this.volume = Math.max(0.0f, Math.min(1.0f, newVolume)); // 限制在0-1之间 } // 核心播放循环:实时生成正弦波并写入AudioTrack private void playLoop() { audioTrack.play(); short[] buffer = new short[BUFFER_SIZE]; double angle = 0.0; final double twoPi = 2 * Math.PI; while (isPlaying) { // 每次循环重新计算步进值(因为频率可能被实时修改) double currentStep = twoPi * frequency / SAMPLE_RATE; for (int i = 0; i < buffer.length; i++) { // 生成正弦波样本 double sample = Math.sin(angle) * Short.MAX_VALUE * volume; buffer[i] = (short) sample; angle += currentStep; // 重置角度防止数值溢出 if (angle > twoPi) { angle -= twoPi; } } // 将生成的音频数据写入AudioTrack audioTrack.write(buffer, 0, buffer.length); } } }
2. Activity中的调用示例
在你的Activity里,你可以创建多个TrackPlayer实例,通过按钮控制播放、停止和参数修改:
import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.SeekBar; import androidx.appcompat.app.AppCompatActivity; public class MainActivity extends AppCompatActivity { private TrackPlayer track1; private TrackPlayer track2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 初始化两个独立轨道 track1 = new TrackPlayer(); track2 = new TrackPlayer(); // 播放按钮:启动双轨,给第二轨设置不同初始频率 Button btnPlay = findViewById(R.id.btn_play); btnPlay.setOnClickListener(v -> { track1.start(); track2.start(); track2.setFrequency(523.25); // 设置为C5音 }); // 停止按钮:停止所有轨道 Button btnStop = findViewById(R.id.btn_stop); btnStop.setOnClickListener(v -> { track1.stop(); track2.stop(); }); // 调节第一轨频率的SeekBar(示例范围:200-800Hz) SeekBar freqSeekBar = findViewById(R.id.seekbar_freq); freqSeekBar.setMax(600); freqSeekBar.setProgress(240); // 默认对应440Hz(200+240) freqSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser) { double newFreq = 200 + progress; track1.setFrequency(newFreq); } } @Override public void onStartTrackingTouch(SeekBar seekBar) {} @Override public void onStopTrackingTouch(SeekBar seekBar) {} }); } @Override protected void onDestroy() { super.onDestroy(); // 页面销毁时务必停止所有轨道,避免资源泄漏 track1.stop(); track2.stop(); } }
关键细节说明
- volatile变量的作用:
isPlaying、frequency这些变量用volatile修饰,是为了保证主线程修改后,播放线程能立刻看到最新值,避免线程缓存导致的参数不实时问题。 - AudioTrack模式选择:用
MODE_STREAM而不是MODE_STATIC,因为我们是实时生成音频数据,不需要提前把所有数据加载到内存。 - 资源释放:在
stop()方法和Activity的onDestroy()里都要释放AudioTrack,否则会造成内存泄漏,甚至影响其他音频播放。 - 多轨性能注意:每个轨道都会占用一定的CPU和内存资源,不要创建过多轨道(比如超过4-6个),否则可能导致卡顿或音频爆音。
- 参数范围控制:比如音量限制在0-1之间,频率可以根据需求设置合理范围(比如20Hz-20000Hz,人耳可听范围)。
这样实现下来,你就能同时播放多个独立的正弦波轨道,随时停止,还能实时修改每个轨道的频率、音量等参数啦!
内容的提问来源于stack exchange,提问作者Tessmus
相关产品推荐
相关产品推荐

