summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThom Johansen <thomj@rockbox.org>2008-05-21 11:19:58 +0000
committerThom Johansen <thomj@rockbox.org>2008-05-21 11:19:58 +0000
commitc0f7eb9f9dda1941ff80571df74d5616a4604ed7 (patch)
tree9196ea881d0a49b0f2f0cb6c577f4de1cc7c9b25
parentc78bb5a00ef4c71c875d33dac2cb97872db9a0d4 (diff)
downloadrockbox-c0f7eb9f9dda1941ff80571df74d5616a4604ed7.tar.gz
rockbox-c0f7eb9f9dda1941ff80571df74d5616a4604ed7.zip
FS #8680. MOD codec by Rainer Sinsch.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17595 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/SOURCES1
-rw-r--r--apps/codecs/Makefile1
-rw-r--r--apps/codecs/SOURCES1
-rw-r--r--apps/codecs/mod.c1327
-rw-r--r--apps/filetypes.c1
-rw-r--r--apps/metadata.c8
-rw-r--r--apps/metadata/metadata_parsers.h1
-rw-r--r--apps/metadata/mod.c62
-rw-r--r--firmware/export/id3.h1
-rw-r--r--firmware/id3.c3
10 files changed, 1406 insertions, 0 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index 8c7cc15ecb..b9b763486f 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -134,6 +134,7 @@ metadata/mp4.c
134metadata/mpc.c 134metadata/mpc.c
135metadata/ogg.c 135metadata/ogg.c
136metadata/sid.c 136metadata/sid.c
137metadata/mod.c
137metadata/spc.c 138metadata/spc.c
138metadata/vorbis.c 139metadata/vorbis.c
139metadata/wave.c 140metadata/wave.c
diff --git a/apps/codecs/Makefile b/apps/codecs/Makefile
index 5a65341d02..7c066d284d 100644
--- a/apps/codecs/Makefile
+++ b/apps/codecs/Makefile
@@ -47,6 +47,7 @@ all: $(LINKCODEC) $(ROCKS)
47ifndef SIMVER 47ifndef SIMVER
48$(BUILDDIR)/%.a : % $(CODECDEPS) 48$(BUILDDIR)/%.a : % $(CODECDEPS)
49 49
50$(OBJDIR)/mod.elf : $(OBJDIR)/mod.o $(OBJDIR)/codec_crt0.o
50$(OBJDIR)/wav.elf : $(OBJDIR)/wav.o $(OBJDIR)/codec_crt0.o 51$(OBJDIR)/wav.elf : $(OBJDIR)/wav.o $(OBJDIR)/codec_crt0.o
51$(OBJDIR)/sid.elf : $(OBJDIR)/sid.o $(OBJDIR)/codec_crt0.o 52$(OBJDIR)/sid.elf : $(OBJDIR)/sid.o $(OBJDIR)/codec_crt0.o
52$(OBJDIR)/adx.elf : $(OBJDIR)/adx.o $(OBJDIR)/codec_crt0.o 53$(OBJDIR)/adx.elf : $(OBJDIR)/adx.o $(OBJDIR)/codec_crt0.o
diff --git a/apps/codecs/SOURCES b/apps/codecs/SOURCES
index a93cb6c0cc..3c0118c1ce 100644
--- a/apps/codecs/SOURCES
+++ b/apps/codecs/SOURCES
@@ -13,6 +13,7 @@ wma.c
13aac.c 13aac.c
14#endif 14#endif
15ape.c 15ape.c
16mod.c
16shorten.c 17shorten.c
17aiff.c 18aiff.c
18speex.c 19speex.c
diff --git a/apps/codecs/mod.c b/apps/codecs/mod.c
new file mode 100644
index 0000000000..0d3eadf773
--- /dev/null
+++ b/apps/codecs/mod.c
@@ -0,0 +1,1327 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * MOD Codec for rockbox
11 *
12 * Written from scratch by Rainer Sinsch
13 * exclusivly for Rockbox in February 2008
14 *
15 * All files in this archive are subject to the GNU General Public License.
16 * See the file COPYING in the source tree root for full license agreement.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22
23 /**************
24 * This version supports large files directly from internal memory management.
25 * There is a drawback however: It may happen that a song is not completely
26 * loaded when the internal rockbox-ringbuffer (approx. 28MB) is filled up
27 * As a workaround make sure you don't have directories with mods larger
28 * than a total of 28MB
29 *************/
30
31#include "debug.h"
32#include "codeclib.h"
33#include <inttypes.h>
34
35#include <stdio.h>
36#include <string.h>
37#include <stdlib.h>
38#include <ctype.h>
39
40
41CODEC_HEADER
42
43#define CHUNK_SIZE (1024*2)
44
45
46/* This codec supports MOD Files:
47 *
48 */
49
50static int32_t samples[CHUNK_SIZE] IBSS_ATTR; /* The sample buffer */
51
52/* Instrument Data */
53struct s_instrument {
54 /* Sample name / description */
55 /*char description[22];*/
56
57 /* Sample length in bytes */
58 unsigned short length;
59
60 /* Sample finetuning (-8 - +7) */
61 signed char finetune;
62
63 /* Sample volume (0 - 64) */
64 signed char volume;
65
66 /* Sample Repeat Position */
67 unsigned short repeatoffset;
68
69 /* Sample Repeat Length */
70 unsigned short repeatlength;
71
72 /* Offset to sample data */
73 unsigned int sampledataoffset;
74};
75
76/* Song Data */
77struct s_song {
78 /* Song name / title description */
79 /*char szTitle[20];*/
80
81 /* No. of channels in song */
82 unsigned char noofchannels;
83
84 /* No. of instruments used (either 15 or 31) */
85 unsigned char noofinstruments;
86
87 /* How many patterns are beeing played? */
88 unsigned char songlength;
89
90 /* Where to jump after the song end? */
91 unsigned char songendjumpposition;
92
93 /* Pointer to the Pattern Order Table */
94 unsigned char *patternordertable;
95
96 /* Pointer to the pattern data */
97 void *patterndata;
98
99 /* Pointer to the sample buffer */
100 signed char *sampledata;
101
102 /* Instrument data */
103 struct s_instrument instrument[31];
104};
105
106struct s_modchannel {
107 /* Current Volume */
108 signed char volume;
109
110 /* Current Offset to period in PeriodTable of notebeeing played
111 (can be temporarily negative) */
112 short periodtableoffset;
113
114 /* Current Period beeing played */
115 short period;
116
117 /* Current effect */
118 unsigned char effect;
119
120 /* Current parameters of effect */
121 unsigned char effectparameter;
122
123 /* Current Instrument beeing played */
124 unsigned char instrument;
125
126 /* Current Vibrato Speed */
127 unsigned char vibratospeed;
128
129 /* Current Vibrato Depth */
130 unsigned char vibratodepth;
131
132 /* Current Position for Vibrato in SinTable */
133 unsigned char vibratosinpos;
134
135 /* Current Tremolo Speed */
136 unsigned char tremolospeed;
137
138 /* Current Tremolo Depth */
139 unsigned char tremolodepth;
140
141 /* Current Position for Tremolo in SinTable */
142 unsigned char tremolosinpos;
143
144 /* Current Speed of Effect "Slide Note up" */
145 unsigned char slideupspeed;
146
147 /* Current Speed of Effect "Slide Note down" */
148 unsigned char slidedownspeed;
149
150 /* Current Speed of the "Slide to Note" effect */
151 unsigned char slidetonotespeed;
152
153 /* Current Period of the "Slide to Note" effect */
154 unsigned short slidetonoteperiod;
155};
156
157struct s_modplayer {
158 /* Ticks per Line */
159 unsigned char ticksperline;
160
161 /* Beats per Minute */
162 unsigned char bpm;
163
164 /* Position of the Song in the Pattern Table (0-127) */
165 unsigned char patterntableposition;
166
167 /* Current Line (may be temporarily < 0) */
168 signed char currentline;
169
170 /* Current Tick */
171 signed char currenttick;
172
173 /* How many samples are required to calculate for each tick? */
174 unsigned int samplespertick;
175
176 /* Information about the channels */
177 struct s_modchannel modchannel[8];
178
179 /* The Amiga Period Table
180 (+1 because we use index 0 for period 0 = no new note) */
181 unsigned short periodtable[37*8+1];
182
183 /* The sinus table [-255,255] */
184 signed short sintable[0x40];
185
186 /* Is the glissando effect enabled? */
187 bool glissandoenabled;
188
189 /* Is the Amiga Filter enabled? */
190 bool amigafilterenabled;
191
192 /* The pattern-line where the loop is carried out (set with e6 command) */
193 unsigned char loopstartline;
194
195 /* Number of times to loop */
196 unsigned char looptimes;
197};
198
199struct s_channel {
200 /* Panning (0 = left, 16 = right) */
201 unsigned char panning;
202
203 /* Sample frequency of the channel */
204 unsigned short frequency;
205
206 /* Position of the sample currently played */
207 unsigned int samplepos;
208
209 /* Fractual Position of the sample currently player */
210 unsigned int samplefractpos;
211
212 /* Loop Sample */
213 bool loopsample;
214
215 /* Loop Position Start */
216 unsigned int loopstart;
217
218 /* Loop Position End */
219 unsigned int loopend;
220
221 /* Is The channel beeing played? */
222 bool channelactive;
223
224 /* The Volume (0..64) */
225 signed char volume;
226
227 /* The last sampledata beeing played (required for interpolation) */
228 signed short lastsampledata;
229};
230
231struct s_mixer {
232 /* The channels */
233 struct s_channel channel[32];
234};
235
236struct s_song modsong IDATA_ATTR; /* The Song */
237struct s_modplayer modplayer IDATA_ATTR; /* The Module Player */
238struct s_mixer mixer IDATA_ATTR;
239
240const unsigned short mixingrate = 44100;
241
242STATICIRAM void mixer_playsample(int channel, int instrument) ICODE_ATTR;
243void mixer_playsample(int channel, int instrument)
244{
245 struct s_channel *p_channel = &mixer.channel[channel];
246 struct s_instrument *p_instrument = &modsong.instrument[instrument];
247
248 p_channel->channelactive = true;
249 p_channel->samplepos = p_instrument->sampledataoffset;
250 p_channel->samplefractpos = 0;
251 p_channel->loopsample = (p_instrument->repeatlength > 2) ? true : false;
252 if (p_channel->loopsample) {
253 p_channel->loopstart = p_instrument->repeatoffset +
254 p_instrument->sampledataoffset;
255 p_channel->loopend = p_channel->loopstart +
256 p_instrument->repeatlength;
257 }
258 else p_channel->loopend = p_instrument->length +
259 p_instrument->sampledataoffset;
260
261 /* Remember the instrument */
262 modplayer.modchannel[channel].instrument = instrument;
263}
264
265inline void mixer_stopsample(int channel)
266{
267 mixer.channel[channel].channelactive = false;
268}
269
270inline void mixer_continuesample(int channel)
271{
272 mixer.channel[channel].channelactive = true;
273}
274
275inline void mixer_setvolume(int channel, int volume)
276{
277 mixer.channel[channel].volume = volume;
278}
279
280inline void mixer_setpanning(int channel, int panning)
281{
282 mixer.channel[channel].panning = panning;
283}
284
285inline void mixer_setamigaperiod(int channel, int amigaperiod)
286{
287 /* Just to make sure we don't devide by zero
288 * amigaperiod shouldn't 0 anyway - if it is the case
289 * then something terribly went wrong */
290 if (amigaperiod == 0)
291 return;
292
293 mixer.channel[channel].frequency = 3579546 / amigaperiod;
294}
295
296/* Initialize the MOD Player with default values and precalc tables */
297STATICIRAM void initmodplayer(void) ICODE_ATTR;
298void initmodplayer(void)
299{
300 unsigned int i,c;
301
302 /* Calculate Amiga Period Values
303 * Start with Period 907 (= C-1 with Finetune -8) and work upwards */
304 double f = 907.0f;
305 /* Index 0 stands for no note (and therefore no period) */
306 modplayer.periodtable[0] = 0;
307 for (i=1;i<297;i++)
308 {
309 modplayer.periodtable[i] = (unsigned short) f;
310 f /= 1.0072464122237039; /* = pow(2.0f, 1.0f/(12.0f*8.0f)); */
311 }
312
313 /*
314 * This is a more accurate but also time more consuming approach
315 * to calculate the amiga period table
316 * Commented out for speed purposes
317 const int finetuning = 8;
318 const int octaves = 3;
319 for (int halftone=0;halftone<=finetuning*octaves*12+7;halftone++)
320 {
321 float e = pow(2.0f, halftone/(12.0f*8.0f));
322 float f = 906.55f/e;
323 modplayer.periodtable[halfetone+1] = (int)(f+0.5f);
324 }
325 */
326
327 /* Calculate Protracker Vibrato sine table
328 * The routine makes use of the Harmonical Oscillator Approach
329 * for calculating sine tables
330 * (see http://membres.lycos.fr/amycoders/tutorials/sintables.html)
331 * The routine presented here calculates a complete sine wave
332 * with 64 values in range [-255,255]
333 */
334 float a, b, d, dd;
335
336 d = 0.09817475f; /* = 2*PI/64 */
337 dd = d*d;
338 a = 0;
339 b = d;
340
341 for (i=0;i<0x40;i++)
342 {
343 modplayer.sintable[i] = (int)(255*a);
344
345 a = a+b;
346 b = b-dd*a;
347 }
348
349 /* Set Default Player Values */
350 modplayer.currentline = 0;
351 modplayer.currenttick = 0;
352 modplayer.patterntableposition = 0;
353 modplayer.bpm = 125;
354 modplayer.ticksperline = 6;
355 modplayer.glissandoenabled = false; /* Disable glissando */
356 modplayer.amigafilterenabled = false; /* Disable the Amiga Filter */
357
358 /* Default Panning Values */
359 int panningvalues[8] = {4,12,12,4,4,12,12,4};
360 for (c=0;c<8;c++)
361 {
362 /* Set Default Panning */
363 mixer_setpanning(c, panningvalues[c]);
364 /* Reset channels in the MOD Player */
365 memset(&modplayer.modchannel[c], 0, sizeof(struct s_modchannel));
366 /* Don't play anything */
367 mixer.channel[c].channelactive = false;
368 }
369
370}
371
372/* Load the MOD File from memory */
373STATICIRAM bool loadmod(void *modfile) ICODE_ATTR;
374bool loadmod(void *modfile)
375{
376 int i;
377 bool periodsconverted = false;
378
379 /* We don't support PowerPacker 2.0 Files */
380 if (memcmp((char*) modfile, "PP20", 4) == 0) return false;
381
382 /* Get the File Format Tag */
383 char *fileformattag = (char*)modfile + 1080;
384
385 /* Find out how many channels and instruments are used */
386 if (memcmp(fileformattag, "2CHN", 4) == 0)
387 {modsong.noofchannels = 2; modsong.noofinstruments = 31;}
388 else if (memcmp(fileformattag, "M.K.", 4) == 0)
389 {modsong.noofchannels = 4; modsong.noofinstruments = 31;}
390 else if (memcmp(fileformattag, "M!K!", 4) == 0)
391 {modsong.noofchannels = 4; modsong.noofinstruments = 31;}
392 else if (memcmp(fileformattag, "4CHN", 4) == 0)
393 {modsong.noofchannels = 4; modsong.noofinstruments = 31;}
394 else if (memcmp(fileformattag, "FLT4", 4) == 0)
395 {modsong.noofchannels = 4; modsong.noofinstruments = 31;}
396 else if (memcmp(fileformattag, "6CHN", 4) == 0)
397 {modsong.noofchannels = 6; modsong.noofinstruments = 31;}
398 else if (memcmp(fileformattag, "8CHN", 4) == 0)
399 {modsong.noofchannels = 8; modsong.noofinstruments = 31;}
400 else if (memcmp(fileformattag, "OKTA", 4) == 0)
401 {modsong.noofchannels = 8; modsong.noofinstruments = 31;}
402 else if (memcmp(fileformattag, "CD81", 4) == 0)
403 {modsong.noofchannels = 8; modsong.noofinstruments = 31;}
404 else if (memcmp(fileformattag, "RS", 2) == 0)
405 {
406 /* This is a special internal in-memory fileformat
407 * where the periods have been already converted to offsets
408 * in our periodtable.
409 * It comes to use when rockbox reloads an already played
410 * module (e.g. on rewinding) */
411 modsong.noofchannels = fileformattag[2];
412 modsong.noofinstruments = fileformattag[3];
413 periodsconverted = true;
414 }
415 else {
416 /* The file has no format tag, so we take a guess */
417 modsong.noofchannels = 4;
418
419 /* For the no of instruments we check if there is a sample #16 and
420 * if it has a (ascii) name */
421 char *p = (char *)modfile + 470;
422 if ((*p >= 32) && (*p <= 126)) modsong.noofinstruments = 31;
423 else modsong.noofinstruments = 15;
424 }
425
426 /* Get the Song title
427 * Skipped here
428 * strncpy(modsong.szTitle, (char*)pMODFile, 20); */
429
430 /* Get the Instrument information */
431 for (i=0;i<modsong.noofinstruments;i++)
432 {
433 struct s_instrument *instrument = &modsong.instrument[i];
434 unsigned char *p = (unsigned char *)modfile + 20 + i*30;
435
436 /*strncpy(instrument->description, (char*)p, 22); */
437 p += 22;
438 instrument->length = (((p[0])<<8) + p[1]) << 1; p+=2;
439 instrument->finetune = *p++ & 0x0f;
440 /* Treat finetuning as signed nibble */
441 if (instrument->finetune > 7) instrument->finetune -= 16;
442 instrument->volume = *p++;
443 instrument->repeatoffset = (((p[0])<<8) + p[1]) << 1; p+= 2;
444 instrument->repeatlength = (((p[0])<<8) + p[1]) << 1;
445 }
446
447 /* Get the pattern information */
448 unsigned char *p = (unsigned char *)modfile + 20 +
449 modsong.noofinstruments*30;
450 modsong.songlength = *p++;
451 modsong.songendjumpposition = *p++;
452 modsong.patternordertable = p;
453
454 /* Find out how many patterns are used within this song */
455 int maxpatterns = 0;
456 for (i=0;i<128;i++)
457 if (modsong.patternordertable[i] > maxpatterns)
458 maxpatterns = modsong.patternordertable[i];
459 maxpatterns++;
460
461 /* Get the pattern data */
462 modsong.patterndata = (char*)modfile + 20 +
463 modsong.noofinstruments*30 + 134;
464
465 /* Convert the period values in the mod file to offsets
466 * in our periodtable (but only, if we haven't done this yet) */
467 p = (unsigned char *) modsong.patterndata;
468 if (!periodsconverted)
469 {
470 int note, note2, channel;
471 for (note=0;note<maxpatterns*64;note++)
472 for (channel=0;channel<modsong.noofchannels;channel++)
473 {
474 int period = ((p[0] & 0x0f) << 8) | p[1];
475 int periodoffset = 0;
476
477 /* Find the offset of the current period */
478 for (note2 = 1; note2 < 12*3+1; note2++)
479 if (abs(modplayer.periodtable[note2*8+1]-period) < 4)
480 {
481 periodoffset = note2*8+1;
482 break;
483 }
484 /* Write back the period offset */
485 p[0] = (periodoffset >> 8) | (p[0] & 0xf0);
486 p[1] = periodoffset & 0xff;
487 p += 4;
488 }
489 /* Remember that we already converted the periods,
490 * in case the file gets reloaded by rewinding */
491 fileformattag[0] = 'R'; fileformattag[1] = 'S';
492 fileformattag[2] = modsong.noofchannels;
493 fileformattag[3] = modsong.noofinstruments;
494 }
495
496 /* Get the samples
497 * Calculation: The Samples come after the pattern data
498 * We know that there are nMaxPatterns and each pattern requires
499 * 4 bytes per note and per channel.
500 * And of course there are always lines in each channel */
501 modsong.sampledata = (signed char*) modsong.patterndata +
502 maxpatterns*4*modsong.noofchannels*64;
503 int sampledataoffset = 0;
504 for (i=0;i<modsong.noofinstruments;i++)
505 {
506 modsong.instrument[i].sampledataoffset = sampledataoffset;
507 sampledataoffset += modsong.instrument[i].length;
508 }
509
510 return true;
511}
512
513/* Apply vibrato to channel */
514STATICIRAM void vibrate(int channel) ICODE_ATTR;
515void vibrate(int channel)
516{
517 struct s_modchannel *p_modchannel = &modplayer.modchannel[channel];
518
519 /* Apply Vibrato
520 * >> 7 is used in the original protracker source code */
521 mixer_setamigaperiod(channel, p_modchannel->period+
522 ((p_modchannel->vibratodepth *
523 modplayer.sintable[p_modchannel->vibratosinpos])>>7));
524
525 /* Foward in Sine Table */
526 p_modchannel->vibratosinpos += p_modchannel->vibratospeed;
527 p_modchannel->vibratosinpos &= 0x3f;
528}
529
530/* Apply tremolo to channel
531 * (same as vibrato, but only apply on volume instead of pitch) */
532STATICIRAM void tremolo(int channel) ICODE_ATTR;
533void tremolo(int channel)
534{
535 struct s_modchannel *p_modchannel = &modplayer.modchannel[channel];
536
537 /* Apply Tremolo
538 * >> 6 is used in the original protracker source code */
539 int volume = (p_modchannel->volume *
540 modplayer.sintable[p_modchannel->tremolosinpos])>>6;
541 if (volume > 64) volume = 64;
542 else if (volume < 0) volume = 0;
543 mixer_setvolume(channel, volume);
544
545 /* Foward in Sine Table */
546 p_modchannel->tremolosinpos += p_modchannel->tremolosinpos;
547 p_modchannel->tremolosinpos &= 0x3f;
548}
549
550/* Apply Slide to Note effect to channel */
551STATICIRAM void slidetonote(int channel) ICODE_ATTR;
552void slidetonote(int channel)
553{
554 struct s_modchannel *p_modchannel = &modplayer.modchannel[channel];
555
556 /* If there hasn't been any slide-to note set up, then return */
557 if (p_modchannel->slidetonoteperiod == 0) return;
558
559 /* Slide note up */
560 if (p_modchannel->slidetonoteperiod > p_modchannel->period)
561 {
562 p_modchannel->period += p_modchannel->slidetonotespeed;
563 if (p_modchannel->period > p_modchannel->slidetonoteperiod)
564 p_modchannel->period = p_modchannel->slidetonoteperiod;
565 }
566 /* Slide note down */
567 else if (p_modchannel->slidetonoteperiod < p_modchannel->period)
568 {
569 p_modchannel->period -= p_modchannel->slidetonotespeed;
570 if (p_modchannel->period < p_modchannel->slidetonoteperiod)
571 p_modchannel->period = p_modchannel->slidetonoteperiod;
572 }
573 mixer_setamigaperiod(channel, p_modchannel->period);
574}
575
576/* Apply Slide to Note effect on channel,
577 * but this time with glissando enabled */
578STATICIRAM void slidetonoteglissando(int channel) ICODE_ATTR;
579void slidetonoteglissando(int channel)
580{
581 struct s_modchannel *p_modchannel = &modplayer.modchannel[channel];
582
583 /* Slide note up */
584 if (p_modchannel->slidetonoteperiod > p_modchannel->period)
585 {
586 p_modchannel->period =
587 modplayer.periodtable[p_modchannel->periodtableoffset+=8];
588 if (p_modchannel->period > p_modchannel->slidetonoteperiod)
589 p_modchannel->period = p_modchannel->slidetonoteperiod;
590 }
591 /* Slide note down */
592 else
593 {
594 p_modchannel->period =
595 modplayer.periodtable[p_modchannel->periodtableoffset-=8];
596 if (p_modchannel->period < p_modchannel->slidetonoteperiod)
597 p_modchannel->period = p_modchannel->slidetonoteperiod;
598 }
599 mixer_setamigaperiod(channel, p_modchannel->period);
600}
601
602/* Apply Volume Slide */
603STATICIRAM void volumeslide(int channel, int effectx, int effecty) ICODE_ATTR;
604void volumeslide(int channel, int effectx, int effecty)
605{
606 struct s_modchannel *p_modchannel = &modplayer.modchannel[channel];
607
608 /* If both X and Y Parameters are non-zero, then the y value is ignored */
609 if (effectx > 0) {
610 p_modchannel->volume += effectx;
611 if (p_modchannel->volume > 64) p_modchannel->volume = 64;
612 }
613 else {
614 p_modchannel->volume -= effecty;
615 if (p_modchannel->volume < 0) p_modchannel->volume = 0;
616 }
617
618 mixer_setvolume(channel, p_modchannel->volume);
619}
620
621/* Play the current line (at tick 0) */
622STATICIRAM void playline(int pattern, int line) ICODE_ATTR;
623void playline(int pattern, int line)
624{
625 int c;
626
627 /* Get pointer to the current pattern */
628 unsigned char *p_line = (unsigned char*)modsong.patterndata;
629 p_line += pattern*64*4*modsong.noofchannels;
630 p_line += line*4*modsong.noofchannels;
631
632 /* Only allow one Patternbreak Commando per Line */
633 bool patternbreakdone = false;
634
635 for (c=0;c<modsong.noofchannels;c++)
636 {
637 struct s_modchannel *p_modchannel = &modplayer.modchannel[c];
638 unsigned char *p_note = p_line + c*4;
639 unsigned char samplenumber = (p_note[0] & 0xf0) | (p_note[2] >> 4);
640 short periodtableoffset = ((p_note[0] & 0x0f) << 8) | p_note[1];
641
642 p_modchannel->effect = p_note[2] & 0x0f;
643 p_modchannel->effectparameter = p_note[3];
644
645 /* Remember Instrument and set Volume if new Instrument triggered */
646 if (samplenumber > 0)
647 {
648 /* And trigger new sample, if new instrument was set */
649 if (samplenumber-1 != p_modchannel->instrument)
650 {
651 /* Advance the new sample to the same offset
652 * the old sample was beeing played */
653 int oldsampleoffset = mixer.channel[c].samplepos -
654 modsong.instrument[
655 p_modchannel->instrument].sampledataoffset;
656 mixer_playsample(c, samplenumber-1);
657 mixer.channel[c].samplepos += oldsampleoffset;
658 }
659
660 /* Remember last played instrument on channel */
661 p_modchannel->instrument = samplenumber-1;
662
663 /* Set Volume to standard instrument volume,
664 * if not overwritten by volume effect */
665 if (p_modchannel->effect != 0x0c)
666 {
667 p_modchannel->volume = modsong.instrument[
668 p_modchannel->instrument].volume;
669 mixer_setvolume(c, p_modchannel->volume);
670 }
671 }
672 /* Trigger new sample if note available */
673 if (periodtableoffset > 0)
674 {
675 /* Restart instrument only when new sample triggered */
676 if (samplenumber != 0)
677 mixer_playsample(c, (samplenumber > 0) ?
678 samplenumber-1 : p_modchannel->instrument);
679
680 /* Set the new amiga period
681 * (but only, if there is no slide to note effect) */
682 if ((p_modchannel->effect != 0x3) &&
683 (p_modchannel->effect != 0x5))
684 {
685 /* Apply finetuning to sample */
686 p_modchannel->periodtableoffset = periodtableoffset +
687 modsong.instrument[p_modchannel->instrument].finetune;
688 p_modchannel->period = modplayer.periodtable[
689 p_modchannel->periodtableoffset];
690 mixer_setamigaperiod(c, p_modchannel->period);
691 /* When a new note is played without slide to note setup,
692 * then disable slide to note */
693 modplayer.modchannel[c].slidetonoteperiod =
694 p_modchannel->period;
695 }
696 }
697 int effectx = p_modchannel->effectparameter>>4;
698 int effecty = p_modchannel->effectparameter&0x0f;
699
700 switch (p_modchannel->effect)
701 {
702 /* Effect 0: Arpeggio */
703 case 0x00:
704 /* Set the base period on tick 0 */
705 if (p_modchannel->effectparameter > 0)
706 mixer_setamigaperiod(c,
707 modplayer.periodtable[
708 p_modchannel->periodtableoffset]);
709 break;
710 /* Slide up (Portamento up) */
711 case 0x01:
712 if (p_modchannel->effectparameter > 0)
713 p_modchannel->slideupspeed =
714 p_modchannel->effectparameter;
715 break;
716
717 /* Slide down (Portamento down) */
718 case 0x02:
719 if (p_modchannel->effectparameter > 0)
720 p_modchannel->slidedownspeed =
721 p_modchannel->effectparameter;
722 break;
723
724 /* Slide to Note */
725 case 0x03:
726 if (p_modchannel->effectparameter > 0)
727 p_modchannel->slidetonotespeed =
728 p_modchannel->effectparameter;
729 /* Get the slide to note directly from the pattern buffer */
730 if (periodtableoffset > 0)
731 p_modchannel->slidetonoteperiod =
732 modplayer.periodtable[periodtableoffset +
733 modsong.instrument[
734 p_modchannel->instrument].finetune];
735 /* If glissando is enabled apply the effect directly here */
736 if (modplayer.glissandoenabled)
737 slidetonoteglissando(c);
738 break;
739
740 /* Set Vibrato */
741 case 0x04:
742 if (effectx > 0) p_modchannel->vibratospeed = effectx;
743 if (effecty > 0) p_modchannel->vibratodepth = effecty;
744 break;
745
746 /* Effect 0x06: Slide to note */
747 case 0x05:
748 /* Get the slide to note directly from the pattern buffer */
749 if (periodtableoffset > 0)
750 p_modchannel->slidetonoteperiod =
751 modplayer.periodtable[periodtableoffset +
752 modsong.instrument[
753 p_modchannel->instrument].finetune];
754 break;
755
756 /* Effect 0x06 is "Continue Effects" */
757 /* It is not processed on tick 0 */
758 case 0x06:
759 break;
760
761 /* Set Tremolo */
762 case 0x07:
763 if (effectx > 0) p_modchannel->tremolodepth = effectx;
764 if (effecty > 0) p_modchannel->tremolospeed = effecty;
765 break;
766
767 /* Set fine panning */
768 case 0x08:
769 /* Internal panning goes from 0..15
770 * Scale the fine panning value to that range */
771 mixer.channel[c].panning = p_modchannel->effectparameter>>4;
772 break;
773
774 /* Set Sample Offset */
775 case 0x09:
776 {
777 struct s_instrument *p_instrument =
778 &modsong.instrument[p_modchannel->instrument];
779 int sampleoffset = p_instrument->sampledataoffset;
780 if (sampleoffset > p_instrument->length)
781 sampleoffset = p_instrument->length;
782 /* Forward the new offset to the mixer */
783 mixer.channel[c].samplepos =
784 p_instrument->sampledataoffset +
785 (p_modchannel->effectparameter<<8);
786 mixer.channel[c].samplefractpos = 0;
787 break;
788 }
789
790 /* Effect 0x0a (Volume slide) is not processed on tick 0 */
791
792 /* Position Jump */
793 case 0x0b:
794 modplayer.currentline = -1;
795 modplayer.patterntableposition = (effectx<<4)+effecty;
796 break;
797
798 /* Set Volume */
799 case 0x0c:
800 p_modchannel->volume = p_modchannel->effectparameter;
801 mixer_setvolume(c, p_modchannel->volume);
802 break;
803
804 /* Pattern break */
805 case 0x0d:
806 modplayer.currentline = effectx*10 + effecty - 1;
807 if (!patternbreakdone)
808 {
809 patternbreakdone = true;
810 modplayer.patterntableposition++;
811 }
812 break;
813
814 /* Extended Effects */
815 case 0x0e:
816 switch (effectx)
817 {
818 /* Set Filter */
819 case 0x0:
820 modplayer.amigafilterenabled =
821 (effecty>0) ? false : true;
822 break;
823 /* Fineslide up */
824 case 0x1:
825 mixer_setamigaperiod(c, p_modchannel->period -=
826 effecty);
827 if (p_modchannel->period <
828 modplayer.periodtable[37*8]) p_modchannel->period = 100;
829 /* Find out the new offset in the period table */
830 if (p_modchannel->periodtableoffset < 36*8)
831 while (modplayer.periodtable[
832 p_modchannel->periodtableoffset+8] >= p_modchannel->period)
833 p_modchannel->periodtableoffset+=8;
834 break;
835 /* Fineslide down */
836 case 0x2:
837 mixer_setamigaperiod(c,
838 p_modchannel->period += effecty);
839 if (p_modchannel->periodtableoffset > 8)
840 while (modplayer.periodtable[
841 p_modchannel->periodtableoffset-8]
842 <= p_modchannel->period)
843 p_modchannel->periodtableoffset-=8;
844 break;
845 /* Set glissando on/off */
846 case 0x3:
847 modplayer.glissandoenabled =
848 (effecty > 0) ? true:false;
849 break;
850 /* Set Vibrato waveform */
851 case 0x4:
852 /* Currently not implemented */
853 break;
854 /* Set Finetune value */
855 case 0x5:
856 /* Treat as signed nibble */
857 if (effecty > 7) effecty -= 16;
858
859 p_modchannel->periodtableoffset +=
860 effecty -
861 modsong.instrument[
862 p_modchannel->instrument].finetune;
863 p_modchannel->period =
864 modplayer.periodtable[
865 p_modchannel->periodtableoffset];
866 modsong.instrument[
867 p_modchannel->instrument].finetune = effecty;
868 break;
869 /* Pattern loop */
870 case 0x6:
871 if (effecty == 0)
872 modplayer.loopstartline = line-1;
873 else
874 {
875 if (modplayer.looptimes == 0)
876 {
877 modplayer.currentline =
878 modplayer.loopstartline;
879 modplayer.looptimes = effecty;
880 }
881 else modplayer.looptimes--;
882 if (modplayer.looptimes > 0)
883 modplayer.currentline =
884 modplayer.loopstartline;
885 }
886 break;
887 /* Set Tremolo waveform */
888 case 0x7:
889 /* Not yet implemented */
890 break;
891 /* Enhanced Effect 8 is not used */
892 case 0x8:
893 break;
894 /* Retrigger sample */
895 case 0x9:
896 /* Only processed on subsequent ticks */
897 break;
898 /* Fine volume slide up */
899 case 0xa:
900 p_modchannel->volume += effecty;
901 if (p_modchannel->volume > 64)
902 p_modchannel->volume = 64;
903 mixer_setvolume(c, p_modchannel->volume);
904 break;
905 /* Fine volume slide down */
906 case 0xb:
907 p_modchannel->volume -= effecty;
908 if (p_modchannel->volume < 0)
909 p_modchannel->volume = 0;
910 mixer_setvolume(c, p_modchannel->volume);
911 break;
912 /* Cut sample */
913 case 0xc:
914 /* Continue sample */
915 mixer_continuesample(c);
916 break;
917 /* Note delay (Usage: $ED + ticks to delay note.) */
918 case 0xd:
919 /* We stop the sample here on tick 0
920 * and restart it later in the effect */
921 if (effecty > 0)
922 mixer.channel[c].channelactive = false;
923 break;
924 }
925 break;
926
927 /* Set Speed */
928 case 0x0f:
929 if (p_modchannel->effectparameter < 32)
930 modplayer.ticksperline = p_modchannel->effectparameter;
931 else
932 modplayer.bpm = p_modchannel->effectparameter;
933 break;
934 }
935 }
936}
937
938/* Play the current effect of the note (ticks 1..speed) */
939STATICIRAM void playeffect(int currenttick) ICODE_ATTR;
940void playeffect(int currenttick)
941{
942 int c;
943
944 for (c=0;c<modsong.noofchannels;c++)
945 {
946 struct s_modchannel *p_modchannel = &modplayer.modchannel[c];
947
948 /* If there is no note active then there are no effects to play */
949 if (p_modchannel->period == 0) continue;
950
951 unsigned char effectx = p_modchannel->effectparameter>>4;
952 unsigned char effecty = p_modchannel->effectparameter&0x0f;
953
954 switch (p_modchannel->effect)
955 {
956 /* Effect 0: Arpeggio */
957 case 0x00:
958 if (p_modchannel->effectparameter > 0)
959 {
960 unsigned short newperiodtableoffset;
961 switch (currenttick % 3)
962 {
963 case 0:
964 mixer_setamigaperiod(c,
965 modplayer.periodtable[
966 p_modchannel->periodtableoffset]);
967 break;
968 case 1:
969 newperiodtableoffset =
970 p_modchannel->periodtableoffset+(effectx<<3);
971 if (newperiodtableoffset < 37*8)
972 mixer_setamigaperiod(c,
973 modplayer.periodtable[
974 newperiodtableoffset]);
975 break;
976 case 2:
977 newperiodtableoffset =
978 p_modchannel->periodtableoffset+(effecty<<3);
979 if (newperiodtableoffset < 37*8)
980 mixer_setamigaperiod(c,
981 modplayer.periodtable[
982 newperiodtableoffset]);
983 break;
984 }
985 }
986 break;
987
988 /* Effect 1: Slide Up */
989 case 0x01:
990 mixer_setamigaperiod(c,
991 p_modchannel->period -= p_modchannel->slideupspeed);
992 /* Find out the new offset in the period table */
993 if (p_modchannel->periodtableoffset <= 37*8)
994 while (modplayer.periodtable[
995 p_modchannel->periodtableoffset] >
996 p_modchannel->period)
997 {
998 p_modchannel->periodtableoffset++;
999 /* Make sure we don't go out of range */
1000 if (p_modchannel->periodtableoffset > 37*8)
1001 {
1002 p_modchannel->periodtableoffset = 37*8;
1003 break;
1004 }
1005 }
1006 break;
1007
1008 /* Effect 2: Slide Down */
1009 case 0x02:
1010 mixer_setamigaperiod(c, p_modchannel->period +=
1011 p_modchannel->slidedownspeed);
1012 /* Find out the new offset in the period table */
1013 if (p_modchannel->periodtableoffset > 8)
1014 while (modplayer.periodtable[
1015 p_modchannel->periodtableoffset] <
1016 p_modchannel->period)
1017 {
1018 p_modchannel->periodtableoffset--;
1019 /* Make sure we don't go out of range */
1020 if (p_modchannel->periodtableoffset < 1)
1021 {
1022 p_modchannel->periodtableoffset = 1;
1023 break;
1024 }
1025 }
1026 break;
1027
1028 /* Effect 3: Slide to Note */
1029 case 0x03:
1030 /* Apply smooth sliding, if no glissando is enabled */
1031 if (modplayer.glissandoenabled == 0)
1032 slidetonote(c);
1033 break;
1034
1035 /* Effect 4: Vibrato */
1036 case 0x04:
1037 vibrate(c);
1038 break;
1039
1040 /* Effect 5: Continue effect 3:'Slide to note',
1041 * but also do Volume slide */
1042 case 0x05:
1043 slidetonote(c);
1044 volumeslide(c, effectx, effecty);
1045 break;
1046
1047 /* Effect 6: Continue effect 4:'Vibrato',
1048 * but also do Volume slide */
1049 case 0x06:
1050 vibrate(c);
1051 volumeslide(c, effectx, effecty);
1052 break;
1053
1054 /* Effect 7: Tremolo */
1055 case 0x07:
1056 tremolo(c);
1057 break;
1058
1059 /* Effect 8 (Set fine panning) is only processed at tick 0 */
1060 /* Effect 9 (Set sample offset) is only processed at tick 0 */
1061
1062 /* Effect A: Volume slide */
1063 case 0x0a:
1064 volumeslide(c, effectx, effecty);
1065 break;
1066
1067 /* Effect B (Position jump) is only processed at tick 0 */
1068 /* Effect C (Set Volume) is only processed at tick 0 */
1069 /* Effect D (Pattern Preak) is only processed at tick 0 */
1070 /* Effect E (Enhanced Effect) */
1071 case 0x0e:
1072 switch (effectx)
1073 {
1074 /* Retrigger sample ($E9 + Tick to Retrig note at) */
1075 case 0x9:
1076 /* Don't device by zero */
1077 if (effecty == 0) effecty = 1;
1078 /* Apply retrig */
1079 if (currenttick % effecty == 0)
1080 mixer_playsample(c, p_modchannel->instrument);
1081 break;
1082 /* Cut note (Usage: $EC + Tick to Cut note at) */
1083 case 0xc:
1084 if (currenttick == effecty)
1085 mixer_stopsample(c);
1086 break;
1087 /* Delay note (Usage: $ED + ticks to delay note) */
1088 case 0xd:
1089 /* If this is the correct tick,
1090 * we start playing the sample now */
1091 if (currenttick == effecty)
1092 mixer.channel[c].channelactive = true;
1093 break;
1094
1095 }
1096 break;
1097 /* Effect F (Set Speed) is only processed at tick 0 */
1098
1099 }
1100 }
1101}
1102
1103inline int clip(int i)
1104{
1105 if (i > 32767) return(32767);
1106 else if (i < -32768) return(-32768);
1107 else return(i);
1108}
1109
1110STATICIRAM void synthrender(void *renderbuffer, int samplecount) ICODE_ATTR;
1111void synthrender(void *renderbuffer, int samplecount)
1112{
1113 /* 125bpm equals to 50Hz (= 0.02s)
1114 * => one tick = mixingrate/50,
1115 * samples passing in one tick:
1116 * mixingrate/(bpm/2.5) = 2.5*mixingrate/bpm */
1117
1118 int *p_left = (int *) renderbuffer; /* int in rockbox */
1119 int *p_right = p_left+1;
1120 signed short s;
1121 int qf_distance, qf_distance2;
1122
1123 int i;
1124
1125 int c, left, right;
1126
1127 for (i=0;i<samplecount;i++)
1128 {
1129 /* New Tick? */
1130 if ((modplayer.samplespertick-- <= 0) &&
1131 (modplayer.patterntableposition < 127))
1132 {
1133 if (modplayer.currenttick == 0)
1134 playline(modsong.patternordertable[
1135 modplayer.patterntableposition], modplayer.currentline);
1136 else playeffect(modplayer.currenttick);
1137
1138 modplayer.currenttick++;
1139
1140 if (modplayer.currenttick >= modplayer.ticksperline)
1141 {
1142 modplayer.currentline++;
1143 modplayer.currenttick = 0;
1144 if (modplayer.currentline == 64)
1145 {
1146 modplayer.patterntableposition++;
1147 if (modplayer.patterntableposition >= modsong.songlength)
1148 /* This is for Noise Tracker
1149 * modplayer.patterntableposition =
1150 * modsong.songendjumpposition;
1151 * More compatible approach is restart from 0 */
1152 modplayer.patterntableposition=0;
1153 modplayer.currentline = 0;
1154 }
1155 }
1156
1157 modplayer.samplespertick = (20*mixingrate/modplayer.bpm)>>3;
1158 }
1159 /* Mix buffers from here
1160 * Walk through all channels */
1161 left=0, right=0;
1162
1163 /* If song has not stopped playing */
1164 if (modplayer.patterntableposition < 127)
1165 /* Loop through all channels */
1166 for (c=0;c<modsong.noofchannels;c++)
1167 {
1168 /* Only mix the sample,
1169 * if channel there is something played on the channel */
1170 if (!mixer.channel[c].channelactive) continue;
1171
1172 /* Loop the sample, if requested? */
1173 if (mixer.channel[c].samplepos >= mixer.channel[c].loopend)
1174 {
1175 if (mixer.channel[c].loopsample)
1176 mixer.channel[c].samplepos -=
1177 (mixer.channel[c].loopend-
1178 mixer.channel[c].loopstart);
1179 else mixer.channel[c].channelactive = false;
1180 }
1181
1182 /* If the sample has stopped playing don't mix it */
1183 if (!mixer.channel[c].channelactive) continue;
1184
1185 /* Get the sample */
1186 s = (signed short)(modsong.sampledata[
1187 mixer.channel[c].samplepos]*mixer.channel[c].volume);
1188
1189 /* Interpolate if the sample-frequency is lower
1190 * than the mixing rate
1191 * If you don't want interpolation simply skip this part */
1192 if (mixer.channel[c].frequency < mixingrate)
1193 {
1194 /* Low precision linear interpolation
1195 * (fast integer based) */
1196 qf_distance = mixer.channel[c].samplefractpos<<16 /
1197 mixingrate;
1198 qf_distance2 = (1<<16)-qf_distance;
1199 s = (qf_distance*s + qf_distance2*
1200 mixer.channel[c].lastsampledata)>>16;
1201 }
1202
1203 /* Save the last played sample for interpolation purposes */
1204 mixer.channel[c].lastsampledata = s;
1205
1206 /* Pan the sample */
1207 left += s*(16-mixer.channel[c].panning)>>3;
1208 right += s*mixer.channel[c].panning>>3;
1209
1210 /* Advance sample */
1211 mixer.channel[c].samplefractpos += mixer.channel[c].frequency;
1212 while (mixer.channel[c].samplefractpos > mixingrate)
1213 {
1214 mixer.channel[c].samplefractpos -= mixingrate;
1215 mixer.channel[c].samplepos++;
1216 }
1217 }
1218 /* If we have more than 4 channels
1219 * we have to make sure that we apply clipping */
1220 if (modsong.noofchannels > 4) {
1221 *p_left = clip(left)<<13;
1222 *p_right = clip(right)<<13;
1223 }
1224 else {
1225 *p_left = left<<13;
1226 *p_right = right<<13;
1227 }
1228 p_left+=2;
1229 p_right+=2;
1230 }
1231}
1232
1233
1234enum codec_status codec_main(void)
1235{
1236 size_t n;
1237 unsigned char *modfile;
1238 int old_patterntableposition;
1239
1240 int bytesdone;
1241
1242 ci->configure(CODEC_SET_FILEBUF_WATERMARK, 1024*512);
1243
1244
1245next_track:
1246 if (codec_init()) {
1247 return CODEC_ERROR;
1248 }
1249
1250 while (!*ci->taginfo_ready && !ci->stop_codec)
1251 ci->sleep(1);
1252
1253 codec_set_replaygain(ci->id3);
1254
1255 /* Load MOD file */
1256 /*
1257 * This is the save way
1258
1259 size_t bytesfree;
1260 unsigned int filesize;
1261
1262 p = modfile;
1263 bytesfree=sizeof(modfile);
1264 while ((n = ci->read_filebuf(p, bytesfree)) > 0) {
1265 p += n;
1266 bytesfree -= n;
1267 if (bytesfree == 0)
1268 return CODEC_ERROR;
1269 }
1270 filesize = p-modfile;
1271
1272 if (filesize == 0)
1273 return CODEC_ERROR;
1274 */
1275
1276 /* Directly use mod in buffer */
1277 ci->seek_buffer(0);
1278 modfile = ci->request_buffer(&n, ci->filesize);
1279 if (!modfile || n < (size_t)ci->filesize) {
1280 return CODEC_ERROR;
1281 }
1282
1283 initmodplayer();
1284 loadmod(modfile);
1285
1286 /* Make use of 44.1khz */
1287 ci->configure(DSP_SET_FREQUENCY, 44100);
1288 /* Sample depth is 28 bit host endian */
1289 ci->configure(DSP_SET_SAMPLE_DEPTH, 28);
1290 /* Stereo output */
1291 ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED);
1292
1293 /* The main decoder loop */
1294 ci->set_elapsed(0);
1295 bytesdone = 0;
1296 old_patterntableposition = 0;
1297
1298 while (1) {
1299 ci->yield();
1300 if (ci->stop_codec || ci->new_track)
1301 break;
1302
1303 if (ci->seek_time) {
1304 /* New time is ready in ci->seek_time */
1305 modplayer.patterntableposition = ci->seek_time/1000;
1306 modplayer.currentline = 0;
1307 ci->seek_complete();
1308 }
1309
1310 if(old_patterntableposition != modplayer.patterntableposition) {
1311 ci->set_elapsed(modplayer.patterntableposition*1000+500);
1312 old_patterntableposition=modplayer.patterntableposition;
1313 }
1314
1315 synthrender(samples, CHUNK_SIZE/2);
1316
1317 bytesdone += CHUNK_SIZE;
1318
1319 ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE/2);
1320
1321 }
1322
1323 if (ci->request_next_track())
1324 goto next_track;
1325
1326 return CODEC_OK;
1327}
diff --git a/apps/filetypes.c b/apps/filetypes.c
index ead2295f70..b884102ed7 100644
--- a/apps/filetypes.c
+++ b/apps/filetypes.c
@@ -68,6 +68,7 @@ const struct filetype inbuilt_filetypes[] = {
68 { "m4a", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, 68 { "m4a", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
69 { "m4b", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, 69 { "m4b", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
70 { "mp4", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, 70 { "mp4", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
71 { "mod", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
71 { "shn", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, 72 { "shn", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
72 { "aif", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, 73 { "aif", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
73 { "aiff",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, 74 { "aiff",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
diff --git a/apps/metadata.c b/apps/metadata.c
index 7b3b314d74..a8821b29df 100644
--- a/apps/metadata.c
+++ b/apps/metadata.c
@@ -184,6 +184,14 @@ bool get_metadata(struct mp3entry* id3, int fd, const char* trackname)
184 184
185 break; 185 break;
186 186
187 case AFMT_MOD:
188 if (!get_mod_metadata(fd, id3))
189 {
190 return false;
191 }
192
193 break;
194
187 case AFMT_SHN: 195 case AFMT_SHN:
188 id3->vbr = true; 196 id3->vbr = true;
189 id3->filesize = filesize(fd); 197 id3->filesize = filesize(fd);
diff --git a/apps/metadata/metadata_parsers.h b/apps/metadata/metadata_parsers.h
index c3265f8a43..b34d09fe4c 100644
--- a/apps/metadata/metadata_parsers.h
+++ b/apps/metadata/metadata_parsers.h
@@ -25,6 +25,7 @@ bool get_mp4_metadata(int fd, struct mp3entry* id3);
25bool get_monkeys_metadata(int fd, struct mp3entry* id3); 25bool get_monkeys_metadata(int fd, struct mp3entry* id3);
26bool get_musepack_metadata(int fd, struct mp3entry *id3); 26bool get_musepack_metadata(int fd, struct mp3entry *id3);
27bool get_sid_metadata(int fd, struct mp3entry* id3); 27bool get_sid_metadata(int fd, struct mp3entry* id3);
28bool get_mod_metadata(int fd, struct mp3entry* id3);
28bool get_spc_metadata(int fd, struct mp3entry* id3); 29bool get_spc_metadata(int fd, struct mp3entry* id3);
29bool get_ogg_metadata(int fd, struct mp3entry* id3); 30bool get_ogg_metadata(int fd, struct mp3entry* id3);
30bool get_wave_metadata(int fd, struct mp3entry* id3); 31bool get_wave_metadata(int fd, struct mp3entry* id3);
diff --git a/apps/metadata/mod.c b/apps/metadata/mod.c
new file mode 100644
index 0000000000..0205fd04bf
--- /dev/null
+++ b/apps/metadata/mod.c
@@ -0,0 +1,62 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 Dave Chapman
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#include <stdio.h>
20#include <string.h>
21#include <stdlib.h>
22#include <ctype.h>
23#include <inttypes.h>
24
25#include "system.h"
26#include "id3.h"
27#include "metadata_common.h"
28#include "rbunicode.h"
29
30bool get_mod_metadata(int fd, struct mp3entry* id3)
31{
32 /* Use the trackname part of the id3 structure as a temporary buffer */
33 unsigned char buf[1084];
34 int read_bytes;
35 char *p;
36
37
38 if ((lseek(fd, 0, SEEK_SET) < 0)
39 || ((read_bytes = read(fd, buf, sizeof(buf))) < 1084))
40 {
41 return false;
42 }
43
44 /* We don't do file format checking here
45 * There can be .mod files without any signatures out there */
46
47 p = id3->id3v2buf;
48
49 /* Copy Title as artist */
50 strcpy(p, &buf[0x00]);
51 id3->artist = p;
52 p += strlen(p)+1;
53
54 id3->bitrate = filesize(fd)/1024; /* size in kb */
55 id3->frequency = 44100;
56 id3->length = 120*1000;
57 id3->vbr = false;
58 id3->filesize = filesize(fd);
59
60 return true;
61}
62
diff --git a/firmware/export/id3.h b/firmware/export/id3.h
index 8aaf76f73c..921e91cdd5 100644
--- a/firmware/export/id3.h
+++ b/firmware/export/id3.h
@@ -57,6 +57,7 @@ enum
57 AFMT_SPC, /* SPC700 save state */ 57 AFMT_SPC, /* SPC700 save state */
58 AFMT_APE, /* Monkey's Audio (APE) */ 58 AFMT_APE, /* Monkey's Audio (APE) */
59 AFMT_WMA, /* WMAV1/V2 in ASF */ 59 AFMT_WMA, /* WMAV1/V2 in ASF */
60 AFMT_MOD, /* Amiga MOD File Format */
60#endif 61#endif
61 62
62 /* add new formats at any index above this line to have a sensible order - 63 /* add new formats at any index above this line to have a sensible order -
diff --git a/firmware/id3.c b/firmware/id3.c
index 0e5c05ca27..2604daf1f7 100644
--- a/firmware/id3.c
+++ b/firmware/id3.c
@@ -112,6 +112,9 @@ const struct afmt_entry audio_formats[AFMT_NUM_CODECS] =
112 /* WMA (WMAV1/V2 in ASF) */ 112 /* WMA (WMAV1/V2 in ASF) */
113 [AFMT_WMA] = 113 [AFMT_WMA] =
114 AFMT_ENTRY("WMA", "wma", NULL, "wma\0wmv\0asf\0" ), 114 AFMT_ENTRY("WMA", "wma", NULL, "wma\0wmv\0asf\0" ),
115 /* Amiga MOD File */
116 [AFMT_MOD] =
117 AFMT_ENTRY("MOD", "mod", NULL, "mod\0" ),
115#endif 118#endif
116}; 119};
117 120