updated on Tue Jan 17 08:05:08 UTC 2012
[aur-mirror.git] / dosbox-svn-patched / mt32.patch
blob236bab8968a5747dc81164bc5f64322c091d24c2
1 diff -urN dosbox.orig/src/dosbox.cpp dosbox/src/dosbox.cpp
2 --- dosbox.orig/src/dosbox.cpp 2011-01-11 20:09:19.031498771 -0300
3 +++ dosbox/src/dosbox.cpp 2011-01-11 20:15:44.304832102 -0300
4 @@ -470,7 +470,7 @@
6 const char* mputypes[] = { "intelligent", "uart", "none",0};
7 // FIXME: add some way to offer the actually available choices.
8 - const char *devices[] = { "default", "win32", "alsa", "oss", "coreaudio", "coremidi","none", 0};
9 + const char *devices[] = { "default", "win32", "alsa", "oss", "coreaudio", "coremidi", "mt32", "none", 0};
10 Pstring = secprop->Add_string("mpu401",Property::Changeable::WhenIdle,"intelligent");
11 Pstring->Set_values(mputypes);
12 Pstring->Set_help("Type of MPU-401 to emulate.");
13 @@ -483,6 +483,11 @@
14 Pstring->Set_help("Special configuration options for the device driver. This is usually the id of the device you want to use.\n"
15 " See the README/Manual for more details.");
17 + const char *mt32rates[] = { "44100", "48000", "32000","22050", "16000", "11025", "8000", "49716", "auto", 0 };
18 + Pstring = secprop->Add_string("mt32rate",Property::Changeable::WhenIdle,"auto");
19 + Pstring->Set_values(mt32rates);
20 + Pstring->Set_help("MT-32 sample rate, only works if mt32 is enabled");
22 #if C_DEBUG
23 secprop=control->AddSection_prop("debug",&DEBUG_Init);
24 #endif
25 diff -urN dosbox.orig/src/gui/Makefile.am dosbox/src/gui/Makefile.am
26 --- dosbox.orig/src/gui/Makefile.am 2011-01-11 20:09:19.064832103 -0300
27 +++ dosbox/src/gui/Makefile.am 2011-01-11 20:15:44.304832102 -0300
28 @@ -7,5 +7,8 @@
29 render_templates_sai.h render_templates_hq.h \
30 render_templates_hq2x.h render_templates_hq3x.h \
31 midi.cpp midi_win32.h midi_oss.h midi_coreaudio.h midi_alsa.h \
32 - midi_coremidi.h sdl_gui.cpp dosbox_splash.h
34 + midi_coremidi.h sdl_gui.cpp dosbox_splash.h \
35 + freeverb.cpp freeverb.h i386.cpp i386.h mt32_file.cpp mt32_file.h \
36 + mt32emu.h part.cpp part.h partial.cpp partial.h partialManager.cpp \
37 + partialManager.h structures.h synth.cpp synth.h tables.cpp tables.h \
38 + midi_mt32.h
39 diff -urN dosbox.orig/src/gui/freeverb.cpp dosbox/src/gui/freeverb.cpp
40 --- dosbox.orig/src/gui/freeverb.cpp 1969-12-31 21:00:00.000000000 -0300
41 +++ dosbox/src/gui/freeverb.cpp 2011-01-11 20:15:44.308165436 -0300
42 @@ -0,0 +1,285 @@
43 +// Comb filter implementation
44 +//
45 +// Written by
46 +// http://www.dreampoint.co.uk
47 +// This code is public domain
49 +#include "./src/gui/freeverb.h"
51 +comb::comb() {
52 + filterstore = 0;
53 + bufidx = 0;
56 +void comb::setbuffer(float *buf, int size) {
57 + buffer = buf;
58 + bufsize = size;
61 +void comb::mute() {
62 + for (int i = 0; i < bufsize; i++)
63 + buffer[i] = 0;
66 +void comb::setdamp(float val) {
67 + damp1 = val;
68 + damp2 = 1 - val;
71 +float comb::getdamp() {
72 + return damp1;
75 +void comb::setfeedback(float val) {
76 + feedback = val;
79 +float comb::getfeedback() {
80 + return feedback;
83 +// Allpass filter implementation
85 +allpass::allpass() {
86 + bufidx = 0;
89 +void allpass::setbuffer(float *buf, int size) {
90 + buffer = buf;
91 + bufsize = size;
94 +void allpass::mute() {
95 + for (int i = 0; i < bufsize; i++)
96 + buffer[i] = 0;
99 +void allpass::setfeedback(float val) {
100 + feedback = val;
103 +float allpass::getfeedback() {
104 + return feedback;
107 +// Reverb model implementation
109 +revmodel::revmodel() {
110 + // Tie the components to their buffers
111 + combL[0].setbuffer(bufcombL1,combtuningL1);
112 + combR[0].setbuffer(bufcombR1,combtuningR1);
113 + combL[1].setbuffer(bufcombL2,combtuningL2);
114 + combR[1].setbuffer(bufcombR2,combtuningR2);
115 + combL[2].setbuffer(bufcombL3,combtuningL3);
116 + combR[2].setbuffer(bufcombR3,combtuningR3);
117 + combL[3].setbuffer(bufcombL4,combtuningL4);
118 + combR[3].setbuffer(bufcombR4,combtuningR4);
119 + combL[4].setbuffer(bufcombL5,combtuningL5);
120 + combR[4].setbuffer(bufcombR5,combtuningR5);
121 + combL[5].setbuffer(bufcombL6,combtuningL6);
122 + combR[5].setbuffer(bufcombR6,combtuningR6);
123 + combL[6].setbuffer(bufcombL7,combtuningL7);
124 + combR[6].setbuffer(bufcombR7,combtuningR7);
125 + combL[7].setbuffer(bufcombL8,combtuningL8);
126 + combR[7].setbuffer(bufcombR8,combtuningR8);
127 + allpassL[0].setbuffer(bufallpassL1,allpasstuningL1);
128 + allpassR[0].setbuffer(bufallpassR1,allpasstuningR1);
129 + allpassL[1].setbuffer(bufallpassL2,allpasstuningL2);
130 + allpassR[1].setbuffer(bufallpassR2,allpasstuningR2);
131 + allpassL[2].setbuffer(bufallpassL3,allpasstuningL3);
132 + allpassR[2].setbuffer(bufallpassR3,allpasstuningR3);
133 + allpassL[3].setbuffer(bufallpassL4,allpasstuningL4);
134 + allpassR[3].setbuffer(bufallpassR4,allpasstuningR4);
136 + // Set default values
137 + allpassL[0].setfeedback(0.5f);
138 + allpassR[0].setfeedback(0.5f);
139 + allpassL[1].setfeedback(0.5f);
140 + allpassR[1].setfeedback(0.5f);
141 + allpassL[2].setfeedback(0.5f);
142 + allpassR[2].setfeedback(0.5f);
143 + allpassL[3].setfeedback(0.5f);
144 + allpassR[3].setfeedback(0.5f);
145 + setmode(initialmode);
146 + setwet(initialwet);
147 + setroomsize(initialroom);
148 + setdry(initialdry);
149 + setdamp(initialdamp);
150 + setwidth(initialwidth);
152 + // Buffer will be full of rubbish - so we MUST mute them
153 + mute();
156 +void revmodel::mute() {
157 + int i;
159 + if (getmode() >= freezemode)
160 + return;
162 + for (i = 0; i < numcombs; i++) {
163 + combL[i].mute();
164 + combR[i].mute();
167 + for (i = 0; i < numallpasses; i++) {
168 + allpassL[i].mute();
169 + allpassR[i].mute();
173 +void revmodel::processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip) {
174 + float outL, outR, input;
176 + while (numsamples-- > 0) {
177 + int i;
179 + outL = outR = 0;
180 + input = (*inputL + *inputR) * gain;
182 + // Accumulate comb filters in parallel
183 + for (i = 0; i < numcombs; i++) {
184 + outL += combL[i].process(input);
185 + outR += combR[i].process(input);
188 + // Feed through allpasses in series
189 + for (i = 0; i < numallpasses; i++) {
190 + outL = allpassL[i].process(outL);
191 + outR = allpassR[i].process(outR);
194 + // Calculate output REPLACING anything already there
195 + *outputL = outL * wet1 + outR * wet2 + *inputL * dry;
196 + *outputR = outR * wet1 + outL * wet2 + *inputR * dry;
198 + // Increment sample pointers, allowing for interleave (if any)
199 + inputL += skip;
200 + inputR += skip;
201 + outputL += skip;
202 + outputR += skip;
206 +void revmodel::processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip) {
207 + float outL, outR, input;
209 + while (numsamples-- > 0) {
210 + int i;
212 + outL = outR = 0;
213 + input = (*inputL + *inputR) * gain;
215 + // Accumulate comb filters in parallel
216 + for (i = 0; i < numcombs; i++) {
217 + outL += combL[i].process(input);
218 + outR += combR[i].process(input);
221 + // Feed through allpasses in series
222 + for (i = 0; i < numallpasses; i++) {
223 + outL = allpassL[i].process(outL);
224 + outR = allpassR[i].process(outR);
227 + // Calculate output MIXING with anything already there
228 + *outputL += outL * wet1 + outR * wet2 + *inputL * dry;
229 + *outputR += outR * wet1 + outL * wet2 + *inputR * dry;
231 + // Increment sample pointers, allowing for interleave (if any)
232 + inputL += skip;
233 + inputR += skip;
234 + outputL += skip;
235 + outputR += skip;
239 +void revmodel::update() {
240 + // Recalculate internal values after parameter change
242 + int i;
244 + wet1 = wet * (width / 2 + 0.5f);
245 + wet2 = wet * ((1 - width) / 2);
247 + if (mode >= freezemode) {
248 + roomsize1 = 1;
249 + damp1 = 0;
250 + gain = muted;
251 + } else {
252 + roomsize1 = roomsize;
253 + damp1 = damp;
254 + gain = fixedgain;
257 + for (i = 0; i < numcombs; i++) {
258 + combL[i].setfeedback(roomsize1);
259 + combR[i].setfeedback(roomsize1);
262 + for (i = 0; i < numcombs; i++) {
263 + combL[i].setdamp(damp1);
264 + combR[i].setdamp(damp1);
268 +// The following get/set functions are not inlined, because
269 +// speed is never an issue when calling them, and also
270 +// because as you develop the reverb model, you may
271 +// wish to take dynamic action when they are called.
273 +void revmodel::setroomsize(float value) {
274 + roomsize = (value * scaleroom) + offsetroom;
275 + update();
278 +float revmodel::getroomsize() {
279 + return (roomsize - offsetroom) / scaleroom;
282 +void revmodel::setdamp(float value) {
283 + damp = value * scaledamp;
284 + update();
287 +float revmodel::getdamp() {
288 + return damp / scaledamp;
291 +void revmodel::setwet(float value) {
292 + wet = value * scalewet;
293 + update();
296 +float revmodel::getwet() {
297 + return wet / scalewet;
300 +void revmodel::setdry(float value) {
301 + dry = value * scaledry;
304 +float revmodel::getdry() {
305 + return dry / scaledry;
308 +void revmodel::setwidth(float value) {
309 + width = value;
310 + update();
313 +float revmodel::getwidth() {
314 + return width;
317 +void revmodel::setmode(float value) {
318 + mode = value;
319 + update();
322 +float revmodel::getmode() {
323 + if (mode >= freezemode)
324 + return 1;
325 + else
326 + return 0;
328 diff -urN dosbox.orig/src/gui/freeverb.h dosbox/src/gui/freeverb.h
329 --- dosbox.orig/src/gui/freeverb.h 1969-12-31 21:00:00.000000000 -0300
330 +++ dosbox/src/gui/freeverb.h 2011-01-11 20:15:44.308165436 -0300
331 @@ -0,0 +1,219 @@
332 +// Macro for killing denormalled numbers
334 +// Written by Jezar at Dreampoint, June 2000
335 +// http://www.dreampoint.co.uk
336 +// Based on IS_DENORMAL macro by Jon Watte
337 +// This code is public domain
339 +#ifndef FREEVERB_H
340 +#define FREEVERB_H
342 +// FIXME: Fix this really ugly hack
343 +inline float undenormalise(void *sample) {
344 + if (((*(unsigned int*)sample) & 0x7f800000) == 0)
345 + return 0.0f;
346 + return *(float*)sample;
349 +// Comb filter class declaration
351 +class comb {
352 +public:
353 + comb();
354 + void setbuffer(float *buf, int size);
355 + inline float process(float inp);
356 + void mute();
357 + void setdamp(float val);
358 + float getdamp();
359 + void setfeedback(float val);
360 + float getfeedback();
361 +private:
362 + float feedback;
363 + float filterstore;
364 + float damp1;
365 + float damp2;
366 + float *buffer;
367 + int bufsize;
368 + int bufidx;
372 +// Big to inline - but crucial for speed
374 +inline float comb::process(float input) {
375 + float output;
377 + output = buffer[bufidx];
378 + undenormalise(&output);
380 + filterstore = (output * damp2) + (filterstore * damp1);
381 + undenormalise(&filterstore);
383 + buffer[bufidx] = input + (filterstore * feedback);
385 + if (++bufidx >= bufsize)
386 + bufidx = 0;
388 + return output;
391 +// Allpass filter declaration
393 +class allpass {
394 +public:
395 + allpass();
396 + void setbuffer(float *buf, int size);
397 + inline float process(float inp);
398 + void mute();
399 + void setfeedback(float val);
400 + float getfeedback();
401 +private:
402 + float feedback;
403 + float *buffer;
404 + int bufsize;
405 + int bufidx;
409 +// Big to inline - but crucial for speed
411 +inline float allpass::process(float input) {
412 + float output;
413 + float bufout;
415 + bufout = buffer[bufidx];
416 + undenormalise(&bufout);
418 + output = -input + bufout;
419 + buffer[bufidx] = input + (bufout * feedback);
421 + if (++bufidx >= bufsize)
422 + bufidx = 0;
424 + return output;
428 +// Reverb model tuning values
430 +const int numcombs = 8;
431 +const int numallpasses = 4;
432 +const float muted = 0;
433 +const float fixedgain = 0.015f;
434 +const float scalewet = 3;
435 +const float scaledry = 2;
436 +const float scaledamp = 0.4f;
437 +const float scaleroom = 0.28f;
438 +const float offsetroom = 0.7f;
439 +const float initialroom = 0.5f;
440 +const float initialdamp = 0.5f;
441 +const float initialwet = 1 / scalewet;
442 +const float initialdry = 0;
443 +const float initialwidth = 1;
444 +const float initialmode = 0;
445 +const float freezemode = 0.5f;
446 +const int stereospread = 23;
448 +// These values assume 44.1KHz sample rate
449 +// they will probably be OK for 48KHz sample rate
450 +// but would need scaling for 96KHz (or other) sample rates.
451 +// The values were obtained by listening tests.
452 +const int combtuningL1 = 1116;
453 +const int combtuningR1 = 1116 + stereospread;
454 +const int combtuningL2 = 1188;
455 +const int combtuningR2 = 1188 + stereospread;
456 +const int combtuningL3 = 1277;
457 +const int combtuningR3 = 1277 + stereospread;
458 +const int combtuningL4 = 1356;
459 +const int combtuningR4 = 1356 + stereospread;
460 +const int combtuningL5 = 1422;
461 +const int combtuningR5 = 1422 + stereospread;
462 +const int combtuningL6 = 1491;
463 +const int combtuningR6 = 1491 + stereospread;
464 +const int combtuningL7 = 1557;
465 +const int combtuningR7 = 1557 + stereospread;
466 +const int combtuningL8 = 1617;
467 +const int combtuningR8 = 1617 + stereospread;
468 +const int allpasstuningL1 = 556;
469 +const int allpasstuningR1 = 556 + stereospread;
470 +const int allpasstuningL2 = 441;
471 +const int allpasstuningR2 = 441 + stereospread;
472 +const int allpasstuningL3 = 341;
473 +const int allpasstuningR3 = 341 + stereospread;
474 +const int allpasstuningL4 = 225;
475 +const int allpasstuningR4 = 225 + stereospread;
478 +// Reverb model declaration
480 +class revmodel {
481 +public:
482 + revmodel();
483 + void mute();
484 + void processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip);
485 + void processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip);
486 + void setroomsize(float value);
487 + float getroomsize();
488 + void setdamp(float value);
489 + float getdamp();
490 + void setwet(float value);
491 + float getwet();
492 + void setdry(float value);
493 + float getdry();
494 + void setwidth(float value);
495 + float getwidth();
496 + void setmode(float value);
497 + float getmode();
498 +private:
499 + void update();
501 + float gain;
502 + float roomsize, roomsize1;
503 + float damp, damp1;
504 + float wet, wet1, wet2;
505 + float dry;
506 + float width;
507 + float mode;
509 + // The following are all declared inline
510 + // to remove the need for dynamic allocation
511 + // with its subsequent error-checking messiness
513 + // Comb filters
514 + comb combL[numcombs];
515 + comb combR[numcombs];
517 + // Allpass filters
518 + allpass allpassL[numallpasses];
519 + allpass allpassR[numallpasses];
521 + // Buffers for the combs
522 + float bufcombL1[combtuningL1];
523 + float bufcombR1[combtuningR1];
524 + float bufcombL2[combtuningL2];
525 + float bufcombR2[combtuningR2];
526 + float bufcombL3[combtuningL3];
527 + float bufcombR3[combtuningR3];
528 + float bufcombL4[combtuningL4];
529 + float bufcombR4[combtuningR4];
530 + float bufcombL5[combtuningL5];
531 + float bufcombR5[combtuningR5];
532 + float bufcombL6[combtuningL6];
533 + float bufcombR6[combtuningR6];
534 + float bufcombL7[combtuningL7];
535 + float bufcombR7[combtuningR7];
536 + float bufcombL8[combtuningL8];
537 + float bufcombR8[combtuningR8];
539 + // Buffers for the allpasses
540 + float bufallpassL1[allpasstuningL1];
541 + float bufallpassR1[allpasstuningR1];
542 + float bufallpassL2[allpasstuningL2];
543 + float bufallpassR2[allpasstuningR2];
544 + float bufallpassL3[allpasstuningL3];
545 + float bufallpassR3[allpasstuningR3];
546 + float bufallpassL4[allpasstuningL4];
547 + float bufallpassR4[allpasstuningR4];
550 +#endif
551 diff -urN dosbox.orig/src/gui/i386.cpp dosbox/src/gui/i386.cpp
552 --- dosbox.orig/src/gui/i386.cpp 1969-12-31 21:00:00.000000000 -0300
553 +++ dosbox/src/gui/i386.cpp 2011-01-11 20:15:44.308165436 -0300
554 @@ -0,0 +1,849 @@
555 +/* Copyright (c) 2003-2005 Various contributors
557 + * Permission is hereby granted, free of charge, to any person obtaining a copy
558 + * of this software and associated documentation files (the "Software"), to
559 + * deal in the Software without restriction, including without limitation the
560 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
561 + * sell copies of the Software, and to permit persons to whom the Software is
562 + * furnished to do so, subject to the following conditions:
564 + * The above copyright notice and this permission notice shall be included in
565 + * all copies or substantial portions of the Software.
567 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
568 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
569 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
570 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
571 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
572 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
573 + * IN THE SOFTWARE.
574 + */
576 +#include "mt32emu.h"
578 +#ifdef MT32EMU_HAVE_X86
580 +namespace MT32Emu {
582 +#ifndef _MSC_VER
584 +#define eflag(value) __asm__ __volatile__("pushfl \n popfl \n" : : "a"(value))
585 +#define cpuid_flag (1 << 21)
587 +static inline bool atti386_DetectCPUID() {
588 + unsigned int result;
590 + // Is there a cpuid?
591 + result = cpuid_flag; // set test
592 + eflag(result);
593 + if (!(result & cpuid_flag))
594 + return false;
596 + result = 0; // clear test
597 + eflag(result);
598 + if (result & cpuid_flag)
599 + return false;
601 + return true;
604 +static inline bool atti386_DetectSIMD() {
605 + unsigned int result;
607 + if (atti386_DetectCPUID() == false)
608 + return false;
610 + /* check cpuid */
611 + __asm__ __volatile__(
612 + "pushl %%ebx \n" \
613 + "movl $1, %%eax \n" \
614 + "cpuid \n" \
615 + "movl %%edx, %0 \n" \
616 + "popl %%ebx \n" \
617 + : "=r"(result) : : "eax", "ecx", "edx");
619 + if (result & (1 << 25))
620 + return true;
622 + return false;
625 +static inline bool atti386_Detect3DNow() {
626 + unsigned int result;
628 + if (atti386_DetectCPUID() == false)
629 + return false;
631 + // get cpuid
632 + __asm__ __volatile__(
633 + "pushl %%ebx \n" \
634 + "movl $0x80000001, %%eax \n" \
635 + "cpuid \n" \
636 + "movl %%edx, %0 \n" \
637 + "popl %%ebx \n" \
638 + : "=r"(result) : : "eax", "ecx", "edx");
640 + if (result & 0x80000000)
641 + return true;
643 + return false;
647 +static inline float atti386_iir_filter_sse(float *output, float *hist1_ptr, float *coef_ptr) {
648 + __asm__ __volatile__ (
649 + "pushl %1 \n" \
650 + "pushl %2 \n" \
651 + "movss 0(%0), %%xmm1 \n" \
652 + "movups 0(%1), %%xmm2 \n" \
653 + "movlps 0(%2), %%xmm3 \n" \
654 + " \n" \
655 + "shufps $0x44, %%xmm3, %%xmm3 \n" \
656 + " \n" \
657 + "mulps %%xmm3, %%xmm2 \n" \
658 + " \n" \
659 + "subss %%xmm2, %%xmm1 \n" \
660 + "shufps $0x39, %%xmm2, %%xmm2 \n" \
661 + "subss %%xmm2, %%xmm1 \n" \
662 + " \n" \
663 + "movss %%xmm1, 0(%2) \n" \
664 + " \n" \
665 + "shufps $0x39, %%xmm2, %%xmm2 \n" \
666 + "addss %%xmm2, %%xmm1 \n" \
667 + " \n" \
668 + "shufps $0x39, %%xmm2, %%xmm2 \n" \
669 + "addss %%xmm2, %%xmm1 \n" \
670 + " \n" \
671 + "movss %%xmm3, 4(%2) \n" \
672 + " \n" \
673 + "addl $16, %1 \n" \
674 + "addl $8, %2 \n" \
675 + " \n" \
676 + "movups 0(%1), %%xmm2 \n" \
677 + " \n" \
678 + "movlps 0(%2), %%xmm3 \n" \
679 + "shufps $0x44, %%xmm3, %%xmm3 \n" \
680 + " \n" \
681 + "mulps %%xmm3, %%xmm2 \n" \
682 + " \n" \
683 + "subss %%xmm2, %%xmm1 \n" \
684 + "shufps $0x39, %%xmm2, %%xmm2 \n" \
685 + "subss %%xmm2, %%xmm1 \n" \
686 + " \n" \
687 + "movss %%xmm1, 0(%2) \n" \
688 + " \n" \
689 + "shufps $0x39, %%xmm2, %%xmm2 \n" \
690 + "addss %%xmm2, %%xmm1 \n" \
691 + " \n" \
692 + "shufps $0x39, %%xmm2, %%xmm2 \n" \
693 + "addss %%xmm2, %%xmm1 \n" \
694 + " \n" \
695 + "movss %%xmm3, 4(%2) \n" \
696 + "movss %%xmm1, 0(%0) \n" \
697 + "popl %2 \n" \
698 + "popl %1 \n" \
699 + : : "r"(output), "r"(coef_ptr), "r"(hist1_ptr)
700 + : "memory"
701 +#ifdef __SSE__
702 + , "xmm1", "xmm2", "xmm3"
703 +#endif
704 + );
706 + return *output;
709 +static inline float atti386_iir_filter_3DNow(float output, float *hist1_ptr, float *coef_ptr) {
710 + float tmp;
712 + __asm__ __volatile__ (
713 + "movq %0, %%mm1 \n" \
714 + " \n" \
715 + "movl %1, %%edi \n" \
716 + "movq 0(%%edi), %%mm2 \n" \
717 + " \n" \
718 + "movl %2, %%eax; \n" \
719 + "movq 0(%%eax), %%mm3 \n" \
720 + " \n" \
721 + "pfmul %%mm3, %%mm2 \n" \
722 + "pfsub %%mm2, %%mm1 \n" \
723 + " \n" \
724 + "psrlq $32, %%mm2 \n" \
725 + "pfsub %%mm2, %%mm1 \n" \
726 + " \n" \
727 + "movd %%mm1, %3 \n" \
728 + " \n" \
729 + "addl $8, %%edi \n" \
730 + "movq 0(%%edi), %%mm2 \n" \
731 + "movq 0(%%eax), %%mm3 \n" \
732 + " \n" \
733 + "pfmul %%mm3, %%mm2 \n" \
734 + "pfadd %%mm2, %%mm1 \n" \
735 + " \n" \
736 + "psrlq $32, %%mm2 \n" \
737 + "pfadd %%mm2, %%mm1 \n" \
738 + " \n" \
739 + "pushl %3 \n" \
740 + "popl 0(%%eax) \n" \
741 + " \n" \
742 + "movd %%mm3, 4(%%eax) \n" \
743 + " \n" \
744 + "addl $8, %%edi \n" \
745 + "addl $8, %%eax \n" \
746 + " \n" \
747 + "movq 0(%%edi), %%mm2 \n" \
748 + "movq 0(%%eax), %%mm3 \n" \
749 + " \n" \
750 + "pfmul %%mm3, %%mm2 \n" \
751 + "pfsub %%mm2, %%mm1 \n" \
752 + " \n" \
753 + "psrlq $32, %%mm2 \n" \
754 + "pfsub %%mm2, %%mm1 \n" \
755 + " \n" \
756 + "movd %%mm1, %3 \n" \
757 + " \n" \
758 + "addl $8, %%edi \n" \
759 + "movq 0(%%edi), %%mm2 \n" \
760 + "movq 0(%%eax), %%mm3 \n" \
761 + " \n" \
762 + "pfmul %%mm3, %%mm2 \n" \
763 + "pfadd %%mm2, %%mm1 \n" \
764 + " \n" \
765 + "psrlq $32, %%mm2 \n" \
766 + "pfadd %%mm2, %%mm1 \n" \
767 + " \n" \
768 + "pushl %3 \n" \
769 + "popl 0(%%eax) \n" \
770 + "movd %%mm3, 4(%%eax) \n" \
771 + " \n" \
772 + "movd %%mm1, %0 \n" \
773 + "femms \n" \
774 + : "=m"(output) : "g"(coef_ptr), "g"(hist1_ptr), "m"(tmp)
775 + : "eax", "edi", "memory"
776 +#ifdef __MMX__
777 + , "mm1", "mm2", "mm3"
778 +#endif
779 + );
781 + return output;
784 +static inline void atti386_produceOutput1(int tmplen, Bit16s myvolume, Bit16s *useBuf, Bit16s *snd) {
785 + __asm__ __volatile__(
786 + "movl %0, %%ecx \n" \
787 + "movw %1, %%ax \n" \
788 + "shll $16, %%eax \n" \
789 + "movw %1, %%ax \n" \
790 + "movd %%eax, %%mm3 \n" \
791 + "movd %%eax, %%mm2 \n" \
792 + "psllq $32, %%mm3 \n" \
793 + "por %%mm2, %%mm3 \n" \
794 + "movl %2, %%esi \n" \
795 + "movl %3, %%edi \n" \
796 + "1: \n" \
797 + "movq 0(%%esi), %%mm1 \n" \
798 + "movq 0(%%edi), %%mm2 \n" \
799 + "pmulhw %%mm3, %%mm1 \n" \
800 + "paddw %%mm2, %%mm1 \n" \
801 + "movq %%mm1, 0(%%edi) \n" \
802 + " \n" \
803 + "addl $8, %%esi \n" \
804 + "addl $8, %%edi \n" \
805 + " \n" \
806 + "decl %%ecx \n" \
807 + "cmpl $0, %%ecx \n" \
808 + "jg 1b \n" \
809 + "emms \n" \
810 + : : "g"(tmplen), "g"(myvolume), "g"(useBuf), "g"(snd)
811 + : "eax", "ecx", "edi", "esi", "memory"
812 +#ifdef __MMX__
813 + , "mm1", "mm2", "mm3"
814 +#endif
815 + );
818 +static inline void atti386_produceOutput2(Bit32u len, Bit16s *snd, float *sndbufl, float *sndbufr, float *multFactor) {
819 + __asm__ __volatile__(
820 + "movl %4, %%ecx \n" \
821 + "shrl $1, %%ecx \n" \
822 + "addl $4, %%ecx \n" \
823 + "pushl %%ecx \n" \
824 + " \n" \
825 + "movl %0, %%esi \n" \
826 + "movups 0(%%esi), %%xmm1 \n" \
827 + " \n" \
828 + "movl %1, %%esi \n" \
829 + "movl %2, %%edi \n" \
830 + "1: \n" \
831 + "xorl %%eax, %%eax \n" \
832 + "movw 0(%1), %%ax \n" \
833 + "cwde \n" \
834 + "incl %1 \n" \
835 + "incl %1 \n" \
836 + "movd %%eax, %%mm1 \n" \
837 + "psrlq $32, %%mm1 \n" \
838 + "movw 0(%1), %%ax \n" \
839 + "incl %1 \n" \
840 + "incl %1 \n" \
841 + "movd %%eax, %%mm2 \n" \
842 + "por %%mm2, %%mm1 \n" \
843 + " \n" \
844 + "decl %%ecx \n" \
845 + "jnz 1b \n" \
846 + " \n" \
847 + "popl %%ecx \n" \
848 + "movl %1, %%esi \n" \
849 + "movl %3, %%edi \n" \
850 + "incl %%esi \n" \
851 + "2: \n" \
852 + "decl %%ecx \n" \
853 + "jnz 2b \n" \
854 + : : "g"(multFactor), "r"(snd), "g"(sndbufl), "g"(sndbufr), "g"(len)
855 + : "eax", "ecx", "edi", "esi", "mm1", "mm2", "xmm1", "memory");
858 +static inline void atti386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) {
859 + __asm__ __volatile__(
860 + "movl %0, %%ecx \n" \
861 + "movl %1, %%esi \n" \
862 + "movl %2, %%edi \n" \
863 + "1: \n" \
864 + "movq 0(%%edi), %%mm1 \n" \
865 + "movq 0(%%esi), %%mm2 \n" \
866 + "paddw %%mm2, %%mm1 \n" \
867 + "movq %%mm1, 0(%%esi) \n" \
868 + "addl $8, %%edi \n" \
869 + "addl $8, %%esi \n" \
870 + "decl %%ecx \n" \
871 + "cmpl $0, %%ecx \n" \
872 + "jg 1b \n" \
873 + "emms \n" \
874 + : : "g"(len), "g"(buf1), "g"(buf2)
875 + : "ecx", "edi", "esi", "memory"
876 +#ifdef __MMX__
877 + , "mm1", "mm2"
878 +#endif
879 + );
882 +static inline void atti386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) {
883 + __asm__ __volatile__(
884 + "movl %0, %%ecx \n" \
885 + "movl %1, %%esi \n" \
886 + "movl %2, %%edi \n" \
887 + "1: \n" \
888 + "movq 0(%%esi), %%mm1 \n" \
889 + "movq 0(%%edi), %%mm2 \n" \
890 + "movq %%mm1, %%mm3 \n" \
891 + "pmulhw %%mm2, %%mm1 \n" \
892 + "paddw %%mm3, %%mm1 \n" \
893 + "movq %%mm1, 0(%%esi) \n" \
894 + "addl $8, %%edi \n" \
895 + "addl $8, %%esi \n" \
896 + "decl %%ecx \n" \
897 + "cmpl $0, %%ecx \n" \
898 + "jg 1b \n" \
899 + "emms \n" \
900 + : : "g"(len), "g"(buf1), "g"(buf2)
901 + : "ecx", "edi", "esi", "memory"
902 +#ifdef __MMX__
903 + , "mm1", "mm2", "mm3"
904 +#endif
905 + );
908 +static inline void atti386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) {
909 + __asm__ __volatile__(
910 + "movl %0, %%ecx \n" \
911 + "movl %1, %%esi \n" \
912 + "movl %2, %%edi \n" \
913 + "1: \n" \
914 + "movq 0(%%esi), %%mm1 \n" \
915 + "movq 0(%%edi), %%mm2 \n" \
916 + "pmulhw %%mm2, %%mm1 \n" \
917 + "movq %%mm1, 0(%%esi) \n" \
918 + "addl $8, %%edi \n" \
919 + "addl $8, %%esi \n" \
920 + "decl %%ecx \n" \
921 + "cmpl $0, %%ecx \n" \
922 + "jg 1b \n" \
923 + "emms \n" \
924 + : : "g"(len), "g"(buf1), "g"(buf2)
925 + : "ecx", "edi", "esi", "memory"
926 +#ifdef __MMX__
927 + , "mm1", "mm2"
928 +#endif
929 + );
932 +static inline void atti386_partialProductOutput(int quadlen, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *p1buf) {
933 + __asm__ __volatile__(
934 + "movl %0, %%ecx \n" \
935 + "movw %1, %%ax \n" \
936 + "shll $16, %%eax \n" \
937 + "movw %2, %%ax \n" \
938 + "movd %%eax, %%mm1 \n" \
939 + "movd %%eax, %%mm2 \n" \
940 + "psllq $32, %%mm1 \n" \
941 + "por %%mm2, %%mm1 \n" \
942 + "movl %3, %%edi \n" \
943 + "movl %4, %%esi \n" \
944 + "pushl %%ebx \n" \
945 + "1: \n" \
946 + "movw 0(%%esi), %%bx \n" \
947 + "addl $2, %%esi \n" \
948 + "movw 0(%%esi), %%dx \n" \
949 + "addl $2, %%esi \n" \
950 + "" \
951 + "movw %%dx, %%ax \n" \
952 + "shll $16, %%eax \n" \
953 + "movw %%dx, %%ax \n" \
954 + "movd %%eax, %%mm2 \n" \
955 + "psllq $32, %%mm2 \n" \
956 + "movw %%bx, %%ax \n" \
957 + "shll $16, %%eax \n" \
958 + "movw %%bx, %%ax \n" \
959 + "movd %%eax, %%mm3 \n" \
960 + "por %%mm3, %%mm2 \n" \
961 + "" \
962 + "pmulhw %%mm1, %%mm2 \n" \
963 + "movq %%mm2, 0(%%edi) \n" \
964 + "addl $8, %%edi \n" \
965 + "" \
966 + "decl %%ecx \n" \
967 + "cmpl $0, %%ecx \n" \
968 + "jg 1b \n" \
969 + "emms \n" \
970 + "popl %%ebx \n" \
971 + : : "g"(quadlen), "g"(leftvol), "g"(rightvol), "g"(partialBuf), "g"(p1buf)
972 + : "eax", "ecx", "edx", "edi", "esi", "memory"
973 +#ifdef __MMX__
974 + , "mm1", "mm2", "mm3"
975 +#endif
976 + );
979 +#endif
981 +bool DetectSIMD() {
982 +#ifdef _MSC_VER
983 + bool found_simd;
984 + __asm {
985 + pushfd
986 + pop eax // get EFLAGS into eax
987 + mov ebx,eax // keep a copy
988 + xor eax,0x200000
989 + // toggle CPUID bit
991 + push eax
992 + popfd // set new EFLAGS
993 + pushfd
994 + pop eax // EFLAGS back into eax
996 + xor eax,ebx
997 + // have we changed the ID bit?
999 + je NO_SIMD
1000 + // No, no CPUID instruction
1002 + // we could toggle the
1003 + // ID bit so CPUID is present
1004 + mov eax,1
1006 + cpuid // get processor features
1007 + test edx,1<<25 // check the SIMD bit
1008 + jz NO_SIMD
1009 + mov found_simd,1
1010 + jmp DONE
1011 + NO_SIMD:
1012 + mov found_simd,0
1013 + DONE:
1015 + return found_simd;
1016 +#else
1017 + return atti386_DetectSIMD();
1018 +#endif
1021 +bool Detect3DNow() {
1022 +#ifdef _MSC_VER
1023 + bool found3D = false;
1024 + __asm {
1025 + pushfd
1026 + pop eax
1027 + mov edx, eax
1028 + xor eax, 00200000h
1029 + push eax
1030 + popfd
1031 + pushfd
1032 + pop eax
1033 + xor eax, edx
1034 + jz NO_3DNOW
1036 + mov eax, 80000000h
1037 + cpuid
1039 + cmp eax, 80000000h
1040 + jbe NO_3DNOW
1042 + mov eax, 80000001h
1043 + cpuid
1044 + test edx, 80000000h
1045 + jz NO_3DNOW
1046 + mov found3D, 1
1047 +NO_3DNOW:
1050 + return found3D;
1051 +#else
1052 + return atti386_Detect3DNow();
1053 +#endif
1056 +float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr) {
1057 + float output;
1059 + // 1st number of coefficients array is overall input scale factor, or filter gain
1060 + output = input * (*coef_ptr++);
1062 +#ifdef _MSC_VER
1063 + __asm {
1065 + movss xmm1, output
1067 + mov eax, coef_ptr
1068 + movups xmm2, [eax]
1070 + mov eax, hist1_ptr
1071 + movlps xmm3, [eax]
1072 + shufps xmm3, xmm3, 44h
1073 + // hist1_ptr+1, hist1_ptr, hist1_ptr+1, hist1_ptr
1075 + mulps xmm2, xmm3
1077 + subss xmm1, xmm2
1078 + // Rotate elements right
1079 + shufps xmm2, xmm2, 39h
1080 + subss xmm1, xmm2
1082 + // Store new_hist
1083 + movss DWORD PTR [eax], xmm1
1085 + // Rotate elements right
1086 + shufps xmm2, xmm2, 39h
1087 + addss xmm1, xmm2
1089 + // Rotate elements right
1090 + shufps xmm2, xmm2, 39h
1091 + addss xmm1, xmm2
1093 + // Store previous hist
1094 + movss DWORD PTR [eax+4], xmm3
1096 + add coef_ptr, 16
1097 + add hist1_ptr, 8
1099 + mov eax, coef_ptr
1100 + movups xmm2, [eax]
1102 + mov eax, hist1_ptr
1103 + movlps xmm3, [eax]
1104 + shufps xmm3, xmm3, 44h
1105 + // hist1_ptr+1, hist1_ptr, hist1_ptr+1, hist1_ptr
1107 + mulps xmm2, xmm3
1109 + subss xmm1, xmm2
1110 + // Rotate elements right
1111 + shufps xmm2, xmm2, 39h
1112 + subss xmm1, xmm2
1114 + // Store new_hist
1115 + movss DWORD PTR [eax], xmm1
1117 + // Rotate elements right
1118 + shufps xmm2, xmm2, 39h
1119 + addss xmm1, xmm2
1121 + // Rotate elements right
1122 + shufps xmm2, xmm2, 39h
1123 + addss xmm1, xmm2
1125 + // Store previous hist
1126 + movss DWORD PTR [eax+4], xmm3
1128 + movss output, xmm1
1130 +#else
1131 + output = atti386_iir_filter_sse(&output, hist1_ptr, coef_ptr);
1132 +#endif
1133 + return output;
1136 +float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr) {
1137 + float output;
1139 + // 1st number of coefficients array is overall input scale factor, or filter gain
1140 + output = input * (*coef_ptr++);
1142 + // I find it very sad that 3DNow requires twice as many instructions as Intel's SSE
1143 + // Intel does have the upper hand here.
1144 +#ifdef _MSC_VER
1145 + float tmp;
1146 + __asm {
1147 + movq mm1, output
1148 + mov ebx, coef_ptr
1149 + movq mm2, [ebx]
1151 + mov eax, hist1_ptr;
1152 + movq mm3, [eax]
1154 + pfmul mm2, mm3
1155 + pfsub mm1, mm2
1157 + psrlq mm2, 32
1158 + pfsub mm1, mm2
1160 + // Store new hist
1161 + movd tmp, mm1
1163 + add ebx, 8
1164 + movq mm2, [ebx]
1165 + movq mm3, [eax]
1167 + pfmul mm2, mm3
1168 + pfadd mm1, mm2
1170 + psrlq mm2, 32
1171 + pfadd mm1, mm2
1173 + push tmp
1174 + pop DWORD PTR [eax]
1176 + movd DWORD PTR [eax+4], mm3
1178 + add ebx, 8
1179 + add eax, 8
1181 + movq mm2, [ebx]
1182 + movq mm3, [eax]
1184 + pfmul mm2, mm3
1185 + pfsub mm1, mm2
1187 + psrlq mm2, 32
1188 + pfsub mm1, mm2
1190 + // Store new hist
1191 + movd tmp, mm1
1193 + add ebx, 8
1194 + movq mm2, [ebx]
1195 + movq mm3, [eax]
1197 + pfmul mm2, mm3
1198 + pfadd mm1, mm2
1200 + psrlq mm2, 32
1201 + pfadd mm1, mm2
1203 + push tmp
1204 + pop DWORD PTR [eax]
1205 + movd DWORD PTR [eax+4], mm3
1207 + movd output, mm1
1209 + femms
1211 +#else
1212 + output = atti386_iir_filter_3DNow(output, hist1_ptr, coef_ptr);
1213 +#endif
1214 + return output;
1217 +#if MT32EMU_USE_MMX > 0
1219 +int i386_partialProductOutput(int len, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *mixedBuf) {
1220 + int tmplen = len >> 1;
1221 + if (tmplen == 0) {
1222 + return 0;
1224 +#ifdef _MSC_VER
1225 + __asm {
1226 + mov ecx,tmplen
1227 + mov ax, leftvol
1228 + shl eax,16
1229 + mov ax, rightvol
1230 + movd mm1, eax
1231 + movd mm2, eax
1232 + psllq mm1, 32
1233 + por mm1, mm2
1234 + mov edi, partialBuf
1235 + mov esi, mixedBuf
1236 +mmxloop1:
1237 + mov bx, [esi]
1238 + add esi,2
1239 + mov dx, [esi]
1240 + add esi,2
1242 + mov ax, dx
1243 + shl eax, 16
1244 + mov ax, dx
1245 + movd mm2,eax
1246 + psllq mm2, 32
1247 + mov ax, bx
1248 + shl eax, 16
1249 + mov ax, bx
1250 + movd mm3,eax
1251 + por mm2,mm3
1253 + pmulhw mm2, mm1
1254 + movq [edi], mm2
1255 + add edi, 8
1257 + dec ecx
1258 + cmp ecx,0
1259 + jg mmxloop1
1260 + emms
1262 +#else
1263 + atti386_partialProductOutput(tmplen, leftvol, rightvol, partialBuf, mixedBuf);
1264 +#endif
1265 + return tmplen << 1;
1268 +int i386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) {
1269 + int tmplen = len >> 2;
1270 + if (tmplen == 0) {
1271 + return 0;
1273 +#ifdef _MSC_VER
1274 + __asm {
1275 + mov ecx, tmplen
1276 + mov esi, buf1
1277 + mov edi, buf2
1279 +mixloop1:
1280 + movq mm1, [edi]
1281 + movq mm2, [esi]
1282 + paddw mm1,mm2
1283 + movq [esi],mm1
1284 + add edi,8
1285 + add esi,8
1287 + dec ecx
1288 + cmp ecx,0
1289 + jg mixloop1
1290 + emms
1292 +#else
1293 + atti386_mixBuffers(buf1, buf2, tmplen);
1294 +#endif
1295 + return tmplen << 2;
1299 +int i386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) {
1300 + int tmplen = len >> 2;
1301 + if (tmplen == 0) {
1302 + return 0;
1304 +#ifdef _MSC_VER
1305 + __asm {
1306 + mov ecx, tmplen
1307 + mov esi, buf1
1308 + mov edi, buf2
1310 +mixloop2:
1311 + movq mm1, [esi]
1312 + movq mm2, [edi]
1313 + movq mm3, mm1
1314 + pmulhw mm1, mm2
1315 + paddw mm1,mm3
1316 + movq [esi],mm1
1317 + add edi,8
1318 + add esi,8
1320 + dec ecx
1321 + cmp ecx,0
1322 + jg mixloop2
1323 + emms
1325 +#else
1326 + atti386_mixBuffersRingMix(buf1, buf2, tmplen);
1327 +#endif
1328 + return tmplen << 2;
1331 +int i386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) {
1332 + int tmplen = len >> 2;
1333 + if (tmplen == 0) {
1334 + return 0;
1336 +#ifdef _MSC_VER
1337 + __asm {
1338 + mov ecx, tmplen
1339 + mov esi, buf1
1340 + mov edi, buf2
1342 +mixloop3:
1343 + movq mm1, [esi]
1344 + movq mm2, [edi]
1345 + pmulhw mm1, mm2
1346 + movq [esi],mm1
1347 + add edi,8
1348 + add esi,8
1350 + dec ecx
1351 + cmp ecx,0
1352 + jg mixloop3
1353 + emms
1355 +#else
1356 + atti386_mixBuffersRing(buf1, buf2, tmplen);
1357 +#endif
1358 + return tmplen << 2;
1361 +int i386_produceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume) {
1362 + int tmplen = (len >> 1);
1363 + if (tmplen == 0) {
1364 + return 0;
1366 +#ifdef _MSC_VER
1367 + __asm {
1368 + mov ecx, tmplen
1369 + mov ax,volume
1370 + shl eax,16
1371 + mov ax,volume
1372 + movd mm3,eax
1373 + movd mm2,eax
1374 + psllq mm3, 32
1375 + por mm3,mm2
1376 + mov esi, useBuf
1377 + mov edi, stream
1378 +mixloop4:
1379 + movq mm1, [esi]
1380 + movq mm2, [edi]
1381 + pmulhw mm1, mm3
1382 + paddw mm1,mm2
1383 + movq [edi], mm1
1385 + add esi,8
1386 + add edi,8
1388 + dec ecx
1389 + cmp ecx,0
1390 + jg mixloop4
1391 + emms
1393 +#else
1394 + atti386_produceOutput1(tmplen, volume, useBuf, stream);
1395 +#endif
1396 + return tmplen << 1;
1399 +#endif
1403 +#endif
1404 diff -urN dosbox.orig/src/gui/i386.h dosbox/src/gui/i386.h
1405 --- dosbox.orig/src/gui/i386.h 1969-12-31 21:00:00.000000000 -0300
1406 +++ dosbox/src/gui/i386.h 2011-01-11 20:15:44.308165436 -0300
1407 @@ -0,0 +1,49 @@
1408 +/* Copyright (c) 2003-2005 Various contributors
1410 + * Permission is hereby granted, free of charge, to any person obtaining a copy
1411 + * of this software and associated documentation files (the "Software"), to
1412 + * deal in the Software without restriction, including without limitation the
1413 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
1414 + * sell copies of the Software, and to permit persons to whom the Software is
1415 + * furnished to do so, subject to the following conditions:
1417 + * The above copyright notice and this permission notice shall be included in
1418 + * all copies or substantial portions of the Software.
1420 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1421 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1422 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1423 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1424 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
1425 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
1426 + * IN THE SOFTWARE.
1427 + */
1429 +#ifndef MT32EMU_I386_H
1430 +#define MT32EMU_I386_H
1432 +namespace MT32Emu {
1433 +#ifdef MT32EMU_HAVE_X86
1435 +// Function that detects the availablity of SSE SIMD instructions
1436 +bool DetectSIMD();
1437 +// Function that detects the availablity of 3DNow instructions
1438 +bool Detect3DNow();
1440 +float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr);
1441 +float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr);
1442 +float iir_filter_normal(float input,float *hist1_ptr, float *coef_ptr);
1444 +#if MT32EMU_USE_MMX > 0
1445 +int i386_partialProductOutput(int len, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *mixedBuf);
1446 +int i386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len);
1447 +int i386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len);
1448 +int i386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len);
1449 +int i386_produceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume);
1450 +#endif
1452 +#endif
1456 +#endif
1457 diff -urN dosbox.orig/src/gui/midi.cpp dosbox/src/gui/midi.cpp
1458 --- dosbox.orig/src/gui/midi.cpp 2011-01-11 20:09:19.064832103 -0300
1459 +++ dosbox/src/gui/midi.cpp 2011-01-11 20:15:44.304832102 -0300
1460 @@ -76,6 +76,7 @@
1461 MidiHandler Midi_none;
1463 /* Include different midi drivers, lowest ones get checked first for default */
1464 +#include "midi_mt32.h"
1466 #if defined(MACOSX)
1468 diff -urN dosbox.orig/src/gui/midi_mt32.h dosbox/src/gui/midi_mt32.h
1469 --- dosbox.orig/src/gui/midi_mt32.h 1969-12-31 21:00:00.000000000 -0300
1470 +++ dosbox/src/gui/midi_mt32.h 2011-01-11 20:15:44.304832102 -0300
1471 @@ -0,0 +1,247 @@
1472 +#include "mt32emu.h"
1473 +#include "mixer.h"
1474 +#include "control.h"
1475 +#include <SDL_thread.h>
1477 +#include <iostream>
1478 +#include <string>
1480 +using namespace std;
1482 +void cpuID(unsigned i, unsigned regs[4]) {
1483 + asm volatile
1484 + ("cpuid" : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3]): "a" (i), "c" (0));
1485 + // ECX is set to zero for CPUID function 4
1488 +int cpu_check(void) {
1489 + unsigned regs[4];
1490 + char vendor[12];
1491 + cpuID(0, regs);
1492 + ((unsigned *)vendor)[0] = regs[1]; // EBX
1493 + ((unsigned *)vendor)[1] = regs[3]; // EDX
1494 + ((unsigned *)vendor)[2] = regs[2]; // ECX
1495 + string cpuVendor = string(vendor, 12);
1497 + // Get CPU features
1498 + cpuID(1, regs);
1499 + unsigned cpuFeatures = regs[3]; // EDX
1500 + if (cpuVendor == "GenuineIntel" && cpuFeatures & (1 << 28)) { // HTT bit
1501 + // Logical core count per CPU
1502 + cpuID(1, regs); unsigned logical = (regs[1] >> 16) & 0xff; // EBX[23:16]
1503 + unsigned cores = logical;
1505 + if (cpuVendor == "GenuineIntel") {
1506 + // Get DCP cache info
1507 + cpuID(4, regs);
1508 + cores = ((regs[0] >> 26) & 0x3f) + 1; // EAX[31:26] + 1
1509 + } else if (cpuVendor == "AuthenticAMD") {
1510 + // Get NC: Number of CPU cores - 1
1511 + cpuID(0x80000008, regs);
1512 + cores = ((unsigned)(regs[2] & 0xff)) + 1; // ECX[7:0] + 1
1513 + } else return 1;
1514 + return cores;
1516 + return 0;
1519 +MT32Emu::Synth *_usesynth;
1520 +MixerChannel *mt32chan;
1522 +static struct {
1523 + SDL_mutex * mutex;
1524 + SDL_Thread * thread;
1525 + SDL_semaphore * sem;
1526 + bool multicore;
1527 + volatile bool running, busy;
1529 + Bit8u len, play;
1530 + Bit8u Temp[MIXER_BUFSIZE], msg[SYSEX_SIZE];
1531 +} mt32;
1533 +static int MT32_Thread(void*) {
1534 + SDL_LockMutex(mt32.mutex);
1536 + while(mt32.running) {
1538 + Bitu len;
1539 + if(!(mt32.play) && !(mt32.len)) {
1540 + SDL_UnlockMutex(mt32.mutex);
1541 + SDL_SemWait(mt32.sem);
1542 + SDL_LockMutex(mt32.mutex);
1545 + mt32.busy = true;
1546 + while(mt32.play) {
1547 + len = mt32.play;
1548 + Bit32u *tmp = ((Bit32u*)mt32.msg);
1549 + SDL_UnlockMutex(mt32.mutex);
1551 + while(len--) {
1552 + _usesynth->playMsg(*tmp++);
1555 + SDL_LockMutex(mt32.mutex);
1556 + len = tmp-(Bit32u*)mt32.msg;
1557 + mt32.play -= len;
1558 + if(mt32.play) SDL_memmove(mt32.msg, tmp, mt32.play<<2);
1561 + while(mt32.len) {
1562 + len = (mt32.len>>2) < MIXER_BUFSIZE ? mt32.len : MIXER_BUFSIZE<<2;
1563 + mt32.len -= len;
1565 +#ifdef MT32DEBUG
1566 + if(mt32.len) LOG_MSG("MT32:WARNING: len left (%d)", mt32.len);
1567 +#endif
1569 + SDL_UnlockMutex(mt32.mutex);
1570 + _usesynth->render((Bit16s *)mt32.Temp, len);
1571 + mt32chan->AddSamples_s16(len,(Bit16s *)mt32.Temp);
1572 + SDL_LockMutex(mt32.mutex);
1573 + break;
1575 + mt32.busy = false;
1578 + SDL_UnlockMutex(mt32.mutex);
1579 + return 0;
1582 +static void MT32_CallBack(Bitu len) {
1583 + if(mt32.multicore) {
1584 + SDL_LockMutex(mt32.mutex);
1585 + mt32.len += len;
1586 + SDL_UnlockMutex(mt32.mutex);
1587 + SDL_SemPost(mt32.sem);
1588 + } else {
1589 + _usesynth->render((Bit16s *)MixTemp, len);
1590 + mt32chan->AddSamples_s16(len,(Bit16s *)MixTemp);
1594 +static int report(void *userData, MT32Emu::ReportType type, const void *reportData) {
1595 + switch(type) {
1596 + case MT32Emu::ReportType_errorControlROM:
1597 + LOG_MSG("MT32:Couldn't find control files");
1598 + break;
1599 + case MT32Emu::ReportType_errorPCMROM:
1600 + LOG_MSG("MT32:Couldn't open MT32_PCM.ROM file");
1601 + break;
1602 + default:
1603 + //LOG(LOG_ALL,LOG_NORMAL)("MT32: Report %d",type);
1604 + break;
1606 + return 0;
1609 +class MidiHandler_mt32: public MidiHandler {
1610 +private:
1611 + MT32Emu::Synth *_synth;
1612 + int _outputRate;
1613 + bool isOpen;
1615 +public:
1616 + MidiHandler_mt32() : isOpen(false),MidiHandler() {};
1617 + const char * GetName(void) { return "mt32";};
1618 + bool Open(const char * conf) {
1619 + MT32Emu::SynthProperties tmpProp;
1620 + memset(&tmpProp, 0, sizeof(tmpProp));
1621 + //tmpProp.sampleRate = atoi(conf);
1622 + Section_prop* section=static_cast<Section_prop *>(control->GetSection("midi"));
1623 + if(!strcmp(section->Get_string("mt32rate"),"auto")) {
1624 + section=static_cast<Section_prop *>(control->GetSection("mixer"));
1625 + tmpProp.sampleRate = section->Get_int("rate");
1626 + if(tmpProp.sampleRate <= 44100) tmpProp.sampleRate = 22050;
1627 + else if(tmpProp.sampleRate <= 11025) tmpProp.sampleRate = 11025;
1628 + else tmpProp.sampleRate = 22050;
1629 + } else {
1630 + tmpProp.sampleRate=atoi(section->Get_string("mt32rate"));
1632 + LOG_MSG("MT32:Set sample rate to %d", tmpProp.sampleRate);
1634 + /* Create MT32 thread */
1635 + mt32.multicore=(cpu_check()>1?true:false);
1636 + if(mt32.multicore) {
1637 + mt32.mutex = SDL_CreateMutex();
1638 + mt32.sem = SDL_CreateSemaphore(0);
1639 + mt32.running = true;
1640 + mt32.busy = false;
1641 + mt32.play = 0;
1642 + mt32.thread = SDL_CreateThread(MT32_Thread, NULL);
1645 + tmpProp.useDefaultReverb = false;
1646 + tmpProp.useReverb = true;
1647 + tmpProp.reverbType = 0;
1648 + tmpProp.reverbTime = 5;
1649 + tmpProp.reverbLevel = 3;
1650 + tmpProp.userData = this;
1651 + //tmpProp.printDebug = &vdebug;
1652 + tmpProp.report = &report;
1653 + _synth = new MT32Emu::Synth();
1654 + if (_synth->open(tmpProp)==0) {
1655 + LOG(LOG_ALL,LOG_ERROR)("MT32:Error initializing emulation");
1656 + return false;
1658 + _usesynth=_synth;
1660 + mt32chan=MIXER_AddChannel(MT32_CallBack,tmpProp.sampleRate,"MT32");
1661 + mt32chan->Enable(false);
1662 + return true;
1663 + };
1664 + void Close(void) {
1665 + if (!isOpen) return;
1666 + mt32chan->Enable(false);
1667 + if(mt32.multicore) {
1668 + mt32.running = false;
1669 + SDL_SemPost(mt32.sem);
1670 + SDL_WaitThread(mt32.thread, NULL);
1671 + SDL_DestroyMutex(mt32.mutex);
1673 + _synth->close();
1674 + delete _synth;
1675 + _synth = NULL;
1676 + isOpen=false;
1677 + };
1678 + void PlayMsg(Bit8u * msg) {
1679 + mt32chan->Enable(true);
1680 + if(mt32.multicore) {
1681 + // Try to queue play commands
1682 + SDL_LockMutex(mt32.mutex);
1683 + // Playcommand buffer full?
1684 + while(!(mt32.play < SYSEX_SIZE>>2)) {
1685 + SDL_UnlockMutex(mt32.mutex);
1686 + LOG_MSG("MT32:Playback buffer full...");
1687 + SDL_LockMutex(mt32.mutex);
1689 + SDL_memcpy(mt32.msg+(mt32.play<<2), msg, sizeof(Bit32u));
1690 + mt32.play ++;
1691 + SDL_UnlockMutex(mt32.mutex);
1692 + SDL_SemPost(mt32.sem);
1693 + } else {
1694 + _synth->playMsg(*((Bit32u*) msg));
1696 + };
1697 + void PlaySysex(Bit8u * sysex,Bitu len) {
1698 + if(mt32.multicore) {
1699 + SDL_LockMutex(mt32.mutex);
1700 + while(mt32.busy) {
1701 + SDL_UnlockMutex(mt32.mutex);
1702 +#ifdef MT32DEBUG
1703 + LOG_MSG("MT32:Waiting to deliver sysex");
1704 +#endif
1705 + SDL_LockMutex(mt32.mutex);
1709 + if(sysex[0] == 0xf0) {
1710 + _synth->playSysex(sysex, len);
1711 + } else {
1712 + _synth->playSysexWithoutFraming(sysex, len);
1714 + if(mt32.multicore) SDL_UnlockMutex(mt32.mutex);
1715 + };
1718 +MidiHandler_mt32 Midi_mt32;
1719 diff -urN dosbox.orig/src/gui/mt32_file.cpp dosbox/src/gui/mt32_file.cpp
1720 --- dosbox.orig/src/gui/mt32_file.cpp 1969-12-31 21:00:00.000000000 -0300
1721 +++ dosbox/src/gui/mt32_file.cpp 2011-01-11 20:15:44.308165436 -0300
1722 @@ -0,0 +1,113 @@
1723 +/* Copyright (c) 2003-2005 Various contributors
1725 + * Permission is hereby granted, free of charge, to any person obtaining a copy
1726 + * of this software and associated documentation files (the "Software"), to
1727 + * deal in the Software without restriction, including without limitation the
1728 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
1729 + * sell copies of the Software, and to permit persons to whom the Software is
1730 + * furnished to do so, subject to the following conditions:
1732 + * The above copyright notice and this permission notice shall be included in
1733 + * all copies or substantial portions of the Software.
1735 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1736 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1737 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1738 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1739 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
1740 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
1741 + * IN THE SOFTWARE.
1742 + */
1745 +#include "mt32emu.h"
1747 +namespace MT32Emu {
1749 + bool ANSIFile::open(const char *filename, OpenMode mode) {
1750 + const char *fmode;
1751 + if (mode == OpenMode_read) {
1752 + fmode = "rb";
1753 + } else {
1754 + fmode = "wb";
1756 + fp = fopen(filename, fmode);
1757 + return (fp != NULL);
1760 + void ANSIFile::close() {
1761 + fclose(fp);
1764 + size_t ANSIFile::read(void *in, size_t size) {
1765 + return fread(in, 1, size, fp);
1768 + bool ANSIFile::readLine(char *in, size_t size) {
1769 + return fgets(in, (int)size, fp) != NULL;
1772 + bool ANSIFile::readBit8u(Bit8u *in) {
1773 + int c = fgetc(fp);
1774 + if (c == EOF)
1775 + return false;
1776 + *in = (Bit8u)c;
1777 + return true;
1780 +bool File::readBit16u(Bit16u *in) {
1781 + Bit8u b[2];
1782 + if (read(&b[0], 2) != 2)
1783 + return false;
1784 + *in = ((b[0] << 8) | b[1]);
1785 + return true;
1788 +bool File::readBit32u(Bit32u *in) {
1789 + Bit8u b[4];
1790 + if (read(&b[0], 4) != 4)
1791 + return false;
1792 + *in = ((b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]);
1793 + return true;
1796 + size_t ANSIFile::write(const void *out, size_t size) {
1797 + return fwrite(out, 1, size, fp);
1800 + bool ANSIFile::writeBit8u(Bit8u out) {
1801 + return fputc(out, fp) != EOF;
1804 +bool File::writeBit16u(Bit16u out) {
1805 + if (!writeBit8u((Bit8u)((out & 0xFF00) >> 8))) {
1806 + return false;
1808 + if (!writeBit8u((Bit8u)(out & 0x00FF))) {
1809 + return false;
1811 + return true;
1814 +bool File::writeBit32u(Bit32u out) {
1815 + if (!writeBit8u((Bit8u)((out & 0xFF000000) >> 24))) {
1816 + return false;
1818 + if (!writeBit8u((Bit8u)((out & 0x00FF0000) >> 16))) {
1819 + return false;
1821 + if (!writeBit8u((Bit8u)((out & 0x0000FF00) >> 8))) {
1822 + return false;
1824 + if (!writeBit8u((Bit8u)(out & 0x000000FF))) {
1825 + return false;
1827 + return true;
1830 + bool ANSIFile::isEOF() {
1831 + return feof(fp) != 0;
1834 +} // End of namespace MT32Emu
1836 diff -urN dosbox.orig/src/gui/mt32_file.h dosbox/src/gui/mt32_file.h
1837 --- dosbox.orig/src/gui/mt32_file.h 1969-12-31 21:00:00.000000000 -0300
1838 +++ dosbox/src/gui/mt32_file.h 2011-01-11 20:15:44.308165436 -0300
1839 @@ -0,0 +1,69 @@
1840 +/* Copyright (c) 2003-2005 Various contributors
1842 + * Permission is hereby granted, free of charge, to any person obtaining a copy
1843 + * of this software and associated documentation files (the "Software"), to
1844 + * deal in the Software without restriction, including without limitation the
1845 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
1846 + * sell copies of the Software, and to permit persons to whom the Software is
1847 + * furnished to do so, subject to the following conditions:
1849 + * The above copyright notice and this permission notice shall be included in
1850 + * all copies or substantial portions of the Software.
1852 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1853 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1854 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1855 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1856 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
1857 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
1858 + * IN THE SOFTWARE.
1859 + */
1861 +#ifndef MT32EMU_FILE_H
1862 +#define MT32EMU_FILE_H
1864 +#include <stdio.h>
1865 +#include <string.h>
1869 +namespace MT32Emu {
1871 +class File {
1872 +public:
1873 + enum OpenMode {
1874 + OpenMode_read = 0,
1875 + OpenMode_write = 1
1876 + };
1877 + virtual ~File() {}
1878 + virtual void close() = 0;
1879 + virtual size_t read(void *in, size_t size) = 0;
1880 + virtual bool readBit8u(Bit8u *in) = 0;
1881 + virtual bool readBit16u(Bit16u *in);
1882 + virtual bool readBit32u(Bit32u *in);
1883 + virtual size_t write(const void *out, size_t size) = 0;
1884 + virtual bool writeBit8u(Bit8u out) = 0;
1885 + // Note: May write a single byte to the file before failing
1886 + virtual bool writeBit16u(Bit16u out);
1887 + // Note: May write some (<4) bytes to the file before failing
1888 + virtual bool writeBit32u(Bit32u out);
1889 + virtual bool isEOF() = 0;
1892 +class ANSIFile: public File {
1893 +private:
1894 + FILE *fp;
1895 +public:
1896 + bool open(const char *filename, OpenMode mode);
1897 + void close();
1898 + size_t read(void *in, size_t size);
1899 + bool readLine(char *in, size_t size);
1900 + bool readBit8u(Bit8u *in);
1901 + size_t write(const void *out, size_t size);
1902 + bool writeBit8u(Bit8u out);
1903 + bool isEOF();
1906 +} // End of namespace MT32Emu
1908 +#endif
1909 diff -urN dosbox.orig/src/gui/mt32emu.h dosbox/src/gui/mt32emu.h
1910 --- dosbox.orig/src/gui/mt32emu.h 1969-12-31 21:00:00.000000000 -0300
1911 +++ dosbox/src/gui/mt32emu.h 2011-01-11 20:15:44.308165436 -0300
1912 @@ -0,0 +1,70 @@
1913 +/* Copyright (c) 2003-2005 Various contributors
1915 + * Permission is hereby granted, free of charge, to any person obtaining a copy
1916 + * of this software and associated documentation files (the "Software"), to
1917 + * deal in the Software without restriction, including without limitation the
1918 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
1919 + * sell copies of the Software, and to permit persons to whom the Software is
1920 + * furnished to do so, subject to the following conditions:
1922 + * The above copyright notice and this permission notice shall be included in
1923 + * all copies or substantial portions of the Software.
1925 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1926 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1927 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1928 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1929 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
1930 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
1931 + * IN THE SOFTWARE.
1932 + */
1934 +#ifndef MT32EMU_MT32EMU_H
1935 +#define MT32EMU_MT32EMU_H
1937 +// Debugging
1938 +//#define MT32DEBUG
1939 +// Show the instruments played
1940 +#define MT32EMU_MONITOR_INSTRUMENTS 1
1941 +// Shows number of partials MT-32 is playing, and on which parts
1942 +#define MT32EMU_MONITOR_PARTIALS 0
1943 +// Determines how the waveform cache file is handled (must be regenerated after sampling rate change)
1944 +#define MT32EMU_WAVECACHEMODE 0 // Load existing cache if possible, otherwise generate and save cache
1945 +//#define MT32EMU_WAVECACHEMODE 1 // Load existing cache if possible, otherwise generate but don't save cache
1946 +//#define MT32EMU_WAVECACHEMODE 2 // Ignore existing cache, generate and save cache
1947 +//#define MT32EMU_WAVECACHEMODE 3 // Ignore existing cache, generate but don't save cache
1949 +// Configuration
1950 +// The maximum number of partials playing simultaneously
1951 +#define MT32EMU_MAX_PARTIALS 32
1952 +// The maximum number of notes playing simultaneously per part.
1953 +// No point making it more than MT32EMU_MAX_PARTIALS, since each note needs at least one partial.
1954 +#define MT32EMU_MAX_POLY 32
1955 +// This calculates the exact frequencies of notes as they are played, instead of offsetting from pre-cached semitones. Potentially very slow.
1956 +#define MT32EMU_ACCURATENOTES 0
1958 +#if (defined (_MSC_VER) && defined(_M_IX86))
1959 +#define MT32EMU_HAVE_X86
1960 +#elif defined(__GNUC__)
1961 +#if __GNUC__ >= 3 && defined(__i386__)
1962 +#define MT32EMU_HAVE_X86
1963 +#endif
1964 +#endif
1966 +#ifdef MT32EMU_HAVE_X86
1967 +#define MT32EMU_USE_MMX 1
1968 +#else
1969 +#define MT32EMU_USE_MMX 0
1970 +#endif
1972 +#include "freeverb.h"
1973 +#include "structures.h"
1974 +#include "i386.h"
1975 +#include "mt32_file.h"
1976 +#include "tables.h"
1977 +#include "partial.h"
1978 +#include "partialManager.h"
1979 +#include "part.h"
1980 +#include "synth.h"
1982 +#endif
1983 diff -urN dosbox.orig/src/gui/part.cpp dosbox/src/gui/part.cpp
1984 --- dosbox.orig/src/gui/part.cpp 1969-12-31 21:00:00.000000000 -0300
1985 +++ dosbox/src/gui/part.cpp 2011-01-11 20:15:44.311498770 -0300
1986 @@ -0,0 +1,633 @@
1987 +/* Copyright (c) 2003-2005 Various contributors
1989 + * Permission is hereby granted, free of charge, to any person obtaining a copy
1990 + * of this software and associated documentation files (the "Software"), to
1991 + * deal in the Software without restriction, including without limitation the
1992 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
1993 + * sell copies of the Software, and to permit persons to whom the Software is
1994 + * furnished to do so, subject to the following conditions:
1996 + * The above copyright notice and this permission notice shall be included in
1997 + * all copies or substantial portions of the Software.
1999 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2000 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2001 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2002 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2003 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2004 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2005 + * IN THE SOFTWARE.
2006 + */
2008 +#include <string.h>
2009 +#include <math.h>
2011 +#include "mt32emu.h"
2013 +namespace MT32Emu {
2015 +static const Bit8u PartialStruct[13] = {
2016 + 0, 0, 2, 2, 1, 3,
2017 + 3, 0, 3, 0, 2, 1, 3 };
2019 +static const Bit8u PartialMixStruct[13] = {
2020 + 0, 1, 0, 1, 1, 0,
2021 + 1, 3, 3, 2, 2, 2, 2 };
2023 +static const float floatKeyfollow[17] = {
2024 + -1.0f, -1.0f/2.0f, -1.0f/4.0f, 0.0f,
2025 + 1.0f/8.0f, 1.0f/4.0f, 3.0f/8.0f, 1.0f/2.0f, 5.0f/8.0f, 3.0f/4.0f, 7.0f/8.0f, 1.0f,
2026 + 5.0f/4.0f, 3.0f/2.0f, 2.0f,
2027 + 1.0009765625f, 1.0048828125f
2030 +//FIXME:KG: Put this dpoly stuff somewhere better
2031 +bool dpoly::isActive() const {
2032 + return partials[0] != NULL || partials[1] != NULL || partials[2] != NULL || partials[3] != NULL;
2035 +Bit32u dpoly::getAge() const {
2036 + for (int i = 0; i < 4; i++) {
2037 + if (partials[i] != NULL) {
2038 + return partials[i]->age;
2041 + return 0;
2044 +RhythmPart::RhythmPart(Synth *useSynth, unsigned int usePartNum): Part(useSynth, usePartNum) {
2045 + strcpy(name, "Rhythm");
2046 + rhythmTemp = &synth->mt32ram.rhythmSettings[0];
2047 + refresh();
2050 +Part::Part(Synth *useSynth, unsigned int usePartNum) {
2051 + this->synth = useSynth;
2052 + this->partNum = usePartNum;
2053 + patchCache[0].dirty = true;
2054 + holdpedal = false;
2055 + patchTemp = &synth->mt32ram.patchSettings[partNum];
2056 + if (usePartNum == 8) {
2057 + // Nasty hack for rhythm
2058 + timbreTemp = NULL;
2059 + } else {
2060 + sprintf(name, "Part %d", partNum + 1);
2061 + timbreTemp = &synth->mt32ram.timbreSettings[partNum];
2063 + currentInstr[0] = 0;
2064 + currentInstr[10] = 0;
2065 + expression = 127;
2066 + volumeMult = 0;
2067 + volumesetting.leftvol = 32767;
2068 + volumesetting.rightvol = 32767;
2069 + bend = 0.0f;
2070 + memset(polyTable,0,sizeof(polyTable));
2071 + memset(patchCache, 0, sizeof(patchCache));
2074 +void Part::setHoldPedal(bool pedalval) {
2075 + if (holdpedal && !pedalval) {
2076 + holdpedal = false;
2077 + stopPedalHold();
2078 + } else {
2079 + holdpedal = pedalval;
2083 +void RhythmPart::setBend(unsigned int midiBend) {
2084 + synth->printDebug("%s: Setting bend (%d) not supported on rhythm", name, midiBend);
2085 + return;
2088 +void Part::setBend(unsigned int midiBend) {
2089 + // FIXME:KG: Slightly unbalanced increments, but I wanted min -1.0, center 0.0 and max 1.0
2090 + if (midiBend <= 0x2000) {
2091 + bend = ((signed int)midiBend - 0x2000) / (float)0x2000;
2092 + } else {
2093 + bend = ((signed int)midiBend - 0x2000) / (float)0x1FFF;
2095 + // Loop through all partials to update their bend
2096 + for (int i = 0; i < MT32EMU_MAX_POLY; i++) {
2097 + for (int j = 0; j < 4; j++) {
2098 + if (polyTable[i].partials[j] != NULL) {
2099 + polyTable[i].partials[j]->setBend(bend);
2105 +void RhythmPart::setModulation(unsigned int midiModulation) {
2106 + synth->printDebug("%s: Setting modulation (%d) not supported on rhythm", name, midiModulation);
2109 +void Part::setModulation(unsigned int midiModulation) {
2110 + // Just a bloody guess, as always, before I get things figured out
2111 + for (int t = 0; t < 4; t++) {
2112 + if (patchCache[t].playPartial) {
2113 + int newrate = (patchCache[t].modsense * midiModulation) >> 7;
2114 + //patchCache[t].lfoperiod = lfotable[newrate];
2115 + patchCache[t].lfodepth = newrate;
2116 + //FIXME:KG: timbreTemp->partial[t].lfo.depth =
2121 +void RhythmPart::refresh() {
2122 + updateVolume();
2123 + // (Re-)cache all the mapped timbres ahead of time
2124 + for (unsigned int drumNum = 0; drumNum < synth->controlROMMap->rhythmSettingsCount; drumNum++) {
2125 + int drumTimbreNum = rhythmTemp[drumNum].timbre;
2126 + if (drumTimbreNum >= 127) // 94 on MT-32
2127 + continue;
2128 + Bit16s pan = rhythmTemp[drumNum].panpot; // They use R-L 0-14...
2129 + // FIXME:KG: Panning cache should be backed up to partials using it, too
2130 + if (pan < 7) {
2131 + drumPan[drumNum].leftvol = pan * 4681;
2132 + drumPan[drumNum].rightvol = 32767;
2133 + } else {
2134 + drumPan[drumNum].rightvol = (14 - pan) * 4681;
2135 + drumPan[drumNum].leftvol = 32767;
2137 + PatchCache *cache = drumCache[drumNum];
2138 + backupCacheToPartials(cache);
2139 + for (int t = 0; t < 4; t++) {
2140 + // Common parameters, stored redundantly
2141 + cache[t].dirty = true;
2142 + cache[t].pitchShift = 0.0f;
2143 + cache[t].benderRange = 0.0f;
2144 + cache[t].pansetptr = &drumPan[drumNum];
2145 + cache[t].reverb = rhythmTemp[drumNum].reverbSwitch > 0;
2150 +void Part::refresh() {
2151 + updateVolume();
2152 + backupCacheToPartials(patchCache);
2153 + for (int t = 0; t < 4; t++) {
2154 + // Common parameters, stored redundantly
2155 + patchCache[t].dirty = true;
2156 + patchCache[t].pitchShift = (patchTemp->patch.keyShift - 24) + (patchTemp->patch.fineTune - 50) / 100.0f;
2157 + patchCache[t].benderRange = patchTemp->patch.benderRange;
2158 + patchCache[t].pansetptr = &volumesetting;
2159 + patchCache[t].reverb = patchTemp->patch.reverbSwitch > 0;
2161 + memcpy(currentInstr, timbreTemp->common.name, 10);
2164 +const char *Part::getCurrentInstr() const {
2165 + return &currentInstr[0];
2168 +void RhythmPart::refreshTimbre(unsigned int absTimbreNum) {
2169 + for (int m = 0; m < 85; m++) {
2170 + if (rhythmTemp[m].timbre == absTimbreNum - 128)
2171 + drumCache[m][0].dirty = true;
2175 +void Part::refreshTimbre(unsigned int absTimbreNum) {
2176 + if (getAbsTimbreNum() == absTimbreNum) {
2177 + memcpy(currentInstr, timbreTemp->common.name, 10);
2178 + patchCache[0].dirty = true;
2182 +int Part::fixBiaslevel(int srcpnt, int *dir) {
2183 + int noteat = srcpnt & 0x3F;
2184 + int outnote;
2185 + if (srcpnt < 64)
2186 + *dir = 0;
2187 + else
2188 + *dir = 1;
2189 + outnote = 33 + noteat;
2190 + //synth->printDebug("Bias note %d, dir %d", outnote, *dir);
2192 + return outnote;
2195 +int Part::fixKeyfollow(int srckey) {
2196 + if (srckey>=0 && srckey<=16) {
2197 + int keyfix[17] = { -256*16, -128*16, -64*16, 0, 32*16, 64*16, 96*16, 128*16, (128+32)*16, 192*16, (192+32)*16, 256*16, (256+64)*16, (256+128)*16, (512)*16, 4100, 4116};
2198 + return keyfix[srckey];
2199 + } else {
2200 + //LOG(LOG_ERROR|LOG_MISC,"Missed key: %d", srckey);
2201 + return 256;
2205 +void Part::abortPoly(dpoly *poly) {
2206 + if (!poly->isPlaying) {
2207 + return;
2209 + for (int i = 0; i < 4; i++) {
2210 + Partial *partial = poly->partials[i];
2211 + if (partial != NULL) {
2212 + partial->deactivate();
2215 + poly->isPlaying = false;
2218 +void Part::setPatch(const PatchParam *patch) {
2219 + patchTemp->patch = *patch;
2222 +void RhythmPart::setTimbre(TimbreParam * /*timbre*/) {
2223 + synth->printDebug("%s: Attempted to call setTimbre() - doesn't make sense for rhythm", name);
2226 +void Part::setTimbre(TimbreParam *timbre) {
2227 + *timbreTemp = *timbre;
2230 +unsigned int RhythmPart::getAbsTimbreNum() const {
2231 + synth->printDebug("%s: Attempted to call getAbsTimbreNum() - doesn't make sense for rhythm", name);
2232 + return 0;
2235 +unsigned int Part::getAbsTimbreNum() const {
2236 + return (patchTemp->patch.timbreGroup * 64) + patchTemp->patch.timbreNum;
2239 +void RhythmPart::setProgram(unsigned int patchNum) {
2240 + synth->printDebug("%s: Attempt to set program (%d) on rhythm is invalid", name, patchNum);
2243 +void Part::setProgram(unsigned int patchNum) {
2244 + setPatch(&synth->mt32ram.patches[patchNum]);
2245 + setTimbre(&synth->mt32ram.timbres[getAbsTimbreNum()].timbre);
2247 + refresh();
2249 + allSoundOff(); //FIXME:KG: Is this correct?
2252 +void Part::backupCacheToPartials(PatchCache cache[4]) {
2253 + // check if any partials are still playing with the old patch cache
2254 + // if so then duplicate the cached data from the part to the partial so that
2255 + // we can change the part's cache without affecting the partial.
2256 + // We delay this until now to avoid a copy operation with every note played
2257 + for (int m = 0; m < MT32EMU_MAX_POLY; m++) {
2258 + for (int i = 0; i < 4; i++) {
2259 + Partial *partial = polyTable[m].partials[i];
2260 + if (partial != NULL && partial->patchCache == &cache[i]) {
2261 + partial->cachebackup = cache[i];
2262 + partial->patchCache = &partial->cachebackup;
2268 +void Part::cacheTimbre(PatchCache cache[4], const TimbreParam *timbre) {
2269 + backupCacheToPartials(cache);
2270 + int partialCount = 0;
2271 + for (int t = 0; t < 4; t++) {
2272 + cache[t].PCMPartial = false;
2273 + if (((timbre->common.pmute >> t) & 0x1) == 1) {
2274 + cache[t].playPartial = true;
2275 + partialCount++;
2276 + } else {
2277 + cache[t].playPartial = false;
2278 + continue;
2281 + // Calculate and cache common parameters
2283 + cache[t].pcm = timbre->partial[t].wg.pcmwave;
2284 + cache[t].useBender = (timbre->partial[t].wg.bender == 1);
2286 + switch (t) {
2287 + case 0:
2288 + cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x2) ? true : false;
2289 + cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12];
2290 + cache[t].structurePosition = 0;
2291 + cache[t].structurePair = 1;
2292 + break;
2293 + case 1:
2294 + cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x1) ? true : false;
2295 + cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12];
2296 + cache[t].structurePosition = 1;
2297 + cache[t].structurePair = 0;
2298 + break;
2299 + case 2:
2300 + cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x2) ? true : false;
2301 + cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34];
2302 + cache[t].structurePosition = 0;
2303 + cache[t].structurePair = 3;
2304 + break;
2305 + case 3:
2306 + cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x1) ? true : false;
2307 + cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34];
2308 + cache[t].structurePosition = 1;
2309 + cache[t].structurePair = 2;
2310 + break;
2311 + default:
2312 + break;
2315 + cache[t].waveform = timbre->partial[t].wg.waveform;
2316 + cache[t].pulsewidth = timbre->partial[t].wg.pulsewid;
2317 + cache[t].pwsens = timbre->partial[t].wg.pwvelo;
2318 + if (timbre->partial[t].wg.keyfollow > 16) {
2319 + synth->printDebug("Bad keyfollow value in timbre!");
2320 + cache[t].pitchKeyfollow = 1.0f;
2321 + } else {
2322 + cache[t].pitchKeyfollow = floatKeyfollow[timbre->partial[t].wg.keyfollow];
2325 + cache[t].pitch = timbre->partial[t].wg.coarse + (timbre->partial[t].wg.fine - 50) / 100.0f + 24.0f;
2326 + cache[t].pitchEnv = timbre->partial[t].env;
2327 + cache[t].pitchEnv.sensitivity = (char)((float)cache[t].pitchEnv.sensitivity * 1.27f);
2328 + cache[t].pitchsustain = cache[t].pitchEnv.level[3];
2330 + // Calculate and cache TVA envelope stuff
2331 + cache[t].ampEnv = timbre->partial[t].tva;
2332 + cache[t].ampEnv.level = (char)((float)cache[t].ampEnv.level * 1.27f);
2334 + cache[t].ampbias[0] = fixBiaslevel(cache[t].ampEnv.biaspoint1, &cache[t].ampdir[0]);
2335 + cache[t].ampblevel[0] = 12 - cache[t].ampEnv.biaslevel1;
2336 + cache[t].ampbias[1] = fixBiaslevel(cache[t].ampEnv.biaspoint2, &cache[t].ampdir[1]);
2337 + cache[t].ampblevel[1] = 12 - cache[t].ampEnv.biaslevel2;
2338 + cache[t].ampdepth = cache[t].ampEnv.envvkf * cache[t].ampEnv.envvkf;
2340 + // Calculate and cache filter stuff
2341 + cache[t].filtEnv = timbre->partial[t].tvf;
2342 + cache[t].filtkeyfollow = fixKeyfollow(cache[t].filtEnv.keyfollow);
2343 + cache[t].filtEnv.envdepth = (char)((float)cache[t].filtEnv.envdepth * 1.27);
2344 + cache[t].tvfbias = fixBiaslevel(cache[t].filtEnv.biaspoint, &cache[t].tvfdir);
2345 + cache[t].tvfblevel = cache[t].filtEnv.biaslevel;
2346 + cache[t].filtsustain = cache[t].filtEnv.envlevel[3];
2348 + // Calculate and cache LFO stuff
2349 + cache[t].lfodepth = timbre->partial[t].lfo.depth;
2350 + cache[t].lfoperiod = synth->tables.lfoPeriod[(int)timbre->partial[t].lfo.rate];
2351 + cache[t].lforate = timbre->partial[t].lfo.rate;
2352 + cache[t].modsense = timbre->partial[t].lfo.modsense;
2354 + for (int t = 0; t < 4; t++) {
2355 + // Common parameters, stored redundantly
2356 + cache[t].dirty = false;
2357 + cache[t].partialCount = partialCount;
2358 + cache[t].sustain = (timbre->common.nosustain == 0);
2360 + //synth->printDebug("Res 1: %d 2: %d 3: %d 4: %d", cache[0].waveform, cache[1].waveform, cache[2].waveform, cache[3].waveform);
2362 +#if MT32EMU_MONITOR_INSTRUMENTS == 1
2363 + synth->printDebug("%s (%s): Recached timbre", name, currentInstr);
2364 + for (int i = 0; i < 4; i++) {
2365 + synth->printDebug(" %d: play=%s, pcm=%s (%d), wave=%d", i, cache[i].playPartial ? "YES" : "NO", cache[i].PCMPartial ? "YES" : "NO", timbre->partial[i].wg.pcmwave, timbre->partial[i].wg.waveform);
2367 +#endif
2370 +const char *Part::getName() const {
2371 + return name;
2374 +void Part::updateVolume() {
2375 + volumeMult = synth->tables.volumeMult[patchTemp->outlevel * expression / 127];
2378 +int Part::getVolume() const {
2379 + // FIXME: Use the mappings for this in the control ROM
2380 + return patchTemp->outlevel * 127 / 100;
2383 +void Part::setVolume(int midiVolume) {
2384 + // FIXME: Use the mappings for this in the control ROM
2385 + patchTemp->outlevel = (Bit8u)(midiVolume * 100 / 127);
2386 + updateVolume();
2387 + synth->printDebug("%s (%s): Set volume to %d", name, currentInstr, midiVolume);
2390 +void Part::setExpression(int midiExpression) {
2391 + expression = midiExpression;
2392 + updateVolume();
2395 +void RhythmPart::setPan(unsigned int midiPan)
2397 + // FIXME:KG: This is unchangeable for drums (they always use drumPan), is that correct?
2398 + synth->printDebug("%s: Setting pan (%d) not supported on rhythm", name, midiPan);
2401 +void Part::setPan(unsigned int midiPan) {
2402 + // FIXME:KG: Tweaked this a bit so that we have a left 100%, center and right 100%
2403 + // (But this makes the range somewhat skewed)
2404 + // Check against the real thing
2405 + // NOTE: Panning is inverted compared to GM.
2406 + if (midiPan < 64) {
2407 + volumesetting.leftvol = (Bit16s)(midiPan * 512);
2408 + volumesetting.rightvol = 32767;
2409 + } else if (midiPan == 64) {
2410 + volumesetting.leftvol = 32767;
2411 + volumesetting.rightvol = 32767;
2412 + } else {
2413 + volumesetting.rightvol = (Bit16s)((127 - midiPan) * 520);
2414 + volumesetting.leftvol = 32767;
2416 + patchTemp->panpot = (Bit8u)(midiPan * 14 / 127);
2417 + //synth->printDebug("%s (%s): Set pan to %d", name, currentInstr, panpot);
2420 +void RhythmPart::playNote(unsigned int key, int vel) {
2421 + if ((key < 24) || key > 108) { // > 87 on MT-32
2422 + synth->printDebug("%s: Attempted to play invalid key %d", name, key);
2423 + return;
2425 + int drumNum = key - 24;
2426 + int drumTimbreNum = rhythmTemp[drumNum].timbre;
2427 + if (drumTimbreNum >= 127) { // 94 on MT-32
2428 + synth->printDebug("%s: Attempted to play unmapped key %d", name, key);
2429 + return;
2431 + int absTimbreNum = drumTimbreNum + 128;
2432 + TimbreParam *timbre = &synth->mt32ram.timbres[absTimbreNum].timbre;
2433 + memcpy(currentInstr, timbre->common.name, 10);
2434 +#if MT32EMU_MONITOR_INSTRUMENTS == 1
2435 + synth->printDebug("%s (%s): starting poly (drum %d, timbre %d) - Vel %d Key %d", name, currentInstr, drumNum, absTimbreNum, vel, key);
2436 +#endif
2437 + if (drumCache[drumNum][0].dirty) {
2438 + cacheTimbre(drumCache[drumNum], timbre);
2440 + playPoly(drumCache[drumNum], key, MIDDLEC, vel);
2443 +void Part::playNote(unsigned int key, int vel) {
2444 + int freqNum = key;
2445 + if (freqNum < 12) {
2446 + synth->printDebug("%s (%s): Attempted to play invalid key %d < 12; moving up by octave", name, currentInstr, key);
2447 + freqNum += 12;
2448 + } else if (freqNum > 108) {
2449 + synth->printDebug("%s (%s): Attempted to play invalid key %d > 108; moving down by octave", name, currentInstr, key);
2450 + while (freqNum > 108) {
2451 + freqNum -= 12;
2454 + // POLY1 mode, Single Assign
2455 + // Haven't found any software that uses any of the other poly modes
2456 + // FIXME:KG: Should this also apply to rhythm?
2457 + for (unsigned int i = 0; i < MT32EMU_MAX_POLY; i++) {
2458 + if (polyTable[i].isActive() && (polyTable[i].key == key)) {
2459 + //AbortPoly(&polyTable[i]);
2460 + stopNote(key);
2461 + break;
2464 +#if MT32EMU_MONITOR_INSTRUMENTS == 1
2465 + synth->printDebug("%s (%s): starting poly - Vel %d Key %d", name, currentInstr, vel, key);
2466 +#endif
2467 + if (patchCache[0].dirty) {
2468 + cacheTimbre(patchCache, timbreTemp);
2470 + playPoly(patchCache, key, freqNum, vel);
2473 +void Part::playPoly(const PatchCache cache[4], unsigned int key, int freqNum, int vel) {
2474 + unsigned int needPartials = cache[0].partialCount;
2475 + unsigned int freePartials = synth->partialManager->getFreePartialCount();
2477 + if (freePartials < needPartials) {
2478 + if (!synth->partialManager->freePartials(needPartials - freePartials, partNum)) {
2479 + synth->printDebug("%s (%s): Insufficient free partials to play key %d (vel=%d); needed=%d, free=%d", name, currentInstr, key, vel, needPartials, synth->partialManager->getFreePartialCount());
2480 + return;
2483 + // Find free poly
2484 + int m;
2485 + for (m = 0; m < MT32EMU_MAX_POLY; m++) {
2486 + if (!polyTable[m].isActive()) {
2487 + break;
2490 + if (m == MT32EMU_MAX_POLY) {
2491 + synth->printDebug("%s (%s): No free poly to play key %d (vel %d)", name, currentInstr, key, vel);
2492 + return;
2495 + dpoly *tpoly = &polyTable[m];
2497 + tpoly->isPlaying = true;
2498 + tpoly->key = key;
2499 + tpoly->isDecay = false;
2500 + tpoly->freqnum = freqNum;
2501 + tpoly->vel = vel;
2502 + tpoly->pedalhold = false;
2504 + bool allnull = true;
2505 + for (int x = 0; x < 4; x++) {
2506 + if (cache[x].playPartial) {
2507 + tpoly->partials[x] = synth->partialManager->allocPartial(partNum);
2508 + allnull = false;
2509 + } else {
2510 + tpoly->partials[x] = NULL;
2514 + if (allnull)
2515 + synth->printDebug("%s (%s): No partials to play for this instrument", name, this->currentInstr);
2517 + tpoly->sustain = cache[0].sustain;
2518 + tpoly->volumeptr = &volumeMult;
2520 + for (int x = 0; x < 4; x++) {
2521 + if (tpoly->partials[x] != NULL) {
2522 + tpoly->partials[x]->startPartial(tpoly, &cache[x], tpoly->partials[cache[x].structurePair]);
2523 + tpoly->partials[x]->setBend(bend);
2528 +static void startDecayPoly(dpoly *tpoly) {
2529 + if (tpoly->isDecay) {
2530 + return;
2532 + tpoly->isDecay = true;
2534 + for (int t = 0; t < 4; t++) {
2535 + Partial *partial = tpoly->partials[t];
2536 + if (partial == NULL)
2537 + continue;
2538 + partial->startDecayAll();
2540 + tpoly->isPlaying = false;
2543 +void Part::allNotesOff() {
2544 + // Note: Unchecked on real MT-32, but the MIDI specification states that all notes off (0x7B)
2545 + // should treat the hold pedal as usual.
2546 + // All *sound* off (0x78) should stop notes immediately regardless of the hold pedal.
2547 + // The latter controller is not implemented on the MT-32 (according to the docs).
2548 + for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
2549 + dpoly *tpoly = &polyTable[q];
2550 + if (tpoly->isPlaying) {
2551 + if (holdpedal)
2552 + tpoly->pedalhold = true;
2553 + else if (tpoly->sustain)
2554 + startDecayPoly(tpoly);
2559 +void Part::allSoundOff() {
2560 + for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
2561 + dpoly *tpoly = &polyTable[q];
2562 + if (tpoly->isPlaying) {
2563 + startDecayPoly(tpoly);
2568 +void Part::stopPedalHold() {
2569 + for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
2570 + dpoly *tpoly;
2571 + tpoly = &polyTable[q];
2572 + if (tpoly->isActive() && tpoly->pedalhold)
2573 + stopNote(tpoly->key);
2577 +void Part::stopNote(unsigned int key) {
2578 + // Non-sustaining instruments ignore stop commands.
2579 + // They die away eventually anyway
2581 +#if MT32EMU_MONITOR_INSTRUMENTS == 1
2582 + synth->printDebug("%s (%s): stopping key %d", name, currentInstr, key);
2583 +#endif
2585 + if (key != 255) {
2586 + for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
2587 + dpoly *tpoly = &polyTable[q];
2588 + if (tpoly->isPlaying && tpoly->key == key) {
2589 + if (holdpedal)
2590 + tpoly->pedalhold = true;
2591 + else if (tpoly->sustain)
2592 + startDecayPoly(tpoly);
2595 + return;
2598 + // Find oldest poly... yes, the MT-32 can be reconfigured to kill different poly first
2599 + // This is simplest
2600 + int oldest = -1;
2601 + Bit32u oldage = 0;
2603 + for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
2604 + dpoly *tpoly = &polyTable[q];
2606 + if (tpoly->isPlaying && !tpoly->isDecay) {
2607 + if (tpoly->getAge() >= oldage) {
2608 + oldage = tpoly->getAge();
2609 + oldest = q;
2614 + if (oldest != -1) {
2615 + startDecayPoly(&polyTable[oldest]);
2620 diff -urN dosbox.orig/src/gui/part.h dosbox/src/gui/part.h
2621 --- dosbox.orig/src/gui/part.h 1969-12-31 21:00:00.000000000 -0300
2622 +++ dosbox/src/gui/part.h 2011-01-11 20:15:44.311498770 -0300
2623 @@ -0,0 +1,112 @@
2624 +/* Copyright (c) 2003-2005 Various contributors
2626 + * Permission is hereby granted, free of charge, to any person obtaining a copy
2627 + * of this software and associated documentation files (the "Software"), to
2628 + * deal in the Software without restriction, including without limitation the
2629 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
2630 + * sell copies of the Software, and to permit persons to whom the Software is
2631 + * furnished to do so, subject to the following conditions:
2633 + * The above copyright notice and this permission notice shall be included in
2634 + * all copies or substantial portions of the Software.
2636 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2637 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2638 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2639 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2640 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2641 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2642 + * IN THE SOFTWARE.
2643 + */
2645 +#ifndef MT32EMU_PART_H
2646 +#define MT32EMU_PART_H
2648 +namespace MT32Emu {
2650 +class Synth;
2652 +class Part {
2653 +private:
2654 + // Pointers to the areas of the MT-32's memory dedicated to this part (for parts 1-8)
2655 + MemParams::PatchTemp *patchTemp;
2656 + TimbreParam *timbreTemp;
2658 + // 0=Part 1, .. 7=Part 8, 8=Rhythm
2659 + unsigned int partNum;
2661 + bool holdpedal;
2663 + StereoVolume volumesetting;
2665 + PatchCache patchCache[4];
2667 + float bend; // -1.0 .. +1.0
2669 + dpoly polyTable[MT32EMU_MAX_POLY];
2671 + void abortPoly(dpoly *poly);
2673 + static int fixKeyfollow(int srckey);
2674 + static int fixBiaslevel(int srcpnt, int *dir);
2676 + void setPatch(const PatchParam *patch);
2678 +protected:
2679 + Synth *synth;
2680 + char name[8]; // "Part 1".."Part 8", "Rhythm"
2681 + char currentInstr[11];
2682 + int expression;
2683 + Bit32u volumeMult;
2685 + void updateVolume();
2686 + void backupCacheToPartials(PatchCache cache[4]);
2687 + void cacheTimbre(PatchCache cache[4], const TimbreParam *timbre);
2688 + void playPoly(const PatchCache cache[4], unsigned int key, int freqNum, int vel);
2689 + const char *getName() const;
2691 +public:
2692 + Part(Synth *synth, unsigned int usePartNum);
2693 + virtual ~Part() {}
2694 + virtual void playNote(unsigned int key, int vel);
2695 + void stopNote(unsigned int key);
2696 + void allNotesOff();
2697 + void allSoundOff();
2698 + int getVolume() const;
2699 + void setVolume(int midiVolume);
2700 + void setExpression(int midiExpression);
2701 + virtual void setPan(unsigned int midiPan);
2702 + virtual void setBend(unsigned int midiBend);
2703 + virtual void setModulation(unsigned int midiModulation);
2704 + virtual void setProgram(unsigned int midiProgram);
2705 + void setHoldPedal(bool pedalval);
2706 + void stopPedalHold();
2707 + virtual void refresh();
2708 + virtual void refreshTimbre(unsigned int absTimbreNum);
2709 + virtual void setTimbre(TimbreParam *timbre);
2710 + virtual unsigned int getAbsTimbreNum() const;
2711 + const char *getCurrentInstr() const;
2714 +class RhythmPart: public Part {
2715 + // Pointer to the area of the MT-32's memory dedicated to rhythm
2716 + const MemParams::RhythmTemp *rhythmTemp;
2718 + // This caches the timbres/settings in use by the rhythm part
2719 + PatchCache drumCache[85][4];
2720 + StereoVolume drumPan[85];
2721 +public:
2722 + RhythmPart(Synth *synth, unsigned int usePartNum);
2723 + void refresh();
2724 + void refreshTimbre(unsigned int timbreNum);
2725 + void setTimbre(TimbreParam *timbre);
2726 + void playNote(unsigned int key, int vel);
2727 + unsigned int getAbsTimbreNum() const;
2728 + void setPan(unsigned int midiPan);
2729 + void setBend(unsigned int midiBend);
2730 + void setModulation(unsigned int midiModulation);
2731 + void setProgram(unsigned int patchNum);
2735 +#endif
2736 diff -urN dosbox.orig/src/gui/partial.cpp dosbox/src/gui/partial.cpp
2737 --- dosbox.orig/src/gui/partial.cpp 1969-12-31 21:00:00.000000000 -0300
2738 +++ dosbox/src/gui/partial.cpp 2011-01-11 20:15:44.311498770 -0300
2739 @@ -0,0 +1,968 @@
2740 +/* Copyright (c) 2003-2005 Various contributors
2742 + * Permission is hereby granted, free of charge, to any person obtaining a copy
2743 + * of this software and associated documentation files (the "Software"), to
2744 + * deal in the Software without restriction, including without limitation the
2745 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
2746 + * sell copies of the Software, and to permit persons to whom the Software is
2747 + * furnished to do so, subject to the following conditions:
2749 + * The above copyright notice and this permission notice shall be included in
2750 + * all copies or substantial portions of the Software.
2752 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2753 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2754 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2755 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2756 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2757 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2758 + * IN THE SOFTWARE.
2759 + */
2761 +#include <stdlib.h>
2762 +#include <math.h>
2763 +#include <string.h>
2765 +#include "mt32emu.h"
2767 +#if defined(MACOSX) || defined(SOLARIS) || defined(__MINGW32__)
2768 +// Older versions of Mac OS X didn't supply a powf function, so using it
2769 +// will cause a binary incompatibility when trying to run a binary built
2770 +// on a newer OS X release on an older one. And Solaris 8 doesn't provide
2771 +// powf, floorf, fabsf etc. at all.
2772 +// Cross-compiled MinGW32 toolchains suffer from a cross-compile bug in
2773 +// libstdc++. math/stubs.o should be empty, but it comes with a symbol for
2774 +// powf, resulting in a linker error because of multiple definitions.
2775 +// Hence we re-define them here. The only potential drawback is that it
2776 +// might be a little bit slower this way.
2777 +#define powf(x,y) ((float)pow(x,y))
2778 +#define floorf(x) ((float)floor(x))
2779 +#define fabsf(x) ((float)fabs(x))
2780 +#endif
2782 +#define FIXEDPOINT_UDIV(x, y, point) (((x) << (point)) / ((y)))
2783 +#define FIXEDPOINT_SDIV(x, y, point) (((x) * (1 << point)) / ((y)))
2784 +#define FIXEDPOINT_UMULT(x, y, point) (((x) * (y)) >> point)
2785 +#define FIXEDPOINT_SMULT(x, y, point) (((x) * (y)) / (1 << point))
2787 +using namespace MT32Emu;
2789 +Partial::Partial(Synth *useSynth) {
2790 + this->synth = useSynth;
2791 + ownerPart = -1;
2792 + poly = NULL;
2793 + pair = NULL;
2794 +#if MT32EMU_ACCURATENOTES == 1
2795 + for (int i = 0; i < 3; i++) {
2796 + noteLookupStorage.waveforms[i] = new Bit16s[65536];
2798 + noteLookup = &noteLookupStorage;
2799 +#endif
2802 +Partial::~Partial() {
2803 +#if MT32EMU_ACCURATENOTES == 1
2804 + for (int i = 0; i < 3; i++) {
2805 + delete[] noteLookupStorage.waveforms[i];
2807 + delete[] noteLookupStorage.wavTable;
2808 +#endif
2811 +int Partial::getOwnerPart() const {
2812 + return ownerPart;
2815 +bool Partial::isActive() {
2816 + return ownerPart > -1;
2819 +const dpoly *Partial::getDpoly() const {
2820 + return this->poly;
2823 +void Partial::activate(int part) {
2824 + // This just marks the partial as being assigned to a part
2825 + ownerPart = part;
2828 +void Partial::deactivate() {
2829 + ownerPart = -1;
2830 + if (poly != NULL) {
2831 + for (int i = 0; i < 4; i++) {
2832 + if (poly->partials[i] == this) {
2833 + poly->partials[i] = NULL;
2834 + break;
2837 + if (pair != NULL) {
2838 + pair->pair = NULL;
2843 +void Partial::initKeyFollow(int key) {
2844 + // Setup partial keyfollow
2845 + // Note follow relative to middle C
2847 + // Calculate keyfollow for pitch
2848 +#if 1
2849 + float rel = key == -1 ? 0.0f : (key - MIDDLEC);
2850 + float newPitch = rel * patchCache->pitchKeyfollow + patchCache->pitch + patchCache->pitchShift;
2851 + //FIXME:KG: Does it truncate the keyfollowed pitch to a semitone (towards MIDDLEC)?
2852 + //int newKey = (int)(rel * patchCache->pitchKeyfollow);
2853 + //float newPitch = newKey + patchCache->pitch + patchCache->pitchShift;
2854 +#else
2855 + float rel = key == -1 ? 0.0f : (key + patchCache->pitchShift - MIDDLEC);
2856 + float newPitch = rel * patchCache->pitchKeyfollow + patchCache->pitch;
2857 +#endif
2858 +#if MT32EMU_ACCURATENOTES == 1
2859 + noteVal = newPitch;
2860 + synth->printDebug("key=%d, pitch=%f, pitchKeyfollow=%f, pitchShift=%f, newPitch=%f", key, (double)patchCache->pitch, (double)patchCache->pitchKeyfollow, (double)patchCache->pitchShift, (double)newPitch);
2861 +#else
2862 + float newPitchInt;
2863 + float newPitchFract = modff(newPitch, &newPitchInt);
2864 + if (newPitchFract > 0.5f) {
2865 + newPitchInt += 1.0f;
2866 + newPitchFract -= 1.0f;
2868 + noteVal = (int)newPitchInt;
2869 + fineShift = (int)(powf(2.0f, newPitchFract / 12.0f) * 4096.0f);
2870 + synth->printDebug("key=%d, pitch=%f, pitchKeyfollow=%f, pitchShift=%f, newPitch=%f, noteVal=%d, fineShift=%d", key, (double)patchCache->pitch, (double)patchCache->pitchKeyfollow, (double)patchCache->pitchShift, (double)newPitch, noteVal, fineShift);
2871 +#endif
2872 + // FIXME:KG: Raise/lower by octaves until in the supported range.
2873 + while (noteVal > HIGHEST_NOTE) // FIXME:KG: see tables.cpp: >108?
2874 + noteVal -= 12;
2875 + while (noteVal < LOWEST_NOTE) // FIXME:KG: see tables.cpp: <12?
2876 + noteVal += 12;
2877 + // Calculate keyfollow for filter
2878 + int keyfollow = ((key - MIDDLEC) * patchCache->filtkeyfollow) / 4096;
2879 + if (keyfollow > 108)
2880 + keyfollow = 108;
2881 + else if (keyfollow < -108)
2882 + keyfollow = -108;
2883 + filtVal = synth->tables.tvfKeyfollowMult[keyfollow + 108];
2884 + realVal = synth->tables.tvfKeyfollowMult[(noteVal - MIDDLEC) + 108];
2887 +int Partial::getKey() const {
2888 + if (poly == NULL) {
2889 + return -1;
2890 + } else {
2891 + return poly->key;
2895 +void Partial::startPartial(dpoly *usePoly, const PatchCache *useCache, Partial *pairPartial) {
2896 + if (usePoly == NULL || useCache == NULL) {
2897 + synth->printDebug("*** Error: Starting partial for owner %d, usePoly=%s, useCache=%s", ownerPart, usePoly == NULL ? "*** NULL ***" : "OK", useCache == NULL ? "*** NULL ***" : "OK");
2898 + return;
2900 + patchCache = useCache;
2901 + poly = usePoly;
2902 + mixType = patchCache->structureMix;
2903 + structurePosition = patchCache->structurePosition;
2905 + play = true;
2906 + initKeyFollow(poly->freqnum); // Initializes noteVal, filtVal and realVal
2907 +#if MT32EMU_ACCURATENOTES == 0
2908 + noteLookup = &synth->tables.noteLookups[noteVal - LOWEST_NOTE];
2909 +#else
2910 + Tables::initNote(synth, &noteLookupStorage, noteVal, (float)synth->myProp.sampleRate, synth->masterTune, synth->pcmWaves, NULL);
2911 +#endif
2912 + keyLookup = &synth->tables.keyLookups[poly->freqnum - 12];
2914 + if (patchCache->PCMPartial) {
2915 + pcmNum = patchCache->pcm;
2916 + if (synth->controlROMMap->pcmCount > 128) {
2917 + // CM-32L, etc. support two "banks" of PCMs, selectable by waveform type parameter.
2918 + if (patchCache->waveform > 1) {
2919 + pcmNum += 128;
2922 + pcmWave = &synth->pcmWaves[pcmNum];
2923 + } else {
2924 + pcmWave = NULL;
2927 + lfoPos = 0;
2928 + pulsewidth = patchCache->pulsewidth + synth->tables.pwVelfollowAdd[patchCache->pwsens][poly->vel];
2929 + if (pulsewidth > 100) {
2930 + pulsewidth = 100;
2931 + } else if (pulsewidth < 0) {
2932 + pulsewidth = 0;
2935 + for (int e = 0; e < 3; e++) {
2936 + envs[e].envpos = 0;
2937 + envs[e].envstat = -1;
2938 + envs[e].envbase = 0;
2939 + envs[e].envdist = 0;
2940 + envs[e].envsize = 0;
2941 + envs[e].sustaining = false;
2942 + envs[e].decaying = false;
2943 + envs[e].prevlevel = 0;
2944 + envs[e].counter = 0;
2945 + envs[e].count = 0;
2947 + ampEnvVal = 0;
2948 + pitchEnvVal = 0;
2949 + pitchSustain = false;
2950 + loopPos = 0;
2951 + partialOff.pcmoffset = partialOff.pcmplace = 0;
2952 + pair = pairPartial;
2953 + useNoisePair = pairPartial == NULL && (mixType == 1 || mixType == 2);
2954 + age = 0;
2955 + alreadyOutputed = false;
2956 + memset(history,0,sizeof(history));
2959 +Bit16s *Partial::generateSamples(long length) {
2960 + if (!isActive() || alreadyOutputed) {
2961 + return NULL;
2963 + if (poly == NULL) {
2964 + synth->printDebug("*** ERROR: poly is NULL at Partial::generateSamples()!");
2965 + return NULL;
2968 + alreadyOutputed = true;
2970 + // Generate samples
2972 + Bit16s *partialBuf = &myBuffer[0];
2973 + Bit32u volume = *poly->volumeptr;
2974 + while (length--) {
2975 + Bit32s envval;
2976 + Bit32s sample = 0;
2977 + if (!envs[EnvelopeType_amp].sustaining) {
2978 + if (envs[EnvelopeType_amp].count <= 0) {
2979 + Bit32u ampval = getAmpEnvelope();
2980 + if (!play) {
2981 + deactivate();
2982 + break;
2984 + if (ampval > 100) {
2985 + ampval = 100;
2988 + ampval = synth->tables.volumeMult[ampval];
2989 + ampval = FIXEDPOINT_UMULT(ampval, synth->tables.tvaVelfollowMult[poly->vel][(int)patchCache->ampEnv.velosens], 8);
2990 + //if (envs[EnvelopeType_amp].sustaining)
2991 + ampEnvVal = ampval;
2993 + --envs[EnvelopeType_amp].count;
2996 + unsigned int lfoShift = 0x1000;
2997 + if (pitchSustain) {
2998 + // Calculate LFO position
2999 + // LFO does not kick in completely until pitch envelope sustains
3000 + if (patchCache->lfodepth > 0) {
3001 + lfoPos++;
3002 + if (lfoPos >= patchCache->lfoperiod)
3003 + lfoPos = 0;
3004 + int lfoatm = FIXEDPOINT_UDIV(lfoPos, patchCache->lfoperiod, 16);
3005 + int lfoatr = synth->tables.sintable[lfoatm];
3006 + lfoShift = synth->tables.lfoShift[patchCache->lfodepth][lfoatr];
3008 + } else {
3009 + // Calculate Pitch envelope
3010 + envval = getPitchEnvelope();
3011 + int pd = patchCache->pitchEnv.depth;
3012 + pitchEnvVal = synth->tables.pitchEnvVal[pd][envval];
3015 + int delta;
3017 + // Wrap positions or end if necessary
3018 + if (patchCache->PCMPartial) {
3019 + // PCM partial
3021 + delta = noteLookup->wavTable[pcmNum];
3022 + int len = pcmWave->len;
3023 + if (partialOff.pcmplace >= len) {
3024 + if (pcmWave->loop) {
3025 + //partialOff.pcmplace = partialOff.pcmoffset = 0;
3026 + partialOff.pcmplace %= len;
3027 + } else {
3028 + play = false;
3029 + deactivate();
3030 + break;
3033 + } else {
3034 + // Synthesis partial
3035 + delta = 0x10000;
3036 + partialOff.pcmplace %= (Bit16u)noteLookup->div2;
3039 + // Build delta for position of next sample
3040 + // Fix delta code
3041 + Bit32u tdelta = delta;
3042 +#if MT32EMU_ACCURATENOTES == 0
3043 + tdelta = FIXEDPOINT_UMULT(tdelta, fineShift, 12);
3044 +#endif
3045 + tdelta = FIXEDPOINT_UMULT(tdelta, pitchEnvVal, 12);
3046 + tdelta = FIXEDPOINT_UMULT(tdelta, lfoShift, 12);
3047 + tdelta = FIXEDPOINT_UMULT(tdelta, bendShift, 12);
3048 + delta = (int)tdelta;
3050 + // Get waveform - either PCM or synthesized sawtooth or square
3051 + if (ampEnvVal > 0) {
3052 + if (patchCache->PCMPartial) {
3053 + // Render PCM sample
3054 + int ra, rb, dist;
3055 + Bit32u taddr;
3056 + Bit32u pcmAddr = pcmWave->addr;
3057 + if (delta < 0x10000) {
3058 + // Linear sound interpolation
3059 + taddr = pcmAddr + partialOff.pcmplace;
3060 + ra = synth->pcmROMData[taddr];
3061 + taddr++;
3062 + if (taddr == pcmAddr + pcmWave->len) {
3063 + // Past end of PCM
3064 + if (pcmWave->loop) {
3065 + rb = synth->pcmROMData[pcmAddr];
3066 + } else {
3067 + rb = 0;
3069 + } else {
3070 + rb = synth->pcmROMData[taddr];
3072 + dist = rb - ra;
3073 + sample = (ra + ((dist * (Bit32s)(partialOff.pcmoffset >> 8)) >> 8));
3074 + } else {
3075 + // Sound decimation
3076 + // The right way to do it is to use a lowpass filter on the waveform before selecting
3077 + // a point. This is too slow. The following approximates this as fast as possible
3078 + int idelta = delta >> 16;
3079 + taddr = pcmAddr + partialOff.pcmplace;
3080 + ra = synth->pcmROMData[taddr++];
3081 + for (int ix = 0; ix < idelta - 1; ix++) {
3082 + if (taddr == pcmAddr + pcmWave->len) {
3083 + // Past end of PCM
3084 + if (pcmWave->loop) {
3085 + taddr = pcmAddr;
3086 + } else {
3087 + // Behave as if all subsequent samples were 0
3088 + break;
3091 + ra += synth->pcmROMData[taddr++];
3093 + sample = ra / idelta;
3095 + } else {
3096 + // Render synthesised sample
3097 + int toff = partialOff.pcmplace;
3098 + int minorplace = partialOff.pcmoffset >> 14;
3099 + Bit32s filterInput;
3100 + Bit32s filtval = getFiltEnvelope();
3102 + //synth->printDebug("Filtval: %d", filtval);
3104 + if ((patchCache->waveform & 1) == 0) {
3105 + // Square waveform. Made by combining two pregenerated bandlimited
3106 + // sawtooth waveforms
3107 + Bit32u ofsA = ((toff << 2) + minorplace) % noteLookup->waveformSize[0];
3108 + int width = FIXEDPOINT_UMULT(noteLookup->div2, synth->tables.pwFactor[pulsewidth], 7);
3109 + Bit32u ofsB = (ofsA + width) % noteLookup->waveformSize[0];
3110 + Bit16s pa = noteLookup->waveforms[0][ofsA];
3111 + Bit16s pb = noteLookup->waveforms[0][ofsB];
3112 + filterInput = pa - pb;
3113 + // Non-bandlimited squarewave
3114 + /*
3115 + ofs = FIXEDPOINT_UMULT(noteLookup->div2, synth->tables.pwFactor[patchCache->pulsewidth], 8);
3116 + if (toff < ofs)
3117 + sample = 1 * WGAMP;
3118 + else
3119 + sample = -1 * WGAMP;
3120 + */
3121 + } else {
3122 + // Sawtooth. Made by combining the full cosine and half cosine according
3123 + // to how it looks on the MT-32. What it really does it takes the
3124 + // square wave and multiplies it by a full cosine
3125 + int waveoff = (toff << 2) + minorplace;
3126 + if (toff < noteLookup->sawTable[pulsewidth])
3127 + filterInput = noteLookup->waveforms[1][waveoff % noteLookup->waveformSize[1]];
3128 + else
3129 + filterInput = noteLookup->waveforms[2][waveoff % noteLookup->waveformSize[2]];
3130 + // This is the correct way
3131 + // Seems slow to me (though bandlimited) -- doesn't seem to
3132 + // sound any better though
3133 + /*
3134 + //int pw = (patchCache->pulsewidth * pulsemod[filtval]) >> 8;
3136 + Bit32u ofs = toff % (noteLookup->div2 >> 1);
3138 + Bit32u ofs3 = toff + FIXEDPOINT_UMULT(noteLookup->div2, synth->tables.pwFactor[patchCache->pulsewidth], 9);
3139 + ofs3 = ofs3 % (noteLookup->div2 >> 1);
3141 + pa = noteLookup->waveforms[0][ofs];
3142 + pb = noteLookup->waveforms[0][ofs3];
3143 + sample = ((pa - pb) * noteLookup->waveforms[2][toff]) / 2;
3144 + */
3147 + //Very exact filter
3148 + if (filtval > ((FILTERGRAN * 15) / 16))
3149 + filtval = ((FILTERGRAN * 15) / 16);
3150 + sample = (Bit32s)(floorf((synth->iirFilter)((float)filterInput, &history[0], synth->tables.filtCoeff[filtval][(int)patchCache->filtEnv.resonance])) / synth->tables.resonanceFactor[patchCache->filtEnv.resonance]);
3151 + if (sample < -32768) {
3152 + synth->printDebug("Overdriven amplitude for %d: %d:=%d < -32768", patchCache->waveform, filterInput, sample);
3153 + sample = -32768;
3155 + else if (sample > 32767) {
3156 + synth->printDebug("Overdriven amplitude for %d: %d:=%d > 32767", patchCache->waveform, filterInput, sample);
3157 + sample = 32767;
3162 + // Add calculated delta to our waveform offset
3163 + Bit32u absOff = ((partialOff.pcmplace << 16) | partialOff.pcmoffset);
3164 + absOff += delta;
3165 + partialOff.pcmplace = (Bit16u)((absOff & 0xFFFF0000) >> 16);
3166 + partialOff.pcmoffset = (Bit16u)(absOff & 0xFFFF);
3168 + // Put volume envelope over generated sample
3169 + sample = FIXEDPOINT_SMULT(sample, ampEnvVal, 9);
3170 + sample = FIXEDPOINT_SMULT(sample, volume, 7);
3171 + envs[EnvelopeType_amp].envpos++;
3172 + envs[EnvelopeType_pitch].envpos++;
3173 + envs[EnvelopeType_filt].envpos++;
3175 + *partialBuf++ = (Bit16s)sample;
3177 + // We may have deactivated and broken out of the loop before the end of the buffer,
3178 + // if so then fill the remainder with 0s.
3179 + if (++length > 0)
3180 + memset(partialBuf, 0, length * 2);
3181 + return &myBuffer[0];
3184 +void Partial::setBend(float factor) {
3185 + if (!patchCache->useBender || factor == 0.0f) {
3186 + bendShift = 4096;
3187 + return;
3189 + // NOTE:KG: We can't do this smoothly with lookup tables, unless we use several MB.
3190 + // FIXME:KG: Bend should be influenced by pitch key-follow too, according to docs.
3191 + float bendSemitones = factor * patchCache->benderRange; // -24 .. 24
3192 + float mult = powf(2.0f, bendSemitones / 12.0f);
3193 + synth->printDebug("setBend(): factor=%f, benderRange=%f, semitones=%f, mult=%f\n", (double)factor, (double)patchCache->benderRange, (double)bendSemitones, (double)mult);
3194 + bendShift = (int)(mult * 4096.0f);
3197 +Bit16s *Partial::mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) {
3198 + if (buf1 == NULL)
3199 + return buf2;
3200 + if (buf2 == NULL)
3201 + return buf1;
3203 + Bit16s *outBuf = buf1;
3204 +#if MT32EMU_USE_MMX >= 1
3205 + // KG: This seems to be fine
3206 + int donelen = i386_mixBuffers(buf1, buf2, len);
3207 + len -= donelen;
3208 + buf1 += donelen;
3209 + buf2 += donelen;
3210 +#endif
3211 + while (len--) {
3212 + *buf1 = *buf1 + *buf2;
3213 + buf1++, buf2++;
3215 + return outBuf;
3218 +Bit16s *Partial::mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) {
3219 + if (buf1 == NULL)
3220 + return NULL;
3221 + if (buf2 == NULL) {
3222 + Bit16s *outBuf = buf1;
3223 + while (len--) {
3224 + if (*buf1 < -8192)
3225 + *buf1 = -8192;
3226 + else if (*buf1 > 8192)
3227 + *buf1 = 8192;
3228 + buf1++;
3230 + return outBuf;
3233 + Bit16s *outBuf = buf1;
3234 +#if MT32EMU_USE_MMX >= 1
3235 + // KG: This seems to be fine
3236 + int donelen = i386_mixBuffersRingMix(buf1, buf2, len);
3237 + len -= donelen;
3238 + buf1 += donelen;
3239 + buf2 += donelen;
3240 +#endif
3241 + while (len--) {
3242 + float a, b;
3243 + a = ((float)*buf1) / 8192.0f;
3244 + b = ((float)*buf2) / 8192.0f;
3245 + a = (a * b) + a;
3246 + if (a > 1.0f)
3247 + a = 1.0f;
3248 + if (a < -1.0f)
3249 + a = -1.0f;
3250 + *buf1 = (Bit16s)(a * 8192.0f);
3251 + buf1++;
3252 + buf2++;
3253 + //buf1[i] = (Bit16s)(((Bit32s)buf1[i] * (Bit32s)buf2[i]) >> 10) + buf1[i];
3255 + return outBuf;
3258 +Bit16s *Partial::mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) {
3259 + if (buf1 == NULL) {
3260 + return NULL;
3262 + if (buf2 == NULL) {
3263 + return NULL;
3266 + Bit16s *outBuf = buf1;
3267 +#if MT32EMU_USE_MMX >= 1
3268 + // FIXME:KG: Not really checked as working
3269 + int donelen = i386_mixBuffersRing(buf1, buf2, len);
3270 + len -= donelen;
3271 + buf1 += donelen;
3272 + buf2 += donelen;
3273 +#endif
3274 + while (len--) {
3275 + float a, b;
3276 + a = ((float)*buf1) / 8192.0f;
3277 + b = ((float)*buf2) / 8192.0f;
3278 + a *= b;
3279 + if (a > 1.0f)
3280 + a = 1.0f;
3281 + if (a < -1.0f)
3282 + a = -1.0f;
3283 + *buf1 = (Bit16s)(a * 8192.0f);
3284 + buf1++;
3285 + buf2++;
3287 + return outBuf;
3290 +void Partial::mixBuffersStereo(Bit16s *buf1, Bit16s *buf2, Bit16s *outBuf, int len) {
3291 + if (buf2 == NULL) {
3292 + while (len--) {
3293 + *outBuf++ = *buf1++;
3294 + *outBuf++ = 0;
3296 + } else if (buf1 == NULL) {
3297 + while (len--) {
3298 + *outBuf++ = 0;
3299 + *outBuf++ = *buf2++;
3301 + } else {
3302 + while (len--) {
3303 + *outBuf++ = *buf1++;
3304 + *outBuf++ = *buf2++;
3309 +bool Partial::produceOutput(Bit16s *partialBuf, long length) {
3310 + if (!isActive() || alreadyOutputed)
3311 + return false;
3312 + if (poly == NULL) {
3313 + synth->printDebug("*** ERROR: poly is NULL at Partial::produceOutput()!");
3314 + return false;
3317 + Bit16s *pairBuf = NULL;
3318 + // Check for dependant partial
3319 + if (pair != NULL) {
3320 + if (!pair->alreadyOutputed) {
3321 + // Note: pair may have become NULL after this
3322 + pairBuf = pair->generateSamples(length);
3324 + } else if (useNoisePair) {
3325 + // Generate noise for pairless ring mix
3326 + pairBuf = synth->tables.noiseBuf;
3329 + Bit16s *myBuf = generateSamples(length);
3331 + if (myBuf == NULL && pairBuf == NULL)
3332 + return false;
3334 + Bit16s *p1buf, *p2buf;
3336 + if (structurePosition == 0 || pairBuf == NULL) {
3337 + p1buf = myBuf;
3338 + p2buf = pairBuf;
3339 + } else {
3340 + p2buf = myBuf;
3341 + p1buf = pairBuf;
3344 + //synth->printDebug("mixType: %d", mixType);
3346 + Bit16s *mixedBuf;
3347 + switch (mixType) {
3348 + case 0:
3349 + // Standard sound mix
3350 + mixedBuf = mixBuffers(p1buf, p2buf, length);
3351 + break;
3353 + case 1:
3354 + // Ring modulation with sound mix
3355 + mixedBuf = mixBuffersRingMix(p1buf, p2buf, length);
3356 + break;
3358 + case 2:
3359 + // Ring modulation alone
3360 + mixedBuf = mixBuffersRing(p1buf, p2buf, length);
3361 + break;
3363 + case 3:
3364 + // Stereo mixing. One partial to one speaker channel, one to another.
3365 + // FIXME:KG: Surely we should be multiplying by the left/right volumes here?
3366 + mixBuffersStereo(p1buf, p2buf, partialBuf, length);
3367 + return true;
3369 + default:
3370 + mixedBuf = mixBuffers(p1buf, p2buf, length);
3371 + break;
3374 + if (mixedBuf == NULL)
3375 + return false;
3377 + Bit16s leftvol, rightvol;
3378 + leftvol = patchCache->pansetptr->leftvol;
3379 + rightvol = patchCache->pansetptr->rightvol;
3381 +#if MT32EMU_USE_MMX >= 2
3382 + // FIXME:KG: This appears to introduce crackle
3383 + int donelen = i386_partialProductOutput(length, leftvol, rightvol, partialBuf, mixedBuf);
3384 + length -= donelen;
3385 + mixedBuf += donelen;
3386 + partialBuf += donelen * 2;
3387 +#endif
3388 + while (length--) {
3389 + *partialBuf++ = (Bit16s)(((Bit32s)*mixedBuf * (Bit32s)leftvol) >> 15);
3390 + *partialBuf++ = (Bit16s)(((Bit32s)*mixedBuf * (Bit32s)rightvol) >> 15);
3391 + mixedBuf++;
3393 + return true;
3396 +Bit32s Partial::getFiltEnvelope() {
3397 + int reshigh;
3399 + int cutoff, depth;
3401 + EnvelopeStatus *tStat = &envs[EnvelopeType_filt];
3403 + if (tStat->decaying) {
3404 + reshigh = tStat->envbase;
3405 + reshigh = (reshigh + ((tStat->envdist * tStat->envpos) / tStat->envsize));
3406 + if (tStat->envpos >= tStat->envsize)
3407 + reshigh = 0;
3408 + } else {
3409 + if (tStat->envstat==4) {
3410 + reshigh = patchCache->filtsustain;
3411 + if (!poly->sustain) {
3412 + startDecay(EnvelopeType_filt, reshigh);
3414 + } else {
3415 + if ((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) {
3416 + if (tStat->envstat==-1)
3417 + tStat->envbase = 0;
3418 + else
3419 + tStat->envbase = patchCache->filtEnv.envlevel[tStat->envstat];
3420 + tStat->envstat++;
3421 + tStat->envpos = 0;
3422 + if (tStat->envstat == 3) {
3423 + tStat->envsize = synth->tables.envTime[(int)patchCache->filtEnv.envtime[tStat->envstat]];
3424 + } else {
3425 + Bit32u envTime = (int)patchCache->filtEnv.envtime[tStat->envstat];
3426 + if (tStat->envstat > 1) {
3427 + int envDiff = abs(patchCache->filtEnv.envlevel[tStat->envstat] - patchCache->filtEnv.envlevel[tStat->envstat - 1]);
3428 + if (envTime > synth->tables.envDeltaMaxTime[envDiff]) {
3429 + envTime = synth->tables.envDeltaMaxTime[envDiff];
3433 + tStat->envsize = (synth->tables.envTime[envTime] * keyLookup->envTimeMult[(int)patchCache->filtEnv.envtkf]) >> 8;
3436 + tStat->envsize++;
3437 + tStat->envdist = patchCache->filtEnv.envlevel[tStat->envstat] - tStat->envbase;
3440 + reshigh = tStat->envbase;
3441 + reshigh = (reshigh + ((tStat->envdist * tStat->envpos) / tStat->envsize));
3444 + tStat->prevlevel = reshigh;
3447 + cutoff = patchCache->filtEnv.cutoff;
3449 + //if (patchCache->waveform==1) reshigh = (reshigh * 3) >> 2;
3451 + depth = patchCache->filtEnv.envdepth;
3453 + //int sensedep = (depth * 127-patchCache->filtEnv.envsense) >> 7;
3454 + depth = FIXEDPOINT_UMULT(depth, synth->tables.tvfVelfollowMult[poly->vel][(int)patchCache->filtEnv.envsense], 8);
3456 + int bias = patchCache->tvfbias;
3457 + int dist;
3459 + if (bias != 0) {
3460 + //FIXME:KG: Is this really based on pitch (as now), or key pressed?
3461 + //synth->printDebug("Cutoff before %d", cutoff);
3462 + if (patchCache->tvfdir == 0) {
3463 + if (noteVal < bias) {
3464 + dist = bias - noteVal;
3465 + cutoff = FIXEDPOINT_UMULT(cutoff, synth->tables.tvfBiasMult[patchCache->tvfblevel][dist], 8);
3467 + } else {
3468 + // > Bias
3469 + if (noteVal > bias) {
3470 + dist = noteVal - bias;
3471 + cutoff = FIXEDPOINT_UMULT(cutoff, synth->tables.tvfBiasMult[patchCache->tvfblevel][dist], 8);
3475 + //synth->printDebug("Cutoff after %d", cutoff);
3478 + depth = (depth * keyLookup->envDepthMult[patchCache->filtEnv.envdkf]) >> 8;
3479 + reshigh = (reshigh * depth) >> 7;
3481 + Bit32s tmp;
3483 + cutoff *= filtVal;
3484 + cutoff /= realVal; //FIXME:KG: With filter keyfollow 0, this makes no sense. What's correct?
3486 + reshigh *= filtVal;
3487 + reshigh /= realVal; //FIXME:KG: As above for cutoff
3489 + if (patchCache->waveform == 1) {
3490 + reshigh = (reshigh * 65) / 100;
3493 + if (cutoff > 100)
3494 + cutoff = 100;
3495 + else if (cutoff < 0)
3496 + cutoff = 0;
3497 + if (reshigh > 100)
3498 + reshigh = 100;
3499 + else if (reshigh < 0)
3500 + reshigh = 0;
3501 + tmp = noteLookup->nfiltTable[cutoff][reshigh];
3502 + //tmp *= keyfollow;
3503 + //tmp /= realfollow;
3505 + //synth->printDebug("Cutoff %d, tmp %d, freq %d", cutoff, tmp, tmp * 256);
3506 + return tmp;
3509 +bool Partial::shouldReverb() {
3510 + if (!isActive())
3511 + return false;
3512 + return patchCache->reverb;
3515 +Bit32u Partial::getAmpEnvelope() {
3516 + Bit32s tc;
3518 + EnvelopeStatus *tStat = &envs[EnvelopeType_amp];
3520 + if (!play)
3521 + return 0;
3523 + if (tStat->decaying) {
3524 + tc = tStat->envbase;
3525 + tc += (tStat->envdist * tStat->envpos) / tStat->envsize;
3526 + if (tc < 0)
3527 + tc = 0;
3528 + if ((tStat->envpos >= tStat->envsize) || (tc == 0)) {
3529 + play = false;
3530 + // Don't have to worry about prevlevel storage or anything, this partial's about to die
3531 + return 0;
3533 + } else {
3534 + if ((tStat->envstat == -1) || (tStat->envpos >= tStat->envsize)) {
3535 + if (tStat->envstat == -1)
3536 + tStat->envbase = 0;
3537 + else
3538 + tStat->envbase = patchCache->ampEnv.envlevel[tStat->envstat];
3539 + tStat->envstat++;
3540 + tStat->envpos = 0;
3541 + if (tStat->envstat == 4) {
3542 + //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
3543 + tc = patchCache->ampEnv.envlevel[3];
3544 + if (!poly->sustain)
3545 + startDecay(EnvelopeType_amp, tc);
3546 + else
3547 + tStat->sustaining = true;
3548 + goto PastCalc;
3550 + Bit8u targetLevel = patchCache->ampEnv.envlevel[tStat->envstat];
3551 + tStat->envdist = targetLevel - tStat->envbase;
3552 + Bit32u envTime = patchCache->ampEnv.envtime[tStat->envstat];
3553 + if (targetLevel == 0) {
3554 + tStat->envsize = synth->tables.envDecayTime[envTime];
3555 + } else {
3556 + int envLevelDelta = abs(tStat->envdist);
3557 + if (envTime > synth->tables.envDeltaMaxTime[envLevelDelta]) {
3558 + envTime = synth->tables.envDeltaMaxTime[envLevelDelta];
3560 + tStat->envsize = synth->tables.envTime[envTime];
3563 + // Time keyfollow is used by all sections of the envelope (confirmed on CM-32L)
3564 + tStat->envsize = FIXEDPOINT_UMULT(tStat->envsize, keyLookup->envTimeMult[(int)patchCache->ampEnv.envtkf], 8);
3566 + switch (tStat->envstat) {
3567 + case 0:
3568 + //Spot for velocity time follow
3569 + //Only used for first attack
3570 + tStat->envsize = FIXEDPOINT_UMULT(tStat->envsize, synth->tables.envTimeVelfollowMult[(int)patchCache->ampEnv.envvkf][poly->vel], 8);
3571 + //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
3572 + break;
3573 + case 1:
3574 + case 2:
3575 + case 3:
3576 + //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
3577 + break;
3578 + default:
3579 + synth->printDebug("Invalid TVA envelope number %d hit!", tStat->envstat);
3580 + break;
3583 + tStat->envsize++;
3585 + if (tStat->envdist != 0) {
3586 + tStat->counter = abs(tStat->envsize / tStat->envdist);
3587 + //synth->printDebug("Pos %d, envsize %d envdist %d", tStat->envstat, tStat->envsize, tStat->envdist);
3588 + } else {
3589 + tStat->counter = 0;
3590 + //synth->printDebug("Pos %d, envsize %d envdist %d", tStat->envstat, tStat->envsize, tStat->envdist);
3593 + tc = tStat->envbase;
3594 + tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize));
3595 + tStat->count = tStat->counter;
3596 +PastCalc:
3597 + tc = (tc * (Bit32s)patchCache->ampEnv.level) / 100;
3600 + // Prevlevel storage is bottle neck
3601 + tStat->prevlevel = tc;
3603 + //Bias level crap stuff now
3605 + for (int i = 0; i < 2; i++) {
3606 + if (patchCache->ampblevel[i]!=0) {
3607 + int bias = patchCache->ampbias[i];
3608 + if (patchCache->ampdir[i]==0) {
3609 + // < Bias
3610 + if (noteVal < bias) {
3611 + int dist = bias - noteVal;
3612 + tc = FIXEDPOINT_UMULT(tc, synth->tables.tvaBiasMult[patchCache->ampblevel[i]][dist], 8);
3614 + } else {
3615 + // > Bias
3616 + if (noteVal > bias) {
3617 + int dist = noteVal - bias;
3618 + tc = FIXEDPOINT_UMULT(tc, synth->tables.tvaBiasMult[patchCache->ampblevel[i]][dist], 8);
3623 + if (tc < 0) {
3624 + synth->printDebug("*** ERROR: tc < 0 (%d) at getAmpEnvelope()", tc);
3625 + tc = 0;
3627 + return (Bit32u)tc;
3630 +Bit32s Partial::getPitchEnvelope() {
3631 + EnvelopeStatus *tStat = &envs[EnvelopeType_pitch];
3633 + Bit32s tc;
3634 + pitchSustain = false;
3635 + if (tStat->decaying) {
3636 + if (tStat->envpos >= tStat->envsize)
3637 + tc = patchCache->pitchEnv.level[4];
3638 + else {
3639 + tc = tStat->envbase;
3640 + tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize));
3642 + } else {
3643 + if (tStat->envstat==3) {
3644 + tc = patchCache->pitchsustain;
3645 + if (poly->sustain)
3646 + pitchSustain = true;
3647 + else
3648 + startDecay(EnvelopeType_pitch, tc);
3649 + } else {
3650 + if ((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) {
3651 + tStat->envstat++;
3653 + tStat->envbase = patchCache->pitchEnv.level[tStat->envstat];
3655 + Bit32u envTime = patchCache->pitchEnv.time[tStat->envstat];
3656 + int envDiff = abs(patchCache->pitchEnv.level[tStat->envstat] - patchCache->pitchEnv.level[tStat->envstat + 1]);
3657 + if (envTime > synth->tables.envDeltaMaxTime[envDiff]) {
3658 + envTime = synth->tables.envDeltaMaxTime[envDiff];
3661 + tStat->envsize = (synth->tables.envTime[envTime] * keyLookup->envTimeMult[(int)patchCache->pitchEnv.timekeyfollow]) >> 8;
3663 + tStat->envpos = 0;
3664 + tStat->envsize++;
3665 + tStat->envdist = patchCache->pitchEnv.level[tStat->envstat + 1] - tStat->envbase;
3667 + tc = tStat->envbase;
3668 + tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize));
3670 + tStat->prevlevel = tc;
3672 + return tc;
3675 +void Partial::startDecayAll() {
3676 + startDecay(EnvelopeType_amp, envs[EnvelopeType_amp].prevlevel);
3677 + startDecay(EnvelopeType_filt, envs[EnvelopeType_filt].prevlevel);
3678 + startDecay(EnvelopeType_pitch, envs[EnvelopeType_pitch].prevlevel);
3679 + pitchSustain = false;
3682 +void Partial::startDecay(EnvelopeType envnum, Bit32s startval) {
3683 + EnvelopeStatus *tStat = &envs[envnum];
3685 + tStat->sustaining = false;
3686 + tStat->decaying = true;
3687 + tStat->envpos = 0;
3688 + tStat->envbase = startval;
3690 + switch (envnum) {
3691 + case EnvelopeType_amp:
3692 + tStat->envsize = FIXEDPOINT_UMULT(synth->tables.envDecayTime[(int)patchCache->ampEnv.envtime[4]], keyLookup->envTimeMult[(int)patchCache->ampEnv.envtkf], 8);
3693 + tStat->envdist = -startval;
3694 + break;
3695 + case EnvelopeType_filt:
3696 + tStat->envsize = FIXEDPOINT_UMULT(synth->tables.envDecayTime[(int)patchCache->filtEnv.envtime[4]], keyLookup->envTimeMult[(int)patchCache->filtEnv.envtkf], 8);
3697 + tStat->envdist = -startval;
3698 + break;
3699 + case EnvelopeType_pitch:
3700 + tStat->envsize = FIXEDPOINT_UMULT(synth->tables.envDecayTime[(int)patchCache->pitchEnv.time[3]], keyLookup->envTimeMult[(int)patchCache->pitchEnv.timekeyfollow], 8);
3701 + tStat->envdist = patchCache->pitchEnv.level[4] - startval;
3702 + break;
3703 + default:
3704 + break;
3706 + tStat->envsize++;
3708 diff -urN dosbox.orig/src/gui/partial.h dosbox/src/gui/partial.h
3709 --- dosbox.orig/src/gui/partial.h 1969-12-31 21:00:00.000000000 -0300
3710 +++ dosbox/src/gui/partial.h 2011-01-11 20:15:44.311498770 -0300
3711 @@ -0,0 +1,148 @@
3712 +/* Copyright (c) 2003-2005 Various contributors
3714 + * Permission is hereby granted, free of charge, to any person obtaining a copy
3715 + * of this software and associated documentation files (the "Software"), to
3716 + * deal in the Software without restriction, including without limitation the
3717 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
3718 + * sell copies of the Software, and to permit persons to whom the Software is
3719 + * furnished to do so, subject to the following conditions:
3721 + * The above copyright notice and this permission notice shall be included in
3722 + * all copies or substantial portions of the Software.
3724 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
3725 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
3726 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
3727 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
3728 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
3729 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
3730 + * IN THE SOFTWARE.
3731 + */
3733 +#ifndef MT32EMU_PARTIAL_H
3734 +#define MT32EMU_PARTIAL_H
3736 +namespace MT32Emu {
3738 +class Synth;
3739 +struct NoteLookup;
3741 +enum EnvelopeType {
3742 + EnvelopeType_amp = 0,
3743 + EnvelopeType_filt = 1,
3744 + EnvelopeType_pitch = 2
3747 +struct EnvelopeStatus {
3748 + Bit32s envpos;
3749 + Bit32s envstat;
3750 + Bit32s envbase;
3751 + Bit32s envdist;
3752 + Bit32s envsize;
3754 + bool sustaining;
3755 + bool decaying;
3756 + Bit32s prevlevel;
3758 + Bit32s counter;
3759 + Bit32s count;
3762 +// Class definition of MT-32 partials. 32 in all.
3763 +class Partial {
3764 +private:
3765 + Synth *synth;
3767 + int ownerPart; // -1 if unassigned
3768 + int mixType;
3769 + int structurePosition; // 0 or 1 of a structure pair
3770 + bool useNoisePair;
3772 + Bit16s myBuffer[MAX_SAMPLE_OUTPUT];
3774 + // Keyfollowed note value
3775 +#if MT32EMU_ACCURATENOTES == 1
3776 + NoteLookup noteLookupStorage;
3777 + float noteVal;
3778 +#else
3779 + int noteVal;
3780 + int fineShift;
3781 +#endif
3782 + const NoteLookup *noteLookup; // LUTs for this noteVal
3783 + const KeyLookup *keyLookup; // LUTs for the clamped (12..108) key
3785 + // Keyfollowed filter values
3786 + int realVal;
3787 + int filtVal;
3789 + // Only used for PCM partials
3790 + int pcmNum;
3791 + PCMWaveEntry *pcmWave;
3793 + int pulsewidth;
3795 + Bit32u lfoPos;
3796 + soundaddr partialOff;
3798 + Bit32u ampEnvVal;
3799 + Bit32u pitchEnvVal;
3801 + float history[32];
3803 + bool pitchSustain;
3805 + int loopPos;
3807 + dpoly *poly;
3809 + int bendShift;
3811 + Bit16s *mixBuffers(Bit16s *buf1, Bit16s *buf2, int len);
3812 + Bit16s *mixBuffersRingMix(Bit16s *buf1, Bit16s *buf2, int len);
3813 + Bit16s *mixBuffersRing(Bit16s *buf1, Bit16s *buf2, int len);
3814 + void mixBuffersStereo(Bit16s *buf1, Bit16s *buf2, Bit16s *outBuf, int len);
3816 + Bit32s getFiltEnvelope();
3817 + Bit32u getAmpEnvelope();
3818 + Bit32s getPitchEnvelope();
3820 + void initKeyFollow(int freqNum);
3822 +public:
3823 + const PatchCache *patchCache;
3824 + EnvelopeStatus envs[3];
3825 + bool play;
3827 + PatchCache cachebackup;
3829 + Partial *pair;
3830 + bool alreadyOutputed;
3831 + Bit32u age;
3833 + Partial(Synth *synth);
3834 + ~Partial();
3836 + int getOwnerPart() const;
3837 + int getKey() const;
3838 + const dpoly *getDpoly() const;
3839 + bool isActive();
3840 + void activate(int part);
3841 + void deactivate(void);
3842 + void startPartial(dpoly *usePoly, const PatchCache *useCache, Partial *pairPartial);
3843 + void startDecay(EnvelopeType envnum, Bit32s startval);
3844 + void startDecayAll();
3845 + void setBend(float factor);
3846 + bool shouldReverb();
3848 + // Returns true only if data written to buffer
3849 + // This function (unlike the one below it) returns processed stereo samples
3850 + // made from combining this single partial with its pair, if it has one.
3851 + bool produceOutput(Bit16s * partialBuf, long length);
3853 + // This function produces mono sample output using the partial's private internal buffer
3854 + Bit16s *generateSamples(long length);
3859 +#endif
3860 diff -urN dosbox.orig/src/gui/partialManager.cpp dosbox/src/gui/partialManager.cpp
3861 --- dosbox.orig/src/gui/partialManager.cpp 1969-12-31 21:00:00.000000000 -0300
3862 +++ dosbox/src/gui/partialManager.cpp 2011-01-11 20:15:44.311498770 -0300
3863 @@ -0,0 +1,272 @@
3864 +/* Copyright (c) 2003-2005 Various contributors
3866 + * Permission is hereby granted, free of charge, to any person obtaining a copy
3867 + * of this software and associated documentation files (the "Software"), to
3868 + * deal in the Software without restriction, including without limitation the
3869 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
3870 + * sell copies of the Software, and to permit persons to whom the Software is
3871 + * furnished to do so, subject to the following conditions:
3873 + * The above copyright notice and this permission notice shall be included in
3874 + * all copies or substantial portions of the Software.
3876 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
3877 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
3878 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
3879 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
3880 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
3881 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
3882 + * IN THE SOFTWARE.
3883 + */
3885 +#include <string.h>
3887 +#include "mt32emu.h"
3889 +using namespace MT32Emu;
3891 +PartialManager::PartialManager(Synth *useSynth) {
3892 + this->synth = useSynth;
3893 + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
3894 + partialTable[i] = new Partial(synth);
3897 +PartialManager::~PartialManager(void) {
3898 + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
3899 + delete partialTable[i];
3902 +void PartialManager::getPerPartPartialUsage(int usage[9]) {
3903 + memset(usage, 0, 9 * sizeof (int));
3904 + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
3905 + if (partialTable[i]->isActive())
3906 + usage[partialTable[i]->getOwnerPart()]++;
3910 +void PartialManager::clearAlreadyOutputed() {
3911 + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
3912 + partialTable[i]->alreadyOutputed = false;
3915 +void PartialManager::ageAll() {
3916 + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
3917 + partialTable[i]->age++;
3920 +bool PartialManager::shouldReverb(int i) {
3921 + return partialTable[i]->shouldReverb();
3924 +bool PartialManager::produceOutput(int i, Bit16s *buffer, Bit32u bufferLength) {
3925 + return partialTable[i]->produceOutput(buffer, bufferLength);
3928 +void PartialManager::deactivateAll() {
3929 + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
3930 + partialTable[i]->deactivate();
3934 +unsigned int PartialManager::setReserve(Bit8u *rset) {
3935 + unsigned int pr = 0;
3936 + for (int x = 0; x < 9; x++) {
3937 + for (int y = 0; y < rset[x]; y++) {
3938 + partialReserveTable[pr] = x;
3939 + pr++;
3942 + return pr;
3945 +Partial *PartialManager::allocPartial(int partNum) {
3946 + Partial *outPartial = NULL;
3948 + // Use the first inactive partial reserved for the specified part (if there are any)
3949 + // Otherwise, use the last inactive partial, if any
3950 + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
3951 + if (!partialTable[i]->isActive()) {
3952 + outPartial = partialTable[i];
3953 + if (partialReserveTable[i] == partNum)
3954 + break;
3957 + if (outPartial != NULL) {
3958 + outPartial->activate(partNum);
3959 + outPartial->age = 0;
3961 + return outPartial;
3964 +unsigned int PartialManager::getFreePartialCount(void) {
3965 + int count = 0;
3966 + memset(partialPart, 0, sizeof(partialPart));
3967 + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
3968 + if (!partialTable[i]->isActive())
3969 + count++;
3970 + else
3971 + partialPart[partialTable[i]->getOwnerPart()]++;
3973 + return count;
3977 +bool PartialManager::freePartials(unsigned int needed, int partNum) {
3978 + int i;
3979 + int myPartPrior = (int)mt32ram.system.reserveSettings[partNum];
3980 + if (myPartPrior<partialPart[partNum]) {
3981 + //This can have more parts, must kill off those with less priority
3982 + int most, mostPart;
3983 + while (needed > 0) {
3984 + int selectPart = -1;
3985 + //Find the worst offender with more partials than allocated and kill them
3986 + most = -1;
3987 + mostPart = -1;
3988 + int diff;
3990 + for (i=0;i<9;i++) {
3991 + diff = partialPart[i] - (int)mt32ram.system.reserveSettings[i];
3993 + if (diff>0) {
3994 + if (diff>most) {
3995 + most = diff;
3996 + mostPart = i;
4000 + selectPart = mostPart;
4001 + if (selectPart == -1) {
4002 + // All parts are within the allocated limits, you suck
4003 + // Look for first partial not of this part that's decaying perhaps?
4004 + return false;
4006 + bool found;
4007 + int oldest;
4008 + int oldnum;
4009 + while (partialPart[selectPart] > (int)mt32ram.system.reserveSettings[selectPart]) {
4010 + oldest = -1;
4011 + oldnum = -1;
4012 + found = false;
4013 + for (i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
4014 + if (partialTable[i]->isActive) {
4015 + if (partialTable[i]->ownerPart == selectPart) {
4016 + found = true;
4017 + if (partialTable[i]->age > oldest) {
4018 + oldest = partialTable[i]->age;
4019 + oldnum = i;
4024 + if (!found) break;
4025 + partialTable[oldnum]->deactivate();
4026 + --partialPart[selectPart];
4027 + --needed;
4031 + return true;
4033 + } else {
4034 + //This part has reached its max, must kill off its own
4035 + bool found;
4036 + int oldest;
4037 + int oldnum;
4038 + while (needed > 0) {
4039 + oldest = -1;
4040 + oldnum = -1;
4041 + found = false;
4042 + for (i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
4043 + if (partialTable[i]->isActive) {
4044 + if (partialTable[i]->ownerPart == partNum) {
4045 + found = true;
4046 + if (partialTable[i]->age > oldest) {
4047 + oldest = partialTable[i]->age;
4048 + oldnum = i;
4053 + if (!found) break;
4054 + partialTable[oldnum]->deactivate();
4055 + --needed;
4057 + // Couldn't free enough partials, sorry
4058 + if (needed>0) return false;
4059 + return true;
4064 +bool PartialManager::freePartials(unsigned int needed, int partNum) {
4065 + if (needed == 0) {
4066 + return true;
4068 + // Reclaim partials reserved for this part
4069 + // Kill those that are already decaying first
4070 + /*
4071 + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
4072 + if (partialReserveTable[i] == partNum) {
4073 + if (partialTable[i]->ownerPart != partNum) {
4074 + if (partialTable[i]->partCache->envs[AMPENV].decaying) {
4075 + partialTable[i]->isActive = false;
4076 + --needed;
4077 + if (needed == 0)
4078 + return true;
4082 + }*/
4083 + // Then kill those with the lowest part priority -- oldest at the moment
4084 + while (needed > 0) {
4085 + Bit32u prior = 0;
4086 + int priornum = -1;
4088 + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
4089 + if (partialReserveTable[i] == partNum && partialTable[i]->isActive() && partialTable[i]->getOwnerPart() != partNum) {
4090 + /*
4091 + if (mt32ram.system.reserveSettings[partialTable[i]->ownerPart] < prior) {
4092 + prior = mt32ram.system.reserveSettings[partialTable[i]->ownerPart];
4093 + priornum = i;
4094 + }*/
4095 + if (partialTable[i]->age >= prior) {
4096 + prior = partialTable[i]->age;
4097 + priornum = i;
4101 + if (priornum != -1) {
4102 + partialTable[priornum]->deactivate();
4103 + --needed;
4104 + } else {
4105 + break;
4109 + // Kill off the oldest partials within this part
4110 + while (needed > 0) {
4111 + Bit32u oldest = 0;
4112 + int oldlist = -1;
4113 + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
4114 + if (partialTable[i]->getOwnerPart() == partNum && partialTable[i]->isActive()) {
4115 + if (partialTable[i]->age >= oldest) {
4116 + oldest = partialTable[i]->age;
4117 + oldlist = i;
4121 + if (oldlist != -1) {
4122 + partialTable[oldlist]->deactivate();
4123 + --needed;
4124 + } else {
4125 + break;
4128 + return needed == 0;
4131 +const Partial *PartialManager::getPartial(unsigned int partialNum) const {
4132 + if (partialNum > MT32EMU_MAX_PARTIALS - 1)
4133 + return NULL;
4134 + return partialTable[partialNum];
4136 diff -urN dosbox.orig/src/gui/partialManager.h dosbox/src/gui/partialManager.h
4137 --- dosbox.orig/src/gui/partialManager.h 1969-12-31 21:00:00.000000000 -0300
4138 +++ dosbox/src/gui/partialManager.h 2011-01-11 20:15:44.311498770 -0300
4139 @@ -0,0 +1,56 @@
4140 +/* Copyright (c) 2003-2005 Various contributors
4142 + * Permission is hereby granted, free of charge, to any person obtaining a copy
4143 + * of this software and associated documentation files (the "Software"), to
4144 + * deal in the Software without restriction, including without limitation the
4145 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
4146 + * sell copies of the Software, and to permit persons to whom the Software is
4147 + * furnished to do so, subject to the following conditions:
4149 + * The above copyright notice and this permission notice shall be included in
4150 + * all copies or substantial portions of the Software.
4152 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4153 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4154 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4155 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4156 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
4157 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
4158 + * IN THE SOFTWARE.
4159 + */
4161 +#ifndef MT32EMU_PARTIALMANAGER_H
4162 +#define MT32EMU_PARTIALMANAGER_H
4164 +namespace MT32Emu {
4166 +class Synth;
4168 +class PartialManager {
4169 +private:
4170 + Synth *synth; // Only used for sending debug output
4172 + Partial *partialTable[MT32EMU_MAX_PARTIALS];
4173 + Bit32s partialReserveTable[MT32EMU_MAX_PARTIALS];
4174 + Bit32s partialPart[9]; // The count of partials played per part
4176 +public:
4178 + PartialManager(Synth *synth);
4179 + ~PartialManager();
4180 + Partial *allocPartial(int partNum);
4181 + unsigned int getFreePartialCount(void);
4182 + bool freePartials(unsigned int needed, int partNum);
4183 + unsigned int setReserve(Bit8u *rset);
4184 + void deactivateAll();
4185 + void ageAll();
4186 + bool produceOutput(int i, Bit16s *buffer, Bit32u bufferLength);
4187 + bool shouldReverb(int i);
4188 + void clearAlreadyOutputed();
4189 + void getPerPartPartialUsage(int usage[9]);
4190 + const Partial *getPartial(unsigned int partialNum) const;
4195 +#endif
4196 diff -urN dosbox.orig/src/gui/structures.h dosbox/src/gui/structures.h
4197 --- dosbox.orig/src/gui/structures.h 1969-12-31 21:00:00.000000000 -0300
4198 +++ dosbox/src/gui/structures.h 2011-01-11 20:15:44.314832104 -0300
4199 @@ -0,0 +1,284 @@
4200 +/* Copyright (c) 2003-2005 Various contributors
4202 + * Permission is hereby granted, free of charge, to any person obtaining a copy
4203 + * of this software and associated documentation files (the "Software"), to
4204 + * deal in the Software without restriction, including without limitation the
4205 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
4206 + * sell copies of the Software, and to permit persons to whom the Software is
4207 + * furnished to do so, subject to the following conditions:
4209 + * The above copyright notice and this permission notice shall be included in
4210 + * all copies or substantial portions of the Software.
4212 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4213 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4214 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4215 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4216 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
4217 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
4218 + * IN THE SOFTWARE.
4219 + */
4221 +#ifndef MT32EMU_STRUCTURES_H
4222 +#define MT32EMU_STRUCTURES_H
4224 +namespace MT32Emu {
4226 +const unsigned int MAX_SAMPLE_OUTPUT = 4096;
4228 +// MT32EMU_MEMADDR() converts from sysex-padded, MT32EMU_SYSEXMEMADDR converts to it
4229 +// Roland provides documentation using the sysex-padded addresses, so we tend to use that in code and output
4230 +#define MT32EMU_MEMADDR(x) ((((x) & 0x7f0000) >> 2) | (((x) & 0x7f00) >> 1) | ((x) & 0x7f))
4231 +#define MT32EMU_SYSEXMEMADDR(x) ((((x) & 0x1FC000) << 2) | (((x) & 0x3F80) << 1) | ((x) & 0x7f))
4233 +#ifdef _MSC_VER
4234 +#define MT32EMU_ALIGN_PACKED __declspec(align(1))
4235 +typedef unsigned __int64 Bit64u;
4236 +typedef signed __int64 Bit64s;
4237 +#else
4238 +#define MT32EMU_ALIGN_PACKED __attribute__((packed))
4239 +typedef unsigned long long Bit64u;
4240 +typedef signed long long Bit64s;
4241 +#endif
4243 +typedef unsigned int Bit32u;
4244 +typedef signed int Bit32s;
4245 +typedef unsigned short int Bit16u;
4246 +typedef signed short int Bit16s;
4247 +typedef unsigned char Bit8u;
4248 +typedef signed char Bit8s;
4250 +// The following structures represent the MT-32's memory
4251 +// Since sysex allows this memory to be written to in blocks of bytes,
4252 +// we keep this packed so that we can copy data into the various
4253 +// banks directly
4254 +#if defined(_MSC_VER) || defined (__MINGW32__)
4255 +#pragma pack(push, 1)
4256 +#else
4257 +#pragma pack(1)
4258 +#endif
4260 +struct TimbreParam {
4261 + struct commonParam {
4262 + char name[10];
4263 + Bit8u pstruct12; // 1&2 0-12 (1-13)
4264 + Bit8u pstruct34; // #3&4 0-12 (1-13)
4265 + Bit8u pmute; // 0-15 (0000-1111)
4266 + Bit8u nosustain; // 0-1(Normal, No sustain)
4267 + } MT32EMU_ALIGN_PACKED common;
4269 + struct partialParam {
4270 + struct wgParam {
4271 + Bit8u coarse; // 0-96 (C1,C#1-C9)
4272 + Bit8u fine; // 0-100 (-50 to +50 (cents?))
4273 + Bit8u keyfollow; // 0-16 (-1,-1/2,0,1,1/8,1/4,3/8,1/2,5/8,3/4,7/8,1,5/4,3/2,2.s1,s2)
4274 + Bit8u bender; // 0,1 (ON/OFF)
4275 + Bit8u waveform; // MT-32: 0-1 (SQU/SAW); LAPC-I: WG WAVEFORM/PCM BANK 0 - 3 (SQU/1, SAW/1, SQU/2, SAW/2)
4276 + Bit8u pcmwave; // 0-127 (1-128)
4277 + Bit8u pulsewid; // 0-100
4278 + Bit8u pwvelo; // 0-14 (-7 - +7)
4279 + } MT32EMU_ALIGN_PACKED wg;
4281 + struct envParam {
4282 + Bit8u depth; // 0-10
4283 + Bit8u sensitivity; // 1-100
4284 + Bit8u timekeyfollow; // 0-4
4285 + Bit8u time[4]; // 1-100
4286 + Bit8u level[5]; // 1-100 (-50 - +50)
4287 + } MT32EMU_ALIGN_PACKED env;
4289 + struct lfoParam {
4290 + Bit8u rate; // 0-100
4291 + Bit8u depth; // 0-100
4292 + Bit8u modsense; // 0-100
4293 + } MT32EMU_ALIGN_PACKED lfo;
4295 + struct tvfParam {
4296 + Bit8u cutoff; // 0-100
4297 + Bit8u resonance; // 0-30
4298 + Bit8u keyfollow; // 0-16 (-1,-1/2,1/4,0,1,1/8,1/4,3/8,1/2,5/8,3/2,7/8,1,5/4,3/2,2,s1,s2)
4299 + Bit8u biaspoint; // 0-127 (<1A-<7C >1A-7C)
4300 + Bit8u biaslevel; // 0-14 (-7 - +7)
4301 + Bit8u envdepth; // 0-100
4302 + Bit8u envsense; // 0-100
4303 + Bit8u envdkf; // DEPTH KEY FOLL0W 0-4
4304 + Bit8u envtkf; // TIME KEY FOLLOW 0-4
4305 + Bit8u envtime[5]; // 1-100
4306 + Bit8u envlevel[4]; // 1-100
4307 + } MT32EMU_ALIGN_PACKED tvf;
4309 + struct tvaParam {
4310 + Bit8u level; // 0-100
4311 + Bit8u velosens; // 0-100
4312 + Bit8u biaspoint1; // 0-127 (<1A-<7C >1A-7C)
4313 + Bit8u biaslevel1; // 0-12 (-12 - 0)
4314 + Bit8u biaspoint2; // 0-127 (<1A-<7C >1A-7C)
4315 + Bit8u biaslevel2; // 0-12 (-12 - 0)
4316 + Bit8u envtkf; // TIME KEY FOLLOW 0-4
4317 + Bit8u envvkf; // VELOS KEY FOLL0W 0-4
4318 + Bit8u envtime[5]; // 1-100
4319 + Bit8u envlevel[4]; // 1-100
4320 + } MT32EMU_ALIGN_PACKED tva;
4321 + } MT32EMU_ALIGN_PACKED partial[4];
4322 +} MT32EMU_ALIGN_PACKED;
4324 +struct PatchParam {
4325 + Bit8u timbreGroup; // TIMBRE GROUP 0-3 (group A, group B, Memory, Rhythm)
4326 + Bit8u timbreNum; // TIMBRE NUMBER 0-63
4327 + Bit8u keyShift; // KEY SHIFT 0-48 (-24 - +24 semitones)
4328 + Bit8u fineTune; // FINE TUNE 0-100 (-50 - +50 cents)
4329 + Bit8u benderRange; // BENDER RANGE 0-24
4330 + Bit8u assignMode; // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4)
4331 + Bit8u reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON)
4332 + Bit8u dummy; // (DUMMY)
4333 +} MT32EMU_ALIGN_PACKED;
4335 +struct MemParams {
4336 + // NOTE: The MT-32 documentation only specifies PatchTemp areas for parts 1-8.
4337 + // The LAPC-I documentation specified an additional area for rhythm at the end,
4338 + // where all parameters but fine tune, assign mode and output level are ignored
4339 + struct PatchTemp {
4340 + PatchParam patch;
4341 + Bit8u outlevel; // OUTPUT LEVEL 0-100
4342 + Bit8u panpot; // PANPOT 0-14 (R-L)
4343 + Bit8u dummyv[6];
4344 + } MT32EMU_ALIGN_PACKED;
4346 + PatchTemp patchSettings[9];
4348 + struct RhythmTemp {
4349 + Bit8u timbre; // TIMBRE 0-94 (M1-M64,R1-30,OFF)
4350 + Bit8u outlevel; // OUTPUT LEVEL 0-100
4351 + Bit8u panpot; // PANPOT 0-14 (R-L)
4352 + Bit8u reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON)
4353 + } MT32EMU_ALIGN_PACKED;
4355 + RhythmTemp rhythmSettings[85];
4357 + TimbreParam timbreSettings[8];
4359 + PatchParam patches[128];
4361 + // NOTE: There are only 30 timbres in the "rhythm" bank for MT-32; the additional 34 are for LAPC-I and above
4362 + struct PaddedTimbre {
4363 + TimbreParam timbre;
4364 + Bit8u padding[10];
4365 + } MT32EMU_ALIGN_PACKED;
4367 + PaddedTimbre timbres[64 + 64 + 64 + 64]; // Group A, Group B, Memory, Rhythm
4369 + struct SystemArea {
4370 + Bit8u masterTune; // MASTER TUNE 0-127 432.1-457.6Hz
4371 + Bit8u reverbMode; // REVERB MODE 0-3 (room, hall, plate, tap delay)
4372 + Bit8u reverbTime; // REVERB TIME 0-7 (1-8)
4373 + Bit8u reverbLevel; // REVERB LEVEL 0-7 (1-8)
4374 + Bit8u reserveSettings[9]; // PARTIAL RESERVE (PART 1) 0-32
4375 + Bit8u chanAssign[9]; // MIDI CHANNEL (PART1) 0-16 (1-16,OFF)
4376 + Bit8u masterVol; // MASTER VOLUME 0-100
4377 + } MT32EMU_ALIGN_PACKED;
4379 + SystemArea system;
4382 +#if defined(_MSC_VER) || defined (__MINGW32__)
4383 +#pragma pack(pop)
4384 +#else
4385 +#pragma pack()
4386 +#endif
4388 +struct PCMWaveEntry {
4389 + Bit32u addr;
4390 + Bit32u len;
4391 + double tune;
4392 + bool loop;
4395 +struct soundaddr {
4396 + Bit16u pcmplace;
4397 + Bit16u pcmoffset;
4400 +struct StereoVolume {
4401 + Bit16s leftvol;
4402 + Bit16s rightvol;
4405 +// This is basically a per-partial, pre-processed combination of timbre and patch/rhythm settings
4406 +struct PatchCache {
4407 + bool playPartial;
4408 + bool PCMPartial;
4409 + int pcm;
4410 + char waveform;
4411 + int pulsewidth;
4412 + int pwsens;
4414 + float pitch;
4416 + int lfodepth;
4417 + int lforate;
4418 + Bit32u lfoperiod;
4419 + int modsense;
4421 + float pitchKeyfollow;
4423 + int filtkeyfollow;
4425 + int tvfbias;
4426 + int tvfblevel;
4427 + int tvfdir;
4429 + int ampbias[2];
4430 + int ampblevel[2];
4431 + int ampdir[2];
4433 + int ampdepth;
4434 + int amplevel;
4436 + bool useBender;
4437 + float benderRange; // 0.0, 1.0, .., 24.0 (semitones)
4439 + TimbreParam::partialParam::envParam pitchEnv;
4440 + TimbreParam::partialParam::tvaParam ampEnv;
4441 + TimbreParam::partialParam::tvfParam filtEnv;
4443 + Bit32s pitchsustain;
4444 + Bit32s filtsustain;
4446 + Bit32u structureMix;
4447 + int structurePosition;
4448 + int structurePair;
4450 + // The following fields are actually common to all partials in the timbre
4451 + bool dirty;
4452 + Bit32u partialCount;
4453 + bool sustain;
4454 + float pitchShift;
4455 + bool reverb;
4456 + const StereoVolume *pansetptr;
4459 +class Partial; // Forward reference for class defined in partial.h
4461 +struct dpoly {
4462 + bool isPlaying;
4464 + unsigned int key;
4465 + int freqnum;
4466 + int vel;
4468 + bool isDecay;
4470 + const Bit32u *volumeptr;
4472 + Partial *partials[4];
4474 + bool pedalhold; // This marks keys that have been released on the keyboard, but are being held by the pedal
4475 + bool sustain;
4477 + bool isActive() const;
4478 + Bit32u getAge() const;
4483 +#endif
4484 diff -urN dosbox.orig/src/gui/synth.cpp dosbox/src/gui/synth.cpp
4485 --- dosbox.orig/src/gui/synth.cpp 1969-12-31 21:00:00.000000000 -0300
4486 +++ dosbox/src/gui/synth.cpp 2011-01-11 20:15:44.314832104 -0300
4487 @@ -0,0 +1,1204 @@
4488 +/* Copyright (c) 2003-2005 Various contributors
4490 + * Permission is hereby granted, free of charge, to any person obtaining a copy
4491 + * of this software and associated documentation files (the "Software"), to
4492 + * deal in the Software without restriction, including without limitation the
4493 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
4494 + * sell copies of the Software, and to permit persons to whom the Software is
4495 + * furnished to do so, subject to the following conditions:
4497 + * The above copyright notice and this permission notice shall be included in
4498 + * all copies or substantial portions of the Software.
4500 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4501 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4502 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4503 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4504 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
4505 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
4506 + * IN THE SOFTWARE.
4507 + */
4509 +#include <math.h>
4510 +#include <string.h>
4511 +#include <stdlib.h>
4513 +#include "support.h"
4514 +#include "mt32emu.h"
4516 +#if defined(MACOSX) || defined(SOLARIS) || defined(__MINGW32__)
4517 +// Older versions of Mac OS X didn't supply a powf function, so using it
4518 +// will cause a binary incompatibility when trying to run a binary built
4519 +// on a newer OS X release on an older one. And Solaris 8 doesn't provide
4520 +// powf, floorf, fabsf etc. at all.
4521 +// Cross-compiled MinGW32 toolchains suffer from a cross-compile bug in
4522 +// libstdc++. math/stubs.o should be empty, but it comes with a symbol for
4523 +// powf, resulting in a linker error because of multiple definitions.
4524 +// Hence we re-define them here. The only potential drawback is that it
4525 +// might be a little bit slower this way.
4526 +#define powf(x,y) ((float)pow(x,y))
4527 +#define floorf(x) ((float)floor(x))
4528 +#define fabsf(x) ((float)fabs(x))
4529 +#endif
4531 +namespace MT32Emu {
4533 +const int MAX_SYSEX_SIZE = 512;
4535 +const ControlROMMap ControlROMMaps[5] = {
4536 + // ID IDc IDbytes PCMmap PCMc tmbrA tmbrAO, tmbrB tmbrBO, tmbrR trC rhythm rhyC rsrv panpot prog
4537 + {0x4014, 22, "\000 ver1.04 14 July 87 ", 0x3000, 128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200, 30, 0x73A6, 85, 0x57C7, 0x57D0, 0x57E2}, // MT-32 revision 0
4538 + {0x4014, 22, "\000 ver1.06 31 Aug, 87 ", 0x3000, 128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200, 30, 0x7414, 85, 0x57D9, 0x57E2, 0x57F4}, // MT-32 revision 0
4539 + {0x4010, 22, "\000 ver1.07 10 Oct, 87 ", 0x3000, 128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200, 30, 0x73fe, 85, 0x57B1, 0x57BA, 0x57CC}, // MT-32 revision 1
4540 + {0x4010, 22, "\000verX.XX 30 Sep, 88 ", 0x3000, 128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200, 30, 0x741C, 85, 0x57E5, 0x57EE, 0x5800}, // MT-32 Blue Ridge mod
4541 + {0x2205, 22, "\000CM32/LAPC1.02 891205", 0x8100, 256, 0x8000, 0x8000, 0x8080, 0x8000, 0x8500, 64, 0x8580, 85, 0x4F93, 0x4F9C, 0x4FAE} // CM-32L
4542 + // (Note that all but CM-32L ROM actually have 86 entries for rhythmTemp)
4545 +float iir_filter_normal(float input, float *hist1_ptr, float *coef_ptr) {
4546 + float *hist2_ptr;
4547 + float output,new_hist;
4549 + hist2_ptr = hist1_ptr + 1; // next history
4551 + // 1st number of coefficients array is overall input scale factor, or filter gain
4552 + output = input * (*coef_ptr++);
4554 + output = output - *hist1_ptr * (*coef_ptr++);
4555 + new_hist = output - *hist2_ptr * (*coef_ptr++); // poles
4557 + output = new_hist + *hist1_ptr * (*coef_ptr++);
4558 + output = output + *hist2_ptr * (*coef_ptr++); // zeros
4560 + *hist2_ptr++ = *hist1_ptr;
4561 + *hist1_ptr++ = new_hist;
4562 + hist1_ptr++;
4563 + hist2_ptr++;
4565 + // i = 1
4566 + output = output - *hist1_ptr * (*coef_ptr++);
4567 + new_hist = output - *hist2_ptr * (*coef_ptr++); // poles
4569 + output = new_hist + *hist1_ptr * (*coef_ptr++);
4570 + output = output + *hist2_ptr * (*coef_ptr++); // zeros
4572 + *hist2_ptr++ = *hist1_ptr;
4573 + *hist1_ptr++ = new_hist;
4575 + return(output);
4578 +Bit8u Synth::calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum) {
4579 + for (unsigned int i = 0; i < len; i++) {
4580 + checksum = checksum + data[i];
4582 + checksum = checksum & 0x7f;
4583 + if (checksum)
4584 + checksum = 0x80 - checksum;
4585 + return checksum;
4588 +Synth::Synth() {
4589 + isOpen = false;
4590 + reverbModel = NULL;
4591 + partialManager = NULL;
4592 + memset(parts, 0, sizeof(parts));
4595 +Synth::~Synth() {
4596 + close(); // Make sure we're closed and everything is freed
4599 +int Synth::report(ReportType type, const void *data) {
4600 + if (myProp.report != NULL) {
4601 + return myProp.report(myProp.userData, type, data);
4603 + return 0;
4606 +void Synth::printDebug(const char *fmt, ...) {
4607 + va_list ap;
4608 + va_start(ap, fmt);
4609 + if (myProp.printDebug != NULL) {
4610 + myProp.printDebug(myProp.userData, fmt, ap);
4611 + } else {
4612 + vprintf(fmt, ap);
4613 + printf("\n");
4615 + va_end(ap);
4618 +void Synth::initReverb(Bit8u newRevMode, Bit8u newRevTime, Bit8u newRevLevel) {
4619 + // FIXME:KG: I don't think it's necessary to recreate the reverbModel... Just set the parameters
4620 + delete reverbModel;
4621 + reverbModel = new revmodel();
4623 + switch (newRevMode) {
4624 + case 0:
4625 + reverbModel->setroomsize(.1f);
4626 + reverbModel->setdamp(.75f);
4627 + break;
4628 + case 1:
4629 + reverbModel->setroomsize(.5f);
4630 + reverbModel->setdamp(.5f);
4631 + break;
4632 + case 2:
4633 + reverbModel->setroomsize(.5f);
4634 + reverbModel->setdamp(.1f);
4635 + break;
4636 + case 3:
4637 + reverbModel->setroomsize(1.0f);
4638 + reverbModel->setdamp(.75f);
4639 + break;
4640 + default:
4641 + reverbModel->setroomsize(.1f);
4642 + reverbModel->setdamp(.5f);
4643 + break;
4645 + reverbModel->setdry(1);
4646 + reverbModel->setwet((float)newRevLevel / 8.0f);
4647 + reverbModel->setwidth((float)newRevTime / 8.0f);
4650 +File *Synth::openFile(const char *filename, File::OpenMode mode) {
4651 + if (myProp.openFile != NULL) {
4652 + return myProp.openFile(myProp.userData, filename, mode);
4654 + char pathBuf[2048];
4655 + if (myProp.baseDir != NULL) {
4656 + strcpy(&pathBuf[0], myProp.baseDir);
4657 + strcat(&pathBuf[0], filename);
4658 + filename = pathBuf;
4660 + ANSIFile *file = new ANSIFile();
4661 + if (!file->open(filename, mode)) {
4662 + delete file;
4663 + return NULL;
4665 + return file;
4668 +void Synth::closeFile(File *file) {
4669 + if (myProp.closeFile != NULL) {
4670 + myProp.closeFile(myProp.userData, file);
4671 + } else {
4672 + file->close();
4673 + delete file;
4677 +bool Synth::loadPreset(File *file) {
4678 + bool inSys = false;
4679 + Bit8u sysexBuf[MAX_SYSEX_SIZE];
4680 + Bit16u syslen = 0;
4681 + bool rc = true;
4682 + for (;;) {
4683 + Bit8u c;
4684 + if (!file->readBit8u(&c)) {
4685 + if (!file->isEOF()) {
4686 + rc = false;
4688 + break;
4690 + sysexBuf[syslen] = c;
4691 + if (inSys) {
4692 + syslen++;
4693 + if (c == 0xF7) {
4694 + playSysex(&sysexBuf[0], syslen);
4695 + inSys = false;
4696 + syslen = 0;
4697 + } else if (syslen == MAX_SYSEX_SIZE) {
4698 + printDebug("MAX_SYSEX_SIZE (%d) exceeded while processing preset, ignoring message", MAX_SYSEX_SIZE);
4699 + inSys = false;
4700 + syslen = 0;
4702 + } else if (c == 0xF0) {
4703 + syslen++;
4704 + inSys = true;
4707 + return rc;
4710 +bool Synth::loadControlROM(const char *filename) {
4711 + File *file = openFile(filename, File::OpenMode_read); // ROM File
4712 + if (file == NULL) {
4713 + return false;
4715 + bool rc = (file->read(controlROMData, CONTROL_ROM_SIZE) == CONTROL_ROM_SIZE);
4717 + closeFile(file);
4718 + if (!rc)
4719 + return rc;
4721 + // Control ROM successfully loaded, now check whether it's a known type
4722 + controlROMMap = NULL;
4723 + for (unsigned int i = 0; i < sizeof (ControlROMMaps) / sizeof (ControlROMMaps[0]); i++) {
4724 + if (memcmp(&controlROMData[ControlROMMaps[i].idPos], ControlROMMaps[i].idBytes, ControlROMMaps[i].idLen) == 0) {
4725 + controlROMMap = &ControlROMMaps[i];
4726 + return true;
4729 + return false;
4732 +bool Synth::loadPCMROM(const char *filename) {
4733 + File *file = openFile(filename, File::OpenMode_read); // ROM File
4734 + if (file == NULL) {
4735 + return false;
4737 + bool rc = true;
4738 + int i;
4739 + for (i = 0; i < pcmROMSize; i++) {
4740 + Bit8u s;
4741 + if (!file->readBit8u(&s)) {
4742 + if (!file->isEOF()) {
4743 + rc = false;
4745 + break;
4747 + Bit8u c;
4748 + if (!file->readBit8u(&c)) {
4749 + if (!file->isEOF()) {
4750 + rc = false;
4751 + } else {
4752 + printDebug("PCM ROM file has an odd number of bytes! Ignoring last");
4754 + break;
4757 + short e;
4758 + int bit;
4759 + int u;
4760 + int order[16] = {0, 9, 1 ,2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 8};
4762 + e = 0;
4763 + for (u = 0; u < 15; u++) {
4764 + if (order[u] < 8)
4765 + bit = (s >> (7 - order[u])) & 0x1;
4766 + else
4767 + bit = (c >> (7 - (order[u] - 8))) & 0x1;
4768 + e = e | (short)(bit << (15 - u));
4771 + /*
4772 + //Bit16s e = ( ((s & 0x7f) << 4) | ((c & 0x40) << 6) | ((s & 0x80) << 6) | ((c & 0x3f))) << 2;
4773 + if (e<0)
4774 + e = -32767 - e;
4775 + int ut = abs(e);
4776 + int dif = 0x7fff - ut;
4777 + x = exp(((float)((float)0x8000-(float)dif) / (float)0x1000));
4778 + e = (int)((float)e * (x/3200));
4779 + */
4781 + // File is companded (dB?), convert to linear PCM
4782 + // MINDB = -96
4783 + // MAXDB = -15
4784 + float testval;
4785 + testval = (float)((~e) & 0x7fff);
4786 + testval = -(testval / 400.00f);
4787 + //testval = -(testval / 341.32291666666666666666666666667);
4788 + float vol = powf(8, testval / 20) * 32767.0f;
4790 + if (e > 0)
4791 + vol = -vol;
4793 + pcmROMData[i] = (Bit16s)vol;
4795 + if (i != pcmROMSize) {
4796 + printDebug("PCM ROM file is too short (expected %d, got %d)", pcmROMSize, i);
4797 + rc = false;
4799 + closeFile(file);
4800 + return rc;
4803 +bool Synth::initPCMList(Bit16u mapAddress, Bit16u count) {
4804 + ControlROMPCMStruct *tps = (ControlROMPCMStruct *)&controlROMData[mapAddress];
4805 + for (int i = 0; i < count; i++) {
4806 + int rAddr = tps[i].pos * 0x800;
4807 + int rLenExp = (tps[i].len & 0x70) >> 4;
4808 + int rLen = 0x800 << rLenExp;
4809 + bool rLoop = (tps[i].len & 0x80) != 0;
4810 + //Bit8u rFlag = tps[i].len & 0x0F;
4811 + Bit16u rTuneOffset = (tps[i].pitchMSB << 8) | tps[i].pitchLSB;
4812 + // The number below is confirmed to a reasonable degree of accuracy on CM-32L
4813 + double STANDARDFREQ = 442.0;
4814 + float rTune = (float)(STANDARDFREQ * pow(2.0, (0x5000 - rTuneOffset) / 4056.0 - 9.0 / 12.0));
4815 + //printDebug("%f,%d,%d", (double)pTune, tps[i].pitchCoarse, tps[i].pitchFine);
4816 + if (rAddr + rLen > pcmROMSize) {
4817 + printDebug("Control ROM error: Wave map entry %d points to invalid PCM address 0x%04X, length 0x%04X", i, rAddr, rLen);
4818 + return false;
4820 + pcmWaves[i].addr = rAddr;
4821 + pcmWaves[i].len = rLen;
4822 + pcmWaves[i].loop = rLoop;
4823 + pcmWaves[i].tune = rTune;
4825 + return false;
4828 +bool Synth::initRhythmTimbre(int timbreNum, const Bit8u *mem, unsigned int memLen) {
4829 + if (memLen < sizeof(TimbreParam::commonParam)) {
4830 + return false;
4832 + TimbreParam *timbre = &mt32ram.timbres[timbreNum].timbre;
4833 + memcpy(&timbre->common, mem, 14);
4834 + unsigned int memPos = 14;
4835 + char drumname[11];
4836 + memset(drumname, 0, 11);
4837 + memcpy(drumname, timbre->common.name, 10);
4838 + for (int t = 0; t < 4; t++) {
4839 + if (((timbre->common.pmute >> t) & 0x1) == 0x1) {
4840 + if (memPos + 58 >= memLen) {
4841 + return false;
4843 + memcpy(&timbre->partial[t], mem + memPos, 58);
4844 + memPos += 58;
4847 + return true;
4850 +bool Synth::initRhythmTimbres(Bit16u mapAddress, Bit16u count) {
4851 + const Bit8u *drumMap = &controlROMData[mapAddress];
4852 + int timbreNum = 192;
4853 + for (Bit16u i = 0; i < count * 2; i += 2) {
4854 + Bit16u address = (drumMap[i + 1] << 8) | drumMap[i];
4855 + /*
4856 + // This check is nonsensical when the control ROM is the full 64KB addressable by 16-bit absolute pointers (which it is)
4857 + if (address >= CONTROL_ROM_SIZE) {
4858 + printDebug("Control ROM error: Timbre map entry 0x%04x points to invalid timbre address 0x%04x", i, address);
4859 + return false;
4861 + */
4862 + if (!initRhythmTimbre(timbreNum++, &controlROMData[address], CONTROL_ROM_SIZE - address)) {
4863 + printDebug("Control ROM error: Timbre map entry 0x%04x points to invalid timbre 0x%04x", i, address);
4864 + return false;
4867 + return true;
4870 +bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int startTimbre) {
4871 + for (Bit16u i = mapAddress; i < mapAddress + 0x80; i += 2) {
4872 + Bit16u address = (controlROMData[i + 1] << 8) | controlROMData[i];
4873 + if (address + sizeof(TimbreParam) > CONTROL_ROM_SIZE) {
4874 + printDebug("Control ROM error: Timbre map entry 0x%04x points to invalid timbre address 0x%04x", i, address);
4875 + return false;
4877 + address = address + offset;
4878 + TimbreParam *timbre = &mt32ram.timbres[startTimbre++].timbre;
4879 + memcpy(timbre, &controlROMData[address], sizeof(TimbreParam));
4881 + return true;
4884 +bool Synth::open(SynthProperties &useProp) {
4885 + if (isOpen)
4886 + return false;
4888 + myProp = useProp;
4889 + if (useProp.baseDir != NULL) {
4890 + myProp.baseDir = new char[strlen(useProp.baseDir) + 1];
4891 + strcpy(myProp.baseDir, useProp.baseDir);
4894 + // This is to help detect bugs
4895 + memset(&mt32ram, '?', sizeof(mt32ram));
4897 + printDebug("Loading Control ROM");
4898 + if (!loadControlROM("CM32L_CONTROL.ROM")) {
4899 + if (!loadControlROM("MT32_CONTROL.ROM")) {
4900 + LOG_MSG("Init Error - Missing or invalid MT32_CONTROL.ROM");
4901 + report(ReportType_errorControlROM, NULL);
4902 + return false;
4906 + // 512KB PCM ROM for MT-32, etc.
4907 + // 1MB PCM ROM for CM-32L, LAPC-I, CM-64, CM-500
4908 + // Note that the size below is given in samples (16-bit), not bytes
4909 + pcmROMSize = controlROMMap->pcmCount == 256 ? 512 * 1024 : 256 * 1024;
4910 + pcmROMData = new Bit16s[pcmROMSize];
4912 + printDebug("Loading PCM ROM");
4913 + if (!loadPCMROM("CM32L_PCM.ROM")) {
4914 + if (!loadPCMROM("MT32_PCM.ROM")) {
4915 + LOG_MSG("Init Error - Missing MT32_PCM.ROM");
4916 + report(ReportType_errorPCMROM, NULL);
4917 + return false;
4921 + printDebug("Initializing Timbre Bank A");
4922 + if (!initTimbres(controlROMMap->timbreAMap, controlROMMap->timbreAOffset, 0)) {
4923 + return false;
4926 + printDebug("Initializing Timbre Bank B");
4927 + if (!initTimbres(controlROMMap->timbreBMap, controlROMMap->timbreBOffset, 64)) {
4928 + return false;
4931 + printDebug("Initializing Timbre Bank R");
4932 + if (!initRhythmTimbres(controlROMMap->timbreRMap, controlROMMap->timbreRCount)) {
4933 + return false;
4936 + printDebug("Initializing Timbre Bank M");
4937 + // CM-64 seems to initialize all bytes in this bank to 0.
4938 + memset(&mt32ram.timbres[128], 0, sizeof (mt32ram.timbres[128]) * 64);
4940 + partialManager = new PartialManager(this);
4942 + pcmWaves = new PCMWaveEntry[controlROMMap->pcmCount];
4944 + printDebug("Initializing PCM List");
4945 + initPCMList(controlROMMap->pcmTable, controlROMMap->pcmCount);
4947 + printDebug("Initializing Rhythm Temp");
4948 + memcpy(mt32ram.rhythmSettings, &controlROMData[controlROMMap->rhythmSettings], controlROMMap->rhythmSettingsCount * 4);
4950 + printDebug("Initializing Patches");
4951 + for (Bit8u i = 0; i < 128; i++) {
4952 + PatchParam *patch = &mt32ram.patches[i];
4953 + patch->timbreGroup = i / 64;
4954 + patch->timbreNum = i % 64;
4955 + patch->keyShift = 24;
4956 + patch->fineTune = 50;
4957 + patch->benderRange = 12;
4958 + patch->assignMode = 0;
4959 + patch->reverbSwitch = 1;
4960 + patch->dummy = 0;
4963 + printDebug("Initializing System");
4964 + // The MT-32 manual claims that "Standard pitch" is 442Hz.
4965 + mt32ram.system.masterTune = 0x4a; // Confirmed
4966 + mt32ram.system.reverbMode = 0; // Confirmed
4967 + mt32ram.system.reverbTime = 5; // Confirmed
4968 + mt32ram.system.reverbLevel = 3; // Confirmed
4969 + memcpy(mt32ram.system.reserveSettings, &controlROMData[controlROMMap->reserveSettings], 9); // Confirmed
4970 + for (Bit8u i = 0; i < 9; i++) {
4971 + // This is the default: {1, 2, 3, 4, 5, 6, 7, 8, 9}
4972 + // An alternative configuration can be selected by holding "Master Volume"
4973 + // and pressing "PART button 1" on the real MT-32's frontpanel.
4974 + // The channel assignment is then {0, 1, 2, 3, 4, 5, 6, 7, 9}
4975 + mt32ram.system.chanAssign[i] = i + 1;
4977 + mt32ram.system.masterVol = 100; // Confirmed
4978 + if (!refreshSystem())
4979 + return false;
4981 + for (int i = 0; i < 8; i++) {
4982 + mt32ram.patchSettings[i].outlevel = 80;
4983 + mt32ram.patchSettings[i].panpot = controlROMData[controlROMMap->panSettings + i];
4984 + memset(mt32ram.patchSettings[i].dummyv, 0, sizeof(mt32ram.patchSettings[i].dummyv));
4985 + parts[i] = new Part(this, i);
4986 + parts[i]->setProgram(controlROMData[controlROMMap->programSettings + i]);
4988 + parts[8] = new RhythmPart(this, 8);
4990 + // For resetting mt32 mid-execution
4991 + mt32default = mt32ram;
4993 + iirFilter = &iir_filter_normal;
4995 +#ifdef MT32EMU_HAVE_X86
4996 + bool availableSSE = DetectSIMD();
4997 + bool available3DNow = Detect3DNow();
4999 + if (availableSSE)
5000 + report(ReportType_availableSSE, NULL);
5001 + if (available3DNow)
5002 + report(ReportType_available3DNow, NULL);
5004 + if (available3DNow) {
5005 + printDebug("Detected and using SIMD (AMD 3DNow) extensions");
5006 + iirFilter = &iir_filter_3dnow;
5007 + report(ReportType_using3DNow, NULL);
5008 + } else if (availableSSE) {
5009 + printDebug("Detected and using SIMD (Intel SSE) extensions");
5010 + iirFilter = &iir_filter_sse;
5011 + report(ReportType_usingSSE, NULL);
5013 +#endif
5015 + isOpen = true;
5016 + isEnabled = false;
5018 + printDebug("*** Initialisation complete ***");
5019 + return true;
5022 +void Synth::close(void) {
5023 + if (!isOpen)
5024 + return;
5026 + tables.freeNotes();
5027 + if (partialManager != NULL) {
5028 + delete partialManager;
5029 + partialManager = NULL;
5032 + if (reverbModel != NULL) {
5033 + delete reverbModel;
5034 + reverbModel = NULL;
5037 + for (int i = 0; i < 9; i++) {
5038 + if (parts[i] != NULL) {
5039 + delete parts[i];
5040 + parts[i] = NULL;
5043 + if (myProp.baseDir != NULL) {
5044 + delete myProp.baseDir;
5045 + myProp.baseDir = NULL;
5048 + delete[] pcmWaves;
5049 + delete[] pcmROMData;
5050 + isOpen = false;
5053 +void Synth::playMsg(Bit32u msg) {
5054 + // FIXME: Implement active sensing
5055 + unsigned char code = (unsigned char)((msg & 0x0000F0) >> 4);
5056 + unsigned char chan = (unsigned char) (msg & 0x00000F);
5057 + unsigned char note = (unsigned char)((msg & 0x00FF00) >> 8);
5058 + unsigned char velocity = (unsigned char)((msg & 0xFF0000) >> 16);
5059 + isEnabled = true;
5061 + //printDebug("Playing chan %d, code 0x%01x note: 0x%02x", chan, code, note);
5063 + signed char part = chantable[chan];
5064 + if (part < 0 || part > 8) {
5065 + printDebug("Play msg on unreg chan %d (%d): code=0x%01x, vel=%d", chan, part, code, velocity);
5066 + return;
5068 + playMsgOnPart(part, code, note, velocity);
5071 +void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity) {
5072 + Bit32u bend;
5074 + //printDebug("Synth::playMsg(0x%02x)",msg);
5075 + switch (code) {
5076 + case 0x8:
5077 + //printDebug("Note OFF - Part %d", part);
5078 + // The MT-32 ignores velocity for note off
5079 + parts[part]->stopNote(note);
5080 + break;
5081 + case 0x9:
5082 + //printDebug("Note ON - Part %d, Note %d Vel %d", part, note, velocity);
5083 + if (velocity == 0) {
5084 + // MIDI defines note-on with velocity 0 as being the same as note-off with velocity 40
5085 + parts[part]->stopNote(note);
5086 + } else {
5087 + parts[part]->playNote(note, velocity);
5089 + break;
5090 + case 0xB: // Control change
5091 + switch (note) {
5092 + case 0x01: // Modulation
5093 + //printDebug("Modulation: %d", velocity);
5094 + parts[part]->setModulation(velocity);
5095 + break;
5096 + case 0x07: // Set volume
5097 + //printDebug("Volume set: %d", velocity);
5098 + parts[part]->setVolume(velocity);
5099 + break;
5100 + case 0x0A: // Pan
5101 + //printDebug("Pan set: %d", velocity);
5102 + parts[part]->setPan(velocity);
5103 + break;
5104 + case 0x0B:
5105 + //printDebug("Expression set: %d", velocity);
5106 + parts[part]->setExpression(velocity);
5107 + break;
5108 + case 0x40: // Hold (sustain) pedal
5109 + //printDebug("Hold pedal set: %d", velocity);
5110 + parts[part]->setHoldPedal(velocity>=64);
5111 + break;
5113 + case 0x79: // Reset all controllers
5114 + //printDebug("Reset all controllers");
5115 + //FIXME: Check for accuracy against real thing
5116 + parts[part]->setVolume(100);
5117 + parts[part]->setExpression(127);
5118 + parts[part]->setPan(64);
5119 + parts[part]->setBend(0x2000);
5120 + parts[part]->setHoldPedal(false);
5121 + break;
5122 + case 0x7B: // All notes off
5123 + //printDebug("All notes off");
5124 + parts[part]->allNotesOff();
5125 + break;
5126 + default:
5127 + printDebug("Unknown MIDI Control code: 0x%02x - vel 0x%02x", note, velocity);
5128 + break;
5130 + break;
5131 + case 0xC: // Program change
5132 + //printDebug("Program change %01x", note);
5133 + parts[part]->setProgram(note);
5134 + break;
5135 + case 0xE: // Pitch bender
5136 + bend = (velocity << 7) | (note);
5137 + //printDebug("Pitch bender %02x", bend);
5138 + parts[part]->setBend(bend);
5139 + break;
5140 + default:
5141 + printDebug("Unknown Midi code: 0x%01x - %02x - %02x", code, note, velocity);
5142 + break;
5145 + //midiOutShortMsg(m_out, msg);
5148 +void Synth::playSysex(const Bit8u *sysex, Bit32u len) {
5149 + if (len < 2) {
5150 + printDebug("playSysex: Message is too short for sysex (%d bytes)", len);
5152 + if (sysex[0] != 0xF0) {
5153 + printDebug("playSysex: Message lacks start-of-sysex (0xF0)");
5154 + return;
5156 + // Due to some programs (e.g. Java) sending buffers with junk at the end, we have to go through and find the end marker rather than relying on len.
5157 + Bit32u endPos;
5158 + for (endPos = 1; endPos < len; endPos++)
5160 + if (sysex[endPos] == 0xF7)
5161 + break;
5163 + if (endPos == len) {
5164 + printDebug("playSysex: Message lacks end-of-sysex (0xf7)");
5165 + return;
5167 + playSysexWithoutFraming(sysex + 1, endPos - 1);
5170 +void Synth::playSysexWithoutFraming(const Bit8u *sysex, Bit32u len) {
5171 + if (len < 4) {
5172 + printDebug("playSysexWithoutFraming: Message is too short (%d bytes)!", len);
5173 + return;
5175 + if (sysex[0] != SYSEX_MANUFACTURER_ROLAND) {
5176 + printDebug("playSysexWithoutFraming: Header not intended for this device manufacturer: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]);
5177 + return;
5179 + if (sysex[2] == SYSEX_MDL_D50) {
5180 + printDebug("playSysexWithoutFraming: Header is intended for model D-50 (not yet supported): %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]);
5181 + return;
5183 + else if (sysex[2] != SYSEX_MDL_MT32) {
5184 + printDebug("playSysexWithoutFraming: Header not intended for model MT-32: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]);
5185 + return;
5187 + playSysexWithoutHeader(sysex[1], sysex[3], sysex + 4, len - 4);
5190 +void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len) {
5191 + if (device > 0x10) {
5192 + // We have device ID 0x10 (default, but changeable, on real MT-32), < 0x10 is for channels
5193 + printDebug("playSysexWithoutHeader: Message is not intended for this device ID (provided: %02x, expected: 0x10 or channel)", (int)device);
5194 + return;
5196 + if (len < 4) {
5197 + printDebug("playSysexWithoutHeader: Message is too short (%d bytes)!", len);
5198 + return;
5200 + unsigned char checksum = calcSysexChecksum(sysex, len - 1, 0);
5201 + if (checksum != sysex[len - 1]) {
5202 + printDebug("playSysexWithoutHeader: Message checksum is incorrect (provided: %02x, expected: %02x)!", sysex[len - 1], checksum);
5203 + return;
5205 + len -= 1; // Exclude checksum
5206 + switch (command) {
5207 + case SYSEX_CMD_DT1:
5208 + writeSysex(device, sysex, len);
5209 + break;
5210 + case SYSEX_CMD_RQ1:
5211 + readSysex(device, sysex, len);
5212 + break;
5213 + default:
5214 + printDebug("playSysexWithoutFraming: Unsupported command %02x", command);
5215 + return;
5219 +void Synth::readSysex(unsigned char /*device*/, const Bit8u * /*sysex*/, Bit32u /*len*/) {
5222 +const MemoryRegion memoryRegions[8] = {
5223 + {MR_PatchTemp, MT32EMU_MEMADDR(0x030000), sizeof(MemParams::PatchTemp), 9},
5224 + {MR_RhythmTemp, MT32EMU_MEMADDR(0x030110), sizeof(MemParams::RhythmTemp), 85},
5225 + {MR_TimbreTemp, MT32EMU_MEMADDR(0x040000), sizeof(TimbreParam), 8},
5226 + {MR_Patches, MT32EMU_MEMADDR(0x050000), sizeof(PatchParam), 128},
5227 + {MR_Timbres, MT32EMU_MEMADDR(0x080000), sizeof(MemParams::PaddedTimbre), 64 + 64 + 64 + 64},
5228 + {MR_System, MT32EMU_MEMADDR(0x100000), sizeof(MemParams::SystemArea), 1},
5229 + {MR_Display, MT32EMU_MEMADDR(0x200000), MAX_SYSEX_SIZE - 1, 1},
5230 + {MR_Reset, MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1}
5233 +const int NUM_REGIONS = sizeof(memoryRegions) / sizeof(MemoryRegion);
5235 +void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) {
5236 + Bit32u addr = (sysex[0] << 16) | (sysex[1] << 8) | (sysex[2]);
5237 + addr = MT32EMU_MEMADDR(addr);
5238 + sysex += 3;
5239 + len -= 3;
5240 + //printDebug("Sysex addr: 0x%06x", MT32EMU_SYSEXMEMADDR(addr));
5241 + // NOTE: Please keep both lower and upper bounds in each check, for ease of reading
5243 + // Process channel-specific sysex by converting it to device-global
5244 + if (device < 0x10) {
5245 + printDebug("WRITE-CHANNEL: Channel %d temp area 0x%06x", device, MT32EMU_SYSEXMEMADDR(addr));
5246 + if (/*addr >= MT32EMU_MEMADDR(0x000000) && */addr < MT32EMU_MEMADDR(0x010000)) {
5247 + int offset;
5248 + if (chantable[device] == -1) {
5249 + printDebug(" (Channel not mapped to a partial... 0 offset)");
5250 + offset = 0;
5251 + } else if (chantable[device] == 8) {
5252 + printDebug(" (Channel mapped to rhythm... 0 offset)");
5253 + offset = 0;
5254 + } else {
5255 + offset = chantable[device] * sizeof(MemParams::PatchTemp);
5256 + printDebug(" (Setting extra offset to %d)", offset);
5258 + addr += MT32EMU_MEMADDR(0x030000) + offset;
5259 + } else if (/*addr >= 0x010000 && */ addr < MT32EMU_MEMADDR(0x020000)) {
5260 + addr += MT32EMU_MEMADDR(0x030110) - MT32EMU_MEMADDR(0x010000);
5261 + } else if (/*addr >= 0x020000 && */ addr < MT32EMU_MEMADDR(0x030000)) {
5262 + int offset;
5263 + if (chantable[device] == -1) {
5264 + printDebug(" (Channel not mapped to a partial... 0 offset)");
5265 + offset = 0;
5266 + } else if (chantable[device] == 8) {
5267 + printDebug(" (Channel mapped to rhythm... 0 offset)");
5268 + offset = 0;
5269 + } else {
5270 + offset = chantable[device] * sizeof(TimbreParam);
5271 + printDebug(" (Setting extra offset to %d)", offset);
5273 + addr += MT32EMU_MEMADDR(0x040000) - MT32EMU_MEMADDR(0x020000) + offset;
5274 + } else {
5275 + printDebug("PlaySysexWithoutHeader: Invalid channel %d address 0x%06x", device, MT32EMU_SYSEXMEMADDR(addr));
5276 + return;
5280 + // Process device-global sysex (possibly converted from channel-specific sysex above)
5281 + for (;;) {
5282 + // Find the appropriate memory region
5283 + int regionNum;
5284 + const MemoryRegion *region = NULL; // Initialized to please compiler
5285 + for (regionNum = 0; regionNum < NUM_REGIONS; regionNum++) {
5286 + region = &memoryRegions[regionNum];
5287 + if (region->contains(addr)) {
5288 + writeMemoryRegion(region, addr, region->getClampedLen(addr, len), sysex);
5289 + break;
5292 + if (regionNum == NUM_REGIONS) {
5293 + printDebug("Sysex write to unrecognised address %06x, len %d", MT32EMU_SYSEXMEMADDR(addr), len);
5294 + break;
5296 + Bit32u next = region->next(addr, len);
5297 + if (next == 0) {
5298 + break;
5300 + addr += next;
5301 + sysex += next;
5302 + len -= next;
5306 +void Synth::readMemory(Bit32u addr, Bit32u len, Bit8u *data) {
5307 + int regionNum;
5308 + const MemoryRegion *region = NULL;
5309 + for (regionNum = 0; regionNum < NUM_REGIONS; regionNum++) {
5310 + region = &memoryRegions[regionNum];
5311 + if (region->contains(addr)) {
5312 + readMemoryRegion(region, addr, len, data);
5313 + break;
5318 +void Synth::readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, Bit8u *data) {
5319 + unsigned int first = region->firstTouched(addr);
5320 + //unsigned int last = region->lastTouched(addr, len);
5321 + unsigned int off = region->firstTouchedOffset(addr);
5322 + len = region->getClampedLen(addr, len);
5324 + unsigned int m;
5326 + switch (region->type) {
5327 + case MR_PatchTemp:
5328 + for (m = 0; m < len; m++)
5329 + data[m] = ((Bit8u *)&mt32ram.patchSettings[first])[off + m];
5330 + break;
5331 + case MR_RhythmTemp:
5332 + for (m = 0; m < len; m++)
5333 + data[m] = ((Bit8u *)&mt32ram.rhythmSettings[first])[off + m];
5334 + break;
5335 + case MR_TimbreTemp:
5336 + for (m = 0; m < len; m++)
5337 + data[m] = ((Bit8u *)&mt32ram.timbreSettings[first])[off + m];
5338 + break;
5339 + case MR_Patches:
5340 + for (m = 0; m < len; m++)
5341 + data[m] = ((Bit8u *)&mt32ram.patches[first])[off + m];
5342 + break;
5343 + case MR_Timbres:
5344 + for (m = 0; m < len; m++)
5345 + data[m] = ((Bit8u *)&mt32ram.timbres[first])[off + m];
5346 + break;
5347 + case MR_System:
5348 + for (m = 0; m < len; m++)
5349 + data[m] = ((Bit8u *)&mt32ram.system)[m + off];
5350 + break;
5351 + default:
5352 + for (m = 0; m < len; m += 2) {
5353 + data[m] = 0xff;
5354 + if (m + 1 < len) {
5355 + data[m+1] = (Bit8u)region->type;
5358 + // TODO: Don't care about the others ATM
5359 + break;
5364 +void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, const Bit8u *data) {
5365 + unsigned int first = region->firstTouched(addr);
5366 + unsigned int last = region->lastTouched(addr, len);
5367 + unsigned int off = region->firstTouchedOffset(addr);
5368 + switch (region->type) {
5369 + case MR_PatchTemp:
5370 + for (unsigned int m = 0; m < len; m++) {
5371 + ((Bit8u *)&mt32ram.patchSettings[first])[off + m] = data[m];
5373 + //printDebug("Patch temp: Patch %d, offset %x, len %d", off/16, off % 16, len);
5375 + for (unsigned int i = first; i <= last; i++) {
5376 + int absTimbreNum = mt32ram.patchSettings[i].patch.timbreGroup * 64 + mt32ram.patchSettings[i].patch.timbreNum;
5377 + char timbreName[11];
5378 + memcpy(timbreName, mt32ram.timbres[absTimbreNum].timbre.common.name, 10);
5379 + timbreName[10] = 0;
5380 + printDebug("WRITE-PARTPATCH (%d-%d@%d..%d): %d; timbre=%d (%s), outlevel=%d", first, last, off, off + len, i, absTimbreNum, timbreName, mt32ram.patchSettings[i].outlevel);
5381 + if (parts[i] != NULL) {
5382 + if (i != 8) {
5383 + // Note: Confirmed on CM-64 that we definitely *should* update the timbre here,
5384 + // but only in the case that the sysex actually writes to those values
5385 + if (i == first && off > 2) {
5386 + printDebug(" (Not updating timbre, since those values weren't touched)");
5387 + } else {
5388 + parts[i]->setTimbre(&mt32ram.timbres[parts[i]->getAbsTimbreNum()].timbre);
5391 + parts[i]->refresh();
5394 + break;
5395 + case MR_RhythmTemp:
5396 + for (unsigned int m = 0; m < len; m++)
5397 + ((Bit8u *)&mt32ram.rhythmSettings[first])[off + m] = data[m];
5398 + for (unsigned int i = first; i <= last; i++) {
5399 + int timbreNum = mt32ram.rhythmSettings[i].timbre;
5400 + char timbreName[11];
5401 + if (timbreNum < 94) {
5402 + memcpy(timbreName, mt32ram.timbres[128 + timbreNum].timbre.common.name, 10);
5403 + timbreName[10] = 0;
5404 + } else {
5405 + strcpy(timbreName, "[None]");
5407 + printDebug("WRITE-RHYTHM (%d-%d@%d..%d): %d; level=%02x, panpot=%02x, reverb=%02x, timbre=%d (%s)", first, last, off, off + len, i, mt32ram.rhythmSettings[i].outlevel, mt32ram.rhythmSettings[i].panpot, mt32ram.rhythmSettings[i].reverbSwitch, mt32ram.rhythmSettings[i].timbre, timbreName);
5409 + if (parts[8] != NULL) {
5410 + parts[8]->refresh();
5412 + break;
5413 + case MR_TimbreTemp:
5414 + for (unsigned int m = 0; m < len; m++)
5415 + ((Bit8u *)&mt32ram.timbreSettings[first])[off + m] = data[m];
5416 + for (unsigned int i = first; i <= last; i++) {
5417 + char instrumentName[11];
5418 + memcpy(instrumentName, mt32ram.timbreSettings[i].common.name, 10);
5419 + instrumentName[10] = 0;
5420 + printDebug("WRITE-PARTTIMBRE (%d-%d@%d..%d): timbre=%d (%s)", first, last, off, off + len, i, instrumentName);
5421 + if (parts[i] != NULL) {
5422 + parts[i]->refresh();
5425 + break;
5426 + case MR_Patches:
5427 + for (unsigned int m = 0; m < len; m++)
5428 + ((Bit8u *)&mt32ram.patches[first])[off + m] = data[m];
5429 + for (unsigned int i = first; i <= last; i++) {
5430 + PatchParam *patch = &mt32ram.patches[i];
5431 + int patchAbsTimbreNum = patch->timbreGroup * 64 + patch->timbreNum;
5432 + char instrumentName[11];
5433 + memcpy(instrumentName, mt32ram.timbres[patchAbsTimbreNum].timbre.common.name, 10);
5434 + instrumentName[10] = 0;
5435 + Bit8u *n = (Bit8u *)patch;
5436 + printDebug("WRITE-PATCH (%d-%d@%d..%d): %d; timbre=%d (%s) %02X%02X%02X%02X%02X%02X%02X%02X", first, last, off, off + len, i, patchAbsTimbreNum, instrumentName, n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7]);
5437 + // FIXME:KG: The below is definitely dodgy. We just guess that this is the patch that the part was using
5438 + // based on a timbre match (but many patches could have the same timbre!)
5439 + // If this refresh is really correct, we should store the patch number in use by each part.
5440 + /*
5441 + for (int part = 0; part < 8; part++) {
5442 + if (parts[part] != NULL) {
5443 + int partPatchAbsTimbreNum = mt32ram.patchSettings[part].patch.timbreGroup * 64 + mt32ram.patchSettings[part].patch.timbreNum;
5444 + if (parts[part]->getAbsTimbreNum() == patchAbsTimbreNum) {
5445 + parts[part]->setPatch(patch);
5446 + parts[part]->RefreshPatch();
5450 + */
5452 + break;
5453 + case MR_Timbres:
5454 + // Timbres
5455 + first += 128;
5456 + last += 128;
5457 + for (unsigned int m = 0; m < len; m++)
5458 + ((Bit8u *)&mt32ram.timbres[first])[off + m] = data[m];
5459 + for (unsigned int i = first; i <= last; i++) {
5460 + char instrumentName[11];
5461 + memcpy(instrumentName, mt32ram.timbres[i].timbre.common.name, 10);
5462 + instrumentName[10] = 0;
5463 + printDebug("WRITE-TIMBRE (%d-%d@%d..%d): %d; name=\"%s\"", first, last, off, off + len, i, instrumentName);
5464 + // FIXME:KG: Not sure if the stuff below should be done (for rhythm and/or parts)...
5465 + // Does the real MT-32 automatically do this?
5466 + for (unsigned int part = 0; part < 9; part++) {
5467 + if (parts[part] != NULL) {
5468 + parts[part]->refreshTimbre(i);
5472 + break;
5473 + case MR_System:
5474 + for (unsigned int m = 0; m < len; m++)
5475 + ((Bit8u *)&mt32ram.system)[m + off] = data[m];
5477 + report(ReportType_devReconfig, NULL);
5479 + printDebug("WRITE-SYSTEM:");
5480 + refreshSystem();
5481 + break;
5482 + case MR_Display:
5483 + char buf[MAX_SYSEX_SIZE];
5484 + memcpy(&buf, &data[0], len);
5485 + buf[len] = 0;
5486 + LOG_MSG("WRITE-LCD: %s", buf);
5487 + report(ReportType_lcdMessage, buf);
5488 + break;
5489 + case MR_Reset:
5490 + printDebug("RESET");
5491 + report(ReportType_devReset, NULL);
5492 + partialManager->deactivateAll();
5493 + mt32ram = mt32default;
5494 + for (int i = 0; i < 9; i++) {
5495 + parts[i]->refresh();
5497 + isEnabled = false;
5498 + break;
5502 +bool Synth::refreshSystem() {
5503 + memset(chantable, -1, sizeof(chantable));
5505 + for (unsigned int i = 0; i < 9; i++) {
5506 + //LOG(LOG_MISC|LOG_ERROR,"Part %d set to MIDI channel %d",i,mt32ram.system.chanAssign[i]);
5507 + if (mt32ram.system.chanAssign[i] == 16 && parts[i] != NULL) {
5508 + parts[i]->allSoundOff();
5509 + } else {
5510 + chantable[(int)mt32ram.system.chanAssign[i]] = (char)i;
5513 + //FIXME:KG: This is just an educated guess.
5514 + // The LAPC-I documentation claims a range of 427.5Hz-452.6Hz (similar to what we have here)
5515 + // The MT-32 documentation claims a range of 432.1Hz-457.6Hz
5516 + masterTune = 440.0f * powf(2.0f, (mt32ram.system.masterTune - 64.0f) / (128.0f * 12.0f));
5517 + printDebug(" Master Tune: %f", (double)masterTune);
5518 + printDebug(" Reverb: mode=%d, time=%d, level=%d", mt32ram.system.reverbMode, mt32ram.system.reverbTime, mt32ram.system.reverbLevel);
5519 + report(ReportType_newReverbMode, &mt32ram.system.reverbMode);
5520 + report(ReportType_newReverbTime, &mt32ram.system.reverbTime);
5521 + report(ReportType_newReverbLevel, &mt32ram.system.reverbLevel);
5523 + if (myProp.useDefaultReverb) {
5524 + initReverb(mt32ram.system.reverbMode, mt32ram.system.reverbTime, mt32ram.system.reverbLevel);
5525 + } else {
5526 + initReverb(myProp.reverbType, myProp.reverbTime, mt32ram.system.reverbLevel);
5529 + Bit8u *rset = mt32ram.system.reserveSettings;
5530 + printDebug(" Partial reserve: 1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]);
5531 + int pr = partialManager->setReserve(rset);
5532 + if (pr != 32)
5533 + printDebug(" (Partial Reserve Table with less than 32 partials reserved!)");
5534 + rset = mt32ram.system.chanAssign;
5535 + printDebug(" Part assign: 1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]);
5536 + printDebug(" Master volume: %d", mt32ram.system.masterVol);
5537 + masterVolume = (Bit16u)(mt32ram.system.masterVol * 32767 / 100);
5538 + if (!tables.init(this, pcmWaves, (float)myProp.sampleRate, masterTune)) {
5539 + report(ReportType_errorSampleRate, NULL);
5540 + return false;
5542 + return true;
5545 +bool Synth::dumpTimbre(File *file, const TimbreParam *timbre, Bit32u address) {
5546 + // Sysex header
5547 + if (!file->writeBit8u(0xF0))
5548 + return false;
5549 + if (!file->writeBit8u(0x41))
5550 + return false;
5551 + if (!file->writeBit8u(0x10))
5552 + return false;
5553 + if (!file->writeBit8u(0x16))
5554 + return false;
5555 + if (!file->writeBit8u(0x12))
5556 + return false;
5558 + char lsb = (char)(address & 0x7f);
5559 + char isb = (char)((address >> 7) & 0x7f);
5560 + char msb = (char)(((address >> 14) & 0x7f) | 0x08);
5562 + //Address
5563 + if (!file->writeBit8u(msb))
5564 + return false;
5565 + if (!file->writeBit8u(isb))
5566 + return false;
5567 + if (!file->writeBit8u(lsb))
5568 + return false;
5570 + //Data
5571 + if (file->write(timbre, 246) != 246)
5572 + return false;
5574 + //Checksum
5575 + unsigned char checksum = calcSysexChecksum((const Bit8u *)timbre, 246, msb + isb + lsb);
5576 + if (!file->writeBit8u(checksum))
5577 + return false;
5579 + //End of sysex
5580 + if (!file->writeBit8u(0xF7))
5581 + return false;
5582 + return true;
5585 +int Synth::dumpTimbres(const char *filename, int start, int len) {
5586 + File *file = openFile(filename, File::OpenMode_write);
5587 + if (file == NULL)
5588 + return -1;
5590 + for (int timbreNum = start; timbreNum < start + len; timbreNum++) {
5591 + int useaddr = (timbreNum - start) * 256;
5592 + TimbreParam *timbre = &mt32ram.timbres[timbreNum].timbre;
5593 + if (!dumpTimbre(file, timbre, useaddr))
5594 + break;
5596 + closeFile(file);
5597 + return 0;
5600 +void ProduceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume) {
5601 +#if MT32EMU_USE_MMX > 2
5602 + //FIXME:KG: This appears to introduce crackle
5603 + int donelen = i386_produceOutput1(useBuf, stream, len, volume);
5604 + len -= donelen;
5605 + stream += donelen * 2;
5606 + useBuf += donelen * 2;
5607 +#endif
5608 + int end = len * 2;
5609 + while (end--) {
5610 + *stream = *stream + (Bit16s)(((Bit32s)*useBuf++ * (Bit32s)volume)>>15);
5611 + stream++;
5615 +void Synth::render(Bit16s *stream, Bit32u len) {
5616 + memset(stream, 0, len * sizeof (Bit16s) * 2);
5617 + if (!isEnabled)
5618 + return;
5619 + while (len > 0) {
5620 + Bit32u thisLen = len > MAX_SAMPLE_OUTPUT ? MAX_SAMPLE_OUTPUT : len;
5621 + doRender(stream, thisLen);
5622 + len -= thisLen;
5623 + stream += 2 * thisLen;
5627 +void Synth::doRender(Bit16s *stream, Bit32u len) {
5628 + partialManager->ageAll();
5630 + if (myProp.useReverb) {
5631 + for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
5632 + if (partialManager->shouldReverb(i)) {
5633 + if (partialManager->produceOutput(i, &tmpBuffer[0], len)) {
5634 + ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume);
5638 + Bit32u m = 0;
5639 + for (unsigned int i = 0; i < len; i++) {
5640 + sndbufl[i] = (float)stream[m] / 32767.0f;
5641 + m++;
5642 + sndbufr[i] = (float)stream[m] / 32767.0f;
5643 + m++;
5645 + reverbModel->processreplace(sndbufl, sndbufr, outbufl, outbufr, len, 1);
5646 + m=0;
5647 + for (unsigned int i = 0; i < len; i++) {
5648 + stream[m] = (Bit16s)(outbufl[i] * 32767.0f);
5649 + m++;
5650 + stream[m] = (Bit16s)(outbufr[i] * 32767.0f);
5651 + m++;
5653 + for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
5654 + if (!partialManager->shouldReverb(i)) {
5655 + if (partialManager->produceOutput(i, &tmpBuffer[0], len)) {
5656 + ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume);
5660 + } else {
5661 + for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
5662 + if (partialManager->produceOutput(i, &tmpBuffer[0], len))
5663 + ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume);
5667 + partialManager->clearAlreadyOutputed();
5669 +#if MT32EMU_MONITOR_PARTIALS == 1
5670 + samplepos += len;
5671 + if (samplepos > myProp.SampleRate * 5) {
5672 + samplepos = 0;
5673 + int partialUsage[9];
5674 + partialManager->GetPerPartPartialUsage(partialUsage);
5675 + printDebug("1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d", partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7]);
5676 + printDebug("Rhythm: %02d TOTAL: %02d", partialUsage[8], MT32EMU_MAX_PARTIALS - partialManager->GetFreePartialCount());
5678 +#endif
5681 +const Partial *Synth::getPartial(unsigned int partialNum) const {
5682 + return partialManager->getPartial(partialNum);
5685 +const Part *Synth::getPart(unsigned int partNum) const {
5686 + if (partNum > 8)
5687 + return NULL;
5688 + return parts[partNum];
5692 diff -urN dosbox.orig/src/gui/synth.h dosbox/src/gui/synth.h
5693 --- dosbox.orig/src/gui/synth.h 1969-12-31 21:00:00.000000000 -0300
5694 +++ dosbox/src/gui/synth.h 2011-01-11 20:15:44.314832104 -0300
5695 @@ -0,0 +1,299 @@
5696 +/* Copyright (c) 2003-2005 Various contributors
5698 + * Permission is hereby granted, free of charge, to any person obtaining a copy
5699 + * of this software and associated documentation files (the "Software"), to
5700 + * deal in the Software without restriction, including without limitation the
5701 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
5702 + * sell copies of the Software, and to permit persons to whom the Software is
5703 + * furnished to do so, subject to the following conditions:
5705 + * The above copyright notice and this permission notice shall be included in
5706 + * all copies or substantial portions of the Software.
5708 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5709 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5710 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
5711 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
5712 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
5713 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
5714 + * IN THE SOFTWARE.
5715 + */
5717 +#ifndef MT32EMU_SYNTH_H
5718 +#define MT32EMU_SYNTH_H
5720 +#include "stdarg.h"
5722 +class revmodel;
5724 +namespace MT32Emu {
5726 +class File;
5727 +class Partial;
5728 +class PartialManager;
5729 +class Part;
5731 +enum ReportType {
5732 + // Errors
5733 + ReportType_errorControlROM = 1,
5734 + ReportType_errorPCMROM,
5735 + ReportType_errorSampleRate,
5737 + // Progress
5738 + ReportType_progressInit,
5740 + // HW spec
5741 + ReportType_availableSSE,
5742 + ReportType_available3DNow,
5743 + ReportType_usingSSE,
5744 + ReportType_using3DNow,
5746 + // General info
5747 + ReportType_lcdMessage,
5748 + ReportType_devReset,
5749 + ReportType_devReconfig,
5750 + ReportType_newReverbMode,
5751 + ReportType_newReverbTime,
5752 + ReportType_newReverbLevel
5755 +struct SynthProperties {
5756 + // Sample rate to use in mixing
5757 + int sampleRate;
5759 + // Flag to activate reverb. True = use reverb, False = no reverb
5760 + bool useReverb;
5761 + // True to use software set reverb settings, False to set reverb settings in
5762 + // following parameters
5763 + bool useDefaultReverb;
5764 + // When not using the default settings, this specifies one of the 4 reverb types
5765 + // 1 = Room 2 = Hall 3 = Plate 4 = Tap
5766 + unsigned char reverbType;
5767 + // This specifies the delay time, from 0-7 (not sure of the actual MT-32's measurement)
5768 + unsigned char reverbTime;
5769 + // This specifies the reverb level, from 0-7 (not sure of the actual MT-32's measurement)
5770 + unsigned char reverbLevel;
5771 + // The name of the directory in which the ROM and data files are stored (with trailing slash/backslash)
5772 + // Not used if "openFile" is set. May be NULL in any case.
5773 + char *baseDir;
5774 + // This is used as the first argument to all callbacks
5775 + void *userData;
5776 + // Callback for reporting various errors and information. May be NULL
5777 + int (*report)(void *userData, ReportType type, const void *reportData);
5778 + // Callback for debug messages, in vprintf() format
5779 + void (*printDebug)(void *userData, const char *fmt, va_list list);
5780 + // Callback for providing an implementation of File, opened and ready for use
5781 + // May be NULL, in which case a default implementation will be used.
5782 + File *(*openFile)(void *userData, const char *filename, File::OpenMode mode);
5783 + // Callback for closing a File. May be NULL, in which case the File will automatically be close()d/deleted.
5784 + void (*closeFile)(void *userData, File *file);
5787 +// This is the specification of the Callback routine used when calling the RecalcWaveforms
5788 +// function
5789 +typedef void (*recalcStatusCallback)(int percDone);
5791 +// This external function recreates the base waveform file (waveforms.raw) using a specifed
5792 +// sampling rate. The callback routine provides interactivity to let the user know what
5793 +// percentage is complete in regenerating the waveforms. When a NULL pointer is used as the
5794 +// callback routine, no status is reported.
5795 +bool RecalcWaveforms(char * baseDir, int sampRate, recalcStatusCallback callBack);
5797 +typedef float (*iir_filter_type)(float input,float *hist1_ptr, float *coef_ptr);
5799 +const Bit8u SYSEX_MANUFACTURER_ROLAND = 0x41;
5801 +const Bit8u SYSEX_MDL_MT32 = 0x16;
5802 +const Bit8u SYSEX_MDL_D50 = 0x14;
5804 +const Bit8u SYSEX_CMD_RQ1 = 0x11; // Request data #1
5805 +const Bit8u SYSEX_CMD_DT1 = 0x12; // Data set 1
5806 +const Bit8u SYSEX_CMD_WSD = 0x40; // Want to send data
5807 +const Bit8u SYSEX_CMD_RQD = 0x41; // Request data
5808 +const Bit8u SYSEX_CMD_DAT = 0x42; // Data set
5809 +const Bit8u SYSEX_CMD_ACK = 0x43; // Acknowledge
5810 +const Bit8u SYSEX_CMD_EOD = 0x45; // End of data
5811 +const Bit8u SYSEX_CMD_ERR = 0x4E; // Communications error
5812 +const Bit8u SYSEX_CMD_RJC = 0x4F; // Rejection
5814 +const unsigned int CONTROL_ROM_SIZE = 64 * 1024;
5816 +struct ControlROMPCMStruct
5818 + Bit8u pos;
5819 + Bit8u len;
5820 + Bit8u pitchLSB;
5821 + Bit8u pitchMSB;
5824 +struct ControlROMMap {
5825 + Bit16u idPos;
5826 + Bit16u idLen;
5827 + const char *idBytes;
5828 + Bit16u pcmTable;
5829 + Bit16u pcmCount;
5830 + Bit16u timbreAMap;
5831 + Bit16u timbreAOffset;
5832 + Bit16u timbreBMap;
5833 + Bit16u timbreBOffset;
5834 + Bit16u timbreRMap;
5835 + Bit16u timbreRCount;
5836 + Bit16u rhythmSettings;
5837 + Bit16u rhythmSettingsCount;
5838 + Bit16u reserveSettings;
5839 + Bit16u panSettings;
5840 + Bit16u programSettings;
5843 +enum MemoryRegionType {
5844 + MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset
5847 +class MemoryRegion {
5848 +public:
5849 + MemoryRegionType type;
5850 + Bit32u startAddr, entrySize, entries;
5852 + int lastTouched(Bit32u addr, Bit32u len) const {
5853 + return (offset(addr) + len - 1) / entrySize;
5855 + int firstTouchedOffset(Bit32u addr) const {
5856 + return offset(addr) % entrySize;
5858 + int firstTouched(Bit32u addr) const {
5859 + return offset(addr) / entrySize;
5861 + Bit32u regionEnd() const {
5862 + return startAddr + entrySize * entries;
5864 + bool contains(Bit32u addr) const {
5865 + return addr >= startAddr && addr < regionEnd();
5867 + int offset(Bit32u addr) const {
5868 + return addr - startAddr;
5870 + Bit32u getClampedLen(Bit32u addr, Bit32u len) const {
5871 + if (addr + len > regionEnd())
5872 + return regionEnd() - addr;
5873 + return len;
5875 + Bit32u next(Bit32u addr, Bit32u len) const {
5876 + if (addr + len > regionEnd()) {
5877 + return regionEnd() - addr;
5879 + return 0;
5884 +class Synth {
5885 +friend class Part;
5886 +friend class RhythmPart;
5887 +friend class Partial;
5888 +friend class Tables;
5889 +private:
5890 + bool isEnabled;
5892 + iir_filter_type iirFilter;
5894 + PCMWaveEntry *pcmWaves; // Array
5896 + const ControlROMMap *controlROMMap;
5897 + Bit8u controlROMData[CONTROL_ROM_SIZE];
5898 + Bit16s *pcmROMData;
5899 + int pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM
5901 + Bit8s chantable[32];
5903 + #if MT32EMU_MONITOR_PARTIALS == 1
5904 + static Bit32s samplepos = 0;
5905 + #endif
5907 + Tables tables;
5909 + MemParams mt32ram, mt32default;
5911 + revmodel *reverbModel;
5913 + float masterTune;
5914 + Bit16u masterVolume;
5916 + bool isOpen;
5918 + PartialManager *partialManager;
5919 + Part *parts[9];
5921 + Bit16s tmpBuffer[MAX_SAMPLE_OUTPUT * 2];
5922 + float sndbufl[MAX_SAMPLE_OUTPUT];
5923 + float sndbufr[MAX_SAMPLE_OUTPUT];
5924 + float outbufl[MAX_SAMPLE_OUTPUT];
5925 + float outbufr[MAX_SAMPLE_OUTPUT];
5927 + SynthProperties myProp;
5929 + bool loadPreset(File *file);
5930 + void initReverb(Bit8u newRevMode, Bit8u newRevTime, Bit8u newRevLevel);
5931 + void doRender(Bit16s * stream, Bit32u len);
5933 + void playAddressedSysex(unsigned char channel, const Bit8u *sysex, Bit32u len);
5934 + void readSysex(unsigned char channel, const Bit8u *sysex, Bit32u len);
5935 + void writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, const Bit8u *data);
5936 + void readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, Bit8u *data);
5938 + bool loadControlROM(const char *filename);
5939 + bool loadPCMROM(const char *filename);
5940 + bool dumpTimbre(File *file, const TimbreParam *timbre, Bit32u addr);
5941 + int dumpTimbres(const char *filename, int start, int len);
5943 + bool initPCMList(Bit16u mapAddress, Bit16u count);
5944 + bool initRhythmTimbres(Bit16u mapAddress, Bit16u count);
5945 + bool initTimbres(Bit16u mapAddress, Bit16u offset, int startTimbre);
5946 + bool initRhythmTimbre(int drumNum, const Bit8u *mem, unsigned int memLen);
5947 + bool refreshSystem();
5949 +protected:
5950 + int report(ReportType type, const void *reportData);
5951 + File *openFile(const char *filename, File::OpenMode mode);
5952 + void closeFile(File *file);
5953 + void printDebug(const char *fmt, ...); // GCC_PRINTF(2, 3);
5955 +public:
5956 + static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum);
5958 + Synth();
5959 + ~Synth();
5961 + // Used to initialize the MT-32. Must be called before any other function.
5962 + // Returns true if initialization was sucessful, otherwise returns false.
5963 + bool open(SynthProperties &useProp);
5965 + // Closes the MT-32 and deallocates any memory used by the synthesizer
5966 + void close(void);
5968 + // Sends a 4-byte MIDI message to the MT-32 for immediate playback
5969 + void playMsg(Bit32u msg);
5970 + void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity);
5972 + // Sends a string of Sysex commands to the MT-32 for immediate interpretation
5973 + // The length is in bytes
5974 + void playSysex(const Bit8u *sysex, Bit32u len);
5975 + void playSysexWithoutFraming(const Bit8u *sysex, Bit32u len);
5976 + void playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len);
5977 + void writeSysex(unsigned char channel, const Bit8u *sysex, Bit32u len);
5979 + // This callback routine is used to have the MT-32 generate samples to the specified
5980 + // output stream. The length is in whole samples, not bytes. (I.E. in 16-bit stereo,
5981 + // one sample is 4 bytes)
5982 + void render(Bit16s * stream, Bit32u len);
5984 + const Partial *getPartial(unsigned int partialNum) const;
5986 + void readMemory(Bit32u addr, Bit32u len, Bit8u *data);
5988 + // partNum should be 0..7 for Part 1..8, or 8 for Rhythm
5989 + const Part *getPart(unsigned int partNum) const;
5994 +#endif
5995 diff -urN dosbox.orig/src/gui/tables.cpp dosbox/src/gui/tables.cpp
5996 --- dosbox.orig/src/gui/tables.cpp 1969-12-31 21:00:00.000000000 -0300
5997 +++ dosbox/src/gui/tables.cpp 2011-01-11 20:15:44.318165437 -0300
5998 @@ -0,0 +1,757 @@
5999 +/* Copyright (c) 2003-2005 Various contributors
6001 + * Permission is hereby granted, free of charge, to any person obtaining a copy
6002 + * of this software and associated documentation files (the "Software"), to
6003 + * deal in the Software without restriction, including without limitation the
6004 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
6005 + * sell copies of the Software, and to permit persons to whom the Software is
6006 + * furnished to do so, subject to the following conditions:
6008 + * The above copyright notice and this permission notice shall be included in
6009 + * all copies or substantial portions of the Software.
6011 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6012 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6013 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6014 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
6015 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
6016 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
6017 + * IN THE SOFTWARE.
6018 + */
6020 +#include <stdlib.h>
6021 +#include <string.h>
6022 +#include <math.h>
6024 +#include "mt32emu.h"
6026 +#if defined(MACOSX) || defined(SOLARIS) || defined(__MINGW32__)
6027 +// Older versions of Mac OS X didn't supply a powf function, so using it
6028 +// will cause a binary incompatibility when trying to run a binary built
6029 +// on a newer OS X release on an older one. And Solaris 8 doesn't provide
6030 +// powf, floorf, fabsf etc. at all.
6031 +// Cross-compiled MinGW32 toolchains suffer from a cross-compile bug in
6032 +// libstdc++. math/stubs.o should be empty, but it comes with a symbol for
6033 +// powf, resulting in a linker error because of multiple definitions.
6034 +// Hence we re-define them here. The only potential drawback is that it
6035 +// might be a little bit slower this way.
6036 +#define powf(x,y) ((float)pow(x,y))
6037 +#define floorf(x) ((float)floor(x))
6038 +#define fabsf(x) ((float)fabs(x))
6039 +#endif
6041 +#define FIXEDPOINT_MAKE(x, point) ((Bit32u)((1 << point) * x))
6043 +namespace MT32Emu {
6045 +//Amplitude time velocity follow exponential coefficients
6046 +static const double tvcatconst[5] = {0.0, 0.002791309, 0.005942882, 0.012652792, 0.026938637};
6047 +static const double tvcatmult[5] = {1.0, 1.072662811, 1.169129367, 1.288579123, 1.229630539};
6049 +// These are division constants for the TVF depth key follow
6050 +static const Bit32u depexp[5] = {3000, 950, 485, 255, 138};
6052 +//Envelope time keyfollow exponential coefficients
6053 +static const double tkcatconst[5] = {0.0, 0.005853144, 0.011148054, 0.019086143, 0.043333215};
6054 +static const double tkcatmult[5] = {1.0, 1.058245688, 1.048488989, 1.016049301, 1.097538067};
6056 +// Begin filter stuff
6058 +// Pre-warp the coefficients of a numerator or denominator.
6059 +// Note that a0 is assumed to be 1, so there is no wrapping
6060 +// of it.
6061 +static void prewarp(double *a1, double *a2, double fc, double fs) {
6062 + double wp;
6064 + wp = 2.0 * fs * tan(DOUBLE_PI * fc / fs);
6066 + *a2 = *a2 / (wp * wp);
6067 + *a1 = *a1 / wp;
6070 +// Transform the numerator and denominator coefficients
6071 +// of s-domain biquad section into corresponding
6072 +// z-domain coefficients.
6074 +// Store the 4 IIR coefficients in array pointed by coef
6075 +// in following order:
6076 +// beta1, beta2 (denominator)
6077 +// alpha1, alpha2 (numerator)
6079 +// Arguments:
6080 +// a0-a2 - s-domain numerator coefficients
6081 +// b0-b2 - s-domain denominator coefficients
6082 +// k - filter gain factor. initially set to 1
6083 +// and modified by each biquad section in such
6084 +// a way, as to make it the coefficient by
6085 +// which to multiply the overall filter gain
6086 +// in order to achieve a desired overall filter gain,
6087 +// specified in initial value of k.
6088 +// fs - sampling rate (Hz)
6089 +// coef - array of z-domain coefficients to be filled in.
6091 +// Return:
6092 +// On return, set coef z-domain coefficients
6093 +static void bilinear(double a0, double a1, double a2, double b0, double b1, double b2, double *k, double fs, float *coef) {
6094 + double ad, bd;
6096 + // alpha (Numerator in s-domain)
6097 + ad = 4. * a2 * fs * fs + 2. * a1 * fs + a0;
6098 + // beta (Denominator in s-domain)
6099 + bd = 4. * b2 * fs * fs + 2. * b1* fs + b0;
6101 + // update gain constant for this section
6102 + *k *= ad/bd;
6104 + // Denominator
6105 + *coef++ = (float)((2. * b0 - 8. * b2 * fs * fs) / bd); // beta1
6106 + *coef++ = (float)((4. * b2 * fs * fs - 2. * b1 * fs + b0) / bd); // beta2
6108 + // Nominator
6109 + *coef++ = (float)((2. * a0 - 8. * a2 * fs * fs) / ad); // alpha1
6110 + *coef = (float)((4. * a2 * fs * fs - 2. * a1 * fs + a0) / ad); // alpha2
6113 +// a0-a2: numerator coefficients
6114 +// b0-b2: denominator coefficients
6115 +// fc: Filter cutoff frequency
6116 +// fs: sampling rate
6117 +// k: overall gain factor
6118 +// coef: pointer to 4 iir coefficients
6119 +static void szxform(double *a0, double *a1, double *a2, double *b0, double *b1, double *b2, double fc, double fs, double *k, float *coef) {
6120 + // Calculate a1 and a2 and overwrite the original values
6121 + prewarp(a1, a2, fc, fs);
6122 + prewarp(b1, b2, fc, fs);
6123 + bilinear(*a0, *a1, *a2, *b0, *b1, *b2, k, fs, coef);
6126 +static void initFilter(float fs, float fc, float *icoeff, float Q) {
6127 + float *coef;
6128 + double a0, a1, a2, b0, b1, b2;
6130 + double k = 1.5; // Set overall filter gain factor
6131 + coef = icoeff + 1; // Skip k, or gain
6133 + // Section 1
6134 + a0 = 1.0;
6135 + a1 = 0;
6136 + a2 = 0;
6137 + b0 = 1.0;
6138 + b1 = 0.765367 / Q; // Divide by resonance or Q
6139 + b2 = 1.0;
6140 + szxform(&a0, &a1, &a2, &b0, &b1, &b2, fc, fs, &k, coef);
6141 + coef += 4; // Point to next filter section
6143 + // Section 2
6144 + a0 = 1.0;
6145 + a1 = 0;
6146 + a2 = 0;
6147 + b0 = 1.0;
6148 + b1 = 1.847759 / Q;
6149 + b2 = 1.0;
6150 + szxform(&a0, &a1, &a2, &b0, &b1, &b2, fc, fs, &k, coef);
6152 + icoeff[0] = (float)k;
6155 +void Tables::initFiltCoeff(float samplerate) {
6156 + for (int j = 0; j < FILTERGRAN; j++) {
6157 + for (int res = 0; res < 31; res++) {
6158 + float tres = resonanceFactor[res];
6159 + initFilter((float)samplerate, (((float)(j+1.0)/FILTERGRAN)) * ((float)samplerate/2), filtCoeff[j][res], tres);
6164 +void Tables::initEnvelopes(float samplerate) {
6165 + for (int lf = 0; lf <= 100; lf++) {
6166 + float elf = (float)lf;
6168 + // General envelope
6169 + // This formula fits observation of the CM-32L by +/- 0.03s or so for the second time value in the filter,
6170 + // when all other times were 0 and all levels were 100. Note that variations occur depending on the level
6171 + // delta of the section, which we're not fully emulating.
6172 + float seconds = powf(2.0f, (elf / 8.0f) + 7.0f) / 32768.0f;
6173 + int samples = (int)(seconds * samplerate);
6174 + envTime[lf] = samples;
6176 + // Cap on envelope times depending on the level delta
6177 + if (elf == 0) {
6178 + envDeltaMaxTime[lf] = 63;
6179 + } else {
6180 + float cap = 11.0f * (float)log(elf) + 64;
6181 + if (cap > 100.0f) {
6182 + cap = 100.0f;
6184 + envDeltaMaxTime[lf] = (int)cap;
6188 + // This (approximately) represents the time durations when the target level is 0.
6189 + // Not sure why this is a special case, but it's seen to be from the real thing.
6190 + seconds = powf(2, (elf / 8.0f) + 6) / 32768.0f;
6191 + envDecayTime[lf] = (int)(seconds * samplerate);
6193 + // I am certain of this: Verified by hand LFO log
6194 + lfoPeriod[lf] = (Bit32u)(((float)samplerate) / (powf(1.088883372f, (float)lf) * 0.021236044f));
6198 +void Tables::initMT32ConstantTables(Synth *synth) {
6199 + int lf;
6200 + synth->printDebug("Initializing Pitch Tables");
6201 + for (lf = -108; lf <= 108; lf++) {
6202 + tvfKeyfollowMult[lf + 108] = (int)(256 * powf(2.0f, (float)(lf / 24.0f)));
6203 + //synth->printDebug("KT %d = %d", f, keytable[f+108]);
6206 + for (int res = 0; res < 31; res++) {
6207 + resonanceFactor[res] = powf((float)res / 30.0f, 5.0f) + 1.0f;
6210 + int period = 65536;
6212 + for (int ang = 0; ang < period; ang++) {
6213 + int halfang = (period / 2);
6214 + int angval = ang % halfang;
6215 + float tval = (((float)angval / (float)halfang) - 0.5f) * 2;
6216 + if (ang >= halfang)
6217 + tval = -tval;
6218 + sintable[ang] = (Bit16s)(tval * 50.0f) + 50;
6221 + int velt, dep;
6222 + float tempdep;
6223 + for (velt = 0; velt < 128; velt++) {
6224 + for (dep = 0; dep < 5; dep++) {
6225 + if (dep > 0) {
6226 + float ff = (float)(exp(3.5f * tvcatconst[dep] * (59.0f - (float)velt)) * tvcatmult[dep]);
6227 + tempdep = 256.0f * ff;
6228 + envTimeVelfollowMult[dep][velt] = (int)tempdep;
6229 + //if ((velt % 16) == 0) {
6230 + // synth->printDebug("Key %d, depth %d, factor %d", velt, dep, (int)tempdep);
6231 + //}
6232 + } else
6233 + envTimeVelfollowMult[dep][velt] = 256;
6236 + for (dep = -7; dep < 8; dep++) {
6237 + float fldep = (float)abs(dep) / 7.0f;
6238 + fldep = powf(fldep,2.5f);
6239 + if (dep < 0)
6240 + fldep = fldep * -1.0f;
6241 + pwVelfollowAdd[dep+7][velt] = Bit32s((fldep * (float)velt * 100) / 128.0);
6245 + for (dep = 0; dep <= 100; dep++) {
6246 + for (velt = 0; velt < 128; velt++) {
6247 + float fdep = (float)dep * 0.000347013f; // Another MT-32 constant
6248 + float fv = ((float)velt - 64.0f)/7.26f;
6249 + float flogdep = powf(10, fdep * fv);
6250 + float fbase;
6252 + if (velt > 64)
6253 + synth->tables.tvfVelfollowMult[velt][dep] = (int)(flogdep * 256.0);
6254 + else {
6255 + //lff = 1 - (pow(((128.0 - (float)lf) / 64.0),.25) * ((float)velt / 96));
6256 + fbase = 1 - (powf(((float)dep / 100.0f),.25f) * ((float)(64-velt) / 96.0f));
6257 + synth->tables.tvfVelfollowMult[velt][dep] = (int)(fbase * 256.0);
6259 + //synth->printDebug("Filvel dep %d velt %d = %x", dep, velt, filveltable[velt][dep]);
6263 + for (lf = 0; lf < 128; lf++) {
6264 + float veloFract = lf / 127.0f;
6265 + for (int velsens = 0; velsens <= 100; velsens++) {
6266 + float sensFract = (velsens - 50) / 50.0f;
6267 + if (velsens < 50) {
6268 + tvaVelfollowMult[lf][velsens] = FIXEDPOINT_MAKE(1.0f / powf(2.0f, veloFract * -sensFract * 127.0f / 20.0f), 8);
6269 + } else {
6270 + tvaVelfollowMult[lf][velsens] = FIXEDPOINT_MAKE(1.0f / powf(2.0f, (1.0f - veloFract) * sensFract * 127.0f / 20.0f), 8);
6275 + for (lf = 0; lf <= 100; lf++) {
6276 + // Converts the 0-100 range used by the MT-32 to volume multiplier
6277 + volumeMult[lf] = FIXEDPOINT_MAKE(powf((float)lf / 100.0f, FLOAT_LN), 7);
6280 + for (lf = 0; lf <= 100; lf++) {
6281 + float mv = lf / 100.0f;
6282 + float pt = mv - 0.5f;
6283 + if (pt < 0)
6284 + pt = 0;
6286 + // Original (CC version)
6287 + //pwFactor[lf] = (int)(pt * 210.04f) + 128;
6289 + // Approximation from sample comparison
6290 + pwFactor[lf] = (int)(pt * 179.0f) + 128;
6293 + for (unsigned int i = 0; i < MAX_SAMPLE_OUTPUT; i++) {
6294 + int myRand;
6295 + myRand = rand();
6296 + //myRand = ((myRand - 16383) * 7168) >> 16;
6297 + // This one is slower but works with all values of RAND_MAX
6298 + myRand = (int)((myRand - RAND_MAX / 2) / (float)RAND_MAX * (7168 / 2));
6299 + //FIXME:KG: Original ultimately set the lowest two bits to 0, for no obvious reason
6300 + noiseBuf[i] = (Bit16s)myRand;
6303 + float tdist;
6304 + float padjtable[51];
6305 + for (lf = 0; lf <= 50; lf++) {
6306 + if (lf == 0)
6307 + padjtable[lf] = 7;
6308 + else if (lf == 1)
6309 + padjtable[lf] = 6;
6310 + else if (lf == 2)
6311 + padjtable[lf] = 5;
6312 + else if (lf == 3)
6313 + padjtable[lf] = 4;
6314 + else if (lf == 4)
6315 + padjtable[lf] = 4 - (0.333333f);
6316 + else if (lf == 5)
6317 + padjtable[lf] = 4 - (0.333333f * 2);
6318 + else if (lf == 6)
6319 + padjtable[lf] = 3;
6320 + else if ((lf > 6) && (lf <= 12)) {
6321 + tdist = (lf-6.0f) / 6.0f;
6322 + padjtable[lf] = 3.0f - tdist;
6323 + } else if ((lf > 12) && (lf <= 25)) {
6324 + tdist = (lf - 12.0f) / 13.0f;
6325 + padjtable[lf] = 2.0f - tdist;
6326 + } else {
6327 + tdist = (lf - 25.0f) / 25.0f;
6328 + padjtable[lf] = 1.0f - tdist;
6330 + //synth->printDebug("lf %d = padj %f", lf, (double)padjtable[lf]);
6333 + float lfp, depf, finalval, tlf;
6334 + int depat, pval, depti;
6335 + for (lf = 0; lf <= 10; lf++) {
6336 + // I believe the depth is cubed or something
6338 + for (depat = 0; depat <= 100; depat++) {
6339 + if (lf > 0) {
6340 + depti = abs(depat - 50);
6341 + tlf = (float)lf - padjtable[depti];
6342 + if (tlf < 0)
6343 + tlf = 0;
6344 + lfp = (float)exp(0.713619942f * tlf) / 407.4945111f;
6346 + if (depat < 50)
6347 + finalval = 4096.0f * powf(2, -lfp);
6348 + else
6349 + finalval = 4096.0f * powf(2, lfp);
6350 + pval = (int)finalval;
6352 + pitchEnvVal[lf][depat] = pval;
6353 + //synth->printDebug("lf %d depat %d pval %d tlf %f lfp %f", lf,depat,pval, (double)tlf, (double)lfp);
6354 + } else {
6355 + pitchEnvVal[lf][depat] = 4096;
6356 + //synth->printDebug("lf %d depat %d pval 4096", lf, depat);
6360 + for (lf = 0; lf <= 100; lf++) {
6361 + // It's linear - verified on MT-32 - one of the few things linear
6362 + lfp = ((float)lf * 0.1904f) / 310.55f;
6364 + for (depat = 0; depat <= 100; depat++) {
6365 + depf = ((float)depat - 50.0f) / 50.0f;
6366 + //finalval = pow(2, lfp * depf * .5);
6367 + finalval = 4096.0f + (4096.0f * lfp * depf);
6369 + pval = (int)finalval;
6371 + lfoShift[lf][depat] = pval;
6373 + //synth->printDebug("lf %d depat %d pval %x", lf,depat,pval);
6377 + for (lf = 0; lf <= 12; lf++) {
6378 + for (int distval = 0; distval < 128; distval++) {
6379 + float amplog, dval;
6380 + if (lf == 0) {
6381 + amplog = 0;
6382 + dval = 1;
6383 + tvaBiasMult[lf][distval] = 256;
6384 + } else {
6385 + /*
6386 + amplog = powf(1.431817011f, (float)lf) / FLOAT_PI;
6387 + dval = ((128.0f - (float)distval) / 128.0f);
6388 + amplog = exp(amplog);
6389 + dval = powf(amplog, dval) / amplog;
6390 + tvaBiasMult[lf][distval] = (int)(dval * 256.0);
6391 + */
6392 + // Lets assume for a second it's linear
6394 + // Distance of full volume reduction
6395 + amplog = (float)(12.0f / (float)lf) * 24.0f;
6396 + if (distval > amplog) {
6397 + tvaBiasMult[lf][distval] = 0;
6398 + } else {
6399 + dval = (amplog - (float)distval) / amplog;
6400 + tvaBiasMult[lf][distval] = (int)(dval * 256.0f);
6403 + //synth->printDebug("Ampbias lf %d distval %d = %f (%x) %f", lf, distval, (double)dval, tvaBiasMult[lf][distval],(double)amplog);
6407 + for (lf = 0; lf <= 14; lf++) {
6408 + for (int distval = 0; distval < 128; distval++) {
6409 + float filval = fabsf((float)((lf - 7) * 12) / 7.0f);
6410 + float amplog, dval;
6411 + if (lf == 7) {
6412 + amplog = 0;
6413 + dval = 1;
6414 + tvfBiasMult[lf][distval] = 256;
6415 + } else {
6416 + //amplog = pow(1.431817011, filval) / FLOAT_PI;
6417 + amplog = powf(1.531817011f, filval) / FLOAT_PI;
6418 + dval = (128.0f - (float)distval) / 128.0f;
6419 + amplog = (float)exp(amplog);
6420 + dval = powf(amplog,dval)/amplog;
6421 + if (lf < 8) {
6422 + tvfBiasMult[lf][distval] = (int)(dval * 256.0f);
6423 + } else {
6424 + dval = powf(dval, 0.3333333f);
6425 + if (dval < 0.01f)
6426 + dval = 0.01f;
6427 + dval = 1 / dval;
6428 + tvfBiasMult[lf][distval] = (int)(dval * 256.0f);
6431 + //synth->printDebug("Fbias lf %d distval %d = %f (%x) %f", lf, distval, dval, tvfBiasMult[lf][distval],(double)amplog);
6436 +// Per-note table initialisation follows
6438 +static void initSaw(NoteLookup *noteLookup, Bit32s div2) {
6439 + int tmpdiv = div2 << 16;
6440 + for (int rsaw = 0; rsaw <= 100; rsaw++) {
6441 + float fsaw;
6442 + if (rsaw < 50)
6443 + fsaw = 50.0f;
6444 + else
6445 + fsaw = (float)rsaw;
6447 + //(66 - (((A8 - 50) / 50) ^ 0.63) * 50) / 132
6448 + float sawfact = (66.0f - (powf((fsaw - 50.0f) / 50.0f, 0.63f) * 50.0f)) / 132.0f;
6449 + noteLookup->sawTable[rsaw] = (int)(sawfact * (float)tmpdiv) >> 16;
6450 + //synth->printDebug("F %d divtable %d saw %d sawtable %d", f, div, rsaw, sawtable[f][rsaw]);
6454 +static void initDep(KeyLookup *keyLookup, float f) {
6455 + for (int dep = 0; dep < 5; dep++) {
6456 + if (dep == 0) {
6457 + keyLookup->envDepthMult[dep] = 256;
6458 + keyLookup->envTimeMult[dep] = 256;
6459 + } else {
6460 + float depfac = 3000.0f;
6461 + float ff, tempdep;
6462 + depfac = (float)depexp[dep];
6464 + ff = (f - (float)MIDDLEC) / depfac;
6465 + tempdep = powf(2, ff) * 256.0f;
6466 + keyLookup->envDepthMult[dep] = (int)tempdep;
6468 + ff = (float)(exp(tkcatconst[dep] * ((float)MIDDLEC - f)) * tkcatmult[dep]);
6469 + keyLookup->envTimeMult[dep] = (int)(ff * 256.0f);
6472 + //synth->printDebug("F %f d1 %x d2 %x d3 %x d4 %x d5 %x", (double)f, noteLookup->fildepTable[0], noteLookup->fildepTable[1], noteLookup->fildepTable[2], noteLookup->fildepTable[3], noteLookup->fildepTable[4]);
6475 +Bit16s Tables::clampWF(Synth *synth, const char *n, float ampVal, double input) {
6476 + Bit32s x = (Bit32s)(input * ampVal);
6477 + if (x < -ampVal - 1) {
6478 + synth->printDebug("%s==%d<-WGAMP-1!", n, x);
6479 + x = (Bit32s)(-ampVal - 1);
6480 + } else if (x > ampVal) {
6481 + synth->printDebug("%s==%d>WGAMP!", n, x);
6482 + x = (Bit32s)ampVal;
6484 + return (Bit16s)x;
6487 +File *Tables::initWave(Synth *synth, NoteLookup *noteLookup, float ampVal, float div2, File *file) {
6488 + int iDiv2 = (int)div2;
6489 + noteLookup->waveformSize[0] = iDiv2 << 1;
6490 + noteLookup->waveformSize[1] = iDiv2 << 1;
6491 + noteLookup->waveformSize[2] = iDiv2 << 2;
6492 + for (int i = 0; i < 3; i++) {
6493 + if (noteLookup->waveforms[i] == NULL) {
6494 + noteLookup->waveforms[i] = new Bit16s[noteLookup->waveformSize[i]];
6497 + if (file != NULL) {
6498 + for (int i = 0; i < 3 && file != NULL; i++) {
6499 + size_t len = noteLookup->waveformSize[i];
6500 + for (unsigned int j = 0; j < len; j++) {
6501 + if (!file->readBit16u((Bit16u *)&noteLookup->waveforms[i][j])) {
6502 + synth->printDebug("Error reading wave file cache!");
6503 + file->close();
6504 + file = NULL;
6505 + break;
6510 + if (file == NULL) {
6511 + double sd = DOUBLE_PI / div2;
6513 + for (int fa = 0; fa < (iDiv2 << 1); fa++) {
6514 + // sa ranges from 0 to 2PI
6515 + double sa = fa * sd;
6517 + // Calculate a sample for the bandlimited sawtooth wave
6518 + double saw = 0.0;
6519 + int sincs = iDiv2 >> 1;
6520 + double sinus = 1.0;
6521 + for (int sincNum = 1; sincNum <= sincs; sincNum++) {
6522 + saw += sin(sinus * sa) / sinus;
6523 + sinus++;
6526 + // This works pretty well
6527 + // Multiplied by 0.84 so that the spikes caused by bandlimiting don't overdrive the amplitude
6528 + noteLookup->waveforms[0][fa] = clampWF(synth, "saw", ampVal, -saw / (0.5 * DOUBLE_PI) * 0.84);
6529 + noteLookup->waveforms[1][fa] = clampWF(synth, "cos", ampVal, -cos(sa / 2.0));
6530 + noteLookup->waveforms[2][fa * 2] = clampWF(synth, "cosoff_0", ampVal, -cos(sa - DOUBLE_PI));
6531 + noteLookup->waveforms[2][fa * 2 + 1] = clampWF(synth, "cosoff_1", ampVal, -cos((sa + (sd / 2)) - DOUBLE_PI));
6534 + return file;
6537 +static void initFiltTable(NoteLookup *noteLookup, float freq, float rate) {
6538 + for (int tr = 0; tr <= 200; tr++) {
6539 + float ftr = (float)tr;
6541 + // Verified exact on MT-32
6542 + if (tr > 100)
6543 + ftr = 100.0f + (powf((ftr - 100.0f) / 100.0f, 3.0f) * 100.0f);
6545 + // I think this is the one
6546 + float brsq = powf(10.0f, (tr / 50.0f) - 1.0f);
6547 + noteLookup->filtTable[0][tr] = (int)((freq * brsq) / (rate / 2) * FILTERGRAN);
6548 + if (noteLookup->filtTable[0][tr]>=((FILTERGRAN*15)/16))
6549 + noteLookup->filtTable[0][tr] = ((FILTERGRAN*15)/16);
6551 + float brsa = powf(10.0f, ((tr / 55.0f) - 1.0f)) / 2.0f;
6552 + noteLookup->filtTable[1][tr] = (int)((freq * brsa) / (rate / 2) * FILTERGRAN);
6553 + if (noteLookup->filtTable[1][tr]>=((FILTERGRAN*15)/16))
6554 + noteLookup->filtTable[1][tr] = ((FILTERGRAN*15)/16);
6558 +static void initNFiltTable(NoteLookup *noteLookup, float freq, float rate) {
6559 + for (int cf = 0; cf <= 100; cf++) {
6560 + float cfmult = (float)cf;
6562 + for (int tf = 0;tf <= 100; tf++) {
6563 + float tfadd = (float)tf;
6565 + //float freqsum = exp((cfmult + tfadd) / 30.0f) / 4.0f;
6566 + //float freqsum = 0.15f * exp(0.45f * ((cfmult + tfadd) / 10.0f));
6568 + float freqsum = powf(2.0f, ((cfmult + tfadd) - 40.0f) / 16.0f);
6570 + noteLookup->nfiltTable[cf][tf] = (int)((freq * freqsum) / (rate / 2) * FILTERGRAN);
6571 + if (noteLookup->nfiltTable[cf][tf] >= ((FILTERGRAN * 15) / 16))
6572 + noteLookup->nfiltTable[cf][tf] = ((FILTERGRAN * 15) / 16);
6577 +File *Tables::initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float masterTune, PCMWaveEntry *pcmWaves, File *file) {
6578 + float freq = (float)(masterTune * pow(2.0, ((double)note - MIDDLEA) / 12.0));
6579 + float div2 = rate * 2.0f / freq;
6580 + noteLookup->div2 = (int)div2;
6582 + if (noteLookup->div2 == 0)
6583 + noteLookup->div2 = 1;
6585 + initSaw(noteLookup, noteLookup->div2);
6587 + //synth->printDebug("Note %f; freq=%f, div=%f", (double)note, (double)freq, (double)rate / freq);
6588 + file = initWave(synth, noteLookup, WGAMP, div2, file);
6590 + // Create the pitch tables
6591 + if (noteLookup->wavTable == NULL)
6592 + noteLookup->wavTable = new Bit32u[synth->controlROMMap->pcmCount];
6593 + double rateMult = 32000.0 / rate;
6594 + double tuner = freq * 65536.0f;
6595 + for (int pc = 0; pc < synth->controlROMMap->pcmCount; pc++) {
6596 + noteLookup->wavTable[pc] = (int)(tuner / pcmWaves[pc].tune * rateMult);
6599 + initFiltTable(noteLookup, freq, rate);
6600 + initNFiltTable(noteLookup, freq, rate);
6601 + return file;
6604 +bool Tables::initNotes(Synth *synth, PCMWaveEntry *pcmWaves, float rate, float masterTune) {
6605 + const char *NoteNames[12] = {
6606 + "C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B "
6607 + };
6608 + char filename[64];
6609 + int intRate = (int)rate;
6610 + char version[4] = {0, 0, 0, 5};
6611 + sprintf(filename, "./waveform/waveformcache-%d-%.2f.raw", intRate, (double)masterTune);
6613 + File *file = NULL;
6614 + char header[20];
6615 + memcpy(header, "MT32WAVE", 8);
6616 + int pos = 8;
6617 + // Version...
6618 + for (int i = 0; i < 4; i++)
6619 + header[pos++] = version[i];
6620 + header[pos++] = (char)((intRate >> 24) & 0xFF);
6621 + header[pos++] = (char)((intRate >> 16) & 0xFF);
6622 + header[pos++] = (char)((intRate >> 8) & 0xFF);
6623 + header[pos++] = (char)(intRate & 0xFF);
6624 + int intTuning = (int)masterTune;
6625 + header[pos++] = (char)((intTuning >> 8) & 0xFF);
6626 + header[pos++] = (char)(intTuning & 0xFF);
6627 + header[pos++] = 0;
6628 + header[pos] = (char)((masterTune - intTuning) * 10);
6629 +#if MT32EMU_WAVECACHEMODE < 2
6630 + bool reading = false;
6631 + file = synth->openFile(filename, File::OpenMode_read);
6632 + if (file != NULL) {
6633 + char fileHeader[20];
6634 + if (file->read(fileHeader, 20) == 20) {
6635 + if (memcmp(fileHeader, header, 20) == 0) {
6636 + Bit16u endianCheck;
6637 + if (file->readBit16u(&endianCheck)) {
6638 + if (endianCheck == 1) {
6639 + reading = true;
6640 + } else {
6641 + synth->printDebug("Endian check in %s does not match expected", filename);
6643 + } else {
6644 + synth->printDebug("Unable to read endian check in %s", filename);
6646 + } else {
6647 + synth->printDebug("Header of %s does not match expected", filename);
6649 + } else {
6650 + synth->printDebug("Error reading 16 bytes of %s", filename);
6652 + if (!reading) {
6653 + file->close();
6654 + file = NULL;
6656 + } else {
6657 + synth->printDebug("Unable to open %s for reading", filename);
6659 +#endif
6661 + float progress = 0.0f;
6662 + bool abort = false;
6663 + synth->report(ReportType_progressInit, &progress);
6664 + for (int f = LOWEST_NOTE; f <= HIGHEST_NOTE; f++) {
6665 + synth->printDebug("Initializing note %s%d", NoteNames[f % 12], (f / 12) - 2);
6666 + NoteLookup *noteLookup = &noteLookups[f - LOWEST_NOTE];
6667 + file = initNote(synth, noteLookup, (float)f, rate, masterTune, pcmWaves, file);
6668 + progress = (f - LOWEST_NOTE + 1) / (float)NUM_NOTES;
6669 + abort = synth->report(ReportType_progressInit, &progress) != 0;
6670 + if (abort)
6671 + break;
6674 +#if MT32EMU_WAVECACHEMODE == 0 || MT32EMU_WAVECACHEMODE == 2
6675 + if (file == NULL) {
6676 + file = synth->openFile(filename, File::OpenMode_write);
6677 + if (file != NULL) {
6678 + if (file->write(header, 20) == 20 && file->writeBit16u(1)) {
6679 + for (int f = 0; f < NUM_NOTES; f++) {
6680 + for (int i = 0; i < 3 && file != NULL; i++) {
6681 + int len = noteLookups[f].waveformSize[i];
6682 + for (int j = 0; j < len; j++) {
6683 + if (!file->writeBit16u(noteLookups[f].waveforms[i][j])) {
6684 + synth->printDebug("Error writing waveform cache file");
6685 + file->close();
6686 + file = NULL;
6687 + break;
6692 + } else {
6693 + synth->printDebug("Error writing 16-byte header to %s - won't continue saving", filename);
6695 + } else {
6696 + synth->printDebug("Unable to open %s for writing - won't be created", filename);
6699 +#endif
6701 + if (file != NULL)
6702 + synth->closeFile(file);
6703 + return !abort;
6706 +void Tables::freeNotes() {
6707 + for (int t = 0; t < 3; t++) {
6708 + for (int m = 0; m < NUM_NOTES; m++) {
6709 + if (noteLookups[m].waveforms[t] != NULL) {
6710 + delete[] noteLookups[m].waveforms[t];
6711 + noteLookups[m].waveforms[t] = NULL;
6712 + noteLookups[m].waveformSize[t] = 0;
6714 + if (noteLookups[m].wavTable != NULL) {
6715 + delete[] noteLookups[m].wavTable;
6716 + noteLookups[m].wavTable = NULL;
6720 + initializedMasterTune = 0.0f;
6723 +Tables::Tables() {
6724 + initializedSampleRate = 0.0f;
6725 + initializedMasterTune = 0.0f;
6726 + memset(&noteLookups, 0, sizeof(noteLookups));
6729 +bool Tables::init(Synth *synth, PCMWaveEntry *pcmWaves, float sampleRate, float masterTune) {
6730 + if (sampleRate <= 0.0f) {
6731 + synth->printDebug("Bad sampleRate (%f <= 0.0f)", sampleRate);
6732 + return false;
6734 + if (initializedSampleRate == 0.0f) {
6735 + initMT32ConstantTables(synth);
6737 + if (initializedSampleRate != sampleRate) {
6738 + initFiltCoeff(sampleRate);
6739 + initEnvelopes(sampleRate);
6740 + for (int key = 12; key <= 108; key++) {
6741 + initDep(&keyLookups[key - 12], (float)key);
6744 + if (initializedSampleRate != sampleRate || initializedMasterTune != masterTune) {
6745 + freeNotes();
6746 + if (!initNotes(synth, pcmWaves, sampleRate, masterTune)) {
6747 + return false;
6749 + initializedSampleRate = sampleRate;
6750 + initializedMasterTune = masterTune;
6752 + return true;
6756 diff -urN dosbox.orig/src/gui/tables.h dosbox/src/gui/tables.h
6757 --- dosbox.orig/src/gui/tables.h 1969-12-31 21:00:00.000000000 -0300
6758 +++ dosbox/src/gui/tables.h 2011-01-11 20:15:44.318165437 -0300
6759 @@ -0,0 +1,116 @@
6760 +/* Copyright (c) 2003-2005 Various contributors
6762 + * Permission is hereby granted, free of charge, to any person obtaining a copy
6763 + * of this software and associated documentation files (the "Software"), to
6764 + * deal in the Software without restriction, including without limitation the
6765 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
6766 + * sell copies of the Software, and to permit persons to whom the Software is
6767 + * furnished to do so, subject to the following conditions:
6769 + * The above copyright notice and this permission notice shall be included in
6770 + * all copies or substantial portions of the Software.
6772 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6773 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6774 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6775 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
6776 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
6777 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
6778 + * IN THE SOFTWARE.
6779 + */
6781 +#ifndef MT32EMU_TABLES_H
6782 +#define MT32EMU_TABLES_H
6784 +namespace MT32Emu {
6786 +// Mathematical constants
6787 +const double DOUBLE_PI = 3.1415926535897932384626433832795;
6788 +const double DOUBLE_LN = 2.3025850929940456840179914546844;
6789 +const float FLOAT_PI = 3.1415926535897932384626433832795f;
6790 +const float FLOAT_LN = 2.3025850929940456840179914546844f;
6792 +// Filter settings
6793 +const int FILTERGRAN = 512;
6795 +// Amplitude of waveform generator
6796 +// FIXME: This value is the amplitude possible whilst avoiding
6797 +// overdriven values immediately after filtering when playing
6798 +// back SQ3MT.MID. Needs to be checked.
6799 +const int WGAMP = 12382;
6801 +const int MIDDLEC = 60;
6802 +const int MIDDLEA = 69; // By this I mean "A above middle C"
6804 +// FIXME:KG: may only need to do 12 to 108
6805 +// 12..108 is the range allowed by note on commands, but the key can be modified by pitch keyfollow
6806 +// and adjustment for timbre pitch, so the results can be outside that range.
6807 +// Should we move it (by octave) into the 12..108 range, or keep it in 0..127 range,
6808 +// or something else altogether?
6809 +const int LOWEST_NOTE = 12;
6810 +const int HIGHEST_NOTE = 127;
6811 +const int NUM_NOTES = HIGHEST_NOTE - LOWEST_NOTE + 1; // Number of slots for note LUT
6813 +class Synth;
6815 +struct NoteLookup {
6816 + Bit32u div2;
6817 + Bit32u *wavTable;
6818 + Bit32s sawTable[101];
6819 + int filtTable[2][201];
6820 + int nfiltTable[101][101];
6821 + Bit16s *waveforms[3];
6822 + Bit32u waveformSize[3];
6825 +struct KeyLookup {
6826 + Bit32s envTimeMult[5]; // For envelope time adjustment for key pressed
6827 + Bit32s envDepthMult[5];
6830 +class Tables {
6831 + float initializedSampleRate;
6832 + float initializedMasterTune;
6833 + void initMT32ConstantTables(Synth *synth);
6834 + static Bit16s clampWF(Synth *synth, const char *n, float ampVal, double input);
6835 + static File *initWave(Synth *synth, NoteLookup *noteLookup, float ampsize, float div2, File *file);
6836 + bool initNotes(Synth *synth, PCMWaveEntry *pcmWaves, float rate, float tuning);
6837 + void initEnvelopes(float sampleRate);
6838 + void initFiltCoeff(float samplerate);
6839 +public:
6840 + // Constant LUTs
6841 + Bit32s tvfKeyfollowMult[217];
6842 + Bit32s tvfVelfollowMult[128][101];
6843 + Bit32s tvfBiasMult[15][128];
6844 + Bit32u tvaVelfollowMult[128][101];
6845 + Bit32s tvaBiasMult[13][128];
6846 + Bit16s noiseBuf[MAX_SAMPLE_OUTPUT];
6847 + Bit16s sintable[65536];
6848 + Bit32s pitchEnvVal[16][101];
6849 + Bit32s envTimeVelfollowMult[5][128];
6850 + Bit32s pwVelfollowAdd[15][128];
6851 + float resonanceFactor[31];
6852 + Bit32u lfoShift[101][101];
6853 + Bit32s pwFactor[101];
6854 + Bit32s volumeMult[101];
6856 + // LUTs varying with sample rate
6857 + Bit32u envTime[101];
6858 + Bit32u envDeltaMaxTime[101];
6859 + Bit32u envDecayTime[101];
6860 + Bit32u lfoPeriod[101];
6861 + float filtCoeff[FILTERGRAN][31][8];
6863 + // Various LUTs for each note and key
6864 + NoteLookup noteLookups[NUM_NOTES];
6865 + KeyLookup keyLookups[97];
6867 + Tables();
6868 + bool init(Synth *synth, PCMWaveEntry *pcmWaves, float sampleRate, float masterTune);
6869 + File *initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float tuning, PCMWaveEntry *pcmWaves, File *file);
6870 + void freeNotes();
6875 +#endif