source: cmedia/trunk/Drv16/fmdo.cpp@ 553

Last change on this file since 553 was 553, checked in by rudi, 14 years ago

Adapt sourcecode to OpenWatcom

File size: 18.1 KB
Line 
1/* SCCSID = src/dev/mme/tropez/fmdo.cpp, tropez, c.basedd 97/07/17 */
2/****************************************************************************
3 * *
4 * Copyright (c) IBM Corporation 1994 - 1997. *
5 * Copyright (c) Voyetra Technologies 1990-1993. All rights reserved *
6 * *
7 * The following IBM OS/2 source code is provided to you solely for the *
8 * the purpose of assisting you in your development of OS/2 device drivers. *
9 * You may use this code in accordance with the IBM License Agreement *
10 * provided in the IBM Device Driver Source Kit for OS/2. *
11 * *
12 ****************************************************************************/
13/**@internal src/dev/mme/tropez/fmdo.cpp, tropez, c.basedd
14 * Entry points for public workers for the FMSYNTH object.
15 * @version 1.2
16 * @context
17 * Unless otherwise noted, all interfaces are Ring-0, 16-bit, kernel stack.
18 * @notes
19 * @history
20 */
21
22extern "C" {
23#include <os2.h>
24#include <os2medef.h>
25#include <ssm.h>
26#include <audio.h>
27#include <meerror.h>
28#include "fmadlib.h"
29#include "fmglobal.h"
30}
31#include "fmsynth.hpp"
32
33#include "4_cmp_p.c" // re-compiling the patch maker
34
35/******************* Misc. declarations and defines ****************/
36
37int percussion_channel = DEF_PERCUSSION_CHANNEL;
38
39static long the_bend_val;
40 /* when a midi message affects all voices playing on a channel */
41 /* the driver calls for_all_ops_on_channel() and hands pointer */
42 /* to a function to be called for each operator. (the little guys) */
43
44/********* HERE ARE SOME LOOKUP TABLES TO MAP MIDI PITCHES TO DRUMS *********/
45
46#define MIN_DRUM_PITCH 35
47#define MAX_DRUM_PITCH 81
48static BYTE xlate_drums[MAX_DRUM_PITCH - MIN_DRUM_PITCH +1] = {
49 BD,
50 BD, // 36
51 0, // 37
52 SD, // 38
53 0, // 39
54 0, // 40 0 means DRUM_2OP
55 0, // 41
56 HIHAT, // 42
57 0, // 43
58 HIHAT, // 44
59 0, // 45
60 0, // 46
61 0, // 47
62 0, // 48
63 CYMB, // 49
64 0, // 50
65 CYMB, // 51
66 0, // 52
67 0, // 53
68 0, // 54
69 0, // 55
70 0, // 56
71 CYMB, // 57
72 0, // 58
73 CYMB, // 59 for Tandy, Grey LMH 8/14/92
74 0, // 60
75 0, // 61
76 0, // 62
77 0, // 63
78 0, // 64
79 0, // 65
80 0, // 66
81 0, // 67
82 0, // 68
83 0, // 69
84 0, // 70
85 0, // 71
86 0, // 72
87 0, // 73
88 0, // 74
89 0, // 75
90 0, // 76
91 0, // 77
92 0, // 78
93 0, // 79
94 0, // 80
95 0 }; // 81
96
97
98/****************** data and function protos for FM cards *******/
99
100CHANNEL_ENTRY channel_stats[NUMBER_OF_CHANNELS] = {0}; /* channel globals */
101signed char coarse_tuning[NUMBER_OF_CHANNELS]; // registered paremeters
102int fine_tuning[NUMBER_OF_CHANNELS];
103long cur_bender[NUMBER_OF_CHANNELS]; // big Global var for easy VIBRO_POLL
104
105char init_pgms[NUMBER_OF_CHANNELS] = { 0 };
106
107/* GLOBAL VARS */
108BYTE last_pgm[MAX_NUMBER_OF_OPERATORS] = {0}; // pgm each operator loaded
109char percussion = DEF_PERC_MODE;
110int gop = 0; // global operator #
111int max_melo_voice = MAX_MELO_MODE_VX; // maximum melodic voices
112int num_slots = NUM_SLOTS_CHIP; // (init to one adlib chip)
113int number_of_operators = NUMBER_OF_OPERATORS_CHIP;
114int fm_mode; // MODE_MELO or MODE_MELO18
115long last_bend[MAX_NUMBER_OF_OPERATORS] = {0};
116 /* pitch bend val of each op */
117
118/* void cur_bend_sub( channel )
119 * Sums up all the Pitch Bend inputs into cur_bender[]
120 */
121static void cur_bend_sub( BYTE mchan )
122{
123cur_bender[mchan] = (long)adlib_pitch_range_step
124 * (signed)(channel_stats[mchan].bender - MID_PITCH)
125 + (long)fine_tuning[mchan] * NR_STEP_PITCH;
126}
127extern char internal_patches[NUMBER_OF_BUILT_IN_VOICES][PATCH_LENGTH];
128
129/******************************************************************/
130/** **/
131/******************* BIG BLOCK OF FM STUFF *****************/
132/* In this module and in ASSIGN.C, an operator is a voice */
133/* In ADLIB.C a voice is 2 (or 4) operators in the FM chip */
134/** **/
135/*****************************************************************/
136
137
138/* noteOn( channel, note, velocity )
139 * Handle a note ON event, implements DRUM CHANNEL
140 * allocates a voice with routines in ASSIGN.C
141 * and uses functions to load FM data int the chip like
142 set_normalized_voice_volume() _NoteOn();
143 set_pitch_of_this_voice() and update_pgm();
144 */
145void FMSYNTH::noteOn( BYTE mchan, BYTE note, BYTE velocity )
146{
147 long bend;
148 int pitch, virt_chan = mchan;
149 BYTE pgm;
150
151 if (!velocity) { // Note On -> Note Off translation
152 noteOff( mchan, note, (BYTE) 0 );
153 return;
154 }
155
156 pitch = note + coarse_tuning[virt_chan];
157
158 if (percussion && virt_chan == PERCUSSION_CHANNEL)
159 {
160 int vx = pitch_to_drum(pitch); // map to drum type (if any)
161 if (vx >= 0) // if there is one
162 {
163 if (vx >= DRUM_PATCH_OFFSET)
164 {
165 add_q2(pitch, virt_chan, &gop);
166 gop += MAX_PERC_MODE_VX +1;
167 if (last_pgm[gop] != (unsigned char )vx) /* if we need to send new dynamic pgm */
168 update_drum_pgm(vx);
169 if (last_bend[gop] != 0) /* if it's different */
170 set_pitch_of_this_voice(gop, (last_bend[gop] = 0));
171 set_normalized_voice_volume(gop, velocity, virt_chan);
172 _NoteOn(gop, -1); // lookup in adlib.c
173 }
174 else
175 {
176 set_normalized_voice_volume( vx, velocity, virt_chan ); /* make drums touch-sensitive */
177 #ifdef FM_MONITOR
178 StringOut("DO: Note On");
179 #endif
180 _NoteOn( vx, MY_BEST_BD_PITCH );
181 }
182 }
183 }
184 else /* melodic voice */
185 {
186 add_q(pitch, virt_chan, &gop); /* update assignment list */
187 {
188 pgm = channel_stats[virt_chan].program; /* this is the program we need in there */
189 if (last_pgm[gop] != pgm) /* if we need to send new dynamic pgm */
190 update_pgm(pgm);
191 }
192 bend = calc_pitch(virt_chan); /* current bender activity on channel */
193 if (last_bend[gop] != bend) /* if it's different */
194 set_pitch_of_this_voice(gop, (last_bend[gop] = bend));
195 set_normalized_voice_volume(gop, velocity, virt_chan); /* always use Note on velocity */
196 _NoteOn(gop, pitch);
197 }
198}
199
200/*************************** the following block of code is for FM ******************/
201
202/* update_pgm()
203 * put a patch into a voice in the chip
204 * here a slot is a real operator in the chip
205 * last_pgm[] is used to save reloading patches
206 */
207void FMSYNTH::update_pgm(int pgm)
208{
209 char * pdata;
210 pdata = internal_patches[pgm];
211
212 set_a_param_in_a_slot(voice_slot_map[gop][0], pdata);
213 pdata += nbLocParam;
214 set_a_param_in_a_slot(voice_slot_map[gop][1], pdata);
215 pdata += nbLocParam;
216 set_a_param_in_a_slot(voice_slot_map[gop][2], pdata);
217 pdata += nbLocParam;
218 set_a_param_in_a_slot(voice_slot_map[gop][3], pdata);
219 last_pgm[gop] = (char) pgm;
220}
221
222
223/* update_drum_pgm()
224 * put a 2op drum patch into a voice in the chip
225 * here a slot is a real operator in the chip
226 */
227void FMSYNTH::update_drum_pgm(int pgm)
228{
229 char * pdata;
230
231 pdata = internal_drums[pgm - DRUM_PATCH_OFFSET];
232 set_a_param_in_a_slot(voice_slot_map[gop][0], pdata);
233 pdata += nbLocParam;
234 set_a_param_in_a_slot(voice_slot_map[gop][1], pdata);
235 last_pgm[gop] = (char) pgm;
236}
237
238/************************* the non-asc version of do_note_off follows *************/
239
240/* noteOff( channel, note, velocity )
241 * Handle a note OFF event, de-allocates the voice, lets the chip release
242 * Does not use the 'velocity' paramater.
243 */
244void FMSYNTH::noteOff( BYTE mchan, BYTE note, BYTE velocity )
245{
246 int pitch = note + coarse_tuning[mchan];
247 BYTE dummy = velocity; // Supress compiler warning on vbl not used.
248
249 if (percussion && mchan == PERCUSSION_CHANNEL)
250 {
251 int vx = pitch_to_drum(pitch);
252 if (vx >= 0)
253 {
254 if (vx >= DRUM_PATCH_OFFSET)
255 {
256 if (remove_target2(pitch, mchan, &gop))
257 _NoteOff(gop + MAX_PERC_MODE_VX +1);
258 }
259 else
260 _NoteOff(vx);
261 }
262 }
263 else
264 {
265 if (remove_target(pitch, mchan, &gop))
266 _NoteOff(gop);
267 }
268}
269
270/* programChange( channel )
271 * Handle program changes. implements drum channel switchs
272 * sets channel_stats[] vars but not notes playing
273 * next note-on on the channel will get the new patch
274 */
275void FMSYNTH::programChange( BYTE mchan, BYTE program_number )
276{
277 if ( ((mchan == percussion_channel) || (mchan == DEF_PERCUSSION_CHANNEL)) &&
278 (program_number >= MIN_PGM_FOR_CTRL))
279 {
280 int perc;
281 switch ( program_number )
282 {
283 case 126: // drum mode
284 perc = 1;
285 max_melo_voice = MAX_PERC_MODE_VX;
286 num_slots = NUM_SLOTS_CHIP;
287 number_of_operators = NUMBER_OF_OPERATORS_CHIP; //### BUG? same assignment both cases
288 fm_mode = MODE_PERC;
289 break;
290
291 case 127: // melod mode
292 perc = 0; // no perc in this mode
293 max_melo_voice = MAX_MELO_MODE_VX; // double the number of voices
294 num_slots = NUM_SLOTS_CHIP;
295 number_of_operators = NUMBER_OF_OPERATORS_CHIP; //### BUG? same assignment both cases
296 fm_mode = MODE_MELO;
297 break;
298 }
299 do_reset(0);
300 SetOplMode(perc);
301 }
302 else
303 {
304 if (program_number < NUMBER_OF_BUILT_IN_VOICES)
305 channel_stats[mchan].program = (char) program_number;
306 }
307}
308
309
310/**************** The following is the FM version of the volume/pan/velocity routines *********/
311
312static char pan_me_bits[4] = { 0x10, 0x30, 0x30, 0x20 };
313
314/* void set_normalized_voice_volume(int voice, int vel)
315 * Sum up components of the final note volume, Pan on OPL3 or Stereo FM
316 * 7/30/90 to make vel work ok
317 * 10/24/90 variable comp ratio
318 */
319
320
321void FMSYNTH::set_normalized_voice_volume(int voice, int vel, int chan)
322{
323 int temp;
324
325 SetVoicePan_and_FB(voice, pan_me_bits[channel_stats[chan].pan >> 5]);
326 temp = vel >> 1; // squish the range down
327 temp += channel_stats[chan].volume >> 1; // add in the volume!
328 temp &= 0x7f; //if (temp > 127)temp = 127; // boost range back up to max
329 SetVoiceVolume(voice, temp);
330}
331
332/************************** The following is the registered parameter / data entry code ************/
333
334/* Theese messages are nasty to parse */
335static int reg_param_lsb = 0;
336static int reg_param_msb = 0;
337static int ready_reg = 0;
338static int last_reg_val_sent = 0;
339
340/* void handle_data_entry( mchan )
341 */
342void FMSYNTH::handle_data_entry( BYTE mchan )
343{
344 // set the variables, do pitch bend
345 if ((ready_reg)&&(!reg_param_msb))
346 switch (reg_param_lsb)
347 {
348 case 0: set_pitch_range(last_reg_val_sent >> 7);
349 break;
350 case 1: fine_tuning[mchan] = last_reg_val_sent - MID_PITCH;
351 cur_bend_sub( mchan );
352 break;
353 case 2: coarse_tuning[mchan] = (signed char)((last_reg_val_sent >> 7) - 64);
354 break;
355 }
356}
357
358
359/* controlChange( channel, number, value )
360 * Handle controller events. the Bx .. .. cases
361 * store controllers like MIDI Volume and Pan, do controller reset
362 */
363void FMSYNTH::controlChange( BYTE mchan, BYTE control_number, BYTE control_value )
364{
365 switch ( control_number )
366 {
367 case 6: // last_reg_val_sent &= 0x007F;
368 last_reg_val_sent = control_value << 7; // data entry MSB
369 set_it:
370 handle_data_entry( mchan );
371 break;
372
373 case 38: last_reg_val_sent &= 0x3F80;
374 last_reg_val_sent |= control_value; // data entry LSB
375 goto set_it;
376
377 case 96: last_reg_val_sent ++;
378 goto set_it;
379 case 97: last_reg_val_sent --;
380 goto set_it;
381
382 case 7: channel_stats[mchan].volume = control_value; // master vol ctrl MSB
383 break;
384 case 10: channel_stats[mchan].pan = control_value; // pan MSB
385 break;
386 case 64: if ((channel_stats[mchan].sus_pedal = control_value) == 0)
387 { // Sustain LMH 5-22-92
388 for_all_ops_on_chan(mchan, ID_do_sus_off);
389 }
390 break;
391 case 98 :
392 case 99 : ready_reg = 0;
393 break;
394 case 100: reg_param_lsb = control_value; // save value
395 goto set_2;
396 case 101: reg_param_msb = control_value; // save value
397 set_2: ready_reg = 1; // note reg or not
398 break;
399 case 123: if ( control_value == 0) /* handle all-notes-off */
400 for_all_ops_on_chan(mchan, ID_shut_off_chan_voice);
401 break;
402 case 124:
403 init_chan_etc();
404 break;
405 }
406}
407
408
409/* do_handle_pitch_bend(op)
410 * Change the pitch of operator op. the little guy called for each voice
411 * if VIBRO_POLL then mchan is used to get the bend of each voice
412 * else the bend val is the same for each voice
413 */
414void FMSYNTH::do_handle_pitch_bend(int op)
415{
416 set_pitch_of_this_voice((unsigned) op, the_bend_val);
417 last_bend[op] = the_bend_val; // this saves time when not VIBRO_POLL ing
418}
419
420/* pitchBend( BYTE mchan )
421 * Handle pitch bends, Ex nn nn messages, the big guy
422 */
423void FMSYNTH::pitchBend( BYTE mchan, BYTE value_lsb, BYTE value_msb )
424{
425 channel_stats[mchan].bender = (((unsigned) value_msb) << 7) + (unsigned) value_lsb;
426 cur_bend_sub( mchan ); // update cur_bender[]
427 the_bend_val = calc_pitch(mchan); /* same for each voice on chan */
428 for_all_ops_on_chan(mchan, ID_do_handle_pitch_bend); /* update all playing voices */
429}
430
431
432/*------------------------------------------------------------------------*/
433
434/* int pitch_to_drum(int pitch)
435 *
436 * accepts a midi pitch, returns a drum define (for Adlib)
437 */
438int FMSYNTH::pitch_to_drum(int pitch)
439{
440 signed int vx = -1; // assume no drum
441 if (pitch >= MIN_DRUM_PITCH && pitch <= MAX_DRUM_PITCH) // if in range of pitches that our table spans
442 {
443 vx = xlate_drums[pitch - MIN_DRUM_PITCH]; // get the drum mapped to this pitch
444 if (vx == 0) // voice or patch ?
445 vx = pitch - (MIN_DRUM_PITCH +1) + DRUM_PATCH_OFFSET;
446 }
447 return vx;
448}
449
450/*----------------------------------------------------------------------*/
451
452/* init_chan_etc()
453 * This routine sets all the channel parameters to default values
454 */
455void FMSYNTH::init_chan_etc(void)
456{
457 register int i;
458
459 for (i = 0; i < NUMBER_OF_CHANNELS; i++)
460 {
461 channel_stats[i].program = init_pgms[i];
462 channel_stats[i].volume = (char) 0x7f;
463 channel_stats[i].bender = MID_PITCH;
464 channel_stats[i].sus_pedal = 0;
465 channel_stats[i].pan = 64;
466 coarse_tuning[i] = 0;
467 fine_tuning[i] = 0; // new pitch bend vars
468 cur_bender[i] = 0; // hold the final LONG
469 }
470 for (i = 0; i < MAX_NUMBER_OF_OPERATORS; i++)
471 {
472 //last_bend[i] = 0x0000; this value is now scaled around 0
473 last_pgm[i] = (char) 0xff;
474 }
475}
476
477/**@internal polyphonicPressure
478 * Standard MIDI channel status command - unimplemented.
479 */
480void FMSYNTH::polyphonicPressure( BYTE mchan, BYTE note, BYTE value )
481{
482 BYTE dummy; // Suppress warnings about parms not used.
483 dummy = mchan;
484 dummy = note;
485 dummy = value;
486}
487
488/**@virtual void channelPressure
489 * Standard MIDI channel status command - unimplemented.
490 */
491void FMSYNTH::channelPressure( BYTE mchan, BYTE value )
492{
493 BYTE dummy; // Suppress warnings about parms not used.
494 dummy = mchan;
495 dummy = value;
496}
Note: See TracBrowser for help on using the repository browser.