diff options
Diffstat (limited to 'src/devices/sound/es5506.cpp')
-rw-r--r-- | src/devices/sound/es5506.cpp | 2274 |
1 files changed, 2274 insertions, 0 deletions
diff --git a/src/devices/sound/es5506.cpp b/src/devices/sound/es5506.cpp new file mode 100644 index 00000000000..d6e1865b8ae --- /dev/null +++ b/src/devices/sound/es5506.cpp @@ -0,0 +1,2274 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/********************************************************************************************** + + Ensoniq ES5505/6 driver + by Aaron Giles + +Ensoniq OTIS - ES5505 Ensoniq OTTO - ES5506 + + OTIS is a VLSI device designed in a 2 micron double metal OTTO is a VLSI device designed in a 1.5 micron double metal + CMOS process. The device is the next generation of audio CMOS process. The device is the next generation of audio + technology from ENSONIQ. This new chip achieves a new technology from ENSONIQ. All calculations in the device are + level of audio fidelity performance. These improvements made with at least 18-bit accuracy. + are achieved through the use of frequency interpolation + and on board real time digital filters. All calculations The major features of OTTO are: + in the device are made with at least 16 bit accuracy. - 68 pin PLCC package + - On chip real time digital filters + The major features of OTIS are: - Frequency interpolation + - 48 Pin dual in line package - 32 independent voices + - On chip real time digital filters - Loop start and stop posistions for each voice + - Frequency interpolation - Bidirectional and reverse looping + - 32 independent voices (up from 25 in DOCII) - 68000 compatibility for asynchronous bus communication + - Loop start and stop positions for each voice - separate host and sound memory interface + - Bidirectional and reverse looping - 6 channel stereo serial communication port + - 68000 compatibility for asynchronous bus communication - Programmable clocks for defining serial protocol + - On board pulse width modulation D to A - Internal volume multiplication and stereo panning + - 4 channel stereo serial communication port - A to D input for pots and wheels + - Internal volume multiplication and stereo panning - Hardware support for envelopes + - A to D input for pots and wheels - Support for dual OTTO systems + - Up to 10MHz operation - Optional compressed data format for sample data + - Up to 16MHz operation + ______ ______ + _|o \__/ |_ + A17/D13 - |_|1 48|_| - VSS A A A A A A + _| |_ 2 1 1 1 1 1 A + A18/D14 - |_|2 47|_| - A16/D12 0 9 8 7 6 5 1 + _| |_ / / / / / / 4 + A19/D15 - |_|3 46|_| - A15/D11 H H H H H H H V V H D D D D D D / + _| |_ D D D D D D D S D D 1 1 1 1 1 1 D + BS - |_|4 45|_| - A14/D10 0 1 2 3 4 5 6 S D 7 5 4 3 2 1 0 9 + _| |_ ------------------------------------+ + PWZERO - |_|5 44|_| - A13/D9 / 9 8 7 6 5 4 3 2 1 6 6 6 6 6 6 6 6 | + _| |_ / 8 7 6 5 4 3 2 1 | + SER0 - |_|6 43|_| - A12/D8 | | + _| E |_ SER0|10 60|A13/D8 + SER1 - |_|7 N 42|_| - A11/D7 SER1|11 59|A12/D7 + _| S |_ SER2|12 58|A11/D6 + SER2 - |_|8 O 41|_| - A10/D6 SER3|13 ENSONIQ 57|A10/D5 + _| N |_ SER4|14 56|A9/D4 + SER3 - |_|9 I 40|_| - A9/D5 SER5|15 55|A8/D3 + _| Q |_ WCLK|16 54|A7/D2 + SERWCLK - |_|10 39|_| - A8/D4 LRCLK|17 ES5506 53|A6/D1 + _| |_ BCLK|18 52|A5/D0 + SERLR - |_|11 38|_| - A7/D3 RESB|19 51|A4 + _| |_ HA5|20 50|A3 + SERBCLK - |_|12 E 37|_| - A6/D2 HA4|21 OTTO 49|A2 + _| S |_ HA3|22 48|A1 + RLO - |_|13 5 36|_| - A5/D1 HA2|23 47|A0 + _| 5 |_ HA1|24 46|BS1 + RHI - |_|14 0 35|_| - A4/D0 HA0|25 45|BS0 + _| 5 |_ POT_IN|26 44|DTACKB + LLO - |_|15 34|_| - CLKIN | 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 4 | + _| |_ | 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 | + LHI - |_|16 33|_| - CAS +--------------------------------------+ + _| |_ B E E B E B B D S B B B E K B W W + POT - |_|17 O 32|_| - AMUX S B L N L S S D S S X S L Q / / + _| T |_ E E R E H M C V V A U A C R R R + DTACK - |_|18 I 31|_| - RAS R R D H R M C I M + _| S |_ _ D A + R/W - |_|19 30|_| - E T + _| |_ O + MS - |_|20 29|_| - IRQ P + _| |_ + CS - |_|21 28|_| - A3 + _| |_ + RES - |_|22 27|_| - A2 + _| |_ + VSS - |_|23 26|_| - A1 + _| |_ + VDD - |_|24 25|_| - A0 + |________________| + +***********************************************************************************************/ + +#include "emu.h" +#include "es5506.h" + + +/********************************************************************************************** + + CONSTANTS + +***********************************************************************************************/ + +#define LOG_COMMANDS 0 +#define RAINE_CHECK 0 + +#if MAKE_WAVS +#include "sound/wavwrite.h" +#endif + + +#define MAX_SAMPLE_CHUNK 10000 +#define ULAW_MAXBITS 8 + +#define CONTROL_BS1 0x8000 +#define CONTROL_BS0 0x4000 +#define CONTROL_CMPD 0x2000 +#define CONTROL_CA2 0x1000 +#define CONTROL_CA1 0x0800 +#define CONTROL_CA0 0x0400 +#define CONTROL_LP4 0x0200 +#define CONTROL_LP3 0x0100 +#define CONTROL_IRQ 0x0080 +#define CONTROL_DIR 0x0040 +#define CONTROL_IRQE 0x0020 +#define CONTROL_BLE 0x0010 +#define CONTROL_LPE 0x0008 +#define CONTROL_LEI 0x0004 +#define CONTROL_STOP1 0x0002 +#define CONTROL_STOP0 0x0001 + +#define CONTROL_BSMASK (CONTROL_BS1 | CONTROL_BS0) +#define CONTROL_CAMASK (CONTROL_CA2 | CONTROL_CA1 | CONTROL_CA0) +#define CONTROL_LPMASK (CONTROL_LP4 | CONTROL_LP3) +#define CONTROL_LOOPMASK (CONTROL_BLE | CONTROL_LPE) +#define CONTROL_STOPMASK (CONTROL_STOP1 | CONTROL_STOP0) + + +es550x_device::es550x_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_sound_interface(mconfig, *this), + m_stream(NULL), + m_sample_rate(0), + m_write_latch(0), + m_read_latch(0), + m_master_clock(0), + m_current_page(0), + m_active_voices(0), + m_mode(0), + m_wst(0), + m_wend(0), + m_lrend(0), + m_irqv(0), + m_scratch(NULL), + m_ulaw_lookup(NULL), + m_volume_lookup(NULL), + #if MAKE_WAVS + m_wavraw(NULL), + #endif + m_eslog(NULL), + m_region0(NULL), + m_region1(NULL), + m_region2(NULL), + m_region3(NULL), + m_channels(0), + m_irq_cb(*this), + m_read_port_cb(*this) +{ + for (int i = 0; i < 4; i++) + { + m_region_base[i] = NULL; + } +} + +const device_type ES5506 = &device_creator<es5506_device>; + +es5506_device::es5506_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : es550x_device(mconfig, ES5506, "ES5506", tag, owner, clock, "es5506", __FILE__) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- +void es550x_device::device_start() +{ +} + +void es5506_device::device_start() +{ + int j; + UINT32 accum_mask; + int channels = 1; /* 1 channel by default, for backward compatibility */ + + /* only override the number of channels if the value is in the valid range 1 .. 6 */ + if (1 <= m_channels && m_channels <= 6) + channels = m_channels; + + /* debugging */ + if (LOG_COMMANDS && !m_eslog) + m_eslog = fopen("es.log", "w"); + + /* create the stream */ + m_stream = machine().sound().stream_alloc(*this, 0, 2 * channels, clock() / (16*32)); + + /* initialize the regions */ + m_region_base[0] = m_region0 ? (UINT16 *)machine().root_device().memregion(m_region0)->base() : NULL; + m_region_base[1] = m_region1 ? (UINT16 *)machine().root_device().memregion(m_region1)->base() : NULL; + m_region_base[2] = m_region2 ? (UINT16 *)machine().root_device().memregion(m_region2)->base() : NULL; + m_region_base[3] = m_region3 ? (UINT16 *)machine().root_device().memregion(m_region3)->base() : NULL; + + /* initialize the rest of the structure */ + m_master_clock = clock(); + m_irq_cb.resolve(); + m_read_port_cb.resolve(); + m_irqv = 0x80; + m_channels = channels; + + /* KT-76 assumes all voices are active on an ES5506 without setting them! */ + m_active_voices = 31; + m_sample_rate = m_master_clock / (16 * (m_active_voices + 1)); + m_stream->set_sample_rate(m_sample_rate); + + /* compute the tables */ + compute_tables(); + + /* init the voices */ + accum_mask = 0xffffffff; + for (j = 0; j < 32; j++) + { + m_voice[j].index = j; + m_voice[j].control = CONTROL_STOPMASK; + m_voice[j].lvol = 0xffff; + m_voice[j].rvol = 0xffff; + m_voice[j].exbank = 0; + m_voice[j].accum_mask = accum_mask; + } + + /* allocate memory */ + m_scratch = auto_alloc_array_clear(machine(), INT32, 2 * MAX_SAMPLE_CHUNK); + + /* register save */ + save_item(NAME(m_sample_rate)); + save_item(NAME(m_write_latch)); + save_item(NAME(m_read_latch)); + + save_item(NAME(m_current_page)); + save_item(NAME(m_active_voices)); + save_item(NAME(m_mode)); + save_item(NAME(m_wst)); + save_item(NAME(m_wend)); + save_item(NAME(m_lrend)); + save_item(NAME(m_irqv)); + + save_pointer(NAME(m_scratch), 2 * MAX_SAMPLE_CHUNK); + + for (j = 0; j < 32; j++) + { + save_item(NAME(m_voice[j].control), j); + save_item(NAME(m_voice[j].freqcount), j); + save_item(NAME(m_voice[j].start), j); + save_item(NAME(m_voice[j].lvol), j); + save_item(NAME(m_voice[j].end), j); + save_item(NAME(m_voice[j].lvramp), j); + save_item(NAME(m_voice[j].accum), j); + save_item(NAME(m_voice[j].rvol), j); + save_item(NAME(m_voice[j].rvramp), j); + save_item(NAME(m_voice[j].ecount), j); + save_item(NAME(m_voice[j].k2), j); + save_item(NAME(m_voice[j].k2ramp), j); + save_item(NAME(m_voice[j].k1), j); + save_item(NAME(m_voice[j].k1ramp), j); + save_item(NAME(m_voice[j].o4n1), j); + save_item(NAME(m_voice[j].o3n1), j); + save_item(NAME(m_voice[j].o3n2), j); + save_item(NAME(m_voice[j].o2n1), j); + save_item(NAME(m_voice[j].o2n2), j); + save_item(NAME(m_voice[j].o1n1), j); + save_item(NAME(m_voice[j].exbank), j); + save_item(NAME(m_voice[j].filtcount), j); + } + + /* success */ +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void es550x_device::device_reset() +{ +} + +//------------------------------------------------- +// device_stop - device-specific stop +//------------------------------------------------- + +void es550x_device::device_stop() +{ + /* debugging */ + if (LOG_COMMANDS && m_eslog) + { + fclose(m_eslog); + m_eslog = NULL; + } + + #if MAKE_WAVS + { + int i; + + for (i = 0; i < MAX_ES5506; i++) + { + if (es5506[i].m_wavraw) + wav_close(es5506[i].m_wavraw); + } + } + #endif +} + +const device_type ES5505 = &device_creator<es5505_device>; + +es5505_device::es5505_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : es550x_device(mconfig, ES5505, "ES5505", tag, owner, clock, "es5505", __FILE__) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void es5505_device::device_start() +{ + int j; + UINT32 accum_mask; + int channels = 1; /* 1 channel by default, for backward compatibility */ + + /* only override the number of channels if the value is in the valid range 1 .. 4 */ + if (1 <= m_channels && m_channels <= 4) + channels = m_channels; + + /* debugging */ + if (LOG_COMMANDS && !m_eslog) + m_eslog = fopen("es.log", "w"); + + /* create the stream */ + m_stream = machine().sound().stream_alloc(*this, 0, 2 * channels, clock() / (16*32)); + + /* initialize the regions */ + m_region_base[0] = m_region0 ? (UINT16 *)machine().root_device().memregion(m_region0)->base() : NULL; + m_region_base[1] = m_region1 ? (UINT16 *)machine().root_device().memregion(m_region1)->base() : NULL; + + /* initialize the rest of the structure */ + m_master_clock = clock(); + m_irq_cb.resolve(); + m_read_port_cb.resolve(); + m_irqv = 0x80; + m_channels = channels; + + /* compute the tables */ + compute_tables(); + + /* init the voices */ + accum_mask = 0x7fffffff; + for (j = 0; j < 32; j++) + { + m_voice[j].index = j; + m_voice[j].control = CONTROL_STOPMASK; + m_voice[j].lvol = 0xffff; + m_voice[j].rvol = 0xffff; + m_voice[j].exbank = 0; + m_voice[j].accum_mask = accum_mask; + } + + /* allocate memory */ + m_scratch = auto_alloc_array_clear(machine(), INT32, 2 * MAX_SAMPLE_CHUNK); + + /* register save */ + save_item(NAME(m_sample_rate)); + save_item(NAME(m_write_latch)); + save_item(NAME(m_read_latch)); + + save_item(NAME(m_current_page)); + save_item(NAME(m_active_voices)); + save_item(NAME(m_mode)); + save_item(NAME(m_wst)); + save_item(NAME(m_wend)); + save_item(NAME(m_lrend)); + save_item(NAME(m_irqv)); + + save_pointer(NAME(m_scratch), 2 * MAX_SAMPLE_CHUNK); + + for (j = 0; j < 32; j++) + { + save_item(NAME(m_voice[j].control), j); + save_item(NAME(m_voice[j].freqcount), j); + save_item(NAME(m_voice[j].start), j); + save_item(NAME(m_voice[j].lvol), j); + save_item(NAME(m_voice[j].end), j); + save_item(NAME(m_voice[j].lvramp), j); + save_item(NAME(m_voice[j].accum), j); + save_item(NAME(m_voice[j].rvol), j); + save_item(NAME(m_voice[j].rvramp), j); + save_item(NAME(m_voice[j].ecount), j); + save_item(NAME(m_voice[j].k2), j); + save_item(NAME(m_voice[j].k2ramp), j); + save_item(NAME(m_voice[j].k1), j); + save_item(NAME(m_voice[j].k1ramp), j); + save_item(NAME(m_voice[j].o4n1), j); + save_item(NAME(m_voice[j].o3n1), j); + save_item(NAME(m_voice[j].o3n2), j); + save_item(NAME(m_voice[j].o2n1), j); + save_item(NAME(m_voice[j].o2n2), j); + save_item(NAME(m_voice[j].o1n1), j); + save_item(NAME(m_voice[j].exbank), j); + save_item(NAME(m_voice[j].filtcount), j); + } + + /* success */ +} + + +/********************************************************************************************** + + update_irq_state -- update the IRQ state + +***********************************************************************************************/ + + +void es550x_device::update_irq_state() +{ + /* ES5505/6 irq line has been set high - inform the host */ + if (!m_irq_cb.isnull()) + m_irq_cb(1); /* IRQB set high */ +} + +void es550x_device::update_internal_irq_state() +{ + /* Host (cpu) has just read the voice interrupt vector (voice IRQ ack). + + Reset the voice vector to show the IRQB line is low (top bit set). + If we have any stacked interrupts (other voices waiting to be + processed - with their IRQ bit set) then they will be moved into + the vector next time the voice is processed. In emulation + terms they get updated next time generate_samples() is called. + */ + + m_irqv=0x80; + + if (!m_irq_cb.isnull()) + m_irq_cb(0); /* IRQB set low */ +} + +/********************************************************************************************** + + compute_tables -- compute static tables + +***********************************************************************************************/ + +void es550x_device::compute_tables() +{ + int i; + + /* allocate ulaw lookup table */ + m_ulaw_lookup = auto_alloc_array_clear(machine(), INT16, 1 << ULAW_MAXBITS); + + /* generate ulaw lookup table */ + for (i = 0; i < (1 << ULAW_MAXBITS); i++) + { + UINT16 rawval = (i << (16 - ULAW_MAXBITS)) | (1 << (15 - ULAW_MAXBITS)); + UINT8 exponent = rawval >> 13; + UINT32 mantissa = (rawval << 3) & 0xffff; + + if (exponent == 0) + m_ulaw_lookup[i] = (INT16)mantissa >> 7; + else + { + mantissa = (mantissa >> 1) | (~mantissa & 0x8000); + m_ulaw_lookup[i] = (INT16)mantissa >> (7 - exponent); + } + } + + /* allocate volume lookup table */ + m_volume_lookup = auto_alloc_array_clear(machine(), UINT16, 4096); + + /* generate volume lookup table */ + for (i = 0; i < 4096; i++) + { + UINT8 exponent = i >> 8; + UINT32 mantissa = (i & 0xff) | 0x100; + + m_volume_lookup[i] = (mantissa << 11) >> (20 - exponent); + } +} + + + +/********************************************************************************************** + + interpolate -- interpolate between two samples + +***********************************************************************************************/ + +#define interpolate(sample1, sample2, accum) \ + (sample1 * (INT32)(0x800 - (accum & 0x7ff)) + \ + sample2 * (INT32)(accum & 0x7ff)) >> 11; + + + +/********************************************************************************************** + + apply_filters -- apply the 4-pole digital filter to the sample + +***********************************************************************************************/ + +#define apply_filters(voice, sample) \ +do \ +{ \ + /* pole 1 is always low-pass using K1 */ \ + sample = ((INT32)(voice->k1 >> 2) * (sample - voice->o1n1) / 16384) + voice->o1n1; \ + voice->o1n1 = sample; \ + \ + /* pole 2 is always low-pass using K1 */ \ + sample = ((INT32)(voice->k1 >> 2) * (sample - voice->o2n1) / 16384) + voice->o2n1; \ + voice->o2n2 = voice->o2n1; \ + voice->o2n1 = sample; \ + \ + /* remaining poles depend on the current filter setting */ \ + switch (voice->control & CONTROL_LPMASK) \ + { \ + case 0: \ + /* pole 3 is high-pass using K2 */ \ + sample = sample - voice->o2n2 + ((INT32)(voice->k2 >> 2) * voice->o3n1) / 32768 + voice->o3n1 / 2; \ + voice->o3n2 = voice->o3n1; \ + voice->o3n1 = sample; \ + \ + /* pole 4 is high-pass using K2 */ \ + sample = sample - voice->o3n2 + ((INT32)(voice->k2 >> 2) * voice->o4n1) / 32768 + voice->o4n1 / 2; \ + voice->o4n1 = sample; \ + break; \ + \ + case CONTROL_LP3: \ + /* pole 3 is low-pass using K1 */ \ + sample = ((INT32)(voice->k1 >> 2) * (sample - voice->o3n1) / 16384) + voice->o3n1; \ + voice->o3n2 = voice->o3n1; \ + voice->o3n1 = sample; \ + \ + /* pole 4 is high-pass using K2 */ \ + sample = sample - voice->o3n2 + ((INT32)(voice->k2 >> 2) * voice->o4n1) / 32768 + voice->o4n1 / 2; \ + voice->o4n1 = sample; \ + break; \ + \ + case CONTROL_LP4: \ + /* pole 3 is low-pass using K2 */ \ + sample = ((INT32)(voice->k2 >> 2) * (sample - voice->o3n1) / 16384) + voice->o3n1; \ + voice->o3n2 = voice->o3n1; \ + voice->o3n1 = sample; \ + \ + /* pole 4 is low-pass using K2 */ \ + sample = ((INT32)(voice->k2 >> 2) * (sample - voice->o4n1) / 16384) + voice->o4n1; \ + voice->o4n1 = sample; \ + break; \ + \ + case CONTROL_LP4 | CONTROL_LP3: \ + /* pole 3 is low-pass using K1 */ \ + sample = ((INT32)(voice->k1 >> 2) * (sample - voice->o3n1) / 16384) + voice->o3n1; \ + voice->o3n2 = voice->o3n1; \ + voice->o3n1 = sample; \ + \ + /* pole 4 is low-pass using K2 */ \ + sample = ((INT32)(voice->k2 >> 2) * (sample - voice->o4n1) / 16384) + voice->o4n1; \ + voice->o4n1 = sample; \ + break; \ + } \ +} while (0) + + + +/********************************************************************************************** + + update_envelopes -- update the envelopes + +***********************************************************************************************/ + +#define update_envelopes(voice, samples) \ +do \ +{ \ + int count = (samples > 1 && samples > voice->ecount) ? voice->ecount : samples; \ + \ + /* decrement the envelope counter */ \ + voice->ecount -= count; \ + \ + /* ramp left volume */ \ + if (voice->lvramp) \ + { \ + voice->lvol += (INT8)voice->lvramp * count; \ + if ((INT32)voice->lvol < 0) voice->lvol = 0; \ + else if (voice->lvol > 0xffff) voice->lvol = 0xffff; \ + } \ + \ + /* ramp right volume */ \ + if (voice->rvramp) \ + { \ + voice->rvol += (INT8)voice->rvramp * count; \ + if ((INT32)voice->rvol < 0) voice->rvol = 0; \ + else if (voice->rvol > 0xffff) voice->rvol = 0xffff; \ + } \ + \ + /* ramp k1 filter constant */ \ + if (voice->k1ramp && ((INT32)voice->k1ramp >= 0 || !(voice->filtcount & 7))) \ + { \ + voice->k1 += (INT8)voice->k1ramp * count; \ + if ((INT32)voice->k1 < 0) voice->k1 = 0; \ + else if (voice->k1 > 0xffff) voice->k1 = 0xffff; \ + } \ + \ + /* ramp k2 filter constant */ \ + if (voice->k2ramp && ((INT32)voice->k2ramp >= 0 || !(voice->filtcount & 7))) \ + { \ + voice->k2 += (INT8)voice->k2ramp * count; \ + if ((INT32)voice->k2 < 0) voice->k2 = 0; \ + else if (voice->k2 > 0xffff) voice->k2 = 0xffff; \ + } \ + \ + /* update the filter constant counter */ \ + voice->filtcount += count; \ + \ +} while (0) + + + +/********************************************************************************************** + + check_for_end_forward + check_for_end_reverse -- check for loop end and loop appropriately + +***********************************************************************************************/ + +#define check_for_end_forward(voice, accum) \ +do \ +{ \ + /* are we past the end? */ \ + if (accum > voice->end && !(voice->control & CONTROL_LEI)) \ + { \ + /* generate interrupt if required */ \ + if (voice->control&CONTROL_IRQE) \ + voice->control |= CONTROL_IRQ; \ + \ + /* handle the different types of looping */ \ + switch (voice->control & CONTROL_LOOPMASK) \ + { \ + /* non-looping */ \ + case 0: \ + voice->control |= CONTROL_STOP0; \ + goto alldone; \ + \ + /* uni-directional looping */ \ + case CONTROL_LPE: \ + accum = (voice->start + (accum - voice->end)) & voice->accum_mask; \ + break; \ + \ + /* trans-wave looping */ \ + case CONTROL_BLE: \ + accum = (voice->start + (accum - voice->end)) & voice->accum_mask; \ + voice->control = (voice->control & ~CONTROL_LOOPMASK) | CONTROL_LEI;\ + break; \ + \ + /* bi-directional looping */ \ + case CONTROL_LPE | CONTROL_BLE: \ + accum = (voice->end - (accum - voice->end)) & voice->accum_mask; \ + voice->control ^= CONTROL_DIR; \ + goto reverse; \ + } \ + } \ +} while (0) + + +#define check_for_end_reverse(voice, accum) \ +do \ +{ \ + /* are we past the end? */ \ + if (accum < voice->start && !(voice->control & CONTROL_LEI)) \ + { \ + /* generate interrupt if required */ \ + if (voice->control&CONTROL_IRQE) \ + voice->control |= CONTROL_IRQ; \ + \ + /* handle the different types of looping */ \ + switch (voice->control & CONTROL_LOOPMASK) \ + { \ + /* non-looping */ \ + case 0: \ + voice->control |= CONTROL_STOP0; \ + goto alldone; \ + \ + /* uni-directional looping */ \ + case CONTROL_LPE: \ + accum = (voice->end - (voice->start - accum)) & voice->accum_mask; \ + break; \ + \ + /* trans-wave looping */ \ + case CONTROL_BLE: \ + accum = (voice->end - (voice->start - accum)) & voice->accum_mask; \ + voice->control = (voice->control & ~CONTROL_LOOPMASK) | CONTROL_LEI;\ + break; \ + \ + /* bi-directional looping */ \ + case CONTROL_LPE | CONTROL_BLE: \ + accum = (voice->start + (voice->start - accum)) & voice->accum_mask;\ + voice->control ^= CONTROL_DIR; \ + goto reverse; \ + } \ + } \ +} while (0) + + + +/********************************************************************************************** + + generate_dummy -- generate nothing, just apply envelopes + +***********************************************************************************************/ + +void es550x_device::generate_dummy(es550x_voice *voice, UINT16 *base, INT32 *lbuffer, INT32 *rbuffer, int samples) +{ + UINT32 freqcount = voice->freqcount; + UINT32 accum = voice->accum & voice->accum_mask; + + /* outer loop, in case we switch directions */ + while (samples > 0 && !(voice->control & CONTROL_STOPMASK)) + { +reverse: + /* two cases: first case is forward direction */ + if (!(voice->control & CONTROL_DIR)) + { + /* loop while we still have samples to generate */ + while (samples--) + { + /* fetch two samples */ + accum = (accum + freqcount) & voice->accum_mask; + + /* update filters/volumes */ + if (voice->ecount != 0) + update_envelopes(voice, 1); + + /* check for loop end */ + check_for_end_forward(voice, accum); + } + } + + /* two cases: second case is backward direction */ + else + { + /* loop while we still have samples to generate */ + while (samples--) + { + /* fetch two samples */ + accum = (accum - freqcount) & voice->accum_mask; + + /* update filters/volumes */ + if (voice->ecount != 0) + update_envelopes(voice, 1); + + /* check for loop end */ + check_for_end_reverse(voice, accum); + } + } + } + + /* if we stopped, process any additional envelope */ +alldone: + voice->accum = accum; + if (samples > 0) + update_envelopes(voice, samples); +} + + + +/********************************************************************************************** + + generate_ulaw -- general u-law decoding routine + +***********************************************************************************************/ + +void es550x_device::generate_ulaw(es550x_voice *voice, UINT16 *base, INT32 *lbuffer, INT32 *rbuffer, int samples) +{ + UINT32 freqcount = voice->freqcount; + UINT32 accum = voice->accum & voice->accum_mask; + INT32 lvol = m_volume_lookup[voice->lvol >> 4]; + INT32 rvol = m_volume_lookup[voice->rvol >> 4]; + + /* pre-add the bank offset */ + base += voice->exbank; + + /* outer loop, in case we switch directions */ + while (samples > 0 && !(voice->control & CONTROL_STOPMASK)) + { +reverse: + /* two cases: first case is forward direction */ + if (!(voice->control & CONTROL_DIR)) + { + /* loop while we still have samples to generate */ + while (samples--) + { + /* fetch two samples */ + INT32 val1 = base[accum >> 11]; + INT32 val2 = base[((accum + (1 << 11)) & voice->accum_mask) >> 11]; + + /* decompress u-law */ + val1 = m_ulaw_lookup[val1 >> (16 - ULAW_MAXBITS)]; + val2 = m_ulaw_lookup[val2 >> (16 - ULAW_MAXBITS)]; + + /* interpolate */ + val1 = interpolate(val1, val2, accum); + accum = (accum + freqcount) & voice->accum_mask; + + /* apply filters */ + apply_filters(voice, val1); + + /* update filters/volumes */ + if (voice->ecount != 0) + { + update_envelopes(voice, 1); + lvol = m_volume_lookup[voice->lvol >> 4]; + rvol = m_volume_lookup[voice->rvol >> 4]; + } + + /* apply volumes and add */ + *lbuffer++ += (val1 * lvol) >> 11; + *rbuffer++ += (val1 * rvol) >> 11; + + /* check for loop end */ + check_for_end_forward(voice, accum); + } + } + + /* two cases: second case is backward direction */ + else + { + /* loop while we still have samples to generate */ + while (samples--) + { + /* fetch two samples */ + INT32 val1 = base[accum >> 11]; + INT32 val2 = base[((accum + (1 << 11)) & voice->accum_mask) >> 11]; + + /* decompress u-law */ + val1 = m_ulaw_lookup[val1 >> (16 - ULAW_MAXBITS)]; + val2 = m_ulaw_lookup[val2 >> (16 - ULAW_MAXBITS)]; + + /* interpolate */ + val1 = interpolate(val1, val2, accum); + accum = (accum - freqcount) & voice->accum_mask; + + /* apply filters */ + apply_filters(voice, val1); + + /* update filters/volumes */ + if (voice->ecount != 0) + { + update_envelopes(voice, 1); + lvol = m_volume_lookup[voice->lvol >> 4]; + rvol = m_volume_lookup[voice->rvol >> 4]; + } + + /* apply volumes and add */ + *lbuffer++ += (val1 * lvol) >> 11; + *rbuffer++ += (val1 * rvol) >> 11; + + /* check for loop end */ + check_for_end_reverse(voice, accum); + } + } + } + + /* if we stopped, process any additional envelope */ +alldone: + voice->accum = accum; + if (samples > 0) + update_envelopes(voice, samples); +} + + + +/********************************************************************************************** + + generate_pcm -- general PCM decoding routine + +***********************************************************************************************/ + +void es550x_device::generate_pcm(es550x_voice *voice, UINT16 *base, INT32 *lbuffer, INT32 *rbuffer, int samples) +{ + UINT32 freqcount = voice->freqcount; + UINT32 accum = voice->accum & voice->accum_mask; + INT32 lvol = m_volume_lookup[voice->lvol >> 4]; + INT32 rvol = m_volume_lookup[voice->rvol >> 4]; + + /* pre-add the bank offset */ + base += voice->exbank; + + /* outer loop, in case we switch directions */ + while (samples > 0 && !(voice->control & CONTROL_STOPMASK)) + { +reverse: + /* two cases: first case is forward direction */ + if (!(voice->control & CONTROL_DIR)) + { + /* loop while we still have samples to generate */ + while (samples--) + { + /* fetch two samples */ + INT32 val1 = (INT16)base[accum >> 11]; + INT32 val2 = (INT16)base[((accum + (1 << 11)) & voice->accum_mask) >> 11]; + + /* interpolate */ + val1 = interpolate(val1, val2, accum); + accum = (accum + freqcount) & voice->accum_mask; + + /* apply filters */ + apply_filters(voice, val1); + + /* update filters/volumes */ + if (voice->ecount != 0) + { + update_envelopes(voice, 1); + lvol = m_volume_lookup[voice->lvol >> 4]; + rvol = m_volume_lookup[voice->rvol >> 4]; + } + + /* apply volumes and add */ + *lbuffer++ += (val1 * lvol) >> 11; + *rbuffer++ += (val1 * rvol) >> 11; + + /* check for loop end */ + check_for_end_forward(voice, accum); + } + } + + /* two cases: second case is backward direction */ + else + { + /* loop while we still have samples to generate */ + while (samples--) + { + /* fetch two samples */ + INT32 val1 = (INT16)base[accum >> 11]; + INT32 val2 = (INT16)base[((accum + (1 << 11)) & voice->accum_mask) >> 11]; + + /* interpolate */ + val1 = interpolate(val1, val2, accum); + accum = (accum - freqcount) & voice->accum_mask; + + /* apply filters */ + apply_filters(voice, val1); + + /* update filters/volumes */ + if (voice->ecount != 0) + { + update_envelopes(voice, 1); + lvol = m_volume_lookup[voice->lvol >> 4]; + rvol = m_volume_lookup[voice->rvol >> 4]; + } + + /* apply volumes and add */ + *lbuffer++ += (val1 * lvol) >> 11; + *rbuffer++ += (val1 * rvol) >> 11; + + /* check for loop end */ + check_for_end_reverse(voice, accum); + } + } + } + + /* if we stopped, process any additional envelope */ +alldone: + voice->accum = accum; + if (samples > 0) + update_envelopes(voice, samples); +} + + + +/********************************************************************************************** + + generate_samples -- tell each voice to generate samples + +***********************************************************************************************/ + +void es5506_device::generate_samples(INT32 **outputs, int offset, int samples) +{ + int v; + + /* skip if nothing to do */ + if (!samples) + return; + + /* clear out the accumulators */ + for (int i = 0; i < m_channels << 1; i++) + { + memset(outputs[i] + offset, 0, sizeof(INT32) * samples); + } + + /* loop over voices */ + for (v = 0; v <= m_active_voices; v++) + { + es550x_voice *voice = &m_voice[v]; + UINT16 *base = m_region_base[voice->control >> 14]; + + /* special case: if end == start, stop the voice */ + if (voice->start == voice->end) + voice->control |= CONTROL_STOP0; + + int voice_channel = (voice->control & CONTROL_CAMASK) >> 10; + int channel = voice_channel % m_channels; + int l = channel << 1; + int r = l + 1; + INT32 *left = outputs[l] + offset; + INT32 *right = outputs[r] + offset; + + /* generate from the appropriate source */ + if (!base) + { + logerror("es5506: NULL region base %d\n",voice->control >> 14); + generate_dummy(voice, base, left, right, samples); + } + else if (voice->control & 0x2000) + generate_ulaw(voice, base, left, right, samples); + else + generate_pcm(voice, base, left, right, samples); + + /* does this voice have it's IRQ bit raised? */ + if (voice->control&CONTROL_IRQ) + { + logerror("es5506: IRQ raised on voice %d!!\n",v); + + /* only update voice vector if existing IRQ is acked by host */ + if (m_irqv&0x80) + { + /* latch voice number into vector, and set high bit low */ + m_irqv=v&0x7f; + + /* take down IRQ bit on voice */ + voice->control&=~CONTROL_IRQ; + + /* inform host of irq */ + update_irq_state(); + } + } + } +} + +void es5505_device::generate_samples(INT32 **outputs, int offset, int samples) +{ + int v; + + /* skip if nothing to do */ + if (!samples) + return; + + /* clear out the accumulators */ + for (int i = 0; i < m_channels << 1; i++) + { + memset(outputs[i] + offset, 0, sizeof(INT32) * samples); + } + + /* loop over voices */ + for (v = 0; v <= m_active_voices; v++) + { + es550x_voice *voice = &m_voice[v]; + UINT16 *base = m_region_base[voice->control >> 14]; + + /* special case: if end == start, stop the voice */ + if (voice->start == voice->end) + voice->control |= CONTROL_STOP0; + + int voice_channel = (voice->control & CONTROL_CAMASK) >> 10; + int channel = voice_channel % m_channels; + int l = channel << 1; + int r = l + 1; + INT32 *left = outputs[l] + offset; + INT32 *right = outputs[r] + offset; + + /* generate from the appropriate source */ + if (!base) + { + logerror("es5506: NULL region base %d\n",voice->control >> 14); + generate_dummy(voice, base, left, right, samples); + } + else if (voice->control & 0x2000) + generate_ulaw(voice, base, left, right, samples); + else + generate_pcm(voice, base, left, right, samples); + + /* does this voice have it's IRQ bit raised? */ + if (voice->control&CONTROL_IRQ) + { + logerror("es5506: IRQ raised on voice %d!!\n",v); + + /* only update voice vector if existing IRQ is acked by host */ + if (m_irqv&0x80) + { + /* latch voice number into vector, and set high bit low */ + m_irqv=v&0x7f; + + /* take down IRQ bit on voice */ + voice->control&=~CONTROL_IRQ; + + /* inform host of irq */ + update_irq_state(); + } + } + } +} + + + +/********************************************************************************************** + + reg_write -- handle a write to the selected ES5506 register + +***********************************************************************************************/ + +inline void es5506_device::reg_write_low(es550x_voice *voice, offs_t offset, UINT32 data) +{ + switch (offset) + { + case 0x00/8: /* CR */ + voice->control = data & 0xffff; + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "voice %d, control=%04x\n", m_current_page & 0x1f, voice->control); + break; + + case 0x08/8: /* FC */ + voice->freqcount = data & 0x1ffff; + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "voice %d, freq count=%08x\n", m_current_page & 0x1f, voice->freqcount); + break; + + case 0x10/8: /* LVOL */ + voice->lvol = data & 0xffff; + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "voice %d, left vol=%04x\n", m_current_page & 0x1f, voice->lvol); + break; + + case 0x18/8: /* LVRAMP */ + voice->lvramp = (data & 0xff00) >> 8; + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "voice %d, left vol ramp=%04x\n", m_current_page & 0x1f, voice->lvramp); + break; + + case 0x20/8: /* RVOL */ + voice->rvol = data & 0xffff; + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "voice %d, right vol=%04x\n", m_current_page & 0x1f, voice->rvol); + break; + + case 0x28/8: /* RVRAMP */ + voice->rvramp = (data & 0xff00) >> 8; + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "voice %d, right vol ramp=%04x\n", m_current_page & 0x1f, voice->rvramp); + break; + + case 0x30/8: /* ECOUNT */ + voice->ecount = data & 0x1ff; + voice->filtcount = 0; + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "voice %d, envelope count=%04x\n", m_current_page & 0x1f, voice->ecount); + break; + + case 0x38/8: /* K2 */ + voice->k2 = data & 0xffff; + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "voice %d, K2=%04x\n", m_current_page & 0x1f, voice->k2); + break; + + case 0x40/8: /* K2RAMP */ + voice->k2ramp = ((data & 0xff00) >> 8) | ((data & 0x0001) << 31); + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "voice %d, K2 ramp=%04x\n", m_current_page & 0x1f, voice->k2ramp); + break; + + case 0x48/8: /* K1 */ + voice->k1 = data & 0xffff; + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "voice %d, K1=%04x\n", m_current_page & 0x1f, voice->k1); + break; + + case 0x50/8: /* K1RAMP */ + voice->k1ramp = ((data & 0xff00) >> 8) | ((data & 0x0001) << 31); + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "voice %d, K1 ramp=%04x\n", m_current_page & 0x1f, voice->k1ramp); + break; + + case 0x58/8: /* ACTV */ + { + m_active_voices = data & 0x1f; + m_sample_rate = m_master_clock / (16 * (m_active_voices + 1)); + m_stream->set_sample_rate(m_sample_rate); + + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "active voices=%d, sample_rate=%d\n", m_active_voices, m_sample_rate); + break; + } + + case 0x60/8: /* MODE */ + m_mode = data & 0x1f; + break; + + case 0x68/8: /* PAR - read only */ + case 0x70/8: /* IRQV - read only */ + break; + + case 0x78/8: /* PAGE */ + m_current_page = data & 0x7f; + break; + } +} + +inline void es5506_device::reg_write_high(es550x_voice *voice, offs_t offset, UINT32 data) +{ + switch (offset) + { + case 0x00/8: /* CR */ + voice->control = data & 0xffff; + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "voice %d, control=%04x\n", m_current_page & 0x1f, voice->control); + break; + + case 0x08/8: /* START */ + voice->start = data & 0xfffff800; + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "voice %d, loop start=%08x\n", m_current_page & 0x1f, voice->start); + break; + + case 0x10/8: /* END */ + voice->end = data & 0xffffff80; + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "voice %d, loop end=%08x\n", m_current_page & 0x1f, voice->end); + break; + + case 0x18/8: /* ACCUM */ + voice->accum = data; + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "voice %d, accum=%08x\n", m_current_page & 0x1f, voice->accum); + break; + + case 0x20/8: /* O4(n-1) */ + voice->o4n1 = (INT32)(data << 14) >> 14; + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "voice %d, O4(n-1)=%05x\n", m_current_page & 0x1f, voice->o4n1 & 0x3ffff); + break; + + case 0x28/8: /* O3(n-1) */ + voice->o3n1 = (INT32)(data << 14) >> 14; + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "voice %d, O3(n-1)=%05x\n", m_current_page & 0x1f, voice->o3n1 & 0x3ffff); + break; + + case 0x30/8: /* O3(n-2) */ + voice->o3n2 = (INT32)(data << 14) >> 14; + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "voice %d, O3(n-2)=%05x\n", m_current_page & 0x1f, voice->o3n2 & 0x3ffff); + break; + + case 0x38/8: /* O2(n-1) */ + voice->o2n1 = (INT32)(data << 14) >> 14; + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "voice %d, O2(n-1)=%05x\n", m_current_page & 0x1f, voice->o2n1 & 0x3ffff); + break; + + case 0x40/8: /* O2(n-2) */ + voice->o2n2 = (INT32)(data << 14) >> 14; + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "voice %d, O2(n-2)=%05x\n", m_current_page & 0x1f, voice->o2n2 & 0x3ffff); + break; + + case 0x48/8: /* O1(n-1) */ + voice->o1n1 = (INT32)(data << 14) >> 14; + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "voice %d, O1(n-1)=%05x\n", m_current_page & 0x1f, voice->o1n1 & 0x3ffff); + break; + + case 0x50/8: /* W_ST */ + m_wst = data & 0x7f; + break; + + case 0x58/8: /* W_END */ + m_wend = data & 0x7f; + break; + + case 0x60/8: /* LR_END */ + m_lrend = data & 0x7f; + break; + + case 0x68/8: /* PAR - read only */ + case 0x70/8: /* IRQV - read only */ + break; + + case 0x78/8: /* PAGE */ + m_current_page = data & 0x7f; + break; + } +} + +inline void es5506_device::reg_write_test(es550x_voice *voice, offs_t offset, UINT32 data) +{ + switch (offset) + { + case 0x00/8: /* CHANNEL 0 LEFT */ + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "Channel 0 left test write %08x\n", data); + break; + + case 0x08/8: /* CHANNEL 0 RIGHT */ + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "Channel 0 right test write %08x\n", data); + break; + + case 0x10/8: /* CHANNEL 1 LEFT */ + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "Channel 1 left test write %08x\n", data); + break; + + case 0x18/8: /* CHANNEL 1 RIGHT */ + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "Channel 1 right test write %08x\n", data); + break; + + case 0x20/8: /* CHANNEL 2 LEFT */ + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "Channel 2 left test write %08x\n", data); + break; + + case 0x28/8: /* CHANNEL 2 RIGHT */ + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "Channel 2 right test write %08x\n", data); + break; + + case 0x30/8: /* CHANNEL 3 LEFT */ + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "Channel 3 left test write %08x\n", data); + break; + + case 0x38/8: /* CHANNEL 3 RIGHT */ + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "Channel 3 right test write %08x\n", data); + break; + + case 0x40/8: /* CHANNEL 4 LEFT */ + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "Channel 4 left test write %08x\n", data); + break; + + case 0x48/8: /* CHANNEL 4 RIGHT */ + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "Channel 4 right test write %08x\n", data); + break; + + case 0x50/8: /* CHANNEL 5 LEFT */ + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "Channel 5 left test write %08x\n", data); + break; + + case 0x58/8: /* CHANNEL 6 RIGHT */ + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "Channel 5 right test write %08x\n", data); + break; + + case 0x60/8: /* EMPTY */ + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "Test write EMPTY %08x\n", data); + break; + + case 0x68/8: /* PAR - read only */ + case 0x70/8: /* IRQV - read only */ + break; + + case 0x78/8: /* PAGE */ + m_current_page = data & 0x7f; + break; + } +} + +WRITE8_MEMBER( es5506_device::write ) +{ + es550x_voice *voice = &m_voice[m_current_page & 0x1f]; + int shift = 8 * (offset & 3); + + /* accumulate the data */ + m_write_latch = (m_write_latch & ~(0xff000000 >> shift)) | (data << (24 - shift)); + + /* wait for a write to complete */ + if (shift != 24) + return; + + /* force an update */ + m_stream->update(); + + /* switch off the page and register */ + if (m_current_page < 0x20) + reg_write_low(voice, offset / 4, m_write_latch); + else if (m_current_page < 0x40) + reg_write_high(voice, offset / 4, m_write_latch); + else + reg_write_test(voice, offset / 4, m_write_latch); + + /* clear the write latch when done */ + m_write_latch = 0; +} + + + +/********************************************************************************************** + + reg_read -- read from the specified ES5506 register + +***********************************************************************************************/ + +inline UINT32 es5506_device::reg_read_low(es550x_voice *voice, offs_t offset) +{ + UINT32 result = 0; + + switch (offset) + { + case 0x00/8: /* CR */ + result = voice->control; + break; + + case 0x08/8: /* FC */ + result = voice->freqcount; + break; + + case 0x10/8: /* LVOL */ + result = voice->lvol; + break; + + case 0x18/8: /* LVRAMP */ + result = voice->lvramp << 8; + break; + + case 0x20/8: /* RVOL */ + result = voice->rvol; + break; + + case 0x28/8: /* RVRAMP */ + result = voice->rvramp << 8; + break; + + case 0x30/8: /* ECOUNT */ + result = voice->ecount; + break; + + case 0x38/8: /* K2 */ + result = voice->k2; + break; + + case 0x40/8: /* K2RAMP */ + result = (voice->k2ramp << 8) | (voice->k2ramp >> 31); + break; + + case 0x48/8: /* K1 */ + result = voice->k1; + break; + + case 0x50/8: /* K1RAMP */ + result = (voice->k1ramp << 8) | (voice->k1ramp >> 31); + break; + + case 0x58/8: /* ACTV */ + result = m_active_voices; + break; + + case 0x60/8: /* MODE */ + result = m_mode; + break; + + case 0x68/8: /* PAR */ + if (!m_read_port_cb.isnull()) + result = m_read_port_cb(0); + break; + + case 0x70/8: /* IRQV */ + result = m_irqv; + update_internal_irq_state(); + break; + + case 0x78/8: /* PAGE */ + result = m_current_page; + break; + } + return result; +} + + +inline UINT32 es5506_device::reg_read_high(es550x_voice *voice, offs_t offset) +{ + UINT32 result = 0; + + switch (offset) + { + case 0x00/8: /* CR */ + result = voice->control; + break; + + case 0x08/8: /* START */ + result = voice->start; + break; + + case 0x10/8: /* END */ + result = voice->end; + break; + + case 0x18/8: /* ACCUM */ + result = voice->accum; + break; + + case 0x20/8: /* O4(n-1) */ + result = voice->o4n1 & 0x3ffff; + break; + + case 0x28/8: /* O3(n-1) */ + result = voice->o3n1 & 0x3ffff; + break; + + case 0x30/8: /* O3(n-2) */ + result = voice->o3n2 & 0x3ffff; + break; + + case 0x38/8: /* O2(n-1) */ + result = voice->o2n1 & 0x3ffff; + break; + + case 0x40/8: /* O2(n-2) */ + result = voice->o2n2 & 0x3ffff; + break; + + case 0x48/8: /* O1(n-1) */ + result = voice->o1n1 & 0x3ffff; + break; + + case 0x50/8: /* W_ST */ + result = m_wst; + break; + + case 0x58/8: /* W_END */ + result = m_wend; + break; + + case 0x60/8: /* LR_END */ + result = m_lrend; + break; + + case 0x68/8: /* PAR */ + if (!m_read_port_cb.isnull()) + result = m_read_port_cb(0); + break; + + case 0x70/8: /* IRQV */ + result = m_irqv; + update_internal_irq_state(); + break; + + case 0x78/8: /* PAGE */ + result = m_current_page; + break; + } + return result; +} +inline UINT32 es5506_device::reg_read_test(es550x_voice *voice, offs_t offset) +{ + UINT32 result = 0; + + switch (offset) + { + case 0x68/8: /* PAR */ + if (!m_read_port_cb.isnull()) + result = m_read_port_cb(0); + break; + + case 0x70/8: /* IRQV */ + result = m_irqv; + break; + + case 0x78/8: /* PAGE */ + result = m_current_page; + break; + } + return result; +} + +READ8_MEMBER( es5506_device::read ) +{ + es550x_voice *voice = &m_voice[m_current_page & 0x1f]; + int shift = 8 * (offset & 3); + + /* only read on offset 0 */ + if (shift != 0) + return m_read_latch >> (24 - shift); + + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "read from %02x/%02x -> ", m_current_page, offset / 4 * 8); + + /* force an update */ + m_stream->update(); + + /* switch off the page and register */ + if (m_current_page < 0x20) + m_read_latch = reg_read_low(voice, offset / 4); + else if (m_current_page < 0x40) + m_read_latch = reg_read_high(voice, offset / 4); + else + m_read_latch = reg_read_test(voice, offset / 4); + + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "%08x\n", m_read_latch); + + /* return the high byte */ + return m_read_latch >> 24; +} + + + +void es5506_device::voice_bank_w(int voice, int bank) +{ + m_voice[voice].exbank=bank; +} + + +/********************************************************************************************** + + reg_write -- handle a write to the selected ES5505 register + +***********************************************************************************************/ + +inline void es5505_device::reg_write_low(es550x_voice *voice, offs_t offset, UINT16 data, UINT16 mem_mask) +{ + switch (offset) + { + case 0x00: /* CR */ + if (ACCESSING_BITS_0_7) + { +#if RAINE_CHECK + voice->control &= ~(CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_DIR); +#else + voice->control &= ~(CONTROL_STOPMASK | CONTROL_BS0 | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ); +#endif + voice->control |= (data & (CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ)) | + ((data << 12) & CONTROL_BS0); + } + if (ACCESSING_BITS_8_15) + { + voice->control &= ~(CONTROL_CA0 | CONTROL_CA1 | CONTROL_LPMASK); + voice->control |= ((data >> 2) & CONTROL_LPMASK) | + ((data << 2) & (CONTROL_CA0 | CONTROL_CA1)); + } + + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "%s:voice %d, control=%04x (raw=%04x & %04x)\n", machine().describe_context(), m_current_page & 0x1f, voice->control, data, mem_mask ^ 0xffff); + break; + + case 0x01: /* FC */ + if (ACCESSING_BITS_0_7) + voice->freqcount = (voice->freqcount & ~0x001fe) | ((data & 0x00ff) << 1); + if (ACCESSING_BITS_8_15) + voice->freqcount = (voice->freqcount & ~0x1fe00) | ((data & 0xff00) << 1); + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "%s:voice %d, freq count=%08x\n", machine().describe_context(), m_current_page & 0x1f, voice->freqcount); + break; + + case 0x02: /* STRT (hi) */ + if (ACCESSING_BITS_0_7) + voice->start = (voice->start & ~0x03fc0000) | ((data & 0x00ff) << 18); + if (ACCESSING_BITS_8_15) + voice->start = (voice->start & ~0x7c000000) | ((data & 0x1f00) << 18); + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "%s:voice %d, loop start=%08x\n", machine().describe_context(), m_current_page & 0x1f, voice->start); + break; + + case 0x03: /* STRT (lo) */ + if (ACCESSING_BITS_0_7) + voice->start = (voice->start & ~0x00000380) | ((data & 0x00e0) << 2); + if (ACCESSING_BITS_8_15) + voice->start = (voice->start & ~0x0003fc00) | ((data & 0xff00) << 2); + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "%s:voice %d, loop start=%08x\n", machine().describe_context(), m_current_page & 0x1f, voice->start); + break; + + case 0x04: /* END (hi) */ + if (ACCESSING_BITS_0_7) + voice->end = (voice->end & ~0x03fc0000) | ((data & 0x00ff) << 18); + if (ACCESSING_BITS_8_15) + voice->end = (voice->end & ~0x7c000000) | ((data & 0x1f00) << 18); +#if RAINE_CHECK + voice->control |= CONTROL_STOP0; +#endif + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "%s:voice %d, loop end=%08x\n", machine().describe_context(), m_current_page & 0x1f, voice->end); + break; + + case 0x05: /* END (lo) */ + if (ACCESSING_BITS_0_7) + voice->end = (voice->end & ~0x00000380) | ((data & 0x00e0) << 2); + if (ACCESSING_BITS_8_15) + voice->end = (voice->end & ~0x0003fc00) | ((data & 0xff00) << 2); +#if RAINE_CHECK + voice->control |= CONTROL_STOP0; +#endif + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "%s:voice %d, loop end=%08x\n", machine().describe_context(), m_current_page & 0x1f, voice->end); + break; + + case 0x06: /* K2 */ + if (ACCESSING_BITS_0_7) + voice->k2 = (voice->k2 & ~0x00f0) | (data & 0x00f0); + if (ACCESSING_BITS_8_15) + voice->k2 = (voice->k2 & ~0xff00) | (data & 0xff00); + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "%s:voice %d, K2=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->k2); + break; + + case 0x07: /* K1 */ + if (ACCESSING_BITS_0_7) + voice->k1 = (voice->k1 & ~0x00f0) | (data & 0x00f0); + if (ACCESSING_BITS_8_15) + voice->k1 = (voice->k1 & ~0xff00) | (data & 0xff00); + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "%s:voice %d, K1=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->k1); + break; + + case 0x08: /* LVOL */ + if (ACCESSING_BITS_8_15) + voice->lvol = (voice->lvol & ~0xff00) | (data & 0xff00); + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "%s:voice %d, left vol=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->lvol); + break; + + case 0x09: /* RVOL */ + if (ACCESSING_BITS_8_15) + voice->rvol = (voice->rvol & ~0xff00) | (data & 0xff00); + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "%s:voice %d, right vol=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->rvol); + break; + + case 0x0a: /* ACC (hi) */ + if (ACCESSING_BITS_0_7) + voice->accum = (voice->accum & ~0x03fc0000) | ((data & 0x00ff) << 18); + if (ACCESSING_BITS_8_15) + voice->accum = (voice->accum & ~0x7c000000) | ((data & 0x1f00) << 18); + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "%s:voice %d, accum=%08x\n", machine().describe_context(), m_current_page & 0x1f, voice->accum); + break; + + case 0x0b: /* ACC (lo) */ + if (ACCESSING_BITS_0_7) + voice->accum = (voice->accum & ~0x000003fc) | ((data & 0x00ff) << 2); + if (ACCESSING_BITS_8_15) + voice->accum = (voice->accum & ~0x0003fc00) | ((data & 0xff00) << 2); + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "%s:voice %d, accum=%08x\n", machine().describe_context(), m_current_page & 0x1f, voice->accum); + break; + + case 0x0c: /* unused */ + break; + + case 0x0d: /* ACT */ + if (ACCESSING_BITS_0_7) + { + m_active_voices = data & 0x1f; + m_sample_rate = m_master_clock / (16 * (m_active_voices + 1)); + m_stream->set_sample_rate(m_sample_rate); + + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "active voices=%d, sample_rate=%d\n", m_active_voices, m_sample_rate); + } + break; + + case 0x0e: /* IRQV - read only */ + break; + + case 0x0f: /* PAGE */ + if (ACCESSING_BITS_0_7) + m_current_page = data & 0x7f; + break; + } +} + + +inline void es5505_device::reg_write_high(es550x_voice *voice, offs_t offset, UINT16 data, UINT16 mem_mask) +{ + switch (offset) + { + case 0x00: /* CR */ + if (ACCESSING_BITS_0_7) + { + voice->control &= ~(CONTROL_STOPMASK | CONTROL_BS0 | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ); + voice->control |= (data & (CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ)) | + ((data << 12) & CONTROL_BS0); + } + if (ACCESSING_BITS_8_15) + { + voice->control &= ~(CONTROL_CA0 | CONTROL_CA1 | CONTROL_LPMASK); + voice->control |= ((data >> 2) & CONTROL_LPMASK) | + ((data << 2) & (CONTROL_CA0 | CONTROL_CA1)); + } + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "%s:voice %d, control=%04x (raw=%04x & %04x)\n", machine().describe_context(), m_current_page & 0x1f, voice->control, data, mem_mask); + break; + + case 0x01: /* O4(n-1) */ + if (ACCESSING_BITS_0_7) + voice->o4n1 = (voice->o4n1 & ~0x00ff) | (data & 0x00ff); + if (ACCESSING_BITS_8_15) + voice->o4n1 = (INT16)((voice->o4n1 & ~0xff00) | (data & 0xff00)); + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "%s:voice %d, O4(n-1)=%05x\n", machine().describe_context(), m_current_page & 0x1f, voice->o4n1 & 0x3ffff); + break; + + case 0x02: /* O3(n-1) */ + if (ACCESSING_BITS_0_7) + voice->o3n1 = (voice->o3n1 & ~0x00ff) | (data & 0x00ff); + if (ACCESSING_BITS_8_15) + voice->o3n1 = (INT16)((voice->o3n1 & ~0xff00) | (data & 0xff00)); + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "%s:voice %d, O3(n-1)=%05x\n", machine().describe_context(), m_current_page & 0x1f, voice->o3n1 & 0x3ffff); + break; + + case 0x03: /* O3(n-2) */ + if (ACCESSING_BITS_0_7) + voice->o3n2 = (voice->o3n2 & ~0x00ff) | (data & 0x00ff); + if (ACCESSING_BITS_8_15) + voice->o3n2 = (INT16)((voice->o3n2 & ~0xff00) | (data & 0xff00)); + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "%s:voice %d, O3(n-2)=%05x\n", machine().describe_context(), m_current_page & 0x1f, voice->o3n2 & 0x3ffff); + break; + + case 0x04: /* O2(n-1) */ + if (ACCESSING_BITS_0_7) + voice->o2n1 = (voice->o2n1 & ~0x00ff) | (data & 0x00ff); + if (ACCESSING_BITS_8_15) + voice->o2n1 = (INT16)((voice->o2n1 & ~0xff00) | (data & 0xff00)); + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "%s:voice %d, O2(n-1)=%05x\n", machine().describe_context(), m_current_page & 0x1f, voice->o2n1 & 0x3ffff); + break; + + case 0x05: /* O2(n-2) */ + if (ACCESSING_BITS_0_7) + voice->o2n2 = (voice->o2n2 & ~0x00ff) | (data & 0x00ff); + if (ACCESSING_BITS_8_15) + voice->o2n2 = (INT16)((voice->o2n2 & ~0xff00) | (data & 0xff00)); + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "%s:voice %d, O2(n-2)=%05x\n", machine().describe_context(), m_current_page & 0x1f, voice->o2n2 & 0x3ffff); + break; + + case 0x06: /* O1(n-1) */ + if (ACCESSING_BITS_0_7) + voice->o1n1 = (voice->o1n1 & ~0x00ff) | (data & 0x00ff); + if (ACCESSING_BITS_8_15) + voice->o1n1 = (INT16)((voice->o1n1 & ~0xff00) | (data & 0xff00)); + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "%s:voice %d, O1(n-1)=%05x (accum=%08x)\n", machine().describe_context(), m_current_page & 0x1f, voice->o2n1 & 0x3ffff, voice->accum); + break; + + case 0x07: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: /* unused */ + break; + + case 0x0d: /* ACT */ + if (ACCESSING_BITS_0_7) + { + m_active_voices = data & 0x1f; + m_sample_rate = m_master_clock / (16 * (m_active_voices + 1)); + m_stream->set_sample_rate(m_sample_rate); + + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "active voices=%d, sample_rate=%d\n", m_active_voices, m_sample_rate); + } + break; + + case 0x0e: /* IRQV - read only */ + break; + + case 0x0f: /* PAGE */ + if (ACCESSING_BITS_0_7) + m_current_page = data & 0x7f; + break; + } +} + + +inline void es5505_device::reg_write_test(es550x_voice *voice, offs_t offset, UINT16 data, UINT16 mem_mask) +{ + switch (offset) + { + case 0x00: /* CH0L */ + case 0x01: /* CH0R */ + case 0x02: /* CH1L */ + case 0x03: /* CH1R */ + case 0x04: /* CH2L */ + case 0x05: /* CH2R */ + case 0x06: /* CH3L */ + case 0x07: /* CH3R */ + break; + + case 0x08: /* SERMODE */ + m_mode = data & 0x0007; + break; + + case 0x09: /* PAR */ + break; + + case 0x0d: /* ACT */ + if (ACCESSING_BITS_0_7) + { + m_active_voices = data & 0x1f; + m_sample_rate = m_master_clock / (16 * (m_active_voices + 1)); + m_stream->set_sample_rate(m_sample_rate); + + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "active voices=%d, sample_rate=%d\n", m_active_voices, m_sample_rate); + } + break; + + case 0x0e: /* IRQV - read only */ + break; + + case 0x0f: /* PAGE */ + if (ACCESSING_BITS_0_7) + m_current_page = data & 0x7f; + break; + } +} + + +WRITE16_MEMBER( es5505_device::write ) +{ + es550x_voice *voice = &m_voice[m_current_page & 0x1f]; + +// logerror("%s:ES5505 write %02x/%02x = %04x & %04x\n", machine().describe_context(), m_current_page, offset, data, mem_mask); + + /* force an update */ + m_stream->update(); + + /* switch off the page and register */ + if (m_current_page < 0x20) + reg_write_low(voice, offset, data, mem_mask); + else if (m_current_page < 0x40) + reg_write_high(voice, offset, data, mem_mask); + else + reg_write_test(voice, offset, data, mem_mask); +} + + + +/********************************************************************************************** + + reg_read -- read from the specified ES5505 register + +***********************************************************************************************/ + +inline UINT16 es5505_device::reg_read_low(es550x_voice *voice, offs_t offset) +{ + UINT16 result = 0; + + switch (offset) + { + case 0x00: /* CR */ + result = (voice->control & (CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ)) | + ((voice->control & CONTROL_BS0) >> 12) | + ((voice->control & CONTROL_LPMASK) << 2) | + ((voice->control & (CONTROL_CA0 | CONTROL_CA1)) >> 2) | + 0xf000; + break; + + case 0x01: /* FC */ + result = voice->freqcount >> 1; + break; + + case 0x02: /* STRT (hi) */ + result = voice->start >> 18; + break; + + case 0x03: /* STRT (lo) */ + result = voice->start >> 2; + break; + + case 0x04: /* END (hi) */ + result = voice->end >> 18; + break; + + case 0x05: /* END (lo) */ + result = voice->end >> 2; + break; + + case 0x06: /* K2 */ + result = voice->k2; + break; + + case 0x07: /* K1 */ + result = voice->k1; + break; + + case 0x08: /* LVOL */ + result = voice->lvol; + break; + + case 0x09: /* RVOL */ + result = voice->rvol; + break; + + case 0x0a: /* ACC (hi) */ + result = voice->accum >> 18; + break; + + case 0x0b: /* ACC (lo) */ + result = voice->accum >> 2; + break; + + case 0x0c: /* unused */ + break; + + case 0x0d: /* ACT */ + result = m_active_voices; + break; + + case 0x0e: /* IRQV */ + result = m_irqv; + update_internal_irq_state(); + break; + + case 0x0f: /* PAGE */ + result = m_current_page; + break; + } + return result; +} + + +inline UINT16 es5505_device::reg_read_high(es550x_voice *voice, offs_t offset) +{ + UINT16 result = 0; + + switch (offset) + { + case 0x00: /* CR */ + result = (voice->control & (CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ)) | + ((voice->control & CONTROL_BS0) >> 12) | + ((voice->control & CONTROL_LPMASK) << 2) | + ((voice->control & (CONTROL_CA0 | CONTROL_CA1)) >> 2) | + 0xf000; + break; + + case 0x01: /* O4(n-1) */ + result = voice->o4n1; + break; + + case 0x02: /* O3(n-1) */ + result = voice->o3n1; + break; + + case 0x03: /* O3(n-2) */ + result = voice->o3n2; + break; + + case 0x04: /* O2(n-1) */ + result = voice->o2n1; + break; + + case 0x05: /* O2(n-2) */ + result = voice->o2n2; + break; + + case 0x06: /* O1(n-1) */ + /* special case for the Taito F3 games: they set the accumulator on a stopped */ + /* voice and assume the filters continue to process the data. They then read */ + /* the O1(n-1) in order to extract raw data from the sound ROMs. Since we don't */ + /* want to waste time filtering stopped channels, we just look for a read from */ + /* this register on a stopped voice, and return the raw sample data at the */ + /* accumulator */ + if ((voice->control & CONTROL_STOPMASK) && m_region_base[voice->control >> 14]) + { + voice->o1n1 = m_region_base[voice->control >> 14][voice->exbank + (voice->accum >> 11)]; + // logerror("%02x %08x ==> %08x\n",voice->o1n1,voice->control >> 14,voice->exbank + (voice->accum >> 11)); + } + result = voice->o1n1; + break; + + case 0x07: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: /* unused */ + break; + + case 0x0d: /* ACT */ + result = m_active_voices; + break; + + case 0x0e: /* IRQV */ + result = m_irqv; + update_internal_irq_state(); + break; + + case 0x0f: /* PAGE */ + result = m_current_page; + break; + } + return result; +} + + +inline UINT16 es5505_device::reg_read_test(es550x_voice *voice, offs_t offset) +{ + UINT16 result = 0; + + switch (offset) + { + case 0x00: /* CH0L */ + case 0x01: /* CH0R */ + case 0x02: /* CH1L */ + case 0x03: /* CH1R */ + case 0x04: /* CH2L */ + case 0x05: /* CH2R */ + case 0x06: /* CH3L */ + case 0x07: /* CH3R */ + break; + + case 0x08: /* SERMODE */ + result = m_mode; + break; + + case 0x09: /* PAR */ + if (!m_read_port_cb.isnull()) + result = m_read_port_cb(0); + break; + + case 0x0f: /* PAGE */ + result = m_current_page; + break; + } + return result; +} + + +READ16_MEMBER( es5505_device::read ) +{ + es550x_voice *voice = &m_voice[m_current_page & 0x1f]; + UINT16 result = 0; + + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "read from %02x/%02x -> ", m_current_page, offset); + + /* force an update */ + m_stream->update(); + + /* switch off the page and register */ + if (m_current_page < 0x20) + result = reg_read_low(voice, offset); + else if (m_current_page < 0x40) + result = reg_read_high(voice, offset); + else + result = reg_read_test(voice, offset); + + if (LOG_COMMANDS && m_eslog) + fprintf(m_eslog, "%04x (accum=%08x)\n", result, voice->accum); + + /* return the high byte */ + return result; +} + + + +void es5505_device::voice_bank_w(int voice, int bank) +{ +#if RAINE_CHECK + m_voice[voice].control = CONTROL_STOPMASK; +#endif + m_voice[voice].exbank=bank; +} + + +//------------------------------------------------- +// sound_stream_update - handle a stream update +//------------------------------------------------- + +void es550x_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +{ +} + +void es5506_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +{ +#if MAKE_WAVS + /* start the logging once we have a sample rate */ + if (m_sample_rate) + { + if (!m_wavraw) + m_wavraw = wav_open("raw.wav", m_sample_rate, 2); + } +#endif + + /* loop until all samples are output */ + int offset = 0; + while (samples) + { + int length = (samples > MAX_SAMPLE_CHUNK) ? MAX_SAMPLE_CHUNK : samples; + + generate_samples(outputs, offset, length); + +#if MAKE_WAVS + /* log the raw data */ + if (m_wavraw) { + /* determine left/right source data */ + INT32 *lsrc = m_scratch, *rsrc = m_scratch + length; + int channel; + memset(lsrc, 0, sizeof(INT32) * length * 2); + /* loop over the output channels */ + for (channel = 0; channel < m_channels; channel++) { + INT32 *l = outputs[(channel << 1)] + offset; + INT32 *r = outputs[(channel << 1) + 1] + offset; + /* add the current channel's samples to the WAV data */ + for (samp = 0; samp < length; samp++) { + lsrc[samp] += l[samp]; + rsrc[samp] += r[samp]; + } + } + wav_add_data_32lr(m_wavraw, lsrc, rsrc, length, 4); + } +#endif + + /* account for these samples */ + offset += length; + samples -= length; + } +} + +void es5505_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +{ +#if MAKE_WAVS + /* start the logging once we have a sample rate */ + if (m_sample_rate) + { + if (!m_wavraw) + m_wavraw = wav_open("raw.wav", m_sample_rate, 2); + } +#endif + + /* loop until all samples are output */ + int offset = 0; + while (samples) + { + int length = (samples > MAX_SAMPLE_CHUNK) ? MAX_SAMPLE_CHUNK : samples; + + generate_samples(outputs, offset, length); + +#if MAKE_WAVS + /* log the raw data */ + if (m_wavraw) { + /* determine left/right source data */ + INT32 *lsrc = m_scratch, *rsrc = m_scratch + length; + int channel; + memset(lsrc, 0, sizeof(INT32) * length * 2); + /* loop over the output channels */ + for (channel = 0; channel < m_channels; channel++) { + INT32 *l = outputs[(channel << 1)] + offset; + INT32 *r = outputs[(channel << 1) + 1] + offset; + /* add the current channel's samples to the WAV data */ + for (samp = 0; samp < length; samp++) { + lsrc[samp] += l[samp]; + rsrc[samp] += r[samp]; + } + } + wav_add_data_32lr(m_wavraw, lsrc, rsrc, length, 4); + } +#endif + + /* account for these samples */ + offset += length; + samples -= length; + } +} |