]> 4ch.mooo.com Git - 16.git/blob - 16/adplug/adplug/src/jbm.cpp
wwww~
[16.git] / 16 / adplug / adplug / src / jbm.cpp
1 /*
2  * Adplug - Replayer for many OPL2/OPL3 audio file formats.
3  * Copyright (C) 1999 - 2007 Simon Peter, <dn.tlp@gmx.net>, et al.
4  * 
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  * 
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  * 
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  * Johannes Bjerregaard's JBM Adlib Music Format player for AdPlug
20  * Written by Dennis Lindroos <lindroos@nls.fi>, February-March 2007
21  * - Designed and coded from scratch (only frequency-table taken from MUSIC.BIN)
22  * - The percussion mode is buggy (?) but i'm not good enough to find them
23  *   and honestly i think the melodic-mode tunes are much better ;)
24  *
25  * This version doesn't use the binstr.h functions (coded with custom func.)
26  * This is my first attempt on writing a musicplayer for AdPlug, and i'm not
27  * coding C++ very often.. 
28  *
29  * Released under the terms of the GNU General Public License.
30  */
31
32 #include "jbm.h"
33
34 static const unsigned short notetable[96] = {
35   0x0158, 0x016d, 0x0183, 0x019a, 0x01b2, 0x01cc, 0x01e7, 0x0204,
36   0x0223, 0x0244, 0x0266, 0x028b, 0x0558, 0x056d, 0x0583, 0x059a,
37   0x05b2, 0x05cc, 0x05e7, 0x0604, 0x0623, 0x0644, 0x0666, 0x068b,
38   0x0958, 0x096d, 0x0983, 0x099a, 0x09b2, 0x09cc, 0x09e7, 0x0a04,
39   0x0a23, 0x0a44, 0x0a66, 0x0a8b, 0x0d58, 0x0d6d, 0x0d83, 0x0d9a,
40   0x0db2, 0x0dcc, 0x0de7, 0x0e04, 0x0e23, 0x0e44, 0x0e66, 0x0e8b,
41   0x1158, 0x116d, 0x1183, 0x119a, 0x11b2, 0x11cc, 0x11e7, 0x1204,
42   0x1223, 0x1244, 0x1266, 0x128b, 0x1558, 0x156d, 0x1583, 0x159a,
43   0x15b2, 0x15cc, 0x15e7, 0x1604, 0x1623, 0x1644, 0x1666, 0x168b,
44   0x1958, 0x196d, 0x1983, 0x199a, 0x19b2, 0x19cc, 0x19e7, 0x1a04,
45   0x1a23, 0x1a44, 0x1a66, 0x1a8b, 0x1d58, 0x1d6d, 0x1d83, 0x1d9a,
46   0x1db2, 0x1dcc, 0x1de7, 0x1e04, 0x1e23, 0x1e44, 0x1e66, 0x1e8b
47 };
48
49 static const unsigned char percmx_tab[4] = { 0x14, 0x12, 0x15, 0x11 };
50 static const unsigned char perchn_tab[5] = { 6, 7, 8, 8, 7 };
51 static unsigned char percmaskoff[5] = { 0xef, 0xf7, 0xfb, 0xfd, 0xfe };
52 static unsigned char percmaskon[5] =  { 0x10, 0x08, 0x04, 0x02, 0x01 };
53
54 static inline unsigned short GET_WORD(unsigned char *b, int x)
55 {
56   return ((unsigned short)(b[x+1] << 8) | b[x]);
57 }
58
59 /*** public methods *************************************/
60
61 CPlayer *CjbmPlayer::factory(Copl *newopl)
62 {
63   return new CjbmPlayer(newopl);
64 }
65
66 bool CjbmPlayer::load(const std::string &filename, const CFileProvider &fp)
67 {
68   binistream    *f = fp.open(filename); if(!f) return false;
69   int           filelen = fp.filesize(f);
70   int           i;
71
72   if (!filelen || !fp.extension(filename, ".jbm")) goto loaderr;
73
74   // Allocate memory buffer m[] and read entire file into it
75
76   m = new unsigned char[filelen];
77   if (f->readString((char *)m, filelen) != filelen) goto loaderr;
78
79   fp.close(f);
80
81   // The known .jbm files always seem to start with the number 0x0002
82
83   if (GET_WORD(m, 0) != 0x0002)
84     return false;
85
86   // Song tempo
87
88   i = GET_WORD(m, 2);
89   timer = 1193810.0 / (i ? i : 0xffff);
90
91   seqtable = GET_WORD(m, 4);
92   instable = GET_WORD(m, 6);
93
94   // The flags word has atleast 1 bit, the Adlib's rhythm mode, but
95   // currently we don't support that :(
96
97   flags = GET_WORD(m, 8);
98
99   // Instrument datas are directly addressed with m[] 
100
101   inscount = (filelen - instable) >> 4;
102
103   // Voice' and sequence pointers
104
105   seqcount = 0xffff;
106   for (i = 0; i < 11; i++) {
107     voice[i].trkpos = voice[i].trkstart = GET_WORD(m, 10 + (i<<1));
108     if (voice[i].trkpos && voice[i].trkpos < seqcount)
109       seqcount = voice[i].trkpos;
110   }
111   seqcount = (seqcount - seqtable) >> 1;
112   sequences = new unsigned short[seqcount];
113   for (i = 0; i < seqcount; i++) 
114     sequences[i] = GET_WORD(m, seqtable + (i<<1));
115
116   rewind(0);
117   return true;
118  loaderr:
119   fp.close(f);
120   return false;
121 }
122
123 bool CjbmPlayer::update()
124 {
125   short c, spos, frq;
126
127   for (c = 0; c < 11; c++) {
128     if (!voice[c].trkpos)               // Unused channel
129       continue;
130
131     if (--voice[c].delay)
132       continue;
133
134     // Turn current note/percussion off
135
136     if (voice[c].note&0x7f)
137       opl_noteonoff(c, &voice[c], 0);
138
139     // Process events until we have a note
140
141     spos = voice[c].seqpos;
142     while(!voice[c].delay) {
143       switch(m[spos]) {
144       case 0xFD:        // Set Instrument
145         voice[c].instr = m[spos+1];
146         set_opl_instrument(c, &voice[c]);
147         spos+=2;
148         break;
149       case 0xFF:        // End of Sequence
150         voice[c].seqno = m[++voice[c].trkpos];
151         if (voice[c].seqno == 0xff) {
152           voice[c].trkpos = voice[c].trkstart;
153           voice[c].seqno = m[voice[c].trkpos];
154           //voicemask &= 0x7ff-(1<<c);
155           voicemask &= ~(1<<c);
156         }
157         spos = voice[c].seqpos = sequences[voice[c].seqno];
158         break;
159       default:  // Note Event
160         if ((m[spos] & 127) > 95)
161           return 0;
162
163         voice[c].note = m[spos];
164         voice[c].vol = m[spos+1];
165         voice[c].delay =
166           (m[spos+2] + (m[spos+3]<<8)) + 1;
167
168         frq = notetable[voice[c].note&127];
169         voice[c].frq[0] = (unsigned char)frq;
170         voice[c].frq[1] = frq >> 8;
171         spos+=4;
172       }
173     }
174     voice[c].seqpos = spos;
175
176     // Write new volume to the carrier operator, or percussion
177
178     if (flags&1 && c > 6)
179       opl->write(0x40 + percmx_tab[c-7], voice[c].vol ^ 0x3f);
180     else
181       opl->write(0x43 + op_table[c], voice[c].vol ^ 0x3f);
182
183     // Write new frequencies and Gate bit
184
185     opl_noteonoff(c, &voice[c], !(voice[c].note & 0x80));
186   }
187   return (voicemask);
188 }
189
190 void CjbmPlayer::rewind(int subsong)
191 {
192   int c;
193
194   voicemask = 0;
195
196   for (c = 0; c < 11; c++) {
197     voice[c].trkpos = voice[c].trkstart;
198
199     if (!voice[c].trkpos) continue;
200
201     voicemask |= (1<<c);
202
203     voice[c].seqno = m[voice[c].trkpos];
204     voice[c].seqpos = sequences[voice[c].seqno];
205
206     voice[c].note = 0;
207     voice[c].delay = 1;
208   }
209
210   opl->init();
211   opl->write(0x01, 32);
212
213   // Set rhythm mode if flags bit #0 is set
214   // AM and Vibrato are full depths (taken from DosBox RAW output)
215   bdreg = 0xC0 | (flags&1)<<5;
216
217   opl->write(0xbd, bdreg);
218
219 #if 0
220   if (flags&1) {
221     voice[7].frq[0] = 0x58; voice[7].frq[1] = 0x09; // XXX
222     voice[8].frq[0] = 0x04; voice[8].frq[1] = 0x0a; // XXX
223     opl_noteonoff(7, &voice[7], 0);
224     opl_noteonoff(8, &voice[8], 0);
225   }
226 #endif
227
228   return;
229 }
230
231 /*** private methods ************************************/
232
233 void CjbmPlayer::opl_noteonoff(int channel, JBMVoice *v, bool state)
234 {
235   if (flags&1 && channel > 5) {
236     // Percussion
237     opl->write(0xa0 + perchn_tab[channel-6], voice[channel].frq[0]);
238     opl->write(0xb0 + perchn_tab[channel-6], voice[channel].frq[1]);
239     opl->write(0xbd,
240                state ? bdreg | percmaskon[channel-6] :
241                bdreg & percmaskoff[channel-6]);
242   } else {
243     // Melodic mode or Rhythm mode melodic channels
244     opl->write(0xa0 + channel, voice[channel].frq[0]);
245     opl->write(0xb0 + channel,
246                state ? voice[channel].frq[1] | 0x20 :
247                voice[channel].frq[1] & 0x1f);
248   }
249   return;
250 }
251
252
253 void CjbmPlayer::set_opl_instrument(int channel, JBMVoice *v)
254 {
255   short i = instable + (v->instr << 4);
256
257   // Sanity check on instr number - or we'll be reading outside m[] !
258
259   if (v->instr >= inscount)
260     return;
261
262   // For rhythm mode, multiplexed drums. I don't care about waveforms!
263   if ((flags&1) & (channel > 6)) {
264     opl->write(0x20 + percmx_tab[channel-7], m[i+0]);
265     opl->write(0x40 + percmx_tab[channel-7], m[i+1] ^ 0x3f);
266     opl->write(0x60 + percmx_tab[channel-7], m[i+2]);
267     opl->write(0x80 + percmx_tab[channel-7], m[i+3]);
268
269     opl->write(0xc0 + perchn_tab[channel-6], m[i+8]&15);
270     return;
271   }
272
273   // AM/VIB/EG/KSR/FRQMUL, KSL/OUTPUT, ADSR for 1st operator
274   opl->write(0x20 + op_table[channel], m[i+0]);
275   opl->write(0x40 + op_table[channel], m[i+1] ^ 0x3f);
276   opl->write(0x60 + op_table[channel], m[i+2]);
277   opl->write(0x80 + op_table[channel], m[i+3]);
278
279   // AM/VIB/EG/KSR/FRQMUL, KSL/OUTPUT, ADSR for 2nd operator
280   opl->write(0x23 + op_table[channel], m[i+4]);
281   opl->write(0x43 + op_table[channel], m[i+5] ^ 0x3f);
282   opl->write(0x63 + op_table[channel], m[i+6]);
283   opl->write(0x83 + op_table[channel], m[i+7]);
284
285   // WAVEFORM for operators
286   opl->write(0xe0 + op_table[channel], (m[i+8]>>4)&3);
287   opl->write(0xe3 + op_table[channel], (m[i+8]>>6)&3);
288
289   // FEEDBACK/FM mode
290   opl->write(0xc0 + channel, m[i+8]&15);
291         
292   return;
293 }