Coverage Report

Created: 2026-11-12 07:28

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
166
{
32
166
  disable_oversampling_ = false;
33
166
  psg_dual = false;
34
166
  psg_t6w28 = false;
35
166
  psg_rate   = 0;
36
166
  set_type( gme_vgm_type );
37
38
166
  static int const types [8] = {
39
166
    wave_type | 1, wave_type | 0, wave_type | 2, noise_type | 0
40
166
  };
41
166
  set_voice_types( types );
42
43
166
  set_silence_lookahead( 1 ); // tracks should already be trimmed
44
45
166
  set_equalizer( make_equalizer( -14.0, 80 ) );
46
166
}
47
48
166
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
134
{
97
134
  if ( remain < gd3_header_size ) return 0;
98
9
  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
134
{
109
134
  if ( size )
110
134
    *size = 0;
111
112
134
  long gd3_offset = get_le32( header().gd3_offset ) - 0x2C;
113
134
  if ( gd3_offset < 0 )
114
0
    return 0;
115
116
134
  byte const* gd3 = data + header_size + gd3_offset;
117
134
  long gd3_size = check_gd3_header( gd3, data_end - gd3 );
118
134
  if ( !gd3_size )
119
134
    return 0;
120
121
0
  if ( size )
122
0
    *size = gd3_size + gd3_header_size;
123
124
0
  return gd3;
125
134
}
126
127
static void get_vgm_length( Vgm_Emu::header_t const& h, track_info_t* out )
128
134
{
129
134
  long length = get_le32( h.track_duration ) * 10 / 441;
130
134
  if ( length > 0 )
131
30
  {
132
30
    long loop = get_le32( h.loop_duration );
133
30
    if ( loop > 0 && get_le32( h.loop_offset ) )
134
10
    {
135
10
      out->loop_length = loop * 10 / 441;
136
10
      out->intro_length = length - out->loop_length;
137
10
    }
138
20
    else
139
20
    {
140
20
      out->length = length; // 1000 / 44100 (VGM files used 44100 as timebase)
141
20
      out->intro_length = length; // make it clear that track is no longer than length
142
20
      out->loop_length = 0;
143
20
    }
144
30
  }
145
134
}
146
147
blargg_err_t Vgm_Emu::track_info_( track_info_t* out, int ) const
148
134
{
149
134
  get_vgm_length( header(), out );
150
151
134
  int size;
152
134
  byte const* gd3 = gd3_data( &size );
153
134
  if ( gd3 )
154
0
    parse_gd3( gd3 + gd3_header_size, gd3 + size, out );
155
156
134
  return 0;
157
134
}
158
159
static blargg_err_t check_vgm_header( Vgm_Emu::header_t const& h )
160
289
{
161
289
  if ( memcmp( h.tag, "Vgm ", 4 ) )
162
6
    return gme_wrong_file_type;
163
283
  return 0;
164
289
}
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
166
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
134
{
222
134
  if ( psg_rate )
223
134
  {
224
134
    vgm_rate = (uint32_t) (44100u * t + 0.5);
225
134
    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
134
    fm_time_factor = 2 + (uint64_t) floor( fm_rate * (1L << fm_time_bits) / vgm_rate + 0.5 );
233
134
  }
234
134
}
235
236
blargg_err_t Vgm_Emu::set_sample_rate_( long sample_rate )
237
166
{
238
166
  RETURN_ERR( blip_buf.set_sample_rate( sample_rate, 1000 / 30 ) );
239
166
  return Classic_Emu::set_sample_rate_( sample_rate );
240
166
}
241
242
blargg_err_t Vgm_Emu::set_multi_channel ( bool is_enabled )
243
166
{
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
166
  {
256
166
    (void) is_enabled;
257
166
    return "multichannel rendering not supported for YM2*** FM sound chip emulators";
258
166
  }
259
166
}
260
261
void Vgm_Emu::update_eq( blip_eq_t const& eq )
262
434
{
263
434
  psg[0].treble_eq( eq );
264
434
  if ( psg_dual )
265
0
    psg[1].treble_eq( eq );
266
434
  dac_synth.treble_eq( eq );
267
434
}
268
269
void Vgm_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
270
940
{
271
940
  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
940
  else
290
940
  {
291
940
    if ( i < psg[0].osc_count )
292
536
      psg[0].osc_output( i, c, l, r );
293
940
  }
294
295
940
}
296
297
void Vgm_Emu::mute_voices_( int mask )
298
134
{
299
134
  Classic_Emu::mute_voices_( mask );
300
134
  dac_synth.output( &blip_buf );
301
134
  if ( uses_fm )
302
101
  {
303
101
    psg[0].output( (mask & 0x80) ? 0 : &blip_buf );
304
101
    if ( psg_dual )
305
0
      psg[1].output( (mask & 0x80) ? 0 : &blip_buf );
306
101
    if ( ym2612[0].enabled() )
307
101
    {
308
101
      dac_synth.volume( (mask & 0x40) ? 0.0 : 0.1115 / 256 * fm_gain * gain() );
309
101
      ym2612[0].mute_voices( mask );
310
101
      if ( ym2612[1].enabled() )
311
17
        ym2612[1].mute_voices( mask );
312
101
    }
313
101
    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
101
  }
325
134
}
326
327
blargg_err_t Vgm_Emu::load_mem_( byte const* new_data, long new_size )
328
300
{
329
300
  blaarg_static_assert( offsetof (header_t,unused2 [8]) == header_size, "VGM Header layout incorrect!" );
330
331
300
  if ( new_size <= header_size )
332
11
    return gme_wrong_file_type;
333
334
289
  header_t const& h = *(header_t const*) new_data;
335
336
289
  RETURN_ERR( check_vgm_header( h ) );
337
338
283
  check( get_le32( h.version ) <= 0x150 );
339
340
  // psg rate
341
283
  psg_rate = get_le32( h.psg_rate );
342
283
  if ( !psg_rate )
343
207
    psg_rate = 3579545;
344
76
  else if ( psg_rate > 100000000 )
345
36
    psg_rate = 100000000 ;
346
283
  psg_dual = ( psg_rate & 0x40000000 ) != 0;
347
283
  psg_t6w28 = ( psg_rate & 0x80000000 ) != 0;
348
283
  psg_rate &= 0x0FFFFFFF;
349
283
  blip_buf.clock_rate( psg_rate );
350
351
283
  data     = new_data;
352
283
  data_end = new_data + new_size;
353
354
  // get loop
355
283
  loop_begin = data_end;
356
283
  if ( get_le32( h.loop_offset ) )
357
62
    loop_begin = &data [get_le32( h.loop_offset ) + offsetof (header_t,loop_offset)];
358
359
283
  set_voice_count( psg[0].osc_count );
360
361
283
  RETURN_ERR( setup_fm() );
362
363
268
  static const char* const fm_names [] = {
364
268
    "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG"
365
268
  };
366
268
  static const char* const psg_names [] = { "Square 1", "Square 2", "Square 3", "Noise" };
367
268
  set_voice_names( uses_fm ? fm_names : psg_names );
368
369
  // do after FM in case output buffer is changed
370
268
  return Classic_Emu::setup_buffer( psg_rate );
371
283
}
372
373
blargg_err_t Vgm_Emu::setup_fm()
374
283
{
375
283
  long ym2612_rate = get_le32( header().ym2612_rate );
376
283
  bool ym2612_dual = ( ym2612_rate & 0x40000000 ) != 0;
377
283
  long ym2413_rate = get_le32( header().ym2413_rate );
378
283
  bool ym2413_dual = ( ym2413_rate & 0x40000000 ) != 0;
379
283
  if ( ym2413_rate && get_le32( header().version ) < 0x110 )
380
48
    update_fm_rates( &ym2413_rate, &ym2612_rate );
381
382
283
  uses_fm = false;
383
384
283
  fm_rate = blip_buf.sample_rate() * oversample_factor;
385
386
283
  if ( ym2612_rate )
387
202
  {
388
202
    ym2612_rate &= ~0xC0000000;
389
202
    uses_fm = true;
390
202
    if ( disable_oversampling_ )
391
0
      fm_rate = ym2612_rate / 144.0;
392
202
    Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, fm_gain * gain() );
393
202
    RETURN_ERR( ym2612[0].set_rate( fm_rate, ym2612_rate ) );
394
202
    ym2612[0].enable( true );
395
202
    if ( ym2612_dual )
396
34
    {
397
34
      RETURN_ERR( ym2612[1].set_rate( fm_rate, ym2612_rate ) );
398
34
      ym2612[1].enable( true );
399
34
    }
400
202
    set_voice_count( 8 );
401
202
  }
402
403
283
  if ( !uses_fm && ym2413_rate )
404
15
  {
405
15
    ym2413_rate &= ~0xC0000000;
406
15
    uses_fm = true;
407
15
    if ( disable_oversampling_ )
408
0
      fm_rate = ym2413_rate / 72.0;
409
15
    Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, fm_gain * gain() );
410
15
    int result = ym2413[0].set_rate( fm_rate, ym2413_rate );
411
15
    if ( result == 2 )
412
15
      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
268
  if ( uses_fm )
427
202
  {
428
202
    RETURN_ERR( Dual_Resampler::reset( blip_buf.length() * blip_buf.sample_rate() / 1000 ) );
429
202
    psg[0].volume( 0.135 * fm_gain * gain() );
430
202
    if ( psg_dual )
431
0
      psg[1].volume( 0.135 * fm_gain * gain() );
432
202
  }
433
66
  else
434
66
  {
435
66
    ym2612[0].enable( false );
436
66
    ym2612[1].enable( false );
437
66
    ym2413[0].enable( false );
438
66
    ym2413[1].enable( false );
439
66
    psg[0].volume( gain() );
440
66
    psg[1].volume( gain() );
441
66
  }
442
443
268
  return 0;
444
268
}
445
446
// Emulation
447
448
blargg_err_t Vgm_Emu::start_track_( int track )
449
134
{
450
134
  RETURN_ERR( Classic_Emu::start_track_( track ) );
451
134
  psg[0].reset( get_le16( header().noise_feedback ), header().noise_width );
452
134
  if ( psg_dual )
453
0
    psg[1].reset( get_le16( header().noise_feedback ), header().noise_width );
454
455
134
  dac_disabled = -1;
456
134
  pos          = data + header_size;
457
134
  pcm_data     = pos;
458
134
  pcm_pos      = pos;
459
134
  dac_amp      = -1;
460
134
  vgm_time     = 0;
461
134
  if ( get_le32( header().version ) >= 0x150 )
462
99
  {
463
99
    long data_offset = get_le32( header().data_offset );
464
99
    check( data_offset );
465
99
    if ( data_offset )
466
18
      pos += data_offset + offsetof (header_t,data_offset) - 0x40;
467
99
  }
468
469
134
  if ( uses_fm )
470
101
  {
471
101
    if ( ym2413[0].enabled() )
472
0
      ym2413[0].reset();
473
474
101
    if ( ym2413[1].enabled() )
475
0
      ym2413[1].reset();
476
477
101
    if ( ym2612[0].enabled() )
478
101
      ym2612[0].reset();
479
480
101
    if ( ym2612[1].enabled() )
481
17
      ym2612[1].reset();
482
483
101
    fm_time_offset = 0;
484
101
    blip_buf.clear();
485
101
    Dual_Resampler::clear();
486
101
  }
487
134
  return 0;
488
134
}
489
490
blargg_err_t Vgm_Emu::run_clocks( blip_time_t& time_io, int msec )
491
85
{
492
85
  time_io = run_commands( msec * vgm_rate / 1000 );
493
85
  psg[0].end_frame( time_io );
494
85
  if ( psg_dual )
495
0
    psg[1].end_frame( time_io );
496
85
  return 0;
497
85
}
498
499
blargg_err_t Vgm_Emu::play_( long count, sample_t* out )
500
678
{
501
678
  if ( !uses_fm )
502
145
    return Classic_Emu::play_( count, out );
503
504
533
  Dual_Resampler::dual_play( count, out, blip_buf );
505
533
  return 0;
506
678
}