]> 4ch.mooo.com Git - 16.git/blob - 16/adplug/adplug/src/bam.cpp
wwww~
[16.git] / 16 / adplug / adplug / src / bam.cpp
1 /*
2  * Adplug - Replayer for many OPL2/OPL3 audio file formats.
3  * Copyright (C) 1999 - 2003 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  * bam.cpp - Bob's Adlib Music Player, by Simon Peter <dn.tlp@gmx.net>
20  *
21  * NOTES:
22  * In my player, the loop counter is stored with the label. This can be
23  * dangerous for some situations (see below), but there shouldn't be any BAM
24  * files triggering this situation.
25  *
26  * From SourceForge Bug #476088:
27  * -----------------------------
28  * Using just one loop counter for each label, my player can't
29  * handle files that loop twice to the same label (if that's at
30  * all possible with BAM). Imagine the following situation:
31  * 
32  * ... [*] ---- [<- *] ---- [<- *] ...
33  *  ^   ^    ^     ^     ^     ^    ^
34  *  |   |    |     |     |     |    |
35  *  +---|----+-----|-----+-----|----+--- normal song data
36  *      +----------|-----------|-------- label 1
37  *                 +-----------+-------- loop points to label 1
38  * 
39  * both loop points loop to the same label. Storing the loop 
40  * count with the label would cause chaos with the counter, 
41  * when the player executes the inner jump.
42  * ------------------
43  * Not to worry. my reference implementation of BAM does not
44  * support the multiple loop situation you describe, and
45  * neither do any BAM-creation programs. Then both loops point
46  * to the same label, the inner loop's counter is just allowed
47  * to clobber the outer loop's counter. No stack is neccisary.
48  */
49
50 #include <string.h>
51 #include "bam.h"
52
53 const unsigned short CbamPlayer::freq[] = {172,182,193,205,217,230,243,258,274,
54 290,307,326,345,365,387,410,435,460,489,517,547,580,614,651,1369,1389,1411,
55 1434,1459,1484,1513,1541,1571,1604,1638,1675,2393,2413,2435,2458,2483,2508,
56 2537,2565,2595,2628,2662,2699,3417,3437,3459,3482,3507,3532,3561,3589,3619,
57 3652,3686,3723,4441,4461,4483,4506,4531,4556,4585,4613,4643,4676,4710,4747,
58 5465,5485,5507,5530,5555,5580,5609,5637,5667,5700,5734,5771,6489,6509,6531,
59 6554,6579,6604,6633,6661,6691,6724,6758,6795,7513,7533,7555,7578,7603,7628,
60 7657,7685,7715,7748,7782,7819,7858,7898,7942,7988,8037,8089,8143,8191,8191,
61 8191,8191,8191,8191,8191,8191,8191,8191,8191,8191};
62
63 CPlayer *CbamPlayer::factory(Copl *newopl)
64 {
65   return new CbamPlayer(newopl);
66 }
67
68 bool CbamPlayer::load(const std::string &filename, const CFileProvider &fp)
69 {
70         binistream *f = fp.open(filename); if(!f) return false;
71         char id[4];
72         unsigned int i;
73
74         size = fp.filesize(f) - 4;      // filesize minus header
75         f->readString(id, 4);
76         if(strncmp(id,"CBMF",4)) { fp.close(f); return false; }
77
78         song = new unsigned char [size];
79         for(i = 0; i < size; i++) song[i] = f->readInt(1);
80
81         fp.close(f);
82         rewind(0);
83         return true;
84 }
85
86 bool CbamPlayer::update()
87 {
88         unsigned char   cmd,c;
89
90         if(del) {
91                 del--;
92                 return !songend;
93         }
94
95         if(pos >= size) {       // EOF detection
96                 pos = 0;
97                 songend = true;
98         }
99
100         while(song[pos] < 128) {
101                 cmd = song[pos] & 240;
102                 c = song[pos] & 15;
103                 switch(cmd) {
104                 case 0:         // stop song
105                         pos = 0;
106                         songend = true;
107                         break;
108                 case 16:        // start note
109                         if(c < 9) {
110                                 opl->write(0xa0 + c, freq[song[++pos]] & 255);
111                                 opl->write(0xb0 + c, (freq[song[pos]] >> 8) + 32);
112                         } else
113                                 pos++;
114                         pos++;
115                         break;
116                 case 32:        // stop note
117                         if(c < 9)
118                                 opl->write(0xb0 + c, 0);
119                         pos++;
120                         break;
121                 case 48:        // define instrument
122                         if(c < 9) {
123                                 opl->write(0x20 + op_table[c],song[pos+1]);
124                                 opl->write(0x23 + op_table[c],song[pos+2]);
125                                 opl->write(0x40 + op_table[c],song[pos+3]);
126                                 opl->write(0x43 + op_table[c],song[pos+4]);
127                                 opl->write(0x60 + op_table[c],song[pos+5]);
128                                 opl->write(0x63 + op_table[c],song[pos+6]);
129                                 opl->write(0x80 + op_table[c],song[pos+7]);
130                                 opl->write(0x83 + op_table[c],song[pos+8]);
131                                 opl->write(0xe0 + op_table[c],song[pos+9]);
132                                 opl->write(0xe3 + op_table[c],song[pos+10]);
133                                 opl->write(0xc0 + c,song[pos+11]);
134                         }
135                         pos += 12;
136                         break;
137                 case 80:        // set label
138                         label[c].target = ++pos;
139                         label[c].defined = true;
140                         break;
141                 case 96:        // jump
142                         if(label[c].defined)
143                                 switch(song[pos+1]) {
144                                 case 254:       // infinite loop
145                                         if(label[c].defined) {
146                                                 pos = label[c].target;
147                                                 songend = true;
148                                                 break;
149                                         }
150                                         // fall through...
151                                 case 255:       // chorus
152                                         if(!chorus && label[c].defined) {
153                                                 chorus = true;
154                                                 gosub = pos + 2;
155                                                 pos = label[c].target;
156                                                 break;
157                                         }
158                                         // fall through...
159                                 case 0:         // end of loop
160                                         pos += 2;
161                                         break;
162                                 default:        // finite loop
163                                         if(!label[c].count) {   // loop elapsed
164                                                 label[c].count = 255;
165                                                 pos += 2;
166                                                 break;
167                                         }
168                                         if(label[c].count < 255)        // loop defined
169                                                 label[c].count--;
170                                         else                                            // loop undefined
171                                                 label[c].count = song[pos+1] - 1;
172                                         pos = label[c].target;
173                                         break;
174                                 }
175                         break;
176                 case 112:       // end of chorus
177                         if(chorus) {
178                                 pos = gosub;
179                                 chorus = false;
180                         } else
181                                 pos++;
182                         break;
183                 default:        // reserved command (skip)
184                         pos++;
185                         break;
186                 }
187         }
188         if(song[pos] >= 128) {          // wait
189                 del = song[pos] - 127;
190                 pos++;
191         }
192         return !songend;
193 }
194
195 void CbamPlayer::rewind(int subsong)
196 {
197         int i;
198
199         pos = 0; songend = false; del = 0; gosub = 0; chorus = false;
200         memset(label, 0, sizeof(label)); label[0].defined = true;
201         for(i = 0; i < 16; i++) label[i].count = 255;   // 255 = undefined
202         opl->init(); opl->write(1,32);
203 }