如何将int16声音样本数组转换为在MonoGame/XNA中使用的字节数组

本文关键字:数组 XNA 字节数 字节 MonoGame int16 声音 样本 转换 样本数 | 更新日期: 2023-09-27 18:07:30

我正在用c#/MonoGame编写libretro前端,我已经设法获得了一个粗糙(但工作)的视频blitter,但现在我正在努力解决声音问题。

来自API:

/* Renders multiple audio frames in one go.
 *
 * One frame is defined as a sample of left and right channels, interleaved.
 * I.e. int16_t buf[4] = { l, r, l, r }; would be 2 frames.
 * Only one of the audio callbacks must ever be used.
 */
typedef size_t (*retro_audio_sample_batch_t)(const int16_t *data,
      size_t frames);

因此,样本是带符号的16位整数。我试图使用SoundEffect从流像这样:

        int size = SoundEffect.GetSampleSizeInBytes(TimeSpan.FromMilliseconds((float)1000/(int)_libretro.GetAVInfo().timing.fps), (int)_libretro.GetAVInfo().timing.sample_rate, AudioChannels.Mono);           
        data = _libretro.GetSoundBuffer().data;
        byte[] buffer = new byte[size];
        for (int i = 0; i < size -1 ; i+=2)
        {
            Int16 chunk = Marshal.ReadInt16(data);
            byte b1 = (byte)(chunk);
            byte b2 = (byte)(chunk >> 8);
            buffer[i+1] = b1;
            buffer[i] = b2;
            //move ahead 4 bytes skipping the second sound channel for now
            data = data + (sizeof(byte)*4);
        }
        SoundEffect sound_left = new SoundEffect(buffer, (int)_libretro.GetAVInfo().timing.sample_rate, AudioChannels.Mono);
        sound_left.Play();

我得到了声音,声音模式是清晰可分辨的,但它是乱码的,你立即发现我的实现有什么问题吗?

如何将int16声音样本数组转换为在MonoGame/XNA中使用的字节数组

这个方法将把样本数据转换为字节数组。它适用于任何声道计数(在单声道和立体声上测试)。

    public static byte[] GetSamplesWaveData(float[] samples, int samplesCount)
    {
        var pcm = new byte[samplesCount * 2];
        int sampleIndex = 0,
            pcmIndex = 0;
        while (sampleIndex < samplesCount)
        {
            var outsample = (short)(samples[sampleIndex] * short.MaxValue);
            pcm[pcmIndex] = (byte)(outsample & 0xff);
            pcm[pcmIndex + 1] = (byte)((outsample >> 8) & 0xff);
            sampleIndex++;
            pcmIndex += 2;
        }
        return pcm;
    }

请注意,float[] samples的值应该在[-1;1]范围内。

我实际上不确定,但是既然你得到了b1和b2,并且你使用了两次b1,也许你只是错误地写成了b1而不是b2:

byte b1 =(byte)(chunk);
byte b2 = (byte)(chunk << 8);
buffer[i] = b1;//thats b1
buffer[i+1] = b1;//thats b1 again

也许你需要这样的东西:

buffer[i+1] = b2;

但是我不确定你在那里想做什么,所以我不知道我的回答是否有任何相关性。

UPDATE现在我认为我明白了,你正在将16位整型转换为几个字节。所以正确的语法应该是:

byte b1 =(byte)(chunk);
byte b2 = (byte)(chunk >> 8); 

因为对话总是占据了最不重要的部分,所以你需要SHR它