From 31f677b73a5a9defa0329bd074c81e77b0d14aab Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Sun, 1 Mar 2015 09:34:54 +0100 Subject: (MESS)-gamete: added noise and envelope emulation [Peter Trauner] --- src/mess/audio/gamate.c | 159 +++++++++++++++++++++++++++------ src/mess/drivers/gamate.c | 214 +++++++++++++++++++++++---------------------- src/mess/includes/gamate.h | 56 +++++++----- 3 files changed, 277 insertions(+), 152 deletions(-) diff --git a/src/mess/audio/gamate.c b/src/mess/audio/gamate.c index 5c682841076..4abb8a5f14b 100644 --- a/src/mess/audio/gamate.c +++ b/src/mess/audio/gamate.c @@ -1,17 +1,22 @@ /*************************************************************************** gamate sound hardware - PeT mess@utanet.at + PeT mess@utanet.at 2015 ***************************************************************************/ #include "emu.h" #include "includes/gamate.h" +#include "ui/ui.h" +enum { ClockDelay=32 }; + // device type definition const device_type GAMATE_SND = &device_creator; - +const int gamate_sound_device::DAConverter[]={ 0, 3, 7, 13, 23, 41, 75, 137, 249, 453, 825, 1499, 2726, 4956, 9011, 16383 }; // (*.55) on the real below index 8 bareless measureable +const UINT8 Mask[]={ 0xff, 0x0f, 0xff, 0x0f, 0xff, 0x0f, 0x1f, 0x3f, 0x1f, 0x1f, 0x1f, 0xff, 0xff, 0xf }; +const int EnvelopeVolumes[]={ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; //************************************************************************** // LIVE DEVICE //************************************************************************** @@ -27,7 +32,6 @@ gamate_sound_device::gamate_sound_device(const machine_config &mconfig, const ch { } - //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- @@ -35,7 +39,7 @@ gamate_sound_device::gamate_sound_device(const machine_config &mconfig, const ch void gamate_sound_device::device_start() { // bind callbacks -// m_irq_cb.bind_relative_to(*owner()); +// m_irq_cb.bind_relative_to(*owner()); memset(m_channels, 0, sizeof(m_channels)); memset(reg, 0, sizeof(reg)); @@ -52,31 +56,94 @@ void gamate_sound_device::sound_stream_update(sound_stream &stream, stream_sampl { stream_sample_t *left=outputs[0], *right=outputs[1]; int i, j; - GAMATE_CHANNEL *channel; - + Tone *channel; + for (i = 0; i < samples; i++, left++, right++) { + noise.pos += noise.step; + while (noise.pos >= 1.0) { + // guess (white noise register taken from supervision) + noise.level = noise.state & 0x40 ? 1 : 0; + bool b1 = (noise.state & 0x40) != 0, b2 = (noise.state & 0x20) != 0; + noise.state=(noise.state<<1)+(b1!=b2?1:0); + noise.pos -= 1; + } + + envelope.pos += envelope.step; + while (envelope.pos >= 1.0) { + envelope.pos -= 1; + envelope.index++; + switch (envelope.control) { + case 0: case 1: case 2: case 3: + case 4: case 5: case 6: case 7: + case 8: case 9: case 0xb: + case 0xd: case 0xf: + if (envelope.index>=ARRAY_LENGTH(EnvelopeVolumes)/2) { + envelope.index=0; + envelope.first=false; + } + break; + default: + if (envelope.index>=ARRAY_LENGTH(EnvelopeVolumes)) { + envelope.index=0; + envelope.first=false; + } + break; + } + + } + *left = 0; *right = 0; for (channel=m_channels, j=0; jsize != 0) { - if (channel->on)//||channel->count) - { - int on = FALSE; - on = channel->pos <= channel->size / 2; - { - INT16 s = on ? channel->volume << 8 : 0; - if (j == 0) - *right += s; - else if (j==1) - *left += s; - else { - *right += s; - *left += s; - } - } + channel->level= channel->pos <= channel->size / 2; + bool l= channel->full_cycle? true: channel->level; + if (!channel->tone) l= l && noise.level; + int volume=0; + if (l) { + if (channel->envelope_on) { + switch (envelope.control) { + case 0: case 1: case 2: case 3: + case 0x9: // one time falling, low + if (envelope.first && channel->level) volume=0xf-EnvelopeVolumes[envelope.index]; + break; + case 4: case 5: case 6: case 7: + case 0xf: // one time rising, low + if (envelope.first && channel->level) volume=EnvelopeVolumes[envelope.index]; + break; + case 8: // falling + if (channel->level) volume=0xf-EnvelopeVolumes[envelope.index]; + break; + case 0xa: // rising, falling + if (channel->level) volume=EnvelopeVolumes[envelope.index]; + break; + case 0xb: // one time falling, high + if (channel->level) volume=envelope.first? 0xf-EnvelopeVolumes[envelope.index]: 0xf; + break; + case 0xc: // rising, low + if (channel->level) volume=envelope.indexlevel) volume=envelope.first? EnvelopeVolumes[envelope.index]: 0xf; + break; + case 0xe: // falling, rising + if (channel->level) volume=0xf-EnvelopeVolumes[envelope.index]; + break; + } + } else { + volume=channel->volume; + } + } + if (j == Right) + *right += Value2Volume(volume); + else if (j==Left) + *left += Value2Volume(volume); + else { + *right += Value2Volume(volume); + *left += Value2Volume(volume); } channel->pos++; if (channel->pos >= channel->size) @@ -94,7 +161,7 @@ WRITE8_MEMBER( gamate_sound_device::device_w ) reg[offset] = data; int chan=-1; - switch (offset) + switch (offset&0xf) { case 0: case 1: @@ -102,11 +169,11 @@ WRITE8_MEMBER( gamate_sound_device::device_w ) case 3: case 4: case 5: - chan=offset/2; + chan=offset/2; size = reg[chan*2] | ((reg[chan*2+1] & 0xf) << 8); if (size) { - m_channels[chan].size= (int) (machine().sample_rate() * (size << 5) / machine().device("maincpu")->unscaled_clock()); + m_channels[chan].size= (int) (machine().sample_rate() * size*ClockDelay / machine().device("maincpu")->unscaled_clock()); } else { @@ -115,13 +182,49 @@ WRITE8_MEMBER( gamate_sound_device::device_w ) m_channels[chan].pos = 0; break; case 6: + size=data&0x1f; + if (size==0) size=1; + noise.step= machine().device("maincpu")->unscaled_clock() / (1.0*ClockDelay*machine().sample_rate()*size); + break; case 7: + m_channels[Right].full_cycle=data&1; + m_channels[Right].tone=data&8; + m_channels[Left].full_cycle=data&2; + m_channels[Left].tone=data&0x10; + m_channels[Both].full_cycle=data&4; + m_channels[Both].tone=data&0x20; + noise.state=1; + noise.pos=0.0; + noise.level=false; + break; case 8: - chan=offset-6; -// m_channels[chan]->on = data & 0x40; -// channel->waveform = (data & 0x30) >> 4; + case 9: + case 0xa: + chan=offset-8; + m_channels[chan].envelope_on = data & 0x10; // buggy aussetzer cube up m_channels[chan].volume = data & 0xf; break; + case 0xb: case 0xc: + size = reg[0xb] | ((reg[0xc]) << 8); + if (size==0) size=1; + envelope.step= machine().device("maincpu")->unscaled_clock() / (1.0*ClockDelay*machine().sample_rate()*size); + break; + case 0xd: + envelope.control=data&0xf; + envelope.pos=0; + envelope.index=0; + envelope.first=true; + break; + } - if (chan!=-1) m_channels[chan].on=m_channels[chan].volume!=0 && m_channels[chan].size>3/* avoid speed loss for unhearable >=23khz*/; + envelope.pos=0; // guess + envelope.index=0; + envelope.first=true; +} + +READ8_MEMBER( gamate_sound_device::device_r ) +{ + UINT8 data=0; + if ((offset&0xf) m_maincpu; @@ -80,13 +84,13 @@ private: required_shared_ptr m_bios; emu_timer *timer1; emu_timer *timer2; - UINT8 bank_multi; + UINT8 bank_multi; }; WRITE8_MEMBER( gamate_state::gamate_cart_protection_w ) { - logerror("%.6f protection write %x %x address:%x data:%x shift:%d\n",machine().time().as_double(), offset, data, card_protection.address, card_protection.cartridge_byte, card_protection.bit_shifter); - + logerror("%.6f protection write %x %x address:%x data:%x shift:%d\n",machine().time().as_double(), offset, data, card_protection.address, card_protection.cartridge_byte, card_protection.bit_shifter); + switch (offset) { case 0: card_protection.failed= card_protection.failed || ((card_protection.cartridge_byte&0x80)!=0) != ((data&4)!=0); @@ -100,10 +104,11 @@ WRITE8_MEMBER( gamate_state::gamate_cart_protection_w ) } READ8_MEMBER( gamate_state::gamate_cart_protection_r ) { - UINT8 ret=1; - if (card_protection.bit_shifter==7 && card_protection.unprotected) { - ret=m_cart->get_rom_base()[bank_multi*0x4000]; - } else { + + UINT8 ret=1; + if (card_protection.bit_shifter==7 && card_protection.unprotected) { + ret=m_cart->get_rom_base()[bank_multi*0x4000]; + } else { card_protection.bit_shifter++; if (card_protection.bit_shifter==8) { card_protection.bit_shifter=0; @@ -112,66 +117,59 @@ READ8_MEMBER( gamate_state::gamate_cart_protection_r ) } ret=(card_protection.cartridge_byte&0x80)?2:0; if (card_protection.bit_shifter==7 && !card_protection.failed) { // now protection chip on cartridge activates cartridge chip select on cpu accesses -// m_maincpu->space(AS_PROGRAM).install_read_handler(0x6000, 0x6000, READ8_DELEGATE(gamate_state, gamate_cart_protection_r)); // next time I will try to get this working +// m_maincpu->space(AS_PROGRAM).install_read_handler(0x6000, 0x6000, READ8_DELEGATE(gamate_state, gamate_cart_protection_r)); // next time I will try to get this working } card_protection.cartridge_byte<<=1; - } - logerror("%.6f protection read %x %x address:%x data:%x shift:%d\n",machine().time().as_double(), offset, ret, card_protection.address, card_protection.cartridge_byte, card_protection.bit_shifter); - return ret; + } + logerror("%.6f protection read %x %x address:%x data:%x shift:%d\n",machine().time().as_double(), offset, ret, card_protection.address, card_protection.cartridge_byte, card_protection.bit_shifter); + return ret; } READ8_MEMBER( gamate_state::protection_r ) { return card_protection.set? 3: 1; } // bits 0 and 1 checked WRITE8_MEMBER( gamate_state::protection_reset ) { - // writes 0x20 - card_protection.address=0x6005-0x6001; - card_protection.bit_shifter=0; - card_protection.cartridge_byte=m_cart->get_rom_base()[card_protection.address++];//m_cart_rom[card_protection.address++]; - card_protection.failed=false; - card_protection.unprotected=false; + // writes 0x20 + card_protection.address=0x6005-0x6001; + card_protection.bit_shifter=0; + card_protection.cartridge_byte=m_cart->get_rom_base()[card_protection.address++];//m_cart_rom[card_protection.address++]; + card_protection.failed=false; + card_protection.unprotected=false; } READ8_MEMBER( gamate_state::newer_protection_set ) { - card_protection.set=true; - return 0; + card_protection.set=true; + return 0; } WRITE8_MEMBER( gamate_state::gamate_video_w ) { - video.reg[offset]=data; - switch (offset) { - case 1: - if (data&0xf) printf("lcd mode %x\n", data); - video.bitmap.write=data&0xc0; // more addressing mode - video.y_increment=data&0x40; - break; - case 2: video.bitmap.xpos=data;break; - case 3: - if (data>=200) printf("lcd ypos: %x\n", data); - video.bitmap.ypos=data; - break; - case 4: video.bitmap.page2=data&0x80;video.x=data&0x7f;break; - case 5: video.y=data;break; - case 7: - if (video.y>=200) - machine().ui().popup_time(2, "bitmap write to x:%x y:%x mode:%x data:%x\n", video.x, video.y, video.reg[1], data); - if (video.bitmap.write) { - video.bitmap.data[video.bitmap.page2][video.y][video.x]=data; - } else { - video.bitmap.data[0][video.y][video.x&(ARRAY_LENGTH(video.bitmap.data[0][0])-1)]=data; - } - if (video.y_increment) video.y++; - else video.x++; - } + video.reg[offset]=data; + switch (offset) { + case 1: + if (data&0xf) printf("lcd mode %x\n", data); + video.y_increment=data&0x40; + break; + case 2: video.bitmap.xpos=data;break; + case 3: + if (data>=200) printf("lcd ypos: %x\n", data); + video.bitmap.ypos=data; + break; + case 4: video.bitmap.page2=data&0x80;video.x=data&0x1f;break; + case 5: video.y=data;break; + case 7: + video.bitmap.data[video.bitmap.page2][video.y][video.x&(ARRAY_LENGTH(video.bitmap.data[0][0])-1)]=data; + if (video.y_increment) video.y++; + else video.x++; // overruns + } } WRITE8_MEMBER( gamate_state::cart_bankswitchmulti_w ) { - bank_multi=data; - membank("bankmulti")->set_base(m_cart->get_rom_base()+0x4000*data+1); + bank_multi=data; + membank("bankmulti")->set_base(m_cart->get_rom_base()+0x4000*data+1); } WRITE8_MEMBER( gamate_state::cart_bankswitch_w ) @@ -181,55 +179,61 @@ WRITE8_MEMBER( gamate_state::cart_bankswitch_w ) READ8_MEMBER( gamate_state::gamate_video_r ) { - if (offset!=6) return 0; - UINT8 data=0; - if (video.bitmap.write) { - data=video.bitmap.data[video.bitmap.page2][video.y][video.x]; - } else { - data=video.bitmap.data[0][video.y][video.x&(ARRAY_LENGTH(video.bitmap.data[0][0])-1)]; - } + if (offset!=6) return 0; + UINT8 data=0; + data=video.bitmap.data[video.bitmap.page2][video.y][video.x&(ARRAY_LENGTH(video.bitmap.data[0][0])-1)]; // if (m_maincpu->pc()<0xf000) // machine().ui().popup_time(2, "lcd read x:%x y:%x mode:%x data:%x\n", video.x, video.y, video.reg[1], data); - return data; + if (video.y_increment) video.y++; + else video.x++; // overruns? + + return data; } WRITE8_MEMBER( gamate_state::gamate_audio_w ) { // printf("audio write %x:%x\n", offset, data);//logerror("%.6f %04x audio write %04x %02x\n",machine().time().as_double(),m_maincpu->pc(),offset,data); - m_sound->device_w(space, offset, data); + m_sound->device_w(space, offset, data); } READ8_MEMBER( gamate_state::gamate_audio_r ) { -// legend of dragon knight -// machine().ui().popup_time(2, "%.6f %04x audio read %04x \n",machine().time().as_double(),m_maincpu->pc(),offset); - return 0; + UINT8 data=m_sound->device_r(space, offset); + return data; } READ8_MEMBER( gamate_state::gamate_pad_r ) { - UINT8 data=m_io_joy->read(); - return data; + UINT8 data=m_io_joy->read(); + return data; } -static ADDRESS_MAP_START( gamate_mem, AS_PROGRAM, 8, gamate_state ) - AM_RANGE(0x0000, 0x03ff) AM_RAM - AM_RANGE(0x4000, 0x400d) AM_READWRITE(gamate_audio_r, gamate_audio_w) - AM_RANGE(0x4400, 0x4400) AM_READ(gamate_pad_r) - AM_RANGE(0x5000, 0x5007) AM_READWRITE(gamate_video_r, gamate_video_w) - AM_RANGE(0x5800, 0x5800) AM_READ(newer_protection_set) - AM_RANGE(0x5900, 0x5900) AM_WRITE(protection_reset) - AM_RANGE(0x5a00, 0x5a00) AM_READ(protection_r) +READ8_MEMBER( gamate_state::gamate_nmi_r ) +{ + UINT8 data=0; + machine().ui().popup_time(2, "nmi/4800 read\n"); + return data; +} - AM_RANGE(0x6001, 0x9fff) AM_READ_BANK("bankmulti") - AM_RANGE(0xa000, 0xdfff) AM_READ_BANK("bank") +static ADDRESS_MAP_START( gamate_mem, AS_PROGRAM, 8, gamate_state ) + AM_RANGE(0x0000, 0x03ff) AM_RAM + AM_RANGE(0x4000, 0x400d) AM_READWRITE(gamate_audio_r, gamate_audio_w) + AM_RANGE(0x4400, 0x4400) AM_READ(gamate_pad_r) + AM_RANGE(0x4800, 0x4800) AM_READ(gamate_nmi_r) + AM_RANGE(0x5000, 0x5007) AM_READWRITE(gamate_video_r, gamate_video_w) + AM_RANGE(0x5800, 0x5800) AM_READ(newer_protection_set) + AM_RANGE(0x5900, 0x5900) AM_WRITE(protection_reset) + AM_RANGE(0x5a00, 0x5a00) AM_READ(protection_r) + + AM_RANGE(0x6001, 0x9fff) AM_READ_BANK("bankmulti") + AM_RANGE(0xa000, 0xdfff) AM_READ_BANK("bank") AM_RANGE(0x6000, 0x6000) AM_READWRITE(gamate_cart_protection_r, gamate_cart_protection_w) AM_RANGE(0x8000, 0x8000) AM_WRITE(cart_bankswitchmulti_w) AM_RANGE(0xc000, 0xc000) AM_WRITE(cart_bankswitch_w) - AM_RANGE(0xf000, 0xffff) AM_ROM AM_SHARE("bios") + AM_RANGE(0xf000, 0xffff) AM_ROM AM_SHARE("bios") ADDRESS_MAP_END @@ -245,7 +249,7 @@ static INPUT_PORTS_START( gamate ) PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_SELECT) PORT_NAME("Select") INPUT_PORTS_END -ATTR_UNUSED static const unsigned short gamate_palette[4] = +static const unsigned short gamate_palette[4] = { 0,1,2,3 }; @@ -254,10 +258,10 @@ ATTR_UNUSED static const unsigned short gamate_palette[4] = /* palette in red, green, blue tribles */ static const unsigned char gamate_colors[4][3] = { - { 255,255,255 }, - { 0xa0, 0xa0, 0xa0 }, - { 0x60, 0x60, 0x60 }, - { 0, 0, 0 } + { 255,255,255 }, + { 0xa0, 0xa0, 0xa0 }, + { 0x60, 0x60, 0x60 }, + { 0, 0, 0 } }; PALETTE_INIT_MEMBER(gamate_state, gamate) @@ -280,27 +284,27 @@ static void BlitPlane(UINT16* line, UINT8 plane1, UINT8 plane2) UINT32 gamate_state::screen_update_gamate(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { - int x, y, j; - for (y=0;y<152;y++) { - for (x=-(video.bitmap.xpos&7), j=0;x<160;x+=8, j++) { - UINT8 d1, d2; - if (video.bitmap.ypos<200) { + int x, y, j; + for (y=0;y<152;y++) { + for (x=-(video.bitmap.xpos&7), j=0;x<160;x+=8, j++) { + UINT8 d1, d2; + if (video.bitmap.ypos<200) { d1=video.bitmap.data[0][(y+video.bitmap.ypos)%200][(j+video.bitmap.xpos/8)&0x1f]; d2=video.bitmap.data[1][(y+video.bitmap.ypos)%200][(j+video.bitmap.xpos/8)&0x1f]; - } else if ((video.bitmap.ypos&0xf)<8) { // lcdtest, of course still some registers not known, my gamate doesn't display bottom lines + } else if ((video.bitmap.ypos&0xf)<8) { // lcdtest, of course still some registers not known, my gamate doesn't display bottom lines; most likely problematic 200 warp around hardware! no real usage int yi=(y+(video.bitmap.ypos&0xf)-8); if (yi<0) yi=video.bitmap.ypos+y; // in this case only 2nd plane used!?, source of first plane? d1=video.bitmap.data[0][yi][(j+video.bitmap.xpos/8)&0x1f]; // value of lines bevor 0 chaos d2=video.bitmap.data[1][yi][(j+video.bitmap.xpos/8)&0x1f]; - } else { + } else { d1=video.bitmap.data[0][y][(j+video.bitmap.xpos/8)&0x1f]; - d2=video.bitmap.data[1][y][(j+video.bitmap.xpos/8)&0x1f]; - } - BlitPlane(&bitmap.pix16(y, x+4), d1, d2); - BlitPlane(&bitmap.pix16(y, x), d1>>4, d2>>4); - } - } - return 0; + d2=video.bitmap.data[1][y][(j+video.bitmap.xpos/8)&0x1f]; + } + BlitPlane(&bitmap.pix16(y, x+4), d1, d2); + BlitPlane(&bitmap.pix16(y, x), d1>>4, d2>>4); + } + } + return 0; } DRIVER_INIT_MEMBER(gamate_state,gamate) @@ -314,11 +318,11 @@ DRIVER_INIT_MEMBER(gamate_state,gamate) void gamate_state::machine_start() { if (m_cart->exists()) { -// m_maincpu->space(AS_PROGRAM).install_read_handler(0x6000, 0x6000, READ8_DELEGATE(gamate_state, gamate_cart_protection_r)); +// m_maincpu->space(AS_PROGRAM).install_read_handler(0x6000, 0x6000, READ8_DELEGATE(gamate_state, gamate_cart_protection_r)); membank("bankmulti")->set_base(m_cart->get_rom_base()+1); membank("bank")->set_base(m_cart->get_rom_base()+0x4000); // bankswitched games in reality no offset } -// m_bios[0xdf1]=0xea; m_bios[0xdf2]=0xea; // default bios: $47 protection readback +// m_bios[0xdf1]=0xea; m_bios[0xdf2]=0xea; // default bios: $47 protection readback card_protection.set=false; bank_multi=0; card_protection.unprotected=false; @@ -378,7 +382,7 @@ static MACHINE_CONFIG_START( gamate, gamate_state ) MCFG_SOUND_ADD("custom", GAMATE_SND, 0) MCFG_SOUND_ROUTE(0, "lspeaker", 0.50) MCFG_SOUND_ROUTE(1, "rspeaker", 0.50) - + MCFG_GENERIC_CARTSLOT_ADD("cartslot", generic_linear_slot, "gamate_cart") MCFG_GENERIC_MANDATORY @@ -396,4 +400,6 @@ ROM_END /* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME */ -CONS( 19??, gamate, 0, 0, gamate, gamate, gamate_state, gamate, "Bit Corp", "Gamate", GAME_IMPERFECT_SOUND) +CONS( 19??, gamate, 0, 0, gamate, gamate, gamate_state, gamate, "Bit Corp", "Gamate", 0) + + diff --git a/src/mess/includes/gamate.h b/src/mess/includes/gamate.h index ea7f3de5c95..55a0be4c6a1 100644 --- a/src/mess/includes/gamate.h +++ b/src/mess/includes/gamate.h @@ -11,25 +11,6 @@ #include "bus/generic/slot.h" #include "bus/generic/carts.h" -struct GAMATE_CHANNEL -{ - GAMATE_CHANNEL() : -// on(0), -// waveform(0), - volume(0), - pos(0), - size(0) -// count(0) - { - } - - int on; - int /*waveform,*/ volume; - int pos; - int size; -// int count; -}; - // ======================> gamate_sound_device @@ -49,11 +30,46 @@ protected: public: DECLARE_WRITE8_MEMBER( device_w ); + DECLARE_READ8_MEMBER( device_r ); private: + static const int DAConverter[]; + static int Value2Volume(int volume) { return DAConverter[volume]*1; } + sound_stream *m_mixer_channel; - GAMATE_CHANNEL m_channels[3]; + struct Tone + { + Tone() : + envelope_on(false), + level(false), + tone(false), full_cycle(false), + volume(0), + pos(0), + size(0) + { + } + + bool envelope_on, level; + bool tone/*else noise*/, full_cycle/* else square signal/pulse */; + int volume; + int pos, size; + }; + enum { Right, Left, Both }; + Tone m_channels[3]; + struct Noise { + Noise(): state(1), level(false), step(0.0), pos(0.0) {} + int state; + bool level; + double step, pos; + } noise; + struct Envelope { + Envelope():control(0), index(0), first(false) {} + int control; + int index; + bool first; + double step, pos; + } envelope; UINT8 reg[14]; }; -- cgit v1.2.3