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, 2002 Miodrag Vallat and others - see file
3 AUTHORS 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 DSIK internal format (DSM) module loader
26
27==============================================================================*/
28
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33#ifdef HAVE_UNISTD_H
34#include <unistd.h>
35#endif
36
37#include <stdio.h>
38#ifdef HAVE_MEMORY_H
39#include <memory.h>
40#endif
41#include <string.h>
42
43#include "mikmod_internals.h"
44
45#ifdef SUNOS
46extern int fprintf(FILE *, const char *, ...);
47#endif
48
49/*========== Module structure */
50
51#define DSM_MAXCHAN (16)
52#define DSM_MAXORDERS (128)
53
54typedef struct DSMSONG {
55 CHAR songname[28];
56 UWORD version;
57 UWORD flags;
58 ULONG reserved2;
59 UWORD numord;
60 UWORD numsmp;
61 UWORD numpat;
62 UWORD numtrk;
63 UBYTE globalvol;
64 UBYTE mastervol;
65 UBYTE speed;
66 UBYTE bpm;
67 UBYTE panpos[DSM_MAXCHAN];
68 UBYTE orders[DSM_MAXORDERS];
69} DSMSONG;
70
71typedef struct DSMINST {
72 CHAR filename[13];
73 UWORD flags;
74 UBYTE volume;
75 ULONG length;
76 ULONG loopstart;
77 ULONG loopend;
78 ULONG reserved1;
79 UWORD c2spd;
80 UWORD period;
81 CHAR samplename[28];
82} DSMINST;
83
84typedef struct DSMNOTE {
85 UBYTE note,ins,vol,cmd,inf;
86} DSMNOTE;
87
88#define DSM_SURROUND (0xa4)
89
90/*========== Loader variables */
91
92static const CHAR* SONGID="SONG";
93static const CHAR* INSTID="INST";
94static const CHAR* PATTID="PATT";
95
96static UBYTE blockid[4];
97static ULONG blockln;
98static ULONG blocklp;
99static DSMSONG* mh=NULL;
100static DSMNOTE* dsmbuf=NULL;
101
102static CHAR DSM_Version[]="DSIK DSM-format";
103
104static const unsigned char DSMSIG[4+4]={'R','I','F','F','D','S','M','F'};
105
106/*========== Loader code */
107
108static int DSM_Test(void)
109{
110 UBYTE id[12];
111
112 if(!_mm_read_UBYTES(id,12,modreader)) return 0;
113 if(!memcmp(id,DSMSIG,4) && !memcmp(id+8,DSMSIG+4,4)) return 1;
114
115 return 0;
116}
117
118static int DSM_Init(void)
119{
120 if(!(dsmbuf=(DSMNOTE *)MikMod_malloc(DSM_MAXCHAN*64*sizeof(DSMNOTE)))) return 0;
121 if(!(mh=(DSMSONG *)MikMod_calloc(1,sizeof(DSMSONG)))) return 0;
122 return 1;
123}
124
125static void DSM_Cleanup(void)
126{
127 MikMod_free(dsmbuf);
128 MikMod_free(mh);
129 dsmbuf = NULL;
130 mh = NULL;
131}
132
133static int GetBlockHeader(void)
134{
135 /* make sure we're at the right position for reading the
136 next riff block, no matter how many bytes read */
137 _mm_fseek(modreader, blocklp+blockln, SEEK_SET);
138
139 while(1) {
140 _mm_read_UBYTES(blockid,4,modreader);
141 blockln=_mm_read_I_ULONG(modreader);
142 if(_mm_eof(modreader)) {
143 _mm_errno = MMERR_LOADING_HEADER;
144 return 0;
145 }
146
147 if(memcmp(blockid,SONGID,4) && memcmp(blockid,INSTID,4) &&
148 memcmp(blockid,PATTID,4)) {
149#ifdef MIKMOD_DEBUG
150 fprintf(stderr,"\rDSM: Skipping unknown block type %4.4s\n",blockid);
151#endif
152 _mm_fseek(modreader, blockln, SEEK_CUR);
153 } else
154 break;
155 }
156
157 blocklp = _mm_ftell(modreader);
158
159 return 1;
160}
161
162static int DSM_ReadPattern(void)
163{
164 int flag,row=0;
165 SWORD length;
166 DSMNOTE *n;
167
168 /* clear pattern data */
169 memset(dsmbuf,255,DSM_MAXCHAN*64*sizeof(DSMNOTE));
170 length=_mm_read_I_SWORD(modreader);
171
172 while(row<64) {
173 flag=_mm_read_UBYTE(modreader);
174 if((_mm_eof(modreader))||(--length<0)) {
175 _mm_errno = MMERR_LOADING_PATTERN;
176 return 0;
177 }
178
179 if(flag) {
180 n=&dsmbuf[((flag&0xf)*64)+row];
181 if(flag&0x80) n->note=_mm_read_UBYTE(modreader);
182 if(flag&0x40) n->ins=_mm_read_UBYTE(modreader);
183 if(flag&0x20) n->vol=_mm_read_UBYTE(modreader);
184 if(flag&0x10) {
185 n->cmd=_mm_read_UBYTE(modreader);
186 n->inf=_mm_read_UBYTE(modreader);
187 }
188 } else
189 row++;
190 }
191
192 return 1;
193}
194
195static UBYTE *DSM_ConvertTrack(DSMNOTE *tr)
196{
197 int t;
198 UBYTE note,ins,vol,cmd,inf;
199
200 UniReset();
201 for(t=0;t<64;t++) {
202 note=tr[t].note;
203 ins=tr[t].ins;
204 vol=tr[t].vol;
205 cmd=tr[t].cmd;
206 inf=tr[t].inf;
207
208 if(ins!=0 && ins!=255) UniInstrument(ins-1);
209 if(note!=255) UniNote(note-1); /* normal note */
210 if(vol<65) UniPTEffect(0xc,vol);
211
212 if(cmd!=255) {
213 if(cmd==0x8) {
214 if(inf==DSM_SURROUND)
215 UniEffect(UNI_ITEFFECTS0,0x91);
216 else
217 if(inf<=0x80) {
218 inf=(inf<0x80)?inf<<1:255;
219 UniPTEffect(cmd,inf);
220 }
221 } else
222 if(cmd==0xb) {
223 if(inf<=0x7f) UniPTEffect(cmd,inf);
224 } else {
225 /* Convert pattern jump from Dec to Hex */
226 if(cmd == 0xd)
227 inf = (((inf&0xf0)>>4)*10)+(inf&0xf);
228 UniPTEffect(cmd,inf);
229 }
230 }
231 UniNewline();
232 }
233 return UniDup();
234}
235
236static int DSM_Load(int curious)
237{
238 int t;
239 DSMINST s;
240 SAMPLE *q;
241 int cursmp=0,curpat=0,track=0;
242 (void)curious;
243
244 blocklp=0;
245 blockln=12;
246
247 if(!GetBlockHeader()) return 0;
248 if(memcmp(blockid,SONGID,4)) {
249 _mm_errno = MMERR_LOADING_HEADER;
250 return 0;
251 }
252
253 _mm_read_UBYTES(mh->songname,28,modreader);
254 mh->version=_mm_read_I_UWORD(modreader);
255 mh->flags=_mm_read_I_UWORD(modreader);
256 mh->reserved2=_mm_read_I_ULONG(modreader);
257 mh->numord=_mm_read_I_UWORD(modreader);
258 mh->numsmp=_mm_read_I_UWORD(modreader);
259 mh->numpat=_mm_read_I_UWORD(modreader);
260 mh->numtrk=_mm_read_I_UWORD(modreader);
261 mh->globalvol=_mm_read_UBYTE(modreader);
262 mh->mastervol=_mm_read_UBYTE(modreader);
263 mh->speed=_mm_read_UBYTE(modreader);
264 mh->bpm=_mm_read_UBYTE(modreader);
265 _mm_read_UBYTES(mh->panpos,DSM_MAXCHAN,modreader);
266 _mm_read_UBYTES(mh->orders,DSM_MAXORDERS,modreader);
267
268 /* set module variables */
269 of.initspeed=mh->speed;
270 of.inittempo=mh->bpm;
271 of.modtype=MikMod_strdup(DSM_Version);
272 of.numchn=mh->numtrk;
273 of.numpat=mh->numpat;
274 of.numtrk=of.numchn*of.numpat;
275 of.songname=DupStr(mh->songname,28,1); /* make a cstr of songname */
276 of.reppos=0;
277 of.flags |= UF_PANNING;
278 /* XXX whenever possible, we should try to determine the original format.
279 Here we assume it was S3M-style wrt bpmlimit... */
280 of.bpmlimit = 32;
281
282 for(t=0;t<DSM_MAXCHAN;t++)
283 of.panning[t]=mh->panpos[t]==DSM_SURROUND?PAN_SURROUND:
284 mh->panpos[t]<0x80?(mh->panpos[t]<<1):255;
285
286 if(!AllocPositions(mh->numord)) return 0;
287 of.numpos=0;
288 for(t=0;t<mh->numord;t++) {
289 int order=mh->orders[t];
290 if(order==255) order=LAST_PATTERN;
291 else if (of.positions[t]>of.numpat) { /* SANITIY CHECK */
292 /* fprintf(stderr,"positions[%d]=%d > numpat=%d\n",t,of.positions[t],of.numpat);*/
293 _mm_errno = MMERR_LOADING_HEADER;
294 return 0;
295 }
296 of.positions[of.numpos]=order;
297 if(mh->orders[t]<254) of.numpos++;
298 }
299
300 of.numins=of.numsmp=mh->numsmp;
301
302 if(!AllocSamples()) return 0;
303 if(!AllocTracks()) return 0;
304 if(!AllocPatterns()) return 0;
305
306 while(cursmp<of.numins||curpat<of.numpat) {
307 if(!GetBlockHeader()) return 0;
308 if(!memcmp(blockid,INSTID,4) && cursmp<of.numins) {
309 q=&of.samples[cursmp];
310
311 /* try to read sample info */
312 _mm_read_UBYTES(s.filename,13,modreader);
313 s.flags=_mm_read_I_UWORD(modreader);
314 s.volume=_mm_read_UBYTE(modreader);
315 s.length=_mm_read_I_ULONG(modreader);
316 s.loopstart=_mm_read_I_ULONG(modreader);
317 s.loopend=_mm_read_I_ULONG(modreader);
318 s.reserved1=_mm_read_I_ULONG(modreader);
319 s.c2spd=_mm_read_I_UWORD(modreader);
320 s.period=_mm_read_I_UWORD(modreader);
321 _mm_read_UBYTES(s.samplename,28,modreader);
322
323 q->samplename=DupStr(s.samplename,28,1);
324 q->seekpos=_mm_ftell(modreader);
325 q->speed=s.c2spd;
326 q->length=s.length;
327 q->loopstart=s.loopstart;
328 q->loopend=s.loopend;
329 q->volume=s.volume;
330
331 if(s.flags&1) q->flags|=SF_LOOP;
332 if(s.flags&2) q->flags|=SF_SIGNED;
333 /* (s.flags&4) means packed sample,
334 but did they really exist in dsm ?*/
335 cursmp++;
336 } else
337 if(!memcmp(blockid,PATTID,4) && curpat<of.numpat) {
338 DSM_ReadPattern();
339 for(t=0;t<of.numchn;t++)
340 if(!(of.tracks[track++]=DSM_ConvertTrack(&dsmbuf[t*64]))) return 0;
341 curpat++;
342 }
343 }
344
345 return 1;
346}
347
348static CHAR *DSM_LoadTitle(void)
349{
350 CHAR s[28];
351
352 _mm_fseek(modreader,12,SEEK_SET);
353 if(!_mm_read_UBYTES(s,28,modreader)) return NULL;
354
355 return(DupStr(s,28,1));
356}
357
358/*========== Loader information */
359
360MIKMODAPI MLOADER load_dsm={
361 NULL,
362 "DSM",
363 "DSM (DSIK internal format)",
364 DSM_Init,
365 DSM_Test,
366 DSM_Load,
367 DSM_Cleanup,
368 DSM_LoadTitle
369};
370
371
372/* ex:set ts=4: */