A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/* MikMod sound library
2 (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS
3 for complete list.
4
5 This library is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of
8 the License, or (at your option) any later version.
9
10 This program 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
13 GNU Library General Public License for more details.
14
15 You should have received a copy of the GNU Library 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
18 02111-1307, USA.
19*/
20
21/*==============================================================================
22
23 $Id$
24
25 All routines dealing with the manipulation of UNITRK streams
26
27==============================================================================*/
28
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33#include "mikmod_internals.h"
34
35#include <string.h>
36
37/* Unibuffer chunk size */
38#define BUFPAGE 128
39
40const UWORD unioperands[UNI_LAST] = {
41 0, /* not used */
42 1, /* UNI_NOTE */
43 1, /* UNI_INSTRUMENT */
44 1, /* UNI_PTEFFECT0 */
45 1, /* UNI_PTEFFECT1 */
46 1, /* UNI_PTEFFECT2 */
47 1, /* UNI_PTEFFECT3 */
48 1, /* UNI_PTEFFECT4 */
49 1, /* UNI_PTEFFECT5 */
50 1, /* UNI_PTEFFECT6 */
51 1, /* UNI_PTEFFECT7 */
52 1, /* UNI_PTEFFECT8 */
53 1, /* UNI_PTEFFECT9 */
54 1, /* UNI_PTEFFECTA */
55 1, /* UNI_PTEFFECTB */
56 1, /* UNI_PTEFFECTC */
57 1, /* UNI_PTEFFECTD */
58 1, /* UNI_PTEFFECTE */
59 1, /* UNI_PTEFFECTF */
60 1, /* UNI_S3MEFFECTA */
61 1, /* UNI_S3MEFFECTD */
62 1, /* UNI_S3MEFFECTE */
63 1, /* UNI_S3MEFFECTF */
64 1, /* UNI_S3MEFFECTI */
65 1, /* UNI_S3MEFFECTQ */
66 1, /* UNI_S3MEFFECTR */
67 1, /* UNI_S3MEFFECTT */
68 1, /* UNI_S3MEFFECTU */
69 0, /* UNI_KEYOFF */
70 1, /* UNI_KEYFADE */
71 2, /* UNI_VOLEFFECTS */
72 1, /* UNI_XMEFFECT4 */
73 1, /* UNI_XMEFFECT6 */
74 1, /* UNI_XMEFFECTA */
75 1, /* UNI_XMEFFECTE1 */
76 1, /* UNI_XMEFFECTE2 */
77 1, /* UNI_XMEFFECTEA */
78 1, /* UNI_XMEFFECTEB */
79 1, /* UNI_XMEFFECTG */
80 1, /* UNI_XMEFFECTH */
81 1, /* UNI_XMEFFECTL */
82 1, /* UNI_XMEFFECTP */
83 1, /* UNI_XMEFFECTX1 */
84 1, /* UNI_XMEFFECTX2 */
85 1, /* UNI_ITEFFECTG */
86 1, /* UNI_ITEFFECTH */
87 1, /* UNI_ITEFFECTI */
88 1, /* UNI_ITEFFECTM */
89 1, /* UNI_ITEFFECTN */
90 1, /* UNI_ITEFFECTP */
91 1, /* UNI_ITEFFECTT */
92 1, /* UNI_ITEFFECTU */
93 1, /* UNI_ITEFFECTW */
94 1, /* UNI_ITEFFECTY */
95 2, /* UNI_ITEFFECTZ */
96 1, /* UNI_ITEFFECTS0 */
97 2, /* UNI_ULTEFFECT9 */
98 2, /* UNI_MEDSPEED */
99 0, /* UNI_MEDEFFECTF1 */
100 0, /* UNI_MEDEFFECTF2 */
101 0, /* UNI_MEDEFFECTF3 */
102 2, /* UNI_OKTARP */
103 0, /* not used */
104 1, /* UNI_S3MEFFECTH */
105 1, /* UNI_ITEFFECTH_OLD */
106 1, /* UNI_ITEFFECTU_OLD */
107 1, /* UNI_GDMEFFECT4 */
108 1, /* UNI_GDMEFFECT7 */
109 1, /* UNI_GDMEFFECT14 */
110 2, /* UNI_MEDEFFECT_VIB */
111 0, /* UNI_MEDEFFECT_FD */
112 1, /* UNI_MEDEFFECT_16 */
113 1, /* UNI_MEDEFFECT_18 */
114 1, /* UNI_MEDEFFECT_1E */
115 1, /* UNI_MEDEFFECT_1F */
116 1, /* UNI_FAREFFECT1 */
117 1, /* UNI_FAREFFECT2 */
118 1, /* UNI_FAREFFECT3 */
119 1, /* UNI_FAREFFECT4 */
120 1, /* UNI_FAREFFECT6 */
121 1, /* UNI_FAREFFECTD */
122 1, /* UNI_FAREFFECTE */
123 1, /* UNI_FAREFFECTF */
124};
125
126/* Sparse description of the internal module format
127 ------------------------------------------------
128
129 A UNITRK stream is an array of bytes representing a single track of a pattern.
130It's made up of 'repeat/length' bytes, opcodes and operands (sort of a assembly
131language):
132
133rrrlllll
134[REP/LEN][OPCODE][OPERAND][OPCODE][OPERAND] [REP/LEN][OPCODE][OPERAND]..
135^ ^ ^
136|-------ROWS 0 - 0+REP of a track---------| |-------ROWS xx - xx+REP of a track...
137
138 The rep/len byte contains the number of bytes in the current row, _including_
139the length byte itself (So the LENGTH byte of row 0 in the previous example
140would have a value of 5). This makes it easy to search through a stream for a
141particular row. A track is concluded by a 0-value length byte.
142
143 The upper 3 bits of the rep/len byte contain the number of times -1 this row
144is repeated for this track. (so a value of 7 means this row is repeated 8 times)
145
146 Opcodes can range from 1 to 255 but currently only opcodes 1 to 62 are being
147used. Each opcode can have a different number of operands. You can find the
148number of operands to a particular opcode by using the opcode as an index into
149the 'unioperands' table.
150
151*/
152
153/*========== Reading routines */
154
155static UBYTE *rowstart; /* startadress of a row */
156static UBYTE *rowend; /* endaddress of a row (exclusive) */
157static UBYTE *rowpc; /* current unimod(tm) programcounter */
158
159static UBYTE lastbyte; /* for UniSkipOpcode() */
160
161void UniSetRow(UBYTE* t)
162{
163 rowstart = t;
164 rowpc = rowstart;
165 rowend = t?rowstart+(*(rowpc++)&0x1f):t;
166}
167
168UBYTE UniGetByte(void)
169{
170 return lastbyte = (rowpc<rowend)?*(rowpc++):0;
171}
172
173UWORD UniGetWord(void)
174{
175 return ((UWORD)UniGetByte()<<8)|UniGetByte();
176}
177
178void UniSkipOpcode(void)
179{
180 if (lastbyte < UNI_LAST) {
181 UWORD t = unioperands[lastbyte];
182
183 while (t--)
184 UniGetByte();
185 }
186}
187
188/* Finds the address of row number 'row' in the UniMod(tm) stream 't' returns
189 NULL if the row can't be found. */
190UBYTE *UniFindRow(UBYTE* t,UWORD row)
191{
192 UBYTE c,l;
193
194 if(t)
195 while(1) {
196 c = *t; /* get rep/len byte */
197 if(!c) return NULL; /* zero ? -> end of track.. */
198 l = (c>>5)+1; /* extract repeat value */
199 if(l>row) break; /* reached wanted row? -> return pointer */
200 row -= l; /* haven't reached row yet.. update row */
201 t += c&0x1f; /* point t to the next row */
202 }
203 return t;
204}
205
206/*========== Writing routines */
207
208static UBYTE *unibuf; /* pointer to the temporary unitrk buffer */
209static UWORD unimax; /* buffer size */
210
211static UWORD unipc; /* buffer cursor */
212static UWORD unitt; /* current row index */
213static UWORD lastp; /* previous row index */
214
215/* Resets index-pointers to create a new track. */
216void UniReset(void)
217{
218 unitt = 0; /* reset index to rep/len byte */
219 unipc = 1; /* first opcode will be written to index 1 */
220 lastp = 0; /* no previous row yet */
221 unibuf[0] = 0; /* clear rep/len byte */
222}
223
224/* Expands the buffer */
225static int UniExpand(int wanted)
226{
227 if ((unipc+wanted)>=unimax) {
228 UBYTE *newbuf;
229
230 /* Expand the buffer by BUFPAGE bytes */
231 newbuf=(UBYTE*)MikMod_realloc(unibuf,(unimax+BUFPAGE)*sizeof(UBYTE));
232
233 /* Check if MikMod_realloc succeeded */
234 if(newbuf) {
235 unibuf = newbuf;
236 unimax+=BUFPAGE;
237 return 1;
238 } else
239 return 0;
240 }
241 return 1;
242}
243
244/* Appends one byte of data to the current row of a track. */
245void UniWriteByte(UBYTE data)
246{
247 if (UniExpand(1))
248 /* write byte to current position and update */
249 unibuf[unipc++]=data;
250}
251
252void UniWriteWord(UWORD data)
253{
254 if (UniExpand(2)) {
255 unibuf[unipc++]=data>>8;
256 unibuf[unipc++]=data&0xff;
257 }
258}
259
260static int MyCmp(const UBYTE* a,const UBYTE* b,UWORD l)
261{
262 UWORD t;
263
264 for(t=0;t<l;t++)
265 if(*(a++)!=*(b++)) return 0;
266 return 1;
267}
268
269/* Closes the current row of a unitrk stream (updates the rep/len byte) and sets
270 pointers to start a new row. */
271void UniNewline(void)
272{
273 UWORD n,l,len;
274
275 n = (unibuf[lastp]>>5)+1; /* repeat of previous row */
276 l = (unibuf[lastp]&0x1f); /* length of previous row */
277
278 len = unipc-unitt; /* length of current row */
279
280 /* Now, check if the previous and the current row are identical.. when they
281 are, just increase the repeat field of the previous row */
282 if(n<8 && len==l && MyCmp(&unibuf[lastp+1],&unibuf[unitt+1],len-1)) {
283 unibuf[lastp]+=0x20;
284 unipc = unitt+1;
285 } else {
286 if (UniExpand(len)) {
287 /* current and previous row aren't equal... update the pointers */
288 unibuf[unitt] = len;
289 lastp = unitt;
290 unitt = unipc++;
291 }
292 }
293}
294
295/* Terminates the current unitrk stream and returns a pointer to a copy of the
296 stream. */
297UBYTE* UniDup(void)
298{
299 void *d;
300
301 if (!UniExpand(unipc-unitt)) return NULL;
302 unibuf[unitt] = 0;
303
304 if(!(d=MikMod_malloc(unipc))) return NULL;
305 memcpy(d,unibuf,unipc);
306
307 return (UBYTE *)d;
308}
309
310int UniInit(void)
311{
312 unimax = BUFPAGE;
313
314 if(!(unibuf=(UBYTE*)MikMod_malloc(unimax*sizeof(UBYTE)))) return 0;
315 return 1;
316}
317
318void UniCleanup(void)
319{
320 MikMod_free(unibuf);
321 unibuf = NULL;
322}
323
324/* ex:set ts=4: */