Mercurial > audlegacy-plugins
comparison src/Input/console/Vgm_Emu.cxx @ 0:13389e613d67 trunk
[svn] - initial import of audacious-plugins tree (lots to do)
author | nenolod |
---|---|
date | Mon, 18 Sep 2006 01:11:49 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:13389e613d67 |
---|---|
1 | |
2 // Game_Music_Emu 0.3.0. http://www.slack.net/~ant/ | |
3 | |
4 #include "Vgm_Emu.h" | |
5 | |
6 #include <math.h> | |
7 #include <string.h> | |
8 #include "blargg_endian.h" | |
9 | |
10 /* Copyright (C) 2003-2006 Shay Green. This module is free software; you | |
11 can redistribute it and/or modify it under the terms of the GNU Lesser | |
12 General Public License as published by the Free Software Foundation; either | |
13 version 2.1 of the License, or (at your option) any later version. This | |
14 module is distributed in the hope that it will be useful, but WITHOUT ANY | |
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
16 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for | |
17 more details. You should have received a copy of the GNU Lesser General | |
18 Public License along with this module; if not, write to the Free Software | |
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ | |
20 | |
21 #include BLARGG_SOURCE_BEGIN | |
22 | |
23 double const gain = 3.0; // FM emulators are internally quieter to avoid 16-bit overflow | |
24 double const rolloff = 0.990; | |
25 double const oversample_factor = 1.5; | |
26 | |
27 Vgm_Emu::Vgm_Emu( bool os, double tempo ) | |
28 { | |
29 oversample = os; | |
30 pos = NULL; | |
31 data = NULL; | |
32 uses_fm = false; | |
33 vgm_rate = (long) (header_t::time_rate * tempo + 0.5); | |
34 | |
35 static equalizer_t const eq = { -14.0, 80 }; | |
36 set_equalizer( eq ); | |
37 psg.volume( 1.0 ); | |
38 } | |
39 | |
40 Vgm_Emu::~Vgm_Emu() | |
41 { | |
42 unload(); | |
43 } | |
44 | |
45 void Vgm_Emu::unload() | |
46 { | |
47 data = NULL; | |
48 pos = NULL; | |
49 set_track_ended( false ); | |
50 mem.clear(); | |
51 } | |
52 | |
53 blargg_err_t Vgm_Emu::set_sample_rate( long sample_rate ) | |
54 { | |
55 BLARGG_RETURN_ERR( blip_buf.set_sample_rate( sample_rate, 1000 / 30 ) ); | |
56 return Classic_Emu::set_sample_rate( sample_rate ); | |
57 } | |
58 | |
59 BOOST::uint8_t const* Vgm_Emu::gd3_data( int* size ) const | |
60 { | |
61 if ( size ) | |
62 *size = 0; | |
63 | |
64 long gd3_offset = get_le32( header_.gd3_offset ); | |
65 if ( !gd3_offset ) | |
66 return NULL; | |
67 | |
68 gd3_offset -= 0x40 - offsetof (header_t,gd3_offset); | |
69 if ( gd3_offset < 0 ) | |
70 return NULL; | |
71 | |
72 byte const* gd3 = data + gd3_offset; | |
73 if ( data_end - gd3 < 16 || 0 != memcmp( gd3, "Gd3 ", 4 ) || get_le32( gd3 + 4 ) >= 0x200 ) | |
74 return NULL; | |
75 | |
76 long gd3_size = get_le32( gd3 + 8 ); | |
77 if ( data_end - gd3 < gd3_size - 12 ) | |
78 return NULL; | |
79 | |
80 if ( size ) | |
81 *size = data_end - gd3; | |
82 return gd3; | |
83 } | |
84 | |
85 void Vgm_Emu::update_eq( blip_eq_t const& eq ) | |
86 { | |
87 psg.treble_eq( eq ); | |
88 dac_synth.treble_eq( eq ); | |
89 } | |
90 | |
91 void Vgm_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) | |
92 { | |
93 if ( i < psg.osc_count ) | |
94 psg.osc_output( i, c, l, r ); | |
95 } | |
96 | |
97 const char** Vgm_Emu::voice_names() const | |
98 { | |
99 static const char* fm_names [] = { | |
100 "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG" | |
101 }; | |
102 if ( uses_fm ) | |
103 return fm_names; | |
104 | |
105 static const char* psg_names [] = { "Square 1", "Square 2", "Square 3", "Noise" }; | |
106 return psg_names; | |
107 } | |
108 | |
109 void Vgm_Emu::mute_voices( int mask ) | |
110 { | |
111 Classic_Emu::mute_voices( mask ); | |
112 dac_synth.output( &blip_buf ); | |
113 if ( uses_fm ) | |
114 { | |
115 psg.output( (mask & 0x80) ? 0 : &blip_buf ); | |
116 if ( ym2612.enabled() ) | |
117 { | |
118 dac_synth.volume( (mask & 0x40) ? 0.0 : 0.1115 / 256 * gain ); | |
119 ym2612.mute_voices( mask ); | |
120 } | |
121 | |
122 if ( ym2413.enabled() ) | |
123 { | |
124 int m = mask & 0x3f; | |
125 if ( mask & 0x20 ) | |
126 m |= 0x01e0; // channels 5-8 | |
127 if ( mask & 0x40 ) | |
128 m |= 0x3e00; | |
129 ym2413.mute_voices( m ); | |
130 } | |
131 } | |
132 } | |
133 | |
134 blargg_err_t Vgm_Emu::load_( const header_t& h, void const* new_data, long new_size ) | |
135 { | |
136 header_ = h; | |
137 | |
138 // compatibility | |
139 if ( 0 != memcmp( header_.tag, "Vgm ", 4 ) ) | |
140 return "Not a VGM file"; | |
141 check( get_le32( header_.version ) <= 0x150 ); | |
142 | |
143 // psg rate | |
144 long psg_rate = get_le32( header_.psg_rate ); | |
145 if ( !psg_rate ) | |
146 psg_rate = 3579545; | |
147 blip_time_factor = (long) floor( (double) (1L << blip_time_bits) / vgm_rate * psg_rate + 0.5 ); | |
148 blip_buf.clock_rate( psg_rate ); | |
149 | |
150 data = (byte*) new_data; | |
151 data_end = data + new_size; | |
152 | |
153 // get loop | |
154 loop_begin = data_end; | |
155 if ( get_le32( header_.loop_offset ) ) | |
156 loop_begin = &data [get_le32( header_.loop_offset ) + offsetof (header_t,loop_offset) - 0x40]; | |
157 | |
158 set_voice_count( psg.osc_count ); | |
159 set_track_count( 1 ); | |
160 | |
161 BLARGG_RETURN_ERR( setup_fm() ); | |
162 | |
163 // do after FM in case output buffer is changed | |
164 BLARGG_RETURN_ERR( Classic_Emu::setup_buffer( psg_rate ) ); | |
165 | |
166 return blargg_success; | |
167 } | |
168 | |
169 blargg_err_t Vgm_Emu::setup_fm() | |
170 { | |
171 long ym2612_rate = get_le32( header_.ym2612_rate ); | |
172 long ym2413_rate = get_le32( header_.ym2413_rate ); | |
173 if ( ym2413_rate && get_le32( header_.version ) < 0x110 ) | |
174 update_fm_rates( &ym2413_rate, &ym2612_rate ); | |
175 | |
176 uses_fm = false; | |
177 | |
178 double fm_rate = blip_buf.sample_rate() * oversample_factor; | |
179 | |
180 if ( ym2612_rate ) | |
181 { | |
182 uses_fm = true; | |
183 if ( !oversample ) | |
184 fm_rate = ym2612_rate / 144.0; | |
185 Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, gain ); | |
186 BLARGG_RETURN_ERR( ym2612.set_rate( fm_rate, ym2612_rate ) ); | |
187 ym2612.enable( true ); | |
188 set_voice_count( 8 ); | |
189 } | |
190 | |
191 if ( !uses_fm && ym2413_rate ) | |
192 { | |
193 uses_fm = true; | |
194 if ( !oversample ) | |
195 fm_rate = ym2413_rate / 72.0; | |
196 Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, gain ); | |
197 int result = ym2413.set_rate( fm_rate, ym2413_rate ); | |
198 if ( result == 2 ) | |
199 return "YM2413 FM sound isn't supported"; | |
200 BLARGG_CHECK_ALLOC( !result ); | |
201 ym2413.enable( true ); | |
202 set_voice_count( 8 ); | |
203 } | |
204 | |
205 if ( uses_fm ) | |
206 { | |
207 //dprintf( "fm_rate: %f\n", fm_rate ); | |
208 fm_time_factor = 2 + (long) floor( fm_rate * (1L << fm_time_bits) / vgm_rate + 0.5 ); | |
209 BLARGG_RETURN_ERR( Dual_Resampler::resize( blip_buf.length() * blip_buf.sample_rate() / 1000 ) ); | |
210 psg.volume( 0.135 * gain ); | |
211 } | |
212 else | |
213 { | |
214 ym2612.enable( false ); | |
215 ym2413.enable( false ); | |
216 psg.volume( 1.0 ); | |
217 } | |
218 | |
219 return blargg_success; | |
220 } | |
221 | |
222 blargg_err_t Vgm_Emu::load( Data_Reader& reader ) | |
223 { | |
224 header_t h; | |
225 BLARGG_RETURN_ERR( reader.read( &h, sizeof h ) ); | |
226 return load( h, reader ); | |
227 } | |
228 | |
229 blargg_err_t Vgm_Emu::load( const header_t& h, Data_Reader& reader ) | |
230 { | |
231 unload(); | |
232 | |
233 // allocate and read data | |
234 long data_size = reader.remain(); | |
235 int const padding = 8; | |
236 BLARGG_RETURN_ERR( mem.resize( data_size + padding ) ); | |
237 blargg_err_t err = reader.read( mem.begin(), data_size ); | |
238 if ( err ) { | |
239 unload(); | |
240 return err; | |
241 } | |
242 memset( &mem [data_size], 0x66, padding ); // pad with end command | |
243 | |
244 return load_( h, mem.begin(), data_size ); | |
245 } | |
246 | |
247 void Vgm_Emu::start_track( int track ) | |
248 { | |
249 require( data ); // file must have been loaded | |
250 | |
251 Classic_Emu::start_track( track ); | |
252 psg.reset(); | |
253 | |
254 dac_disabled = -1; | |
255 pcm_data = data; | |
256 pcm_pos = data; | |
257 dac_amp = -1; | |
258 vgm_time = 0; | |
259 pos = data; | |
260 if ( get_le32( header_.version ) >= 0x150 ) | |
261 { | |
262 long data_offset = get_le32( header_.data_offset ); | |
263 check( data_offset ); | |
264 if ( data_offset ) | |
265 pos += data_offset + offsetof (header_t,data_offset) - 0x40; | |
266 } | |
267 | |
268 if ( uses_fm ) | |
269 { | |
270 if ( ym2413.enabled() ) | |
271 ym2413.reset(); | |
272 | |
273 if ( ym2612.enabled() ) | |
274 ym2612.reset(); | |
275 | |
276 fm_time_offset = 0; | |
277 blip_buf.clear(); | |
278 Dual_Resampler::clear(); | |
279 } | |
280 } | |
281 | |
282 long Vgm_Emu::run( int msec, bool* added_stereo ) | |
283 { | |
284 blip_time_t psg_end = run_commands( msec * vgm_rate / 1000 ); | |
285 *added_stereo = psg.end_frame( psg_end ); | |
286 return psg_end; | |
287 } | |
288 | |
289 void Vgm_Emu::play( long count, sample_t* out ) | |
290 { | |
291 require( pos ); // track must have been started | |
292 | |
293 if ( uses_fm ) | |
294 Dual_Resampler::play( count, out, blip_buf ); | |
295 else | |
296 Classic_Emu::play( count, out ); | |
297 } | |
298 |