2

I'm writing a libretro frontend in C#/MonoGame, I've managed to get a crude (but working) video blitter but now I'm struggling with sound.

From the 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);

So, the samples are signed 16 bit integers. I'm trying to use SoundEffect from Stream like this:

        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();

And I'm getting sound and the sound pattern is clearly distingishable but it's garbled, do you see anything immediately wrong with my implementation?

Morgoth
  • 4,935
  • 8
  • 40
  • 66
Radius
  • 350
  • 3
  • 14

2 Answers2

3

This method will convert the samples data to the bytes array. It works with any channels count (tested on mono and stereo).

    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;
    }

Please note that the float[] samples values are expected to be in range [-1;1].

ai_enabled
  • 71
  • 5
1

I actually didnt know for sure, but since you get b1 an b2, and you used b1 twice, maybe you just mistakely wrote b1 instead of b2:

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

maybe you need something like:

buffer[i+1] = b2;

But i dont know for sure what are you trying to do there, so i dont know if my answer has any relevance.

UPDATE Now i do think that i understand, you are converting 16bit-int to couple of bytes. so the right syntax will be:

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

because the conversation took always the least significant part, so you need to shr it.

ArBel
  • 79
  • 10
  • You welcome, you also should use shr instead of shl, shl will produce always 0 to b2. – ArBel Aug 12 '15 at 06:37
  • Are you sure that you should use little-endian order? try swaping `buffer[i] = b2; buffer[i+1] = b1;` – ArBel Aug 12 '15 at 06:41
  • I tried that from the very beginning, it's no longer recognizable if I do that (just tried again to be sure) – Radius Aug 12 '15 at 06:43
  • I dont get it, why `Int16 chunk = Marshal.ReadByte(data);` if `Marshal.ReadByte(data);` return byte? (as the name implies and as written here https://msdn.microsoft.com/en-us/library/vstudio/a0c0f616(v=vs.100).aspx) – ArBel Aug 12 '15 at 06:50
  • That's another error on my part... sorry I've been at this for a few hours. Fixed that but it's still wrong, seems better though but it's so garbled it's hard to tell. – Radius Aug 12 '15 at 06:54
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/86754/discussion-between-andressm-and-arbel). – Radius Aug 12 '15 at 06:57