1 // Game_Music_Emu 0.6.0. http://www.slack.net/~ant/
2
3 #include "Music_Player.h"
4
5 #include <string.h>
6 #include <ctype.h>
7
8 /* Copyright (C) 2005-2010 by Shay Green. Permission is hereby granted, free of
9 charge, to any person obtaining a copy of this software module and associated
10 documentation files (the "Software"), to deal in the Software without
11 restriction, including without limitation the rights to use, copy, modify,
12 merge, publish, distribute, sublicense, and/or sell copies of the Software, and
13 to permit persons to whom the Software is furnished to do so, subject to the
14 following conditions: The above copyright notice and this permission notice
15 shall be included in all copies or substantial portions of the Software. THE
16 SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
17 INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
18 PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
22
23 #define RETURN_ERR( expr ) \
24 do {\
25 gme_err_t err_ = (expr);\
26 if ( err_ )\
27 return err_;\
28 } while ( 0 )
29
30 // Number of audio buffers per second. Adjust if you encounter audio skipping.
31 const int fill_rate = 45;
32
33 // Simple sound driver using SDL
34 typedef void (*sound_callback_t)( void* data, short* out, int count );
35 static const char* sound_init( long sample_rate, int buf_size, sound_callback_t, void* data );
36 static void sound_start();
37 static void sound_stop();
38 static void sound_cleanup();
39
Music_Player()40 Music_Player::Music_Player()
41 {
42 emu_ = 0;
43 scope_buf = 0;
44 paused = false;
45 track_info_ = NULL;
46 }
47
init(long rate)48 gme_err_t Music_Player::init( long rate )
49 {
50 sample_rate = rate;
51
52 int min_size = sample_rate * 2 / fill_rate;
53 int buf_size = 512;
54 while ( buf_size < min_size )
55 buf_size *= 2;
56
57 return sound_init( sample_rate, buf_size, fill_buffer, this );
58 }
59
stop()60 void Music_Player::stop()
61 {
62 sound_stop();
63 gme_delete( emu_ );
64 emu_ = NULL;
65 }
66
~Music_Player()67 Music_Player::~Music_Player()
68 {
69 stop();
70 sound_cleanup();
71 gme_free_info( track_info_ );
72 }
73
load_file(const char * path)74 gme_err_t Music_Player::load_file( const char* path )
75 {
76 stop();
77
78 RETURN_ERR( gme_open_file( path, &emu_, sample_rate ) );
79
80 char m3u_path [256 + 5];
81 strncpy( m3u_path, path, 256 );
82 m3u_path [256] = 0;
83 char* p = strrchr( m3u_path, '.' );
84 if ( !p )
85 p = m3u_path + strlen( m3u_path );
86 strcpy( p, ".m3u" );
87 if ( gme_load_m3u( emu_, m3u_path ) ) { } // ignore error
88
89 return 0;
90 }
91
track_count() const92 int Music_Player::track_count() const
93 {
94 return emu_ ? gme_track_count( emu_ ) : false;
95 }
96
start_track(int track)97 gme_err_t Music_Player::start_track( int track )
98 {
99 if ( emu_ )
100 {
101 gme_free_info( track_info_ );
102 track_info_ = NULL;
103 RETURN_ERR( gme_track_info( emu_, &track_info_, track ) );
104
105 // Sound must not be running when operating on emulator
106 sound_stop();
107 RETURN_ERR( gme_start_track( emu_, track ) );
108
109 // Calculate track length
110 if ( track_info_->length <= 0 )
111 track_info_->length = track_info_->intro_length +
112 track_info_->loop_length * 2;
113
114 if ( track_info_->length <= 0 )
115 track_info_->length = (long) (2.5 * 60 * 1000);
116 gme_set_fade( emu_, track_info_->length );
117
118 paused = false;
119 sound_start();
120 }
121 return 0;
122 }
123
pause(int b)124 void Music_Player::pause( int b )
125 {
126 paused = b;
127 if ( b )
128 sound_stop();
129 else
130 sound_start();
131 }
132
suspend()133 void Music_Player::suspend()
134 {
135 if ( !paused )
136 sound_stop();
137 }
138
resume()139 void Music_Player::resume()
140 {
141 if ( !paused )
142 sound_start();
143 }
144
track_ended() const145 bool Music_Player::track_ended() const
146 {
147 return emu_ ? gme_track_ended( emu_ ) : false;
148 }
149
set_stereo_depth(double tempo)150 void Music_Player::set_stereo_depth( double tempo )
151 {
152 suspend();
153 gme_set_stereo_depth( emu_, tempo );
154 resume();
155 }
156
enable_accuracy(bool b)157 void Music_Player::enable_accuracy( bool b )
158 {
159 suspend();
160 gme_enable_accuracy( emu_, b );
161 resume();
162 }
163
set_tempo(double tempo)164 void Music_Player::set_tempo( double tempo )
165 {
166 suspend();
167 gme_set_tempo( emu_, tempo );
168 resume();
169 }
170
mute_voices(int mask)171 void Music_Player::mute_voices( int mask )
172 {
173 suspend();
174 gme_mute_voices( emu_, mask );
175 gme_ignore_silence( emu_, mask != 0 );
176 resume();
177 }
178
fill_buffer(void * data,sample_t * out,int count)179 void Music_Player::fill_buffer( void* data, sample_t* out, int count )
180 {
181 Music_Player* self = (Music_Player*) data;
182 if ( self->emu_ )
183 {
184 if ( gme_play( self->emu_, count, out ) ) { } // ignore error
185
186 if ( self->scope_buf )
187 memcpy( self->scope_buf, out, self->scope_buf_size * sizeof *self->scope_buf );
188 }
189 }
190
191 // Sound output driver using SDL
192
193 #include "SDL.h"
194
195 static sound_callback_t sound_callback;
196 static void* sound_callback_data;
197
sdl_callback(void * data,Uint8 * out,int count)198 static void sdl_callback( void* data, Uint8* out, int count )
199 {
200 if ( sound_callback )
201 sound_callback( sound_callback_data, (short*) out, count / 2 );
202 }
203
sound_init(long sample_rate,int buf_size,sound_callback_t cb,void * data)204 static const char* sound_init( long sample_rate, int buf_size,
205 sound_callback_t cb, void* data )
206 {
207 sound_callback = cb;
208 sound_callback_data = data;
209
210 static SDL_AudioSpec as; // making static clears all fields to 0
211 as.freq = sample_rate;
212 as.format = AUDIO_S16SYS;
213 as.channels = 2;
214 as.callback = sdl_callback;
215 as.samples = buf_size;
216 if ( SDL_OpenAudio( &as, 0 ) < 0 )
217 {
218 const char* err = SDL_GetError();
219 if ( !err )
220 err = "Couldn't open SDL audio";
221 return err;
222 }
223
224 return 0;
225 }
226
sound_start()227 static void sound_start()
228 {
229 SDL_PauseAudio( false );
230 }
231
sound_stop()232 static void sound_stop()
233 {
234 SDL_PauseAudio( true );
235
236 // be sure audio thread is not active
237 SDL_LockAudio();
238 SDL_UnlockAudio();
239 }
240
sound_cleanup()241 static void sound_cleanup()
242 {
243 sound_stop();
244 SDL_CloseAudio();
245 }
246