source: cmedia/trunk/Drv16/fmadlib.cpp

Last change on this file was 354, checked in by stevenhl, 17 years ago

Import untested baseline cmedia sources, work products and binaries
Binaries and work products should be deleted from repository.
once new builds are verified to work.

File size: 20.9 KB
Line 
1
2/* SCCSID = src/dev/mme/tropez/fmadlib.cpp, tropez, c.basedd 97/07/17 */
3/****************************************************************************
4 * *
5 * Copyright (c) IBM Corporation 1994 - 1997. *
6 * Copyright (c) Voyetra Technologies 1990-1993. All rights reserved *
7 * *
8 * The following IBM OS/2 source code is provided to you solely for the *
9 * the purpose of assisting you in your development of OS/2 device drivers. *
10 * You may use this code in accordance with the IBM License Agreement *
11 * provided in the IBM Device Driver Source Kit for OS/2. *
12 * *
13 ****************************************************************************/
14/**@internal src/dev/mme/tropez/fmadlib.cpp, tropez, c.basedd
15 * Allocation of FM synth resources to note generation.
16 * @version 1.2
17 * @context
18 * Unless otherwise noted, all interfaces are Ring-0, 16-bit, kernel stack.
19 * @notes
20 * @history
21 */
22
23extern "C" {
24#include <os2.h>
25#include <os2medef.h>
26#include <ssm.h>
27#include <audio.h>
28#include <meerror.h>
29#include "fmadlib.h"
30#include "fmglobal.h"
31}
32#include "fmsynth.hpp"
33
34#define TOM_PITCH 24
35#define TOM_TO_SD 7 /* 7 steps between voice 7 & 8 */
36#define SD_PITCH (TOM_PITCH + TOM_TO_SD)
37
38 /* macro to point into the patch data for a parameter */
39#define FETCH_PARAM( slot, prm) ((unsigned)(*(slot_parameter_map[slot] + prm)))
40
41#define HIGH_BYTE( word) ( ((char *)(&word))[ 1]) /* 80x86-8 only .. */
42
43typedef char SLOT_PARAM;
44
45 /* pitch bend variables */
46static char note_octave_map[ 96] = {0}; /* table of (0..95) DIV 12 */
47static char note_semi_map[ 96] = {0}; /* table of (0..95) MOD 12 */
48
49 /* used in set_the_ksl_in_a_slot(int slot) */
50static char vol_of_this_slot[ TOT_SLOTS] = {0}; /* relative volume of slots */
51
52#include "2op_Cdrm.c"
53#include "cmp_drum.c"
54
55char key_on_voice[MAX_NUMBER_OF_VOICES] = {0}; /* state of keyOn bit of each voice */
56static char current_voice_pitch[ MAX_NUMBER_OF_VOICES] = {0}; /* pitch of last note of each voice */
57
58 /* pointers to the patches for each slot */
59static SLOT_PARAM * slot_parameter_map[ TOT_SLOTS] = {0};
60
61static char percussion_enable_bits = 0; /* control bits of percussive voices */
62static char percussion_enable_bit_masks[] =
63 {
64 0x10, 0x08, 0x04, 0x02, 0x01
65 };
66 /* in 4OP mode, the first 6 2OP voices of each half chip become */
67 /* 3 4OP voices. The extra Vx registers control the PAN and */
68 /* CONFIGURATION of those operators. the SndOutputVx function */
69 /* needs to be able to set theese. The extra 2OP voices are used */
70 /* for 2OP drums and and drum-mode-drums. they are accessed from */
71 /* the other modules as higher voice numbers */
72
73/* here is a map of the voice to SndOutputVx to Vx register map for OPL3_FM */
74/* first half Vx voice OutputVx second half Vx voice OutputVx */
75 /* 0 0 0 | 0 3 3 */
76 /* 1 1 1 | 1 4 4 */
77 /* 2 2 2 | 2 5 5 */
78 /* 3 - 12 | 3 - 15 */
79 /* 4 - 13 | 4 - 16 */
80 /* 5 - 14 | 5 - 17 */
81 /* 6 9 9 | 6 6 6 */
82 /* 7 10 10 | 7 7 7 */
83 /* 8 11 11 | 8 8 8 */
84
85char voice_slot_map[MAX_NUMBER_OF_OPERATORS] [ 4 ] = {
86 {0, 3, 6, 9}, // 6 '4-OP' voices
87 {1, 4, 7, 10},
88 {2, 5, 8, 11},
89 {0 +18, 3 +18, 6 +18, 9 +18},
90 {1 +18, 4 +18, 7 +18, 10 +18},
91 {2 +18, 5 +18, 8 +18, 11 +18},
92 {12 +18, 15 +18, 12 +18, 15 +18}, // and 6 '2-OP' left over
93 {13 +18, 16 +18, 13 +18, 16 +18}, // 3 slots for 2OP drums
94 {14 +18, 17 +18, 14 +18, 17 +18},
95 {12, 15, 12, 15}, // these slots -not array vals- are used for dreum mode drums
96 {13, 16, 13, 16}, // SetVoiceVolume compares
97 {14, 17, 14, 17} // slot[0] to slot[2] to see if 2 OP
98 };
99
100static char perc_slot_map[] [ 4 ] = {
101 {12, 15, 12, 15}, // Bass Drum: slot 12 and 15
102 {16, 16, 16, 16}, // SD: slot 16
103 {14, 14, 14, 14}, // TOM: slot 14
104 {17, 17, 17, 17}, // TOP-CYM: slot 17
105 {13, 13, 13, 13} }; // HH: slot 13
106 // SetVoiceVolume compares
107 // slot[0] to slot[2] to see if 2 OP
108
109// for allocated voices to voice registers in SndOutputVx
110static char second_chip[6] = { 0, 1, 1, 0, 0, 1 };
111static char voice_to_Vx[6] = { 0, 0, 6, 6, 3, 3 };
112
113/* a voice +12 means voice + 3 in SndOutputVx */
114
115static char slot_to_voice_map2[NUM_SLOTS_CHIP] = {
116 0, 1, 2, 0, 1, 2,
117 0, 1, 2, 0, 1, 2,
118 9,10,11, 9,10,11, // these are DRUM MODE slots
119 3, 4, 5, 3, 4, 5,
120 3, 4, 5, 3, 4, 5,
121 6, 7, 8, 6, 7, 8 };
122
123
124/* This table tells if the slot is a car -OP2 OP4- (0),
125 dummy mod -OP3- (1) or a mod -OP1- (2). Mod gets FeedBack and CNT
126 Only the CNT == !FM and PAN bits are signifigant for the dummy mod */
127static char slot_is_mod[NUM_SLOTS_CHIP] = {
128 2, 2, 2, 0, 0, 0, 1, 1, 1, 0, 0, 0, 2, 2, 2, 0, 0, 0,
129 2, 2, 2, 0, 0, 0, 1, 1, 1, 0, 0, 0, 2, 2, 2, 0, 0, 0 };
130
131
132static char slot_offset_map2[NUM_SLOTS_CHIP] = {
133 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21
134};
135
136
137int adlib_pitch_range_step = 0; /* pitch variation, half-tone [+1,+12] */
138static unsigned micro_freq_lookup[ NR_STEP_PITCH] [ 12] = {0};
139static int semi_offset_lookup[MAX_NUMBER_OF_VOICES] = {0};
140static unsigned * pointer_to_current_freqs[MAX_NUMBER_OF_VOICES] = {0};
141
142int sapi_pitch_range=2;
143
144/* Local FUNCTIONS (scoped within this file).
145*/
146static void change_pitch_a_bit(int voice, long pitchBend);
147static void SetFNum( unsigned * fNumVec, int num, int den);
148static void InitFNums(void);
149
150
151void FMSYNTH::set_pitch_range(unsigned p_range)
152{
153 if ( p_range > 12)
154 p_range = 12;
155 if ( p_range < 1)
156 p_range = 1;
157
158 adlib_pitch_range_step = p_range * NR_STEP_PITCH;
159}
160
161long FMSYNTH::calc_pitch(int m_chan)
162{
163 return cur_bender[m_chan];
164}
165
166/* set_pitch_of_this_voice(voice, pitchBend)
167 * set the pitch of the voice to its current pitch + pitchBend
168 */
169void FMSYNTH::set_pitch_of_this_voice(unsigned voice, long pitchBend)
170{
171if( ! percussion || voice <= BD) /* melodic, bass drum */
172 {
173 change_pitch_a_bit(voice, pitchBend);
174 SetFreq(voice, current_voice_pitch[voice]);
175 }
176}
177
178/* InitSlotParams()
179 * put the static drum data in the drum-mode-drum slots
180 */
181void FMSYNTH::InitSlotParams(void)
182{
183 int i;
184 static int firstTimeEver = 1;
185
186 if (firstTimeEver)
187 {
188 for (i=0; i < number_of_operators; i++)
189 {
190 gop = i;
191 update_pgm(0); // start at pgm 0 9/20/90
192 }
193 gop = 0;
194 firstTimeEver = 0;
195 }
196
197 if( percussion)
198 {
199 set_a_param_in_a_slot( 12, bass_drum_preset0);
200 set_a_param_in_a_slot( 15, bass_drum_preset1);
201 set_a_param_in_a_slot( 16, sdOpr);
202 set_a_param_in_a_slot( 14, tom_tom_preset);
203 set_a_param_in_a_slot( 17, crash_cymbal_preset);
204 set_a_param_in_a_slot( 13, high_hat_preset);
205 }
206}
207
208
209/* CalcPremFNum(numDeltaDemiTon, denDeltaDemiTon)
210 * Pitch Bend utilities
211 */
212static long CalcPremFNum(int numDeltaDemiTon, int denDeltaDemiTon)
213{
214 static long f8 = 0;
215 static long fNum8 = 0;
216 long d100;
217
218 d100 = denDeltaDemiTon * 100;
219 f8 = ( d100 + 6 * numDeltaDemiTon) * (26044L * 2L); /* 260.44 * 100 * 2 */
220 f8 /= d100 * 25;
221 fNum8 = f8 * 16384; /*( 16384L * 9L); */
222 fNum8 *= 9L;
223 fNum8 /= 179L * 625L;
224 return(fNum8);
225}
226
227
228/* SetFNum(fNumVec, num, den)
229 * Pitch Bend utilities
230*/
231static long val = 0;
232
233static void SetFNum( unsigned * fNumVec, int num, int den)
234{
235 int i;
236
237 *fNumVec++ = (unsigned)(4 +(val = CalcPremFNum( num, den))) >> 3;
238 for ( i = 1; i < 12; i++)
239 {
240 val *= 106;
241 *fNumVec++ = (unsigned)(4 +(val /= 100)) >> 3;
242 }
243}
244
245
246/* InitFNums()
247 * Pitch Bend utilities
248*/
249static void InitFNums(void)
250{
251 unsigned i, j, k, num, numStep, pas;
252
253 numStep = 100 / NR_STEP_PITCH;
254 for( num = pas = 0; pas < NR_STEP_PITCH; pas++, num += numStep)
255 SetFNum( micro_freq_lookup[ pas], num, 100);
256
257 for( i = 0; i < MAX_NUMBER_OF_VOICES; i++)
258 {
259 pointer_to_current_freqs[ i] = (unsigned *) micro_freq_lookup[ 0];
260 semi_offset_lookup[ i] = 0;
261 }
262
263 for( k= i = 0; i < 8; i++)
264 {
265 for( j = 0; j < 12; j++, k++)
266 {
267 note_octave_map[ k] = (char)i;
268 note_semi_map[ k] = (char)j;
269 }
270 }
271}
272
273
274/* change_pitch_a_bit(voice, pitchBend)
275 * Pitch Bend a voice, optimized for time if called with the same pitchBend
276 * I will not pretend to understand this or the 3 routines above - LMH
277*/
278static void change_pitch_a_bit(int voice, long pitchBend)
279{
280 int temp_var_1, temp_pitch, delta;
281 static long oldL = ~0;
282 static int oldHt = 0;
283 static unsigned * oldPtr = 0;
284
285 if( oldL == pitchBend)
286 { /* optimization ... */
287 pointer_to_current_freqs[ voice] = oldPtr;
288 semi_offset_lookup[ voice] = oldHt;
289 }
290 else
291 {
292 temp_var_1 = (int) (pitchBend / MID_PITCH);
293 if( temp_var_1 < 0)
294 {
295 temp_pitch = NR_STEP_PITCH -1 -temp_var_1;
296 oldHt = semi_offset_lookup[ voice] = -(temp_pitch / NR_STEP_PITCH);
297 delta = (temp_pitch - NR_STEP_PITCH +1) % NR_STEP_PITCH;
298 if( delta) delta = NR_STEP_PITCH - delta;
299 }
300 else
301 {
302 oldHt = semi_offset_lookup[ voice] = temp_var_1 / NR_STEP_PITCH;
303 delta = temp_var_1 % NR_STEP_PITCH;
304 }
305 oldPtr = pointer_to_current_freqs[ voice] = (unsigned *) micro_freq_lookup[ delta];
306 oldL = pitchBend;
307 }
308}
309
310
311/* set_a_param_in_a_slot(slot, cParam)
312 * saves the pointer to the patch data and calls
313 * set_all_the_params_in_a_slot() to fill the operator with the data
314 * called for each slot in a voice from update_pgm() in DO.C when a note
315 * is going to be played in a voice which last played a diifernt patch
316 */
317void FMSYNTH::set_a_param_in_a_slot(unsigned slot, char * cParam)
318{
319 slot_parameter_map[ slot ] = cParam;
320 set_all_the_params_in_a_slot( slot);
321}
322
323/* static int slot_to_voice_map(int slot)
324 * used to get the voice for SndOutputVx()
325 * so set_all_the_params_in_a_slot()
326 * can set the ModFeedback and other Vx register stuff
327 */
328static int slot_to_voice_map(int slot)
329{
330return slot_to_voice_map2[slot];
331}
332
333
334/* static int slot_is_modulator(int slot)
335 * used by set_all_the_params_in_a_slot() to decide if it should
336 * set the ModFeedback and other Vx register stuff
337 */
338static int slot_is_modulator(int slot)
339{
340 return (slot_is_mod[slot]);
341}
342
343/* static void SndOutputSlot(int addr, int slot, int val)
344 * write to a register on the FM chip (or chips for DUAL FM)
345 * slot is our internal logical slot number
346 */
347void FMSYNTH::SndOutputSlot(int addr, int slot, int val)
348{
349 if (slot < (NUM_SLOTS_HALF))
350 SndOutput1(addr + slot_offset_map2[slot], val);
351 else
352 SndOpl3_2_1(addr + slot_offset_map2[slot - NUM_SLOTS_HALF], val);
353}
354
355
356/* void SndOutputVx()
357 * Write to a Vx registers on the chip (or chips if DUAL FM)
358 */
359void FMSYNTH::SndOutputVx(int addr, int vx, int val)
360{
361 int div = vx / 3;
362 int adr = addr + voice_to_Vx[div] + (vx - div * 3);
363 if (second_chip[div])
364 SndOpl3_2_1(adr, val);
365 else
366 SndOutput1(adr, val);
367}
368
369/* SetVoicePan_and_FB()
370 * put the PAN bits in the Vx register with the CONFIG bit
371 * this must be done to 2 registers per 4OP voice if OPL3_FM
372 */
373void FMSYNTH::SetVoicePan_and_FB(int voice, char pan) /* FeedBack field and C = ! FM */
374{
375 char * slot;
376
377 if (percussion && (voice >= BD))
378 {
379 if (voice != BD) // trying to fix Bass, LMH 8/92
380 return;
381 slot = perc_slot_map[0]; // needed for 4OP driver
382 }
383 else
384 slot = voice_slot_map[voice];
385 SndOutputVx( 0xC0 , voice,
386 ((FETCH_PARAM(slot[0], prmFeedBack) & 0x0F) ^ 1) | pan);
387 if (voice <= MAX_PERC_MODE_VX) // percussive mode too
388 SndOutputVx( 0xC0 , voice + 12,
389 ((FETCH_PARAM(slot[2], prmFeedBack) & 0x0F) ^ 1) | pan);
390}
391
392/* set_all_the_params_in_a_slot(slot)
393 * fills the slot (operator) with the pointer to the patch data
394 */
395void FMSYNTH::set_all_the_params_in_a_slot(int slot)
396{
397 register SLOT_PARAM * slot_ptr = slot_parameter_map[slot] ;
398
399 /* will be incremented for COMPRESS */
400 send_modulation_params();
401 SndOutputSlot( 0x20 , slot, * slot_ptr++);
402 set_the_ksl_in_a_slot( slot);
403 slot_ptr++;
404
405 SndOutputSlot( 0x60 , slot, * slot_ptr++); /* set up the attack and dec */
406 SndOutputSlot( 0x80 , slot, * slot_ptr++); /* set up the sustain and release */
407
408 if (slot_is_modulator(slot) == 2) // if OP3
409 SetVoicePan_and_FB(slot_to_voice_map(slot), 0x30); // turn it on
410
411 /* FeedBack and Wave Sel in same byte */
412 SndOutputSlot( 0xE0 , slot, * slot_ptr >> 5);
413}
414
415
416/* set_the_ksl_in_a_slot(slot)
417 * calculate the volume for the slot and put it in the chip with
418 * the KeyboardScaLing
419 */
420void FMSYNTH::set_the_ksl_in_a_slot(int slot)
421{
422 unsigned temp_var_1;
423
424 temp_var_1 = vol_of_this_slot[slot] *
425 ( ~ FETCH_PARAM(slot, prmLevel) & 0x3f) + 1;
426 temp_var_1 = 63 - (temp_var_1 / MAX_VOLUME);
427 temp_var_1 |= FETCH_PARAM( slot, prmKsl) & 0xC0;
428 SndOutputSlot( 0x40 , slot, temp_var_1);
429}
430
431/* send_modulation_params()
432 * Set the AM Depth, VIB depth & Rhythm for the whole chip
433 */
434void FMSYNTH::send_modulation_params(void)
435{
436 unsigned temp_var_1;
437
438 temp_var_1 = 0x80 | 0x40; /* max mod 7/17/90 bgf */
439 if (percussion)
440 temp_var_1 |= 0x20;
441 temp_var_1 |= percussion_enable_bits;
442 SndOutput1( 0xBD, temp_var_1); //???????? for dual mode?
443}
444
445/* SetFreq(voice, pitch)
446 Change pitch of voices 0 to 8, for melodic or percussive mode.
447 * save off the given MIDI note num and combine it with the
448 * pitch bend and key-on bit and write it all to the Vx register
449*/
450void FMSYNTH::SetFreq(unsigned voice, int pitch)
451{
452 unsigned int local_temp;
453 static unsigned int temp_freq = 0;
454
455 current_voice_pitch[ voice] = (char) pitch;
456 pitch += semi_offset_lookup[ voice];
457 if( pitch > 95)
458 pitch = 95;
459 if( pitch < 0)
460 pitch = 0;
461 temp_freq = * ( pointer_to_current_freqs[ voice] + note_semi_map[ pitch]);
462 SndOutputVx( 0xA0, voice, temp_freq);
463 local_temp = key_on_voice[ voice] ? 32 : 0;
464 local_temp += ( (unsigned)note_octave_map[ pitch] << 2) + ( 0x3 & HIGH_BYTE( temp_freq) );
465 SndOutputVx( 0xB0 , voice, local_temp);
466}
467
468
469/* SoundChut(voice)
470 Set the frequency of voice 'voice' to 0 hertz.
471*/
472void FMSYNTH::SoundChut(int voice)
473{
474 SndOutputVx( 0xA0 ,voice, 0);
475 SndOutputVx( 0xB0 ,voice, 0);
476}
477
478
479/* SetOplMode(mode)
480 * do global FM chip stuff
481 * 0 = melodic
482 * 1 = perc
483 */
484void FMSYNTH::SetOplMode(int mode)
485{
486 if( mode)
487 { /* changing into percussion mode */
488 SoundChut( BD); /* turn off these operators */
489 key_on_voice[BD] = 0;
490 SoundChut( SD);
491 SoundChut( TOM);
492 key_on_voice[TOM] = 0;
493 SetFreq( TOM, TOM_PITCH); /* set the frequency for the last 4 percussion voices */
494 key_on_voice[SD] = 0;
495 SetFreq( SD, SD_PITCH);
496 }
497 percussion = (char) mode; /* set global var */
498 percussion_enable_bits = 0; /* zero all percussion bits (turn off all perc voices) */
499 SndOpl3_2_1( 4, 0x3F); /* 4 OP me */
500
501 InitSlotParams(); /* load all operators with default voices */
502 send_modulation_params(); /* send rhythm voice bits to turn 'em off */
503}
504
505/* SoundColdInit()
506 Must be called for start-up initialization.
507 Return 0 if harware not found.
508 what about dual mode? @@@@@@@@@@@@@@@
509*/
510void FMSYNTH::SoundColdInit()
511{
512 int i;
513
514 SndOpl3_2_1( 5, 1); /* set NEW bit */
515
516 InitFNums();
517 SetOplMode( DEF_PERC_MODE); /* melodic mode?? */
518 send_modulation_params(); /* these routines prepare bytes according to glob vars */
519 SndOutput1( 0x08, 0); /* set the note select */
520 for( i = 0 ; i < number_of_operators; i++)
521 SoundChut( i);
522 set_pitch_range(sapi_pitch_range); /* default pitch range is 2 half-tone */
523 for( i = 0; i < num_slots; i++)
524 SndOutputSlot( 0xE0 , i, 0); /* choose normal sine wave for all operators */
525}
526
527 /* how do I tell if slot is REALLY a carrier for given config, */
528 /* bit 0 - OP1, bit 1 - OP2, bit 2 - OP3, bit 3 - OP4 */
529static char config_map[4] = { 0x0D, 0x09, 0x0A, 0x0A };
530 /* cfg 1 -OP1,OP3,OP4... ^should be 8, but make 2OP DRUMS work */
531
532/* SetVoiceVolume(voice, volume)
533 * set the volume of all the operators playing a voice
534 * set the modulators to MAX and carriers to volume given
535 * so that the timbre stays the same for different volumes
536 * for SUPER_SAPI this is configurable.
537 */
538void FMSYNTH::SetVoiceVolume(int voice, unsigned volume)
539{
540 int i, opcfg; char * slot;
541 if (percussion && (voice >= BD))
542 {
543 slot = perc_slot_map[voice -BD];
544 opcfg = (voice == BD) ? 2 : 1; // yes, set volume of DRUMS
545 }
546 else
547 {
548 slot = voice_slot_map[voice];
549 opcfg = ((FETCH_PARAM(slot[0], prmFm) & 1) << 1)
550 + (FETCH_PARAM(slot[2], prmFm) & 1);
551 opcfg = (int)config_map[opcfg];
552 }
553 i = 0;
554 do {
555 vol_of_this_slot[slot[i]] = (opcfg & (1 << i)) ? (char)volume : (char)MAX_VOLUME;
556 set_the_ksl_in_a_slot(slot[i]); /* send to chip */
557
558 // dp2("set vol op, vol", slot[i], vol_of_this_slot[slot[i]]);
559 i ++; // loop 4 times for melodic voices
560 } while ((i < 4) && (slot[0] != slot[i])); // 2 times for base or 2OP drums, else once
561}
562
563/* _NoteOn(voice, pitch)
564 * set the key-on bit and put it in the chip or set drum-mode-drum bits
565 */
566void FMSYNTH::_NoteOn(unsigned voice, int pitch)
567{
568 if (pitch < 0)
569 pitch = drum_pitch_array[last_pgm[voice] -DRUM_PATCH_OFFSET];
570 pitch -= ( MID_C - CHIP_MID_C);
571
572 if( pitch < 0)
573 pitch = 0;
574
575 if (percussion && (voice >= BD))
576 { /* this is a percussive voice */
577 if (voice == BD)
578 SetFreq(BD, pitch);
579 percussion_enable_bits |= percussion_enable_bit_masks[ voice - BD]; /* OR in the on bit for perc voice */
580 send_modulation_params();
581 }
582 else
583 {
584 key_on_voice[voice] = 1;
585 SetFreq(voice, pitch);
586 }
587}
588
589
590/* _NoteOff(voice)
591 * clear key-on bit or drum-mode-drum bits
592 */
593void FMSYNTH::_NoteOff(unsigned voice)
594{
595if (percussion && (voice >= BD))
596 {
597 percussion_enable_bits &= ~percussion_enable_bit_masks[ voice - BD];
598 send_modulation_params();
599 }
600else
601 {
602 key_on_voice[voice] = 0; /* shut off */
603 SetFreq((unsigned) voice, current_voice_pitch[voice]);
604 }
605}
606
Note: See TracBrowser for help on using the repository browser.