1 /*
2  * Copyright (c) 2009, The MilkyTracker Team.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * - Redistributions of source code must retain the above copyright notice,
9  *   this list of conditions and the following disclaimer.
10  * - Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * - Neither the name of the <ORGANIZATION> nor the names of its contributors
14  *   may be used to endorse or promote products derived from this software
15  *   without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 /*
31  *  LoaderDBM.cpp
32  *  MilkyPlay Module Loader: Digibooster Pro
33  *
34  *  Created by Peter Barth on 06.03.06.
35  *
36  */
37 
38 #include "Loaders.h"
39 
40 // to-do: improve detection
identifyModule(const mp_ubyte * buffer)41 const char* LoaderDBM::identifyModule(const mp_ubyte* buffer)
42 {
43 	// check for .DTM module
44 	if (memcmp(buffer,"DBM0",4))
45 		return NULL;
46 
47 	return "DBM";
48 }
49 
50 #define CLEAN_DBM \
51 	{ \
52 		mp_uint32 i; \
53 		delete[] srcIns; \
54 		for (i = 0; i < header->smpnum; i++) \
55 			delete[] srcSmp[i].sample; \
56 		delete[] srcSmp; \
57 		delete[] orderListLengths; \
58 		for (i = 0; i < numSubSongs; i++) \
59 			delete[] orderLists[i]; \
60 		for (i = 0; i < header->patnum; i++) \
61 			delete[] patterns[i]; \
62 		delete[] orderLists; \
63 		delete[] patterns; \
64 	}
65 
66 
67 struct DBMInstrument
68 {
69 	mp_ubyte	name[30];
70 	mp_uword	sampnum;
71 	mp_uword	volume;
72 	mp_uint32	finetune;
73 	mp_uint32	repstart;
74 	mp_uint32	replen;
75 	mp_uword	panning;
76 	mp_uword	flags;
77 	// added by ME
78 	mp_sint32	venvnum;
79 	mp_sint32	penvnum;
80 };
81 
82 struct DBMSample
83 {
84 	mp_uint32	flags;
85 	mp_uint32	samplen; // bit 0 set - 8 bit sample
86                          // bit 1 set - 16 bit sample
87                          // bit 2 set - 32 bit sample
88 	mp_ubyte*	sample;
89 };
90 
convertDBMffects(mp_ubyte & effect,mp_ubyte & operand)91 static void convertDBMffects(mp_ubyte& effect, mp_ubyte& operand)
92 {
93 	//static const char eff[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
94 
95 	switch (effect)
96 	{
97 		case 0x00:
98 			if (operand)
99 				effect = 0x20;
100 			break;
101 
102 		case 0x01:
103 			if ((operand & 0xF0) == 0xF0)
104 			{
105 				// doc says: 1Fx == fine portamento up
106 				effect = 0x31;
107 				operand &= 0x0F;
108 			}
109 			break;
110 		case 0x02:
111 			if ((operand & 0xF0) == 0xF0)
112 			{
113 				// doc says: 2Fx == fine portamento down
114 				effect = 0x32;
115 				operand &= 0x0F;
116 			}
117 			break;
118 
119 		case 0x03:
120 		case 0x04:
121 			break;
122 
123 		case 0x05:
124 			if ((operand & 0x0F) == 0x0F)
125 			{
126 				// doc says: 5xF == Tone portamento+Fine Volume slide up
127 				// !! unsupported !!
128 				// Might be possible with MDL way of combining portamento + volume slide
129 				// but with S3M volslide instead of MDL volslide
130 				goto missing;
131 			}
132 			else if ((operand & 0xF0) == 0xF0)
133 			{
134 				// doc says: 5Fx == Tone portamento+Fine Volume slide down
135 				// !! unsupported !!
136 				// Might be possible with MDL way of combining portamento + volume slide
137 				// but with S3M volslide instead of MDL volslide
138 				goto missing;
139 			}
140 			break;
141 
142 		case 0x06:
143 			if ((operand & 0x0F) == 0x0F)
144 			{
145 				// doc says: 6xF == Vibrato+Fine Volume slide up
146 				// !! unsupported !!
147 				// Might be possible with MDL way of combining vibrato + volume slide
148 				// but with S3M volslide instead of MDL volslide
149 				goto missing;
150 			}
151 			else if ((operand & 0xF0) == 0xF0)
152 			{
153 				// doc says: 6Fx == Vibrato+Fine Volume slide down
154 				// !! unsupported !!
155 				// Might be possible with MDL way of combining vibrato + volume slide
156 				// but with S3M volslide instead of MDL volslide
157 				goto missing;
158 			}
159 			break;
160 
161 		// Tremolo doesn't exist, huh?
162 		case 0x07:
163 		// Panning
164 		case 0x08:
165 		// Sample offset
166 		case 0x09:
167 			break;
168 
169 		// Volslide (S3M volslide fits best probably)
170 		case 0x0A:
171 			effect = 0x49;
172 			break;
173 
174 		// Position jump
175 		case 0x0B:
176 			break;
177 
178 		// set volume
179 		case 0x0C:
180 		// set global volume
181 		case 0x10:
182 			operand = XModule::vol64to255(operand);
183 			break;
184 
185 		// Pattern break
186 		case 0x0D:
187 			break;
188 
189 		// subcommands
190 		case 0x0E:
191 		{
192 			effect = 0x30 + (operand >> 4);
193 			operand &= 0xF;
194 			switch (effect)
195 			{
196 				// Set filter: unsupported
197 				case 0x30:
198 					goto missing;
199 					break;
200 				// Play backwards
201 				case 0x33:
202 					// AMS can do that
203 					effect = 0x4F;
204 					operand = 1;
205 					break;
206 				// Turn off sound in channel?
207 				// can probably be emulated with AMS set channel volume
208 				// right now unsupported
209 				case 0x34:
210 					goto missing;
211 					break;
212 				// Turn on/off sound in channel?
213 				// can probably be emulated with AMS set channel volume
214 				// right now unsupported
215 				case 0x35:
216 					goto missing;
217 					break;
218 				// set loop/loop
219 				case 0x36:
220 					goto missing;
221 					break;
222 				// set offset for loop or what?
223 				case 0x37:
224 					goto missing;
225 					break;
226 			}
227 			break;
228 		}
229 
230 		// Pattern break
231 		case 0x0F:
232 			break;
233 
234 		case 0x11:
235 			break;
236 
237 		// key off
238 		case 0x14:
239 			if (operand == 0)
240 				// AMS key of at tick
241 				effect = 0x51;
242 			break;
243 
244 		// Set envelope position
245 		case 0x15:
246 			break;
247 
248 		// Sample offset slide (unsupported)
249 		case 0x18:
250 			goto missing;
251 			break;
252 
253 		// set real BPM
254 		case 0x1C:
255 			effect = 0x52;
256 			break;
257 
258 		// echo effects (unsupported)
259 		case 0x1f:
260 		case 0x20:
261 		case 0x21:
262 		case 0x22:
263 		case 0x23:
264 			goto missing;
265 			break;
266 
267 		default:
268 #ifdef VERBOSE
269 			printf("Unknown DBM effect %x with operand %x\n", effect, operand);
270 #endif
271 missing:
272 #ifdef VERBOSE
273 			printf("Missing DBM effect %x with operand %x\n", effect, operand);
274 #endif
275 			effect = operand = 0;
276 
277 	}
278 
279 }
280 
convertDBMEnvelope(TEnvelope & outEnv,const mp_ubyte * inEnv)281 static void convertDBMEnvelope(TEnvelope& outEnv, const mp_ubyte* inEnv)
282 {
283 	memset(&outEnv, 0, sizeof(TEnvelope));
284 	outEnv.type = *inEnv & 7;
285 
286 #ifdef VERBOSE
287 	if (*inEnv >> 3)
288 	{
289 		printf("Second sustain point used");
290 
291 	}
292 #endif
293 
294 	// one single point?
295 	// disable envelope
296 	if (!*(inEnv+1))
297 	{
298 		outEnv.type &= ~1;
299 		return;
300 	}
301 
302 	// DBM stores numpoints-1
303 	outEnv.num = *(inEnv+1) + 1;
304 	outEnv.sustain = *(inEnv+2);
305 	outEnv.loops = *(inEnv+3);
306 	outEnv.loope = *(inEnv+4);
307 
308 	inEnv+=6;
309 
310 	for (mp_sint32 i = 0; i < outEnv.num; i++)
311 	{
312 		outEnv.env[i][0] = BigEndian::GET_WORD(inEnv);
313 		outEnv.env[i][1] = BigEndian::GET_WORD(inEnv+2) << 2;
314 		inEnv+=4;
315 	}
316 }
317 
load(XMFileBase & f,XModule * module)318 mp_sint32 LoaderDBM::load(XMFileBase& f, XModule* module)
319 {
320 	module->cleanUp();
321 
322 	// this will make code much easier to read
323 	TXMHeader*		header = &module->header;
324 	TXMInstrument*	instr  = module->instr;
325 	TXMSample*		smp	   = module->smp;
326 	TXMPattern*		phead  = module->phead;
327 
328 	// we're already out of memory here
329 	if (!phead || !instr || !smp)
330 		return MP_OUT_OF_MEMORY;
331 
332 	mp_sint32 i,j;
333 
334 	mp_uword numSubSongs		= 0;
335 	mp_uword** orderLists		= NULL;
336 	mp_uword* orderListLengths	= NULL;
337 	DBMInstrument* srcIns		= NULL;
338 	mp_ubyte** patterns			= NULL;
339 	DBMSample* srcSmp			= NULL;
340 
341 	while (true)
342 	{
343 		mp_ubyte ID[4], buffer[4];
344 
345 		mp_uint32 bytesRead = f.read(ID, 4, 1);
346 
347 		if (bytesRead != 4)
348 			break;
349 
350 		bytesRead = f.read(buffer, 4, 1);
351 
352 		if (bytesRead != 4)
353 			break;
354 
355 		mp_uint32 chunkLen = BigEndian::GET_DWORD(buffer);
356 
357 		switch (BigEndian::GET_DWORD(ID))
358 		{
359 			case 0x44424D30:	// 'DBM0'
360 				memcpy(header->sig, ID, 4);
361 				break;
362 
363 			case 0x4E414D45:	// 'NAME'
364 			{
365 				mp_ubyte* name = new mp_ubyte[chunkLen];
366 				f.read(name, 1, chunkLen);
367 				memcpy(header->name, name, chunkLen > MP_MAXTEXT ? MP_MAXTEXT : chunkLen);
368 				delete[] name;
369 				break;
370 			}
371 
372 			case 0x494E464F:	// 'INFO'
373 				f.read(buffer, 1, 2);
374 				header->insnum = BigEndian::GET_WORD(buffer);
375 				f.read(buffer, 1, 2);
376 				header->smpnum = BigEndian::GET_WORD(buffer);
377 
378 				srcSmp = new DBMSample[header->smpnum];
379 
380 				f.read(buffer, 1, 2);
381 				numSubSongs = BigEndian::GET_WORD(buffer);
382 
383 				// Allocate order list table
384 				orderLists = new mp_uword*[numSubSongs];
385 				orderListLengths = new mp_uword[numSubSongs];
386 
387 				f.read(buffer, 1, 2);
388 				header->patnum = BigEndian::GET_WORD(buffer);
389 
390 				patterns = new mp_ubyte*[header->patnum];
391 
392 				f.read(buffer, 1, 2);
393 				header->channum = BigEndian::GET_WORD(buffer);
394 				break;
395 
396 			case 0x534F4E47:	// 'SONG'
397 			{
398 				// Skip name
399 				for (i = 0; i < numSubSongs; i++)
400 				{
401 					mp_ubyte name[44];
402 					f.read(name, 1, 44);
403 
404 					f.read(buffer, 1, 2);
405 					orderListLengths[i] = BigEndian::GET_WORD(buffer);
406 					orderLists[i] = new mp_uword[BigEndian::GET_WORD(buffer)];
407 
408 					for (j = 0; j < orderListLengths[i]; j++)
409 					{
410 						f.read(buffer, 1, 2);
411 						orderLists[i][j] = BigEndian::GET_WORD(buffer);
412 					}
413 				}
414 				break;
415 			}
416 
417 			case 0x494E5354:	// 'INST'
418 			{
419 				mp_uword insNum = chunkLen / 50;
420 
421 				srcIns = new DBMInstrument[insNum];
422 
423 				for (i = 0; i < insNum; i++)
424 				{
425 					f.read(srcIns[i].name, 1, 30);
426 					f.read(buffer, 2, 1);
427 					srcIns[i].sampnum = BigEndian::GET_WORD(buffer);
428 					f.read(buffer, 2, 1);
429 					srcIns[i].volume = BigEndian::GET_WORD(buffer);
430 					f.read(buffer, 4, 1);
431 					srcIns[i].finetune = BigEndian::GET_DWORD(buffer);
432 					f.read(buffer, 4, 1);
433 					srcIns[i].repstart = BigEndian::GET_DWORD(buffer);
434 					f.read(buffer, 4, 1);
435 					srcIns[i].replen = BigEndian::GET_DWORD(buffer);
436 					f.read(buffer, 2, 1);
437 					srcIns[i].panning = BigEndian::GET_WORD(buffer);
438 					f.read(buffer, 2, 1);
439 					srcIns[i].flags = BigEndian::GET_WORD(buffer);
440 
441 					srcIns[i].venvnum = srcIns[i].penvnum = -1;
442 				}
443 				break;
444 			}
445 
446 			case 0x50415454:	// 'PATT'
447 			{
448 				for (i = 0; i < header->patnum; i++)
449 				{
450 					f.read(buffer, 2, 1);
451 					phead[i].rows = BigEndian::GET_WORD(buffer);
452 					f.read(buffer, 4, 1);
453 					phead[i].len = BigEndian::GET_DWORD(buffer);
454 
455 					patterns[i] = new mp_ubyte[phead[i].len];
456 					f.read(patterns[i], 1, phead[i].len);
457 				}
458 				break;
459 			}
460 
461 			case 0x534D504C:	// 'SMPL'
462 			{
463 				for (i = 0; i < header->smpnum; i++)
464 				{
465 					f.read(buffer, 4, 1);
466 					srcSmp[i].flags = BigEndian::GET_DWORD(buffer);
467 					f.read(buffer, 4, 1);
468 					srcSmp[i].samplen = BigEndian::GET_DWORD(buffer);
469 
470 					if (srcSmp[i].flags == 1)
471 					{
472 						srcSmp[i].sample = new mp_ubyte[srcSmp[i].samplen];
473 						module->loadSample(f, srcSmp[i].sample, srcSmp[i].samplen, srcSmp[i].samplen);
474 					}
475 					else if (srcSmp[i].flags == 2)
476 					{
477 						srcSmp[i].sample = new mp_ubyte[srcSmp[i].samplen*2];
478 						module->loadSample(f, srcSmp[i].sample, srcSmp[i].samplen*2, srcSmp[i].samplen, XModule::ST_16BIT | XModule::ST_BIGENDIAN);
479 					}
480 					else
481 					{
482 #ifdef VERBOSE
483 						printf("Unsupported sample type");
484 #endif
485 					}
486 				}
487 				break;
488 			}
489 
490 			case 0x56454E56:	// 'VENV'
491 			{
492 				f.read(buffer, 2, 1);
493 				mp_uword numEnvelopes = BigEndian::GET_WORD(buffer);
494 				for (i = 0; i < numEnvelopes; i++)
495 				{
496 					f.read(buffer, 2, 1);
497 					mp_uword index = BigEndian::GET_WORD(buffer);
498 
499 					if (index)
500 						srcIns[index-1].venvnum = module->numVEnvs;
501 
502 					mp_ubyte env[134];
503 					f.read(env, 1, 134);
504 
505 					TEnvelope venv;
506 					convertDBMEnvelope(venv, env);
507 
508 					if (!module->addVolumeEnvelope(venv))
509 					{
510 						CLEAN_DBM;
511 						return MP_OUT_OF_MEMORY;
512 					}
513 				}
514 				break;
515 			}
516 
517 			case 0x50454E56:	// 'PENV'
518 			{
519 				f.read(buffer, 2, 1);
520 				mp_uword numEnvelopes = BigEndian::GET_WORD(buffer);
521 				for (i = 0; i < numEnvelopes; i++)
522 				{
523 					f.read(buffer, 2, 1);
524 					mp_uword index = BigEndian::GET_WORD(buffer);
525 
526 					if (index)
527 						srcIns[index-1].penvnum = module->numPEnvs;
528 
529 					mp_ubyte env[134];
530 					f.read(env, 1, 134);
531 
532 					TEnvelope penv;
533 					convertDBMEnvelope(penv, env);
534 
535 					if (!module->addPanningEnvelope(penv))
536 					{
537 						CLEAN_DBM;
538 						return MP_OUT_OF_MEMORY;
539 					}
540 				}
541 				break;
542 			}
543 
544 			default:
545 				f.seekWithBaseOffset(f.posWithBaseOffset() + chunkLen);
546 
547 		}
548 	}
549 
550 	if (!(orderListLengths && srcIns && patterns && srcSmp))
551 		return MP_LOADER_FAILED;
552 
553 	// Convert orderlist, subsongs are not supported yet
554 	j = 0;
555 	for (i = 0; i < orderListLengths[j]; i++)
556 	{
557 		if (i < 256)
558 			header->ord[i] = (mp_ubyte)orderLists[j][i];
559 	}
560 
561 	header->ordnum = orderListLengths[j];
562 
563 	header->mainvol = 255;
564 	header->speed = 125;
565 	header->tempo = 6;
566 	header->flags = XModule::MODULE_OLDPTINSTRUMENTCHANGE | XModule::MODULE_PTNEWINSTRUMENT;
567 	header->volenvnum = module->numVEnvs;
568 	header->panenvnum = module->numPEnvs;
569 
570 	// Convert patterns
571 	for (i = 0; i < header->patnum; i++)
572 	{
573 		phead[i].effnum = 2;
574 		phead[i].channum = (mp_ubyte)header->channum;
575 
576 		const mp_sint32 bps = phead[i].effnum*2+2;
577 
578 		phead[i].patternData = new mp_ubyte[phead[i].rows*header->channum*bps];
579 
580 		// out of memory?
581 		if (phead[i].patternData == NULL)
582 		{
583 			CLEAN_DBM;
584 			return MP_OUT_OF_MEMORY;
585 		}
586 
587 		memset(phead[i].patternData, 0, phead[i].rows*header->channum*bps);
588 
589 		mp_uint32 currentRow = 0;
590 
591 		j = 0;
592 
593 		mp_ubyte* src = patterns[i];
594 
595 		while (j < (signed)phead[i].len && currentRow < phead[i].rows)
596 		{
597 			mp_ubyte pack = src[j];
598 			j++;
599 			if (!pack)
600 				currentRow++;
601 			else
602 			{
603 				mp_ubyte note = 0, ins = 0;
604 				mp_ubyte eff1 = 0, op1 = 0, eff2 = 0, op2 = 0;
605 
606 				mp_ubyte channel = pack-1;
607 
608 				pack = src[j];
609 				j++;
610 
611 				// Note present?
612 				if (pack & 0x01)
613 				{
614 					if (src[j] == 0x1F)
615 						note = XModule::NOTE_OFF;
616 					else
617 						note = (src[j] >> 4) * 12 + ((src[j] & 0xF)+1);
618 					j++;
619 				}
620 				// Instrument present?
621 				if (pack & 0x02)
622 				{
623 					ins = src[j];
624 					j++;
625 				}
626 				// Effect 1 present?
627 				if (pack & 0x04)
628 				{
629 					eff1 = src[j];
630 					j++;
631 				}
632 				// Operand 1 present?
633 				if (pack & 0x08)
634 				{
635 					op1 = src[j];
636 					j++;
637 				}
638 				// Effect 2 present?
639 				if (pack & 0x10)
640 				{
641 					eff2 = src[j];
642 					j++;
643 				}
644 				// Operand 1 present?
645 				if (pack & 0x20)
646 				{
647 					op2 = src[j];
648 					j++;
649 				}
650 
651 				convertDBMffects(eff1, op1);
652 				convertDBMffects(eff2, op2);
653 
654 				mp_ubyte* dstSlot = phead[i].patternData +
655 									currentRow * header->channum*bps + channel*bps;
656 
657 				*dstSlot++ = note;
658 				*dstSlot++ = ins;
659 				*dstSlot++ = eff1;
660 				*dstSlot++ = op1;
661 				*dstSlot++ = eff2;
662 				*dstSlot++ = op2;
663 			}
664 		}
665 
666 	}
667 
668 	// convert instrument data
669 	for (i = 0; i < header->insnum; i++)
670 	{
671 		memcpy(instr[i].name, srcIns[i].name, MP_MAXTEXT);
672 		for (j = 0; j < 120; j++)
673 			instr[i].snum[j] = i;
674 
675 		j = srcIns[i].sampnum;
676 
677 		if (j && j <= header->smpnum)
678 		{
679 			j--;
680 			if (srcSmp[j].samplen)
681 			{
682 				instr[i].samp = 1;
683 
684 				smp[i].flags = 1 | (srcIns[i].panning ? 2 : 0);
685 				smp[i].pan = (srcIns[i].panning ? srcIns[i].panning - 1 : 0x80);
686 				smp[i].vol = XModule::vol64to255(srcIns[i].volume);
687 
688 				smp[i].samplen = srcSmp[j].samplen;
689 
690 				if (srcSmp[j].flags == 1)
691 				{
692 					smp[i].sample = (mp_sbyte*)module->allocSampleMem(smp[i].samplen);
693 					memcpy(smp[i].sample, srcSmp[j].sample, smp[i].samplen);
694 				}
695 				else if (srcSmp[j].flags == 2)
696 				{
697 					smp[i].sample = (mp_sbyte*)module->allocSampleMem(smp[i].samplen*2);
698 					memcpy(smp[i].sample, srcSmp[j].sample, smp[i].samplen*2);
699 					smp[i].type |= 16;
700 				}
701 
702 				XModule::convertc4spd(srcIns[i].finetune, &smp[i].finetune, &smp[i].relnote);
703 
704 				smp[i].loopstart = srcIns[i].repstart;
705 				smp[i].looplen = srcIns[i].replen;
706 
707 				if (((srcIns[i].flags & 3) == 1) && smp[i].looplen)
708 					smp[i].type |= 1;
709 				else if (((srcIns[i].flags & 2) == 2) && smp[i].looplen)
710 					smp[i].type |= 2;
711 
712 				if (srcIns[i].venvnum >= 0)
713 					smp[i].venvnum = srcIns[i].venvnum+1;
714 				if (srcIns[i].penvnum >= 0)
715 					smp[i].penvnum = srcIns[i].penvnum+1;
716 			}
717 		}
718 	}
719 
720 	CLEAN_DBM;
721 
722 	header->smpnum = header->insnum;
723 
724 	strcpy(header->tracker,"Digibooster Pro");
725 
726 	module->setDefaultPanning();
727 
728 	module->postProcessSamples();
729 
730 	return MP_OK;
731 }
732