summaryrefslogtreecommitdiff
path: root/plugins/adplug/adplug/cmf.cpp
blob: 1f6e6bf54f01658779fbfb7624352b15f5436c4b (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
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
/*
 * Adplug - Replayer for many OPL2/OPL3 audio file formats.
 * Copyright (C) 1999 - 2009 Simon Peter, <dn.tlp@gmx.net>, et al.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * cmf.cpp - CMF player by Adam Nielsen <malvineous@shikadi.net>
 *   Subset of CMF reader in MOPL code (Malvineous' OPL player), no seeking etc.
 */

#include <stdint.h> // for uintxx_t
#include <cassert>
#include <math.h> // for pow() etc.
#include <string.h> // for memset
#include "debug.h"
#include "cmf.h"

// ------------------------------
// OPTIONS
// ------------------------------

// The official Creative Labs CMF player seems to ignore the note velocity
// (playing every note at the same volume), but you can uncomment this to
// allow the note velocity to affect the volume (as presumably the composer
// originally intended.)
//
//#define USE_VELOCITY
//
// The Xargon demo song is a good example of a song that uses note velocity.

// OPL register offsets
#define BASE_CHAR_MULT  0x20
#define BASE_SCAL_LEVL  0x40
#define BASE_ATCK_DCAY  0x60
#define BASE_SUST_RLSE  0x80
#define BASE_FNUM_L     0xA0
#define BASE_KEYON_FREQ 0xB0
#define BASE_RHYTHM     0xBD
#define BASE_WAVE       0xE0
#define BASE_FEED_CONN  0xC0

#define OPLBIT_KEYON    0x20 // Bit in BASE_KEYON_FREQ register for turning a note on

// Supplied with a channel, return the offset from a base OPL register for the
// Modulator cell (e.g. channel 4's modulator is at offset 0x09.  Since 0x60 is
// the attack/decay function, register 0x69 will thus set the attack/decay for
// channel 4's modulator.)  (channels go from 0 to 8 inclusive)
#define OPLOFFSET(channel)   (((channel) / 3) * 8 + ((channel) % 3))

// These 16 instruments are repeated to fill up the 128 available slots.  A CMF
// file can override none/some/all of the 128 slots with custom instruments,
// so any that aren't overridden are still available for use with these default
// patches.  The Word Rescue CMFs are good examples of songs that rely on these
// default patches.
uint8_t cDefaultPatches[] =
"\x01\x11\x4F\x00\xF1\xD2\x53\x74\x00\x00\x06"
"\x07\x12\x4F\x00\xF2\xF2\x60\x72\x00\x00\x08"
"\x31\xA1\x1C\x80\x51\x54\x03\x67\x00\x00\x0E"
"\x31\xA1\x1C\x80\x41\x92\x0B\x3B\x00\x00\x0E"
"\x31\x16\x87\x80\xA1\x7D\x11\x43\x00\x00\x08"
"\x30\xB1\xC8\x80\xD5\x61\x19\x1B\x00\x00\x0C"
"\xF1\x21\x01\x00\x97\xF1\x17\x18\x00\x00\x08"
"\x32\x16\x87\x80\xA1\x7D\x10\x33\x00\x00\x08"
"\x01\x12\x4F\x00\x71\x52\x53\x7C\x00\x00\x0A"
"\x02\x03\x8D\x00\xD7\xF5\x37\x18\x00\x00\x04"
"\x21\x21\xD1\x00\xA3\xA4\x46\x25\x00\x00\x0A"
"\x22\x22\x0F\x00\xF6\xF6\x95\x36\x00\x00\x0A"
"\xE1\xE1\x00\x00\x44\x54\x24\x34\x02\x02\x07"
"\xA5\xB1\xD2\x80\x81\xF1\x03\x05\x00\x00\x02"
"\x71\x22\xC5\x00\x6E\x8B\x17\x0E\x00\x00\x02"
"\x32\x21\x16\x80\x73\x75\x24\x57\x00\x00\x0E";


CPlayer *CcmfPlayer::factory(Copl *newopl)
{
  return new CcmfPlayer(newopl);
}

CcmfPlayer::CcmfPlayer(Copl *newopl) :
	CPlayer(newopl),
	data(NULL),
	pInstruments(NULL),
	bPercussive(false),
	iTranspose(0),
	iPrevCommand(0)
{
	assert(OPLOFFSET(1-1) == 0x00);
	assert(OPLOFFSET(5-1) == 0x09);
	assert(OPLOFFSET(9-1) == 0x12);
}

CcmfPlayer::~CcmfPlayer()
{
	if (this->data) delete[] data;
}

bool CcmfPlayer::load(const std::string &filename, const CFileProvider &fp)
{
  binistream *f = fp.open(filename); if(!f) return false;

	char cSig[4];
	f->readString(cSig, 4);
	if (
		(cSig[0] != 'C') ||
		(cSig[1] != 'T') ||
		(cSig[2] != 'M') ||
		(cSig[3] != 'F')
	) {
		// Not a CMF file
		fp.close(f);
		return false;
	}
	uint16_t iVer = f->readInt(2);
	if ((iVer != 0x0101) && (iVer != 0x0100)) {
		AdPlug_LogWrite("CMF file is not v1.0 or v1.1 (reports %d.%d)\n", iVer >> 8 , iVer & 0xFF);
		fp.close(f);
		return false;
	}

	this->cmfHeader.iInstrumentBlockOffset = f->readInt(2);
	this->cmfHeader.iMusicOffset = f->readInt(2);
	this->cmfHeader.iTicksPerQuarterNote = f->readInt(2);
	this->cmfHeader.iTicksPerSecond = f->readInt(2);
	this->cmfHeader.iTagOffsetTitle = f->readInt(2);
	this->cmfHeader.iTagOffsetComposer = f->readInt(2);
	this->cmfHeader.iTagOffsetRemarks = f->readInt(2);
	f->readString((char *)this->cmfHeader.iChannelsInUse, 16);
	if (iVer == 0x0100) {
		this->cmfHeader.iNumInstruments = f->readInt(1);
		this->cmfHeader.iTempo = 0;
	} else { // 0x0101
		this->cmfHeader.iNumInstruments = f->readInt(2);
		this->cmfHeader.iTempo = f->readInt(2);
	}

	// Load the instruments

	f->seek(this->cmfHeader.iInstrumentBlockOffset);
	this->pInstruments = new SBI[
		(this->cmfHeader.iNumInstruments < 128) ? 128 : this->cmfHeader.iNumInstruments
	];  // Always at least 128 available for use

	for (int i = 0; i < this->cmfHeader.iNumInstruments; i++) {
		this->pInstruments[i].op[0].iCharMult = f->readInt(1);
		this->pInstruments[i].op[1].iCharMult = f->readInt(1);
		this->pInstruments[i].op[0].iScalingOutput = f->readInt(1);
		this->pInstruments[i].op[1].iScalingOutput = f->readInt(1);
		this->pInstruments[i].op[0].iAttackDecay = f->readInt(1);
		this->pInstruments[i].op[1].iAttackDecay = f->readInt(1);
		this->pInstruments[i].op[0].iSustainRelease = f->readInt(1);
		this->pInstruments[i].op[1].iSustainRelease = f->readInt(1);
		this->pInstruments[i].op[0].iWaveSel = f->readInt(1);
		this->pInstruments[i].op[1].iWaveSel = f->readInt(1);
		this->pInstruments[i].iConnection = f->readInt(1);
		f->seek(5, binio::Add);  // skip over the padding bytes
	}

	// Set the rest of the instruments to the CMF defaults
	for (int i = this->cmfHeader.iNumInstruments; i < 128; i++) {
		this->pInstruments[i].op[0].iCharMult =       cDefaultPatches[(i % 16) * 11 + 0];
		this->pInstruments[i].op[1].iCharMult =       cDefaultPatches[(i % 16) * 11 + 1];
		this->pInstruments[i].op[0].iScalingOutput =  cDefaultPatches[(i % 16) * 11 + 2];
		this->pInstruments[i].op[1].iScalingOutput =  cDefaultPatches[(i % 16) * 11 + 3];
		this->pInstruments[i].op[0].iAttackDecay =    cDefaultPatches[(i % 16) * 11 + 4];
		this->pInstruments[i].op[1].iAttackDecay =    cDefaultPatches[(i % 16) * 11 + 5];
		this->pInstruments[i].op[0].iSustainRelease = cDefaultPatches[(i % 16) * 11 + 6];
		this->pInstruments[i].op[1].iSustainRelease = cDefaultPatches[(i % 16) * 11 + 7];
		this->pInstruments[i].op[0].iWaveSel =        cDefaultPatches[(i % 16) * 11 + 8];
		this->pInstruments[i].op[1].iWaveSel =        cDefaultPatches[(i % 16) * 11 + 9];
		this->pInstruments[i].iConnection =           cDefaultPatches[(i % 16) * 11 + 10];
	}

	if (this->cmfHeader.iTagOffsetTitle) {
		f->seek(this->cmfHeader.iTagOffsetTitle);
		this->strTitle = f->readString('\0');
	}
	if (this->cmfHeader.iTagOffsetComposer) {
		f->seek(this->cmfHeader.iTagOffsetComposer);
		this->strComposer = f->readString('\0');
	}
	if (this->cmfHeader.iTagOffsetRemarks) {
		f->seek(this->cmfHeader.iTagOffsetRemarks);
		this->strRemarks = f->readString('\0');
	}

	// Load the MIDI data into memory
  f->seek(this->cmfHeader.iMusicOffset);
  this->iSongLen = fp.filesize(f) - this->cmfHeader.iMusicOffset;
  this->data = new unsigned char[this->iSongLen];
  f->readString((char *)data, this->iSongLen);

  fp.close(f);
	rewind(0);

  return true;
}

bool CcmfPlayer::update()
{
	// This has to be here and not in getrefresh() for some reason.
	this->iDelayRemaining = 0;

	// Read in the next event
	while (!this->iDelayRemaining) {
		uint8_t iCommand = this->data[this->iPlayPointer++];
		if ((iCommand & 0x80) == 0) {
			// Running status, use previous command
			this->iPlayPointer--;
			iCommand = this->iPrevCommand;
		} else {
			this->iPrevCommand = iCommand;
		}
		uint8_t iChannel = iCommand & 0x0F;
		switch (iCommand & 0xF0) {
			case 0x80: { // Note off (two data bytes)
				uint8_t iNote = this->data[this->iPlayPointer++];
				uint8_t iVelocity = this->data[this->iPlayPointer++]; // release velocity
				this->cmfNoteOff(iChannel, iNote, iVelocity);
				break;
			}
			case 0x90: { // Note on (two data bytes)
				uint8_t iNote = this->data[this->iPlayPointer++];
				uint8_t iVelocity = this->data[this->iPlayPointer++]; // attack velocity
				if (iVelocity) {
					this->cmfNoteOn(iChannel, iNote, iVelocity);
				} else {
					// This is a note-off instead (velocity == 0)
					this->cmfNoteOff(iChannel, iNote, iVelocity); // 64 is the MIDI default note-off velocity
					break;
				}
				break;
			}
			case 0xA0: { // Polyphonic key pressure (two data bytes)
				uint8_t iNote = this->data[this->iPlayPointer++];
				uint8_t iPressure = this->data[this->iPlayPointer++];
				AdPlug_LogWrite("CMF: Key pressure not yet implemented! (wanted ch%d/note %d set to %d)\n", iChannel, iNote, iPressure);
				break;
			}
			case 0xB0: { // Controller (two data bytes)
				uint8_t iController = this->data[this->iPlayPointer++];
				uint8_t iValue = this->data[this->iPlayPointer++];
				this->MIDIcontroller(iChannel, iController, iValue);
				break;
			}
			case 0xC0: { // Instrument change (one data byte)
				uint8_t iNewInstrument = this->data[this->iPlayPointer++];
				this->chMIDI[iChannel].iPatch = iNewInstrument;
				AdPlug_LogWrite("CMF: Remembering MIDI channel %d now uses patch %d\n", iChannel, iNewInstrument);
				break;
			}
			case 0xD0: { // Channel pressure (one data byte)
				uint8_t iPressure = this->data[this->iPlayPointer++];
				AdPlug_LogWrite("CMF: Channel pressure not yet implemented! (wanted ch%d set to %d)\n", iChannel, iPressure);
				break;
			}
			case 0xE0: { // Pitch bend (two data bytes)
				uint8_t iLSB = this->data[this->iPlayPointer++];
				uint8_t iMSB = this->data[this->iPlayPointer++];
				uint16_t iValue = (iMSB << 7) | iLSB;
				// 8192 is middle/off, 0 is -2 semitones, 16384 is +2 semitones
				this->chMIDI[iChannel].iPitchbend = iValue;
				AdPlug_LogWrite("CMF: Channel %d pitchbent to %d (%+.2f)\n", iChannel + 1, iValue, (float)(iValue - 8192) / 8192);
				break;
			}
			case 0xF0: // System message (arbitrary data bytes)
				switch (iCommand) {
					case 0xF0: { // Sysex
						uint8_t iNextByte;
						AdPlug_LogWrite("Sysex message: ");
						do {
							iNextByte = this->data[this->iPlayPointer++];
							AdPlug_LogWrite("%02X", iNextByte);
						} while ((iNextByte & 0x80) == 0);
						AdPlug_LogWrite("\n");
						// This will have read in the terminating EOX (0xF7) message too
						break;
					}
 					case 0xF1: // MIDI Time Code Quarter Frame
						this->data[this->iPlayPointer++]; // message data (ignored)
 						break;
 					case 0xF2: // Song position pointer
						this->data[this->iPlayPointer++]; // message data (ignored)
						this->data[this->iPlayPointer++];
 						break;
 					case 0xF3: // Song select
						this->data[this->iPlayPointer++]; // message data (ignored)
 						AdPlug_LogWrite("CMF: MIDI Song Select is not implemented.\n");
						break;
					case 0xF6: // Tune request
						break;
					case 0xF7: // End of System Exclusive (EOX) - should never be read, should be absorbed by Sysex handling code
						break;

					// These messages are "real time", meaning they can be sent between
					// the bytes of other messages - but we're lazy and don't handle these
					// here (hopefully they're not necessary in a MIDI file, and even less
					// likely to occur in a CMF.)
					case 0xF8: // Timing clock (sent 24 times per quarter note, only when playing)
					case 0xFA: // Start
					case 0xFB: // Continue
					case 0xFE: // Active sensing (sent every 300ms or MIDI connection assumed lost)
						break;
					case 0xFC: // Stop
						AdPlug_LogWrite("CMF: Received Real Time Stop message (0xFC)\n");
						this->bSongEnd = true;
						this->iPlayPointer = 0; // for repeat in endless-play mode
						break;
					case 0xFF: { // System reset, used as meta-events in a MIDI file
						uint8_t iEvent = this->data[this->iPlayPointer++];
						switch (iEvent) {
							case 0x2F: // end of track
								AdPlug_LogWrite("CMF: End-of-track, stopping playback\n");
								this->bSongEnd = true;
								this->iPlayPointer = 0; // for repeat in endless-play mode
								break;
							default:
								AdPlug_LogWrite("CMF: Unknown MIDI meta-event 0xFF 0x%02X\n", iEvent);
								break;
						}
						break;
					}
					default:
						AdPlug_LogWrite("CMF: Unknown MIDI system command 0x%02X\n", iCommand);
						break;
				}
				break;
			default:
				AdPlug_LogWrite("CMF: Unknown MIDI command 0x%02X\n", iCommand);
				break;
		}

		if (this->iPlayPointer >= this->iSongLen) {
			this->bSongEnd = true;
			this->iPlayPointer = 0; // for repeat in endless-play mode
		}

		// Read in the number of ticks until the next event
		this->iDelayRemaining = this->readMIDINumber();
	}

	return !this->bSongEnd;
}

void CcmfPlayer::rewind(int subsong)
{
  this->opl->init();

	// Initialise

  // Enable use of WaveSel register on OPL3 (even though we're only an OPL2!)
  // Apparently this enables nine-channel mode?
	this->writeOPL(0x01, 0x20);

	// Disable OPL3 mode (can be left enabled by a previous non-CMF song)
	this->writeOPL(0x05, 0x00);

	// Really make sure CSM+SEL are off (again, Creative's player...)
	this->writeOPL(0x08, 0x00);

	// This freq setting is required for the hihat to sound correct at the start
	// of funky.cmf, even though it's for an unrelated channel.
	// If it's here however, it makes the hihat in Word Rescue's theme.cmf
	// sound really bad.
	// TODO: How do we figure out whether we need it or not???
	this->writeOPL(BASE_FNUM_L + 8, 514 & 0xFF);
	this->writeOPL(BASE_KEYON_FREQ + 8, (1 << 2) | (514 >> 8));

	// default freqs?
	this->writeOPL(BASE_FNUM_L + 7, 509 & 0xFF);
	this->writeOPL(BASE_KEYON_FREQ + 7, (2 << 2) | (509 >> 8));
	this->writeOPL(BASE_FNUM_L + 6, 432 & 0xFF);
	this->writeOPL(BASE_KEYON_FREQ + 6, (2 << 2) | (432 >> 8));

	// Amplify AM + VIB depth.  Creative's CMF player does this, and there
	// doesn't seem to be any way to stop it from doing so - except for the
	// non-standard controller 0x63 I added :-)
	this->writeOPL(0xBD, 0xC0);

	this->bSongEnd = false;
	this->iPlayPointer = 0;
	this->iPrevCommand = 0; // just in case

	// Read in the number of ticks until the first event
	this->iDelayRemaining = this->readMIDINumber();

  // Reset song state.  This used to be in the constructor, but the XMMS2
  // plugin sets the song length before starting playback.  AdPlug plays the
  // song in its entirety (with no synth) to determine the song length, which
  // results in the state variables below matching the end of the song.  When
  // the real OPL synth is activated for playback, it no longer matches the
  // state variables and the instruments are not set correctly!
	for (int i = 0; i < 9; i++) {
		this->chOPL[i].iNoteStart = 0; // no note playing atm
		this->chOPL[i].iMIDINote = -1;
		this->chOPL[i].iMIDIChannel = -1;
		this->chOPL[i].iMIDIPatch = -1;

		this->chMIDI[i].iPatch = -2;
		this->chMIDI[i].iPitchbend = 8192;
	}
	for (int i = 9; i < 16; i++) {
		this->chMIDI[i].iPatch = -2;
		this->chMIDI[i].iPitchbend = 8192;
	}

	memset(this->iCurrentRegs, 0, 256);

	return;
}

// Return value: 1 == 1 second, 2 == 0.5 seconds
float CcmfPlayer::getrefresh()
{
	if (this->iDelayRemaining) {
		return (float)this->cmfHeader.iTicksPerSecond / (float)this->iDelayRemaining;
	} else {
		// Delay-remaining is zero (e.g. start of song) so use a tiny delay
		return this->cmfHeader.iTicksPerSecond; // wait for one tick
	}
}

std::string CcmfPlayer::gettitle()
{
	return this->strTitle;
}
std::string CcmfPlayer::getauthor()
{
	return this->strComposer;
}
std::string CcmfPlayer::getdesc()
{
	return this->strRemarks;
}


//
// PROTECTED
//

// Read a variable-length integer from MIDI data
uint32_t CcmfPlayer::readMIDINumber()
{
	uint32_t iValue = 0;
	for (int i = 0; i < 4; i++) {
		uint8_t iNext = this->data[this->iPlayPointer++];
		iValue <<= 7;
		iValue |= (iNext & 0x7F); // ignore the MSB
		if ((iNext & 0x80) == 0) break; // last byte has the MSB unset
	}
	return iValue;
}

// iChannel: OPL channel (0-8)
// iOperator: 0 == Modulator, 1 == Carrier
//   Source - source operator to read from instrument definition
//   Dest - destination operator on OPL chip
// iInstrument: Index into this->pInstruments array of CMF instruments
void CcmfPlayer::writeInstrumentSettings(uint8_t iChannel, uint8_t iOperatorSource, uint8_t iOperatorDest, uint8_t iInstrument)
{
	assert(iChannel <= 8);

	uint8_t iOPLOffset = OPLOFFSET(iChannel);
	if (iOperatorDest) iOPLOffset += 3; // Carrier if iOperator == 1 (else Modulator)

	this->writeOPL(BASE_CHAR_MULT + iOPLOffset, this->pInstruments[iInstrument].op[iOperatorSource].iCharMult);
	this->writeOPL(BASE_SCAL_LEVL + iOPLOffset, this->pInstruments[iInstrument].op[iOperatorSource].iScalingOutput);
	this->writeOPL(BASE_ATCK_DCAY + iOPLOffset, this->pInstruments[iInstrument].op[iOperatorSource].iAttackDecay);
	this->writeOPL(BASE_SUST_RLSE + iOPLOffset, this->pInstruments[iInstrument].op[iOperatorSource].iSustainRelease);
	this->writeOPL(BASE_WAVE      + iOPLOffset, this->pInstruments[iInstrument].op[iOperatorSource].iWaveSel);

	// TODO: Check to see whether we should only be loading this for one or both operators
	this->writeOPL(BASE_FEED_CONN + iChannel, this->pInstruments[iInstrument].iConnection);
	return;
}

// Write a byte to the OPL "chip" and update the current record of register states
void CcmfPlayer::writeOPL(uint8_t iRegister, uint8_t iValue)
{
	this->opl->write(iRegister, iValue);
	this->iCurrentRegs[iRegister] = iValue;
	return;
}

void CcmfPlayer::cmfNoteOn(uint8_t iChannel, uint8_t iNote, uint8_t iVelocity)
{
	uint8_t iBlock = iNote / 12;
	if (iBlock > 1) iBlock--; // keep in the same range as the Creative player
	//if (iBlock > 7) iBlock = 7; // don't want to go out of range

	double d = pow(2, (
		(double)iNote + (
			(this->chMIDI[iChannel].iPitchbend - 8192) / 8192.0
		) + (
			this->iTranspose / 128
		) - 9) / 12.0 - (iBlock - 20))
		* 440.0 / 32.0 / 50000.0;
	uint16_t iOPLFNum = (uint16_t)(d+0.5);
	if (iOPLFNum > 1023) AdPlug_LogWrite("CMF: This note is out of range! (send this song to malvineous@shikadi.net!)\n");

	// See if we're playing a rhythm mode percussive instrument
	if ((iChannel > 10) && (this->bPercussive)) {
		uint8_t iPercChannel = this->getPercChannel(iChannel);

		// Will have to set every time (easier) than figuring out whether the mod
		// or car needs to be changed.
		//if (this->chOPL[iPercChannel].iMIDIPatch != this->chMIDI[iChannel].iPatch) {
			this->MIDIchangeInstrument(iPercChannel, iChannel, this->chMIDI[iChannel].iPatch);
		//}

		/*  Velocity calculations - TODO: Work out the proper formula

		iVelocity -> iLevel  (values generated by Creative's player)
		7f -> 00
		7c -> 00

		7b -> 09
		73 -> 0a
		6b -> 0b
		63 -> 0c
		5b -> 0d
		53 -> 0e
		4b -> 0f
		43 -> 10
		3b -> 11
		33 -> 13
		2b -> 15
		23 -> 19
		1b -> 1b
		13 -> 1d
		0b -> 1f
		03 -> 21

		02 -> 21
		00 -> N/A (note off)
		*/
		// Approximate formula, need to figure out more accurate one (my maths isn't so good...)
		int iLevel = 0x25 - sqrt(iVelocity * 16/*6*/);//(127 - iVelocity) * 0x20 / 127;
		if (iVelocity > 0x7b) iLevel = 0; // full volume
		if (iLevel < 0) iLevel = 0;
		if (iLevel > 0x3F) iLevel = 0x3F;
		//if (iVelocity < 0x40) iLevel = 0x10;

		int iOPLOffset = BASE_SCAL_LEVL + OPLOFFSET(iPercChannel);
		//if ((iChannel == 11) || (iChannel == 12) || (iChannel == 14)) {
		if (iChannel == 11) iOPLOffset += 3; // only do bassdrum carrier for volume control
		//iOPLOffset += 3; // carrier
		this->writeOPL(iOPLOffset, (this->iCurrentRegs[iOPLOffset] & ~0x3F) | iLevel);//(iVelocity * 0x3F / 127));
		//}
		// Bass drum (ch11) uses both operators
		//if (iChannel == 11) this->writeOPL(iOPLOffset + 3, (this->iCurrentRegs[iOPLOffset + 3] & ~0x3F) | iLevel);

/*		#ifdef USE_VELOCITY  // Official CMF player seems to ignore velocity levels
			uint16_t iLevel = 0x2F - (iVelocity * 0x2F / 127); // 0x2F should be 0x3F but it's too quiet then
			AdPlug_LogWrite("%02X + vel %d (lev %02X) == %02X\n", this->iCurrentRegs[iOPLOffset], iVelocity, iLevel, (this->iCurrentRegs[iOPLOffset] & ~0x3F) | iLevel);
			//this->writeOPL(iOPLOffset, (this->iCurrentRegs[iOPLOffset] & ~0x3F) | (0x3F - (iVelocity >> 1)));//(iVelocity * 0x3F / 127));
			this->writeOPL(iOPLOffset, (this->iCurrentRegs[iOPLOffset] & ~0x3F) | iLevel);//(iVelocity * 0x3F / 127));
		#endif*/

		// Apparently you can't set the frequency for the cymbal or hihat?
		// Vinyl requires you don't set it, Kiloblaster requires you do!
		this->writeOPL(BASE_FNUM_L + iPercChannel, iOPLFNum & 0xFF);
		this->writeOPL(BASE_KEYON_FREQ + iPercChannel, (iBlock << 2) | ((iOPLFNum >> 8) & 0x03));

		uint8_t iBit = 1 << (15 - iChannel);

		// Turn the perc instrument off if it's already playing (OPL can't do
		// polyphonic notes w/ percussion)
		if (this->iCurrentRegs[BASE_RHYTHM] & iBit) this->writeOPL(BASE_RHYTHM, this->iCurrentRegs[BASE_RHYTHM] & ~iBit);

		// I wonder whether we need to delay or anything here?

		// Turn the note on
		//if (iChannel == 15) {
		this->writeOPL(BASE_RHYTHM, this->iCurrentRegs[BASE_RHYTHM] | iBit);
		//AdPlug_LogWrite("CMF: Note %d on MIDI channel %d (mapped to OPL channel %d-1) - vel %02X, fnum %d/%d\n", iNote, iChannel, iPercChannel+1, iVelocity, iOPLFNum, iBlock);
		//}

		this->chOPL[iPercChannel].iNoteStart = ++this->iNoteCount;
		this->chOPL[iPercChannel].iMIDIChannel = iChannel;
		this->chOPL[iPercChannel].iMIDINote = iNote;

	} else { // Non rhythm-mode or a normal instrument channel

		// Figure out which OPL channel to play this note on
		int iOPLChannel = -1;
		int iNumChannels = this->bPercussive ? 6 : 9;
		for (int i = iNumChannels - 1; i >= 0; i--) {
			// If there's no note playing on this OPL channel, use that
			if (this->chOPL[i].iNoteStart == 0) {
				iOPLChannel = i;
				// See if this channel is already set to the instrument we want.
				if (this->chOPL[i].iMIDIPatch == this->chMIDI[iChannel].iPatch) {
					// It is, so stop searching
					break;
				} // else keep searching just in case there's a better match
			}
		}
		if (iOPLChannel == -1) {
			// All channels were in use, find the one with the longest note
			iOPLChannel = 0;
			int iEarliest = this->chOPL[0].iNoteStart;
			for (int i = 1; i < iNumChannels; i++) {
				if (this->chOPL[i].iNoteStart < iEarliest) {
					// Found a channel with a note being played for longer
					iOPLChannel = i;
					iEarliest = this->chOPL[i].iNoteStart;
				}
			}
			AdPlug_LogWrite("CMF: Too many polyphonic notes, cutting note on channel %d\n", iOPLChannel);
		}

		// Run through all the channels with negative notestart values - these
		// channels have had notes recently stop - and increment the counter
		// to slowly move the channel closer to being reused for a future note.
		//for (int i = 0; i < iNumChannels; i++) {
		//	if (this->chOPL[i].iNoteStart < 0) this->chOPL[i].iNoteStart++;
		//}

		// Now the new note should be played on iOPLChannel, but see if the instrument
		// is right first.
		if (this->chOPL[iOPLChannel].iMIDIPatch != this->chMIDI[iChannel].iPatch) {
			this->MIDIchangeInstrument(iOPLChannel, iChannel, this->chMIDI[iChannel].iPatch);
		}

		this->chOPL[iOPLChannel].iNoteStart = ++this->iNoteCount;
		this->chOPL[iOPLChannel].iMIDIChannel = iChannel;
		this->chOPL[iOPLChannel].iMIDINote = iNote;

		#ifdef USE_VELOCITY  // Official CMF player seems to ignore velocity levels
			// Adjust the channel volume to match the note velocity
			uint8_t iOPLOffset = BASE_SCAL_LEVL + OPLOFFSET(iChannel) + 3; // +3 == Carrier
			uint16_t iLevel = 0x2F - (iVelocity * 0x2F / 127); // 0x2F should be 0x3F but it's too quiet then
			this->writeOPL(iOPLOffset, (this->iCurrentRegs[iOPLOffset] & ~0x3F) | iLevel);
		#endif

		// Set the frequency and play the note
		this->writeOPL(BASE_FNUM_L + iOPLChannel, iOPLFNum & 0xFF);
		this->writeOPL(BASE_KEYON_FREQ + iOPLChannel, OPLBIT_KEYON | (iBlock << 2) | ((iOPLFNum & 0x300) >> 8));
	}
	return;
}

void CcmfPlayer::cmfNoteOff(uint8_t iChannel, uint8_t iNote, uint8_t iVelocity)
{
	if ((iChannel > 10) && (this->bPercussive)) {
		int iOPLChannel = this->getPercChannel(iChannel);
		if (this->chOPL[iOPLChannel].iMIDINote != iNote) return; // there's a different note playing now
		this->writeOPL(BASE_RHYTHM, this->iCurrentRegs[BASE_RHYTHM] & ~(1 << (15 - iChannel)));
		this->chOPL[iOPLChannel].iNoteStart = 0; // channel free
	} else { // Non rhythm-mode or a normal instrument channel
		int iOPLChannel = -1;
		int iNumChannels = this->bPercussive ? 6 : 9;
		for (int i = 0; i < iNumChannels; i++) {
			if (
				(this->chOPL[i].iMIDIChannel == iChannel) &&
				(this->chOPL[i].iMIDINote == iNote) &&
				(this->chOPL[i].iNoteStart != 0)
			) {
				// Found the note, switch it off
				this->chOPL[i].iNoteStart = 0;
				iOPLChannel = i;
				break;
			}
		}
		if (iOPLChannel == -1) return;

		this->writeOPL(BASE_KEYON_FREQ + iOPLChannel, this->iCurrentRegs[BASE_KEYON_FREQ + iOPLChannel] & ~OPLBIT_KEYON);
	}
	return;
}

uint8_t CcmfPlayer::getPercChannel(uint8_t iChannel)
{
	switch (iChannel) {
		case 11: return 7-1; // Bass drum
		case 12: return 8-1; // Snare drum
		case 13: return 9-1; // Tom tom
		case 14: return 9-1; // Top cymbal
		case 15: return 8-1; // Hihat
	}
	AdPlug_LogWrite("CMF ERR: Tried to get the percussion channel from MIDI channel %d - this shouldn't happen!\n", iChannel);
	return 0;
}


void CcmfPlayer::MIDIchangeInstrument(uint8_t iOPLChannel, uint8_t iMIDIChannel, uint8_t iNewInstrument)
{
	if ((iMIDIChannel > 10) && (this->bPercussive)) {
		switch (iMIDIChannel) {
			case 11: // Bass drum (operator 13+16 == channel 7 modulator+carrier)
				this->writeInstrumentSettings(7-1, 0, 0, iNewInstrument);
				this->writeInstrumentSettings(7-1, 1, 1, iNewInstrument);
				break;
			case 12: // Snare drum (operator 17 == channel 8 carrier)
			//case 15:
				this->writeInstrumentSettings(8-1, 0, 1, iNewInstrument);

				//
				//this->writeInstrumentSettings(8-1, 0, 0, iNewInstrument);
				break;
			case 13: // Tom tom (operator 15 == channel 9 modulator)
			//case 14:
				this->writeInstrumentSettings(9-1, 0, 0, iNewInstrument);

				//
				//this->writeInstrumentSettings(9-1, 0, 1, iNewInstrument);
				break;
			case 14: // Top cymbal (operator 18 == channel 9 carrier)
				this->writeInstrumentSettings(9-1, 0, 1, iNewInstrument);
				break;
			case 15: // Hi-hat (operator 14 == channel 8 modulator)
				this->writeInstrumentSettings(8-1, 0, 0, iNewInstrument);
				break;
			default:
				AdPlug_LogWrite("CMF: Invalid MIDI channel %d (not melodic and not percussive!)\n", iMIDIChannel + 1);
				break;
		}
		this->chOPL[iOPLChannel].iMIDIPatch = iNewInstrument;
	} else {
		// Standard nine OPL channels
		this->writeInstrumentSettings(iOPLChannel, 0, 0, iNewInstrument);
		this->writeInstrumentSettings(iOPLChannel, 1, 1, iNewInstrument);
		this->chOPL[iOPLChannel].iMIDIPatch = iNewInstrument;
	}
	return;
}

void CcmfPlayer::MIDIcontroller(uint8_t iChannel, uint8_t iController, uint8_t iValue)
{
	switch (iController) {
		case 0x63:
			// Custom extension to allow CMF files to switch the AM+VIB depth on and
			// off (officially both are on, and there's no way to switch them off.)
			// Controller values:
			//   0 == AM+VIB off
			//   1 == VIB on
			//   2 == AM on
			//   3 == AM+VIB on
			if (iValue) {
				this->writeOPL(BASE_RHYTHM, (this->iCurrentRegs[BASE_RHYTHM] & ~0xC0) | (iValue << 6)); // switch AM+VIB extension on
			} else {
				this->writeOPL(BASE_RHYTHM, this->iCurrentRegs[BASE_RHYTHM] & ~0xC0); // switch AM+VIB extension off
			}
			AdPlug_LogWrite("CMF: AM+VIB depth change - AM %s, VIB %s\n",
				(this->iCurrentRegs[BASE_RHYTHM] & 0x80) ? "on" : "off",
				(this->iCurrentRegs[BASE_RHYTHM] & 0x40) ? "on" : "off");
			break;
		case 0x66:
			AdPlug_LogWrite("CMF: Song set marker to 0x%02X\n", iValue);
			break;
		case 0x67:
			this->bPercussive = (iValue != 0);
			if (this->bPercussive) {
				this->writeOPL(BASE_RHYTHM, this->iCurrentRegs[BASE_RHYTHM] | 0x20); // switch rhythm-mode on
			} else {
				this->writeOPL(BASE_RHYTHM, this->iCurrentRegs[BASE_RHYTHM] & ~0x20); // switch rhythm-mode off
			}
			AdPlug_LogWrite("CMF: Percussive/rhythm mode %s\n", this->bPercussive ? "enabled" : "disabled");
			break;
		case 0x68:
			// TODO: Shouldn't this just affect the one channel, not the whole song?  -- have pitchbends for that
			this->iTranspose = iValue;
			AdPlug_LogWrite("CMF: Transposing all notes up by %d * 1/128ths of a semitone.\n", iValue);
			break;
		case 0x69:
			this->iTranspose = -iValue;
			AdPlug_LogWrite("CMF: Transposing all notes down by %d * 1/128ths of a semitone.\n", iValue);
			break;
		default:
			AdPlug_LogWrite("CMF: Unsupported MIDI controller 0x%02X, ignoring.\n", iController);
			break;
	}
	return;
}