2017-04-17 10:27:19 +00:00
//-----------------------------------------------------------------------------
//
// Copyright 2002-2016 Randy Heit
// Copyright 2005-2014 Simon Howard
// Copyright 2017 Christoph Oelckers
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//-----------------------------------------------------------------------------
//
// OPL IO interface. Partly built from the non-MusLib code in the old version
// plus some additions from Chocolate Doom.
//
2017-04-15 23:36:53 +00:00
2017-04-17 07:27:11 +00:00
# include <math.h>
2017-04-15 23:36:53 +00:00
# include "genmidi.h"
# include "oplio.h"
# include "opl.h"
# include "c_cvars.h"
# include "templates.h"
const double HALF_PI = ( M_PI * 0.5 ) ;
EXTERN_CVAR ( Int , opl_core )
extern int current_opl_core ;
OPLio : : ~ OPLio ( )
{
}
void OPLio : : SetClockRate ( double samples_per_tick )
{
}
void OPLio : : WriteDelay ( int ticks )
{
}
//----------------------------------------------------------------------------
//
// Initialize OPL emulator
//
//----------------------------------------------------------------------------
int OPLio : : Init ( uint32_t numchips , bool stereo , bool initopl3 )
{
assert ( numchips > = 1 & & numchips < = countof ( chips ) ) ;
uint32_t i ;
IsOPL3 = ( current_opl_core = = 1 | | current_opl_core = = 2 | | current_opl_core = = 3 ) ;
memset ( chips , 0 , sizeof ( chips ) ) ;
if ( IsOPL3 )
{
numchips = ( numchips + 1 ) > > 1 ;
}
for ( i = 0 ; i < numchips ; + + i )
{
OPLEmul * chip = IsOPL3 ? ( current_opl_core = = 1 ? DBOPLCreate ( stereo ) : ( current_opl_core = = 2 ? JavaOPLCreate ( stereo ) : NukedOPL3Create ( stereo ) ) ) : YM3812Create ( stereo ) ;
if ( chip = = NULL )
{
break ;
}
chips [ i ] = chip ;
}
NumChips = i ;
NumChannels = i * ( IsOPL3 ? OPL3_NUM_VOICES : OPL_NUM_VOICES ) ;
WriteInitState ( initopl3 ) ;
return i ;
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void OPLio : : WriteInitState ( bool initopl3 )
{
for ( uint32_t k = 0 ; k < NumChips ; + + k )
{
int chip = k < < ( int ) IsOPL3 ;
if ( IsOPL3 & & initopl3 )
{
WriteRegister ( chip , OPL_REG_OPL3_ENABLE , 1 ) ;
WriteRegister ( chip , OPL_REG_4OPMODE_ENABLE , 0 ) ;
}
WriteRegister ( chip , OPL_REG_WAVEFORM_ENABLE , WAVEFORM_ENABLED ) ;
WriteRegister ( chip , OPL_REG_PERCUSSION_MODE , 0 ) ; // should be the default, but cannot verify for some of the cores.
}
// Reset all channels.
for ( uint32_t k = 0 ; k < NumChannels ; k + + )
{
MuteChannel ( k ) ;
WriteValue ( OPL_REGS_FREQ_2 , k , 0 ) ;
}
}
//----------------------------------------------------------------------------
//
// Deinitialize emulator before shutdown
//
//----------------------------------------------------------------------------
void OPLio : : Reset ( void )
{
for ( size_t i = 0 ; i < countof ( chips ) ; + + i )
{
if ( chips [ i ] ! = NULL )
{
delete chips [ i ] ;
chips [ i ] = NULL ;
}
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void OPLio : : WriteRegister ( int chipnum , uint32_t reg , uint8_t data )
{
if ( IsOPL3 )
{
reg | = ( chipnum & 1 ) < < 8 ;
chipnum > > = 1 ;
}
if ( chips [ chipnum ] ! = nullptr )
{
chips [ chipnum ] - > WriteReg ( reg , data ) ;
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void OPLio : : WriteValue ( uint32_t regbase , uint32_t channel , uint8_t value )
{
WriteRegister ( channel / OPL_NUM_VOICES , regbase + ( channel % OPL_NUM_VOICES ) , value ) ;
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
static const int voice_operators [ OPL_NUM_VOICES ] = { 0x00 , 0x01 , 0x02 , 0x08 , 0x09 , 0x0a , 0x10 , 0x11 , 0x12 } ;
void OPLio : : WriteOperator ( uint32_t regbase , uint32_t channel , int index , uint8_t data2 )
{
WriteRegister ( channel / OPL_NUM_VOICES , regbase + voice_operators [ channel % OPL_NUM_VOICES ] + 3 * index , data2 ) ;
}
//----------------------------------------------------------------------------
//
// Write frequency/octave/keyon data to a channel
//
// [RH] This is totally different from the original MUS library code
// but matches exactly what DMX does. I haven't a clue why there are 284
// special bytes at the beginning of the table for the first few notes.
// That last byte in the table doesn't look right, either, but that's what
// it really is.
//
//----------------------------------------------------------------------------
static const uint16_t frequencies [ ] = { // (this is the table from Chocolate Doom, which contains the same values as ZDoom's original one but is better formatted.
0x133 , 0x133 , 0x134 , 0x134 , 0x135 , 0x136 , 0x136 , 0x137 , // -1
0x137 , 0x138 , 0x138 , 0x139 , 0x139 , 0x13a , 0x13b , 0x13b ,
0x13c , 0x13c , 0x13d , 0x13d , 0x13e , 0x13f , 0x13f , 0x140 ,
0x140 , 0x141 , 0x142 , 0x142 , 0x143 , 0x143 , 0x144 , 0x144 ,
0x145 , 0x146 , 0x146 , 0x147 , 0x147 , 0x148 , 0x149 , 0x149 , // -2
0x14a , 0x14a , 0x14b , 0x14c , 0x14c , 0x14d , 0x14d , 0x14e ,
0x14f , 0x14f , 0x150 , 0x150 , 0x151 , 0x152 , 0x152 , 0x153 ,
0x153 , 0x154 , 0x155 , 0x155 , 0x156 , 0x157 , 0x157 , 0x158 ,
// These are used for the first seven MIDI note values:
0x158 , 0x159 , 0x15a , 0x15a , 0x15b , 0x15b , 0x15c , 0x15d , // 0
0x15d , 0x15e , 0x15f , 0x15f , 0x160 , 0x161 , 0x161 , 0x162 ,
0x162 , 0x163 , 0x164 , 0x164 , 0x165 , 0x166 , 0x166 , 0x167 ,
0x168 , 0x168 , 0x169 , 0x16a , 0x16a , 0x16b , 0x16c , 0x16c ,
0x16d , 0x16e , 0x16e , 0x16f , 0x170 , 0x170 , 0x171 , 0x172 , // 1
0x172 , 0x173 , 0x174 , 0x174 , 0x175 , 0x176 , 0x176 , 0x177 ,
0x178 , 0x178 , 0x179 , 0x17a , 0x17a , 0x17b , 0x17c , 0x17c ,
0x17d , 0x17e , 0x17e , 0x17f , 0x180 , 0x181 , 0x181 , 0x182 ,
0x183 , 0x183 , 0x184 , 0x185 , 0x185 , 0x186 , 0x187 , 0x188 , // 2
0x188 , 0x189 , 0x18a , 0x18a , 0x18b , 0x18c , 0x18d , 0x18d ,
0x18e , 0x18f , 0x18f , 0x190 , 0x191 , 0x192 , 0x192 , 0x193 ,
0x194 , 0x194 , 0x195 , 0x196 , 0x197 , 0x197 , 0x198 , 0x199 ,
0x19a , 0x19a , 0x19b , 0x19c , 0x19d , 0x19d , 0x19e , 0x19f , // 3
0x1a0 , 0x1a0 , 0x1a1 , 0x1a2 , 0x1a3 , 0x1a3 , 0x1a4 , 0x1a5 ,
0x1a6 , 0x1a6 , 0x1a7 , 0x1a8 , 0x1a9 , 0x1a9 , 0x1aa , 0x1ab ,
0x1ac , 0x1ad , 0x1ad , 0x1ae , 0x1af , 0x1b0 , 0x1b0 , 0x1b1 ,
0x1b2 , 0x1b3 , 0x1b4 , 0x1b4 , 0x1b5 , 0x1b6 , 0x1b7 , 0x1b8 , // 4
0x1b8 , 0x1b9 , 0x1ba , 0x1bb , 0x1bc , 0x1bc , 0x1bd , 0x1be ,
0x1bf , 0x1c0 , 0x1c0 , 0x1c1 , 0x1c2 , 0x1c3 , 0x1c4 , 0x1c4 ,
0x1c5 , 0x1c6 , 0x1c7 , 0x1c8 , 0x1c9 , 0x1c9 , 0x1ca , 0x1cb ,
0x1cc , 0x1cd , 0x1ce , 0x1ce , 0x1cf , 0x1d0 , 0x1d1 , 0x1d2 , // 5
0x1d3 , 0x1d3 , 0x1d4 , 0x1d5 , 0x1d6 , 0x1d7 , 0x1d8 , 0x1d8 ,
0x1d9 , 0x1da , 0x1db , 0x1dc , 0x1dd , 0x1de , 0x1de , 0x1df ,
0x1e0 , 0x1e1 , 0x1e2 , 0x1e3 , 0x1e4 , 0x1e5 , 0x1e5 , 0x1e6 ,
0x1e7 , 0x1e8 , 0x1e9 , 0x1ea , 0x1eb , 0x1ec , 0x1ed , 0x1ed , // 6
0x1ee , 0x1ef , 0x1f0 , 0x1f1 , 0x1f2 , 0x1f3 , 0x1f4 , 0x1f5 ,
0x1f6 , 0x1f6 , 0x1f7 , 0x1f8 , 0x1f9 , 0x1fa , 0x1fb , 0x1fc ,
0x1fd , 0x1fe , 0x1ff , 0x200 , 0x201 , 0x201 , 0x202 , 0x203 ,
// First note of looped range used for all octaves:
0x204 , 0x205 , 0x206 , 0x207 , 0x208 , 0x209 , 0x20a , 0x20b , // 7
0x20c , 0x20d , 0x20e , 0x20f , 0x210 , 0x210 , 0x211 , 0x212 ,
0x213 , 0x214 , 0x215 , 0x216 , 0x217 , 0x218 , 0x219 , 0x21a ,
0x21b , 0x21c , 0x21d , 0x21e , 0x21f , 0x220 , 0x221 , 0x222 ,
0x223 , 0x224 , 0x225 , 0x226 , 0x227 , 0x228 , 0x229 , 0x22a , // 8
0x22b , 0x22c , 0x22d , 0x22e , 0x22f , 0x230 , 0x231 , 0x232 ,
0x233 , 0x234 , 0x235 , 0x236 , 0x237 , 0x238 , 0x239 , 0x23a ,
0x23b , 0x23c , 0x23d , 0x23e , 0x23f , 0x240 , 0x241 , 0x242 ,
0x244 , 0x245 , 0x246 , 0x247 , 0x248 , 0x249 , 0x24a , 0x24b , // 9
0x24c , 0x24d , 0x24e , 0x24f , 0x250 , 0x251 , 0x252 , 0x253 ,
0x254 , 0x256 , 0x257 , 0x258 , 0x259 , 0x25a , 0x25b , 0x25c ,
0x25d , 0x25e , 0x25f , 0x260 , 0x262 , 0x263 , 0x264 , 0x265 ,
0x266 , 0x267 , 0x268 , 0x269 , 0x26a , 0x26c , 0x26d , 0x26e , // 10
0x26f , 0x270 , 0x271 , 0x272 , 0x273 , 0x275 , 0x276 , 0x277 ,
0x278 , 0x279 , 0x27a , 0x27b , 0x27d , 0x27e , 0x27f , 0x280 ,
0x281 , 0x282 , 0x284 , 0x285 , 0x286 , 0x287 , 0x288 , 0x289 ,
0x28b , 0x28c , 0x28d , 0x28e , 0x28f , 0x290 , 0x292 , 0x293 , // 11
0x294 , 0x295 , 0x296 , 0x298 , 0x299 , 0x29a , 0x29b , 0x29c ,
0x29e , 0x29f , 0x2a0 , 0x2a1 , 0x2a2 , 0x2a4 , 0x2a5 , 0x2a6 ,
0x2a7 , 0x2a9 , 0x2aa , 0x2ab , 0x2ac , 0x2ae , 0x2af , 0x2b0 ,
0x2b1 , 0x2b2 , 0x2b4 , 0x2b5 , 0x2b6 , 0x2b7 , 0x2b9 , 0x2ba , // 12
0x2bb , 0x2bd , 0x2be , 0x2bf , 0x2c0 , 0x2c2 , 0x2c3 , 0x2c4 ,
0x2c5 , 0x2c7 , 0x2c8 , 0x2c9 , 0x2cb , 0x2cc , 0x2cd , 0x2ce ,
0x2d0 , 0x2d1 , 0x2d2 , 0x2d4 , 0x2d5 , 0x2d6 , 0x2d8 , 0x2d9 ,
0x2da , 0x2dc , 0x2dd , 0x2de , 0x2e0 , 0x2e1 , 0x2e2 , 0x2e4 , // 13
0x2e5 , 0x2e6 , 0x2e8 , 0x2e9 , 0x2ea , 0x2ec , 0x2ed , 0x2ee ,
0x2f0 , 0x2f1 , 0x2f2 , 0x2f4 , 0x2f5 , 0x2f6 , 0x2f8 , 0x2f9 ,
0x2fb , 0x2fc , 0x2fd , 0x2ff , 0x300 , 0x302 , 0x303 , 0x304 ,
0x306 , 0x307 , 0x309 , 0x30a , 0x30b , 0x30d , 0x30e , 0x310 , // 14
0x311 , 0x312 , 0x314 , 0x315 , 0x317 , 0x318 , 0x31a , 0x31b ,
0x31c , 0x31e , 0x31f , 0x321 , 0x322 , 0x324 , 0x325 , 0x327 ,
0x328 , 0x329 , 0x32b , 0x32c , 0x32e , 0x32f , 0x331 , 0x332 ,
0x334 , 0x335 , 0x337 , 0x338 , 0x33a , 0x33b , 0x33d , 0x33e , // 15
0x340 , 0x341 , 0x343 , 0x344 , 0x346 , 0x347 , 0x349 , 0x34a ,
0x34c , 0x34d , 0x34f , 0x350 , 0x352 , 0x353 , 0x355 , 0x357 ,
0x358 , 0x35a , 0x35b , 0x35d , 0x35e , 0x360 , 0x361 , 0x363 ,
0x365 , 0x366 , 0x368 , 0x369 , 0x36b , 0x36c , 0x36e , 0x370 , // 16
0x371 , 0x373 , 0x374 , 0x376 , 0x378 , 0x379 , 0x37b , 0x37c ,
0x37e , 0x380 , 0x381 , 0x383 , 0x384 , 0x386 , 0x388 , 0x389 ,
0x38b , 0x38d , 0x38e , 0x390 , 0x392 , 0x393 , 0x395 , 0x397 ,
0x398 , 0x39a , 0x39c , 0x39d , 0x39f , 0x3a1 , 0x3a2 , 0x3a4 , // 17
0x3a6 , 0x3a7 , 0x3a9 , 0x3ab , 0x3ac , 0x3ae , 0x3b0 , 0x3b1 ,
0x3b3 , 0x3b5 , 0x3b7 , 0x3b8 , 0x3ba , 0x3bc , 0x3bd , 0x3bf ,
0x3c1 , 0x3c3 , 0x3c4 , 0x3c6 , 0x3c8 , 0x3ca , 0x3cb , 0x3cd ,
// The last note has an incomplete range, and loops round back to
// the start. Note that the last value is actually a buffer overrun
// and does not fit with the other values.
0x3cf , 0x3d1 , 0x3d2 , 0x3d4 , 0x3d6 , 0x3d8 , 0x3da , 0x3db , // 18
0x3dd , 0x3df , 0x3e1 , 0x3e3 , 0x3e4 , 0x3e6 , 0x3e8 , 0x3ea ,
0x3ec , 0x3ed , 0x3ef , 0x3f1 , 0x3f3 , 0x3f5 , 0x3f6 , 0x3f8 ,
0x3fa , 0x3fc , 0x3fe , 0x36c ,
} ;
void OPLio : : WriteFrequency ( uint32_t channel , uint32_t note , uint32_t pitch , uint32_t keyon )
{
int octave = 0 ;
int j = ( note < < 5 ) + pitch ;
if ( j < 0 )
{
j = 0 ;
}
else if ( j > = 284 )
{
j - = 284 ;
octave = j / ( 32 * 12 ) ;
if ( octave > 7 )
{
octave = 7 ;
}
j = ( j % ( 32 * 12 ) ) + 284 ;
}
int i = frequencies [ j ] | ( octave < < 10 ) ;
WriteValue ( OPL_REGS_FREQ_1 , channel , ( uint8_t ) i ) ;
WriteValue ( OPL_REGS_FREQ_2 , channel , ( uint8_t ) ( i > > 8 ) | ( keyon < < 5 ) ) ;
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
static uint8_t volumetable [ 128 ] = {
0 , 1 , 3 , 5 , 6 , 8 , 10 , 11 ,
13 , 14 , 16 , 17 , 19 , 20 , 22 , 23 ,
25 , 26 , 27 , 29 , 30 , 32 , 33 , 34 ,
36 , 37 , 39 , 41 , 43 , 45 , 47 , 49 ,
50 , 52 , 54 , 55 , 57 , 59 , 60 , 61 ,
63 , 64 , 66 , 67 , 68 , 69 , 71 , 72 ,
73 , 74 , 75 , 76 , 77 , 79 , 80 , 81 ,
82 , 83 , 84 , 84 , 85 , 86 , 87 , 88 ,
89 , 90 , 91 , 92 , 92 , 93 , 94 , 95 ,
96 , 96 , 97 , 98 , 99 , 99 , 100 , 101 ,
101 , 102 , 103 , 103 , 104 , 105 , 105 , 106 ,
107 , 107 , 108 , 109 , 109 , 110 , 110 , 111 ,
112 , 112 , 113 , 113 , 114 , 114 , 115 , 115 ,
116 , 117 , 117 , 118 , 118 , 119 , 119 , 120 ,
120 , 121 , 121 , 122 , 122 , 123 , 123 , 123 ,
124 , 124 , 125 , 125 , 126 , 126 , 127 , 127 } ;
2017-04-16 22:46:27 +00:00
void OPLio : : WriteVolume ( uint32_t channel , struct GenMidiVoice * voice , uint32_t vol1 , uint32_t vol2 , uint32_t vol3 )
2017-04-15 23:36:53 +00:00
{
if ( voice ! = nullptr )
{
uint32_t full_volume = volumetable [ MIN < uint32_t > ( 127 , ( uint32_t ) ( ( uint64_t ) vol1 * vol2 * vol3 ) / ( 127 * 127 ) ) ] ;
int reg_volume2 = ( ( 0x3f - voice - > carrier . level ) * full_volume ) / 128 ;
reg_volume2 = ( 0x3f - reg_volume2 ) | voice - > carrier . scale ;
WriteOperator ( OPL_REGS_LEVEL , channel , 1 , reg_volume2 ) ;
int reg_volume1 ;
if ( voice - > feedback & 0x01 )
{
// Chocolate Doom says:
// If we are using non-modulated feedback mode, we must set the
// volume for both voices.
// Note that the same register volume value is written for
// both voices, always calculated from the carrier's level
// value.
// But Muslib does it differently than the comment above states. Which one is correct?
reg_volume1 = ( ( 0x3f - voice - > modulator . level ) * full_volume ) / 128 ;
reg_volume1 = ( 0x3f - reg_volume1 ) | voice - > modulator . scale ;
}
else
{
reg_volume1 = voice - > modulator . level | voice - > modulator . scale ;
}
WriteOperator ( OPL_REGS_LEVEL , channel , 0 , reg_volume1 ) ;
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
2017-04-16 22:46:27 +00:00
void OPLio : : WritePan ( uint32_t channel , struct GenMidiVoice * voice , int pan )
2017-04-15 23:36:53 +00:00
{
if ( voice ! = 0 )
{
WriteValue ( OPL_REGS_FEEDBACK , channel , voice - > feedback | ( pan > = 28 ? 0x20 : 0 ) | ( pan < = 100 ? 0x10 : 0 ) ) ;
// Set real panning if we're using emulated chips.
int chanper = IsOPL3 ? OPL3_NUM_VOICES : OPL_NUM_VOICES ;
int which = channel / chanper ;
if ( chips [ which ] ! = NULL )
{
// This is the MIDI-recommended pan formula. 0 and 1 are
// both hard left so that 64 can be perfectly center.
double level = ( pan < = 1 ) ? 0 : ( pan - 1 ) / 126.0 ;
chips [ which ] - > SetPanning ( channel % chanper ,
( float ) cos ( HALF_PI * level ) , ( float ) sin ( HALF_PI * level ) ) ;
}
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
2017-04-16 22:46:27 +00:00
void OPLio : : WriteTremolo ( uint32_t channel , struct GenMidiVoice * voice , bool vibrato )
2017-04-15 23:36:53 +00:00
{
int val1 = voice - > modulator . tremolo , val2 = voice - > carrier . tremolo ;
if ( vibrato )
{
if ( voice - > feedback & 1 ) val1 | = 0x40 ;
val2 | = 0x40 ;
}
WriteOperator ( OPL_REGS_TREMOLO , channel , 1 , val2 ) ;
WriteOperator ( OPL_REGS_TREMOLO , channel , 0 , val2 ) ;
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void OPLio : : MuteChannel ( uint32_t channel )
{
WriteOperator ( OPL_REGS_LEVEL , channel , 1 , NO_VOLUME ) ;
WriteOperator ( OPL_REGS_ATTACK , channel , 1 , MAX_ATTACK_DECAY ) ;
WriteOperator ( OPL_REGS_SUSTAIN , channel , 1 , NO_SUSTAIN_MAX_RELEASE ) ;
WriteOperator ( OPL_REGS_LEVEL , channel , 0 , NO_VOLUME ) ;
WriteOperator ( OPL_REGS_ATTACK , channel , 0 , MAX_ATTACK_DECAY ) ;
WriteOperator ( OPL_REGS_SUSTAIN , channel , 0 , NO_SUSTAIN_MAX_RELEASE ) ;
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void OPLio : : LoadOperatorData ( uint32_t channel , int op_index , genmidi_op_t * data , bool max_level , bool vibrato )
{
// The scale and level fields must be combined for the level register.
// For the carrier wave we always set the maximum level.
int level = data - > scale ;
if ( max_level ) level | = 0x3f ;
else level | = data - > level ;
int tremolo = data - > tremolo ;
if ( vibrato ) tremolo | = 0x40 ;
WriteOperator ( OPL_REGS_LEVEL , channel , op_index , level ) ;
WriteOperator ( OPL_REGS_TREMOLO , channel , op_index , tremolo ) ;
WriteOperator ( OPL_REGS_ATTACK , channel , op_index , data - > attack ) ;
WriteOperator ( OPL_REGS_SUSTAIN , channel , op_index , data - > sustain ) ;
WriteOperator ( OPL_REGS_WAVEFORM , channel , op_index , data - > waveform ) ;
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
2017-04-16 22:46:27 +00:00
void OPLio : : WriteInstrument ( uint32_t channel , struct GenMidiVoice * voice , bool vibrato )
2017-04-15 23:36:53 +00:00
{
bool modulating = ( voice - > feedback & 0x01 ) = = 0 ;
// Doom loads the second operator first, then the first.
// The carrier is set to minimum volume until the voice volume
// is set later. If we are not using modulating mode, we must set both to minimum volume.
LoadOperatorData ( channel , 1 , & voice - > carrier , true , vibrato ) ;
LoadOperatorData ( channel , 0 , & voice - > modulator , ! modulating , vibrato & & modulating ) ;
// The feedback register is written by the calling code.
}