mirror of
https://github.com/holub/mame
synced 2025-07-02 16:49:22 +03:00
ctk551: implement sound, promote to working (#8960)
This commit is contained in:
parent
6d9b8c9c75
commit
7ce27dadde
@ -11,7 +11,7 @@
|
|||||||
- Two timers, three 8-bit ports, two 8-bit ADCs
|
- Two timers, three 8-bit ports, two 8-bit ADCs
|
||||||
- Keyboard controller w/ key velocity detection
|
- Keyboard controller w/ key velocity detection
|
||||||
- MIDI UART
|
- MIDI UART
|
||||||
- 24-voice PCM sound (currently not emulated / fully understood)
|
- 24-voice DPCM sound
|
||||||
|
|
||||||
Earlier and later Casio keyboard models contain "uPD912" and "uPD914" chips,
|
Earlier and later Casio keyboard models contain "uPD912" and "uPD914" chips,
|
||||||
which are presumably similar.
|
which are presumably similar.
|
||||||
@ -52,9 +52,9 @@ void gt913_device::map(address_map &map)
|
|||||||
map(0xfac0, 0xffbf).ram(); // CTK-551 zeroes out this range at $0418
|
map(0xfac0, 0xffbf).ram(); // CTK-551 zeroes out this range at $0418
|
||||||
|
|
||||||
/* ffc0-ffcb: sound */
|
/* ffc0-ffcb: sound */
|
||||||
map(0xffc0, 0xffc5).rw(m_sound, FUNC(gt913_sound_hle_device::data_r), FUNC(gt913_sound_hle_device::data_w));
|
map(0xffc0, 0xffc5).rw(m_sound, FUNC(gt913_sound_device::data_r), FUNC(gt913_sound_device::data_w));
|
||||||
map(0xffc6, 0xffc7).w(m_sound, FUNC(gt913_sound_hle_device::command_w));
|
map(0xffc6, 0xffc7).w(m_sound, FUNC(gt913_sound_device::command_w));
|
||||||
map(0xffca, 0xffcb).r(m_sound, FUNC(gt913_sound_hle_device::status_r));
|
map(0xffca, 0xffcb).r(m_sound, FUNC(gt913_sound_device::status_r));
|
||||||
|
|
||||||
/* ffd0-ffd5: key controller */
|
/* ffd0-ffd5: key controller */
|
||||||
map(0xffd0, 0xffd1).r(m_kbd, FUNC(gt913_kbd_hle_device::read));
|
map(0xffd0, 0xffd1).r(m_kbd, FUNC(gt913_kbd_hle_device::read));
|
||||||
@ -88,7 +88,14 @@ void gt913_device::device_add_mconfig(machine_config &config)
|
|||||||
{
|
{
|
||||||
GT913_INTC(config, "intc");
|
GT913_INTC(config, "intc");
|
||||||
|
|
||||||
GT913_SOUND_HLE(config, m_sound, 0);
|
/*
|
||||||
|
generate sound at 104 cycles per sample (= 144.231 kHz sample clock to the DAC)
|
||||||
|
on keyboard models that include a DSP, this also results in a multiple
|
||||||
|
of the 36.058 kHz CPU->DSP sync signal shown in some schematics (WK-1200 and others)
|
||||||
|
*/
|
||||||
|
GT913_SOUND(config, m_sound, std::round(clock() / 104.0f));
|
||||||
|
m_sound->set_device_rom_tag(tag());
|
||||||
|
|
||||||
GT913_KBD_HLE(config, m_kbd, 0);
|
GT913_KBD_HLE(config, m_kbd, 0);
|
||||||
m_kbd->irq_cb().set([this](int val) { if (val) m_intc->internal_interrupt(5); });
|
m_kbd->irq_cb().set([this](int val) { if (val) m_intc->internal_interrupt(5); });
|
||||||
GT913_IO_HLE(config, m_io_hle, "intc", 6, 7);
|
GT913_IO_HLE(config, m_io_hle, "intc", 6, 7);
|
||||||
@ -102,7 +109,7 @@ void gt913_device::device_add_mconfig(machine_config &config)
|
|||||||
|
|
||||||
void gt913_device::uart_rate_w(uint8_t data)
|
void gt913_device::uart_rate_w(uint8_t data)
|
||||||
{
|
{
|
||||||
m_sci->brr_w(data >> 1);
|
m_sci->brr_w(data >> 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gt913_device::uart_control_w(uint8_t data)
|
void gt913_device::uart_control_w(uint8_t data)
|
||||||
|
@ -62,7 +62,7 @@ protected:
|
|||||||
required_device<gt913_intc_device> m_intc;
|
required_device<gt913_intc_device> m_intc;
|
||||||
|
|
||||||
/* sound */
|
/* sound */
|
||||||
required_device<gt913_sound_hle_device> m_sound;
|
required_device<gt913_sound_device> m_sound;
|
||||||
|
|
||||||
/* key controller */
|
/* key controller */
|
||||||
required_device<gt913_kbd_hle_device> m_kbd;
|
required_device<gt913_kbd_hle_device> m_kbd;
|
||||||
|
@ -107,7 +107,7 @@
|
|||||||
5e00 ff00 0 bsr rel8 -
|
5e00 ff00 0 bsr rel8 -
|
||||||
5f00 fff0 0 mov.w imm16 r16l
|
5f00 fff0 0 mov.w imm16 r16l
|
||||||
|
|
||||||
66000000 ff00ff8f 0 btst imm3 abs8
|
66000000 ff00ff8f 0 btst imm3 r16ihh
|
||||||
|
|
||||||
6800 ff80 0 mov.b r16ih r8l
|
6800 ff80 0 mov.b r16ih r8l
|
||||||
6900 ff88 0 mov.w r16ih r16l
|
6900 ff88 0 mov.w r16ih r16l
|
||||||
|
@ -108,9 +108,9 @@ void gt913_io_hle_device::timer_adjust(offs_t num)
|
|||||||
logerror("unknown timer %u prescaler %u (pc = %04x)\n", num, m_timer_control[num] & 0x7, m_cpu->pc());
|
logerror("unknown timer %u prescaler %u (pc = %04x)\n", num, m_timer_control[num] & 0x7, m_cpu->pc());
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case 0:
|
case 0:
|
||||||
clocks <<= 1; break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
clocks <<= 10; break;
|
clocks <<= 9; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
attotime period = m_cpu->clocks_to_attotime(clocks);
|
attotime period = m_cpu->clocks_to_attotime(clocks);
|
||||||
|
@ -8,13 +8,13 @@
|
|||||||
which is then input to either a serial DAC or a HG51B-based DSP,
|
which is then input to either a serial DAC or a HG51B-based DSP,
|
||||||
depending on the model of keyboard.
|
depending on the model of keyboard.
|
||||||
|
|
||||||
Currently, the actual sample format in ROM is unknown.
|
The sample format, as well as other details such as the linear interpolation,
|
||||||
The serial output is twos-complement 16-bit PCM, but the data in ROM
|
are covered in these two Japanese patents:
|
||||||
doesn't seem to be - reading it as such produces sounds that are
|
https://patents.google.com/patent/JP3603343B2/en
|
||||||
somewhat recognizable, but highly distorted.
|
https://patents.google.com/patent/JPH07199996A/en
|
||||||
|
|
||||||
For now, all known (and unknown) register writes are just logged
|
TODO: Volume envelope rates still need adjusting.
|
||||||
without generating any sound.
|
(See comment in gt913_sound_device::command_w regarding command 6007)
|
||||||
|
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
@ -26,140 +26,319 @@
|
|||||||
// DEVICE DEFINITIONS
|
// DEVICE DEFINITIONS
|
||||||
//**************************************************************************
|
//**************************************************************************
|
||||||
|
|
||||||
DEFINE_DEVICE_TYPE(GT913_SOUND_HLE, gt913_sound_hle_device, "gt913_sound_hle", "Casio GT913F sound (HLE)")
|
DEFINE_DEVICE_TYPE(GT913_SOUND, gt913_sound_device, "gt913_sound_hle", "Casio GT913F sound")
|
||||||
|
|
||||||
gt913_sound_hle_device::gt913_sound_hle_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
|
// expand 2-bit exponent deltas
|
||||||
device_t(mconfig, GT913_SOUND_HLE, tag, owner, clock)
|
const u8 gt913_sound_device::exp_2_to_3[4] = { 0, 1, 2, 7 };
|
||||||
|
|
||||||
|
// sign-extend 7-bit sample deltas
|
||||||
|
const s8 gt913_sound_device::sample_7_to_8[128] =
|
||||||
|
{
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||||
|
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||||
|
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||||
|
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||||
|
-64, -63, -62, -61, -60, -59, -58, -57, -56, -55, -54, -53, -52, -51, -50, -49,
|
||||||
|
-48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33,
|
||||||
|
-32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17,
|
||||||
|
-16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1
|
||||||
|
};
|
||||||
|
|
||||||
|
gt913_sound_device::gt913_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
|
||||||
|
: device_t(mconfig, GT913_SOUND, tag, owner, clock)
|
||||||
|
, device_sound_interface(mconfig, *this)
|
||||||
|
, device_rom_interface(mconfig, *this)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void gt913_sound_hle_device::device_start()
|
void gt913_sound_device::device_start()
|
||||||
{
|
{
|
||||||
|
m_stream = stream_alloc(0, 2, clock());
|
||||||
|
|
||||||
save_item(NAME(m_gain));
|
save_item(NAME(m_gain));
|
||||||
save_item(NAME(m_data));
|
save_item(NAME(m_data));
|
||||||
|
|
||||||
save_item(NAME(m_volume_target));
|
save_item(STRUCT_MEMBER(m_voices, m_enable));
|
||||||
save_item(NAME(m_volume_rate));
|
|
||||||
|
save_item(STRUCT_MEMBER(m_voices, m_addr_start));
|
||||||
|
save_item(STRUCT_MEMBER(m_voices, m_addr_end));
|
||||||
|
save_item(STRUCT_MEMBER(m_voices, m_addr_loop));
|
||||||
|
|
||||||
|
save_item(STRUCT_MEMBER(m_voices, m_addr_current));
|
||||||
|
save_item(STRUCT_MEMBER(m_voices, m_addr_frac));
|
||||||
|
save_item(STRUCT_MEMBER(m_voices, m_pitch));
|
||||||
|
|
||||||
|
save_item(STRUCT_MEMBER(m_voices, m_sample));
|
||||||
|
save_item(STRUCT_MEMBER(m_voices, m_sample_next));
|
||||||
|
save_item(STRUCT_MEMBER(m_voices, m_exp));
|
||||||
|
|
||||||
|
save_item(STRUCT_MEMBER(m_voices, m_volume_current));
|
||||||
|
save_item(STRUCT_MEMBER(m_voices, m_volume_target));
|
||||||
|
save_item(STRUCT_MEMBER(m_voices, m_volume_rate));
|
||||||
|
save_item(STRUCT_MEMBER(m_voices, m_volume_end));
|
||||||
|
|
||||||
|
save_item(STRUCT_MEMBER(m_voices, m_balance));
|
||||||
|
save_item(STRUCT_MEMBER(m_voices, m_gain));
|
||||||
}
|
}
|
||||||
|
|
||||||
void gt913_sound_hle_device::device_reset()
|
void gt913_sound_device::device_reset()
|
||||||
{
|
{
|
||||||
m_gain = 0;
|
m_gain = 0;
|
||||||
std::memset(m_data, 0, sizeof(m_data));
|
std::memset(m_data, 0, sizeof(m_data));
|
||||||
std::memset(m_volume_target, 0, sizeof(m_volume_target));
|
|
||||||
std::memset(m_volume_rate, 0, sizeof(m_volume_rate));
|
std::memset(m_voices, 0, sizeof(m_voices));
|
||||||
}
|
}
|
||||||
|
|
||||||
void gt913_sound_hle_device::data_w(offs_t offset, uint16_t data)
|
void gt913_sound_device::sound_stream_update(sound_stream& stream, std::vector<read_stream_view> const& inputs, std::vector<write_stream_view>& outputs)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < outputs[0].samples(); i++)
|
||||||
|
{
|
||||||
|
s64 left = 0, right = 0;
|
||||||
|
|
||||||
|
for (auto& voice : m_voices)
|
||||||
|
{
|
||||||
|
if (voice.m_enable)
|
||||||
|
mix_sample(voice, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
outputs[0].put_int_clamp(i, (left * m_gain) >> 26, 32678);
|
||||||
|
outputs[1].put_int_clamp(i, (right * m_gain) >> 26, 32768);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gt913_sound_device::rom_bank_updated()
|
||||||
|
{
|
||||||
|
m_stream->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void gt913_sound_device::mix_sample(voice_t& voice, s64& left, s64& right)
|
||||||
|
{
|
||||||
|
// update sample position
|
||||||
|
voice.m_addr_frac += voice.m_pitch;
|
||||||
|
while (voice.m_addr_frac >= (1 << 25))
|
||||||
|
{
|
||||||
|
voice.m_addr_frac -= (1 << 25);
|
||||||
|
update_sample(voice);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update volume envelope
|
||||||
|
if (voice.m_volume_target > voice.m_volume_current
|
||||||
|
&& (voice.m_volume_target - voice.m_volume_current) > voice.m_volume_rate)
|
||||||
|
{
|
||||||
|
voice.m_volume_current += voice.m_volume_rate;
|
||||||
|
}
|
||||||
|
else if (voice.m_volume_target < voice.m_volume_current
|
||||||
|
&& (voice.m_volume_current - voice.m_volume_target) > voice.m_volume_rate)
|
||||||
|
{
|
||||||
|
voice.m_volume_current -= voice.m_volume_rate;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
voice.m_volume_current = voice.m_volume_target;
|
||||||
|
}
|
||||||
|
|
||||||
|
// interpolate, apply envelope + channel gain, and mix into output
|
||||||
|
const u8 step = (voice.m_addr_frac >> 22) & 7;
|
||||||
|
const u8 env = (voice.m_volume_current >> 24);
|
||||||
|
/*
|
||||||
|
the current envelope level effects amplitude non-linearly, just apply the value twice
|
||||||
|
(this hardware family is branded as "A² (A-Square) Sound Source" in some of Casio's
|
||||||
|
promotional materials, possibly for this reason?)
|
||||||
|
*/
|
||||||
|
const s64 sample = ((s64)voice.m_sample + (voice.m_sample_next * step / 8)) * voice.m_gain * env * env;
|
||||||
|
|
||||||
|
left += sample * voice.m_balance[0];
|
||||||
|
right += sample * voice.m_balance[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void gt913_sound_device::update_sample(voice_t& voice)
|
||||||
|
{
|
||||||
|
voice.m_sample += voice.m_sample_next;
|
||||||
|
|
||||||
|
if (voice.m_addr_current == (voice.m_addr_loop | 1))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
The last 12 bytes of each sample are a table containing five sample and exponent value pairs
|
||||||
|
for the data words immediately after the loop point. The first pair corresponds to what the
|
||||||
|
sample and exponent value will be _after_ processing the first word after the loop,
|
||||||
|
so once we've reached that point, use those values to reload the current sample and exponent
|
||||||
|
*/
|
||||||
|
const u32 addr_loop_data = (voice.m_addr_end + 1) & ~1;
|
||||||
|
|
||||||
|
voice.m_sample_next = read_word(addr_loop_data) - voice.m_sample;
|
||||||
|
voice.m_exp = read_word(addr_loop_data + 10) & 7;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
For all other samples, just get the next sample delta value.
|
||||||
|
For even-numbered samples, also update the exponent/shift value.
|
||||||
|
*/
|
||||||
|
const u16 word = read_word(voice.m_addr_current & ~1);
|
||||||
|
s16 delta = 0;
|
||||||
|
|
||||||
|
if (!BIT(voice.m_addr_current, 0))
|
||||||
|
{
|
||||||
|
voice.m_exp += exp_2_to_3[word & 3];
|
||||||
|
voice.m_exp &= 7;
|
||||||
|
delta = sample_7_to_8[(word >> 2) & 0x7f];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delta = sample_7_to_8[word >> 9];
|
||||||
|
}
|
||||||
|
|
||||||
|
voice.m_sample_next = delta * (1 << voice.m_exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
voice.m_addr_current++;
|
||||||
|
if (voice.m_addr_current == voice.m_addr_end)
|
||||||
|
{
|
||||||
|
voice.m_addr_current = voice.m_addr_loop;
|
||||||
|
|
||||||
|
if (voice.m_addr_loop == voice.m_addr_end)
|
||||||
|
voice.m_enable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gt913_sound_device::data_w(offs_t offset, u16 data)
|
||||||
{
|
{
|
||||||
assert(offset < 3);
|
assert(offset < 3);
|
||||||
m_data[offset] = data;
|
m_data[offset] = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t gt913_sound_hle_device::data_r(offs_t offset)
|
u16 gt913_sound_device::data_r(offs_t offset)
|
||||||
{
|
{
|
||||||
assert(offset < 3);
|
assert(offset < 3);
|
||||||
return m_data[offset];
|
return m_data[offset];
|
||||||
}
|
}
|
||||||
|
|
||||||
void gt913_sound_hle_device::command_w(uint16_t data)
|
void gt913_sound_device::command_w(u16 data)
|
||||||
{
|
{
|
||||||
uint8_t voicenum = (data & 0x1f00) >> 8;
|
m_stream->update();
|
||||||
uint16_t voicecmd = data & 0x60ff;
|
|
||||||
|
const uint8_t voicenum = (data & 0x1f00) >> 8;
|
||||||
|
const uint16_t voicecmd = data & 0x60ff;
|
||||||
|
|
||||||
if (data == 0x0012)
|
if (data == 0x0012)
|
||||||
{
|
{
|
||||||
uint8_t gain = m_data[0] & 0x3f;
|
m_gain = m_data[0] & 0x3f;
|
||||||
if (gain != m_gain)
|
return;
|
||||||
logerror("gain %u\n", gain);
|
|
||||||
m_gain = gain;
|
|
||||||
}
|
}
|
||||||
else if (voicenum >= 24)
|
else if (voicenum >= 24)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (voicecmd == 0x0008)
|
|
||||||
|
auto& voice = m_voices[voicenum];
|
||||||
|
if (voicecmd == 0x0008)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Set the voice's sample start point as a ROM address.
|
sample start addresses seem to need to be word-aligned to decode properly
|
||||||
This is usually word-aligned, but not always
|
(see: ctk551 "Trumpet" patch, which will have a bad exponent value otherwise)
|
||||||
(e.g. ctk551's lowest piano sample is at address 0x5a801)
|
this apparently doesn't apply to end/loop addresses, though, or else samples
|
||||||
|
may loop badly or even become noticeably detuned
|
||||||
|
TODO: is the LSB of start addresses supposed to indicate something else, then?
|
||||||
*/
|
*/
|
||||||
uint32_t samplestart = (m_data[1] | (m_data[2] << 16)) & 0x1fffff;
|
voice.m_addr_start = (m_data[1] | (m_data[2] << 16)) & 0x1ffffe;
|
||||||
logerror("voice %u sample start 0x%06x\n", voicenum, samplestart);
|
|
||||||
}
|
}
|
||||||
else if (voicecmd == 0x0000)
|
else if (voicecmd == 0x0000)
|
||||||
{
|
{
|
||||||
/*
|
voice.m_addr_end = (m_data[0] | (m_data[1] << 16)) & 0x1fffff;
|
||||||
Set the voice's sample end point as a ROM address.
|
|
||||||
*/
|
|
||||||
uint32_t sampleend = (m_data[0] | (m_data[1] << 16)) & 0x1fffff;
|
|
||||||
logerror("voice %u sample end 0x%06x\n", voicenum, sampleend);
|
|
||||||
}
|
}
|
||||||
else if (voicecmd == 0x2000)
|
else if (voicecmd == 0x2000)
|
||||||
{
|
{
|
||||||
/*
|
voice.m_addr_loop = (m_data[0] | (m_data[1] << 16)) & 0x1fffff;
|
||||||
Set the voice's sample loop point as a ROM address.
|
|
||||||
*/
|
|
||||||
uint32_t sampleloop = (m_data[0] | (m_data[1] << 16)) & 0x1fffff;
|
|
||||||
logerror("voice %u sample loop 0x%06x\n", voicenum, sampleloop);
|
|
||||||
}
|
}
|
||||||
else if (voicecmd == 0x200a)
|
else if (voicecmd == 0x200a)
|
||||||
{
|
{
|
||||||
logerror("voice %u cmd 0x200a (data = %02x)\n", voicenum, m_data[2] & 0xff);
|
/* TODO: what does bit 4 of data[2] do? ctk551 sets it unconditionally */
|
||||||
|
voice.m_exp = m_data[2] & 7;
|
||||||
}
|
}
|
||||||
else if (voicecmd == 0x200b)
|
else if (voicecmd == 0x200b)
|
||||||
{
|
{
|
||||||
/*
|
bool enable = BIT(m_data[2], 7);
|
||||||
Turn this voice on/off.
|
if (enable && !m_voices[voicenum].m_enable)
|
||||||
ctk551 turns output off before assigning a new note or instrument to this voice,
|
{
|
||||||
then turns output back on afterward
|
voice.m_addr_current = voice.m_addr_start;
|
||||||
*/
|
voice.m_addr_frac = 0;
|
||||||
logerror("voice %u output %s\n", voicenum, BIT(m_data[2], 7) ? "on" : "off");
|
voice.m_sample = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
voice.m_enable = enable;
|
||||||
|
voice.m_volume_end &= enable;
|
||||||
}
|
}
|
||||||
else if (voicecmd == 0x4004)
|
else if (voicecmd == 0x4004)
|
||||||
{
|
{
|
||||||
/*
|
voice.m_balance[0] = (m_data[1] & 0xe0) >> 5;
|
||||||
Set this voice's panning, in the form of left and right volume levels (3 bits each)
|
voice.m_balance[1] = (m_data[1] & 0x1c) >> 2;
|
||||||
*/
|
|
||||||
uint8_t left = (m_data[1] & 0xe0) >> 5;
|
|
||||||
uint8_t right = (m_data[1] & 0x1c) >> 2;
|
|
||||||
logerror("voice %u left %u right %u\n", voicenum, left, right);
|
|
||||||
}
|
}
|
||||||
else if (voicecmd == 0x4005)
|
else if (voicecmd == 0x4005)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Set the current pitch of this voice.
|
for pitch, data[1] apparently contains both the most and least significant of 4 bytes,
|
||||||
The actual format of the value is unknown, but presumably some kind of fixed point
|
with data0 in the middle. strange, but apparently correct (see higher octaves of ctk551 E.Piano2)
|
||||||
*/
|
*/
|
||||||
uint32_t pitch = (m_data[0] << 8) | (m_data[1] >> 8);
|
voice.m_pitch = (m_data[1] << 24) | (m_data[0] << 8) | (m_data[1] >> 8);
|
||||||
logerror("voice %u pitch 0x%06x\n", voicenum, pitch);
|
|
||||||
}
|
}
|
||||||
else if (voicecmd == 0x6006)
|
else if (voicecmd == 0x6006)
|
||||||
{
|
{
|
||||||
logerror("voice %u cmd 0x6006 (data = %02x)\n", voicenum, m_data[1] & 0xff);
|
/*
|
||||||
|
per-voice gain used for normalizing samples
|
||||||
|
currently treated such that the lower 3 bits are fractional
|
||||||
|
*/
|
||||||
|
voice.m_gain = m_data[1] & 0xff;
|
||||||
}
|
}
|
||||||
else if (voicecmd == 0x6007)
|
else if (voicecmd == 0x6007)
|
||||||
{
|
{
|
||||||
|
logerror("voice %u volume %u rate %u\n", voicenum, (m_data[0] >> 8), m_data[0] & 0xff);
|
||||||
/*
|
/*
|
||||||
Raise or lower the volume to a specified level at a specified rate.
|
only set a new volume level/rate if we haven't previously indicated the end of an envelope,
|
||||||
The actual volume level is probably 7.8 fixed point or something like that, but this command
|
unless the new level also has the high bit set. otherwise, a timer irq may try to update the
|
||||||
only sets the most significant bits.
|
normal envelope while other code is trying to force a note off
|
||||||
*/
|
*/
|
||||||
logerror("voice %u volume %u rate %u\n", voicenum, (m_data[0] >> 8) & 0x7f, m_data[0] & 0xff);
|
const bool end = BIT(m_data[0], 15);
|
||||||
m_volume_target[voicenum] = m_data[0] & 0x7f00;
|
if (!voice.m_volume_end || end)
|
||||||
m_volume_rate[voicenum] = m_data[0] & 0xff;
|
{
|
||||||
|
voice.m_volume_end = end;
|
||||||
|
|
||||||
|
voice.m_volume_target = (m_data[0] & 0x7f00) << 16;
|
||||||
|
/*
|
||||||
|
In addition to volume levels applying non-linearly, envelope rates
|
||||||
|
are also non-linear. Unfortunately, with the ctk-551's limited patch set and
|
||||||
|
lack of editing features, figuring out the correct behavior isn't easy.
|
||||||
|
This is essentially a rough estimate until a higher-end model (ctk-601 series, etc)
|
||||||
|
can be dumped and used for more detailed testing.
|
||||||
|
*/
|
||||||
|
const u8 x = m_data[0] & 0xff;
|
||||||
|
if (x >= 127)
|
||||||
|
voice.m_volume_rate = x << 21;
|
||||||
|
else if (x >= 63)
|
||||||
|
voice.m_volume_rate = x << 16;
|
||||||
|
else if (x >= 47)
|
||||||
|
voice.m_volume_rate = x << 14;
|
||||||
|
else if (x >= 31)
|
||||||
|
voice.m_volume_rate = x << 11;
|
||||||
|
else if (x >= 23)
|
||||||
|
voice.m_volume_rate = x << 9;
|
||||||
|
else if (x >= 15)
|
||||||
|
voice.m_volume_rate = x << 7;
|
||||||
|
else
|
||||||
|
voice.m_volume_rate = x << 5;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (voicecmd == 0x2028)
|
else if (voicecmd == 0x2028)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
ctk551 issues this command and then reads the voice's current volume from data0
|
ctk551 issues this command and then reads the voice's current volume from data0
|
||||||
to determine if it's time to start the next part of the volume envelope or not.
|
to determine if it's time to start the next part of the volume envelope or not.
|
||||||
For now, just return the "target" volume immediately
|
|
||||||
(TODO: also figure out what it expects to be returned in data1)
|
|
||||||
*/
|
*/
|
||||||
m_data[0] = m_volume_target[voicenum];
|
m_data[0] = voice.m_enable ? (voice.m_volume_current >> 16) : 0;
|
||||||
m_data[1] = 0;
|
/*
|
||||||
|
data1 is used to read consecutive output sample and detect zero crossings when
|
||||||
|
applying volume or expression changes to a MIDI channel
|
||||||
|
*/
|
||||||
|
m_data[1] = voice.m_sample;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -167,9 +346,11 @@ void gt913_sound_hle_device::command_w(uint16_t data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t gt913_sound_hle_device::status_r()
|
u16 gt913_sound_device::status_r()
|
||||||
{
|
{
|
||||||
/* ctk551 reads the current gain level out of the lower 6 bits and ignores the rest
|
/*
|
||||||
it's unknown what, if anything, the other bits are supposed to contain */
|
ctk551 reads the current gain level out of the lower 6 bits and ignores the rest
|
||||||
|
it's unknown what, if anything, the other bits are supposed to contain
|
||||||
|
*/
|
||||||
return m_gain & 0x3f;
|
return m_gain & 0x3f;
|
||||||
}
|
}
|
||||||
|
@ -9,37 +9,78 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "dirom.h"
|
||||||
|
|
||||||
//**************************************************************************
|
//**************************************************************************
|
||||||
// TYPE DEFINITIONS
|
// TYPE DEFINITIONS
|
||||||
//**************************************************************************
|
//**************************************************************************
|
||||||
|
|
||||||
// ======================> gt913_sound_hle_device
|
// ======================> gt913_sound_device
|
||||||
|
|
||||||
class gt913_sound_hle_device : public device_t
|
class gt913_sound_device : public device_t,
|
||||||
|
public device_sound_interface,
|
||||||
|
public device_rom_interface<21, 1, 0, ENDIANNESS_BIG>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// construction/destruction
|
static constexpr feature_type imperfect_features() { return feature::SOUND; }
|
||||||
gt913_sound_hle_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
|
|
||||||
|
|
||||||
void data_w(offs_t offset, uint16_t data);
|
// construction/destruction
|
||||||
uint16_t data_r(offs_t offset);
|
gt913_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0);
|
||||||
void command_w(uint16_t data);
|
|
||||||
uint16_t status_r();
|
void data_w(offs_t offset, u16 data);
|
||||||
|
u16 data_r(offs_t offset);
|
||||||
|
void command_w(u16 data);
|
||||||
|
u16 status_r();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// device_t overrides
|
// device_t overrides
|
||||||
virtual void device_start() override;
|
virtual void device_start() override;
|
||||||
virtual void device_reset() override;
|
virtual void device_reset() override;
|
||||||
|
|
||||||
private:
|
// device_sound_interface overrides
|
||||||
uint8_t m_gain;
|
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
|
||||||
uint16_t m_data[3];
|
|
||||||
|
|
||||||
uint16_t m_volume_target[24];
|
// device_rom_interface overrides
|
||||||
uint8_t m_volume_rate[24];
|
virtual void rom_bank_updated() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
sound_stream *m_stream;
|
||||||
|
|
||||||
|
u8 m_gain;
|
||||||
|
u16 m_data[3];
|
||||||
|
|
||||||
|
static const u8 exp_2_to_3[4];
|
||||||
|
static const s8 sample_7_to_8[128];
|
||||||
|
|
||||||
|
struct voice_t
|
||||||
|
{
|
||||||
|
bool m_enable;
|
||||||
|
|
||||||
|
u32 m_addr_start;
|
||||||
|
u32 m_addr_end;
|
||||||
|
u32 m_addr_loop;
|
||||||
|
|
||||||
|
u32 m_addr_current;
|
||||||
|
u32 m_addr_frac, m_pitch;
|
||||||
|
|
||||||
|
s16 m_sample, m_sample_next;
|
||||||
|
u8 m_exp;
|
||||||
|
|
||||||
|
u32 m_volume_current, m_volume_target;
|
||||||
|
u32 m_volume_rate;
|
||||||
|
bool m_volume_end;
|
||||||
|
|
||||||
|
u8 m_balance[2];
|
||||||
|
u8 m_gain;
|
||||||
|
};
|
||||||
|
|
||||||
|
void mix_sample(voice_t& voice, s64& left, s64& right);
|
||||||
|
void update_sample(voice_t& voice);
|
||||||
|
|
||||||
|
voice_t m_voices[24];
|
||||||
};
|
};
|
||||||
|
|
||||||
// device type definition
|
// device type definition
|
||||||
DECLARE_DEVICE_TYPE(GT913_SOUND_HLE, gt913_sound_hle_device)
|
DECLARE_DEVICE_TYPE(GT913_SOUND, gt913_sound_device)
|
||||||
|
|
||||||
#endif // MAME_AUDIO_GT913_H
|
#endif // MAME_AUDIO_GT913_H
|
||||||
|
@ -53,6 +53,7 @@
|
|||||||
#include "video/hd44780.h"
|
#include "video/hd44780.h"
|
||||||
#include "emupal.h"
|
#include "emupal.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
|
#include "speaker.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@ -64,6 +65,7 @@ public:
|
|||||||
, m_maincpu(*this, "maincpu")
|
, m_maincpu(*this, "maincpu")
|
||||||
, m_lcdc(*this, "lcdc")
|
, m_lcdc(*this, "lcdc")
|
||||||
, m_led_touch(*this, "led_touch")
|
, m_led_touch(*this, "led_touch")
|
||||||
|
, m_led_power(*this, "led_power")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,6 +92,7 @@ private:
|
|||||||
required_device<hd44780_device> m_lcdc;
|
required_device<hd44780_device> m_lcdc;
|
||||||
|
|
||||||
output_finder<> m_led_touch;
|
output_finder<> m_led_touch;
|
||||||
|
output_finder<> m_led_power;
|
||||||
|
|
||||||
ioport_value m_switch;
|
ioport_value m_switch;
|
||||||
};
|
};
|
||||||
@ -97,7 +100,6 @@ private:
|
|||||||
|
|
||||||
INPUT_CHANGED_MEMBER(ctk551_state::switch_w)
|
INPUT_CHANGED_MEMBER(ctk551_state::switch_w)
|
||||||
{
|
{
|
||||||
logerror("switch_w: %x\n", param);
|
|
||||||
if (!oldval && newval)
|
if (!oldval && newval)
|
||||||
{
|
{
|
||||||
if (m_switch == 0x1 && param != m_switch)
|
if (m_switch == 0x1 && param != m_switch)
|
||||||
@ -112,8 +114,11 @@ INPUT_CHANGED_MEMBER(ctk551_state::switch_w)
|
|||||||
WRITE_LINE_MEMBER(ctk551_state::apo_w)
|
WRITE_LINE_MEMBER(ctk551_state::apo_w)
|
||||||
{
|
{
|
||||||
logerror("apo_w: %x\n", state);
|
logerror("apo_w: %x\n", state);
|
||||||
/* TODO: when 1, this should turn off the LCD, speakers, etc.
|
/* auto power off - disable the LCD
|
||||||
the CPU will go to sleep until the power switch triggers a NMI */
|
the CPU will go to sleep until the power switch triggers a NMI */
|
||||||
|
if (state)
|
||||||
|
m_lcdc->reset();
|
||||||
|
m_led_power = !state;
|
||||||
}
|
}
|
||||||
|
|
||||||
HD44780_PIXEL_UPDATE(ctk551_state::lcd_update)
|
HD44780_PIXEL_UPDATE(ctk551_state::lcd_update)
|
||||||
@ -140,6 +145,7 @@ void ctk551_state::ctk551_io_map(address_map &map)
|
|||||||
void ctk551_state::driver_start()
|
void ctk551_state::driver_start()
|
||||||
{
|
{
|
||||||
m_led_touch.resolve();
|
m_led_touch.resolve();
|
||||||
|
m_led_power.resolve();
|
||||||
|
|
||||||
m_switch = 0x2;
|
m_switch = 0x2;
|
||||||
|
|
||||||
@ -149,8 +155,11 @@ void ctk551_state::driver_start()
|
|||||||
void ctk551_state::ctk551(machine_config &config)
|
void ctk551_state::ctk551(machine_config &config)
|
||||||
{
|
{
|
||||||
// CPU
|
// CPU
|
||||||
GT913(config, m_maincpu, 30'000'000);
|
// 30MHz oscillator, divided down internally (otherwise the test mode's OK/NG sounds play at double speed)
|
||||||
|
GT913(config, m_maincpu, 30'000'000 / 2);
|
||||||
m_maincpu->set_addrmap(AS_IO, &ctk551_state::ctk551_io_map);
|
m_maincpu->set_addrmap(AS_IO, &ctk551_state::ctk551_io_map);
|
||||||
|
m_maincpu->subdevice<gt913_sound_device>("gt_sound")->add_route(0, "lspeaker", 1.0);
|
||||||
|
m_maincpu->subdevice<gt913_sound_device>("gt_sound")->add_route(1, "rspeaker", 1.0);
|
||||||
|
|
||||||
// MIDI
|
// MIDI
|
||||||
auto &mdin(MIDI_PORT(config, "mdin"));
|
auto &mdin(MIDI_PORT(config, "mdin"));
|
||||||
@ -177,6 +186,9 @@ void ctk551_state::ctk551(machine_config &config)
|
|||||||
screen.set_palette("palette");
|
screen.set_palette("palette");
|
||||||
|
|
||||||
PALETTE(config, "palette", FUNC(ctk551_state::palette_init), 2);
|
PALETTE(config, "palette", FUNC(ctk551_state::palette_init), 2);
|
||||||
|
|
||||||
|
SPEAKER(config, "lspeaker").front_left();
|
||||||
|
SPEAKER(config, "rspeaker").front_right();
|
||||||
}
|
}
|
||||||
|
|
||||||
INPUT_PORTS_START(ctk551)
|
INPUT_PORTS_START(ctk551)
|
||||||
@ -356,4 +368,4 @@ ROM_END
|
|||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
|
// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
|
||||||
SYST( 1999, ctk551, 0, 0, ctk551, ctk551, ctk551_state, empty_init, "Casio", "CTK-551", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
|
SYST( 1999, ctk551, 0, 0, ctk551, ctk551, ctk551_state, empty_init, "Casio", "CTK-551", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
|
||||||
|
Loading…
Reference in New Issue
Block a user