首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >安卓AudioTrack播放短阵(16bit)

安卓AudioTrack播放短阵(16bit)
EN

Stack Overflow用户
提问于 2018-07-11 06:00:06
回答 2查看 812关注 0票数 0

我有一个应用程序,播放音频。它通过RTP接收编码的音频数据,并将其解码为16位数组。解码的16位数组被转换为8位数组(字节数组),因为这是某些其他功能所需的。

即使音频播放正在工作,它也在不断中断,并且很难识别音频输出。如果我仔细听,我可以告诉它正在播放正确的音频。

我怀疑这是因为我将16位数据流转换为字节数组,并使用AudioTrack类的write(byte[],int,int,AudioTrack.WRITE_NON_BLOCKING)进行音频播放。

因此,我将字节数组转换回短数组,并使用write(short[],int,int,AudioTrack.WRITE_NON_BLOCKING)方法来查看它是否可以解决问题。

然而,现在根本没有音频声音了。在调试输出中,我可以看到短数组包含数据。

可能的原因是什么?

下面是AUdioTrak初始化

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            sampleRate      =AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC);

            minimumBufferSize = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);

            audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
                    AudioFormat.CHANNEL_OUT_STEREO,
                    AudioFormat.ENCODING_PCM_16BIT,
                    minimumBufferSize,
                    AudioTrack.MODE_STREAM);

下面是将短数组转换为字节数组的代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
for (int i=0;i<internalBuffer.length;i++){

        bufferIndex = i*2;
        buffer[bufferIndex] = shortToByte(internalBuffer[i])[0];
        buffer[bufferIndex+1] = shortToByte(internalBuffer[i])[1];
    }

下面是将字节数组转换为短数组的方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public short[] getShortAudioBuffer(byte[] b){
    short audioBuffer[] = null;
    int index = 0;
    int audioSize = 0;
    ByteBuffer byteBuffer = ByteBuffer.allocate(2);

    if ((b ==null) && (b.length<2)){
        return null;
    }else{
        audioSize = (b.length - (b.length%2));
        audioBuffer   = new short[audioSize/2];
    }

    if ((audioSize/2) < 2)
        return null;

    byteBuffer.order(ByteOrder.LITTLE_ENDIAN);


    for(int i=0;i<audioSize/2;i++){
        index = i*2;
        byteBuffer.put(b[index]);
        byteBuffer.put(b[index+1]);
        audioBuffer[i] = byteBuffer.getShort(0);
        byteBuffer.clear();
        System.out.print(Integer.toHexString(audioBuffer[i]) + " ");
    }
    System.out.println();

    return audioBuffer;
}

音频使用opus库进行解码,配置如下;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
opus_decoder_ctl(dec,OPUS_SET_APPLICATION(OPUS_APPLICATION_AUDIO));
    opus_decoder_ctl(dec,OPUS_SET_SIGNAL(OPUS_SIGNAL_MUSIC));
    opus_decoder_ctl(dec,OPUS_SET_FORCE_CHANNELS(OPUS_AUTO));
    opus_decoder_ctl(dec,OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND));
    opus_decoder_ctl(dec,OPUS_SET_PACKET_LOSS_PERC(0));
    opus_decoder_ctl(dec,OPUS_SET_COMPLEXITY(10));     // highest complexity
    opus_decoder_ctl(dec,OPUS_SET_LSB_DEPTH(16)); // 16bit = two byte samples
    opus_decoder_ctl(dec,OPUS_SET_DTX(0));             // default - not using discontinuous transmission
    opus_decoder_ctl(dec,OPUS_SET_VBR(1));             // use variable bit rate
    opus_decoder_ctl(dec,OPUS_SET_VBR_CONSTRAINT(0));  // unconstrained
    opus_decoder_ctl(dec,OPUS_SET_INBAND_FEC(0));      // no forward error correction
EN

回答 2

Stack Overflow用户

发布于 2018-07-11 13:38:55

假设您有一个包含要播放的16位单通道数据的short[]数组。然后,每个样本是一个介于-32768和32767之间的值,该值表示确切时刻的信号振幅。0值表示中间点(无信号)。这个数组可以通过ENCODING_PCM_16BIT格式编码传递给音轨。

但当使用播放ENCODING_PCM_8BIT时,事情变得很奇怪(参见AudioFormat)

在这种情况下,每个样本由一个字节编码。但是每个字节都是无符号的。也就是说,它的值介于0和255之间,而128表示中间点。

Java没有无符号字节格式。字节格式是带符号的。例如,值-128...1将表示128...255的实际值。因此,在转换为字节数组时必须小心,否则它将是一个几乎无法识别源声音的噪声。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
short[] input16 = ... // the source 16-bit audio data;

byte[] output8 = new byte[input16.length];

for (int i = 0 ; i < input16.length ; i++) {
  // To convert 16 bit signed sample to 8 bit unsigned
  // We add 128 (for rounding), then shift it right 8 positions
  // Then add 128 to be in range 0..255
  int sample = ((input16[i] + 128) >> 8) + 128;
  if (sample > 255) sample = 255; // strip out overload
  output8[i] = (byte)(sample); // cast to signed byte type
}

要执行向后转换,所有这些都应该是相同的:每个单个样本都要转换为输出信号的一个样本

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
byte[] input8 = // source 8-bit unsigned audio data;

short[] output16 = new short[input8.length];

for (int i = 0 ; i < input8.length ; i++) { 
  // to convert signed byte back to unsigned value just use bitwise AND with 0xFF
  // then we need subtract 128 offset
  // Then, just scale up the value by 256 to fit 16-bit range
  output16[i] = (short)(((input8[i] & 0xFF) - 128) * 256);
}
票数 0
EN

Stack Overflow用户

发布于 2018-07-12 22:25:04

解决了使用位运算符而不是ByteArray时无法将数据从字节数组转换为短数组的问题。这可能是因为没有在ByteArray中设置正确的参数,或者它不适合这样的转换。

尽管如此,使用位运算符实现转换解决了这个问题。由于原来的问题已经通过这种方法解决了,请考虑这是最终的答案。

我将提出一个关于播放问题的单独主题。

感谢您的所有支持。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51278201

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文