纯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
相关产品推荐
相关产品推荐

