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 Ultratracker (ULT) module loader
24
25==============================================================================*/
26
27#ifdef HAVE_CONFIG_H
28#include "config.h"
29#endif
30
31#ifdef HAVE_UNISTD_H
32#include <unistd.h>
33#endif
34
35#include <stdio.h>
36#ifdef HAVE_MEMORY_H
37#include <memory.h>
38#endif
39#include <string.h>
40
41#include "mikmod_internals.h"
42
43#ifdef SUNOS
44extern int fprintf(FILE *, const char *, ...);
45#endif
46
47/*========== Module structure */
48
49/* header */
50typedef struct ULTHEADER {
51 CHAR id[16];
52 CHAR songtitle[32];
53 UBYTE reserved;
54} ULTHEADER;
55
56/* sample information */
57typedef struct ULTSAMPLE {
58 CHAR samplename[32];
59 CHAR dosname[12];
60 SLONG loopstart;
61 SLONG loopend;
62 SLONG sizestart;
63 SLONG sizeend;
64 UBYTE volume;
65 UBYTE flags;
66 UWORD speed;
67 SWORD finetune;
68} ULTSAMPLE;
69
70typedef struct ULTEVENT {
71 UBYTE note,sample,eff,dat1,dat2;
72} ULTEVENT;
73
74/*========== Loader variables */
75
76#define ULTS_16BITS 4
77#define ULTS_LOOP 8
78#define ULTS_REVERSE 16
79
80#define ULT_VERSION_LEN 18
81static CHAR ULT_Version[ULT_VERSION_LEN+1]="Ultra Tracker v1.x";
82
83static ULTEVENT ev;
84
85/*========== Loader code */
86
87static int ULT_Test(void)
88{
89 CHAR id[16];
90
91 if(!_mm_read_string(id,15,modreader)) return 0;
92 if(strncmp(id,"MAS_UTrack_V00",14)) return 0;
93 if((id[14]<'1')||(id[14]>'4')) return 0;
94 return 1;
95}
96
97static int ULT_Init(void)
98{
99 return 1;
100}
101
102static void ULT_Cleanup(void)
103{
104}
105
106static UBYTE ReadUltEvent(ULTEVENT* event)
107{
108 UBYTE flag,rep=1;
109
110 flag = _mm_read_UBYTE(modreader);
111 if(flag==0xfc) {
112 rep = _mm_read_UBYTE(modreader);
113 event->note =_mm_read_UBYTE(modreader);
114 } else
115 event->note = flag;
116
117 event->sample =_mm_read_UBYTE(modreader);
118 event->eff =_mm_read_UBYTE(modreader);
119 event->dat1 =_mm_read_UBYTE(modreader);
120 event->dat2 =_mm_read_UBYTE(modreader);
121
122 return rep;
123}
124
125static int ULT_Load(int curious)
126{
127 int t,u,tracks=0;
128 SAMPLE *q;
129 ULTSAMPLE s;
130 ULTHEADER mh;
131 UBYTE nos,noc,RBnop;
132 (void)curious;
133
134 /* try to read module header */
135 _mm_read_string(mh.id,15,modreader);
136 _mm_read_string(mh.songtitle,32,modreader);
137 mh.reserved=_mm_read_UBYTE(modreader);
138
139 if(_mm_eof(modreader)) {
140 _mm_errno = MMERR_LOADING_HEADER;
141 return 0;
142 }
143
144 ULT_Version[ULT_VERSION_LEN-1]='3'+(mh.id[14]-'1');
145 of.modtype = DupStr(ULT_Version,ULT_VERSION_LEN,1);
146 of.initspeed = 6;
147 of.inittempo = 125;
148 of.reppos = 0;
149
150 /* read songtext */
151 if ((mh.id[14]>'1')&&(mh.reserved))
152 if(!ReadLinedComment(mh.reserved * 32, 32)) return 0;
153
154 nos=_mm_read_UBYTE(modreader);
155 if(_mm_eof(modreader)) {
156 _mm_errno = MMERR_LOADING_HEADER;
157 return 0;
158 }
159
160 of.songname=DupStr(mh.songtitle,32,1);
161 of.numins=of.numsmp=nos;
162
163 if(!AllocSamples()) return 0;
164 q = of.samples;
165 for(t=0;t<nos;t++) {
166 /* try to read sample info */
167 _mm_read_string(s.samplename,32,modreader);
168 _mm_read_string(s.dosname,12,modreader);
169 s.loopstart =_mm_read_I_ULONG(modreader);
170 s.loopend =_mm_read_I_ULONG(modreader);
171 s.sizestart =_mm_read_I_ULONG(modreader);
172 s.sizeend =_mm_read_I_ULONG(modreader);
173 s.volume =_mm_read_UBYTE(modreader);
174 s.flags =_mm_read_UBYTE(modreader);
175 s.speed =(mh.id[14]>='4')?_mm_read_I_UWORD(modreader):8363;
176 s.finetune =_mm_read_I_SWORD(modreader);
177
178 if(_mm_eof(modreader)) {
179 _mm_errno = MMERR_LOADING_SAMPLEINFO;
180 return 0;
181 }
182
183 q->samplename=DupStr(s.samplename,32,1);
184 /* The correct formula would be
185 s.speed * pow(2, (double)s.finetune / (OCTAVE * 32768))
186 but to avoid libm, we'll use a first order approximation.
187 1/567290 == Ln(2)/OCTAVE/32768 */
188 if(!s.finetune) q->speed = s.speed;
189 else q->speed= s.speed*((double)s.finetune/567290.0 + 1.0);
190 q->length = s.sizeend-s.sizestart;
191 q->volume = s.volume>>2;
192 q->loopstart = s.loopstart;
193 q->loopend = s.loopend;
194 q->flags = SF_SIGNED;
195 if(s.flags&ULTS_LOOP) q->flags|=SF_LOOP;
196 else q->loopstart = q->loopend = 0;
197 if(s.flags&ULTS_16BITS) {
198 s.sizeend+=(s.sizeend-s.sizestart);
199 s.sizestart<<=1;
200 q->flags|=SF_16BITS;
201 q->loopstart>>=1;
202 q->loopend>>=1;
203 }
204 q++;
205 }
206
207 if(!AllocPositions(256)) return 0;
208 for(t=0;t<256;t++)
209 of.positions[t]=_mm_read_UBYTE(modreader);
210
211 noc=_mm_read_UBYTE(modreader);
212 RBnop=_mm_read_UBYTE(modreader);
213
214 of.numchn=++noc;
215 of.numpat=++RBnop;
216 of.numtrk=of.numchn*of.numpat;
217
218 for(t=0;t<256;t++) {
219 if(of.positions[t]==255) {
220 of.positions[t]=LAST_PATTERN;
221 break;
222 }
223 if (of.positions[t]>of.numpat) { /* SANITIY CHECK */
224 /* fprintf(stderr,"positions[%d]=%d > numpat=%d\n",t,of.positions[t],of.numpat);*/
225 _mm_errno = MMERR_LOADING_HEADER;
226 return 0;
227 }
228 }
229 of.numpos=t;
230
231 if(!AllocTracks()) return 0;
232 if(!AllocPatterns()) return 0;
233 for(u=0;u<of.numchn;u++)
234 for(t=0;t<of.numpat;t++)
235 of.patterns[(t*of.numchn)+u]=tracks++;
236
237 /* Secunia SA37775 / CVE-2009-3996 */
238 if (of.numchn>=UF_MAXCHAN)
239 of.numchn=UF_MAXCHAN - 1;
240
241 /* read pan position table for v1.5 and higher */
242 if(mh.id[14]>='3') {
243 for(t=0;t<of.numchn;t++) of.panning[t]=_mm_read_UBYTE(modreader)<<4;
244 of.flags |= UF_PANNING;
245 }
246
247 for(t=0;t<of.numtrk;t++) {
248 int rep,row=0;
249 /* FIXME: unrolling continuous portamento is a HACK and needs to
250 * be replaced with a real continuous effect. This implementation
251 * breaks when tone portamento continues between patterns. See
252 * discussion in https://github.com/sezero/mikmod/pull/40 . */
253 int continuePortaToNote = 0;
254
255 UniReset();
256 while(row<64) {
257 rep=ReadUltEvent(&ev);
258
259 if(_mm_eof(modreader)) {
260 _mm_errno = MMERR_LOADING_TRACK;
261 return 0;
262 }
263
264 while(rep--) {
265 UBYTE eff;
266 int offset;
267
268 if(ev.sample) UniInstrument(ev.sample-1);
269 if(ev.note) {
270 UniNote(ev.note+2*OCTAVE-1);
271 continuePortaToNote = 0;
272 }
273
274 /* first effect - various fixes by Alexander Kerkhove and
275 Thomas Neumann */
276 eff = ev.eff>>4;
277
278 if (continuePortaToNote && (eff != 0x3) && ((ev.eff & 0xf) != 0x3))
279 UniEffect(UNI_ITEFFECTG, 0);
280
281 switch(eff) {
282 case 0x3: /* tone portamento */
283 UniEffect(UNI_ITEFFECTG,ev.dat2);
284 continuePortaToNote = 1;
285 break;
286 case 0x5:
287 break;
288 case 0x9: /* sample offset */
289 offset=(ev.dat2<<8)|((ev.eff&0xf)==9?ev.dat1:0);
290 UniEffect(UNI_ULTEFFECT9,offset);
291 break;
292 case 0xb: /* panning */
293 UniPTEffect(8,ev.dat2*0xf);
294 of.flags |= UF_PANNING;
295 break;
296 case 0xc: /* volume */
297 UniPTEffect(eff,ev.dat2>>2);
298 break;
299 default:
300 UniPTEffect(eff,ev.dat2);
301 break;
302 }
303
304 /* second effect */
305 eff=ev.eff&0xf;
306 switch(eff) {
307 case 0x3: /* tone portamento */
308 UniEffect(UNI_ITEFFECTG,ev.dat1);
309 continuePortaToNote = 1;
310 break;
311 case 0x5:
312 break;
313 case 0x9: /* sample offset */
314 if((ev.eff>>4)!=9)
315 UniEffect(UNI_ULTEFFECT9,((UWORD)ev.dat1)<<8);
316 break;
317 case 0xb: /* panning */
318 UniPTEffect(8,ev.dat1*0xf);
319 of.flags |= UF_PANNING;
320 break;
321 case 0xc: /* volume */
322 UniPTEffect(eff,ev.dat1>>2);
323 break;
324 default:
325 UniPTEffect(eff,ev.dat1);
326 break;
327 }
328
329 UniNewline();
330 row++;
331 }
332 }
333 if(!(of.tracks[t]=UniDup())) return 0;
334 }
335 return 1;
336}
337
338static CHAR * ULT_LoadTitle(void)
339{
340 CHAR s[32];
341
342 _mm_fseek(modreader,15,SEEK_SET);
343 if(!_mm_read_UBYTES(s,32,modreader)) return NULL;
344
345 return(DupStr(s,32,1));
346}
347
348/*========== Loader information */
349
350MIKMODAPI MLOADER load_ult={
351 NULL,
352 "ULT",
353 "ULT (UltraTracker)",
354 ULT_Init,
355 ULT_Test,
356 ULT_Load,
357 ULT_Cleanup,
358 ULT_LoadTitle
359};
360
361/* ex:set ts=4: */