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

纯C++ Android应用中AAudio播放WAV文件失败问题排查求助

纯C++ Android应用中AAudio播放WAV文件失败排查

基本信息与问题

尝试在纯C++ Android应用中使用AAudio播放.wav文件,但代码无法正常运行,需排查失效原因。

WAV文件信息

使用mediainfo generated.wav获取的文件信息:

General
Complete name                            : generated.wav
Format                                   : Wave
Format settings                          : PcmWaveformat
File size                                : 19.5 KiB
Duration                                 : 624 ms
Overall bit rate mode                    : Constant
Overall bit rate                         : 257 kb/s

Audio
Format                                   : PCM
Format settings                          : Little / Signed
Codec ID                                 : 1
Duration                                 : 624 ms
Bit rate mode                            : Constant
Bit rate                                 : 256 kb/s
Channel(s)                               : 1 channel
Sampling rate                            : 16.0 kHz
Bit depth                                : 16 bits
Stream size                              : 19.5 KiB (100%)

代码实现

JNI播放逻辑

extern "C"
JNIEXPORT void JNICALL
Java_com_example_demojavacpp_MainActivity_playAudioFromFile(JNIEnv *env, jobject thiz, jstring filePath) {
    const char* filename = env->GetStringUTFChars(filePath, 0);

    // Open the WAV file
    FILE* file = fopen(filename, "rb");
    if (file == nullptr) {
        __android_log_print(ANDROID_LOG_ERROR, "TTS", "Failed to open file: %s", filename);
        env->ReleaseStringUTFChars(filePath, filename);
        return;
    }else{
        __android_log_print(ANDROID_LOG_INFO, "TTS", "Open file: %s", filename);
    }

    // Read WAV header (skip for now, you can check it if necessary)
    unsigned char header[44];
    fread(header, sizeof(unsigned char), 44, file);  // Typical WAV header size

    // Read the audio data (after header)
    fseek(file, 0, SEEK_END);
    size_t fileSize = ftell(file);
    fseek(file, 44, SEEK_SET);  // Skip header
    size_t dataSize = fileSize - 44;
    float * audioData = new float [dataSize];
    fread(audioData, sizeof(unsigned char), dataSize, file);
    fclose(file);

    // Initialize AAudio player
    AAudioPlayer player;
    if (!player.initialize(16000, 1)) {  // Set sample rate (44100 Hz for example) and channels (1 for mono)
        __android_log_print(ANDROID_LOG_ERROR, "AAUDIO", "Failed to initialize AAudio player.");
        delete[] audioData;  // Clean up the allocated memory
        env->ReleaseStringUTFChars(filePath, filename);
        return;
    }

    // Play the audio
    if (!player.playAudio(audioData, dataSize / sizeof(float))) {
        __android_log_print(ANDROID_LOG_ERROR, "AAUDIO", "Failed to play audio.");
    } else {
        __android_log_print(ANDROID_LOG_INFO, "AAUDIO", "Audio is playing.");
    }

    // Clean up
    delete[] audioData;  // Free the allocated memory for audio data
    env->ReleaseStringUTFChars(filePath, filename);
}

AAudio核心实现

bool AAudioPlayer::initialize(int sampleRate, int channelCount) {
    aaudio_result_t result;

    // Create an AAudio stream builder
    result = AAudio_createStreamBuilder(&builder);
    if (result != AAUDIO_OK) {
        LOGE("Failed to create AAudio stream builder");
        return false;
    }

    // Set format, sample rate, and channel count
    AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
    AAudioStreamBuilder_setChannelCount(builder, channelCount);
    AAudioStreamBuilder_setSampleRate(builder, sampleRate);

    // Create the AAudio stream
    result = AAudioStreamBuilder_openStream(builder, &stream);
    if (result != AAUDIO_OK) {
        LOGE("Failed to create AAudio stream");
        return false;
    }

    return true;
}


bool AAudioPlayer::playAudio(const float * audioData, size_t numFrames) {
    aaudio_result_t result;

    // Allocate buffer
    bufferSize = numFrames;
    buffer = new float[bufferSize];
    memcpy(buffer, audioData, numFrames * sizeof(float));

    // Start the audio stream
    result = AAudioStream_requestStart(stream);
    if (result != AAUDIO_OK) {
        LOGE("Failed to start AAudio stream");
        aaudio_stream_state_t state = AAudioStream_getState(stream);
        __android_log_print(ANDROID_LOG_ERROR, "TTS", "Audio stream state: %d", state);
        return false;
    }

    // Write data to the stream
    result = AAudioStream_write(stream, buffer, bufferSize, 0);
    if (result != AAUDIO_OK) {
        LOGE("Failed to write audio data to stream");
        aaudio_stream_state_t state = AAudioStream_getState(stream);
        __android_log_print(ANDROID_LOG_ERROR, "TTS", "Audio stream state: %d", state);
        return false;
    }

    return true;
}

运行结果

日志显示文件打开成功,但播放流程失败,提示音频流启动或数据写入错误。


问题排查与修复方案

1. 音频数据格式转换错误

WAV文件为16位有符号小端整数格式,但代码直接将文件字节读取到float数组,未做格式转换。AAudio配置的是AAUDIO_FORMAT_PCM_FLOAT,需将16位整数转换为[-1.0f, 1.0f]范围的浮点数:

// 替换原数据读取逻辑
size_t dataSize = fileSize - 44;
int16_t* intData = new int16_t[dataSize / sizeof(int16_t)];
fread(intData, sizeof(int16_t), dataSize / sizeof(int16_t), file);

// 转换为float格式
float* audioData = new float[dataSize / sizeof(int16_t)];
for (size_t i = 0; i < dataSize / sizeof(int16_t); i++) {
    audioData[i] = static_cast<float>(intData[i]) / 32768.0f;
}
delete[] intData;

2. 帧数计算错误

原代码用dataSize / sizeof(float)计算帧数,但实际单声道16位音频的每帧占2字节,正确计算应为:

size_t numFrames = dataSize / sizeof(int16_t); // 替换原错误计算方式

3. AAudio流方向未明确设置

初始化AAudio时需明确指定流方向为输出,否则可能导致流创建失败:

// 在AAudioPlayer::initialize中添加该行
AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);

4. 资源泄漏与播放流程不完整

  • AAudioPlayer::playAudio中分配的buffer未释放,存在内存泄漏;
  • 写入数据后未等待播放完成就返回,音频可能被中断;
  • 需补充流停止与资源释放逻辑:
bool AAudioPlayer::playAudio(const float * audioData, size_t numFrames) {
    aaudio_result_t result;

    float* buffer = new float[numFrames];
    memcpy(buffer, audioData, numFrames * sizeof(float));

    result = AAudioStream_requestStart(stream);
    if (result != AAUDIO_OK) {
        LOGE("Failed to start AAudio stream");
        delete[] buffer;
        return false;
    }

    // 设置1秒超时避免无限阻塞
    result = AAudioStream_write(stream, buffer, numFrames, 1000000);
    if (result != AAUDIO_OK) {
        LOGE("Failed to write audio data to stream");
        delete[] buffer;
        return false;
    }

    // 等待播放完成并释放资源
    AAudioStream_requestStop(stream);
    AAudioStream_close(stream);
    delete[] buffer;

    return true;
}

5. WAV头未做验证

硬编码跳过44字节WAV头存在风险,部分非标准WAV文件的头长度可能不同。建议解析WAV头获取准确的数据起始偏移、采样率、声道数等参数,避免读取错误位置的音频数据。


内容的提问来源于stack exchange,提问作者Xinyu Liu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 10:59:58