• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

How to load audio file to and play from buffer

M

MightyJo

Guest
I am trying to write a RAW signed 16-bit PCM audio file into a buffer but for some reason it just won't play.
Here's what I did:


Create event
Code:
audio = 0;
start = 1;
audiobuff = buffer_create(5643776, buffer_grow, 2);

Step event
Code:
if start = 1
{
start = 2;
buffer_seek(audiobuff, buffer_seek_start, 0);
buffer_write(audiobuff, buffer_s16, working_directory + "test.raw");
audio = audio_create_buffer_sound(audiobuff, buffer_s16, 44100, 0, 1411200, audio_stereo);
audio_play_sound(audio, 1, true);
}
The file is named test.raw and is located in the project's "Included Files" folder.
I've been searching the web and scratching my head for days, please help.
 

Blokatt

Member
You can only use buffer_write to write individual values into a buffer. What you're essentially doing is writing the first two characters of the file path into the buffer (likely not even that, since it's probably in Unicode).
Now, there are functions like buffer_load that allow loading external data straight into a buffer, however, according to the docs, those only work with files generated by the buffer saving functions (such as buffer_save). What should work, however, is loading the file using the file_bin functions, converting that into a buffer, saving that buffer and subsequently loading that buffer using whatever buffer loading function you want. This way you don't have to convert the file every time you run your game.

Also, there's no need to use a dynamically-sized buffer for this.
 
Last edited:
M

MightyJo

Guest
I got the file_bin part, I can manage saving a loading a buffer but could you please point me on how to convert the file into a buffer?
 
W

Wraithious

Guest
I think you may have to use a base64 type buffer to load the file into the buffer as one complete string, When I was creating my GMAudiosave Modder Edition extension for android I spent a couple of weeks trying different things that didn't work, (see this thread for more info) but I ended up loading my files into a buffer like this:
Code:
fname = "starter.raw";//can be any file type recognized by gms (except mp3 which is copywrite protected)
if(file_exists(fname)){
  file = file_bin_open(fname,0);
  size = file_bin_size(file);
  gameTransfer = buffer_create(1024, buffer_grow, 1);
  for(var i=0;i<size;i++;){
    buffer_write(gameTransfer, buffer_u8, file_bin_read_byte(file));
  }
  file_bin_close(file);}
  var dataString = buffer_base64_encode(gameTransfer, 0, buffer_get_size(gameTransfer));
  //Do stuff with dataString (the base 64 buffer)
 
Last edited by a moderator:

Blokatt

Member
Wait, GM can covert compressed audio files stored inside a buffer into playable sounds? How come? I would expect it to only work with PCM.
 
W

Wraithious

Guest
Wait, GM can covert compressed audio files stored inside a buffer into playable sounds? How come? I would expect it to only work with PCM.
I don't think you can play .raw files in standard audio software, they have to be converted to .wav or .ogg or .mp3 or something, + gms says that all audio added to the project in the normal way gets converted to .ogg, but gms's new record audio functions create a .raw in a buffer then decode it to play it, if I'm guessing correctly the op wants to take a .raw file from his included files and use the gms recorder function to play it back which probably will work, I know in android it doesn't so that's why I made that extension to allow recording and saving a playable file in a few different formats, for example if .wav was picked as an output I dumped the .raw recorded data into a buffer and sent it to a typical wave headder method in java,
Code:
    private void WriteWaveFileHeader(FileOutputStream out, long totalAudioLen, long totalDataLen, long longSampleRate, int channels, long byteRate) throws IOException {
        byte[] header = new byte[44];
        header[0] = 'R';header[1] = 'I';header[2] = 'F';header[3] = 'F';
        header[4] = (byte) (totalDataLen & 0xff);
        header[5] = (byte) ((totalDataLen >> 8) & 0xff);
        header[6] = (byte) ((totalDataLen >> 16) & 0xff);
        header[7] = (byte) ((totalDataLen >> 24) & 0xff);
        header[8] = 'W';header[9] = 'A';header[10] = 'V';header[11] = 'E';header[12] = 'f';header[13] = 'm';header[14] = 't';
        header[15] = ' ';
        header[16] = 16;header[17] = 0;header[18] = 0;
        header[19] = 0;
        header[20] = 1;
        header[21] = 0;
        header[22] = (byte) channels;
        header[23] = 0;
        header[24] = (byte) (longSampleRate & 0xff);
        header[25] = (byte) ((longSampleRate >> 8) & 0xff);
        header[26] = (byte) ((longSampleRate >> 16) & 0xff);
        header[27] = (byte) ((longSampleRate >> 24) & 0xff);
        header[28] = (byte) (byteRate & 0xff);
        header[29] = (byte) ((byteRate >> 8) & 0xff);
        header[30] = (byte) ((byteRate >> 16) & 0xff);
        header[31] = (byte) ((byteRate >> 24) & 0xff);
        header[32] = (byte) (2 * 16 / 8);
        header[33] = 0;
        header[34] = RECORDER_BPP;
        header[35] = 0;
        header[36] = 'd';header[37] = 'a';header[38] = 't';header[39] = 'a';
        header[40] = (byte) (totalAudioLen & 0xff);
        header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
        header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
        header[43] = (byte) ((totalAudioLen >> 24) & 0xff);
        out.write(header, 0, 44);
        Log.i("yoyo", "463: Writing Header");
    }
I'm guessing gms does the same for windows but in c++ or something, not sure.
 
M

MightyJo

Guest
Hey guys, thanks a lot for the help so far. I am really close to finishing this. Just have one little problem. I implemented Wraithious' code and it works perfectly with buffer_u8. Unfortunately not the same can be said about buffer_s16 which is the one I need. I am exporting a RAW signed 16-bit PCM sound file using Audacity. Exporting to RAW unsigned 8-bit PCM adds too much noise to the sound.

This is how my code looks now:

Create event
Code:
start = 1;
audio = 0;
sound = working_directory + "test.raw";
audio_buff = buffer_create(1024, buffer_grow, 2);
Step event
Code:
if start = 1
{
    start = 2

    if(file_exists(sound))
    {
        file = file_bin_open(sound,0);
        size = file_bin_size(file);
       
        for(var i=0;i<size;i++;)
        {
            buffer_write(audio_buff, buffer_s16, file_bin_read_byte(file));
        }
       
        file_bin_close(file);
    }
   
    audio = audio_create_buffer_sound(audio_buff, buffer_s16, 48000, 0, 384000, audio_stereo);
}

if keyboard_check_pressed(ord('S'))
{
    audio_play_sound(audio, 1, true);
}
The result of this is a horrible distorted slowed down mess. If I export the sound in RAW unsigned 8-bit PCM and use buffer_u8 it works flawlessly. Any ideas on what should be changed?
 
W

Wraithious

Guest
Hey guys, thanks a lot for the help so far. I am really close to finishing this. Just have one little problem. I implemented Wraithious' code and it works perfectly with buffer_u8. Unfortunately not the same can be said about buffer_s16 which is the one I need. I am exporting a RAW signed 16-bit PCM sound file using Audacity. Exporting to RAW unsigned 8-bit PCM adds too much noise to the sound.

This is how my code looks now:

Create event
Code:
start = 1;
audio = 0;
sound = working_directory + "test.raw";
audio_buff = buffer_create(1024, buffer_grow, 2);
Step event
Code:
if start = 1
{
    start = 2

    if(file_exists(sound))
    {
        file = file_bin_open(sound,0);
        size = file_bin_size(file);
      
        for(var i=0;i<size;i++;)
        {
            buffer_write(audio_buff, buffer_s16, file_bin_read_byte(file));
        }
      
        file_bin_close(file);
    }
  
    audio = audio_create_buffer_sound(audio_buff, buffer_s16, 48000, 0, 384000, audio_stereo);
}

if keyboard_check_pressed(ord('S'))
{
    audio_play_sound(audio, 1, true);
}
The result of this is a horrible distorted slowed down mess. If I export the sound in RAW unsigned 8-bit PCM and use buffer_u8 it works flawlessly. Any ideas on what should be changed?
Cool it sounds like you are getting close! I'm wondering if it might be an issue with the channels (stereo mono), for the heck of it try changing the last argument of this line to mono
Code:
audio = audio_create_buffer_sound(audio_buff, buffer_s16, 48000, 0, 384000, audio_stereo);
I remember i had the opposite issue where the audio played too fast and high pitched and it was because i had the channel option in my headder file set to 2 and was feeding it a mono audio file
 
M

MightyJo

Guest
Nope, that's not it. Changing it to mono only makes it sound even slower.

I also toyed around with the alignment of the buffer_create function. The documentation on buffer_create says that "Signed or unsigned 8bit integers can be aligned to any value..." and when I used buffer_u8 buffer type and file and set the alignment to 1 (just like in your example) everything worked perfectly. If I changed to alignment to 2 the audio would go 50% slower by the sound of it.

Now in the current case the documentation says "Signed or unsigned 16bit integers should be aligned to 2 bytes". Weirdly enough if I set it to either 1 or 2 it sounds exactly the same but if I set it to 3 or 4 it sounds slower but the same on both.

This might not be the problem. I'm just telling you what I've tried just in case it gives you any ideas.
 
W

Wraithious

Guest
Nope, that's not it. Changing it to mono only makes it sound even slower.

I also toyed around with the alignment of the buffer_create function. The documentation on buffer_create says that "Signed or unsigned 8bit integers can be aligned to any value..." and when I used buffer_u8 buffer type and file and set the alignment to 1 (just like in your example) everything worked perfectly. If I changed to alignment to 2 the audio would go 50% slower by the sound of it.

Now in the current case the documentation says "Signed or unsigned 16bit integers should be aligned to 2 bytes". Weirdly enough if I set it to either 1 or 2 it sounds exactly the same but if I set it to 3 or 4 it sounds slower but the same on both.

This might not be the problem. I'm just telling you what I've tried just in case it gives you any ideas.
hmm, all I can think of is there is a mis-match of the buffer types there, I know the reason I used buffer_u8 with an alignment of 1 and the base-64 encoding was to make sure the file was properly coppied as-is into the buffer, and then on my java side decoded the buffer the same way using a base-64 decoder. so I'm not to sure about using a u_16, it may not be compatible with the rest of that code, altho I learned alot about buffers in my project I'm still not an expert at them :(
 
M

MightyJo

Guest
No worries mate. Thanks a ton for the help so far.
If anyone else has any other idea please share.
 

RizbIT

Member
after you record audio using gms function and end recording i copy the recorded data to a new buffer.

How do i save this recorded audio to an audio file ogg/wav or mp3 and then clean up and end?

only asking here as the op most likely to know what i want
 
S

STH135

Guest
Don't know if you or anyone needs this now, but in

Code:
buffer_write(audio_buff, buffer_s16, file_bin_read_byte(file));
use buffer_u8 instead of buffer_s16. Only in buffer_write. Don't know why but it works perfectly, for me at least.
 

DaveInDev

Member
well, this post is old, but it seems that it did not lead to a conclusion, and as I was also looking how to load a wav file into a buffer and then a sound, after some tests, here is a code that is working, in case someone is needing :

NB: this is for a 44100khz/16bit/stereo wav file, but you can adapt, or even better : decode the wav file header and adjust audio_create_buffer_sound...

GML:
#macro size_wav_header 44

filename = "snd_asr2.wav";

sid_buf = -1;

if(file_exists(filename))
{
    file = file_bin_open(filename,0);
    size = file_bin_size(file);
    
    audio_buff = buffer_create(size, buffer_fixed, 1);
      
    for(var i=0; i<size; i++;)
        buffer_write(audio_buff, buffer_u8, file_bin_read_byte(file));
      
    file_bin_close(file);

    sid_buf = audio_create_buffer_sound(
        audio_buff, buffer_s16, 44100, size_wav_header, size-size_wav_header, audio_stereo);
        
    audio_play_sound(sid_buf, 1, false);
}
 

CrispyBun

Member
Well I'll also reply here, since there aren't many threads that discuss this topic.
Adding to what @DaveInDev said, you can do something like:

GML:
audio_buff_length = buffer_get_size(audio_buff);
sid_buf = audio_create_buffer_sound(audio_buff, buffer_s16, 44100, size_wav_header, audio_buff_length, audio_stereo);
Which will make it play the whole sound.
Other than that, the code works great!

I think the last piece of the puzzle missing is - how would one go about decoding the wav file header?
EDIT: Okay, in this stack overflow question, someone lists where the headers are and what they mean:

That should be it :p
 
Last edited:
Top