game-music-emu: Merge branch 'master' of git@github.com:libgme/game-music-emu.git

From cae71005d53086d85c5458fa140013bea9886714 Mon Sep 17 00:00:00 2001
From: Mike Will <[EMAIL REDACTED]>
Date: Wed, 11 Oct 2023 00:42:19 -0400
Subject: [PATCH] Add a multi-channel example

Similar to `demo`, `demo_multi` writes 10 seconds of audio to
`out.wav`, but it sends all even-numbered voices to the left channel
and all odd-numbered voices to the right channel.
---
 demo/CMakeLists.txt |  4 ++
 demo/basics_multi.c | 89 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 93 insertions(+)
 create mode 100644 demo/basics_multi.c

diff --git a/demo/CMakeLists.txt b/demo/CMakeLists.txt
index 6e18f1e..b4453f7 100644
--- a/demo/CMakeLists.txt
+++ b/demo/CMakeLists.txt
@@ -28,6 +28,10 @@ add_custom_command(TARGET demo_mem
 
 target_link_libraries(demo_mem gme)
 
+
+add_executable(demo_multi Wave_Writer.cpp basics_multi.c)
+target_link_libraries(demo_multi gme)
+
 #
 # Testing
 #
diff --git a/demo/basics_multi.c b/demo/basics_multi.c
new file mode 100644
index 0000000..7205778
--- /dev/null
+++ b/demo/basics_multi.c
@@ -0,0 +1,89 @@
+/* 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>
+#include <string.h>
+
+void handle_error( const char *str )
+{
+	if ( str )
+	{
+		printf( "Error: %s\n", str ); getchar();
+		exit( EXIT_FAILURE );
+	}
+}
+
+short avg( short *ptr, int n )
+{
+	int sum = 0, i = n;
+	while ( i-- )
+		sum += ptr[i];
+	return sum / n;
+}
+
+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 multi-channel 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();
+	
+	const int frames = 64;
+	const int voices = 8;
+	const int channels = 2;
+	const int buf_size = frames * voices * channels;
+	
+	/* Record 10 seconds of track */
+	while ( gme_tell( emu ) < 10 * 1000L )
+	{
+		/* Sample buffer */
+		short buf [buf_size], *in = buf, *out = buf;
+		
+		/* Fill sample buffer */
+		handle_error( gme_play( emu, buf_size, buf ) );
+		
+		/* Render frames for stereo output by sending
+		   even-numbered voices to the left channel, and
+		   odd-numbered voices to the right channel */
+		for ( int f = frames; f--; out += channels )
+		{
+			int i = 0;
+			short ch [channels];
+			memset( ch, 0, channels * sizeof( short ) );
+			for ( int v = voices; v--; in += channels )
+			{
+				ch[i] += avg( in, channels );
+				i = ( ++i >= channels ) ? 0 : i;
+			}
+			for ( int c = channels; c--; )
+				out[c] = ch[c];
+		}
+		
+		/* Write samples to wave file */
+		wave_write( buf, frames * channels );
+	}
+	
+	/* Cleanup */
+	gme_delete( emu );
+	wave_close();
+	
+	return 0;
+}