1 /***************************************************************************
2     Interface to Ported Z80 Code.
3     Handles the interface between 68000 program code and Z80.
4 
5     Also abstracted here, so the more complex OSound class isn't exposed
6     to the main code directly
7 
8     Copyright Chris White.
9     See license.txt for more details.
10 ***************************************************************************/
11 
12 #include "engine/outrun.hpp"
13 #include "engine/audio/osound.hpp"
14 #include "engine/audio/osoundint.hpp"
15 
16 OSoundInt osoundint;
17 OSound osound;
18 
OSoundInt()19 OSoundInt::OSoundInt()
20 {
21     pcm_ram = new uint8_t[PCM_RAM_SIZE];
22     has_booted = false;
23 }
24 
~OSoundInt()25 OSoundInt::~OSoundInt()
26 {
27     delete[] pcm_ram;
28 }
29 
init()30 void OSoundInt::init()
31 {
32     if (pcm == NULL)
33         pcm = new SegaPCM(SOUND_CLOCK, &roms.pcm, pcm_ram, SegaPCM::BANK_512);
34 
35     if (ym == NULL)
36         ym = new YM2151(0.5f, SOUND_CLOCK);
37 
38     pcm->init(config.sound.rate, config.fps);
39     ym->init(config.sound.rate, config.fps);
40 
41     reset();
42 
43     // Clear PCM Chip RAM
44     for (uint16_t i = 0; i < PCM_RAM_SIZE; i++)
45         pcm_ram[i] = 0;
46 
47     for (uint8_t i = 0; i < 8; i++)
48         engine_data[i] = 0;
49 
50     osound.init(ym, pcm_ram);
51 }
52 
53 // Clear sound queue
54 // Source: 0x5086
reset()55 void OSoundInt::reset()
56 {
57     sound_counter = 0;
58     sound_head    = 0;
59     sound_tail    = 0;
60     sounds_queued = 0;
61 
62     audio_ticks = 0;
63 }
64 
tick()65 void OSoundInt::tick()
66 {
67     // The audio code is updated 125 times per second
68     audio_ticks += (125.0 / config.fps);
69 
70     // Ticks per frame will vary between 2 and 3 at 60fps.
71     const int max_ticks = (int) audio_ticks;
72 
73     for (int i = 0; i < max_ticks; i++)
74     {
75         play_queued_sound(); // Process audio commands from main program code
76         osound.tick();       // Tick Ported Z80 Audio Code
77     }
78 
79     audio_ticks -= max_ticks;
80 }
81 
82 // ----------------------------------------------------------------------------
83 // Sound Queuing Code
84 // ----------------------------------------------------------------------------
85 
86 // Play Queued Sounds & Send Engine Noise Commands to Z80
87 // Was called by horizontal interrupt routine
88 // Source: 0x564E
play_queued_sound()89 void OSoundInt::play_queued_sound()
90 {
91     if (!has_booted)
92     {
93         sound_head = 0;
94         sounds_queued = 0;
95         return;
96     }
97 
98     // Process the lot in one go.
99     for (int counter = 0; counter < 8; counter++)
100     {
101         // Process queued sound
102         if (counter == 0)
103         {
104             if (sounds_queued != 0)
105             {
106                 osound.command_input = queue[sound_head];
107                 sound_head = (sound_head + 1) & QUEUE_LENGTH;
108                 sounds_queued--;
109             }
110             else
111             {
112                 osound.command_input = sound::RESET;
113             }
114         }
115         // Process player engine sounds and passing traffic
116         else
117         {
118             osound.engine_data[counter] = engine_data[counter];
119         }
120     }
121 }
122 
123 // Queue a sound in service mode
124 // Used to trigger both sound effects and music
125 // Source: 0x56C6
queue_sound_service(uint8_t snd)126 void OSoundInt::queue_sound_service(uint8_t snd)
127 {
128     if (has_booted)
129         add_to_queue(snd);
130     else
131         queue_clear();
132 }
133 
134 // Queue a sound in-game
135 // Note: This version has an additional check, so that certain sounds aren't played depending on game mode
136 // Source: 0x56D4
queue_sound(uint8_t snd)137 void OSoundInt::queue_sound(uint8_t snd)
138 {
139     if (has_booted)
140     {
141         if (outrun.game_state == GS_ATTRACT)
142         {
143             // Return if we are not playing sound in attract mode
144             if (!config.sound.advertise && snd != sound::COIN_IN) return;
145 
146             // Do not play music in attract mode, even if attract sound enabled
147             if (snd == sound::MUSIC_BREEZE || snd == sound::MUSIC_MAGICAL ||
148                 snd == sound::MUSIC_SPLASH || snd == sound::MUSIC_LASTWAVE)
149                 return;
150         }
151         add_to_queue(snd);
152     }
153     else
154         queue_clear();
155 }
156 
add_to_queue(uint8_t snd)157 void OSoundInt::add_to_queue(uint8_t snd)
158 {
159     // Add sound to the tail end of the queue
160     queue[sound_tail] = snd;
161     sound_tail = (sound_tail + 1) & QUEUE_LENGTH;
162     sounds_queued++;
163 }
164 
queue_clear()165 void OSoundInt::queue_clear()
166 {
167     sound_tail = 0;
168     sounds_queued = 0;
169 }