summaryrefslogtreecommitdiff
path: root/plugins/gme/game-music-emu-0.6pre/gme/Music_Emu.cpp
blob: ed760afd0c5dae4ac1ba892b4b9bc99cefea18f0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/

#include "Music_Emu.h"

/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */

#include "blargg_source.h"

int const stereo = 2; // number of channels for stereo

Music_Emu::equalizer_t const Music_Emu::tv_eq = { -8.0, 180 };

void Music_Emu::clear_track_vars()
{
	current_track_ = -1;
	warning(); // clear warning
	track_filter.stop();
}

void Music_Emu::unload()
{
	voice_count_ = 0;
	clear_track_vars();
	Gme_File::unload();
}

Music_Emu::gme_t()
{
	effects_buffer_ = NULL;
	sample_rate_    = 0;
	mute_mask_      = 0;
	tempo_          = 1.0;
	gain_           = 1.0;
	
	// defaults
	tfilter = track_filter.setup();
	set_max_initial_silence( 15 );
	set_silence_lookahead( 3 );
	ignore_silence( false );
	
	equalizer_.treble = -1.0;
	equalizer_.bass   = 60;
	
	static const char* const names [] = {
		"Voice 1", "Voice 2", "Voice 3", "Voice 4",
		"Voice 5", "Voice 6", "Voice 7", "Voice 8"
	};
	set_voice_names( names );
	Music_Emu::unload(); // clears fields
}

Music_Emu::~gme_t()
{
	assert( !effects_buffer_ );
}

blargg_err_t Music_Emu::set_sample_rate( int rate )
{
	require( !sample_rate() ); // sample rate can't be changed once set
	RETURN_ERR( set_sample_rate_( rate ) );
	RETURN_ERR( track_filter.init( this ) );
	sample_rate_ = rate;
	tfilter.max_silence = 6 * stereo * sample_rate();
	return blargg_ok;
}

void Music_Emu::pre_load()
{
	require( sample_rate() ); // set_sample_rate() must be called before loading a file
	Gme_File::pre_load();
}

void Music_Emu::set_equalizer( equalizer_t const& eq )
{
	// TODO: why is GCC generating memcpy call here?
	// Without the 'if', valgrind flags it.
	if ( &eq != &equalizer_ )
		equalizer_ = eq;
	set_equalizer_( eq );
}

void Music_Emu::mute_voice( int index, bool mute )
{
	require( (unsigned) index < (unsigned) voice_count() );
	int bit = 1 << index;
	int mask = mute_mask_ | bit;
	if ( !mute )
		mask ^= bit;
	mute_voices( mask );
}

void Music_Emu::mute_voices( int mask )
{
	require( sample_rate() ); // sample rate must be set first
	mute_mask_ = mask;
	mute_voices_( mask );
}

const char* Music_Emu::voice_name( int i ) const
{
	if ( (unsigned) i < (unsigned) voice_count_ )
		return voice_names_ [i];
	
	//check( false ); // TODO: enable?
	return "";
}

void Music_Emu::set_tempo( double t )
{
	require( sample_rate() ); // sample rate must be set first
	double const min = 0.02;
	double const max = 4.00;
	if ( t < min ) t = min;
	if ( t > max ) t = max;
	tempo_ = t;
	set_tempo_( t );
}

blargg_err_t Music_Emu::post_load()
{
	set_tempo( tempo_ );
	remute_voices();
	return Gme_File::post_load();
}

// Tell/Seek

int Music_Emu::msec_to_samples( int msec ) const
{
	int sec = msec / 1000;
	msec -= sec * 1000;
	return (sec * sample_rate() + msec * sample_rate() / 1000) * stereo;
}

int Music_Emu::tell() const
{
	int rate = sample_rate() * stereo;
	int sec = track_filter.sample_count() / rate;
	return sec * 1000 + (track_filter.sample_count() - sec * rate) * 1000 / rate;
}

blargg_err_t Music_Emu::seek( int msec )
{
	int time = msec_to_samples( msec );
	if ( time < track_filter.sample_count() )
		RETURN_ERR( start_track( current_track_ ) );
	return skip( time - track_filter.sample_count() );
}

blargg_err_t Music_Emu::skip( int count )
{
	require( current_track() >= 0 ); // start_track() must have been called already
	return track_filter.skip( count );
}

blargg_err_t Music_Emu::skip_( int count )
{
	// for long skip, mute sound
	const int threshold = 32768;
	if ( count > threshold )
	{
		int saved_mute = mute_mask_;
		mute_voices( ~0 );
		
		int n = count - threshold/2;
		n &= ~(2048-1); // round to multiple of 2048
		count -= n;
		RETURN_ERR( track_filter.skip_( n ) );
		
		mute_voices( saved_mute );
	}
	
	return track_filter.skip_( count );
}

// Playback

blargg_err_t Music_Emu::start_track( int track )
{
	clear_track_vars();
	
	int remapped = track;
	RETURN_ERR( remap_track_( &remapped ) );
	current_track_ = track;
	blargg_err_t err = start_track_( remapped );
	if ( err )
	{
		current_track_ = -1;
		return err;
	}
	
	// convert filter times to samples
	Track_Filter::setup_t s = tfilter;
	s.max_initial *= sample_rate() * stereo;
	#if GME_DISABLE_SILENCE_LOOKAHEAD
		s.lookahead = 1;
	#endif
	track_filter.setup( s );
	
	return track_filter.start_track();
}

void Music_Emu::set_fade( int start_msec, int length_msec )
{
	track_filter.set_fade( msec_to_samples( start_msec ),
			length_msec * sample_rate() / (1000 / stereo) );
}

blargg_err_t Music_Emu::play( int out_count, sample_t out [] )
{
	require( current_track() >= 0 );
	require( out_count % stereo == 0 );
	
	return track_filter.play( out_count, out );
}

// Gme_Info_

blargg_err_t Gme_Info_::set_sample_rate_( int )             { return blargg_ok; }
void         Gme_Info_::pre_load()                          { Gme_File::pre_load(); } // skip Music_Emu
blargg_err_t Gme_Info_::post_load()                         { return Gme_File::post_load(); } // skip Music_Emu
void         Gme_Info_::set_equalizer_( equalizer_t const& ){ check( false ); }
void         Gme_Info_::mute_voices_( int )                 { check( false ); }
void         Gme_Info_::set_tempo_( double )                { }
blargg_err_t Gme_Info_::start_track_( int )                 { return BLARGG_ERR( BLARGG_ERR_CALLER, "can't play file opened for info only" ); }
blargg_err_t Gme_Info_::play_( int, sample_t [] )           { return BLARGG_ERR( BLARGG_ERR_CALLER, "can't play file opened for info only" ); }