fix FS#9042 - MOD codec: SoundTracker variant MOD files will not play
[Rockbox.git] / apps / codecs / mod.c
blob7bc87c8a420ebedd6bf81eda2643ce068ed1362d
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * MOD Codec for rockbox
12 * Written from scratch by Rainer Sinsch
13 * exclusivly for Rockbox in February 2008
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
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 *************/
31 #include "debug.h"
32 #include "codeclib.h"
33 #include <inttypes.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <ctype.h>
41 CODEC_HEADER
43 #define CHUNK_SIZE (1024*2)
46 /* This codec supports MOD Files:
50 static int32_t samples[CHUNK_SIZE] IBSS_ATTR; /* The sample buffer */
52 /* Instrument Data */
53 struct s_instrument {
54 /* Sample name / description */
55 /*char description[22];*/
57 /* Sample length in bytes */
58 unsigned short length;
60 /* Sample finetuning (-8 - +7) */
61 signed char finetune;
63 /* Sample volume (0 - 64) */
64 signed char volume;
66 /* Sample Repeat Position */
67 unsigned short repeatoffset;
69 /* Sample Repeat Length */
70 unsigned short repeatlength;
72 /* Offset to sample data */
73 unsigned int sampledataoffset;
76 /* Song Data */
77 struct s_song {
78 /* Song name / title description */
79 /*char szTitle[20];*/
81 /* No. of channels in song */
82 unsigned char noofchannels;
84 /* No. of instruments used (either 15 or 31) */
85 unsigned char noofinstruments;
87 /* How many patterns are beeing played? */
88 unsigned char songlength;
90 /* Where to jump after the song end? */
91 unsigned char songendjumpposition;
93 /* Pointer to the Pattern Order Table */
94 unsigned char *patternordertable;
96 /* Pointer to the pattern data */
97 void *patterndata;
99 /* Pointer to the sample buffer */
100 signed char *sampledata;
102 /* Instrument data */
103 struct s_instrument instrument[31];
106 struct s_modchannel {
107 /* Current Volume */
108 signed char volume;
110 /* Current Offset to period in PeriodTable of notebeeing played
111 (can be temporarily negative) */
112 short periodtableoffset;
114 /* Current Period beeing played */
115 short period;
117 /* Current effect */
118 unsigned char effect;
120 /* Current parameters of effect */
121 unsigned char effectparameter;
123 /* Current Instrument beeing played */
124 unsigned char instrument;
126 /* Current Vibrato Speed */
127 unsigned char vibratospeed;
129 /* Current Vibrato Depth */
130 unsigned char vibratodepth;
132 /* Current Position for Vibrato in SinTable */
133 unsigned char vibratosinpos;
135 /* Current Tremolo Speed */
136 unsigned char tremolospeed;
138 /* Current Tremolo Depth */
139 unsigned char tremolodepth;
141 /* Current Position for Tremolo in SinTable */
142 unsigned char tremolosinpos;
144 /* Current Speed of Effect "Slide Note up" */
145 unsigned char slideupspeed;
147 /* Current Speed of Effect "Slide Note down" */
148 unsigned char slidedownspeed;
150 /* Current Speed of the "Slide to Note" effect */
151 unsigned char slidetonotespeed;
153 /* Current Period of the "Slide to Note" effect */
154 unsigned short slidetonoteperiod;
157 struct s_modplayer {
158 /* Ticks per Line */
159 unsigned char ticksperline;
161 /* Beats per Minute */
162 unsigned char bpm;
164 /* Position of the Song in the Pattern Table (0-127) */
165 unsigned char patterntableposition;
167 /* Current Line (may be temporarily < 0) */
168 signed char currentline;
170 /* Current Tick */
171 signed char currenttick;
173 /* How many samples are required to calculate for each tick? */
174 unsigned int samplespertick;
176 /* Information about the channels */
177 struct s_modchannel modchannel[8];
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];
183 /* The sinus table [-255,255] */
184 signed short sintable[0x40];
186 /* Is the glissando effect enabled? */
187 bool glissandoenabled;
189 /* Is the Amiga Filter enabled? */
190 bool amigafilterenabled;
192 /* The pattern-line where the loop is carried out (set with e6 command) */
193 unsigned char loopstartline;
195 /* Number of times to loop */
196 unsigned char looptimes;
199 struct s_channel {
200 /* Panning (0 = left, 16 = right) */
201 unsigned char panning;
203 /* Sample frequency of the channel */
204 unsigned short frequency;
206 /* Position of the sample currently played */
207 unsigned int samplepos;
209 /* Fractual Position of the sample currently player */
210 unsigned int samplefractpos;
212 /* Loop Sample */
213 bool loopsample;
215 /* Loop Position Start */
216 unsigned int loopstart;
218 /* Loop Position End */
219 unsigned int loopend;
221 /* Is The channel beeing played? */
222 bool channelactive;
224 /* The Volume (0..64) */
225 signed char volume;
227 /* The last sampledata beeing played (required for interpolation) */
228 signed short lastsampledata;
231 struct s_mixer {
232 /* The channels */
233 struct s_channel channel[32];
236 struct s_song modsong IDATA_ATTR; /* The Song */
237 struct s_modplayer modplayer IDATA_ATTR; /* The Module Player */
238 struct s_mixer mixer IDATA_ATTR;
240 const unsigned short mixingrate = 44100;
242 STATICIRAM void mixer_playsample(int channel, int instrument) ICODE_ATTR;
243 void mixer_playsample(int channel, int instrument)
245 struct s_channel *p_channel = &mixer.channel[channel];
246 struct s_instrument *p_instrument = &modsong.instrument[instrument];
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;
258 else p_channel->loopend = p_instrument->length +
259 p_instrument->sampledataoffset;
261 /* Remember the instrument */
262 modplayer.modchannel[channel].instrument = instrument;
265 inline void mixer_stopsample(int channel)
267 mixer.channel[channel].channelactive = false;
270 inline void mixer_continuesample(int channel)
272 mixer.channel[channel].channelactive = true;
275 inline void mixer_setvolume(int channel, int volume)
277 mixer.channel[channel].volume = volume;
280 inline void mixer_setpanning(int channel, int panning)
282 mixer.channel[channel].panning = panning;
285 inline void mixer_setamigaperiod(int channel, int amigaperiod)
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;
293 mixer.channel[channel].frequency = 3579546 / amigaperiod;
296 /* Initialize the MOD Player with default values and precalc tables */
297 STATICIRAM void initmodplayer(void) ICODE_ATTR;
298 void initmodplayer(void)
300 unsigned int i,c;
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++)
309 modplayer.periodtable[i] = (unsigned short) f;
310 f /= 1.0072464122237039; /* = pow(2.0f, 1.0f/(12.0f*8.0f)); */
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++)
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);
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]
334 float a, b, d, dd;
336 d = 0.09817475f; /* = 2*PI/64 */
337 dd = d*d;
338 a = 0;
339 b = d;
341 for (i=0;i<0x40;i++)
343 modplayer.sintable[i] = (int)(255*a);
345 a = a+b;
346 b = b-dd*a;
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 */
358 /* Default Panning Values */
359 int panningvalues[8] = {4,12,12,4,4,12,12,4};
360 for (c=0;c<8;c++)
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;
372 /* Load the MOD File from memory */
373 STATICIRAM bool loadmod(void *modfile) ICODE_ATTR;
374 bool loadmod(void *modfile)
376 int i;
377 unsigned char *periodsconverted;
379 /* We don't support PowerPacker 2.0 Files */
380 if (memcmp((char*) modfile, "PP20", 4) == 0) return false;
382 /* Get the File Format Tag */
383 char *fileformattag = (char*)modfile + 1080;
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 {
405 /* The file has no format tag, so most likely soundtracker */
406 modsong.noofchannels = 4;
407 modsong.noofinstruments = 15;
410 /* Get the Song title
411 * Skipped here
412 * strncpy(modsong.szTitle, (char*)pMODFile, 20); */
414 /* Get the Instrument information */
415 for (i=0;i<modsong.noofinstruments;i++)
417 struct s_instrument *instrument = &modsong.instrument[i];
418 unsigned char *p = (unsigned char *)modfile + 20 + i*30;
420 /*strncpy(instrument->description, (char*)p, 22); */
421 p += 22;
422 instrument->length = (((p[0])<<8) + p[1]) << 1; p+=2;
423 instrument->finetune = *p++ & 0x0f;
424 /* Treat finetuning as signed nibble */
425 if (instrument->finetune > 7) instrument->finetune -= 16;
426 instrument->volume = *p++;
427 instrument->repeatoffset = (((p[0])<<8) + p[1]) << 1; p+= 2;
428 instrument->repeatlength = (((p[0])<<8) + p[1]) << 1;
431 /* Get the pattern information */
432 unsigned char *p = (unsigned char *)modfile + 20 +
433 modsong.noofinstruments*30;
434 modsong.songlength = *p++;
435 modsong.songendjumpposition = *p++;
436 modsong.patternordertable = p;
438 /* Find out how many patterns are used within this song */
439 int maxpatterns = 0;
440 for (i=0;i<128;i++)
441 if (modsong.patternordertable[i] > maxpatterns)
442 maxpatterns = modsong.patternordertable[i];
443 maxpatterns++;
445 /* use 'restartposition' (historically set to 127) which is not used here
446 as a marker that periods have already been converted */
448 periodsconverted = (char*)modfile + 20 + modsong.noofinstruments*30 + 1;
450 /* Get the pattern data; ST doesn't have fileformattag, so 4 bytes less */
451 modsong.patterndata = periodsconverted +
452 (modsong.noofinstruments==15 ? 129 : 133);
454 /* Convert the period values in the mod file to offsets
455 * in our periodtable (but only, if we haven't done this yet) */
456 p = (unsigned char *) modsong.patterndata;
457 if (*periodsconverted != 0xfe)
459 int note, note2, channel;
460 for (note=0;note<maxpatterns*64;note++)
461 for (channel=0;channel<modsong.noofchannels;channel++)
463 int period = ((p[0] & 0x0f) << 8) | p[1];
464 int periodoffset = 0;
466 /* Find the offset of the current period */
467 for (note2 = 1; note2 < 12*3+1; note2++)
468 if (abs(modplayer.periodtable[note2*8+1]-period) < 4)
470 periodoffset = note2*8+1;
471 break;
473 /* Write back the period offset */
474 p[0] = (periodoffset >> 8) | (p[0] & 0xf0);
475 p[1] = periodoffset & 0xff;
476 p += 4;
478 /* Remember that we already converted the periods,
479 * in case the file gets reloaded by rewinding
480 * with 0xfe (arbitary magic value > 127) */
481 *periodsconverted = 0xfe;
484 /* Get the samples
485 * Calculation: The Samples come after the pattern data
486 * We know that there are nMaxPatterns and each pattern requires
487 * 4 bytes per note and per channel.
488 * And of course there are always lines in each channel */
489 modsong.sampledata = (signed char*) modsong.patterndata +
490 maxpatterns*4*modsong.noofchannels*64;
491 int sampledataoffset = 0;
492 for (i=0;i<modsong.noofinstruments;i++)
494 modsong.instrument[i].sampledataoffset = sampledataoffset;
495 sampledataoffset += modsong.instrument[i].length;
498 return true;
501 /* Apply vibrato to channel */
502 STATICIRAM void vibrate(int channel) ICODE_ATTR;
503 void vibrate(int channel)
505 struct s_modchannel *p_modchannel = &modplayer.modchannel[channel];
507 /* Apply Vibrato
508 * >> 7 is used in the original protracker source code */
509 mixer_setamigaperiod(channel, p_modchannel->period+
510 ((p_modchannel->vibratodepth *
511 modplayer.sintable[p_modchannel->vibratosinpos])>>7));
513 /* Foward in Sine Table */
514 p_modchannel->vibratosinpos += p_modchannel->vibratospeed;
515 p_modchannel->vibratosinpos &= 0x3f;
518 /* Apply tremolo to channel
519 * (same as vibrato, but only apply on volume instead of pitch) */
520 STATICIRAM void tremolo(int channel) ICODE_ATTR;
521 void tremolo(int channel)
523 struct s_modchannel *p_modchannel = &modplayer.modchannel[channel];
525 /* Apply Tremolo
526 * >> 6 is used in the original protracker source code */
527 int volume = (p_modchannel->volume *
528 modplayer.sintable[p_modchannel->tremolosinpos])>>6;
529 if (volume > 64) volume = 64;
530 else if (volume < 0) volume = 0;
531 mixer_setvolume(channel, volume);
533 /* Foward in Sine Table */
534 p_modchannel->tremolosinpos += p_modchannel->tremolosinpos;
535 p_modchannel->tremolosinpos &= 0x3f;
538 /* Apply Slide to Note effect to channel */
539 STATICIRAM void slidetonote(int channel) ICODE_ATTR;
540 void slidetonote(int channel)
542 struct s_modchannel *p_modchannel = &modplayer.modchannel[channel];
544 /* If there hasn't been any slide-to note set up, then return */
545 if (p_modchannel->slidetonoteperiod == 0) return;
547 /* Slide note up */
548 if (p_modchannel->slidetonoteperiod > p_modchannel->period)
550 p_modchannel->period += p_modchannel->slidetonotespeed;
551 if (p_modchannel->period > p_modchannel->slidetonoteperiod)
552 p_modchannel->period = p_modchannel->slidetonoteperiod;
554 /* Slide note down */
555 else if (p_modchannel->slidetonoteperiod < p_modchannel->period)
557 p_modchannel->period -= p_modchannel->slidetonotespeed;
558 if (p_modchannel->period < p_modchannel->slidetonoteperiod)
559 p_modchannel->period = p_modchannel->slidetonoteperiod;
561 mixer_setamigaperiod(channel, p_modchannel->period);
564 /* Apply Slide to Note effect on channel,
565 * but this time with glissando enabled */
566 STATICIRAM void slidetonoteglissando(int channel) ICODE_ATTR;
567 void slidetonoteglissando(int channel)
569 struct s_modchannel *p_modchannel = &modplayer.modchannel[channel];
571 /* Slide note up */
572 if (p_modchannel->slidetonoteperiod > p_modchannel->period)
574 p_modchannel->period =
575 modplayer.periodtable[p_modchannel->periodtableoffset+=8];
576 if (p_modchannel->period > p_modchannel->slidetonoteperiod)
577 p_modchannel->period = p_modchannel->slidetonoteperiod;
579 /* Slide note down */
580 else
582 p_modchannel->period =
583 modplayer.periodtable[p_modchannel->periodtableoffset-=8];
584 if (p_modchannel->period < p_modchannel->slidetonoteperiod)
585 p_modchannel->period = p_modchannel->slidetonoteperiod;
587 mixer_setamigaperiod(channel, p_modchannel->period);
590 /* Apply Volume Slide */
591 STATICIRAM void volumeslide(int channel, int effectx, int effecty) ICODE_ATTR;
592 void volumeslide(int channel, int effectx, int effecty)
594 struct s_modchannel *p_modchannel = &modplayer.modchannel[channel];
596 /* If both X and Y Parameters are non-zero, then the y value is ignored */
597 if (effectx > 0) {
598 p_modchannel->volume += effectx;
599 if (p_modchannel->volume > 64) p_modchannel->volume = 64;
601 else {
602 p_modchannel->volume -= effecty;
603 if (p_modchannel->volume < 0) p_modchannel->volume = 0;
606 mixer_setvolume(channel, p_modchannel->volume);
609 /* Play the current line (at tick 0) */
610 STATICIRAM void playline(int pattern, int line) ICODE_ATTR;
611 void playline(int pattern, int line)
613 int c;
615 /* Get pointer to the current pattern */
616 unsigned char *p_line = (unsigned char*)modsong.patterndata;
617 p_line += pattern*64*4*modsong.noofchannels;
618 p_line += line*4*modsong.noofchannels;
620 /* Only allow one Patternbreak Commando per Line */
621 bool patternbreakdone = false;
623 for (c=0;c<modsong.noofchannels;c++)
625 struct s_modchannel *p_modchannel = &modplayer.modchannel[c];
626 unsigned char *p_note = p_line + c*4;
627 unsigned char samplenumber = (p_note[0] & 0xf0) | (p_note[2] >> 4);
628 short periodtableoffset = ((p_note[0] & 0x0f) << 8) | p_note[1];
630 p_modchannel->effect = p_note[2] & 0x0f;
631 p_modchannel->effectparameter = p_note[3];
633 /* Remember Instrument and set Volume if new Instrument triggered */
634 if (samplenumber > 0)
636 /* And trigger new sample, if new instrument was set */
637 if (samplenumber-1 != p_modchannel->instrument)
639 /* Advance the new sample to the same offset
640 * the old sample was beeing played */
641 int oldsampleoffset = mixer.channel[c].samplepos -
642 modsong.instrument[
643 p_modchannel->instrument].sampledataoffset;
644 mixer_playsample(c, samplenumber-1);
645 mixer.channel[c].samplepos += oldsampleoffset;
648 /* Remember last played instrument on channel */
649 p_modchannel->instrument = samplenumber-1;
651 /* Set Volume to standard instrument volume,
652 * if not overwritten by volume effect */
653 if (p_modchannel->effect != 0x0c)
655 p_modchannel->volume = modsong.instrument[
656 p_modchannel->instrument].volume;
657 mixer_setvolume(c, p_modchannel->volume);
660 /* Trigger new sample if note available */
661 if (periodtableoffset > 0)
663 /* Restart instrument only when new sample triggered */
664 if (samplenumber != 0)
665 mixer_playsample(c, (samplenumber > 0) ?
666 samplenumber-1 : p_modchannel->instrument);
668 /* Set the new amiga period
669 * (but only, if there is no slide to note effect) */
670 if ((p_modchannel->effect != 0x3) &&
671 (p_modchannel->effect != 0x5))
673 /* Apply finetuning to sample */
674 p_modchannel->periodtableoffset = periodtableoffset +
675 modsong.instrument[p_modchannel->instrument].finetune;
676 p_modchannel->period = modplayer.periodtable[
677 p_modchannel->periodtableoffset];
678 mixer_setamigaperiod(c, p_modchannel->period);
679 /* When a new note is played without slide to note setup,
680 * then disable slide to note */
681 modplayer.modchannel[c].slidetonoteperiod =
682 p_modchannel->period;
685 int effectx = p_modchannel->effectparameter>>4;
686 int effecty = p_modchannel->effectparameter&0x0f;
688 switch (p_modchannel->effect)
690 /* Effect 0: Arpeggio */
691 case 0x00:
692 /* Set the base period on tick 0 */
693 if (p_modchannel->effectparameter > 0)
694 mixer_setamigaperiod(c,
695 modplayer.periodtable[
696 p_modchannel->periodtableoffset]);
697 break;
698 /* Slide up (Portamento up) */
699 case 0x01:
700 if (p_modchannel->effectparameter > 0)
701 p_modchannel->slideupspeed =
702 p_modchannel->effectparameter;
703 break;
705 /* Slide down (Portamento down) */
706 case 0x02:
707 if (p_modchannel->effectparameter > 0)
708 p_modchannel->slidedownspeed =
709 p_modchannel->effectparameter;
710 break;
712 /* Slide to Note */
713 case 0x03:
714 if (p_modchannel->effectparameter > 0)
715 p_modchannel->slidetonotespeed =
716 p_modchannel->effectparameter;
717 /* Get the slide to note directly from the pattern buffer */
718 if (periodtableoffset > 0)
719 p_modchannel->slidetonoteperiod =
720 modplayer.periodtable[periodtableoffset +
721 modsong.instrument[
722 p_modchannel->instrument].finetune];
723 /* If glissando is enabled apply the effect directly here */
724 if (modplayer.glissandoenabled)
725 slidetonoteglissando(c);
726 break;
728 /* Set Vibrato */
729 case 0x04:
730 if (effectx > 0) p_modchannel->vibratospeed = effectx;
731 if (effecty > 0) p_modchannel->vibratodepth = effecty;
732 break;
734 /* Effect 0x06: Slide to note */
735 case 0x05:
736 /* Get the slide to note directly from the pattern buffer */
737 if (periodtableoffset > 0)
738 p_modchannel->slidetonoteperiod =
739 modplayer.periodtable[periodtableoffset +
740 modsong.instrument[
741 p_modchannel->instrument].finetune];
742 break;
744 /* Effect 0x06 is "Continue Effects" */
745 /* It is not processed on tick 0 */
746 case 0x06:
747 break;
749 /* Set Tremolo */
750 case 0x07:
751 if (effectx > 0) p_modchannel->tremolodepth = effectx;
752 if (effecty > 0) p_modchannel->tremolospeed = effecty;
753 break;
755 /* Set fine panning */
756 case 0x08:
757 /* Internal panning goes from 0..15
758 * Scale the fine panning value to that range */
759 mixer.channel[c].panning = p_modchannel->effectparameter>>4;
760 break;
762 /* Set Sample Offset */
763 case 0x09:
765 struct s_instrument *p_instrument =
766 &modsong.instrument[p_modchannel->instrument];
767 int sampleoffset = p_instrument->sampledataoffset;
768 if (sampleoffset > p_instrument->length)
769 sampleoffset = p_instrument->length;
770 /* Forward the new offset to the mixer */
771 mixer.channel[c].samplepos =
772 p_instrument->sampledataoffset +
773 (p_modchannel->effectparameter<<8);
774 mixer.channel[c].samplefractpos = 0;
775 break;
778 /* Effect 0x0a (Volume slide) is not processed on tick 0 */
780 /* Position Jump */
781 case 0x0b:
782 modplayer.currentline = -1;
783 modplayer.patterntableposition = (effectx<<4)+effecty;
784 break;
786 /* Set Volume */
787 case 0x0c:
788 p_modchannel->volume = p_modchannel->effectparameter;
789 mixer_setvolume(c, p_modchannel->volume);
790 break;
792 /* Pattern break */
793 case 0x0d:
794 modplayer.currentline = effectx*10 + effecty - 1;
795 if (!patternbreakdone)
797 patternbreakdone = true;
798 modplayer.patterntableposition++;
800 break;
802 /* Extended Effects */
803 case 0x0e:
804 switch (effectx)
806 /* Set Filter */
807 case 0x0:
808 modplayer.amigafilterenabled =
809 (effecty>0) ? false : true;
810 break;
811 /* Fineslide up */
812 case 0x1:
813 mixer_setamigaperiod(c, p_modchannel->period -=
814 effecty);
815 if (p_modchannel->period <
816 modplayer.periodtable[37*8]) p_modchannel->period = 100;
817 /* Find out the new offset in the period table */
818 if (p_modchannel->periodtableoffset < 36*8)
819 while (modplayer.periodtable[
820 p_modchannel->periodtableoffset+8] >= p_modchannel->period)
821 p_modchannel->periodtableoffset+=8;
822 break;
823 /* Fineslide down */
824 case 0x2:
825 mixer_setamigaperiod(c,
826 p_modchannel->period += effecty);
827 if (p_modchannel->periodtableoffset > 8)
828 while (modplayer.periodtable[
829 p_modchannel->periodtableoffset-8]
830 <= p_modchannel->period)
831 p_modchannel->periodtableoffset-=8;
832 break;
833 /* Set glissando on/off */
834 case 0x3:
835 modplayer.glissandoenabled =
836 (effecty > 0) ? true:false;
837 break;
838 /* Set Vibrato waveform */
839 case 0x4:
840 /* Currently not implemented */
841 break;
842 /* Set Finetune value */
843 case 0x5:
844 /* Treat as signed nibble */
845 if (effecty > 7) effecty -= 16;
847 p_modchannel->periodtableoffset +=
848 effecty -
849 modsong.instrument[
850 p_modchannel->instrument].finetune;
851 p_modchannel->period =
852 modplayer.periodtable[
853 p_modchannel->periodtableoffset];
854 modsong.instrument[
855 p_modchannel->instrument].finetune = effecty;
856 break;
857 /* Pattern loop */
858 case 0x6:
859 if (effecty == 0)
860 modplayer.loopstartline = line-1;
861 else
863 if (modplayer.looptimes == 0)
865 modplayer.currentline =
866 modplayer.loopstartline;
867 modplayer.looptimes = effecty;
869 else modplayer.looptimes--;
870 if (modplayer.looptimes > 0)
871 modplayer.currentline =
872 modplayer.loopstartline;
874 break;
875 /* Set Tremolo waveform */
876 case 0x7:
877 /* Not yet implemented */
878 break;
879 /* Enhanced Effect 8 is not used */
880 case 0x8:
881 break;
882 /* Retrigger sample */
883 case 0x9:
884 /* Only processed on subsequent ticks */
885 break;
886 /* Fine volume slide up */
887 case 0xa:
888 p_modchannel->volume += effecty;
889 if (p_modchannel->volume > 64)
890 p_modchannel->volume = 64;
891 mixer_setvolume(c, p_modchannel->volume);
892 break;
893 /* Fine volume slide down */
894 case 0xb:
895 p_modchannel->volume -= effecty;
896 if (p_modchannel->volume < 0)
897 p_modchannel->volume = 0;
898 mixer_setvolume(c, p_modchannel->volume);
899 break;
900 /* Cut sample */
901 case 0xc:
902 /* Continue sample */
903 mixer_continuesample(c);
904 break;
905 /* Note delay (Usage: $ED + ticks to delay note.) */
906 case 0xd:
907 /* We stop the sample here on tick 0
908 * and restart it later in the effect */
909 if (effecty > 0)
910 mixer.channel[c].channelactive = false;
911 break;
913 break;
915 /* Set Speed */
916 case 0x0f:
917 if (p_modchannel->effectparameter < 32)
918 modplayer.ticksperline = p_modchannel->effectparameter;
919 else
920 modplayer.bpm = p_modchannel->effectparameter;
921 break;
926 /* Play the current effect of the note (ticks 1..speed) */
927 STATICIRAM void playeffect(int currenttick) ICODE_ATTR;
928 void playeffect(int currenttick)
930 int c;
932 for (c=0;c<modsong.noofchannels;c++)
934 struct s_modchannel *p_modchannel = &modplayer.modchannel[c];
936 /* If there is no note active then there are no effects to play */
937 if (p_modchannel->period == 0) continue;
939 unsigned char effectx = p_modchannel->effectparameter>>4;
940 unsigned char effecty = p_modchannel->effectparameter&0x0f;
942 switch (p_modchannel->effect)
944 /* Effect 0: Arpeggio */
945 case 0x00:
946 if (p_modchannel->effectparameter > 0)
948 unsigned short newperiodtableoffset;
949 switch (currenttick % 3)
951 case 0:
952 mixer_setamigaperiod(c,
953 modplayer.periodtable[
954 p_modchannel->periodtableoffset]);
955 break;
956 case 1:
957 newperiodtableoffset =
958 p_modchannel->periodtableoffset+(effectx<<3);
959 if (newperiodtableoffset < 37*8)
960 mixer_setamigaperiod(c,
961 modplayer.periodtable[
962 newperiodtableoffset]);
963 break;
964 case 2:
965 newperiodtableoffset =
966 p_modchannel->periodtableoffset+(effecty<<3);
967 if (newperiodtableoffset < 37*8)
968 mixer_setamigaperiod(c,
969 modplayer.periodtable[
970 newperiodtableoffset]);
971 break;
974 break;
976 /* Effect 1: Slide Up */
977 case 0x01:
978 mixer_setamigaperiod(c,
979 p_modchannel->period -= p_modchannel->slideupspeed);
980 /* Find out the new offset in the period table */
981 if (p_modchannel->periodtableoffset <= 37*8)
982 while (modplayer.periodtable[
983 p_modchannel->periodtableoffset] >
984 p_modchannel->period)
986 p_modchannel->periodtableoffset++;
987 /* Make sure we don't go out of range */
988 if (p_modchannel->periodtableoffset > 37*8)
990 p_modchannel->periodtableoffset = 37*8;
991 break;
994 break;
996 /* Effect 2: Slide Down */
997 case 0x02:
998 mixer_setamigaperiod(c, p_modchannel->period +=
999 p_modchannel->slidedownspeed);
1000 /* Find out the new offset in the period table */
1001 if (p_modchannel->periodtableoffset > 8)
1002 while (modplayer.periodtable[
1003 p_modchannel->periodtableoffset] <
1004 p_modchannel->period)
1006 p_modchannel->periodtableoffset--;
1007 /* Make sure we don't go out of range */
1008 if (p_modchannel->periodtableoffset < 1)
1010 p_modchannel->periodtableoffset = 1;
1011 break;
1014 break;
1016 /* Effect 3: Slide to Note */
1017 case 0x03:
1018 /* Apply smooth sliding, if no glissando is enabled */
1019 if (modplayer.glissandoenabled == 0)
1020 slidetonote(c);
1021 break;
1023 /* Effect 4: Vibrato */
1024 case 0x04:
1025 vibrate(c);
1026 break;
1028 /* Effect 5: Continue effect 3:'Slide to note',
1029 * but also do Volume slide */
1030 case 0x05:
1031 slidetonote(c);
1032 volumeslide(c, effectx, effecty);
1033 break;
1035 /* Effect 6: Continue effect 4:'Vibrato',
1036 * but also do Volume slide */
1037 case 0x06:
1038 vibrate(c);
1039 volumeslide(c, effectx, effecty);
1040 break;
1042 /* Effect 7: Tremolo */
1043 case 0x07:
1044 tremolo(c);
1045 break;
1047 /* Effect 8 (Set fine panning) is only processed at tick 0 */
1048 /* Effect 9 (Set sample offset) is only processed at tick 0 */
1050 /* Effect A: Volume slide */
1051 case 0x0a:
1052 volumeslide(c, effectx, effecty);
1053 break;
1055 /* Effect B (Position jump) is only processed at tick 0 */
1056 /* Effect C (Set Volume) is only processed at tick 0 */
1057 /* Effect D (Pattern Preak) is only processed at tick 0 */
1058 /* Effect E (Enhanced Effect) */
1059 case 0x0e:
1060 switch (effectx)
1062 /* Retrigger sample ($E9 + Tick to Retrig note at) */
1063 case 0x9:
1064 /* Don't device by zero */
1065 if (effecty == 0) effecty = 1;
1066 /* Apply retrig */
1067 if (currenttick % effecty == 0)
1068 mixer_playsample(c, p_modchannel->instrument);
1069 break;
1070 /* Cut note (Usage: $EC + Tick to Cut note at) */
1071 case 0xc:
1072 if (currenttick == effecty)
1073 mixer_stopsample(c);
1074 break;
1075 /* Delay note (Usage: $ED + ticks to delay note) */
1076 case 0xd:
1077 /* If this is the correct tick,
1078 * we start playing the sample now */
1079 if (currenttick == effecty)
1080 mixer.channel[c].channelactive = true;
1081 break;
1084 break;
1085 /* Effect F (Set Speed) is only processed at tick 0 */
1091 inline int clip(int i)
1093 if (i > 32767) return(32767);
1094 else if (i < -32768) return(-32768);
1095 else return(i);
1098 STATICIRAM void synthrender(void *renderbuffer, int samplecount) ICODE_ATTR;
1099 void synthrender(void *renderbuffer, int samplecount)
1101 /* 125bpm equals to 50Hz (= 0.02s)
1102 * => one tick = mixingrate/50,
1103 * samples passing in one tick:
1104 * mixingrate/(bpm/2.5) = 2.5*mixingrate/bpm */
1106 int *p_left = (int *) renderbuffer; /* int in rockbox */
1107 int *p_right = p_left+1;
1108 signed short s;
1109 int qf_distance, qf_distance2;
1111 int i;
1113 int c, left, right;
1115 for (i=0;i<samplecount;i++)
1117 /* New Tick? */
1118 if ((modplayer.samplespertick-- <= 0) &&
1119 (modplayer.patterntableposition < 127))
1121 if (modplayer.currenttick == 0)
1122 playline(modsong.patternordertable[
1123 modplayer.patterntableposition], modplayer.currentline);
1124 else playeffect(modplayer.currenttick);
1126 modplayer.currenttick++;
1128 if (modplayer.currenttick >= modplayer.ticksperline)
1130 modplayer.currentline++;
1131 modplayer.currenttick = 0;
1132 if (modplayer.currentline == 64)
1134 modplayer.patterntableposition++;
1135 if (modplayer.patterntableposition >= modsong.songlength)
1136 /* This is for Noise Tracker
1137 * modplayer.patterntableposition =
1138 * modsong.songendjumpposition;
1139 * More compatible approach is restart from 0 */
1140 modplayer.patterntableposition=0;
1141 modplayer.currentline = 0;
1145 modplayer.samplespertick = (20*mixingrate/modplayer.bpm)>>3;
1147 /* Mix buffers from here
1148 * Walk through all channels */
1149 left=0, right=0;
1151 /* If song has not stopped playing */
1152 if (modplayer.patterntableposition < 127)
1153 /* Loop through all channels */
1154 for (c=0;c<modsong.noofchannels;c++)
1156 /* Only mix the sample,
1157 * if channel there is something played on the channel */
1158 if (!mixer.channel[c].channelactive) continue;
1160 /* Loop the sample, if requested? */
1161 if (mixer.channel[c].samplepos >= mixer.channel[c].loopend)
1163 if (mixer.channel[c].loopsample)
1164 mixer.channel[c].samplepos -=
1165 (mixer.channel[c].loopend-
1166 mixer.channel[c].loopstart);
1167 else mixer.channel[c].channelactive = false;
1170 /* If the sample has stopped playing don't mix it */
1171 if (!mixer.channel[c].channelactive) continue;
1173 /* Get the sample */
1174 s = (signed short)(modsong.sampledata[
1175 mixer.channel[c].samplepos]*mixer.channel[c].volume);
1177 /* Interpolate if the sample-frequency is lower
1178 * than the mixing rate
1179 * If you don't want interpolation simply skip this part */
1180 if (mixer.channel[c].frequency < mixingrate)
1182 /* Low precision linear interpolation
1183 * (fast integer based) */
1184 qf_distance = mixer.channel[c].samplefractpos<<16 /
1185 mixingrate;
1186 qf_distance2 = (1<<16)-qf_distance;
1187 s = (qf_distance*s + qf_distance2*
1188 mixer.channel[c].lastsampledata)>>16;
1191 /* Save the last played sample for interpolation purposes */
1192 mixer.channel[c].lastsampledata = s;
1194 /* Pan the sample */
1195 left += s*(16-mixer.channel[c].panning)>>3;
1196 right += s*mixer.channel[c].panning>>3;
1198 /* Advance sample */
1199 mixer.channel[c].samplefractpos += mixer.channel[c].frequency;
1200 while (mixer.channel[c].samplefractpos > mixingrate)
1202 mixer.channel[c].samplefractpos -= mixingrate;
1203 mixer.channel[c].samplepos++;
1206 /* If we have more than 4 channels
1207 * we have to make sure that we apply clipping */
1208 if (modsong.noofchannels > 4) {
1209 *p_left = clip(left)<<13;
1210 *p_right = clip(right)<<13;
1212 else {
1213 *p_left = left<<13;
1214 *p_right = right<<13;
1216 p_left+=2;
1217 p_right+=2;
1222 enum codec_status codec_main(void)
1224 size_t n;
1225 unsigned char *modfile;
1226 int old_patterntableposition;
1228 int bytesdone;
1230 ci->configure(CODEC_SET_FILEBUF_WATERMARK, 1024*512);
1233 next_track:
1234 if (codec_init()) {
1235 return CODEC_ERROR;
1238 while (!*ci->taginfo_ready && !ci->stop_codec)
1239 ci->sleep(1);
1241 codec_set_replaygain(ci->id3);
1243 /* Load MOD file */
1245 * This is the save way
1247 size_t bytesfree;
1248 unsigned int filesize;
1250 p = modfile;
1251 bytesfree=sizeof(modfile);
1252 while ((n = ci->read_filebuf(p, bytesfree)) > 0) {
1253 p += n;
1254 bytesfree -= n;
1255 if (bytesfree == 0)
1256 return CODEC_ERROR;
1258 filesize = p-modfile;
1260 if (filesize == 0)
1261 return CODEC_ERROR;
1264 /* Directly use mod in buffer */
1265 ci->seek_buffer(0);
1266 modfile = ci->request_buffer(&n, ci->filesize);
1267 if (!modfile || n < (size_t)ci->filesize) {
1268 return CODEC_ERROR;
1271 initmodplayer();
1272 loadmod(modfile);
1274 /* Make use of 44.1khz */
1275 ci->configure(DSP_SET_FREQUENCY, 44100);
1276 /* Sample depth is 28 bit host endian */
1277 ci->configure(DSP_SET_SAMPLE_DEPTH, 28);
1278 /* Stereo output */
1279 ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED);
1281 /* The main decoder loop */
1282 ci->set_elapsed(0);
1283 bytesdone = 0;
1284 old_patterntableposition = 0;
1286 while (1) {
1287 ci->yield();
1288 if (ci->stop_codec || ci->new_track)
1289 break;
1291 if (ci->seek_time) {
1292 /* New time is ready in ci->seek_time */
1293 modplayer.patterntableposition = ci->seek_time/1000;
1294 modplayer.currentline = 0;
1295 ci->seek_complete();
1298 if(old_patterntableposition != modplayer.patterntableposition) {
1299 ci->set_elapsed(modplayer.patterntableposition*1000+500);
1300 old_patterntableposition=modplayer.patterntableposition;
1303 synthrender(samples, CHUNK_SIZE/2);
1305 bytesdone += CHUNK_SIZE;
1307 ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE/2);
1311 if (ci->request_next_track())
1312 goto next_track;
1314 return CODEC_OK;