summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/es5503.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound/es5503.cpp')
-rw-r--r--src/devices/sound/es5503.cpp165
1 files changed, 103 insertions, 62 deletions
diff --git a/src/devices/sound/es5503.cpp b/src/devices/sound/es5503.cpp
index f22d11473fc..fc23656d36b 100644
--- a/src/devices/sound/es5503.cpp
+++ b/src/devices/sound/es5503.cpp
@@ -2,7 +2,7 @@
// copyright-holders:R. Belmont
/*
- ES5503 - Ensoniq ES5503 "DOC" emulator v2.1.2
+ ES5503 - Ensoniq ES5503 "DOC" emulator v2.3
By R. Belmont.
Copyright R. Belmont.
@@ -13,8 +13,8 @@
(used in the "Soundscape" series of ISA PC sound cards) followed on a fundamentally
similar architecture.
- Bugs: On the real silicon, oscillators 30 and 31 have random volume fluctuations and are
- unusable for playback. We don't attempt to emulate that. :-)
+ Bugs: On the real silicon, the uppermost enabled oscillator contributes to the output 3 times.
+ This is likely why the Apple IIgs system software doesn't let you use oscillators 30 and 31.
Additionally, in "swap" mode, there's one cycle when the switch takes place where the
oscillator's output is 0x80 (centerline) regardless of the sample data. This can
@@ -33,6 +33,9 @@
2.1.2 (RB) - Fixed SoundSmith POLY.SYNTH inst where one-shot on the even oscillator and swap on the odd should loop.
Conversely, the intro voice in FTA Delta Demo has swap on the even and one-shot on the odd and doesn't
want to loop.
+ 2.1.3 (RB) - Fixed oscillator enable register off-by-1 which caused everything to be half a step sharp.
+ 2.2 (RB) - More precise one-shot even/swap odd behavior from hardware observations with Ian Brumby's SWAPTEST.
+ 2.3 (RB) - Sync & AM modes added, emulate the volume glitch for the highest-numbered enabled oscillator.
*/
#include "emu.h"
@@ -55,45 +58,62 @@ static constexpr int resshifts[8] = { 9, 10, 11, 12, 13, 14, 15, 16 };
// es5503_device - constructor
//-------------------------------------------------
-es5503_device::es5503_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, ES5503, tag, owner, clock),
- device_sound_interface(mconfig, *this),
- device_rom_interface(mconfig, *this),
- m_irq_func(*this),
- m_adc_func(*this)
+es5503_device::es5503_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, ES5503, tag, owner, clock),
+ device_sound_interface(mconfig, *this),
+ device_rom_interface(mconfig, *this),
+ m_irq_func(*this),
+ m_adc_func(*this, 0)
{
}
//-------------------------------------------------
-// device_timer - called when our device timer expires
+// delayed_stream_update -
//-------------------------------------------------
-void es5503_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr)
+TIMER_CALLBACK_MEMBER(es5503_device::delayed_stream_update)
{
m_stream->update();
}
//-------------------------------------------------
-// rom_bank_updated - the rom bank has changed
+// rom_bank_pre_change - refresh the stream if the
+// ROM banking changes
//-------------------------------------------------
-void es5503_device::rom_bank_updated()
+void es5503_device::rom_bank_pre_change()
{
m_stream->update();
}
// halt_osc: handle halting an oscillator
-// chip = chip ptr
// onum = oscillator #
// type = 1 for 0 found in sample data, 0 for hit end of table size
void es5503_device::halt_osc(int onum, int type, uint32_t *accumulator, int resshift)
{
ES5503Osc *pOsc = &oscillators[onum];
ES5503Osc *pPartner = &oscillators[onum^1];
- const int mode = (pOsc->control>>1) & 3;
+ int mode = (pOsc->control>>1) & 3;
const int partnerMode = (pPartner->control>>1) & 3;
+ // check for sync mode
+ if (mode == MODE_SYNCAM)
+ {
+ if (!(onum & 1))
+ {
+ // we're even, so if the odd oscillator 1 below us is playing,
+ // restart it.
+ if (!(oscillators[onum - 1].control & 1))
+ {
+ oscillators[onum - 1].accumulator = 0;
+ }
+ }
+
+ // loop this oscillator for both sync and AM
+ mode = MODE_FREE;
+ }
+
// if 0 found in sample data or mode is not free-run, halt this oscillator
if ((mode != MODE_FREE) || (type != 0))
{
@@ -102,28 +122,28 @@ void es5503_device::halt_osc(int onum, int type, uint32_t *accumulator, int ress
else // preserve the relative phase of the oscillator when looping
{
uint16_t wtsize = pOsc->wtsize - 1;
- uint32_t altram = (*accumulator) >> resshift;
-
- if (altram > wtsize)
- {
- altram -= wtsize;
- }
- else
- {
- altram = 0;
- }
-
- *accumulator = altram << resshift;
+ *accumulator -= (wtsize << resshift);
}
- // if we're in swap mode or we're the even oscillator and the partner is in swap mode,
- // start the partner.
- if ((mode == MODE_SWAP) || ((partnerMode == MODE_SWAP) && ((onum & 1)==0)))
+ // if we're in swap mode, start the partner
+ if (mode == MODE_SWAP)
{
pPartner->control &= ~1; // clear the halt bit
pPartner->accumulator = 0; // and make sure it starts from the top (does this also need phase preservation?)
}
+ else
+ {
+ // if we're not swap and we're the even oscillator of the pair and the partner's swap
+ // but we aren't, we retrigger (!!!) Verified on IIgs hardware.
+ if ((partnerMode == MODE_SWAP) && ((onum & 1)==0))
+ {
+ pOsc->control &= ~1;
+ // preserve the phase in this case too
+ uint16_t wtsize = pOsc->wtsize - 1;
+ *accumulator -= (wtsize << resshift);
+ }
+ }
// IRQ enabled for this voice?
if (pOsc->control & 0x08)
{
@@ -144,7 +164,7 @@ void es5503_device::sound_stream_update(sound_stream &stream, std::vector<read_s
for (int chan = 0; chan < output_channels; chan++)
{
- for (osc = 0; osc < (oscsenabled+1); osc++)
+ for (osc = 0; osc < oscsenabled; osc++)
{
ES5503Osc *pOsc = &oscillators[osc];
@@ -152,13 +172,14 @@ void es5503_device::sound_stream_update(sound_stream &stream, std::vector<read_s
{
uint32_t wtptr = pOsc->wavetblpointer & wavemasks[pOsc->wavetblsize], altram;
uint32_t acc = pOsc->accumulator;
- uint16_t wtsize = pOsc->wtsize - 1;
+ const uint16_t wtsize = pOsc->wtsize - 1;
uint8_t ctrl = pOsc->control;
- uint16_t freq = pOsc->freq;
+ const uint16_t freq = pOsc->freq;
int16_t vol = pOsc->vol;
int8_t data = -128;
- int resshift = resshifts[pOsc->resolution] - pOsc->wavetblsize;
- uint32_t sizemask = accmasks[pOsc->wavetblsize];
+ const int resshift = resshifts[pOsc->resolution] - pOsc->wavetblsize;
+ const uint32_t sizemask = accmasks[pOsc->wavetblsize];
+ const int mode = (pOsc->control>>1) & 3;
mixp = &m_mix_buffer[0] + chan;
for (snum = 0; snum < samples; snum++)
@@ -178,7 +199,39 @@ void es5503_device::sound_stream_update(sound_stream &stream, std::vector<read_s
}
else
{
- *mixp += data * vol;
+ if (mode != MODE_SYNCAM)
+ {
+ *mixp += data * vol;
+ if (chan == (output_channels - 1))
+ {
+ *mixp += data * vol;
+ *mixp += data * vol;
+ }
+ }
+ else
+ {
+ // if we're odd, we play nothing ourselves
+ if (osc & 1)
+ {
+ if (osc < 31)
+ {
+ // if the next oscillator up is playing, it's volume becomes our control
+ if (!(oscillators[osc + 1].control & 1))
+ {
+ oscillators[osc + 1].vol = data ^ 0x80;
+ }
+ }
+ }
+ else // hard sync, both oscillators play?
+ {
+ *mixp += data * vol;
+ if (chan == (output_channels - 1))
+ {
+ *mixp += data * vol;
+ *mixp += data * vol;
+ }
+ }
+ }
mixp += output_channels;
if (altram >= wtsize)
@@ -214,9 +267,6 @@ void es5503_device::sound_stream_update(sound_stream &stream, std::vector<read_s
void es5503_device::device_start()
{
- m_irq_func.resolve_safe();
- m_adc_func.resolve_safe(0);
-
rege0 = 0xff;
save_pointer(STRUCT_MEMBER(oscillators, freq), 32);
@@ -230,17 +280,16 @@ void es5503_device::device_start()
save_pointer(STRUCT_MEMBER(oscillators, accumulator), 32);
save_pointer(STRUCT_MEMBER(oscillators, irqpend), 32);
- output_rate = (clock() / 8) / (2 + oscsenabled);
+ oscsenabled = 1;
+ output_rate = (clock() / 8) / (oscsenabled + 2);
m_stream = stream_alloc(0, output_channels, output_rate);
- m_timer = timer_alloc(0, nullptr);
- attotime update_rate = output_rate ? attotime::from_hz(output_rate) : attotime::never;
- m_timer->adjust(update_rate, 0, update_rate);
+ m_timer = timer_alloc(FUNC(es5503_device::delayed_stream_update), this);
}
void es5503_device::device_clock_changed()
{
- output_rate = (clock() / 8) / (2 + oscsenabled);
+ output_rate = (clock() / 8) / (oscsenabled + 2);
m_stream->set_sample_rate(output_rate);
m_mix_buffer.resize((output_rate/50)*8);
@@ -268,10 +317,9 @@ void es5503_device::device_reset()
}
oscsenabled = 1;
+ notify_clock_changed();
m_channel_strobe = 0;
-
- output_rate = (clock()/8)/34; // (input clock / 8) / # of oscs. enabled + 2
}
u8 es5503_device::read(offs_t offset)
@@ -327,7 +375,7 @@ u8 es5503_device::read(offs_t offset)
m_irq_func(0);
// scan all oscillators
- for (i = 0; i < oscsenabled+1; i++)
+ for (i = 0; i < oscsenabled; i++)
{
if (oscillators[i].irqpend)
{
@@ -343,7 +391,7 @@ u8 es5503_device::read(offs_t offset)
}
// if any oscillators still need to be serviced, assert IRQ again immediately
- for (i = 0; i < oscsenabled+1; i++)
+ for (i = 0; i < oscsenabled; i++)
{
if (oscillators[i].irqpend)
{
@@ -352,10 +400,10 @@ u8 es5503_device::read(offs_t offset)
}
}
- return retval;
+ return retval | 0x41;
case 0xe1: // oscillator enable
- return oscsenabled<<1;
+ return (oscsenabled - 1) << 1;
case 0xe2: // A/D converter
return m_adc_func();
@@ -397,7 +445,7 @@ void es5503_device::write(offs_t offset, u8 data)
break;
case 0xa0: // oscillator control
- // if a fresh key-on, reset the accumulator
+ // key on?
if ((oscillators[osc].control & 1) && (!(data&1)))
{
oscillators[osc].accumulator = 0;
@@ -429,18 +477,11 @@ void es5503_device::write(offs_t offset, u8 data)
break;
case 0xe1: // oscillator enable
- {
- oscsenabled = (data>>1) & 0x1f;
-
- output_rate = (clock()/8)/(2+oscsenabled);
- m_stream->set_sample_rate(output_rate);
-
- m_mix_buffer.resize((output_rate/50)*8);
-
- attotime update_rate = output_rate ? attotime::from_hz(output_rate) : attotime::never;
- m_timer->adjust(update_rate, 0, update_rate);
+ // The number here is the number of oscillators to enable -1 times 2. You can never
+ // have zero oscilllators enabled. So a value of 62 enables all 32 oscillators.
+ oscsenabled = ((data>>1) & 0x1f) + 1;
+ notify_clock_changed();
break;
- }
case 0xe2: // A/D converter
break;