- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我使用 oboe在我的 ndk 库中播放声音,我使用 OpenSL with Android extensions将 wav 文件解码为 PCM。解码后的签名 16 位 PCM 存储在内存中 ( std::forward_list<int16_t>
),然后通过回调将它们发送到双簧管流中。我可以从手机听到的声音在音量级别上类似于原始 wav 文件,但是,这种声音的“质量”却不是——它会爆裂和噼啪作响。
我猜我以错误的顺序或格式(采样率?)在音频流中发送了 PCM。如何将 OpenSL 解码与双簧管音频流一起使用?
为了将文件解码为 PCM,我使用 AndroidSimpleBufferQueue 作为接收器,并使用 AndroidFD 和 AAssetManager 作为源:
// Loading asset
AAsset* asset = AAssetManager_open(manager, path, AASSET_MODE_UNKNOWN);
off_t start, length;
int fd = AAsset_openFileDescriptor(asset, &start, &length);
AAsset_close(asset);
// Creating audio source
SLDataLocator_AndroidFD loc_fd = { SL_DATALOCATOR_ANDROIDFD, fd, start, length };
SLDataFormat_MIME format_mime = { SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_UNSPECIFIED };
SLDataSource audio_source = { &loc_fd, &format_mime };
// Creating audio sink
SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1 };
SLDataFormat_PCM pcm = {
.formatType = SL_DATAFORMAT_PCM,
.numChannels = 2,
.samplesPerSec = SL_SAMPLINGRATE_44_1,
.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16,
.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16,
.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
.endianness = SL_BYTEORDER_LITTLEENDIAN
};
SLDataSink sink = { &loc_bq, &pcm };
然后我注册回调、排队缓冲区并将 PCM 从缓冲区移动到存储直到完成。
注意:wav 音频文件也是 2 channel 签名 16 位 44.1Hz PCM
我的双簧管流配置是一样的:
AudioStreamBuilder builder;
builder.setChannelCount(2);
builder.setSampleRate(44100);
builder.setCallback(this);
builder.setFormat(AudioFormat::I16);
builder.setPerformanceMode(PerformanceMode::LowLatency);
builder.setSharingMode(SharingMode::Exclusive);
音频渲染是这样工作的:
// Oboe stream callback
audio_engine::onAudioReady(AudioStream* self, void* audio_data, int32_t num_frames) {
auto stream = static_cast<int16_t*>(audio_data);
sound->render(stream, num_frames);
}
// Sound::render method
sound::render(int16_t* audio_data, int32_t num_frames) {
auto iter = pcm_data.begin();
std::advance(iter, cur_frame);
const int32_t rem_size = std::min(num_frames, size - cur_frame);
for(int32_t i = 0; i < rem_size; ++i, std::next(iter), ++cur_frame) {
audio_data[i] += *iter;
}
}
最佳答案
看起来您的 render() 方法混淆了样本和帧。帧是一组同时采样。在立体流中,每个帧有两个样本。
我认为您的迭代器是在样本基础上工作的。换句话说,next(iter) 将前进到下一个样本,而不是下一帧。试试这个(未经测试的)代码。
sound::render(int16_t* audio_data, int32_t num_frames) {
auto iter = pcm_data.begin();
const int samples_per_frame = 2; // stereo
std::advance(iter, cur_sample);
const int32_t num_samples = std::min(num_frames * samples_per_frame,
total_samples - cur_sample);
for(int32_t i = 0; i < num_samples; ++i, std::next(iter), ++cur_sample) {
audio_data[i] += *iter;
}
}
关于android - 如何用双簧管正确播放解码后的内存 PCM?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53479297/
我正在开发一个 Android 应用程序,用于处理基本上是 USB 麦克风的设备。我需要读取输入数据并进行处理。有时,我需要向设备发送数据(4 short * channel 数,通常为 2)并且此数
我正在尝试在我的 android 应用程序中播放原始 (int16 PCM) 编码的音频文件。我一直在关注并通读双簧管文档/示例,以尝试播放我自己的音频文件之一。 我需要播放的音频文件大约是 6kb,
JS: function OrderFormController($scope, $http) { $scope.sampleJSON = []; $scope.sampleJSONDuplicate
我是一名优秀的程序员,十分优秀!