1 /*
2  * SoundFX Macs Opera CMF Player --  Copyright (c) 2017 Sebastian Kienzl <seb@knzl.de>
3  *
4  * Part of Adplug - Replayer for many OPL2/OPL3 audio file formats.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 /*
22  * Most songs in this format use rhythm mode.
23  * The emus don't support rhythm mode too well, "nuked" seems good.
24  * I didn't verify with real hardware, so I don't know how it's supposed to sound.
25  */
26 
27 #include <string.h>
28 #include <stddef.h>
29 #include "cmfmcsop.h"
30 #include "debug.h"
31 
factory(Copl * newopl)32 CPlayer* CcmfmacsoperaPlayer::factory(Copl* newopl)
33 {
34 	return new CcmfmacsoperaPlayer(newopl);
35 }
36 
37 static const int16_t fNumbers[] = { 0x157, 0x16b, 0x181, 0x198, 0x1b0, 0x1ca, 0x1e5, 0x202, 0x221, 0x242, 0x264, 0x288 };
38 
39 // Register offset for slot, e.g. 0x20 + slotRegisterOffsets[slotNr]
40 static const int8_t slotRegisterOffsets[] = {
41 	0,   1,  2,  3,  4,  5,
42 	8,   9, 10, 11, 12, 13,
43 	16, 17, 18, 19, 20, 21
44 };
45 
46 /*
47 Channel     Slot
48           Op1  Op2
49 ===================
50  0        0    3
51  1        1    4
52  2        2    5
53  3        6    9
54  4        7   10
55  5        8   11
56  6       12   15
57  7       13   16
58  8       14   17
59 
60  Rhythm Mode
61 -------------------
62  BD      12   15   <- like Channel 6 in "melody mode"
63  SD      16
64  TOM     14
65  TC      17
66  HH      13
67 */
68 
69 // Slots for Op1/Op2 for channel
70 static const struct {
71 	int8_t slotOp1;
72 	int8_t slotOp2;
73 }
74 channelSlots[] = { {0, 3}, {1, 4}, {2, 5}, {6, 9}, {7, 10}, {8, 11}, {12, 15}, {13, 16}, {14, 17}};
75 
76 /*
77 Note that
78  op_table[chan]     == slotRegisterOffets[channelSlots[chan].slotOp1]
79  op_table[chan] + 3 == slotRegisterOffets[channelSlots[chan].slotOp2]
80 However, the slot-numbers are needed for rhythm mode, thus the extra tables.
81 */
82 
83 // Rhythm mode mapping: Which slots for which channel (note that the BD uses 12/15 like in melody mode)
84 static const int8_t channelSlotsRhythm[] = { -1, -1, -1, -1, -1, -1, 12, 16, 14, 17, 13};
85 
86 enum {
87 	CHANNEL_RHY_BD  = 6,
88 	CHANNEL_RHY_SN  = 7,
89 	CHANNEL_RHY_TOM = 8,
90 	CHANNEL_RHY_TC  = 9,
91 	CHANNEL_RHY_HH  = 10
92 };
93 
CcmfmacsoperaPlayer(Copl * newopl)94 CcmfmacsoperaPlayer::CcmfmacsoperaPlayer(Copl* newopl): CPlayer(newopl)
95 {
96 }
97 
gettype()98 std::string CcmfmacsoperaPlayer::gettype() { return std::string("SoundFX Macs Opera CMF"); }
99 
100 // helper class to close a binistream when leaving context
101 class binistream_closer {
102 public:
binistream_closer(const CFileProvider & fp,binistream * strm)103 	binistream_closer(const CFileProvider& fp, binistream* strm): fp(fp), strm(strm) {}
~binistream_closer()104 	~binistream_closer() { if(strm) fp.close(strm); }
105 	const CFileProvider& fp;
106 	binistream* strm;
107 };
108 
load(const std::string & filename,const CFileProvider & fp)109 bool CcmfmacsoperaPlayer::load(const std::string& filename, const CFileProvider& fp)
110 {
111 	if (!fp.extension(filename, ".cmf"))
112 		return false;
113 
114 	binistream* f = fp.open(filename);
115 	if (!f)
116 		return false;
117 
118 	binistream_closer _closer(fp, f);
119 
120 	std::string signature = f->readString();
121 	if (signature != "A.H.")
122 		return false;
123 
124 	// the pattern order is 99 shorts
125 	nrOfOrders = -1;
126 	for (int i = 0; i < 99; ++i) {
127 		patternOrder[i] = f->readInt(2);
128 		// count number of orders, only assign once
129 		if (patternOrder[i] == 99 && nrOfOrders < 0)
130 			nrOfOrders = i;
131 	}
132 
133 	if (nrOfOrders == -1) // no end of song marker found, assume 99
134 		nrOfOrders = 99;
135 
136 	nrOfPatterns = f->readInt(2);
137 
138 	int speed = f->readInt(2);
139 	if (speed < 1 || speed > 3)
140 		return false;
141 
142 	// 1 => 18 rows/s, 2 => 9 rows/s, 3 => 4.5 rows/s
143 	// (With 18.2 the speed matches the speed with DOSBox, otherwise it's ~2 BPM too slow)
144 	speedRowsPerSec = 18.2f / (1 << (speed - 1));
145 
146 	rhythmMode = f->readInt(2) == 1;
147 	int nrOfInstruments = f->readInt(2);
148 
149 	if (!loadInstruments(f, nrOfInstruments))
150 		return false;
151 
152 	if (!loadPatterns(f))
153 		return false;
154 
155 	rewind(0);
156 
157 	return true;
158 }
159 
loadInstruments(binistream * f,int nrOfInstruments)160 bool CcmfmacsoperaPlayer::loadInstruments(binistream* f, int nrOfInstruments)
161 {
162 	if (nrOfInstruments > 0xff)
163 		return false;
164 
165 	instruments.resize(nrOfInstruments);
166 
167 	static const intptr_t loadOffsets[] = {
168 		offsetof(Instrument, op[0].ksl),
169 		offsetof(Instrument, op[0].multiple),
170 		offsetof(Instrument, feedback),
171 		offsetof(Instrument, op[0].attackRate),
172 		offsetof(Instrument, op[0].sustainLevel),
173 		offsetof(Instrument, op[0].egType),
174 		offsetof(Instrument, op[0].decayRate),
175 		offsetof(Instrument, op[0].releaseRate),
176 		offsetof(Instrument, op[0].totalLevel),
177 		offsetof(Instrument, op[0].ampMod),
178 		offsetof(Instrument, op[0].vib),
179 		offsetof(Instrument, op[0].ksr),
180 		offsetof(Instrument, connection),
181 
182 		offsetof(Instrument, op[1].ksl),
183 		offsetof(Instrument, op[1].multiple),
184 		-1,
185 		offsetof(Instrument, op[1].attackRate),
186 		offsetof(Instrument, op[1].sustainLevel),
187 		offsetof(Instrument, op[1].egType),
188 		offsetof(Instrument, op[1].decayRate),
189 		offsetof(Instrument, op[1].releaseRate),
190 		offsetof(Instrument, op[1].totalLevel),
191 		offsetof(Instrument, op[1].ampMod),
192 		offsetof(Instrument, op[1].vib),
193 		offsetof(Instrument, op[1].ksr),
194 		-1,
195 		offsetof(Instrument, op[0].waveSelect),
196 		offsetof(Instrument, op[1].waveSelect)
197 	};
198 
199 	for (int i = 0; i < nrOfInstruments; ++i) {
200 		for (int j = 0; j < sizeof(loadOffsets)/sizeof(loadOffsets[0]); ++j) {
201 			int v = f->readInt(2);
202 			if (loadOffsets[j] >= 0 ) {
203 				*(int16_t*)((char*)&instruments[i] + loadOffsets[j]) = v;
204 			}
205 		}
206 
207 		f->readString(instruments[i].name, 13);
208 		instruments[i].name[13] = 0;
209 	}
210 
211 	return !f->ateof();
212 }
213 
loadPatterns(binistream * f)214 bool CcmfmacsoperaPlayer::loadPatterns(binistream* f)
215 {
216 	if (nrOfPatterns > 0xff)
217 		return false;
218 
219 	patterns.resize(nrOfPatterns);
220 
221 	for (int i = 0; i < nrOfPatterns; ++i) {
222 		while (!f->eof()) {
223 			// 6 bytes per event, if the 1st byte is 0xff, end of pattern
224 			NoteEvent n;
225 
226 			n.row = f->readInt(1);
227 			if (n.row == 0xff)
228 				break;
229 
230 			uint8_t* d = &n.col;
231 			for (int j = 0; j < 5; ++j)
232 				*d++ = f->readInt(1);
233 
234 			n.instrument--;
235 			patterns[i].push_back(n);
236 		}
237 	}
238 
239 	return true;
240 }
241 
isValidChannel(int channelNr) const242 bool CcmfmacsoperaPlayer::isValidChannel(int channelNr) const
243 {
244 	return channelNr >= 0 && ((rhythmMode && channelNr < 11) || (!rhythmMode && channelNr < 9));
245 }
246 
isRhythmChannel(int channelNr) const247 bool CcmfmacsoperaPlayer::isRhythmChannel(int channelNr) const
248 {
249 	return rhythmMode && channelNr >= 6;
250 }
251 
getSlotRegister(int base,int slotNr)252 static inline int getSlotRegister(int base, int slotNr)
253 {
254 	return base + slotRegisterOffsets[slotNr];
255 }
256 
setSlot(int slotNr,const SlotSettings & settings)257 void CcmfmacsoperaPlayer::setSlot(int slotNr, const SlotSettings& settings)
258 {
259 	opl->write(getSlotRegister(0x20, slotNr), (settings.multiple & 0xf) | ((settings.ksr & 1) << 4) | ((settings.egType & 1) << 5) | ((settings.vib & 1) << 6) | ((settings.ampMod & 1) << 7));
260 	opl->write(getSlotRegister(0x60, slotNr), ((settings.attackRate & 0xf) << 4) | (settings.decayRate & 0xf));
261 	opl->write(getSlotRegister(0x80, slotNr), ((settings.sustainLevel & 0xf) << 4) | (settings.releaseRate & 0xf));
262 	opl->write(getSlotRegister(0xe0, slotNr), settings.waveSelect & 3);
263 }
264 
setInstrument(int channelNr,const Instrument & inst)265 bool CcmfmacsoperaPlayer::setInstrument(int channelNr, const Instrument& inst)
266 {
267 	if (!isValidChannel(channelNr))
268 		return false;
269 
270 	if (channelCurrentInstrument[channelNr] != &inst) {
271 		if (!isRhythmChannel(channelNr) || channelNr == CHANNEL_RHY_BD) {
272 			// normal 2-op channel (or BD in rhythm mode, which also has two slots)
273 			opl->write(0xc0 + channelNr, ((inst.feedback & 7) << 1) | (1 - (inst.connection & 1)));
274 			setSlot(channelSlots[channelNr].slotOp1, inst.op[0]);
275 			setSlot(channelSlots[channelNr].slotOp2, inst.op[1]);
276 		}
277 		else {
278 			// rhythm channel
279 			setSlot(channelSlotsRhythm[channelNr], inst.op[0]);
280 		}
281 
282 		channelCurrentInstrument[channelNr] = &inst;
283 	}
284 
285 	return true;
286 }
287 
calculateAttenuation(int attenuation,int volumeColumn)288 static int calculateAttenuation(int attenuation, int volumeColumn)
289 {
290 	if (attenuation  < 0  ) attenuation  = 0;
291 	if (attenuation  > 63 ) attenuation  = 63;
292 	if (volumeColumn < 0  ) volumeColumn = 0;
293 	if (volumeColumn > 127) volumeColumn = 127;
294 
295 	// Instruments have a configured attenuation.
296 	// The "rest" of the attenuation (max. 63, 6 bits in "total level") is scaled by volumeColumn.
297 	// (This seems right for most values I've tested. But with att=8/vol=115, we get 13 here while SOUNDFX gets 14.
298 	return attenuation + ((63 - attenuation) * (127 - volumeColumn)) / 127;
299 }
300 
keyOn(int channelNr)301 void CcmfmacsoperaPlayer::keyOn(int channelNr)
302 {
303 	if (!isValidChannel(channelNr))
304 		return;
305 
306 	if (!isRhythmChannel(channelNr)) {
307 		current0xBx[channelNr] |= (1 << 5);
308 		opl->write(0xb0 + channelNr, current0xBx[channelNr]);
309 	}
310 	else {
311 		// Channel 6 (BD) -> D4 .. Channel 10 (HH) -> D0
312 		current0xBD |= 1 << (10 - channelNr);
313 		opl->write(0xbd, current0xBD);
314 	}
315 }
316 
keyOff(int channelNr)317 void CcmfmacsoperaPlayer::keyOff(int channelNr)
318 {
319 	if (!isValidChannel(channelNr))
320 		return;
321 
322 	if (!isRhythmChannel(channelNr)) {
323 		current0xBx[channelNr] &= ~(1 << 5);
324 		opl->write(0xb0 + channelNr, current0xBx[channelNr]);
325 	}
326 	else {
327 		// Channel 6 (BD) -> D4 .. Channel 10 (HH) -> D0
328 		current0xBD &= ~(1 << (10 - channelNr));
329 		opl->write(0xbd, current0xBD);
330 	}
331 }
332 
setAxBx(int channelNr,int Ax,int Bx)333 void CcmfmacsoperaPlayer::setAxBx(int channelNr, int Ax, int Bx)
334 {
335 	if (channelNr < 0 || channelNr >= 8)
336 		return;
337 
338 	opl->write(0xa0 + channelNr, Ax);
339 	current0xBx[channelNr] = Bx;
340 	opl->write(0xb0 + channelNr, current0xBx[channelNr]);
341 }
342 
setNote(int channelNr,int note)343 bool CcmfmacsoperaPlayer::setNote(int channelNr, int note)
344 {
345 	if (!isValidChannel(channelNr))
346 		return false;
347 
348 	if (note < 23 || note >= 120)
349 		return false;
350 
351 	int fNumber = fNumbers[note % 12];
352 
353 	int Ax = fNumber & 0xff;
354 	int Bx = ((fNumber >> 8) & 3) | ((note / 12 - 2) << 2);
355 
356 	if (!isRhythmChannel(channelNr)) {
357 		setAxBx(channelNr, Ax, Bx);
358 	}
359 	else {
360 		// not entirely right, but this is what the original player seems to do
361 		if (channelNr == CHANNEL_RHY_BD)
362 			setAxBx(6, Ax, Bx);
363 		setAxBx(7, Ax, Bx);
364 		if (channelNr == CHANNEL_RHY_SN || channelNr == CHANNEL_RHY_TOM)
365 			setAxBx(8, Ax, Bx);
366 	}
367 
368 	return true;
369 }
370 
setVolume(int channelNr,int vol)371 void CcmfmacsoperaPlayer::setVolume(int channelNr, int vol)
372 {
373 	if (!isValidChannel(channelNr) || !channelCurrentInstrument[channelNr])
374 		return;
375 
376 	const Instrument& inst = *channelCurrentInstrument[channelNr];
377 
378 	if (!isRhythmChannel(channelNr) || channelNr == CHANNEL_RHY_BD) {
379 		// normal 2-op channel (or BD in rhythm mode, which also has two slots)
380 
381 		// Set volume on OP1: If OP1 modulates OP2 (inst.connection != 0), don't scale its volume by n.volume
382 		opl->write(
383 			getSlotRegister(0x40, channelSlots[channelNr].slotOp1),
384 			((inst.op[0].ksl & 3) << 6) |
385 			  (inst.connection == 0 ? calculateAttenuation(inst.op[0].totalLevel, vol) : (inst.op[0].totalLevel & 0x3f))
386 		);
387 
388 		// Set volume on OP2
389 		opl->write(
390 			getSlotRegister(0x40, channelSlots[channelNr].slotOp2),
391 			((inst.op[1].ksl & 3) << 6) | calculateAttenuation(inst.op[1].totalLevel, vol)
392 		);
393 	}
394 	else {
395 		// rhythm channel
396 		opl->write(
397 			getSlotRegister(0x40, channelSlotsRhythm[channelNr]),
398 			((inst.op[1].ksl & 3) << 6) | calculateAttenuation(inst.op[0].totalLevel, vol)
399 		);
400 	}
401 }
402 
advanceRow()403 bool CcmfmacsoperaPlayer::advanceRow()
404 {
405 	for (;;) {
406 		if (currentRow < 0 || ++currentRow >= 64) {
407 			// next pattern
408 			currentRow = 0;
409 			currentPatternIndex = 0;
410 
411 			do {
412 				currentOrderIndex++;
413 
414 				// check bounds
415 				if (currentOrderIndex < 0 || currentOrderIndex >= sizeof(patternOrder) / sizeof(patternOrder[0]))
416 					return false;
417 
418 				// end of song?
419 				if (patternOrder[currentOrderIndex] == 99)
420 					return false;
421 
422 			} while (patternOrder[currentOrderIndex] >= patterns.size()); // loop to skip invalid pattern references
423 
424 			AdPlug_LogWrite("order %d, pattern %d\n", currentOrderIndex, patternOrder[currentOrderIndex]);
425 		}
426 
427 		// check for pattern break
428 		const Pattern &p = patterns[patternOrder[currentOrderIndex]];
429 		if (currentPatternIndex < p.size() && p[currentPatternIndex].row == currentRow && p[currentPatternIndex].note == 1) {
430 			currentRow = -1;
431 		}
432 		else // no pattern break, done!
433 			break;
434 	}
435 
436 	return true;
437 }
438 
processNoteEvent(const CcmfmacsoperaPlayer::NoteEvent & n)439 void CcmfmacsoperaPlayer::processNoteEvent(const CcmfmacsoperaPlayer::NoteEvent &n)
440 {
441 	int channelNr = n.col;
442 
443 	if (!isValidChannel(channelNr))
444 		return;
445 
446 	keyOff(channelNr);
447 
448 	if (n.note == 4) // key off: done here
449 		return;
450 
451 	if (n.instrument >= 0 && n.instrument < instruments.size())
452 		setInstrument(channelNr, instruments[n.instrument]);
453 
454 	setVolume(channelNr, n.volume);
455 
456 	if (setNote(channelNr, n.note))
457 		keyOn(channelNr);
458 }
459 
update()460 bool CcmfmacsoperaPlayer::update()
461 {
462 	AdPlug_LogWrite( "%2d: ", currentRow);
463 
464 	const Pattern& p = patterns[patternOrder[currentOrderIndex]];
465 
466 	int currentCol = 0;
467 	for (; currentPatternIndex < p.size() && p[currentPatternIndex].row == currentRow; ++currentPatternIndex) {
468 		const NoteEvent& n = p[currentPatternIndex];
469 
470 		for (; currentCol < n.col; ++currentCol)
471 			AdPlug_LogWrite("             ");
472 		AdPlug_LogWrite( "%2d %2d %2x %2d  ", n.note, n.instrument, n.volume, n.pitch);
473 
474 		currentCol++;
475 		processNoteEvent(n);
476 	}
477 	AdPlug_LogWrite("\n");
478 
479 	bool stillPlaying = advanceRow();
480 
481 	if (!stillPlaying) {
482 		resetPlayer();
483 		// Once the song is done, keep on returning false.
484 		// TODO: returning "false" once should suffice, but it doesn't (at least with adplay-unix&SDL)
485 		// "songDone" is only reset in rewind().
486 		songDone = true;
487 	}
488 
489 	return !songDone;
490 }
491 
resetPlayer()492 void CcmfmacsoperaPlayer::resetPlayer()
493 {
494 	currentRow = -1;
495 	currentOrderIndex = -1;
496 	advanceRow();
497 }
498 
rewind(int subsong)499 void CcmfmacsoperaPlayer::rewind(int subsong)
500 {
501 	opl->init();
502 	opl->write(1, 1 << 5);  // enable wave select
503 
504 	// 0xbd:5 (RHY) enables "rhythm mode"
505 	current0xBD = rhythmMode ? (1 << 5) : 0;
506 	opl->write(0xbd, current0xBD);
507 
508 	memset(current0xBx, 0, sizeof(current0xBx));
509 	memset(channelCurrentInstrument, 0, sizeof(channelCurrentInstrument));
510 
511 	// This isn't right for all channels.
512 	// No songs I've seen rely on the undocumented "default instrument", so ...
513 	static const Instrument defaultInstrument = {
514 		{{0, 1, 15, 5, 0, 1, 0, 0, 0, 0, 0, 0},
515 		 {0, 1, 15, 7, 0, 2, 0, 0, 0, 0, 1, 0}},
516 		3, 0, {'D', 'e', 'f', 'a', 'u', 'l', 't', 0, 0, 0, 0, 0, 0, 0 }
517 	};
518 	for (int i = 0; i < 11; ++i)
519 		setInstrument(i, defaultInstrument);
520 
521 	// see comment in update() why this is needed
522 	songDone = false;
523 
524 	resetPlayer();
525 }
526