我在C#/ MonoGame中写了一个libretro前端,我设法得到一个原始(但正在工作)的视频阻击器,但现在我正在与声音挣扎。
来自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位整数。 我尝试使用Stream中的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();
我发出声音并且声音模式显然是可以辨别的,但它是乱码,你看到我的实施有什么问题吗?
答案 0 :(得分: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 现在我认为我理解,你将16bit-int转换成几个字节。所以正确的语法是:
byte b1 =(byte)(chunk);
byte b2 = (byte)(chunk >> 8);
因为对话总是最不重要的部分,所以你需要把它缩小。
答案 1 :(得分:1)
此方法将样本数据转换为bytes数组。它适用于任何通道数(在单声道和立体声测试)。
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]
范围内。