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

Last change on this file since 354 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: 18.2 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 */
480virtual void 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 */
491virtual void FMSYNTH::channelPressure( BYTE mchan, BYTE value )
492{
493 BYTE dummy; // Suppress warnings about parms not used.
494 dummy = mchan;
495 dummy = value;
496}
497
Note: See TracBrowser for help on using the repository browser.