libgme / game-music-emu

Blargg's video game music emulation library, which allows audio applications to easily add playback support for the music of many classic video game consoles.
GNU Lesser General Public License v2.1
59 stars 12 forks source link

Can You Explain how Multi Channel Works? #51

Closed Wohlstand closed 9 months ago

Wohlstand commented 1 year ago

Original report by Thysbelon (Bitbucket: Thysbelon, GitHub: Thysbelon).


I am using the latest source code (commit 6cd4bdb). I am trying to use the gme_new_emu_multi_channel function to play the different voices of an NSF file in the left ear and the right ear, instead of all centered as is the default. I know the comments in gme.h document the multi channel functionality, but I am still having trouble understanding how it works.

I know that a regular emulator will always output in two channels (stereo), even if the console it’s emulating does not support stereo, like the NES, in which case the left and right ear samples will be duplicates. The output generated by gme_play is an array of int16 samples which alternate between the left and right ear, like this:

[left ear sample, right ear sample, left ear sample, right ear sample … ]

I assumed that the output of using gme_play on a multi channel emulator would be an array like this:

[voice 1 sample, voice 2 sample, voice 3 sample, voice 4 sample, voice 5 sample, voice 6 sample, voice 7 sample, voice 8 sample, voice 1 sample, voice 2 sample … ]

However, all of my attempts to play back the music only result in a glitchy sound.

I have considered that it may be the number returned by gme_voice_count instead of 8. I have tried multiplying the buffer size by the voice count. I still cannot get it to work. A file of example code would be appreciated, as I cannot find any demos that use gme_new_multi_channel.

Thank you.

myQwil commented 9 months ago

That's because it's formatted to play through 16 individual speakers. It won't work if you try taking the samples and directly sending them through 2 speakers.

I use multi-channel audio with Pure Data, and what I do is split the voices up to be sent through their own individual audio streams. From there, we can change the volume level of each voice independently from one another.

myQwil commented 9 months ago

here is a simple example to demonstrate how multi-channel audio works. It takes the first 2 voices and separates them into left and right channels.

demo/basics.c:

/* C example that opens a game music file and records 10 seconds to "out.wav" */

#include "gme/gme.h"

#include "Wave_Writer.h" /* wave_ functions for writing sound file */
#include <stdlib.h>
#include <stdio.h>

void handle_error( const char* str );

int main(int argc, char *argv[])
{
    const char *filename = "test.nsf"; /* Default file to open */
    if ( argc >= 2 )
        filename = argv[1];

    int sample_rate = 44100; /* number of samples per second */
    /* index of track to play (0 = first) */
    int track = argc >= 3 ? atoi(argv[2]) : 0;

    /* Open music file in new emulator */
    gme_type_t file_type = gme_identify_extension( filename );
    Music_Emu* emu = gme_new_emu_multi_channel( file_type, sample_rate );
    handle_error( gme_load_file( emu, filename ) );

    /* Start track */
    handle_error( gme_start_track( emu, track ) );

    /* Begin writing to wave file */
    wave_open( sample_rate, "out.wav" );
    wave_enable_stereo();

    /* Record 10 seconds of track */
    while ( gme_tell( emu ) < 10 * 1000L )
    {
        /* Sample buffer */
        short buf [16];

        /* Fill sample buffer */
        handle_error( gme_play( emu, 16, buf ) );
        buf[1] = buf[2];

        /* Write samples to wave file */
        wave_write( buf, 2 );
    }

    /* Cleanup */
    gme_delete( emu );
    wave_close();

    return 0;
}

void handle_error( const char* str )
{
    if ( str )
    {
        printf( "Error: %s\n", str ); getchar();
        exit( EXIT_FAILURE );
    }
}
Thysbelon commented 9 months ago

Thank you. Please close this issue.