You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:26:29