/gme/Gym_Emu.cpp

http://game-music-emu.googlecode.com/ · C++ · 380 lines · 288 code · 68 blank · 24 comment · 64 complexity · 72903e598c85ac23481635147fcc32bd MD5 · raw file

  1. // Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
  2. #include "Gym_Emu.h"
  3. #include "blargg_endian.h"
  4. #include <string.h>
  5. /* Copyright (C) 2003-2006 Shay Green. This module is free software; you
  6. can redistribute it and/or modify it under the terms of the GNU Lesser
  7. General Public License as published by the Free Software Foundation; either
  8. version 2.1 of the License, or (at your option) any later version. This
  9. module is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  11. FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
  12. details. You should have received a copy of the GNU Lesser General Public
  13. License along with this module; if not, write to the Free Software Foundation,
  14. Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
  15. #include "blargg_source.h"
  16. double const min_tempo = 0.25;
  17. double const oversample_factor = 5 / 3.0;
  18. double const fm_gain = 3.0;
  19. const long base_clock = 53700300;
  20. const long clock_rate = base_clock / 15;
  21. Gym_Emu::Gym_Emu()
  22. {
  23. data = 0;
  24. pos = 0;
  25. set_type( gme_gym_type );
  26. static const char* const names [] = {
  27. "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG"
  28. };
  29. set_voice_names( names );
  30. set_silence_lookahead( 1 ); // tracks should already be trimmed
  31. }
  32. Gym_Emu::~Gym_Emu() { }
  33. // Track info
  34. static void get_gym_info( Gym_Emu::header_t const& h, long length, track_info_t* out )
  35. {
  36. if ( !memcmp( h.tag, "GYMX", 4 ) )
  37. {
  38. length = length * 50 / 3; // 1000 / 60
  39. long loop = get_le32( h.loop_start );
  40. if ( loop )
  41. {
  42. out->intro_length = loop * 50 / 3;
  43. out->loop_length = length - out->intro_length;
  44. }
  45. else
  46. {
  47. out->length = length;
  48. out->intro_length = length; // make it clear that track is no longer than length
  49. out->loop_length = 0;
  50. }
  51. // more stupidity where the field should have been left
  52. if ( strcmp( h.song, "Unknown Song" ) )
  53. GME_COPY_FIELD( h, out, song );
  54. if ( strcmp( h.game, "Unknown Game" ) )
  55. GME_COPY_FIELD( h, out, game );
  56. if ( strcmp( h.copyright, "Unknown Publisher" ) )
  57. GME_COPY_FIELD( h, out, copyright );
  58. if ( strcmp( h.dumper, "Unknown Person" ) )
  59. GME_COPY_FIELD( h, out, dumper );
  60. if ( strcmp( h.comment, "Header added by YMAMP" ) )
  61. GME_COPY_FIELD( h, out, comment );
  62. }
  63. }
  64. blargg_err_t Gym_Emu::track_info_( track_info_t* out, int ) const
  65. {
  66. get_gym_info( header_, track_length(), out );
  67. return 0;
  68. }
  69. static long gym_track_length( byte const* p, byte const* end )
  70. {
  71. long time = 0;
  72. while ( p < end )
  73. {
  74. switch ( *p++ )
  75. {
  76. case 0:
  77. time++;
  78. break;
  79. case 1:
  80. case 2:
  81. p += 2;
  82. break;
  83. case 3:
  84. p += 1;
  85. break;
  86. }
  87. }
  88. return time;
  89. }
  90. long Gym_Emu::track_length() const { return gym_track_length( data, data_end ); }
  91. static blargg_err_t check_header( byte const* in, long size, int* data_offset = 0 )
  92. {
  93. if ( size < 4 )
  94. return gme_wrong_file_type;
  95. if ( memcmp( in, "GYMX", 4 ) == 0 )
  96. {
  97. if ( size < Gym_Emu::header_size + 1 )
  98. return gme_wrong_file_type;
  99. if ( memcmp( ((Gym_Emu::header_t const*) in)->packed, "\0\0\0\0", 4 ) != 0 )
  100. return "Packed GYM file not supported";
  101. if ( data_offset )
  102. *data_offset = Gym_Emu::header_size;
  103. }
  104. else if ( *in > 3 )
  105. {
  106. return gme_wrong_file_type;
  107. }
  108. return 0;
  109. }
  110. struct Gym_File : Gme_Info_
  111. {
  112. byte const* file_begin;
  113. byte const* file_end;
  114. int data_offset;
  115. Gym_File() { set_type( gme_gym_type ); }
  116. blargg_err_t load_mem_( byte const* in, long size )
  117. {
  118. file_begin = in;
  119. file_end = in + size;
  120. data_offset = 0;
  121. return check_header( in, size, &data_offset );
  122. }
  123. blargg_err_t track_info_( track_info_t* out, int ) const
  124. {
  125. long length = gym_track_length( &file_begin [data_offset], file_end );
  126. get_gym_info( *(Gym_Emu::header_t const*) file_begin, length, out );
  127. return 0;
  128. }
  129. };
  130. static Music_Emu* new_gym_emu () { return BLARGG_NEW Gym_Emu ; }
  131. static Music_Emu* new_gym_file() { return BLARGG_NEW Gym_File; }
  132. static gme_type_t_ const gme_gym_type_ = { "Sega Genesis", 1, &new_gym_emu, &new_gym_file, "GYM", 0 };
  133. gme_type_t const gme_gym_type = &gme_gym_type_;
  134. // Setup
  135. blargg_err_t Gym_Emu::set_sample_rate_( long sample_rate )
  136. {
  137. blip_eq_t eq( -32, 8000, sample_rate );
  138. apu.treble_eq( eq );
  139. dac_synth.treble_eq( eq );
  140. apu.volume( 0.135 * fm_gain * gain() );
  141. dac_synth.volume( 0.125 / 256 * fm_gain * gain() );
  142. double factor = Dual_Resampler::setup( oversample_factor, 0.990, fm_gain * gain() );
  143. fm_sample_rate = sample_rate * factor;
  144. RETURN_ERR( blip_buf.set_sample_rate( sample_rate, int (1000 / 60.0 / min_tempo) ) );
  145. blip_buf.clock_rate( clock_rate );
  146. RETURN_ERR( fm.set_rate( fm_sample_rate, base_clock / 7.0 ) );
  147. RETURN_ERR( Dual_Resampler::reset( long (1.0 / 60 / min_tempo * sample_rate) ) );
  148. return 0;
  149. }
  150. void Gym_Emu::set_tempo_( double t )
  151. {
  152. if ( t < min_tempo )
  153. {
  154. set_tempo( min_tempo );
  155. return;
  156. }
  157. if ( blip_buf.sample_rate() )
  158. {
  159. clocks_per_frame = long (clock_rate / 60 / tempo());
  160. Dual_Resampler::resize( long (sample_rate() / (60.0 * tempo())) );
  161. }
  162. }
  163. void Gym_Emu::mute_voices_( int mask )
  164. {
  165. Music_Emu::mute_voices_( mask );
  166. fm.mute_voices( mask );
  167. dac_muted = (mask & 0x40) != 0;
  168. apu.output( (mask & 0x80) ? 0 : &blip_buf );
  169. }
  170. blargg_err_t Gym_Emu::load_mem_( byte const* in, long size )
  171. {
  172. assert( offsetof (header_t,packed [4]) == header_size );
  173. int offset = 0;
  174. RETURN_ERR( check_header( in, size, &offset ) );
  175. set_voice_count( 8 );
  176. data = in + offset;
  177. data_end = in + size;
  178. loop_begin = 0;
  179. if ( offset )
  180. header_ = *(header_t const*) in;
  181. else
  182. memset( &header_, 0, sizeof header_ );
  183. return 0;
  184. }
  185. // Emulation
  186. blargg_err_t Gym_Emu::start_track_( int track )
  187. {
  188. RETURN_ERR( Music_Emu::start_track_( track ) );
  189. pos = data;
  190. loop_remain = get_le32( header_.loop_start );
  191. prev_dac_count = 0;
  192. dac_enabled = false;
  193. dac_amp = -1;
  194. fm.reset();
  195. apu.reset();
  196. blip_buf.clear();
  197. Dual_Resampler::clear();
  198. return 0;
  199. }
  200. void Gym_Emu::run_dac( int dac_count )
  201. {
  202. // Guess beginning and end of sample and adjust rate and buffer position accordingly.
  203. // count dac samples in next frame
  204. int next_dac_count = 0;
  205. const byte* p = this->pos;
  206. int cmd;
  207. while ( (cmd = *p++) != 0 )
  208. {
  209. int data = *p++;
  210. if ( cmd <= 2 )
  211. ++p;
  212. if ( cmd == 1 && data == 0x2A )
  213. next_dac_count++;
  214. }
  215. // detect beginning and end of sample
  216. int rate_count = dac_count;
  217. int start = 0;
  218. if ( !prev_dac_count && next_dac_count && dac_count < next_dac_count )
  219. {
  220. rate_count = next_dac_count;
  221. start = next_dac_count - dac_count;
  222. }
  223. else if ( prev_dac_count && !next_dac_count && dac_count < prev_dac_count )
  224. {
  225. rate_count = prev_dac_count;
  226. }
  227. // Evenly space samples within buffer section being used
  228. blip_resampled_time_t period = blip_buf.resampled_duration( clocks_per_frame ) / rate_count;
  229. blip_resampled_time_t time = blip_buf.resampled_time( 0 ) +
  230. period * start + (period >> 1);
  231. int dac_amp = this->dac_amp;
  232. if ( dac_amp < 0 )
  233. dac_amp = dac_buf [0];
  234. for ( int i = 0; i < dac_count; i++ )
  235. {
  236. int delta = dac_buf [i] - dac_amp;
  237. dac_amp += delta;
  238. dac_synth.offset_resampled( time, delta, &blip_buf );
  239. time += period;
  240. }
  241. this->dac_amp = dac_amp;
  242. }
  243. void Gym_Emu::parse_frame()
  244. {
  245. int dac_count = 0;
  246. const byte* pos = this->pos;
  247. if ( loop_remain && !--loop_remain )
  248. loop_begin = pos; // find loop on first time through sequence
  249. int cmd;
  250. while ( (cmd = *pos++) != 0 )
  251. {
  252. int data = *pos++;
  253. if ( cmd == 1 )
  254. {
  255. int data2 = *pos++;
  256. if ( data != 0x2A )
  257. {
  258. if ( data == 0x2B )
  259. dac_enabled = (data2 & 0x80) != 0;
  260. fm.write0( data, data2 );
  261. }
  262. else if ( dac_count < (int) sizeof dac_buf )
  263. {
  264. dac_buf [dac_count] = data2;
  265. dac_count += dac_enabled;
  266. }
  267. }
  268. else if ( cmd == 2 )
  269. {
  270. fm.write1( data, *pos++ );
  271. }
  272. else if ( cmd == 3 )
  273. {
  274. apu.write_data( 0, data );
  275. }
  276. else
  277. {
  278. // to do: many GYM streams are full of errors, and error count should
  279. // reflect cases where music is really having problems
  280. //log_error();
  281. --pos; // put data back
  282. }
  283. }
  284. // loop
  285. if ( pos >= data_end )
  286. {
  287. check( pos == data_end );
  288. if ( loop_begin )
  289. pos = loop_begin;
  290. else
  291. set_track_ended();
  292. }
  293. this->pos = pos;
  294. // dac
  295. if ( dac_count && !dac_muted )
  296. run_dac( dac_count );
  297. prev_dac_count = dac_count;
  298. }
  299. int Gym_Emu::play_frame( blip_time_t blip_time, int sample_count, sample_t* buf )
  300. {
  301. if ( !track_ended() )
  302. parse_frame();
  303. apu.end_frame( blip_time );
  304. memset( buf, 0, sample_count * sizeof *buf );
  305. fm.run( sample_count >> 1, buf );
  306. return sample_count;
  307. }
  308. blargg_err_t Gym_Emu::play_( long count, sample_t* out )
  309. {
  310. Dual_Resampler::dual_play( count, out, blip_buf );
  311. return 0;
  312. }