blob: 280477fed92f0d786b6dd30bcdbccef7be3d405e [file] [log] [blame] [raw]
/*
* Creative Music System device output support for DOSMid
* Copyright 2021 Tronix
* Copyright 2024 Rivoreo
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef CMS
//#define CMS_DEBUG // verbose to CMSLOG debug file
//#define DRUMS_ONLY // for percussion debug purpouses
#include "defines.h"
#include "cms.h"
#ifdef CMSLPT
#include "lpt.h"
#endif
#ifdef MSDOS
#include <conio.h>
#else
#include "unixpio.h"
#endif
#include <stddef.h>
#include <assert.h>
#ifdef CMS_DEBUG
#include <stdio.h>
#include <stdarg.h>
#endif
#ifdef CMS_DEBUG
static void debug_log(const char *fmt, ...)
{
FILE *log_f;
va_list ap;
log_f = fopen("cmslog", "a");
if(!log_f) return;
va_start(ap, fmt);
vfprintf(log_f, fmt, ap);
va_end(ap);
fclose(log_f);
}
#endif
struct mid_channel {
unsigned char note;
unsigned char priority;
unsigned char ch;
unsigned char voice;
unsigned char velocity;
};
static struct mid_channel cms_synth[MAX_CMS_CHANNELS];
static const unsigned short freqtable[128] = {
8, 9, 9, 10, 10, 11, 12, 12, 13, 14, 15, 15,
16, 17, 18, 19, 21, 22, 23, 24, 26, 28, 29, 31,
33, 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62,
65, 69, 73, 78, 82, 87, 92, 98, 104, 110, 117, 123,
131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247,
262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494,
523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,
1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976,
2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459, 7902,
8372, 8870, 9397, 9956,10548,11175,11840,12544};
static const unsigned short pitchtable[256] = { /* pitch wheel */
29193U,29219U,29246U,29272U,29299U,29325U,29351U,29378U, /* -128 */
29405U,29431U,29458U,29484U,29511U,29538U,29564U,29591U, /* -120 */
29618U,29644U,29671U,29698U,29725U,29752U,29778U,29805U, /* -112 */
29832U,29859U,29886U,29913U,29940U,29967U,29994U,30021U, /* -104 */
30048U,30076U,30103U,30130U,30157U,30184U,30212U,30239U, /* -96 */
30266U,30293U,30321U,30348U,30376U,30403U,30430U,30458U, /* -88 */
30485U,30513U,30541U,30568U,30596U,30623U,30651U,30679U, /* -80 */
30706U,30734U,30762U,30790U,30817U,30845U,30873U,30901U, /* -72 */
30929U,30957U,30985U,31013U,31041U,31069U,31097U,31125U, /* -64 */
31153U,31181U,31209U,31237U,31266U,31294U,31322U,31350U, /* -56 */
31379U,31407U,31435U,31464U,31492U,31521U,31549U,31578U, /* -48 */
31606U,31635U,31663U,31692U,31720U,31749U,31778U,31806U, /* -40 */
31835U,31864U,31893U,31921U,31950U,31979U,32008U,32037U, /* -32 */
32066U,32095U,32124U,32153U,32182U,32211U,32240U,32269U, /* -24 */
32298U,32327U,32357U,32386U,32415U,32444U,32474U,32503U, /* -16 */
32532U,32562U,32591U,32620U,32650U,32679U,32709U,32738U, /* -8 */
32768U,32798U,32827U,32857U,32887U,32916U,32946U,32976U, /* 0 */
33005U,33035U,33065U,33095U,33125U,33155U,33185U,33215U, /* 8 */
33245U,33275U,33305U,33335U,33365U,33395U,33425U,33455U, /* 16 */
33486U,33516U,33546U,33576U,33607U,33637U,33667U,33698U, /* 24 */
33728U,33759U,33789U,33820U,33850U,33881U,33911U,33942U, /* 32 */
33973U,34003U,34034U,34065U,34095U,34126U,34157U,34188U, /* 40 */
34219U,34250U,34281U,34312U,34343U,34374U,34405U,34436U, /* 48 */
34467U,34498U,34529U,34560U,34591U,34623U,34654U,34685U, /* 56 */
34716U,34748U,34779U,34811U,34842U,34874U,34905U,34937U, /* 64 */
34968U,35000U,35031U,35063U,35095U,35126U,35158U,35190U, /* 72 */
35221U,35253U,35285U,35317U,35349U,35381U,35413U,35445U, /* 80 */
35477U,35509U,35541U,35573U,35605U,35637U,35669U,35702U, /* 88 */
35734U,35766U,35798U,35831U,35863U,35895U,35928U,35960U, /* 96 */
35993U,36025U,36058U,36090U,36123U,36155U,36188U,36221U, /* 104 */
36254U,36286U,36319U,36352U,36385U,36417U,36450U,36483U, /* 112 */
36516U,36549U,36582U,36615U,36648U,36681U,36715U,36748U}; /* 120 */
static const unsigned char CMSFreqMap[128] = {
0 , 3, 7, 11, 15, 19, 23, 27,
31 , 34, 38, 41, 45, 48, 51, 55,
58 , 61, 64, 66, 69, 72, 75, 77,
80 , 83, 86, 88, 91, 94, 96, 99,
102,104,107,109,112,114,116,119,
121,123,125,128,130,132,134,136,
138,141,143,145,147,149,151,153,
155,157,159,161,162,164,166,168,
170,172,174,175,177,179,181,182,
184,186,188,189,191,193,194,196,
197,199,200,202,203,205,206,208,
209,210,212,213,214,216,217,218,
219,221,222,223,225,226,227,228,
229,231,232,233,234,235,236,237,
239,240,241,242,243,244,245,246,
247,249,250,251,252,253,254,255
};
#ifndef MSDOS
static int cms_fd = -1;
#endif
// CMS I/O port address
static unsigned short int cms_port;
#ifdef CMSLPT
static int is_cmslpt;
#endif
#if 0
// The 12 note-within-an-octave values for the SAA1099, starting at B
static unsigned char noteAdr[] = {5, 32, 60, 85, 110, 132, 153, 173, 192, 210, 227, 243};
#endif
// Volume
static const unsigned char atten[128] = {
0,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,
3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,
5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,
7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,
9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,
11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,
13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
};
// Logic channel - first chip/second chip
static const unsigned char ChanReg[12] = {000,001,002,003,004,005,000,001,002,003,004,005};
// Set octave command
static const unsigned char OctavReg[12] = {0x10,0x10,0x11,0x11,0x12,0x12,0x10,0x10,0x11,0x11,0x12,0x12};
static unsigned char octave_store[12];
static unsigned char voice_enable[2];
static unsigned char note_priority;
static unsigned short int channel_pitch[16];
static unsigned char channel_volume[16];
static signed char pan[16];
static void write_cms(unsigned char chip_i, unsigned char reg, unsigned char value) {
#ifdef CMS_DEBUG
debug_log("function: write_cms(%hhu, 0x%hhx, 0x%hhx)\n", chip_i, reg, value);
#endif
assert(chip_i == 0 || chip_i == 1);
#ifdef CMSLPT
if(is_cmslpt) {
unsigned int ctrl = chip_i ? 6 : 12;
#ifndef MSDOS
if(cms_fd != -1) {
write_lpt_fd(cms_fd, reg, ctrl);
write_lpt_fd(cms_fd, value, ctrl | 1);
} else
#endif
{
#ifdef HAVE_PORT_IO
write_lpt(cms_port, reg, ctrl);
write_lpt(cms_port, value, ctrl | 1);
#endif
}
} else
#endif
{
#ifdef HAVE_PORT_IO
unsigned int port = cms_port + (chip_i ? 2 : 0);
outp(port + 1, reg); /* Select register */
outp(port, value); /* Set value of the register */
#endif
}
}
#if defined MSDOS && !defined _QC
static void __declspec(naked) asm_write_cms() {
__asm {
push ax
push dx
push bx
xchg ax, dx
xor bx, bx
mov bl, dl
mov dl, dh
xor dh, dh
cmp ax, 0
je first
dec ax
first:
call write_cms
pop bx
pop dx
pop ax
ret
}
}
#endif
void cms_reset(
#ifndef MSDOS
int fd,
#endif
unsigned short int port, int is_on_lpt) {
int i;
#ifndef MSDOS
assert(fd == -1 || is_on_lpt);
cms_fd = fd;
#endif
cms_port = port;
#ifdef CMSLPT
is_cmslpt = is_on_lpt;
#endif
for(i = 0; i < 2; i++) {
int j;
for(j = 0; j < 32; j++) write_cms(i, j, 0);
write_cms(i, 0x1c, 0x2);
write_cms(i, 0x1c, 0x1);
}
for (i=0; i<12; i++) octave_store[i] = 0;
voice_enable[0] = 0;
voice_enable[1] = 0;
for (i=0; i<MAX_CMS_CHANNELS; i++) {
struct mid_channel *mch = cms_synth + i;
mch->note = 0;
mch->priority = 0;
mch->ch = 0;
mch->voice = 0;
mch->velocity = 0;
}
for (i=0; i<16; i++) {
channel_pitch[i] = 8192;
channel_volume[i] = 127;
pan[i] = 0;
}
note_priority = 0;
}
static void cms_disable_voice(unsigned char voice)
{
if (voice > 5) {
voice_enable[1] &= ~(1 << ChanReg[voice]);
write_cms(1, 0x14, voice_enable[1]);
} else {
voice_enable[0] &= ~(1 << ChanReg[voice]);
write_cms(0, 0x14, voice_enable[0]);
}
}
static void cms_enable_voice(unsigned char voice, unsigned char freq, unsigned char octave, unsigned char left_amplitude, unsigned char right_amplitude)
{
#if defined MSDOS && !defined _QC
__asm {
xor bh,bh
mov bl,voice
xor dx, dx
cmp bl,06h ; check channel num > 5?
jl setOctave ; yes - set port = port + 2
add dx,2
setOctave:
mov bl,ChanReg[bx] ; bx = true channel (0 - 5) //FIXME swap down
mov ah,OctavReg[bx] ; ah = Set octave command //FIXME swap up
;
; ah now = register
; 0,1,6,7=$10
; 2,3,8,9=$11
; 4,5,10,11=$12
;
; CMS octave regs are write only, so we have to track
; the values in adjoining voices manually
mov al,ah
xor ah,ah ; ax = set octave cmd (10h - 12h)
mov di,ax ; di = ax
sub di,010h ; di = octave cmd - 10h (0..2 index)
mov cl,voice
cmp cl,06h
jl skip_inc
add di,3
skip_inc:
mov ah,al ; set ah back to octave cmd
mov al,byte ptr octave_store[di]
mov bh,octave
test bl,01h
jnz shiftOctave
and al,0F0h
jmp outOctave
shiftOctave:
and al,0Fh
mov cl,4
shl bh,cl
outOctave:
or al,bh
mov byte ptr octave_store[di],al
call asm_write_cms ; set octave to CMS
setAmp:
mov al,byte ptr left_amplitude
mov ah,byte ptr right_amplitude
;and al,0Fh
mov cl,4
shl ah,cl
or al,ah
mov ah,bl
call asm_write_cms
setFreq:
mov al,byte ptr freq
or ah,08h
call asm_write_cms
voiceEnable:
xor di,di
mov cl,voice
cmp cl,06h
jl skip_inc2
inc di
skip_inc2:
mov cl,bl
mov bl,voice_enable[di]
mov bh,01h
shl bh,cl
or bl,bh
mov voice_enable[di],bl
mov ax, dx
mov dx, 14h
cmp ax, 0
je first
dec ax
first:
call write_cms
}
#else
unsigned char chip_i = voice > 5;
unsigned char *octave_p = octave_store + (OctavReg[voice] - 0x10);
if(voice > 5) octave_p += 3;
if (!(ChanReg[voice] & 1)) {
*octave_p = (*octave_p & 0xF0) | octave;
} else {
*octave_p = (*octave_p & 0xF) | (octave << 4);
}
write_cms(chip_i, OctavReg[voice], *octave_p);
write_cms(chip_i, ChanReg[voice], (right_amplitude << 4) | left_amplitude);
write_cms(chip_i, ChanReg[voice] | 0x8, freq);
voice_enable[chip_i] |= 1 << ChanReg[voice];
write_cms(chip_i, 0x14, voice_enable[chip_i]);
#endif
}
static void cms_set_volume(unsigned char voice, unsigned char left_amplitude, unsigned char right_amplitude) {
#if defined MSDOS && !defined _QC
__asm {
xor bh,bh
mov bl, voice
xor dx, dx
cmp bl, 6 ; check channel num > 5?
jl skip_inc ; yes - set port = port + 2
add dx, 2
skip_inc:
mov bl, ChanReg[bx] ; bx = true channel (0 - 5)
mov al, byte ptr left_amplitude
mov ah, byte ptr right_amplitude
mov cl, 4
shl ah, cl
or al, ah
mov ah, bl
call asm_write_cms
}
#else
write_cms(voice > 5, ChanReg[voice], (right_amplitude << 4) | (left_amplitude & 0xf));
#endif
}
static int scale_velocity(int velocity, unsigned char volume, signed char pan) {
if(volume < 127) velocity = velocity * volume / 127;
if(!pan) return velocity;
velocity += velocity * pan / 47;
if(velocity < 0) return 0;
if(velocity > 127) return 127;
return velocity;
}
// ****
// High-level CMS synth procedures
// ****
void cms_pitchwheel(int channel, int pitchwheel)
{
int i;
unsigned short notefreq;
unsigned char note;
int pitch;
unsigned char octave;
channel_pitch[channel] = pitchwheel;
for(i=0; i<MAX_CMS_CHANNELS; i++) {
const struct mid_channel *mch = cms_synth + i;
if (mch->ch == channel && mch->note != 0) {
note = cms_synth[i].note;
pitch = pitchwheel;
if (pitch != 0) {
if (pitch > 127) {
pitch = 127;
} else if (pitch < -128) {
pitch = -128;
}
}
notefreq = ((unsigned long int)freqtable[note] * pitchtable[pitch + 128]) >> 15;
if (notefreq > 31 && notefreq < 7824) {
int left_velocity, right_velocity;
octave = 4;
while (notefreq < 489) {
notefreq=notefreq * 2;
octave--;
}
while (notefreq > 977) {
notefreq=notefreq / 2;
octave++;
}
#ifndef DRUMS_ONLY
left_velocity = scale_velocity(mch->velocity, channel_volume[channel], -pan[channel]);
right_velocity = scale_velocity(mch->velocity, channel_volume[channel], pan[channel]);
cms_enable_voice(mch->voice, CMSFreqMap[((notefreq-489)*128) / 489], octave, atten[left_velocity], atten[right_velocity]);
#endif
}
}
}
}
void cms_noteoff(unsigned char channel, unsigned char note)
{
int i, j;
if (channel == 9) {
#ifdef CMS_DEBUG
debug_log("DRUM OFF note %u\n", note);
#endif
write_cms(1, 0x19, 0x0);
write_cms(1, 0x15, 0x0); // noise ch 11
cms_disable_voice(11);
} else {
for(i=0; i<MAX_CMS_CHANNELS; i++) {
struct mid_channel *mch = cms_synth + i;
if(mch->note != note) continue;
if(mch->ch != channel) continue;
// decrease priority for all notes greater than current
for (j=0; j<MAX_CMS_CHANNELS; j++) {
if (cms_synth[j].priority <= mch->priority) continue;
cms_synth[j].priority--;
}
if (note_priority != 0) note_priority--;
cms_disable_voice(i);
mch->note = 0;
mch->priority = 0;
mch->ch = 0;
mch->voice = 0;
mch->velocity = 0;
return;
}
// Note not found, ignore note off command
#ifdef CMS_DEBUG
debug_log("note %u channel %u not found\n", (unsigned int)channel, (unsigned int)note);
#endif
}
}
void cms_noteon(unsigned char channel, unsigned char note, unsigned char velocity)
{
int left_velocity, right_velocity;
int i;
unsigned char voice;
int pitch;
if(velocity) {
left_velocity = scale_velocity(velocity, channel_volume[channel], -pan[channel]);
right_velocity = scale_velocity(velocity, channel_volume[channel], pan[channel]);
}
if (channel == 9)
{
if (velocity != 0)
{
#ifdef CMS_DEBUG
debug_log("DRUM ON note= %u\n",note);
#endif
write_cms(1, 0x19, 0x84); // single dacay
switch (note) {
case 37: // Side Stick
write_cms(1, 0x16, 0x00); // noise gen 1 31.3kHz
write_cms(1, 0x15, 0x20); // noise ch 11
cms_enable_voice(11, 0, 2, atten[left_velocity], atten[right_velocity]);
break;
case 38: // Acoustic Snare
write_cms(1, 0x16, 0x00); // noise gen 1 31.3kHz
write_cms(1, 0x15, 0x20); // noise ch 11
cms_enable_voice(11, 0, 0, atten[left_velocity], atten[right_velocity]);
break;
case 39: // Hand Clap
write_cms(1, 0x16, 0x10); // noise gen 1 15.6kHz
write_cms(1, 0x15, 0x20); // noise ch 11
cms_enable_voice(11, 0, 3, atten[left_velocity], atten[right_velocity]);
break;
case 40: // Electric Snare
write_cms(1, 0x16, 0x10); // noise gen 1 15.6kHz
write_cms(1, 0x15, 0x20); // noise ch 11
cms_enable_voice(11, 0, 1, atten[left_velocity], atten[right_velocity]);
break;
case 42: // Closed Hi Hat
write_cms(1, 0x16, 0x10); // noise gen 1 15.6kHz
write_cms(1, 0x15, 0x20); // noise ch 11
cms_enable_voice(11, 0, 2, atten[left_velocity], atten[right_velocity]);
break;
case 44: // Pedal Hi-Hat
write_cms(1, 0x16, 0x10); // noise gen 1 15.6kHz
write_cms(1, 0x15, 0x20); // noise ch 11
cms_enable_voice(11, 0, 0, atten[left_velocity], atten[right_velocity]);
break;
case 52: // Chinese Cymbal
write_cms(1, 0x16, 0x20); // noise gen 1 7.6kHz
write_cms(1, 0x15, 0x20); // noise ch 11
cms_enable_voice(11, 0, 2, atten[left_velocity], atten[right_velocity]);
break;
case 55: // Splash Cymbal
write_cms(1, 0x16, 0x20); // noise gen 1 7.6kHz
write_cms(1, 0x15, 0x20); // noise ch 11
cms_enable_voice(11, 0, 0, atten[left_velocity], atten[right_velocity]);
break;
case 71: // Short Whistle
case 72: // Long Whistle
cms_enable_voice(11, 0, 6, atten[left_velocity], atten[right_velocity]);
break;
default:
cms_enable_voice(11, 0, 1, atten[left_velocity], atten[right_velocity]);
}
}
else
{
write_cms(1, 0x19, 0x0);
write_cms(1, 0x15, 0x0); // noise ch 11
cms_disable_voice(11);
}
} else if (velocity != 0) {
/*
unsigned char note_cms = note+1;
unsigned char octave = (note_cms / 12) - 1; //Some fancy math to get the correct octave
unsigned char noteVal = note_cms - ((octave + 1) * 12); //More fancy math to get the correct note
*/
struct mid_channel *mch = NULL;
unsigned int notefreq;
note_priority++;
voice = MAX_CMS_CHANNELS;
for(i=0; i<MAX_CMS_CHANNELS; i++) {
if(cms_synth[i].note == 0) {
voice = i;
mch = cms_synth + i;
break;
}
}
// We run out of voices, find low priority voice
if(!mch) {
unsigned char min_prior = cms_synth[0].priority;
#ifdef CMS_DEBUG
debug_log("out of voice. note priority %u\n", note_priority);
for (i=0; i<MAX_CMS_CHANNELS; i++) debug_log("%u ", cms_synth[i].priority);
debug_log("\n");
#endif
// find note with min prioryty
mch = cms_synth;
voice = 0;
for (i=1; i<MAX_CMS_CHANNELS; i++) {
if (cms_synth[i].priority >= min_prior) continue;
mch = cms_synth + i;
voice = i;
min_prior = mch->priority;
}
// decrease all notes priority by one
for (i=0; i<MAX_CMS_CHANNELS; i++) {
if (cms_synth[i].priority != 0) cms_synth[i].priority--;
}
// decrease current priority
if (note_priority != 0) note_priority--;
#ifdef CMS_DEBUG
debug_log("find low priority voice %u, note priority %u\n", voice, note_priority);
for (i=0; i<MAX_CMS_CHANNELS; i++) debug_log("%u ", cms_synth[i].priority);
debug_log("\n");
#endif
}
pitch = channel_pitch[channel];
if (pitch != 0) {
if (pitch > 127) {
pitch = 127;
} else if (pitch < -128) {
pitch = -128;
}
}
notefreq = ((unsigned long int)freqtable[note] * pitchtable[pitch + 128]) >> 15;
if (notefreq > 31 && notefreq < 7824) {
unsigned char octave = 4;
while (notefreq < 489) {
notefreq = notefreq * 2;
octave--;
}
while (notefreq > 977) {
notefreq = notefreq / 2;
octave++;
}
#ifndef DRUMS_ONLY
cms_enable_voice(voice, CMSFreqMap[((notefreq-489)*128) / 489], octave, atten[left_velocity], atten[right_velocity]);
#endif
mch->note = note;
mch->priority = note_priority;
mch->velocity = velocity;
mch->ch = channel;
mch->voice = voice;
}
} else {
cms_noteoff(channel,note);
}
}
#if 0
void cms_tick(void)
{
}
#endif
void cms_controller(unsigned char channel, unsigned char id, unsigned char val)
{
int i;
switch(id) {
case 7:
// Volume
if(val > 127) val = 127;
channel_volume[channel] = val;
for(i=0; i<MAX_CMS_CHANNELS; i++) {
int left_velocity, right_velocity;
const struct mid_channel *mch = cms_synth + i;
if(!mch->note) continue;
if(mch->ch != channel) continue;
left_velocity = scale_velocity(mch->velocity, val, -pan[channel]);
right_velocity = scale_velocity(mch->velocity, val, pan[channel]);
cms_set_volume(i, atten[left_velocity], atten[right_velocity]);
}
break;
case 10:
// Pan
if(val > 127) val = 127;
pan[channel] = (signed char)val - 64;
break;
case 121:
// Reset controllers
for (i=0; i<16; i++) {
channel_pitch[i] = 8192;
channel_volume[i] = 127;
pan[i] = 0;
}
// Fallthrough
case 120:
case 123:
// All notes off
for (i=0; i<MAX_CMS_CHANNELS; i++) {
struct mid_channel *mch = cms_synth + i;
if (mch->note != 0) {
cms_disable_voice(i);
mch->note = 0;
mch->priority = 0;
mch->ch = 0;
mch->voice = 0;
mch->velocity = 0;
}
}
break;
}
}
#endif