1 
2 /*
3  * REminiscence - Flashback interpreter
4  * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
5  */
6 
7 #include "mixer.h"
8 #include "systemstub.h"
9 #include "util.h"
10 
Mixer(FileSystem * fs,SystemStub * stub)11 Mixer::Mixer(FileSystem *fs, SystemStub *stub)
12 	: _stub(stub), _musicType(MT_NONE), _cpc(this, fs), _mod(this, fs), _ogg(this, fs), _sfx(this) {
13 	_musicTrack = -1;
14 	_backgroundMusicType = MT_NONE;
15 }
16 
init()17 void Mixer::init() {
18 	memset(_channels, 0, sizeof(_channels));
19 	_premixHook = 0;
20 	_stub->startAudio(Mixer::mixCallback, this);
21 }
22 
free()23 void Mixer::free() {
24 	setPremixHook(0, 0);
25 	stopAll();
26 	_stub->stopAudio();
27 }
28 
setPremixHook(PremixHook premixHook,void * userData)29 void Mixer::setPremixHook(PremixHook premixHook, void *userData) {
30 	debug(DBG_SND, "Mixer::setPremixHook()");
31 	LockAudioStack las(_stub);
32 	_premixHook = premixHook;
33 	_premixHookData = userData;
34 }
35 
play(const uint8_t * data,uint32_t len,uint16_t freq,uint8_t volume)36 void Mixer::play(const uint8_t *data, uint32_t len, uint16_t freq, uint8_t volume) {
37 	debug(DBG_SND, "Mixer::play(%d, %d)", freq, volume);
38 	LockAudioStack las(_stub);
39 	MixerChannel *ch = 0;
40 	for (int i = 0; i < NUM_CHANNELS; ++i) {
41 		MixerChannel *cur = &_channels[i];
42 		if (cur->active) {
43 			if (cur->chunk.data == data) {
44 				cur->chunkPos = 0;
45 				cur->volume = volume;
46 				return;
47 			}
48 		} else {
49 			ch = cur;
50 			break;
51 		}
52 	}
53 	if (ch) {
54 		ch->active = true;
55 		ch->volume = volume;
56 		ch->chunk.data = data;
57 		ch->chunk.len = len;
58 		ch->chunkPos = 0;
59 		ch->chunkInc = (freq << FRAC_BITS) / _stub->getOutputSampleRate();
60 	}
61 }
62 
isPlaying(const uint8_t * data) const63 bool Mixer::isPlaying(const uint8_t *data) const {
64 	debug(DBG_SND, "Mixer::isPlaying");
65 	LockAudioStack las(_stub);
66 	for (int i = 0; i < NUM_CHANNELS; ++i) {
67 		const MixerChannel *ch = &_channels[i];
68 		if (ch->active && ch->chunk.data == data) {
69 			return true;
70 		}
71 	}
72 	return false;
73 }
74 
getSampleRate() const75 uint32_t Mixer::getSampleRate() const {
76 	return _stub->getOutputSampleRate();
77 }
78 
stopAll()79 void Mixer::stopAll() {
80 	debug(DBG_SND, "Mixer::stopAll()");
81 	LockAudioStack las(_stub);
82 	for (uint8_t i = 0; i < NUM_CHANNELS; ++i) {
83 		_channels[i].active = false;
84 	}
85 }
86 
isMusicSfx(int num)87 static bool isMusicSfx(int num) {
88 	return (num >= 68 && num <= 75);
89 }
90 
playMusic(int num)91 void Mixer::playMusic(int num) {
92 	debug(DBG_SND, "Mixer::playMusic(%d)", num);
93 	int trackNum = -1;
94 	if (num == 1) { // menu screen
95 		trackNum = 2;
96 	} else if (num > MUSIC_TRACK) {
97 		trackNum = num - MUSIC_TRACK;
98 	}
99 	if (trackNum != -1 && trackNum != _musicTrack) {
100 		if (_ogg.playTrack(trackNum)) {
101 			_backgroundMusicType = _musicType = MT_OGG;
102 			_musicTrack = trackNum;
103 			return;
104 		}
105 		if (_cpc.playTrack(trackNum)) {
106 			_backgroundMusicType = _musicType = MT_CPC;
107 			_musicTrack = trackNum;
108 			return;
109 		}
110 	}
111 	if ((_musicType == MT_OGG || _musicType == MT_CPC) && isMusicSfx(num)) { // do not play level action music with background music
112 		return;
113 	}
114 	if (isMusicSfx(num)) { // level action sequence
115 		_sfx.play(num);
116 		if (_sfx._playing) {
117 			_musicType = MT_SFX;
118 		}
119 	} else { // cutscene
120 		_mod.play(num);
121 		if (_mod._playing) {
122 			_musicType = MT_MOD;
123 		}
124 	}
125 }
126 
stopMusic()127 void Mixer::stopMusic() {
128 	debug(DBG_SND, "Mixer::stopMusic()");
129 	switch (_musicType) {
130 	case MT_NONE:
131 		break;
132 	case MT_MOD:
133 		_mod.stop();
134 		break;
135 	case MT_OGG:
136 		_ogg.pauseTrack();
137 		break;
138 	case MT_SFX:
139 		_sfx.stop();
140 		break;
141 	case MT_CPC:
142 		_cpc.pauseTrack();
143 		break;
144 	}
145 	_musicType = MT_NONE;
146 	if (_musicTrack != -1) {
147 		switch (_backgroundMusicType) {
148 		case MT_OGG:
149 			_ogg.resumeTrack();
150 			_musicType = MT_OGG;
151 			break;
152 		case MT_CPC:
153 			_cpc.resumeTrack();
154 			_musicType = MT_CPC;
155 			break;
156 		default:
157 			break;
158 		}
159 	}
160 }
161 
162 static const bool kUseNr = false;
163 
nr(int16_t * buf,int len)164 static void nr(int16_t *buf, int len) {
165 	static int prev = 0;
166 	for (int i = 0; i < len; ++i) {
167 		const int vnr = buf[i] >> 1;
168 		buf[i] = vnr + prev;
169 		prev = vnr;
170 	}
171 }
172 
mix(int16_t * out,int len)173 void Mixer::mix(int16_t *out, int len) {
174 	if (_premixHook) {
175 		if (!_premixHook(_premixHookData, out, len)) {
176 			_premixHook = 0;
177 			_premixHookData = 0;
178 		}
179 	}
180 	for (uint8_t i = 0; i < NUM_CHANNELS; ++i) {
181 		MixerChannel *ch = &_channels[i];
182 		if (ch->active) {
183 			for (int pos = 0; pos < len; ++pos) {
184 				if ((ch->chunkPos >> FRAC_BITS) >= (ch->chunk.len - 1)) {
185 					ch->active = false;
186 					break;
187 				}
188 				const int sample = ch->chunk.getPCM(ch->chunkPos >> FRAC_BITS) * ch->volume / Mixer::MAX_VOLUME;
189 				out[pos] = ADDC_S16(out[pos], S8_to_S16(sample));
190 				ch->chunkPos += ch->chunkInc;
191 			}
192 		}
193 	}
194 	if (kUseNr) {
195 		nr(out, len);
196 	}
197 }
198 
mixCallback(void * param,int16_t * buf,int len)199 void Mixer::mixCallback(void *param, int16_t *buf, int len) {
200 	((Mixer *)param)->mix(buf, len);
201 }
202