Coverage Report

Created: 2026-10-17 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/contrib/contrib-build/game-music-emu/gme/Vgm_Emu.cpp
Line
Count
Source
1
// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/
2
3
#include "Vgm_Emu.h"
4
5
#include "blargg_endian.h"
6
#include <string.h>
7
#include <math.h>
8
#include <algorithm>
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 more
17
details. You should have received a copy of the GNU Lesser General Public
18
License along with this module; if not, write to the Free Software Foundation,
19
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
20
21
#include "blargg_source.h"
22
23
static double const fm_gain = 3.0; // FM emulators are internally quieter to avoid 16-bit overflow
24
static double const rolloff = 0.990;
25
static double const oversample_factor = 1.0;
26
27
using std::min;
28
using std::max;
29
30
Vgm_Emu::Vgm_Emu()
31
132
{
32
132
  disable_oversampling_ = false;
33
132
  psg_dual = false;
34
132
  psg_t6w28 = false;
35
132
  psg_rate   = 0;
36
132
  set_type( gme_vgm_type );
37
38
132
  static int const types [8] = {
39
132
    wave_type | 1, wave_type | 0, wave_type | 2, noise_type | 0
40
132
  };
41
132
  set_voice_types( types );
42
43
132
  set_silence_lookahead( 1 ); // tracks should already be trimmed
44
45
132
  set_equalizer( make_equalizer( -14.0, 80 ) );
46
132
}
47
48
132
Vgm_Emu::~Vgm_Emu() { }
49
50
// Track info
51
52
static byte const* skip_gd3_str( byte const* in, byte const* end )
53
0
{
54
0
  while ( end - in >= 2 )
55
0
  {
56
0
    in += 2;
57
0
    if ( !(in [-2] | in [-1]) )
58
0
      break;
59
0
  }
60
0
  return in;
61
0
}
62
63
static byte const* get_gd3_str( byte const* in, byte const* end, char* field )
64
0
{
65
0
  byte const* mid = skip_gd3_str( in, end );
66
0
  int len = (mid - in) / 2 - 1;
67
0
  if ( len > 0 )
68
0
  {
69
0
    len = min( len, (int) Gme_File::max_field_ );
70
0
    field [len] = 0;
71
0
    for ( int i = 0; i < len; i++ )
72
0
      field [i] = (in [i * 2 + 1] ? '?' : in [i * 2]); // TODO: convert to utf-8
73
0
  }
74
0
  return mid;
75
0
}
76
77
static byte const* get_gd3_pair( byte const* in, byte const* end, char* field )
78
0
{
79
0
  return skip_gd3_str( get_gd3_str( in, end, field ), end );
80
0
}
81
82
static void parse_gd3( byte const* in, byte const* end, track_info_t* out )
83
0
{
84
0
  in = get_gd3_pair( in, end, out->song );
85
0
  in = get_gd3_pair( in, end, out->game );
86
0
  in = get_gd3_pair( in, end, out->system );
87
0
  in = get_gd3_pair( in, end, out->author );
88
0
  in = get_gd3_str ( in, end, out->copyright );
89
0
  in = get_gd3_pair( in, end, out->dumper );
90
0
  /*in =*/ get_gd3_str ( in, end, out->comment );
91
0
}
92
93
static int const gd3_header_size = 12;
94
95
static long check_gd3_header( byte const* h, long remain )
96
107
{
97
107
  if ( remain < gd3_header_size ) return 0;
98
6
  if ( memcmp( h, "Gd3 ", 4 ) ) return 0;
99
0
  if ( get_le32( h + 4 ) >= 0x200 ) return 0;
100
101
0
  long gd3_size = get_le32( h + 8 );
102
0
  if ( gd3_size > remain - gd3_header_size ) return 0;
103
104
0
  return gd3_size;
105
0
}
106
107
byte const* Vgm_Emu::gd3_data( int* size ) const
108
107
{
109
107
  if ( size )
110
107
    *size = 0;
111
112
107
  long gd3_offset = get_le32( header().gd3_offset ) - 0x2C;
113
107
  if ( gd3_offset < 0 )
114
0
    return 0;
115
116
107
  byte const* gd3 = data + header_size + gd3_offset;
117
107
  long gd3_size = check_gd3_header( gd3, data_end - gd3 );
118
107
  if ( !gd3_size )
119
107
    return 0;
120
121
0
  if ( size )
122
0
    *size = gd3_size + gd3_header_size;
123
124
0
  return gd3;
125
107
}
126
127
static void get_vgm_length( Vgm_Emu::header_t const& h, track_info_t* out )
128
107
{
129
107
  long length = get_le32( h.track_duration ) * 10 / 441;
130
107
  if ( length > 0 )
131
21
  {
132
21
    long loop = get_le32( h.loop_duration );
133
21
    if ( loop > 0 && get_le32( h.loop_offset ) )
134
8
    {
135
8
      out->loop_length = loop * 10 / 441;
136
8
      out->intro_length = length - out->loop_length;
137
8
    }
138
13
    else
139
13
    {
140
13
      out->length = length; // 1000 / 44100 (VGM files used 44100 as timebase)
141
13
      out->intro_length = length; // make it clear that track is no longer than length
142
13
      out->loop_length = 0;
143
13
    }
144
21
  }
145
107
}
146
147
blargg_err_t Vgm_Emu::track_info_( track_info_t* out, int ) const
148
107
{
149
107
  get_vgm_length( header(), out );
150
151
107
  int size;
152
107
  byte const* gd3 = gd3_data( &size );
153
107
  if ( gd3 )
154
0
    parse_gd3( gd3 + gd3_header_size, gd3 + size, out );
155
156
107
  return 0;
157
107
}
158
159
static blargg_err_t check_vgm_header( Vgm_Emu::header_t const& h )
160
232
{
161
232
  if ( memcmp( h.tag, "Vgm ", 4 ) )
162
6
    return gme_wrong_file_type;
163
226
  return 0;
164
232
}
165
166
struct Vgm_File : Gme_Info_
167
{
168
  Vgm_Emu::header_t h;
169
  blargg_vector<byte> gd3;
170
171
0
  Vgm_File() { set_type( gme_vgm_type ); }
172
173
  blargg_err_t load_( Data_Reader& in )
174
0
  {
175
0
    long file_size = in.remain();
176
0
    if ( file_size <= Vgm_Emu::header_size )
177
0
      return gme_wrong_file_type;
178
179
0
    RETURN_ERR( in.read( &h, Vgm_Emu::header_size ) );
180
0
    RETURN_ERR( check_vgm_header( h ) );
181
182
0
    long gd3_offset = get_le32( h.gd3_offset ) - 0x2C;
183
0
    long remain = file_size - Vgm_Emu::header_size - gd3_offset;
184
0
    byte gd3_h [gd3_header_size];
185
0
    if ( gd3_offset > 0 && remain >= gd3_header_size )
186
0
    {
187
0
      RETURN_ERR( in.skip( gd3_offset ) );
188
0
      RETURN_ERR( in.read( gd3_h, sizeof gd3_h ) );
189
0
      long gd3_size = check_gd3_header( gd3_h, remain );
190
0
      if ( gd3_size )
191
0
      {
192
0
        RETURN_ERR( gd3.resize( gd3_size ) );
193
0
        RETURN_ERR( in.read( gd3.begin(), gd3.size() ) );
194
0
      }
195
0
    }
196
0
    return 0;
197
0
  }
198
199
  blargg_err_t track_info_( track_info_t* out, int ) const
200
0
  {
201
0
    get_vgm_length( h, out );
202
0
    if ( gd3.size() )
203
0
      parse_gd3( gd3.begin(), gd3.end(), out );
204
0
    return 0;
205
0
  }
206
};
207
208
132
static Music_Emu* new_vgm_emu () { return BLARGG_NEW Vgm_Emu ; }
209
0
static Music_Emu* new_vgm_file() { return BLARGG_NEW Vgm_File; }
210
211
static gme_type_t_ const gme_vgm_type_ = { "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGM", 1 };
212
extern gme_type_t const gme_vgm_type = &gme_vgm_type_;
213
214
static gme_type_t_ const gme_vgz_type_ = { "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGZ", 1 };
215
extern gme_type_t const gme_vgz_type = &gme_vgz_type_;
216
217
218
// Setup
219
220
void Vgm_Emu::set_tempo_( double t )
221
107
{
222
107
  if ( psg_rate )
223
107
  {
224
107
    vgm_rate = (uint32_t) (44100u * t + 0.5);
225
107
    blip_time_factor = (uint64_t) floor( double (1L << blip_time_bits) / vgm_rate * psg_rate + 0.5 );
226
    //debug_printf( "blip_time_factor: %ld\n", blip_time_factor );
227
    //debug_printf( "vgm_rate: %ld\n", vgm_rate );
228
    // TODO: remove? calculates vgm_rate more accurately (above differs at most by one Hz only)
229
    //blip_time_factor = (long) floor( double (1L << blip_time_bits) * psg_rate / 44100 / t + 0.5 );
230
    //vgm_rate = (long) floor( double (1L << blip_time_bits) * psg_rate / blip_time_factor + 0.5 );
231
232
107
    fm_time_factor = 2 + (uint64_t) floor( fm_rate * (1L << fm_time_bits) / vgm_rate + 0.5 );
233
107
  }
234
107
}
235
236
blargg_err_t Vgm_Emu::set_sample_rate_( long sample_rate )
237
132
{
238
132
  RETURN_ERR( blip_buf.set_sample_rate( sample_rate, 1000 / 30 ) );
239
132
  return Classic_Emu::set_sample_rate_( sample_rate );
240
132
}
241
242
blargg_err_t Vgm_Emu::set_multi_channel ( bool is_enabled )
243
132
{
244
  // we acutally should check here whether this is classic emu or not
245
  // however set_multi_channel() is called before setup_fm() resulting in uninited is_classic_emu()
246
  // hard code it to unsupported
247
#if 0
248
  if ( is_classic_emu() )
249
  {
250
    RETURN_ERR( Music_Emu::set_multi_channel_( is_enabled ) );
251
    return 0;
252
  }
253
  else
254
#endif
255
132
  {
256
132
    (void) is_enabled;
257
132
    return "multichannel rendering not supported for YM2*** FM sound chip emulators";
258
132
  }
259
132
}
260
261
void Vgm_Emu::update_eq( blip_eq_t const& eq )
262
346
{
263
346
  psg[0].treble_eq( eq );
264
346
  if ( psg_dual )
265
0
    psg[1].treble_eq( eq );
266
346
  dac_synth.treble_eq( eq );
267
346
}
268
269
void Vgm_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
270
732
{
271
732
  if ( psg_dual )
272
0
  {
273
0
    if ( psg_t6w28 )
274
0
    {
275
      // TODO: Make proper output of each PSG chip: 0 - all right, 1 - all left
276
0
      if ( i < psg[0].osc_count )
277
0
        psg[0].osc_output( i, c, r, r );
278
0
      if ( i < psg[1].osc_count )
279
0
        psg[1].osc_output( i, c, l, l );
280
0
    }
281
0
    else
282
0
    {
283
0
      if ( i < psg[0].osc_count )
284
0
        psg[0].osc_output( i, c, l, r );
285
0
      if ( i < psg[1].osc_count )
286
0
        psg[1].osc_output( i, c, l, r );
287
0
    }
288
0
  }
289
732
  else
290
732
  {
291
732
    if ( i < psg[0].osc_count )
292
428
      psg[0].osc_output( i, c, l, r );
293
732
  }
294
295
732
}
296
297
void Vgm_Emu::mute_voices_( int mask )
298
107
{
299
107
  Classic_Emu::mute_voices_( mask );
300
107
  dac_synth.output( &blip_buf );
301
107
  if ( uses_fm )
302
76
  {
303
76
    psg[0].output( (mask & 0x80) ? 0 : &blip_buf );
304
76
    if ( psg_dual )
305
0
      psg[1].output( (mask & 0x80) ? 0 : &blip_buf );
306
76
    if ( ym2612[0].enabled() )
307
76
    {
308
76
      dac_synth.volume( (mask & 0x40) ? 0.0 : 0.1115 / 256 * fm_gain * gain() );
309
76
      ym2612[0].mute_voices( mask );
310
76
      if ( ym2612[1].enabled() )
311
12
        ym2612[1].mute_voices( mask );
312
76
    }
313
76
    if ( ym2413[0].enabled() )
314
0
    {
315
0
      int m = mask & 0x3F;
316
0
      if ( mask & 0x20 )
317
0
        m |= 0x01E0; // channels 5-8
318
0
      if ( mask & 0x40 )
319
0
        m |= 0x3E00;
320
0
      ym2413[0].mute_voices( m );
321
0
      if ( ym2413[1].enabled() )
322
0
        ym2413[1].mute_voices( m );
323
0
    }
324
76
  }
325
107
}
326
327
blargg_err_t Vgm_Emu::load_mem_( byte const* new_data, long new_size )
328
239
{
329
239
  blaarg_static_assert( offsetof (header_t,unused2 [8]) == header_size, "VGM Header layout incorrect!" );
330
331
239
  if ( new_size <= header_size )
332
7
    return gme_wrong_file_type;
333
334
232
  header_t const& h = *(header_t const*) new_data;
335
336
232
  RETURN_ERR( check_vgm_header( h ) );
337
338
226
  check( get_le32( h.version ) <= 0x150 );
339
340
  // psg rate
341
226
  psg_rate = get_le32( h.psg_rate );
342
226
  if ( !psg_rate )
343
159
    psg_rate = 3579545;
344
67
  else if ( psg_rate > 100000000 )
345
31
    psg_rate = 100000000 ;
346
226
  psg_dual = ( psg_rate & 0x40000000 ) != 0;
347
226
  psg_t6w28 = ( psg_rate & 0x80000000 ) != 0;
348
226
  psg_rate &= 0x0FFFFFFF;
349
226
  blip_buf.clock_rate( psg_rate );
350
351
226
  data     = new_data;
352
226
  data_end = new_data + new_size;
353
354
  // get loop
355
226
  loop_begin = data_end;
356
226
  if ( get_le32( h.loop_offset ) )
357
49
    loop_begin = &data [get_le32( h.loop_offset ) + offsetof (header_t,loop_offset)];
358
359
226
  set_voice_count( psg[0].osc_count );
360
361
226
  RETURN_ERR( setup_fm() );
362
363
214
  static const char* const fm_names [] = {
364
214
    "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG"
365
214
  };
366
214
  static const char* const psg_names [] = { "Square 1", "Square 2", "Square 3", "Noise" };
367
214
  set_voice_names( uses_fm ? fm_names : psg_names );
368
369
  // do after FM in case output buffer is changed
370
214
  return Classic_Emu::setup_buffer( psg_rate );
371
226
}
372
373
blargg_err_t Vgm_Emu::setup_fm()
374
226
{
375
226
  long ym2612_rate = get_le32( header().ym2612_rate );
376
226
  bool ym2612_dual = ( ym2612_rate & 0x40000000 ) != 0;
377
226
  long ym2413_rate = get_le32( header().ym2413_rate );
378
226
  bool ym2413_dual = ( ym2413_rate & 0x40000000 ) != 0;
379
226
  if ( ym2413_rate && get_le32( header().version ) < 0x110 )
380
33
    update_fm_rates( &ym2413_rate, &ym2612_rate );
381
382
226
  uses_fm = false;
383
384
226
  fm_rate = blip_buf.sample_rate() * oversample_factor;
385
386
226
  if ( ym2612_rate )
387
152
  {
388
152
    ym2612_rate &= ~0xC0000000;
389
152
    uses_fm = true;
390
152
    if ( disable_oversampling_ )
391
0
      fm_rate = ym2612_rate / 144.0;
392
152
    Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, fm_gain * gain() );
393
152
    RETURN_ERR( ym2612[0].set_rate( fm_rate, ym2612_rate ) );
394
152
    ym2612[0].enable( true );
395
152
    if ( ym2612_dual )
396
24
    {
397
24
      RETURN_ERR( ym2612[1].set_rate( fm_rate, ym2612_rate ) );
398
24
      ym2612[1].enable( true );
399
24
    }
400
152
    set_voice_count( 8 );
401
152
  }
402
403
226
  if ( !uses_fm && ym2413_rate )
404
12
  {
405
12
    ym2413_rate &= ~0xC0000000;
406
12
    uses_fm = true;
407
12
    if ( disable_oversampling_ )
408
0
      fm_rate = ym2413_rate / 72.0;
409
12
    Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, fm_gain * gain() );
410
12
    int result = ym2413[0].set_rate( fm_rate, ym2413_rate );
411
12
    if ( result == 2 )
412
12
      return "YM2413 FM sound isn't supported";
413
0
    CHECK_ALLOC( !result );
414
0
    ym2413[0].enable( true );
415
0
    if ( ym2413_dual )
416
0
    {
417
0
      ym2413[1].enable( true );
418
0
      int result = ym2413[1].set_rate( fm_rate, ym2413_rate );
419
0
      if ( result == 2 )
420
0
        return "YM2413 FM sound isn't supported";
421
0
      CHECK_ALLOC( !result );
422
0
    }
423
0
    set_voice_count( 8 );
424
0
  }
425
426
214
  if ( uses_fm )
427
152
  {
428
152
    RETURN_ERR( Dual_Resampler::reset( blip_buf.length() * blip_buf.sample_rate() / 1000 ) );
429
152
    psg[0].volume( 0.135 * fm_gain * gain() );
430
152
    if ( psg_dual )
431
0
      psg[1].volume( 0.135 * fm_gain * gain() );
432
152
  }
433
62
  else
434
62
  {
435
62
    ym2612[0].enable( false );
436
62
    ym2612[1].enable( false );
437
62
    ym2413[0].enable( false );
438
62
    ym2413[1].enable( false );
439
62
    psg[0].volume( gain() );
440
62
    psg[1].volume( gain() );
441
62
  }
442
443
214
  return 0;
444
214
}
445
446
// Emulation
447
448
blargg_err_t Vgm_Emu::start_track_( int track )
449
107
{
450
107
  RETURN_ERR( Classic_Emu::start_track_( track ) );
451
107
  psg[0].reset( get_le16( header().noise_feedback ), header().noise_width );
452
107
  if ( psg_dual )
453
0
    psg[1].reset( get_le16( header().noise_feedback ), header().noise_width );
454
455
107
  dac_disabled = -1;
456
107
  pos          = data + header_size;
457
107
  pcm_data     = pos;
458
107
  pcm_pos      = pos;
459
107
  dac_amp      = -1;
460
107
  vgm_time     = 0;
461
107
  if ( get_le32( header().version ) >= 0x150 )
462
82
  {
463
82
    long data_offset = get_le32( header().data_offset );
464
82
    check( data_offset );
465
82
    if ( data_offset )
466
15
      pos += data_offset + offsetof (header_t,data_offset) - 0x40;
467
82
  }
468
469
107
  if ( uses_fm )
470
76
  {
471
76
    if ( ym2413[0].enabled() )
472
0
      ym2413[0].reset();
473
474
76
    if ( ym2413[1].enabled() )
475
0
      ym2413[1].reset();
476
477
76
    if ( ym2612[0].enabled() )
478
76
      ym2612[0].reset();
479
480
76
    if ( ym2612[1].enabled() )
481
12
      ym2612[1].reset();
482
483
76
    fm_time_offset = 0;
484
76
    blip_buf.clear();
485
76
    Dual_Resampler::clear();
486
76
  }
487
107
  return 0;
488
107
}
489
490
blargg_err_t Vgm_Emu::run_clocks( blip_time_t& time_io, int msec )
491
82
{
492
82
  time_io = run_commands( msec * vgm_rate / 1000 );
493
82
  psg[0].end_frame( time_io );
494
82
  if ( psg_dual )
495
0
    psg[1].end_frame( time_io );
496
82
  return 0;
497
82
}
498
499
blargg_err_t Vgm_Emu::play_( long count, sample_t* out )
500
508
{
501
508
  if ( !uses_fm )
502
141
    return Classic_Emu::play_( count, out );
503
504
367
  Dual_Resampler::dual_play( count, out, blip_buf );
505
367
  return 0;
506
508
}