AudioTrack
本篇文章目标
- 了解 java 层 Audio Track 的使用流程和具体参数的作用
- 了解 C++ 层 Audio Track 的使用流程和具体参数的作用
java 层 Audio Track 的使用
Audio Track 官方文档:
https://developer.android.com/reference/android/media/AudioTrack
Audio Track 源码:
Audio Track java demo 代码
frameworks/base/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioTrackTest.java
public void testSetPlaybackRate() throws Exception {
// constants for test
final String TEST_NAME = "testSetPlaybackRate";
final int TEST_SR = 22050;
final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
//-------- initialization --------------
int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT,
minBuffSize, TEST_MODE);
byte data[] = new byte[minBuffSize/2];
//-------- test --------------
track.write(data, 0, data.length);
track.write(data, 0, data.length);
assumeTrue(TEST_NAME, track.getState() == AudioTrack.STATE_INITIALIZED);
track.play();
assertTrue(TEST_NAME, track.setPlaybackRate((int)(TEST_SR/2)) == AudioTrack.SUCCESS);
//-------- tear down --------------
track.release();
}
Audio Track 创建参数解析
Audio Track 数据传输模式
- AudioTrack.MODE_STREAM
在流模式(MODE_STREAM)下,音频数据是动态写入到AudioTrack对象中的。这意味着你可以一边播放一边不断地提供数据。这种模式适用于持续不断的音频流,比如音乐播放或者网络音频流。流模式通常用于播放较长的音频文件或者实时的音频流,因为它不需要一次性将所有的音频数据加载到内存中。
使用流模式时,应用程序需要不断地调用write()方法来提供新的音频数据。AudioTrack会处理这些数据,将它们发送到音频缓冲区,然后由音频硬件进行播放。
- AudioTrack.MODE_STATIC
静态模式(MODE_STATIC)允许应用程序一次性将所有音频数据加载到AudioTrack中。这种模式适合播放时间较短的音频片段,如游戏的音效。由于数据是预先加载的,因此可以立即播放,而且在播放时不会有延迟。
在静态模式下,整个音频样本是在创建AudioTrack对象时一次性写入的。之后,可以重复播放这些数据而无需再次写入。这种模式对于资源有限的环境非常有用,因为它减少了CPU的使用,但是它受到可用内存大小的限制。
stream type
AudioAttribute
基本使用流程
- new Audio Track
- 写数据
- 播放数据
- release Audio Track
C++ 层 Audio Track 的使用
Audio Track C++ demo 代码:
bp 文件修改:
cc_test {
name: "atdemo",
gtest: false,
srcs: ["audio_track_demo.cpp"],
shared_libs: [
"liblog",
"libcutils",
"libutils",
"libbinder",
"libhardware_legacy",
"libmedia",
"libaudioclient",
],
header_libs: [
"libmediametrics_headers",
],
cflags: [
"-Wall",
"-Werror",
"-Wno-error=deprecated-declarations",
"-Wunused",
"-Wunreachable-code",
],
}
产物路径:Android12\out\target\product\tcl9618\data\nativetest
demo代码:
#define LOG_TAG "atdemo"
#include <stdlib.h>
#include <stdio.h>
#include <cutils/properties.h>
#include <media/AudioSystem.h>
#include <media/AudioTrack.h>
#include <math.h>
#include <binder/MemoryDealer.h>
#include <binder/MemoryHeapBase.h>
#include <binder/MemoryBase.h>
#include <binder/ProcessState.h>
#include <utils/Log.h>
#include <fcntl.h>
namespace android {
class AudioTrackTest{
public:
AudioTrackTest(void);
~AudioTrackTest() {};
void Execute(void);
int Test01();
int Test02();
void Generate(short *buffer, long bufferSz, long amplitude, unsigned long &phi, long dPhi);
void InitSine();
short ComputeSine(long amplitude, long phi);
#define SIN_SZ 1024
short sin1024[SIN_SZ]; // sine table 2*pi = 1024
};
/************************************************************
*
* Constructor
*
************************************************************/
AudioTrackTest::AudioTrackTest(void) {
InitSine(); // init sine table
}
/************************************************************
*
*
************************************************************/
void AudioTrackTest::Execute(void) {
if (Test01() == 0) {
printf("01 passed\n");
} else {
printf("01 failed\n");
}
if (Test02() == 0) {
printf("02 passed\n");
} else {
printf("02 failed\n");
}
}
/************************************************************
*
* Shared memory test
*
************************************************************/
#define BUF_SZ 441000
int AudioTrackTest::Test01() {
printf("Test01\n");
sp<MemoryDealer> heap;
sp<IMemory> iMem;
uint8_t* data;
short smpBuf[BUF_SZ];
long rate = 44100;
unsigned long phi;
unsigned long dPhi;
long amplitude;
long freq = 1237;
float f0;
f0 = pow(2., 32.) * freq / (float)rate;
dPhi = (unsigned long)f0;
amplitude = 1000;
phi = 0;
Generate(smpBuf, BUF_SZ, amplitude, phi, dPhi); // fill buffer
printf("before loop\n");
for (int i = 0; i < 1; i++) {
printf("new MemoryDealer\n");
heap = new MemoryDealer(1024*1024*4, "AudioTrack Heap Base");
iMem = heap->allocate(BUF_SZ*sizeof(short));
data = static_cast<uint8_t*>(iMem->unsecurePointer());
memcpy(data, smpBuf, BUF_SZ*sizeof(short));
printf("new AudioTrack\n");
sp<AudioTrack> track = new AudioTrack(AUDIO_STREAM_MUSIC,// stream type
rate,
AUDIO_FORMAT_PCM_16_BIT,// word length, PCM
AUDIO_CHANNEL_OUT_MONO,
iMem);
printf("track initCheck\n");
status_t status = track->initCheck();
if(status != NO_ERROR) {
track.clear();
printf("Failed for initCheck()\n");
return -1;
}
printf("set volume 1.0\n");
track->setVolume(1.0f);
// start play
printf("start\n");
track->start();
printf("sleep 2s\n");
usleep(2000000);
printf("stop\n");
track->stop();
iMem.clear();
heap.clear();
usleep(20000);
}
return 0;
}
//#define FRAME_COUNT 1024
//#define pcm_file "/data/audio.pcm"
int AudioTrackTest::Test02() {
// long rate = 44100;
// int fd = 0;
// int length = 0;
size_t MinFrameCount = 0;
// unsigned char mBuffer[FRAME_COUNT];
// fd = open(pcm_file, O_RDWR);
// if (fd < 0) {
// printf("%s open failed\n", pcm_file);
// return -1;
// }
short smpBuf[BUF_SZ];
long rate = 44100;
unsigned long phi;
unsigned long dPhi;
long amplitude;
long freq = 1237;
float f0;
f0 = pow(2., 32.) * freq / (float)rate;
dPhi = (unsigned long)f0;
amplitude = 1000;
phi = 0;
Generate(smpBuf, BUF_SZ, amplitude, phi, dPhi); // fill buffer
AudioTrack::getMinFrameCount(&MinFrameCount, AUDIO_STREAM_MUSIC, rate);
printf("MinFrameCount = %zu\n", MinFrameCount);
sp<AudioTrack> track = new AudioTrack(AUDIO_STREAM_MUSIC, // stream type
rate,
AUDIO_FORMAT_PCM_16_BIT, // word length, PCM
AUDIO_CHANNEL_OUT_STEREO, 0);
status_t status = track->initCheck();
if (status != NO_ERROR) {
track.clear();
printf("Failed for initCheck()");
return -1;
}
printf("set volume 1.0\n");
track->setVolume(1.0f);
// start play
printf("start");
track->start();
int us;
for (int i = 0; i < 2; i++) {
Generate(smpBuf, BUF_SZ, amplitude, phi, dPhi); // fill buffer
// length = read(fd, mBuffer, FRAME_COUNT);
track->write(smpBuf, BUF_SZ, true);
us = BUF_SZ * 1000 / (rate * 2 * 16);
usleep(us);
printf("write data:%d\n", i);
}
usleep(20000);
printf("stop");
track->stop();
usleep(20000);
return 0;
}
/************************************************************
*
* Generate a mono buffer
* Error is less than 3lsb
*
************************************************************/
void AudioTrackTest::Generate(short *buffer, long bufferSz, long amplitude, unsigned long &phi, long dPhi)
{
// fill buffer
for(int i0=0; i0<bufferSz; i0++) {
buffer[i0] = ComputeSine( amplitude, phi);
phi += dPhi;
}
}
/************************************************************
*
* Generate a sine
* Error is less than 3lsb
*
************************************************************/
short AudioTrackTest::ComputeSine(long amplitude, long phi)
{
long pi13 = 25736; // 2^13*pi
long sample;
long l0, l1;
sample = (amplitude*sin1024[(phi>>22) & 0x3ff]) >> 15;
// correct with interpolation
l0 = (phi>>12) & 0x3ff; // 2^20 * x / (2*pi)
l1 = (amplitude*sin1024[((phi>>22) + 256) & 0x3ff]) >> 15; // 2^15*cosine
l0 = (l0 * l1) >> 10;
l0 = (l0 * pi13) >> 22;
sample = sample + l0;
return (short)sample;
}
/************************************************************
*
* init sine table
*
************************************************************/
void AudioTrackTest::InitSine(void) {
printf("InitSine\n");
double phi = 0;
double dPhi = 2 * M_PI / SIN_SZ;
for(int i0 = 0; i0<SIN_SZ; i0++) {
long d0;
d0 = 32768. * sin(phi);
phi += dPhi;
if(d0 >= 32767) d0 = 32767;
if(d0 <= -32768) d0 = -32768;
sin1024[i0] = (short)d0;
}
}
/************************************************************
*
* main in name space
*
************************************************************/
int main() {
printf("android main in\n");
ProcessState::self()->startThreadPool();
AudioTrackTest *test;
test = new AudioTrackTest();
printf("test->Execute()\n");
test->Execute();
delete test;
return 0;
}
}
/************************************************************
*
* global main
*
************************************************************/
int main() {
printf("main in\n");
return android::main();
}