summaryrefslogtreecommitdiff
path: root/libsidplay2/sidplay-libs-2.1.0/libsidplay/src/config.cpp
blob: 0b4a71da9d068f3010da50af85e888bb3b039a67 (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
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
/***************************************************************************
                          config.cpp  -  Library Configuration Code
                             -------------------
    begin                : Fri Jul 27 2001
    copyright            : (C) 2001 by Simon White
    email                : s_a_white@email.com
 ***************************************************************************/
/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
/***************************************************************************
 *  $Log: config.cpp,v $
 *  Revision 1.29  2002/10/15 18:14:02  s_a_white
 *  Removed upper limit frequency limit check to allow for oversampling.
 *
 *  Revision 1.28  2002/10/02 19:46:36  s_a_white
 *  RSID support & fix sid model forced operation.
 *
 *  Revision 1.27  2002/09/09 18:09:06  s_a_white
 *  Added error message for psid specific flag set in strict real C64 mode.
 *
 *  Revision 1.26  2002/08/10 22:35:56  s_a_white
 *  Small clock speed fix for when both clockSpeed and clockDefault are set
 *  to SID2_CLOCK_CORRECT.
 *
 *  Revision 1.25  2002/07/17 19:25:28  s_a_white
 *  Temp fix to allow SID model to change for current builder.
 *
 *  Revision 1.24  2002/04/14 21:46:50  s_a_white
 *  PlaySID reads fixed to come from RAM only.
 *
 *  Revision 1.23  2002/03/11 18:01:30  s_a_white
 *  Prevent lockup if config call fails with existing and old configurations.
 *
 *  Revision 1.22  2002/03/04 19:05:49  s_a_white
 *  Fix C++ use of nothrow.
 *
 *  Revision 1.21  2002/03/03 22:01:58  s_a_white
 *  New clock speed & sid model interface.
 *
 *  Revision 1.20  2002/02/18 21:59:10  s_a_white
 *  Added two new clock modes (FIXED).  Seems to be a requirement for
 *  HVSC/sidplayw.
 *
 *  Revision 1.19  2002/02/04 22:08:14  s_a_white
 *  Fixed main voice/sample gains.
 *
 *  Revision 1.18  2002/01/29 21:50:33  s_a_white
 *  Auto switching to a better emulation mode.  m_tuneInfo reloaded after a
 *  config.  Initial code added to support more than two sids.
 *
 *  Revision 1.17  2002/01/16 19:11:38  s_a_white
 *  Always release sid emulations now on a call to sidCreate until a better
 *  method is implemented for hardware emulations with locked sids.
 *
 *  Revision 1.16  2002/01/16 08:23:45  s_a_white
 *  Force a clock speed when unknown.
 *
 *  Revision 1.15  2002/01/15 19:12:54  s_a_white
 *  PSID2NG update.
 *
 *  Revision 1.14  2002/01/14 23:18:56  s_a_white
 *  Make sure xsid releases the old sid emulation when there are errors gaining
 *  a new one.
 *
 *  Revision 1.13  2001/12/20 20:15:23  s_a_white
 *  Fixed bad environment initialisation when switching to legacy modes.
 *
 *  Revision 1.12  2001/12/13 08:28:08  s_a_white
 *  Added namespace support to fix problems with xsidplay.
 *
 *  Revision 1.11  2001/12/11 19:24:15  s_a_white
 *  More GCC3 Fixes.
 *
 *  Revision 1.10  2001/11/23 22:59:59  s_a_white
 *  Added new header
 *
 *  Revision 1.9  2001/10/02 18:27:55  s_a_white
 *  Updated to use new sidbuilder classes.
 *
 *  Revision 1.8  2001/09/20 20:33:54  s_a_white
 *  sid2 now gets correctly set to nullsid for a bad create call.
 *
 *  Revision 1.7  2001/09/20 19:34:11  s_a_white
 *  Error checking added for the builder create calls.
 *
 *  Revision 1.6  2001/09/17 19:02:38  s_a_white
 *  Now uses fixed point maths for sample output and rtc.
 *
 *  Revision 1.5  2001/09/01 11:13:56  s_a_white
 *  Fixes sidplay1 environment modes.
 *
 *  Revision 1.4  2001/08/20 18:24:50  s_a_white
 *  tuneInfo in the info structure now correctly has the sid revision setup.
 *
 *  Revision 1.3  2001/08/12 18:22:45  s_a_white
 *  Fixed bug in Player::sidEmulation call.
 *
 *  Revision 1.2  2001/07/27 12:51:40  s_a_white
 *  Removed warning.
 *
 *  Revision 1.1  2001/07/27 12:12:23  s_a_white
 *  Initial release.
 *
 ***************************************************************************/

#include "sid2types.h"
#include "player.h"

#ifdef HAVE_EXCEPTIONS
#   include <new>
#endif

SIDPLAY2_NAMESPACE_START

// An instance of this structure is used to transport emulator settings
// to and from the interface class.

int Player::config (const sid2_config_t &cfg)
{
    if (m_running)
    {
        m_errorString = ERR_CONF_WHILST_ACTIVE;
        goto Player_configure_error;
    }

    // Check for base sampling frequency
    if (cfg.frequency < 4000)
    {   // Rev 1.6 (saw) - Added descriptive error
        m_errorString = ERR_UNSUPPORTED_FREQ;
        goto Player_configure_error;
    }

    // Check for legal precision
    switch (cfg.precision)
    {
    case 8:
    case 16:
    case 24:
        if (cfg.precision > SID2_MAX_PRECISION)
        {   // Rev 1.6 (saw) - Added descriptive error
            m_errorString = ERR_UNSUPPORTED_PRECISION;
            goto Player_configure_error;
        }
    break;

    default:
        // Rev 1.6 (saw) - Added descriptive error
        m_errorString = ERR_UNSUPPORTED_PRECISION;
        goto Player_configure_error;
    }
   
    // Only do these if we have a loaded tune
    if (m_tune)
    {
        float64_t cpuFreq;
        // Reset tune info
        m_tune->getInfo(m_tuneInfo);

        // External Setups
        if (sidCreate (cfg.sidEmulation, cfg.sidModel, cfg.sidDefault) < 0)
        {
            m_errorString      = cfg.sidEmulation->error ();
            m_cfg.sidEmulation = NULL;
            goto Player_configure_restore;
        }
        // Must be this order:
        // Determine clock speed
        cpuFreq = clockSpeed (cfg.clockSpeed, cfg.clockDefault,
                              cfg.clockForced);
        // Fixed point conversion 16.16
        m_samplePeriod = (event_clock_t) (cpuFreq /
                         (float64_t) cfg.frequency *
                         (1 << 16) * m_fastForwardFactor);
        // Setup fake cia
        sid6526.clock ((uint_least16_t)(cpuFreq / VIC_FREQ_PAL + 0.5));
        if (m_tuneInfo.songSpeed  == SIDTUNE_SPEED_CIA_1A ||
            m_tuneInfo.clockSpeed == SIDTUNE_CLOCK_NTSC)
        {
            sid6526.clock ((uint_least16_t)(cpuFreq / VIC_FREQ_NTSC + 0.5));
        }
        // Configure, setup and install C64 environment/events
        if (environment (cfg.environment) < 0)
            goto Player_configure_restore;
        // Start the real time clock event
        rtc.clock (cpuFreq);
    }
    sidSamples (cfg.sidSamples);

    // All parameters check out, so configure player.
    m_info.channels = 1;
    if (cfg.playback == sid2_stereo)
        m_info.channels++;

    m_sidAddress[0]  = m_tuneInfo.sidChipBase1;
    m_sidAddress[1]  = m_tuneInfo.sidChipBase2;

    // Only force dual sids if second wasn't detected
    if (!m_sidAddress[1] && cfg.forceDualSids)
        m_sidAddress[1] = 0xd500; // Assumed

    m_leftVolume  = cfg.leftVolume;
    m_rightVolume = cfg.rightVolume;

    if (cfg.playback != sid2_mono)
    {   // Try Spliting channels across 2 sids
        if (!m_sidAddress[1])
        {
            m_sidAddress[1] = m_sidAddress[0];

            // Mute Voices
            sid[0]->voice (0, 0, true);
            sid[0]->voice (2, 0, true);
            sid[1]->voice (1, 0, true);
            // 2 Voices scaled to unity from 4 (was !SID_VOL)
            //    m_leftVolume  *= 2;
            //    m_rightVolume *= 2;
            // 2 Voices scaled to unity from 3 (was SID_VOL)
            //        m_leftVolume  *= 3;
            //        m_leftVolume  /= 2;
            //    m_rightVolume *= 3;
            //    m_rightVolume /= 2;
        }

        if (cfg.playback == sid2_left)
            xsid.mute (true);
    }

    // Setup the audio side, depending on the audio hardware
    // and the information returned by sidtune
    switch (cfg.precision)
    {
    case 8:
        if (!m_sidAddress[1])
        {
            if (cfg.playback == sid2_stereo)
                output = &Player::stereoOut8MonoIn;
            else
                output = &Player::monoOut8MonoIn;
        }
        else
        {
            switch (cfg.playback)
            {
            case sid2_stereo: // Stereo Hardware
                output = &Player::stereoOut8StereoIn;
            break;

            case sid2_right: // Mono Hardware,
                output = &Player::monoOut8StereoRIn;
            break;

            case sid2_left:
                output = &Player::monoOut8MonoIn;
            break;

            case sid2_mono:
                output = &Player::monoOut8StereoIn;
            break;
            }
        }
    break;
            
    case 16:
        if (!m_sidAddress[1])
        {
            if (cfg.playback == sid2_stereo)
                output = &Player::stereoOut16MonoIn;
            else
                output = &Player::monoOut16MonoIn;
        }
        else
        {
            switch (cfg.playback)
            {
            case sid2_stereo: // Stereo Hardware
                output = &Player::stereoOut16StereoIn;
            break;

            case sid2_right: // Mono Hardware,
                output = &Player::monoOut16StereoRIn;
            break;

            case sid2_left:
                output = &Player::monoOut16MonoIn;
            break;

            case sid2_mono:
                output = &Player::monoOut16StereoIn;
            break;
            }
        }
    }

    // Update Configuration
    m_cfg = cfg;

    if (m_cfg.optimisation > SID2_MAX_OPTIMISATION)
        m_cfg.optimisation = SID2_MAX_OPTIMISATION;    
return 0;

Player_configure_restore:
    // Try restoring old configuration
    if (&m_cfg != &cfg)
        config (m_cfg);
Player_configure_error:
    return -1;
}

// Clock speed changes due to loading a new song
float64_t Player::clockSpeed (sid2_clock_t userClock, sid2_clock_t defaultClock,
                              bool forced)
{
    float64_t cpuFreq = CLOCK_FREQ_PAL;

    // Detect the Correct Song Speed
    // Determine song speed when unknown
    if (m_tuneInfo.clockSpeed == SIDTUNE_CLOCK_UNKNOWN)
    {
        switch (defaultClock)
        {
        case SID2_CLOCK_PAL:
            m_tuneInfo.clockSpeed = SIDTUNE_CLOCK_PAL;
            break;
        case SID2_CLOCK_NTSC:
            m_tuneInfo.clockSpeed = SIDTUNE_CLOCK_NTSC;
            break;
        case SID2_CLOCK_CORRECT:
            // No default so base it on emulation clock
            m_tuneInfo.clockSpeed = SIDTUNE_CLOCK_ANY;
        }
    }

    // Since song will run correct at any clock speed
    // set tune speed to the current emulation
    if (m_tuneInfo.clockSpeed == SIDTUNE_CLOCK_ANY)
    {
        if (userClock == SID2_CLOCK_CORRECT)
            userClock  = defaultClock;
            
        switch (userClock)
        {
        case SID2_CLOCK_NTSC:
            m_tuneInfo.clockSpeed = SIDTUNE_CLOCK_NTSC;
            break;
        case SID2_CLOCK_PAL:
        default:
            m_tuneInfo.clockSpeed = SIDTUNE_CLOCK_PAL;
            break;
        }
    }

    if (userClock == SID2_CLOCK_CORRECT)
    {
        switch (m_tuneInfo.clockSpeed)
        {
        case SIDTUNE_CLOCK_NTSC:
            userClock = SID2_CLOCK_NTSC;
            break;
        case SIDTUNE_CLOCK_PAL:
            userClock = SID2_CLOCK_PAL;
            break;
        }
    }

    if (forced)
    {
        m_tuneInfo.clockSpeed = SIDTUNE_CLOCK_PAL;
        if (userClock == SID2_CLOCK_NTSC)
            m_tuneInfo.clockSpeed = SIDTUNE_CLOCK_NTSC;
    }

    if (m_tuneInfo.clockSpeed == SIDTUNE_CLOCK_PAL)
        vic.chip (MOS6569);
    else // if (tuneInfo.clockSpeed == SIDTUNE_CLOCK_NTSC)
        vic.chip (MOS6567R8);

    if (userClock == SID2_CLOCK_PAL)
    {
        cpuFreq = CLOCK_FREQ_PAL;
        m_tuneInfo.speedString = TXT_PAL_VBI;
        if (m_tuneInfo.songSpeed == SIDTUNE_SPEED_CIA_1A)
            m_tuneInfo.speedString = TXT_PAL_CIA;
        else if (m_tuneInfo.clockSpeed == SIDTUNE_CLOCK_NTSC)
            m_tuneInfo.speedString = TXT_PAL_VBI_FIXED;
    }
    else // if (userClock == SID2_CLOCK_NTSC)
    {
        cpuFreq = CLOCK_FREQ_NTSC;
        m_tuneInfo.speedString = TXT_NTSC_VBI;
        if (m_tuneInfo.songSpeed == SIDTUNE_SPEED_CIA_1A)
            m_tuneInfo.speedString = TXT_NTSC_CIA;
        else if (m_tuneInfo.clockSpeed == SIDTUNE_CLOCK_PAL)
            m_tuneInfo.speedString = TXT_NTSC_VBI_FIXED;
    }
    return cpuFreq;
}

int Player::environment (sid2_env_t env)
{
    switch (m_tuneInfo.compatibility)
    {
    case SIDTUNE_COMPATIBILITY_R64:
        env = sid2_envR;
        break;
    case SIDTUNE_COMPATIBILITY_PSID:
        if (env == sid2_envR)
            env  = sid2_envBS;
    }

    // Environment already set?
    if (!(m_ram && (m_info.environment == env)))
    {   // Setup new player environment
        m_info.environment = env;
        if (m_ram)
        {
            if (m_ram == m_rom)
               delete [] m_ram;
            else
            {
               delete [] m_rom;
               delete [] m_ram;
            }
        }

#ifdef HAVE_EXCEPTIONS
        m_ram = new(std::nothrow) uint8_t[0x10000];
#else
        m_ram = new uint8_t[0x10000];
#endif

        // Setup the access functions to the environment
        // and the properties the memory has.
        if (m_info.environment == sid2_envPS)
        {   // Playsid has no roms and SID exists in ram space
            m_rom = m_ram;
            m_readMemByte     = &Player::readMemByte_player;
            m_writeMemByte    = &Player::writeMemByte_playsid;
            m_readMemDataByte = &Player::readMemByte_plain;
        }
        else
        {
#ifdef HAVE_EXCEPTIONS
            m_rom = new(std::nothrow) uint8_t[0x10000];
#else
            m_rom = new uint8_t[0x10000];
#endif

            switch (m_info.environment)
            {
            case sid2_envTP:
                m_readMemByte     = &Player::readMemByte_player;
                m_writeMemByte    = &Player::writeMemByte_sidplay;
                m_readMemDataByte = &Player::readMemByte_sidplaytp;
            break;

            //case sid2_envTR:
            case sid2_envBS:
                m_readMemByte     = &Player::readMemByte_player;
                m_writeMemByte    = &Player::writeMemByte_sidplay;
                m_readMemDataByte = &Player::readMemByte_sidplaybs;
            break;

            case sid2_envR:
            default: // <-- Just to please compiler
                m_readMemByte     = &Player::readMemByte_player;
                m_writeMemByte    = &Player::writeMemByte_sidplay;
                m_readMemDataByte = &Player::readMemByte_sidplaybs;
            break;
            }
        }
    }

    {   // Have to reload the song into memory as
        // everything has changed
        int ret;
        sid2_env_t old = m_info.environment;
        m_info.environment = env;
        ret = initialise ();
        m_info.environment = old;
        return ret;
    }
}

// Integrate SID emulation from the builder class into
// libsidplay2
int Player::sidCreate (sidbuilder *builder, sid2_model_t userModel,
                       sid2_model_t defaultModel)
{
    sid[0] = xsid.emulation ();
   /* @FIXME@ Removed as prevents SID
    * Model being updated
    ****************************************
    // If we are already using the emulation
    // then don't change
    if (builder == sid[0]->builder ())
    {
        sid[0] = &xsid;
        return 0;
    }
    ****************************************/

    // Make xsid forget it's emulation
    xsid.emulation (&nullsid);

    {   // Release old sids
        for (int i = 0; i < SID2_MAX_SIDS; i++)
        {
            sidbuilder *b;
            b = sid[i]->builder ();
            if (b)
                b->unlock (sid[i]);
        }
    }

    if (!builder)
    {   // No sid
        for (int i = 0; i < SID2_MAX_SIDS; i++)
            sid[i] = &nullsid;
    }
    else
    {   // Detect the Correct SID model
        // Determine model when unknown
        if (m_tuneInfo.sidModel == SIDTUNE_SIDMODEL_UNKNOWN)
        {
            switch (defaultModel)
            {
            case SID2_MOS6581:
                m_tuneInfo.sidModel = SIDTUNE_SIDMODEL_6581;
                break;
            case SID2_MOS8580:
                m_tuneInfo.sidModel = SIDTUNE_SIDMODEL_8580;
                break;
            case SID2_MODEL_CORRECT:
                // No default so base it on emulation clock
                m_tuneInfo.sidModel = SIDTUNE_SIDMODEL_ANY;
            }
        }

        // Since song will run correct on any sid model
        // set it to the current emulation
        if (m_tuneInfo.sidModel == SIDTUNE_SIDMODEL_ANY)
        {
            if (userModel == SID2_MODEL_CORRECT)
                userModel  = defaultModel;
            
            switch (userModel)
            {
            case SID2_MOS8580:
                m_tuneInfo.sidModel = SIDTUNE_SIDMODEL_8580;
                break;
            case SID2_MOS6581:
            default:
                m_tuneInfo.sidModel = SIDTUNE_SIDMODEL_6581;
                break;
            }
        }

        switch (userModel)
        {
        case SID2_MODEL_CORRECT:
            switch (m_tuneInfo.sidModel)
            {
            case SIDTUNE_SIDMODEL_8580:
                userModel = SID2_MOS8580;
                break;
            case SIDTUNE_SIDMODEL_6581:
                userModel = SID2_MOS6581;
                break;
            }
            break;
        // Fixup tune information if model is forced
        case SID2_MOS6581:
            m_tuneInfo.sidModel = SIDTUNE_SIDMODEL_6581;
            break;
        case SID2_MOS8580:
            m_tuneInfo.sidModel = SIDTUNE_SIDMODEL_8580;
            break;
        }

        for (int i = 0; i < SID2_MAX_SIDS; i++)
        {   // Get first SID emulation
            sid[i] = builder->lock (this, userModel);
            if (!sid[i])
                sid[i] = &nullsid;
            if ((i == 0) && !*builder)
                return -1;
        }
    }
    xsid.emulation (sid[0]);
    sid[0] = &xsid;
    return 0;
}

void Player::sidSamples (bool enable)
{
    int_least8_t gain = 0;
    xsid.sidSamples (enable);

    // Now balance voices
    if (!enable)
        gain = -25;

    xsid.gain (-100 - gain);
    sid[0] = xsid.emulation ();
    for (int i = 0; i < SID2_MAX_SIDS; i++)
        sid[i]->gain (gain);
    sid[0] = &xsid;
}

SIDPLAY2_NAMESPACE_STOP