diff options
author | Thom Johansen <thomj@rockbox.org> | 2008-05-21 11:19:58 +0000 |
---|---|---|
committer | Thom Johansen <thomj@rockbox.org> | 2008-05-21 11:19:58 +0000 |
commit | c0f7eb9f9dda1941ff80571df74d5616a4604ed7 (patch) | |
tree | 9196ea881d0a49b0f2f0cb6c577f4de1cc7c9b25 | |
parent | c78bb5a00ef4c71c875d33dac2cb97872db9a0d4 (diff) | |
download | rockbox-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/SOURCES | 1 | ||||
-rw-r--r-- | apps/codecs/Makefile | 1 | ||||
-rw-r--r-- | apps/codecs/SOURCES | 1 | ||||
-rw-r--r-- | apps/codecs/mod.c | 1327 | ||||
-rw-r--r-- | apps/filetypes.c | 1 | ||||
-rw-r--r-- | apps/metadata.c | 8 | ||||
-rw-r--r-- | apps/metadata/metadata_parsers.h | 1 | ||||
-rw-r--r-- | apps/metadata/mod.c | 62 | ||||
-rw-r--r-- | firmware/export/id3.h | 1 | ||||
-rw-r--r-- | firmware/id3.c | 3 |
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 | |||
134 | metadata/mpc.c | 134 | metadata/mpc.c |
135 | metadata/ogg.c | 135 | metadata/ogg.c |
136 | metadata/sid.c | 136 | metadata/sid.c |
137 | metadata/mod.c | ||
137 | metadata/spc.c | 138 | metadata/spc.c |
138 | metadata/vorbis.c | 139 | metadata/vorbis.c |
139 | metadata/wave.c | 140 | metadata/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) | |||
47 | ifndef SIMVER | 47 | ifndef 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 | |||
13 | aac.c | 13 | aac.c |
14 | #endif | 14 | #endif |
15 | ape.c | 15 | ape.c |
16 | mod.c | ||
16 | shorten.c | 17 | shorten.c |
17 | aiff.c | 18 | aiff.c |
18 | speex.c | 19 | speex.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 | |||
41 | CODEC_HEADER | ||
42 | |||
43 | #define CHUNK_SIZE (1024*2) | ||
44 | |||
45 | |||
46 | /* This codec supports MOD Files: | ||
47 | * | ||
48 | */ | ||
49 | |||
50 | static int32_t samples[CHUNK_SIZE] IBSS_ATTR; /* The sample buffer */ | ||
51 | |||
52 | /* Instrument Data */ | ||
53 | struct 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 */ | ||
77 | struct 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 | |||
106 | struct 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 | |||
157 | struct 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 | |||
199 | struct 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 | |||
231 | struct s_mixer { | ||
232 | /* The channels */ | ||
233 | struct s_channel channel[32]; | ||
234 | }; | ||
235 | |||
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; | ||
239 | |||
240 | const unsigned short mixingrate = 44100; | ||
241 | |||
242 | STATICIRAM void mixer_playsample(int channel, int instrument) ICODE_ATTR; | ||
243 | void 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 | |||
265 | inline void mixer_stopsample(int channel) | ||
266 | { | ||
267 | mixer.channel[channel].channelactive = false; | ||
268 | } | ||
269 | |||
270 | inline void mixer_continuesample(int channel) | ||
271 | { | ||
272 | mixer.channel[channel].channelactive = true; | ||
273 | } | ||
274 | |||
275 | inline void mixer_setvolume(int channel, int volume) | ||
276 | { | ||
277 | mixer.channel[channel].volume = volume; | ||
278 | } | ||
279 | |||
280 | inline void mixer_setpanning(int channel, int panning) | ||
281 | { | ||
282 | mixer.channel[channel].panning = panning; | ||
283 | } | ||
284 | |||
285 | inline 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 */ | ||
297 | STATICIRAM void initmodplayer(void) ICODE_ATTR; | ||
298 | void 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 */ | ||
373 | STATICIRAM bool loadmod(void *modfile) ICODE_ATTR; | ||
374 | bool 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 */ | ||
514 | STATICIRAM void vibrate(int channel) ICODE_ATTR; | ||
515 | void 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) */ | ||
532 | STATICIRAM void tremolo(int channel) ICODE_ATTR; | ||
533 | void 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 */ | ||
551 | STATICIRAM void slidetonote(int channel) ICODE_ATTR; | ||
552 | void 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 */ | ||
578 | STATICIRAM void slidetonoteglissando(int channel) ICODE_ATTR; | ||
579 | void 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 */ | ||
603 | STATICIRAM void volumeslide(int channel, int effectx, int effecty) ICODE_ATTR; | ||
604 | void 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) */ | ||
622 | STATICIRAM void playline(int pattern, int line) ICODE_ATTR; | ||
623 | void 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) */ | ||
939 | STATICIRAM void playeffect(int currenttick) ICODE_ATTR; | ||
940 | void 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 | |||
1103 | inline int clip(int i) | ||
1104 | { | ||
1105 | if (i > 32767) return(32767); | ||
1106 | else if (i < -32768) return(-32768); | ||
1107 | else return(i); | ||
1108 | } | ||
1109 | |||
1110 | STATICIRAM void synthrender(void *renderbuffer, int samplecount) ICODE_ATTR; | ||
1111 | void 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 | |||
1234 | enum 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 | |||
1245 | next_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); | |||
25 | bool get_monkeys_metadata(int fd, struct mp3entry* id3); | 25 | bool get_monkeys_metadata(int fd, struct mp3entry* id3); |
26 | bool get_musepack_metadata(int fd, struct mp3entry *id3); | 26 | bool get_musepack_metadata(int fd, struct mp3entry *id3); |
27 | bool get_sid_metadata(int fd, struct mp3entry* id3); | 27 | bool get_sid_metadata(int fd, struct mp3entry* id3); |
28 | bool get_mod_metadata(int fd, struct mp3entry* id3); | ||
28 | bool get_spc_metadata(int fd, struct mp3entry* id3); | 29 | bool get_spc_metadata(int fd, struct mp3entry* id3); |
29 | bool get_ogg_metadata(int fd, struct mp3entry* id3); | 30 | bool get_ogg_metadata(int fd, struct mp3entry* id3); |
30 | bool get_wave_metadata(int fd, struct mp3entry* id3); | 31 | bool 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 | |||
30 | bool 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 | ||