casio/ct8000.cpp: Emulated Casiotone 8000 keyboard and related systems. (#13237)

* sound/flt_biquad.cpp: Added Sallen-Key high-pass filters.
* sound/bbd.cpp: Added MN3207P variant.
* sound/upd931.cpp: Emulated µPD931 synthesis chip.

New working systems
--------------------
Casio Casiotone 8000 [=CO=Windler, Devin Acker]
Casio Casiotone FK-1 [BCM, Devin Acker]

New systems marked not working
------------------------
Casio Casiotone MB-1 [=CO=Windler, Devin Acker]
This commit is contained in:
Devin Acker 2025-01-25 12:47:44 -05:00 committed by GitHub
parent 7cc6ac973b
commit efee42e5e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 2978 additions and 103 deletions

View File

@ -1503,6 +1503,18 @@ if (SOUNDS["LC82310"]~=null) then
}
end
---------------------------------------------------
-- NEC uPD931
--@src/devices/sound/upd931.h,SOUNDS["UPD931"] = true
---------------------------------------------------
if (SOUNDS["UPD931"]~=null) then
files {
MAME_DIR .. "src/devices/sound/upd931.cpp",
MAME_DIR .. "src/devices/sound/upd931.h",
}
end
---------------------------------------------------
-- NEC uPD933
--@src/devices/sound/upd933.h,SOUNDS["UPD933"] = true

View File

@ -52,7 +52,8 @@ void bbd_device_base<Entries, Outputs>::device_start()
template<int Entries, int Outputs>
void bbd_device_base<Entries, Outputs>::device_clock_changed()
{
m_stream->set_sample_rate(sample_rate());
if (m_cv_handler.isnull())
m_stream->set_sample_rate(sample_rate());
}
@ -163,3 +164,16 @@ mn3204p_device::mn3204p_device(const machine_config &mconfig, const char *tag, d
bbd_device_base(mconfig, tag, owner, clock, MN3204P)
{
}
//**************************************************************************
// MN3207P
//**************************************************************************
// device type definition
DEFINE_DEVICE_TYPE(MN3207, mn3207_device, "mn3207", "MN3207 BBD")
mn3207_device::mn3207_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
bbd_device_base(mconfig, tag, owner, clock, MN3207)
{
}

View File

@ -90,4 +90,15 @@ public:
DECLARE_DEVICE_TYPE(MN3204P, mn3204p_device)
// ======================> mn3207_device
class mn3207_device : public bbd_device_base<1024, 2>
{
public:
mn3207_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
};
DECLARE_DEVICE_TYPE(MN3207, mn3207_device)
#endif // MAME_SOUND_BBD_H

View File

@ -7,18 +7,15 @@
It has a number of constructor-helpers for automatically generating
a biquad filter equivalent to the filter response of a few standard
analog second order filter topographies.
analog first and second order filter topographies.
This biquad filter implementation is based on one written by Frank
Palazzolo, K. Wilkins, Couriersud, and Derrick Renaud, with some changes:
* It uses the Q factor directly in the filter definitions, rather than the damping factor (1/2Q)
* It implements every common type of digital biquad filter which I could find documentation for.
* The filter is Direct-form II instead of Direct-form I, which results in shorter compiled code.
* (There are advantages to Direct-form I if the code used fixed-point math, but it does not.)
* Optional direct control of the 5 normalized biquad parameters for a custom/raw parameter filter.
Possibly useful features which aren't implemented because nothing uses them yet:
* More Sallen-Key filter variations (band-pass, high-pass)
*/
#include "emu.h"
#include "flt_biquad.h"
@ -149,60 +146,106 @@ void filter_biquad_device::modify_raw(double a1, double a2, double b0, double b1
// Sallen-Key filters
// (sometimes referred to as KRC or VCVS filter structures)
/*
* The calculation of the cutoff parameter for the Sallen-Key low-pass and
* high-pass is identical, the only differences being the biquad filter
* type used, and the calculation of the q factor.
*/
filter_biquad_device::biquad_params filter_biquad_device::opamp_sk_lphp_calc(biquad_type type, double r1, double r2, double r3, double r4, double c1, double c2)
{
filter_biquad_device::biquad_params r;
if ((r1 == 0) || (r2 == 0) || (r3 == 0) || (r4 == 0) || (c1 == 0) || (c2 == 0))
{
fatalerror("filter_biquad_device::opamp_sk_lphp_calc() - no parameters can be 0; parameters were: r1: %f, r2: %f, r3: %f, r4: %f, c1: %f, c2: %f", r1, r2, r3, r4, c1, c2); /* Filter can not be setup. Undefined results. */
}
r.type = type;
r.gain = 1.0 + (r4 / r3); // == (r3 + r4) / r3
r.fc = 1.0 / (2 * M_PI * sqrt(r1 * r2 * c1 * c2));
if (type == biquad_type::LOWPASS)
r.q = sqrt(r1 * r2 * c1 * c2) / ((r1 * c2) + (r2 * c2) + ((r1 * c1) * (1.0 - r.gain)));
else if (type == biquad_type::HIGHPASS)
r.q = sqrt(r1 * r2 * c1 * c2) / ((r1 * c2) + (r1 * c1) + ((r2 * c2) * (1.0 - r.gain)));
else
r.q = M_SQRT2/2.0; // we shouldn't get here but fail gracefully if we do.
LOGMASKED(LOG_SETUP,"filter_biquad_device::opamp_sk_lphp_calc(%f, %f, %f, %f, %f, %f) yields: fc = %f, Q = %f, gain = %f\n", r1, r2, r3, r4, c1*1000000, c2*1000000, r.fc, r.q, r.gain);
return r;
}
/* Setup a biquad filter structure based on a single op-amp Sallen-Key low-pass filter circuit.
* This is sometimes, incorrectly, called a "Butterworth" filter structure.
*
* .----------------------------.
* | |
* --- c1 |
* --- |
* | |
* r1 | r2 |\ |
* In >----ZZZZ----+--ZZZZ---+--------+ | \ |
* | '--|+ \ |
* --- c2 | >--+------> out
* --- .--|- / |
* | | | / |
* gnd | |/ |
* | |
* | r4 |
* +--ZZZZ---'
* |
* Z
* Z r3
* Z
* |
* gnd
* ,--------------------------.
* | |
* --- c1 |
* --- |
* | |\ |
* r1 | r2 | \ |
* In >---ZZZZ--+--ZZZZ--+----------|+ \ |
* | | >--+---> out
* --- c2 ,--|- / |
* --- | | / |
* | | |/ |
* | | |
* gnd | r4 |
* +--ZZZZ---'
* |
* Z r3
* Z
* Z
* |
* gnd
*/
filter_biquad_device& filter_biquad_device::opamp_sk_lowpass_setup(double r1, double r2, double r3, double r4, double c1, double c2)
{
filter_biquad_device::biquad_params p = opamp_sk_lowpass_calc(r1, r2, r3, r4, c1, c2);
filter_biquad_device::biquad_params p = opamp_sk_lphp_calc(biquad_type::LOWPASS, r1, r2, r3, r4, c1, c2);
return setup(p);
}
void filter_biquad_device::opamp_sk_lowpass_modify(double r1, double r2, double r3, double r4, double c1, double c2)
{
filter_biquad_device::biquad_params p = opamp_sk_lowpass_calc(r1, r2, r3, r4, c1, c2);
filter_biquad_device::biquad_params p = opamp_sk_lphp_calc(biquad_type::LOWPASS, r1, r2, r3, r4, c1, c2);
modify(p);
}
filter_biquad_device::biquad_params filter_biquad_device::opamp_sk_lowpass_calc(double r1, double r2, double r3, double r4, double c1, double c2)
/* Setup a biquad filter structure based on a single op-amp Sallen-Key high-pass filter circuit.
*
* ,--------------------------.
* | |
* Z r1 |
* Z |
* Z |\ |
* c1 | c2 | \ |
* In >----||---+---||---+----------|+ \ |
* | | >--+---> out
* Z r2 ,--|- / |
* Z | | / |
* Z | |/ |
* | | |
* gnd | r4 |
* +--ZZZZ---'
* |
* Z r3
* Z
* Z
* |
* gnd
*/
filter_biquad_device& filter_biquad_device::opamp_sk_highpass_setup(double r1, double r2, double r3, double r4, double c1, double c2)
{
filter_biquad_device::biquad_params r;
if ((r1 == 0) || (r2 == 0) || (r3 == 0) || (r4 == 0) || (c1 == 0) || (c2 == 0))
{
fatalerror("filter_biquad_device::opamp_sk_lowpass_calc() - no parameters can be 0; parameters were: r1: %f, r2: %f, r3: %f, r4: %f, c1: %f, c2: %f", r1, r2, r3, r4, c1, c2); /* Filter can not be setup. Undefined results. */
}
r.type = biquad_type::LOWPASS;
r.gain = 1.0 + (r4 / r3); // == (r3 + r4) / r3
r.fc = 1.0 / (2 * M_PI * sqrt(r1 * r2 * c1 * c2));
r.q = sqrt(r1 * r2 * c1 * c2) / ((r1 * c2) + (r2 * c2) + ((r2 * c1) * (1.0 - r.gain)));
LOGMASKED(LOG_SETUP,"filter_biquad_device::opamp_sk_lowpass_calc(%f, %f, %f, %f, %f, %f) yields: fc = %f, Q = %f, gain = %f\n", r1, r2, r3, r4, c1*1000000, c2*1000000, r.fc, r.q, r.gain);
return r;
filter_biquad_device::biquad_params p = opamp_sk_lphp_calc(biquad_type::HIGHPASS, r1, r2, r3, r4, c1, c2);
return setup(p);
}
// TODO when needed: Sallen-Key high-pass filter
void filter_biquad_device::opamp_sk_highpass_modify(double r1, double r2, double r3, double r4, double c1, double c2)
{
filter_biquad_device::biquad_params p = opamp_sk_lphp_calc(biquad_type::HIGHPASS, r1, r2, r3, r4, c1, c2);
modify(p);
}
// TODO when needed: Sallen-Key band-pass (there are several versions of this in the 1955 Sallen-Key paper)
// Multiple-Feedback filters
@ -210,24 +253,39 @@ filter_biquad_device::biquad_params filter_biquad_device::opamp_sk_lowpass_calc(
/* Setup a biquad filter structure based on a single op-amp Multiple-Feedback low-pass filter circuit.
* This is sometimes called a "Rauch" filter circuit.
* NOTE: There is a well known 'proper' 1st order version of this circuit where
* r2 is a dead short, and c1 omitted. As an exception to the usual rule about
* missing components, set both c1 and r2 to 0 in this case.
* NOTE: There is a variant of this filter with the c1 capacitor left off, and
* r2 present. if so, set c1 to 0 and r2 to its expected value.
* TODO: make this compatible with the RES_M(999.99) and RES_R(0.001) rules!
* r2 is a dead short, and c1 omitted: set r2 to 0 or RES_R(0.001) and c1 to 0
* in this case.
* NOTE: There is a variant of this filter where r2 is present but c1 is
* omitted: set r2 to its expected value, and c1 to 0.
*
* .--------+---------.
* | | |
* Z --- c2 |
* Z r3 --- |
* Z | |
* r1 | r2 | |\ |
* In >----ZZZZ----+---------+--ZZZZ--+ | \ |
* | '--|- \ |
* --- c1 | >--+------> out
* --- .--|+ /
* | | | /
* gnd vRef >---' |/
* Typical variant: (set c1 to 0 if missing)
* ,--------+---------.
* | | |
* Z r3 --- c2 |
* Z --- |
* Z | |
* r1 | r2 | |\ |
* In >---ZZZZ--+-------+--ZZZZ--+ | \ |
* | `--|- \ |
* --- c1 | >--+---> out
* --- ,--|+ /
* | | | /
* | vRef >---' |/
* gnd
*
* First order variant: (set c1 and r2 to 0)
* ,-------+---------.
* | | |
* Z r3 --- c2 |
* Z --- |
* Z | |
* r1 | | |\ |
* In >---ZZZZ--+-------+ | \ |
* `--|- \ |
* | >--+---> out
* ,--|+ /
* | | /
* vRef >--' |/
*
*/
filter_biquad_device& filter_biquad_device::opamp_mfb_lowpass_setup(double r1, double r2, double r3, double c1, double c2)
@ -269,23 +327,21 @@ filter_biquad_device::biquad_params filter_biquad_device::opamp_mfb_lowpass_calc
/* Setup a biquad filter structure based on a single op-amp Multiple-Feedback band-pass filter circuit.
* This is sometimes called a "modified Deliyannis" or "Deliyannis-friend" filter circuit,
* or an "Infinite Gain Multiple-Feedback [band-pass] Filter" aka "IGMF".
* TODO: There is a documented modification to this filter which adds a resistor ladder between
* ground and the op-amp output, with the 'rung' of the ladder connecting to the + input of
* the op-amp, and this allows more control of the filter.
* NOTE2: If r2 is not present, then set it to RES_M(999.99), the code will effectively be an Infinite Gain MFB Bandpass.
*
* .--------+---------.
* | | |
* --- c1 Z |
* --- Z r3 |
* | Z |
* r1 | c2 | |\ |
* In >----ZZZZ----+---------+--||----+ | \ |
* Z '--|- \ |
* Z r2 | >--+------> out
* Z .--|+ /
* | | | /
* gnd vRef >---' |/
* ,--------+---------.
* | | |
* --- c1 Z r3 |
* --- Z |
* | Z |
* r1 | c2 | |\ |
* In >---ZZZZ--+-------+---||---+ | \ |
* | `--|- \ |
* Z r2 | >--+---> out
* Z ,--|+ /
* Z | | /
* | vRef >---' |/
* gnd
*
*/
filter_biquad_device& filter_biquad_device::opamp_mfb_bandpass_setup(double r1, double r2, double r3, double c1, double c2)
@ -305,18 +361,18 @@ filter_biquad_device& filter_biquad_device::opamp_mfb_bandpass_setup(double r1,
/* Setup a biquad filter structure based on a single op-amp Multiple-Feedback high-pass filter circuit.
*
* .--------+---------.
* | | |
* --- c3 Z |
* --- Z r2 |
* | Z |
* c1 | c2 | |\ |
* In >-----||-----+---------+---||---+ | \ |
* Z '--|- \ |
* Z r1 | >--+------> out
* Z .--|+ /
* | | | /
* gnd vRef >---' |/
* ,--------+---------.
* | | |
* --- c3 Z |
* --- Z r2 |
* | Z |
* c1 | c2 | |\ |
* In >---||----+-------+---||---+ | \ |
* Z `--|- \ |
* Z r1 | >--+---> out
* Z ,--|+ /
* | | | /
* gnd vRef >---' |/
*
*/
filter_biquad_device& filter_biquad_device::opamp_mfb_highpass_setup(double r1, double r2, double c1, double c2, double c3)
@ -333,23 +389,26 @@ filter_biquad_device& filter_biquad_device::opamp_mfb_highpass_setup(double r1,
return setup(biquad_type::HIGHPASS, fc, q, gain);
}
// Other filters:
// Differentiator Filter
/* Setup a biquad filter structure based on a single op-amp Differentiator band-pass filter circuit.
* This circuit is sometimes called an "Inverting Band Pass Filter Circuit"
*
* .--------+---------.
* | | |
* --- c2 Z |
* --- Z r2 |
* | Z |
* r1 c1 | | |\ |
* In >----ZZZZ-----||-----+--------+ | \ |
* '--|- \ |
* | >--+------> out
* .--|+ /
* | | /
* vRef >---' |/
* ,--------+---------.
* | | |
* --- c2 Z r2 |
* --- Z |
* | Z |
* r1 c1 | | |\ |
* In >---ZZZZ-----||---+--------+ | \ |
* `--|- \ |
* | >--+---> out
* ,--|+ /
* | | /
* vRef >---' |/
*
*/
filter_biquad_device& filter_biquad_device::opamp_diff_bandpass_setup(double r1, double r2, double c1, double c2)

View File

@ -52,16 +52,18 @@ public:
// Helper setup functions to create common filters representable by biquad filters:
// (and, as needed, modify/update/recalc helpers)
// universal calculator for both Sallen-Key low-pass and high-pass
biquad_params opamp_sk_lphp_calc(biquad_type type, double r1, double r2, double r3, double r4, double c1, double c2);
// Sallen-Key low-pass
filter_biquad_device& opamp_sk_lowpass_setup(double r1, double r2, double r3, double r4, double c1, double c2);
void opamp_sk_lowpass_modify(double r1, double r2, double r3, double r4, double c1, double c2);
biquad_params opamp_sk_lowpass_calc(double r1, double r2, double r3, double r4, double c1, double c2);
// TODO when needed: Sallen-Key band-pass
// Sallen-Key high-pass
filter_biquad_device& opamp_sk_highpass_setup(double r1, double r2, double r3, double r4, double c1, double c2);
void opamp_sk_highpass_modify(double r1, double r2, double r3, double r4, double c1, double c2);
// TODO when needed: Sallen-Key band-reject
// TODO when needed: Sallen-Key high-pass
// TODO when needed: Sallen-Key band-pass (there are several versions of this in the 1955 Sallen-Key paper)
// Multiple-Feedback low-pass
filter_biquad_device& opamp_mfb_lowpass_setup(double r1, double r2, double r3, double c1, double c2);

View File

@ -0,0 +1,599 @@
// license:BSD-3-Clause
// copyright-holders:Devin Acker
/***************************************************************************
NEC/Casio uPD931 synthesis chip
Many details of this implementation are based on research and notes by Robin Whittle:
https://www.firstpr.com.au/rwi/casio/Casio-931-2006-06-17.txt
Any references to MT-65 behavior are based on this document.
TODO:
- implement vibrato register (CT-8000 doesn't use it)
- a few other unknown/unclear bits in the flags shift register
***************************************************************************/
#include "emu.h"
#include "upd931.h"
#include <algorithm>
#include <cmath>
DEFINE_DEVICE_TYPE(UPD931, upd931_device, "upd931", "NEC uPD931")
upd931_device::upd931_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: device_t(mconfig, UPD931, tag, owner, clock)
, device_sound_interface(mconfig, *this)
, device_memory_interface(mconfig, *this)
, m_io_config("io", ENDIANNESS_LITTLE, 8, 8, 0, address_map_constructor(FUNC(upd931_device::io_map), this))
, m_retrig_timer(nullptr)
, m_filter_cb(*this)
, m_sync_cb(*this)
, m_master(true)
{
}
/**************************************************************************/
device_memory_interface::space_config_vector upd931_device::memory_space_config() const
{
return space_config_vector{std::make_pair(AS_IO, &m_io_config)};
}
/**************************************************************************/
void upd931_device::io_map(address_map &map)
{
map(0x20, 0x27).w(FUNC(upd931_device::note_w));
map(0x30, 0x37).w(FUNC(upd931_device::octave_w));
// waveform write position: ct8000 uses 40, mt65 uses 60
map(0x40, 0x40).mirror(0x20).w(FUNC(upd931_device::wave_pos_w));
map(0xa7, 0xa7).w(FUNC(upd931_device::wave_data_w));
map(0xb0, 0xb7).w(FUNC(upd931_device::flags_w)); // lower address bits are ignored
map(0xb8, 0xbf).w(FUNC(upd931_device::status_latch_w));
// vibrato/sustain: ct8000 uses c0-c7, mt65 uses d0-d7 (lower two bits are also ignored)
map(0xc0, 0xc3).mirror(0x10).w(FUNC(upd931_device::vibrato_w));
map(0xc4, 0xc7).mirror(0x10).w(FUNC(upd931_device::sustain_w));
map(0xe0, 0xe7).w(FUNC(upd931_device::note_on_w));
map(0xf4, 0xf4).lw8(NAME([this] (offs_t offset, u8 data) { m_filter_cb(data); }));
}
/**************************************************************************/
void upd931_device::device_start()
{
space(AS_IO).specific(m_io);
m_stream = stream_alloc(0, 1, clock() / CLOCKS_PER_SAMPLE);
if (m_master)
{
m_retrig_timer = timer_alloc(FUNC(upd931_device::timer_tick), this);
reset_timer();
}
m_last_clock = clock();
m_db = 0xf;
m_i1 = m_i2 = m_i3 = 1;
m_status = 0xf;
// generate note frequencies based on CT-8000 crystal frequency (probably not entirely accurate)
const unsigned sample_rate = 4'946'864 / CLOCKS_PER_SAMPLE;
for (int i = 0; i < 73; i++)
{
// A4 is note 33, 442 Hz
const double freq = 442.0 * pow(2, (i - 33) / 12.0);
m_pitch[i] = (1 << PITCH_SHIFT) * (freq * 16 / sample_rate);
}
save_item(NAME(m_db));
save_item(NAME(m_i1));
save_item(NAME(m_i2));
save_item(NAME(m_i3));
save_item(NAME(m_addr));
save_item(NAME(m_data));
save_item(NAME(m_status));
save_item(NAME(m_wave));
save_item(NAME(m_wave_pos));
save_item(NAME(m_vibrato));
save_item(NAME(m_sustain));
save_item(NAME(m_reverb));
save_item(NAME(m_flags));
save_item(NAME(m_last_clock));
save_item(STRUCT_MEMBER(m_voice, m_note));
save_item(STRUCT_MEMBER(m_voice, m_octave));
save_item(STRUCT_MEMBER(m_voice, m_pitch));
save_item(STRUCT_MEMBER(m_voice, m_pitch_counter));
save_item(STRUCT_MEMBER(m_voice, m_timbre_shift));
save_item(STRUCT_MEMBER(m_voice, m_wave_pos));
save_item(STRUCT_MEMBER(m_voice, m_wave_out));
save_item(STRUCT_MEMBER(m_voice, m_env_state));
save_item(STRUCT_MEMBER(m_voice, m_env_counter));
save_item(STRUCT_MEMBER(m_voice, m_env_level));
save_item(STRUCT_MEMBER(m_voice, m_force_release));
}
/**************************************************************************/
TIMER_CALLBACK_MEMBER(upd931_device::timer_tick)
{
m_sync_cb(1);
sync_w(1);
}
/**************************************************************************/
void upd931_device::device_reset()
{
std::fill(std::begin(m_wave[0]), std::end(m_wave[0]), 0);
std::fill(std::begin(m_wave[1]), std::end(m_wave[1]), 0);
m_wave_pos = 0;
m_vibrato = 0;
m_sustain = 0;
m_reverb = 0;
m_flags = 0;
std::fill(m_voice.begin(), m_voice.end(), voice_t());
m_filter_cb(0);
m_sync_cb(0);
}
/**************************************************************************/
void upd931_device::device_clock_changed()
{
m_stream->set_sample_rate(clock() / CLOCKS_PER_SAMPLE);
if (m_retrig_timer)
{
const u64 ticks_left = m_retrig_timer->remaining().as_ticks(m_last_clock);
const attotime remaining = attotime::from_ticks(ticks_left, clock());
const attotime period = attotime::from_ticks(RETRIG_RATE, clock());
m_retrig_timer->adjust(remaining, 0, period);
}
m_last_clock = clock();
}
/**************************************************************************/
void upd931_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++)
{
s32 sample = 0;
for (voice_t &voice : m_voice)
{
update_env(voice);
update_wave(voice);
sample += voice.m_wave_out[0] * (voice.m_env_level[0] >> VOLUME_SHIFT);
sample += voice.m_wave_out[1] * (voice.m_env_level[1] >> VOLUME_SHIFT);
}
outputs[0].put_int_clamp(i, sample, 1 << 16);
}
}
/**************************************************************************/
void upd931_device::i1_w(int state)
{
if (!m_i3 && m_i1 && !state)
{
// address high nibble on I1 falling edge
m_addr &= 0x0f;
m_addr |= (m_db << 4);
}
m_i1 = state;
}
/**************************************************************************/
void upd931_device::i2_w(int state)
{
if (!m_i3)
{
if (!m_i2 && state)
{
// address low nibble on I2 rising edge
m_addr &= 0xf0;
m_addr |= (m_db & 0xf);
// apply register write
m_stream->update();
m_io.write_byte(m_addr, m_data & 0xf);
}
else if (m_i2 && !state)
{
// data on I2 falling edge
m_data = m_db;
}
}
m_i2 = state;
}
/**************************************************************************/
void upd931_device::i3_w(int state)
{
m_i3 = state;
}
/**************************************************************************/
void upd931_device::db_w(u8 data)
{
m_db = data;
}
/**************************************************************************/
u8 upd931_device::db_r()
{
if (m_i1 && m_i2 && !m_i3)
return m_status;
return 0xf;
}
/**************************************************************************/
void upd931_device::sync_w(int state)
{
if (BIT(m_flags, FLAG_RETRIGGER))
{
m_stream->update();
for (voice_t &voice : m_voice)
{
if (voice.m_env_state == ENV_DECAY1 || voice.m_env_state == ENV_DECAY2)
{
voice.m_env_state = ENV_ATTACK1;
voice.m_env_counter = 0;
}
}
}
}
/**************************************************************************/
void upd931_device::note_w(offs_t offset, u8 data)
{
m_voice[offset].m_note = data;
}
/**************************************************************************/
void upd931_device::octave_w(offs_t offset, u8 data)
{
m_voice[offset].m_octave = m_data;
}
/**************************************************************************/
void upd931_device::wave_pos_w(u8 data)
{
m_wave_pos = data;
}
/**************************************************************************/
void upd931_device::wave_data_w(u8 data)
{
const u8 sel = BIT(m_flags, FLAG_WAVE_SEL);
m_wave[sel][m_wave_pos & 0xf] = data;
}
/**************************************************************************/
void upd931_device::flags_w(u8 data)
{
m_flags >>= 1;
m_flags |= ((data & 1) << FLAG_WAVE_SEL);
}
/**************************************************************************/
void upd931_device::status_latch_w(offs_t offset, u8 data)
{
// TODO: more details. ct8000 only checks if bits 1-3 are all zero or not
if (m_voice[offset].m_env_state == ENV_IDLE)
m_status = 0xf;
else
m_status = 0;
}
/**************************************************************************/
void upd931_device::vibrato_w(u8 data)
{
// TODO: implement this. ct8000 always writes 0 since it uses the external VCO vibrato instead
m_vibrato = data & 3;
}
/**************************************************************************/
void upd931_device::sustain_w(u8 data)
{
m_sustain = data & 3;
m_reverb = BIT(data, 1, 2) ? 1 : 0;
}
/**************************************************************************/
void upd931_device::note_on_w(offs_t offset, u8 data)
{
voice_t &voice = m_voice[offset];
if (BIT(data, 0))
note_on(voice);
else
voice.m_env_state = ENV_RELEASE;
// mt65 turns off sustain and reverb when changing tones, ct8000 does this instead
// (and also when playing a new note that is already playing)
voice.m_force_release = BIT(data, 3);
}
/**************************************************************************/
void upd931_device::note_on(voice_t &voice)
{
if (voice.m_note >= 0x2 && voice.m_note <= 0xe)
{
const u8 note = voice.m_note - 2;
u8 octave = voice.m_octave & 7;
if (octave >= 2)
octave -= 2; // octave values 0-1 are the same as 2-3
/*
setting bit 3 of the octave reduces the duty cycle of individual notes, which is
implemented here by changing which part of the phase counter to use as the sample address.
ct8000 uses this for a few of its presets to produce a simple key-scaling effect.
*/
if (BIT(voice.m_octave, 3))
voice.m_timbre_shift = 3 - octave;
else
voice.m_timbre_shift = 0;
voice.m_pitch = m_pitch[octave * 12 + note];
}
else
{
voice.m_pitch = 0;
}
voice.m_pitch_counter = 0;
voice.m_wave_pos = 0xff;
voice.m_wave_out[0] = voice.m_wave_out[1] = 0;
voice.m_env_state = ENV_ATTACK1;
voice.m_env_counter = 0;
if (m_master)
reset_timer();
}
/**************************************************************************/
void upd931_device::reset_timer()
{
const attotime period = attotime::from_ticks(RETRIG_RATE, clock());
m_retrig_timer->adjust(period, 0, period);
}
/**************************************************************************/
void upd931_device::update_env(voice_t &voice)
{
const unsigned shift = BIT(m_flags, FLAG_ENV_SHIFT, 2);
switch (voice.m_env_state)
{
case ENV_IDLE:
return;
case ENV_ATTACK1:
{
static const u16 rates[] =
{
0, 2048, 512, 256, 160, 80, 32, 8
};
const u8 val = BIT(m_flags, FLAG_ATTACK1, 3);
u32 rate;
if (val == 0)
rate = VOLUME_MAX; // value 0 = instant
else if (val < 4 && voice.m_env_counter >= (0xe0 << VOLUME_SHIFT))
rate = 160 << shift; // values 1-3 slow down at 7/8 of max volume
else
rate = rates[val] << shift;
voice.m_env_counter = std::min(voice.m_env_counter + rate, VOLUME_MAX);
// only increase wave A level if it isn't set to rise during attack2 instead
if (BIT(m_flags, FLAG_ATTACK2_A))
voice.m_env_level[0] = 0;
else
voice.m_env_level[0] = voice.m_env_counter;
voice.m_env_level[1] = voice.m_env_counter;
if (voice.m_env_counter >= VOLUME_MAX)
{
voice.m_env_counter = 0;
voice.m_env_state = ENV_ATTACK2;
}
break;
}
case ENV_ATTACK2:
{
static const u32 rates[] =
{
0, 2048, 256, 128, 64, 32, 16, 8
};
const u8 val = BIT(m_flags, FLAG_ATTACK2, 3);
u32 rate;
if (val == 0)
rate = VOLUME_MAX;
else
rate = rates[val] << shift;
voice.m_env_counter = std::min(voice.m_env_counter + rate, VOLUME_MAX);
// fade wave A in, if specified
if (BIT(m_flags, FLAG_ATTACK2_A))
voice.m_env_level[0] = voice.m_env_counter;
// fade wave B out, if specified
if (BIT(m_flags, FLAG_ATTACK2_B))
voice.m_env_level[1] = VOLUME_MAX - voice.m_env_counter;
if (voice.m_env_counter >= VOLUME_MAX)
voice.m_env_state = ENV_DECAY1;
break;
}
case ENV_DECAY1:
{
static const u32 rates[] =
{
2048, 640, 160, 32, 16, 8, 2, 0
};
const u8 val = BIT(m_flags, FLAG_DECAY1, 3);
const u32 rate = rates[val] << shift;
if (voice.m_env_counter < rate)
{
voice.m_env_counter = 0;
voice.m_env_state = ENV_IDLE;
}
else
{
voice.m_env_counter -= rate;
}
voice.m_env_level[0] = voice.m_env_counter;
// only fade wave B if it didn't already fade out during attack2
if (voice.m_env_level[1])
voice.m_env_level[1] = voice.m_env_counter;
if (!BIT(m_flags, FLAG_DECAY2_DISABLE))
{
// transition to decay2 at 1/2 or 1/4 of max volume, if enabled
const u8 decay2_level = BIT(m_flags, FLAG_DECAY2_LEVEL) ? 0x40 : 0x80;
if (voice.m_env_counter < (decay2_level << VOLUME_SHIFT))
voice.m_env_state = ENV_DECAY2;
}
break;
}
case ENV_DECAY2:
{
// apply reverb rate (below 1/8 max volume) or decay2 rate
u16 rate;
if (m_reverb && voice.m_env_counter < (0x20 << VOLUME_SHIFT))
rate = 1 << shift;
else if (BIT(m_flags, FLAG_DECAY2))
rate = 3 << shift;
else
rate = 6 << shift;
if (voice.m_env_counter < rate)
{
voice.m_env_counter = 0;
voice.m_env_state = ENV_IDLE;
}
else
{
voice.m_env_counter -= rate;
}
voice.m_env_level[0] = voice.m_env_counter;
if (voice.m_env_level[1])
voice.m_env_level[1] = voice.m_env_counter;
break;
}
case ENV_RELEASE:
{
// apply reverb rate (below 1/8 max volume), sustain rate (if enabled) or default release rate
u16 rate = 512 << shift;
if (!voice.m_force_release)
{
if (m_reverb && voice.m_env_counter < (0x20 << VOLUME_SHIFT))
rate = 1 << shift;
else if (m_sustain == 1)
rate = 16 << shift;
else if (m_sustain == 2)
rate = 12 << shift;
}
if (voice.m_env_counter < rate)
{
voice.m_env_counter = 0;
voice.m_env_state = ENV_IDLE;
}
else
{
voice.m_env_counter -= rate;
}
// fade each wave individually because if keyed off during attack, they may be different from each other
voice.m_env_level[0] = std::min(voice.m_env_level[0], voice.m_env_counter);
voice.m_env_level[1] = std::min(voice.m_env_level[1], voice.m_env_counter);
break;
}
}
}
/**************************************************************************/
void upd931_device::update_wave(voice_t &voice)
{
voice.m_pitch_counter += voice.m_pitch;
const u8 cycle = BIT(voice.m_pitch_counter, PITCH_SHIFT + 4, 2);
/*
the part of the counter which is treated as the sample address depends on if key scaling is active.
if it is active, then the voice alternates between a narrowed waveform and silence, which alters
the timbre without affecting pitch
*/
u8 pos = BIT(voice.m_pitch_counter, PITCH_SHIFT - voice.m_timbre_shift, 4 + voice.m_timbre_shift);
if (pos == voice.m_wave_pos || pos >= 0x10)
return;
voice.m_wave_pos = pos;
// play every other cycle backwards, if enabled
if (BIT(m_flags, FLAG_MIRROR) && BIT(cycle, 0))
pos ^= 0xf;
// flag bits determine which of 4 consecutive cycles to apply waveforms A and B
const unsigned cycle_mode[] =
{
BIT(m_flags, FLAG_MODE_A, 2),
BIT(m_flags, FLAG_MODE_B, 2)
};
static const u8 cycle_mask[] =
{
0xf, // always on
0x5, // on, off, on, off
0x1, // on 1x, off 3x
0x3 // on 2x, off 2x
};
static const s8 steps[] =
{
0, 1, 2, 2, 4, 4, 8, 8,
0, -1, -2, -2, -4, -4, -8, -8
};
for (int i = 0; i < 2; i++)
{
// check if this waveform is enabled for this cycle
if (!BIT(cycle_mask[cycle_mode[i]], cycle))
continue;
s8 step = steps[m_wave[i][pos] & 0xf];
// invert waveform on every other cycle, if enabled
if (BIT(m_flags, FLAG_INVERT) && BIT(cycle, 0))
step = -step;
voice.m_wave_out[i] += step;
voice.m_wave_out[i] = util::sext(voice.m_wave_out[i] & 0x3f, 6);
}
}

153
src/devices/sound/upd931.h Normal file
View File

@ -0,0 +1,153 @@
// license:BSD-3-Clause
// copyright-holders: Devin Acker
/***************************************************************************
NEC/Casio uPD931 synthesis chip
***************************************************************************/
#ifndef MAME_SOUND_UPD931_H
#define MAME_SOUND_UPD931_H
#pragma once
#include <array>
class upd931_device : public device_t, public device_sound_interface, public device_memory_interface
{
public:
static constexpr feature_type imperfect_features() { return feature::SOUND; }
upd931_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0);
auto &set_master(bool master) { m_master = master; return *this; }
auto filter_cb() { return m_filter_cb.bind(); }
auto sync_cb() { return m_sync_cb.bind(); }
void i1_w(int state);
void i2_w(int state);
void i3_w(int state);
void db_w(u8 data);
u8 db_r();
void sync_w(int state);
protected:
device_memory_interface::space_config_vector memory_space_config() const override ATTR_COLD;
virtual void device_start() override ATTR_COLD;
virtual void device_reset() override ATTR_COLD;
virtual void device_clock_changed() override;
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
private:
static constexpr unsigned PITCH_SHIFT = 15;
static constexpr unsigned VOLUME_SHIFT = 15;
static constexpr unsigned VOLUME_MAX = (0xff << VOLUME_SHIFT);
static constexpr unsigned CLOCKS_PER_SAMPLE = 8;
static constexpr unsigned RETRIG_RATE = 0x60000;
enum
{
FLAG_DECAY1 = 0, // bits 0-2 = decay1 rate
FLAG_ATTACK2 = 3, // bits 3-5 = attack2 rate
FLAG_ATTACK1 = 6, // bits 6-8 = attack1 rate
FLAG_DECAY2_LEVEL = 9, // bit 9 = decay1->decay2 transition point
FLAG_DECAY2 = 10, // bit 10 = decay2 rate
FLAG_RETRIGGER = 11, // bit 11 = retrigger note during decay (mandolin effect)
FLAG_ENV_SPLIT = 12, // bit 12 = wave A envelope split(?)
FLAG_ATTACK2_B = 13, // bit 13 = wave B fades out during attack2
FLAG_ATTACK2_A = 14, // bit 14 = wave A fades in during attack2
FLAG_DECAY2_DISABLE = 15, // bit 15 = disable decay2
FLAG_ENV_SHIFT = 16, // bit 16-17 = envelope/vibrato rate shift
FLAG_MIRROR = 19, // bit 19 = mirror waveform on alternate cycles
FLAG_INVERT = 20, // bit 20 = invert waveform on alternate cycles
FLAG_MODE_B = 21, // bit 21-22 = wave B output mode
FLAG_MODE_A = 23, // bit 23-24 = wave A output mode
FLAG_WAVE_SEL = 25, // bit 25 = wave data input select
};
enum
{
ENV_IDLE,
ENV_ATTACK1,
ENV_ATTACK2,
ENV_DECAY1,
ENV_DECAY2,
ENV_RELEASE
};
struct voice_t
{
u8 m_note = 0;
u8 m_octave = 0;
u16 m_pitch = 0;
u32 m_pitch_counter = 0;
s8 m_timbre_shift = 0;
u8 m_wave_pos = 0;
s8 m_wave_out[2] = {0};
u8 m_env_state = ENV_IDLE;
u32 m_env_counter = 0;
u32 m_env_level[2] = {0};
u8 m_force_release = 0;
};
void io_map(address_map &map) ATTR_COLD;
TIMER_CALLBACK_MEMBER(timer_tick);
void note_w(offs_t offset, u8 data);
void octave_w(offs_t offset, u8 data);
void wave_pos_w(u8 data);
void wave_data_w(u8 data);
void flags_w(u8 data);
void status_latch_w(offs_t offset, u8 data);
void vibrato_w(u8 data);
void sustain_w(u8 data);
void note_on_w(offs_t offset, u8 data);
void note_on(voice_t &voice);
void reset_timer();
void update_env(voice_t &voice);
void update_wave(voice_t &voice);
address_space_config m_io_config;
memory_access<8, 0, 0, ENDIANNESS_LITTLE>::specific m_io;
sound_stream *m_stream;
emu_timer *m_retrig_timer;
devcb_write8 m_filter_cb;
devcb_write_line m_sync_cb;
bool m_master;
u16 m_pitch[12*6 + 1]; // valid note range = C1 to C7
u8 m_db;
u8 m_i1, m_i2, m_i3;
u8 m_addr, m_data;
u8 m_status;
// data global to all voices
u8 m_wave[2][16];
u8 m_wave_pos;
u8 m_vibrato;
u8 m_sustain;
u8 m_reverb;
u32 m_flags;
u32 m_last_clock;
std::array<voice_t, 8> m_voice;
};
DECLARE_DEVICE_TYPE(UPD931, upd931_device)
#endif // MAME_SOUND_UPD931_H

View File

@ -119,6 +119,7 @@ const double XTAL::known_xtals[] = {
4'433'619, // 4.433619_MHz_XTAL PAL color subcarrier (technically 4.43361875mhz)
4'608'000, // 4.608_MHz_XTAL Luxor ABC-77 keyboard (Keytronic custom part #48-300-107 is equivalent)
4'915'200, // 4.9152_MHz_XTAL -
4'946'864, // 4.946864_MHz_XTAL Casiotone 8000
4'952'000, // 4.952_MHz_XTAL IGS M036 based mahjong games, for TT5665 sound chip
5'000'000, // 5_MHz_XTAL Mutant Night
5'068'800, // 5.0688_MHz_XTAL Usually used as MC2661 or COM8116 baud rate clock

857
src/mame/casio/ct8000.cpp Normal file
View File

@ -0,0 +1,857 @@
// license:BSD-3-Clause
// copyright-holders:Devin Acker
// thanks-to:BCM
/***************************************************************************
Casiotone 8000 / "Symphonytron" system
The Symphonytron was a modular electronic organ produced by Casio in 1983.
The full system consists of:
- up to two Casiotone 8000 keyboards (8049 CPU, 2x uPD931 "vowel-consonant synthesis")
- RC-1 accompaniment unit (uPD7801 CPU, uPD930 rhythm generator, analog percussion)
- MB-1 memory unit (8049 CPU, 2x uPD931, RAM cartridge slot)
- FK-1 pedal keyboard (8049 CPU, single uPD931)
- CS-100 or CS-200 keyboard stand with built-in mixer
The keyboards and memory unit all connect to the RC-1 via 14-pin DIN connectors.
Although the RAM cart slot is located on the MB-1, all actual access to the cart is controlled
remotely by the RC-1, which uses the cart to record and play back both rhythm/chord and melody
data. The MB-1's sound hardware is then used to play back recorded melody data independently of
the keyboards. The RC-1 also has a "tone mix" feature, where note data received from one keyboard
is automatically forwarded to the other.
The individual units can also be used on their own; the MB-1 will also respond to notes and tone
selection commands via the DIN connector, but it needs the RC-1 present to do much else.
It's marked as "not working" for this reason.
This driver also features MIDI in/thru support via an "adapter" device which translates a subset
of MIDI messages into the protocol used with the original connectors.
TODO:
- volume/expression pedal (for all systems)
- fix aliasing in BBD output for some presets
***************************************************************************/
#include "emu.h"
#include "ct8000_midi.h"
#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/i8243.h"
#include "machine/rescap.h"
#include "sound/bbd.h"
#include "sound/flt_biquad.h"
#include "sound/flt_rc.h"
#include "sound/flt_vol.h"
#include "sound/mixer.h"
#include "sound/upd931.h"
#include "speaker.h"
#define LOG_VCO (1<<1)
// #define VERBOSE (LOG_GENERAL | LOG_VCO)
#include "logmacro.h"
#include "ct8000.lh"
#include "ctfk1.lh"
namespace {
class ct8000_state : public driver_device
{
public:
ct8000_state(machine_config const &mconfig, device_type type, char const *tag)
: driver_device(mconfig, type, tag)
, m_maincpu(*this, "maincpu")
, m_io(*this, "ioport%c", 'a')
, m_midi(*this, "midi")
, m_931a(*this, "upd931a")
, m_931b(*this, "upd931b")
, m_mixer(*this, "mixer")
, m_filter_rc(*this, "filter_rc%u", 0)
, m_filter_bq(*this, "filter_bq%u", 0)
, m_bbd(*this, "bbd")
, m_bank(*this, "bank")
, m_inputs(*this, "KC%X", 0) // schematic uses KC0-9, A-C
{
}
void config_base(machine_config &config) ATTR_COLD;
void ctmb1(machine_config &config) ATTR_COLD;
void ct8000(machine_config &config) ATTR_COLD;
void ctfk1(machine_config &config) ATTR_COLD;
ioport_value switch_r() { return m_switch; }
DECLARE_INPUT_CHANGED_MEMBER(switch_w);
DECLARE_INPUT_CHANGED_MEMBER(switch_clear_w);
protected:
void ct8000_map(address_map &map) ATTR_COLD;
void ct8000_io_map(address_map &map) ATTR_COLD;
virtual void driver_start() override ATTR_COLD;
void p1_w(u8 data);
u8 p1_r();
void p2_w(u8 data);
u8 p2_r();
void p4a_w(u8 data);
void p5a_w(u8 data);
void p6a_w(u8 data);
void p7a_w(u8 data);
void p4b_w(u8 data);
void p7b_w(u8 data);
u8 keys_r();
void filter_main_w(u8 data);
void filter_sub_w(u8 data);
void filter_bass_w(u8 data);
void pll_w(offs_t offset, u8 data);
virtual void update_clocks();
attoseconds_t chorus_cv(attotime const &curtime);
required_device<i8049_device> m_maincpu;
required_device_array<i8243_device, 2> m_io;
required_device<ct8000_midi_device> m_midi;
required_device<upd931_device> m_931a;
optional_device<upd931_device> m_931b;
required_device<mixer_device> m_mixer;
optional_device_array<filter_rc_device, 3> m_filter_rc;
optional_device_array<filter_biquad_device, 3> m_filter_bq;
optional_device<mn3207_device> m_bbd;
required_memory_bank m_bank;
optional_ioport_array<13> m_inputs;
u16 m_key_select;
u8 m_key_enable;
ioport_value m_switch;
u16 m_pll_counter[2];
u16 m_pll_ref[2];
u8 m_clock_select;
u8 m_clock_div;
};
void ct8000_state::ct8000_map(address_map &map)
{
map(0x800, 0xfff).bankr("bank");
}
void ct8000_state::ct8000_io_map(address_map &map)
{
map(0x00, 0xff).rw(FUNC(ct8000_state::keys_r), FUNC(ct8000_state::pll_w));
}
//**************************************************************************
void ct8000_state::config_base(machine_config &config)
{
I8049(config, m_maincpu, 4.946864_MHz_XTAL);
m_maincpu->set_addrmap(AS_PROGRAM, &ct8000_state::ct8000_map);
m_maincpu->set_addrmap(AS_IO, &ct8000_state::ct8000_io_map);
m_maincpu->p1_out_cb().set(FUNC(ct8000_state::p1_w));
m_maincpu->p1_in_cb().set(FUNC(ct8000_state::p1_r));
m_maincpu->p2_out_cb().set(FUNC(ct8000_state::p2_w));
m_maincpu->p2_in_cb().set(FUNC(ct8000_state::p2_r));
CT8000_MIDI(config, m_midi);
m_midi->int_cb().set_inputline(m_maincpu, MCS48_INPUT_IRQ);
m_maincpu->t0_in_cb().set(m_midi, FUNC(ct8000_midi_device::ack_r));
midi_port_device &mdin(MIDI_PORT(config, "mdin", midiin_slot, "midiin"));
mdin.rxd_handler().set(m_midi, FUNC(ct8000_midi_device::rx_w));
MIDI_PORT(config, "mdthru", midiout_slot, "midiout");
mdin.rxd_handler().append("mdthru", FUNC(midi_port_device::write_txd));
I8243(config, m_io[0]);
m_maincpu->prog_out_cb().set(m_io[0], FUNC(i8243_device::prog_w));
m_io[0]->p4_out_cb().set(FUNC(ct8000_state::p4a_w));
m_io[0]->p5_out_cb().set(FUNC(ct8000_state::p5a_w));
m_io[0]->p6_out_cb().set(FUNC(ct8000_state::p6a_w));
m_io[0]->p7_out_cb().set(FUNC(ct8000_state::p7a_w));
I8243(config, m_io[1]);
m_maincpu->prog_out_cb().append(m_io[1], FUNC(i8243_device::prog_w));
m_io[1]->p4_out_cb().set(FUNC(ct8000_state::p4b_w));
m_io[1]->p5_in_cb().set(m_midi, FUNC(ct8000_midi_device::data_r));
m_io[1]->p5_out_cb().set_nop();
m_io[1]->p6_out_cb().set(m_midi, FUNC(ct8000_midi_device::data_w));
m_io[1]->p7_out_cb().set(FUNC(ct8000_state::p7b_w));
}
//**************************************************************************
void ct8000_state::ctmb1(machine_config &config)
{
config_base(config);
SPEAKER(config, "lspeaker").front_left();
SPEAKER(config, "rspeaker").front_right();
MIXER(config, m_mixer);
m_mixer->add_route(0, "lspeaker", 1.0);
m_mixer->add_route(0, "rspeaker", 1.0);
// 931 A - sub (consonant) waveform
UPD931(config, m_931a, m_maincpu->clock());
m_931a->set_master(false);
m_931a->filter_cb().set(FUNC(ct8000_state::filter_sub_w));
/*
Both of the uPD931s have a fairly large amount of headroom in their outputs,
which is partly compensated for via adjustable gain (see filter_main_w and filter_sub_w),
but the base output gain should be boosted as well to provide a decent volume.
*/
m_931a->add_route(0, m_filter_rc[0], 2.0);
// sub HPF
FILTER_RC(config, m_filter_rc[0]).add_route(0, m_filter_bq[0], 1.0);
FILTER_BIQUAD(config, m_filter_bq[0]).add_route(0, m_filter_rc[1], 1.0);
// sub LPF
FILTER_RC(config, m_filter_rc[1]).add_route(0, m_filter_bq[1], 1.0);
FILTER_BIQUAD(config, m_filter_bq[1]).add_route(0, "filter_ac0", 1.0);
FILTER_RC(config, "filter_ac0").set_ac().add_route(0, m_mixer, 1.0);
// 931 B - main (vowel) waveform
UPD931(config, m_931b, m_maincpu->clock());
m_931b->sync_cb().set(m_931a, FUNC(upd931_device::sync_w));
m_931b->filter_cb().set(FUNC(ct8000_state::filter_main_w));
m_931b->add_route(0, m_filter_rc[2], 2.0);
// main LPF
FILTER_RC(config, m_filter_rc[2]).add_route(0, m_filter_bq[2], 1.0);
FILTER_BIQUAD(config, m_filter_bq[2]).add_route(0, "filter_ac1", 1.0);
FILTER_RC(config, "filter_ac1").set_ac().add_route(0, m_mixer, 1.0);
MN3207(config, m_bbd).set_cv_handler(FUNC(ct8000_state::chorus_cv));
m_mixer->add_route(0, m_bbd, 1.0);
m_bbd->add_route(ALL_OUTPUTS, "chorus", 0.5);
auto &bbd_mixer = MIXER(config, "chorus");
bbd_mixer.add_route(0, "lspeaker", 0.4);
bbd_mixer.add_route(0, "rspeaker", -0.4);
}
//**************************************************************************
void ct8000_state::ct8000(machine_config &config)
{
ctmb1(config);
config.set_default_layout(layout_ct8000);
}
//**************************************************************************
void ct8000_state::ctfk1(machine_config &config)
{
config_base(config);
m_io[0]->p4_out_cb().set_nop(); // no switchable clock
// valid program/patch numbers on FK-1 start at 1 instead of 0 for some reason
m_midi->set_base_program(1);
SPEAKER(config, "speaker").front_center();
MIXER(config, m_mixer).add_route(0, "speaker", 1.0);
UPD931(config, m_931a, m_maincpu->clock());
m_931a->filter_cb().set(FUNC(ct8000_state::filter_bass_w));
m_931a->add_route(0, m_filter_rc[0], 4.0); // boost volume even more since the FK-1 is monophonic
FILTER_RC(config, m_filter_rc[0]).add_route(0, m_filter_bq[0], 1.0);
FILTER_BIQUAD(config, m_filter_bq[0]).add_route(0, "filter_ac", 1.0);
FILTER_RC(config, "filter_ac").set_ac().add_route(0, m_mixer, 1.0);
config.set_default_layout(layout_ctfk1);
}
//**************************************************************************
void ct8000_state::driver_start()
{
m_bank->configure_entries(0, 2, memregion("bankrom")->base(), 0x800);
m_key_select = 0xffff;
m_key_enable = 0;
m_switch = 0;
m_pll_counter[0] = m_pll_counter[1] = 0;
m_pll_ref[0] = m_pll_ref[1] = 0;
m_clock_select = m_clock_div = 0;
save_item(NAME(m_key_select));
save_item(NAME(m_key_enable));
save_item(NAME(m_switch));
save_item(NAME(m_pll_counter));
save_item(NAME(m_pll_ref));
save_item(NAME(m_clock_select));
save_item(NAME(m_clock_div));
}
//**************************************************************************
void ct8000_state::p1_w(u8 data)
{
// bit 0-3: 931 data
// bit 4: 931 strobe 1 (I1)
// bit 5: 931 strobe 2 (I2)
// bit 6: 931 B select (ct8000) / 931 select (fk1)
// bit 7: 931 A select (ct8000) / unused (fk1)
m_931a->db_w(data & 0xf);
m_931a->i1_w(BIT(data, 4));
m_931a->i2_w(BIT(data, 5));
if (m_931b)
{
m_931b->db_w(data & 0xf);
m_931b->i1_w(BIT(data, 4));
m_931b->i2_w(BIT(data, 5));
m_931b->i3_w(BIT(data, 6));
m_931a->i3_w(BIT(data, 7));
}
else
{
m_931a->i3_w(BIT(data, 6));
}
}
//**************************************************************************
u8 ct8000_state::p1_r()
{
u8 status = m_931a->db_r();
if (m_931b)
status &= m_931b->db_r();
return 0xf0 | status;
}
//**************************************************************************
void ct8000_state::p2_w(u8 data)
{
m_io[0]->p2_w(data & 0xf);
m_io[1]->p2_w(data & 0xf);
m_io[0]->cs_w(BIT(data, 4));
m_io[1]->cs_w(BIT(data, 6));
m_bank->set_entry(BIT(data, 7));
}
//**************************************************************************
u8 ct8000_state::p2_r()
{
return 0xf0 | (m_io[0]->p2_r() & m_io[1]->p2_r());
}
//**************************************************************************
void ct8000_state::p4a_w(u8 data)
{
// bit 0: master 931 clock select (1 = master VCO, 0 = slave VCO)
// bit 1-2: master 931 clock divider (3, 6, 12, 4)
// bit 3: 931 reset
m_clock_select = BIT(~data, 0);
m_clock_div = BIT(data, 1, 2);
update_clocks();
}
//**************************************************************************
void ct8000_state::p5a_w(u8 data)
{
// key matrix out bits 4-7
m_key_select &= 0xff0f;
m_key_select |= (data << 4);
}
//**************************************************************************
void ct8000_state::p6a_w(u8 data)
{
// bit 0: unused
// bit 1: key matrix in enable (active low)
// bit 2-3: key matrix out bits 8-9
m_key_enable = BIT(~data, 1);
m_key_select &= 0xfcff;
m_key_select |= (BIT(data, 2, 2) << 8);
}
//**************************************************************************
void ct8000_state::p7a_w(u8 data)
{
// key matrix out bits 0-3
m_key_select &= 0xfff0;
m_key_select |= data;
}
//**************************************************************************
void ct8000_state::p4b_w(u8 data)
{
// bit 0: data out strobe to RC-1
// bit 1: data in ack to RC-1 (active low)
// bit 2: stereo chorus enable (active low)
// bit 3: stereo chorus enable for line in (active low)
m_midi->strobe_w(BIT(data, 0));
m_midi->ack_w(BIT(data, 1));
if (m_bbd)
m_bbd->set_input_gain(0, BIT(data, 2) ? 0 : 1.0);
}
//**************************************************************************
void ct8000_state::p7b_w(u8 data)
{
// bit 0-2: key matrix out bits 10-12
// bit 3: mute
m_key_select &= 0xe3ff;
m_key_select |= ((data & 0x7) << 10);
// mute is applied to mixed/filtered 931 output before the stereo chorus
const double gain = BIT(data, 3) ? 1.0 : 0.0;
m_mixer->set_input_gain(0, gain);
m_mixer->set_input_gain(1, gain);
}
//**************************************************************************
u8 ct8000_state::keys_r()
{
u8 data = 0xff;
if (m_key_enable)
{
for (int i = 0; i < m_inputs.size(); i++)
if (!BIT(m_key_select, i))
data &= m_inputs[i].read_safe(0xff);
}
return data;
}
//**************************************************************************
INPUT_CHANGED_MEMBER(ct8000_state::switch_w)
{
if (!oldval && newval)
m_switch |= param;
}
//**************************************************************************
INPUT_CHANGED_MEMBER(ct8000_state::switch_clear_w)
{
if (!oldval && newval)
m_switch = 0;
}
//**************************************************************************
void ct8000_state::filter_main_w(u8 data)
{
// bit 0-1 = main LPF cutoff
// bit 2-3 = absolute gain
double r, c;
if (BIT(data, 1))
{
r = RES_K(68);
c = CAP_P(470) + BIT(data, 0) ? CAP_P(330) : 0;
}
else
{
r = RES_K(43);
c = CAP_P(390);
}
m_filter_rc[2]->filter_rc_set_RC(filter_rc_device::LOWPASS, r, 0, 0, CAP_N(2.2));
m_filter_bq[2]->opamp_sk_lowpass_modify(r, r, RES_M(999.99), RES_R(0.001), CAP_N(5.6), c);
static const double gain[] = { 3.79, 2.82, 1.94, 1 };
m_mixer->set_output_gain(0, gain[BIT(data, 2, 2)]);
}
//**************************************************************************
void ct8000_state::filter_sub_w(u8 data)
{
// bit 0 = sub HPF enable
// bit 1 = sub LPF cutoff
// bit 2 = sub LPF disable
// bit 3 = consonant gain
if (BIT(data, 0))
{
const double c = CAP_N(2.2);
m_filter_rc[0]->filter_rc_set_RC(filter_rc_device::HIGHPASS, RES_K(18), 0, 0, c);
m_filter_bq[0]->opamp_sk_highpass_modify(RES_K(6.8), RES_K(220), RES_M(999.99), RES_R(0.001), c, c);
}
else
{
m_filter_rc[0]->filter_rc_set_RC(filter_rc_device::HIGHPASS, 0, 0, 0, 0);
m_filter_bq[0]->modify_raw(0, 0, 1, 0, 0);
}
if (BIT(data, 2))
{
m_filter_rc[1]->filter_rc_set_RC(filter_rc_device::LOWPASS, 0, 0, 0, 0);
m_filter_bq[1]->modify_raw(0, 0, 1, 0, 0);
}
else
{
const double r = BIT(data, 1) ? RES_K(22) : RES_K(56);
const double c = BIT(data, 1) ? CAP_P(430) : CAP_P(330);
m_filter_rc[1]->filter_rc_set_RC(filter_rc_device::LOWPASS, r, 0, 0, CAP_N(2.2));
m_filter_bq[1]->opamp_sk_lowpass_modify(r, r, RES_M(999.99), RES_R(0.001), CAP_N(5.6), c);
}
m_filter_bq[1]->set_output_gain(0, BIT(data, 3) ? 0.468 : 1);
}
//**************************************************************************
void ct8000_state::filter_bass_w(u8 data)
{
// bit 0 = LPF cutoff
// bit 1 = LPF enable (seems to be incorrectly inverted in the schematic)
// bit 2-3 = gain
if (BIT(data, 1))
{
const double r = RES_K(12);
const double c = CAP_N(3.3) + BIT(data, 0) ? CAP_N(10) : 0;
m_filter_rc[0]->filter_rc_set_RC(filter_rc_device::LOWPASS, r, 0, 0, CAP_N(22));
m_filter_bq[0]->opamp_sk_lowpass_modify(r, r, RES_M(999.99), RES_R(0.001), CAP_N(56), c);
}
else
{
m_filter_rc[0]->filter_rc_set_RC(filter_rc_device::LOWPASS, 0, 0, 0, 0);
m_filter_bq[0]->modify_raw(0, 0, 1, 0, 0);
}
static const double gain[] = { 3.79, 2.82, 1.94, 1 };
m_mixer->set_output_gain(ALL_OUTPUTS, gain[BIT(data, 2, 2)]);
}
//**************************************************************************
void ct8000_state::pll_w(offs_t offset, u8 data)
{
data &= 0xf;
for (int i = 0; i < 2; i++)
{
if (!BIT(offset, 3 + i))
{
switch (offset & 7)
{
case 0:
m_pll_counter[i] &= 0x3ff0;
m_pll_counter[i] |= data;
break;
case 1:
m_pll_counter[i] &= 0x3f0f;
m_pll_counter[i] |= (data << 4);
break;
case 2:
m_pll_counter[i] &= 0x30ff;
m_pll_counter[i] |= (data << 8);
break;
case 3:
m_pll_counter[i] &= 0x0fff;
m_pll_counter[i] |= ((data & 3) << 12);
break;
case 4:
m_pll_ref[i] &= 0xff0;
m_pll_ref[i] |= data;
break;
case 5:
m_pll_ref[i] &= 0xf0f;
m_pll_ref[i] |= (data << 4);
break;
case 6:
m_pll_ref[i] &= 0x0ff;
m_pll_ref[i] |= (data << 8);
break;
default:
break;
}
}
}
update_clocks();
}
//**************************************************************************
void ct8000_state::update_clocks()
{
double clock_scale[2] = { 1.0, 1.0 };
// master VCO freq (controls 931 B)
const int sel = m_clock_select & 1;
if (m_931b && m_pll_counter[sel] && m_pll_ref[sel])
{
// PLLs are tuned so that increasing/decreasing the counter by 1 raises or lowers pitch by ~1.5 cents
clock_scale[0] = (2.0 * m_pll_counter[sel]) / m_pll_ref[sel];
switch (m_clock_div & 3)
{
case 0: clock_scale[0] /= 3; break;
case 1: clock_scale[0] /= 6; break;
case 2: clock_scale[0] /= 12; break;
case 3: clock_scale[0] /= 4; break;
}
m_931b->set_clock_scale(clock_scale[0]);
}
// slave VCO freq (controls 931 A and CPU)
if (m_pll_counter[1] && m_pll_ref[1])
{
clock_scale[1] = (2.0 * m_pll_counter[1]) / (3 * m_pll_ref[1]);
m_maincpu->set_clock_scale(clock_scale[1]);
m_931a->set_clock_scale(clock_scale[1]);
}
LOGMASKED(LOG_VCO, "VCO #1 %.3f MHz, #2 %.3f MHz\n",
clock_scale[0] * m_maincpu->unscaled_clock() / 1'000'000,
clock_scale[1] * m_maincpu->unscaled_clock() / 1'000'000);
}
//**************************************************************************
attoseconds_t ct8000_state::chorus_cv(attotime const &cvtime)
{
// 62.5 to 80 kHz, varies at 0.6666... Hz
double pos = cvtime.as_double() / 1.5;
pos -= std::floor(pos);
pos = (pos < 0.5) ? (2 * pos) : 2 * (1.0 - pos);
const double bbd_freq = 62500 + (80000 - 62500) * pos;
return HZ_TO_ATTOSECONDS(bbd_freq);
}
INPUT_PORTS_START(ct8000)
PORT_START("KC0")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C2")
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C#2")
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D2")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D#2")
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("E2")
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F2")
PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("KC1")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F#2")
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G2")
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G#2")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A2")
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A#2")
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("B2")
PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("KC2")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C3")
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C#3")
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D3")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D#3")
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("E3")
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F3")
PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("KC3")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F#3")
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G3")
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G#3")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A3")
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A#3")
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("B3")
PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("KC4")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C4")
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C#4")
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D4")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D#4")
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("E4")
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F4")
PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("KC5")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F#4")
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G4")
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G#4")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A4")
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A#4")
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("B4")
PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("KC6")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C5")
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C#5")
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D5")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D#5")
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("E5")
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F5")
PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("KC7")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F#5")
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G5")
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G#5")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A5")
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A#5")
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("B5")
PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("KC8")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C6")
PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("KC9")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Sustain Pedal")
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tune Up")
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tune Down")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Stereo Chorus") PORT_TOGGLE
PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("KCA")
PORT_BIT(0x07, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_CUSTOM_MEMBER(FUNC(ct8000_state::switch_r))
PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("KCB")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tone Memory 1")
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tone Memory 2")
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tone Memory 3")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tone Memory 4")
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tone Set") PORT_TOGGLE
PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("KCC")
PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Vibrato") PORT_TOGGLE
PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("SWITCH")
PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Effect Cancel") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ct8000_state::switch_clear_w), 0)
PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Sustain") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ct8000_state::switch_w), 0x1)
PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Reverb") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ct8000_state::switch_w), 0x4)
INPUT_PORTS_END
INPUT_PORTS_START(ctfk1)
PORT_START("KC0")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C1")
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C#1")
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D1")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D#1")
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("E1")
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F1")
PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("KC1")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F#1")
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G1")
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G#1")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A1")
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A#1")
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("B1")
PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("KC2")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C2")
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C#2")
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D2")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D#2")
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("E2")
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F2")
PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("KC9")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tune Up")
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tune Down")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Bass Select") PORT_TOGGLE
PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("KCA")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Sustain") PORT_TOGGLE
PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("KCB")
PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED) // KI0->KCB diode
PORT_BIT(0x0e, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tone Set") PORT_TOGGLE
PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END
INPUT_PORTS_START(ctmb1)
PORT_START("KC0")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Reverse")
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Play")
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Forward")
PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("KC1")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Keyboard Synchro")
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Record")
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Reset")
PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("KC2")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Pause")
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Demonstration")
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Number Select")
PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("KC9")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tune Up")
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tune Down")
PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END
ROM_START(ct8000)
ROM_REGION(0x800, "maincpu", 0)
ROM_LOAD("upd8049c-364.bin", 0x000, 0x800, CRC(8c9b3f40) SHA1(5507117671300fe350df39b4ca70239079a1b0e7))
ROM_REGION(0x1000, "bankrom", 0)
ROM_LOAD("ct8000_2732.bin", 0x0000, 0x1000, CRC(e6f1f3f9) SHA1(637a9d2b6be2f12240724c8aa5744c5f28ac4af0))
ROM_END
ROM_START(ctfk1)
ROM_REGION(0x800, "maincpu", 0)
ROM_LOAD("upd8049c-364.bin", 0x000, 0x800, CRC(8c9b3f40) SHA1(5507117671300fe350df39b4ca70239079a1b0e7))
ROM_REGION(0x1000, "bankrom", 0)
ROM_LOAD("fk1_2732.bin", 0x0000, 0x1000, CRC(f7416356) SHA1(6d92aa5f377769327451334324f1f32b52951968))
ROM_END
ROM_START(ctmb1)
ROM_REGION(0x800, "maincpu", 0)
ROM_LOAD("upd8049c-364.bin", 0x000, 0x800, CRC(8c9b3f40) SHA1(5507117671300fe350df39b4ca70239079a1b0e7))
ROM_REGION(0x1000, "bankrom", 0)
ROM_LOAD("mb1_2732.bin", 0x0000, 0x1000, CRC(fc128110) SHA1(8ff31fdccb8200836cf414bc709d5b9a68279205))
ROM_END
} // anonymous namespace
// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
SYST( 1983, ct8000, 0, 0, ct8000, ct8000, ct8000_state, empty_init, "Casio", "Casiotone 8000", MACHINE_SUPPORTS_SAVE )
SYST( 1983, ctfk1, 0, 0, ctfk1, ctfk1, ct8000_state, empty_init, "Casio", "Casiotone FK-1", MACHINE_SUPPORTS_SAVE )
SYST( 1983, ctmb1, 0, 0, ctmb1, ctmb1, ct8000_state, empty_init, "Casio", "Casiotone MB-1", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )

View File

@ -0,0 +1,409 @@
// license:BSD-3-Clause
// copyright-holders:Devin Acker
/***************************************************************************
Casiotone 8000 / Symphonytron MIDI interface
Translates MIDI messages into the 4-bit parallel protocol used by the Symphonytron.
Aside from Note Off/On and Program Change messages, this also handles:
- CC #1 (vibrato)
- CC #64 (sustain pedal)
- CC #69 (sustain switch)
- CC #91 (reverb)
- CC #93 (chorus)
- CC #120 (all sound off)
- CC #121 (reset controllers)
- CC #123 (all notes off)
- CC #124-127 (equivalent to 123, otherwise no effect)
- MIDI reset (status 0xFF)
Vibrato, reverb, and chorus CCs all behave as switches, where 64-127 is "on".
-------------------------------------------------------------------------------
Summary of 4-bit Symphonytron messages
Messages are sent in 4-bit increments over the DIN connector, in the following order:
- high nibble of command
- number of following nibbles (normally 1 or 3)
- low nibble of command
- high nibble of data (if any)
- low nibble of data (if any)
The exception is the reset command, which is a single 0xF nibble.
The CT-8000 recognizes these messages:
10 xx - set effects
11 xx - lock effects (corresponding buttons/pedal do nothing while set)
12 xx - ??? effects
^^ bit 0 = reverb
bit 1 = vibrato
bit 2 = sustain pedal (only affects the local keyboard)
bit 3 = stereo chorus
bit 4 = sustain switch
20 xx - set lowest possible note to send (default is none)
21 xx - set highest possible note to send (default is none)
22 xx - set lowest possible note to receive/play (default is 0x21 = C2)
23 xx - set highest possible note to receive/play (default is 0x61 = C6)
24 - stop all notes
28 xx - note on or off
^^ bit 7 = note on, bits 6-4 = octave, bits 3-0 = note from 0x1 (C) to 0xC (B)
C0 xx - set tuning (signed, 1.5 cent increments, 0x32 = quarter tone up)
E1 xx - set patch number
F - reset instrument
The MB-1 recognizes all of the above, plus:
A0 xx - ???
A1 xx - ???
A2 xx - ???
A3 xx - ???
A4 - clear values from A0-A3
***************************************************************************/
#include "emu.h"
#include "ct8000_midi.h"
#include <algorithm>
DEFINE_DEVICE_TYPE(CT8000_MIDI, ct8000_midi_device, "ct8000_midi", "Casiotone 8000 MIDI adapter")
namespace {
INPUT_PORTS_START(ct8000_midi)
PORT_START("CHANNEL")
PORT_CONFNAME(0xff, 0x00, "MIDI channel")
PORT_CONFSETTING( 0x00, "1")
PORT_CONFSETTING( 0x01, "2")
PORT_CONFSETTING( 0x02, "3")
PORT_CONFSETTING( 0x03, "4")
PORT_CONFSETTING( 0x04, "5")
PORT_CONFSETTING( 0x05, "6")
PORT_CONFSETTING( 0x06, "7")
PORT_CONFSETTING( 0x07, "8")
PORT_CONFSETTING( 0x08, "9")
PORT_CONFSETTING( 0x09, "10")
PORT_CONFSETTING( 0x0a, "11")
PORT_CONFSETTING( 0x0b, "12")
PORT_CONFSETTING( 0x0c, "13")
PORT_CONFSETTING( 0x0d, "14")
PORT_CONFSETTING( 0x0e, "15")
PORT_CONFSETTING( 0x0f, "16")
INPUT_PORTS_END
} // anonymous namespace
ct8000_midi_device::ct8000_midi_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, CT8000_MIDI, tag, owner, clock)
, device_serial_interface(mconfig, *this)
, m_channel(*this, "CHANNEL")
, m_int_cb(*this)
, m_base_program(0)
{
}
/**************************************************************************/
ioport_constructor ct8000_midi_device::device_input_ports() const
{
return INPUT_PORTS_NAME(ct8000_midi);
}
/**************************************************************************/
void ct8000_midi_device::device_start()
{
m_midi_data[0] = m_midi_data[1] = 0;
m_ack = 1;
std::fill(std::begin(m_out_data), std::end(m_out_data), 0);
m_in_data = 0xf;
m_in_strobe = 0;
set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1);
set_rcv_rate(31250);
set_tra_rate(0);
save_item(NAME(m_status));
save_item(NAME(m_midi_data));
save_item(NAME(m_midi_data_pos));
save_item(NAME(m_midi_data_size));
save_item(NAME(m_midi_notes_on));
save_item(NAME(m_sustain));
save_item(NAME(m_ack));
save_item(NAME(m_int));
save_item(NAME(m_out_data));
save_item(NAME(m_out_data_read));
save_item(NAME(m_out_data_write));
save_item(NAME(m_out_data_space));
save_item(NAME(m_out_data_controls));
save_item(NAME(m_in_data));
save_item(NAME(m_in_strobe));
}
/**************************************************************************/
void ct8000_midi_device::device_reset()
{
m_status = 0;
m_midi_data_pos = m_midi_data_size = 0;
m_out_data_space = sizeof(m_out_data);
m_sustain = 0;
std::fill(std::begin(m_midi_notes_on), std::begin(m_midi_notes_on), 0);
m_out_data_read = m_out_data_write = 0;
m_out_data_controls = 0;
m_int_cb(m_int = 0);
}
/**************************************************************************/
void ct8000_midi_device::rcv_complete()
{
receive_register_extract();
if (is_receive_framing_error())
{
m_status = 0;
return;
}
const u8 data = get_received_char();
if (data < 0x80)
{
if (m_status >= 0x80)
{
m_midi_data[m_midi_data_pos++] = data;
if (m_midi_data_pos == m_midi_data_size)
{
midi_message();
m_midi_data_pos = 0;
}
}
}
else if (data < 0xf0)
{
m_status = data;
if (data >= 0xc0 && data <= 0xdf)
m_midi_data_size = 1;
else
m_midi_data_size = 2;
m_midi_data_pos = 0;
}
else if (data < 0xf8)
{
m_status = 0;
}
else if (data == 0xff)
{
reset();
send_nibble(0xf);
}
}
/**************************************************************************/
u8 ct8000_midi_device::data_r()
{
return m_out_data[m_out_data_read];
}
/**************************************************************************/
void ct8000_midi_device::ack_w(int state)
{
if (m_ack && !state)
{
m_int_cb(m_int = 0);
m_out_data_read++;
m_out_data_space++;
m_out_data_read %= sizeof(m_out_data);
}
else if (!m_ack && state)
{
if (m_out_data_read != m_out_data_write)
m_int_cb(m_int = 1);
}
m_ack = state;
}
/**************************************************************************/
void ct8000_midi_device::strobe_w(int state)
{
if (!m_in_strobe && state)
logerror("data write: %x\n", m_in_data);
m_in_strobe = state;
}
/**************************************************************************/
void ct8000_midi_device::midi_message()
{
if ((m_status & 0xf) != m_channel->read())
return;
switch (m_status >> 4)
{
case 0x8: // note off
m_midi_data[1] = 0;
[[fallthrough]];
case 0x9: // note on
if (m_midi_data[0] >= NOTE_MIN && m_midi_data[0] <= NOTE_MAX)
send_note(m_midi_data[0] - NOTE_MIN, m_midi_data[1]);
break;
case 0xb: // controller
switch (m_midi_data[0])
{
case 1: // mod wheel / vibrato
update_control(FX_VIBRATO);
break;
case 64: // sustain pedal
update_sustain(m_midi_data[1]);
break;
case 69: // hold 2 / sustain switch
update_control(FX_SUSTAIN);
break;
case 91: // reverb
update_control(FX_REVERB);
break;
case 93: // chorus
update_control(FX_CHORUS);
break;
case 120: // all sound off
send_cmd(CMD_STOP);
break;
case 121: // reset controllers
m_out_data_controls = 0;
send_cmd(CMD_EFFECTS, 0);
update_sustain(0);
break;
case 123: // all notes off
case 124: // omni off
case 125: // omni on
case 126: // mono mode
case 127: // poly mode
for (unsigned i = 0; i < sizeof(m_midi_notes_on); i++)
{
if (m_midi_notes_on[i])
send_note(i, 0);
}
break;
}
break;
case 0xc: // program change
send_cmd(CMD_TONE, m_midi_data[0] + m_base_program);
break;
}
}
/**************************************************************************/
void ct8000_midi_device::send_nibble(u8 data)
{
m_out_data[m_out_data_write++] = data;
m_out_data_write %= sizeof(m_out_data);
m_out_data_space--;
if (m_ack && !m_int)
m_int_cb(m_int = 1);
}
/**************************************************************************/
void ct8000_midi_device::send_cmd(u8 cmd)
{
if (m_out_data_space >= 3)
{
send_nibble(cmd >> 4);
send_nibble(1); // number of following nibbles
send_nibble(cmd & 0xf);
}
}
/**************************************************************************/
void ct8000_midi_device::send_cmd(u8 cmd, u8 data)
{
if (m_out_data_space >= 5)
{
send_nibble(cmd >> 4);
send_nibble(3); // number of following nibbles
send_nibble(cmd & 0xf);
send_nibble(data >> 4);
send_nibble(data & 0xf);
}
}
/**************************************************************************/
void ct8000_midi_device::send_note(u8 note, u8 on)
{
if (!on && m_sustain)
{
m_midi_notes_on[note] = 0x80;
return;
}
u8 out_note = ((note / 12) << 4) + (note % 12) + 1;
if (on && m_midi_notes_on[note])
{
// force existing note off even if sustained
send_cmd(CMD_NOTE, out_note);
}
if (on)
out_note |= 0x80;
send_cmd(CMD_NOTE, out_note);
m_midi_notes_on[note] = on;
}
/**************************************************************************/
void ct8000_midi_device::update_control(u8 control)
{
if (m_midi_data[1] >= 0x40)
{
if (m_out_data_controls & control)
return;
m_out_data_controls |= control;
}
else
{
if (!(m_out_data_controls & control))
return;
m_out_data_controls &= ~control;
}
send_cmd(CMD_EFFECTS, m_out_data_controls);
}
/**************************************************************************/
void ct8000_midi_device::update_sustain(u8 value)
{
if (value >= 0x40)
{
m_sustain = 1;
}
else if (m_sustain)
{
m_sustain = 0;
for (unsigned i = 0; i < sizeof(m_midi_notes_on); i++)
{
if (BIT(m_midi_notes_on[i], 7))
send_note(i, 0);
}
}
}

View File

@ -0,0 +1,89 @@
// license:BSD-3-Clause
// copyright-holders: Devin Acker
#ifndef MAME_CASIO_CT8000_MIDI_H
#define MAME_CASIO_CT8000_MIDI_H
#pragma once
#include "diserial.h"
class ct8000_midi_device : public device_t, public device_serial_interface
{
public:
ct8000_midi_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
auto &set_base_program(u8 base) { m_base_program = base; return *this; }
auto int_cb() { return m_int_cb.bind(); }
u8 data_r();
void data_w(u8 data) { m_in_data = data; }
int ack_r() { return m_in_strobe; }
void ack_w(int state);
void strobe_w(int state);
protected:
virtual ioport_constructor device_input_ports() const override ATTR_COLD;
virtual void device_start() override ATTR_COLD;
virtual void device_reset() override ATTR_COLD;
virtual void rcv_complete() override;
private:
static constexpr unsigned NOTE_MIN = 12; // C0
static constexpr unsigned NOTE_MAX = 107; // B7
enum
{
CMD_EFFECTS = 0x10,
CMD_STOP = 0x24,
CMD_NOTE = 0x28,
CMD_TONE = 0xe1,
};
enum
{
FX_REVERB = 0x01,
FX_VIBRATO = 0x02,
FX_PEDAL = 0x04, // only affects the local keyboard, so unused here
FX_CHORUS = 0x08,
FX_SUSTAIN = 0x10
};
void midi_message();
void send_nibble(u8 data);
void send_cmd(u8 cmd);
void send_cmd(u8 cmd, u8 data);
void send_note(u8 note, u8 on);
void update_control(u8 control);
void update_sustain(u8 value);
required_ioport m_channel;
devcb_write_line m_int_cb;
u8 m_base_program;
u8 m_status;
u8 m_midi_data[2];
u8 m_midi_data_pos, m_midi_data_size;
u8 m_midi_notes_on[NOTE_MAX - NOTE_MIN + 1];
u8 m_sustain;
u8 m_ack;
u8 m_int;
u8 m_out_data[0x1000];
u16 m_out_data_read, m_out_data_write;
u16 m_out_data_space;
u8 m_out_data_controls;
u8 m_in_data;
u8 m_in_strobe;
};
DECLARE_DEVICE_TYPE(CT8000_MIDI, ct8000_midi_device)
#endif // MAME_CASIO_CT8000_MIDI_H

423
src/mame/layout/ct8000.lay Normal file
View File

@ -0,0 +1,423 @@
<?xml version="1.0"?>
<!--
license:CC0-1.0
-->
<mamelayout version="2">
<element name="bg"><rect><color red="0.75" green="0.75" blue="0.775" /></rect></element>
<element name="line"><rect><color red="0.55" green="0.55" blue="0.575" /></rect></element>
<element name="line_white"><rect><color red="1" green="1" blue="0.95" /></rect></element>
<element name="line_black"><rect><color red="0" green="0" blue="0" /></rect></element>
<element name="button_back">
<rect><bounds x="0" y="0" width="1" height="20" /><color red="0.55" green="0.55" blue="0.575" /></rect>
<rect><bounds x="0" y="1" width="1" height="18" /><color red="0.65" green="0.65" blue="0.675" /></rect>
</element>
<element name="panel_back"><rect><color red="0.7" green="0.7" blue="0.725" /></rect></element>
<!-- text elements -->
<element name="cancel"><text string="CANCEL"><color red="0" blue="0" green="0" /></text></element>
<element name="sustain"><text string="SUSTAIN"><color red="0" blue="0" green="0" /></text></element>
<element name="reverb"><text string="REVERB"><color red="0" blue="0" green="0" /></text></element>
<element name="vibrato"><text string="VIBRATO"><color red="0" blue="0" green="0" /></text></element>
<element name="stereo"><text string="STEREO"><color red="0" blue="0" green="0" /></text></element>
<element name="chorus"><text string="CHORUS"><color red="0" blue="0" green="0" /></text></element>
<element name="effect"><text string="EFFECT"><color red="0" blue="0" green="0" /></text></element>
<element name="tone_memory"><text string="TONE MEMORY"><color red="0" blue="0" green="0" /></text></element>
<element name="num1"><text string="1"><color red="0" blue="0" green="0" /></text></element>
<element name="num2"><text string="2"><color red="0" blue="0" green="0" /></text></element>
<element name="num3"><text string="3"><color red="0" blue="0" green="0" /></text></element>
<element name="num4"><text string="4"><color red="0" blue="0" green="0" /></text></element>
<element name="mode"><text string="MODE"><color red="0" blue="0" green="0" /></text></element>
<element name="set"><text string="SET"><color red="0" blue="0" green="0" /></text></element>
<element name="play"><text string="PLAY"><color red="0" blue="0" green="0" /></text></element>
<element name="tune"><text string="TUNE"><color red="0" blue="0" green="0" /></text></element>
<element name="tone1"><text string="PIANO 1"><color red="0" blue="0" green="0" /></text></element>
<element name="tone2"><text string="PIPE ORGAN 1"><color red="0" blue="0" green="0" /></text></element>
<element name="tone3"><text string="PIANO 2"><color red="0" blue="0" green="0" /></text></element>
<element name="tone4"><text string="PIPE ORGAN 2"><color red="0" blue="0" green="0" /></text></element>
<element name="tone5"><text string="PIANO 3"><color red="0" blue="0" green="0" /></text></element>
<element name="tone6"><text string="PIANO 4"><color red="0" blue="0" green="0" /></text></element>
<element name="tone7"><text string="PIPE ORGAN 3"><color red="0" blue="0" green="0" /></text></element>
<element name="tone8"><text string="HONKY TONK"><color red="0" blue="0" green="0" /></text></element>
<element name="tone8b"><text string="PIANO"><color red="0" blue="0" green="0" /></text></element>
<element name="tone9"><text string="PIPE ORGAN 4"><color red="0" blue="0" green="0" /></text></element>
<element name="tone10"><text string="ZIMBALON"><color red="0" blue="0" green="0" /></text></element>
<element name="tone11"><text string="PIPE ORGAN 5"><color red="0" blue="0" green="0" /></text></element>
<element name="tone12"><text string="SYNTH"><color red="0" blue="0" green="0" /></text></element>
<element name="tone12b"><text string="SOUND"><color red="0" blue="0" green="0" /></text></element>
<element name="tone13"><text string="HARPSI-"><color red="0" blue="0" green="0" /></text></element>
<element name="tone13b"><text string="CHORD 1"><color red="0" blue="0" green="0" /></text></element>
<element name="tone14"><text string="ACCORDION"><color red="0" blue="0" green="0" /></text></element>
<element name="tone15"><text string="HARPSI-"><color red="0" blue="0" green="0" /></text></element>
<element name="tone15b"><text string="CHORD 2"><color red="0" blue="0" green="0" /></text></element>
<element name="tone16"><text string="BAGPIPE"><color red="0" blue="0" green="0" /></text></element>
<element name="tone17"><text string="CELESTA 1"><color red="0" blue="0" green="0" /></text></element>
<element name="tone18"><text string="CELESTA 2"><color red="0" blue="0" green="0" /></text></element>
<element name="tone19"><text string="OBOE"><color red="0" blue="0" green="0" /></text></element>
<element name="tone20"><text string="MARIMBA"><color red="0" blue="0" green="0" /></text></element>
<element name="tone21"><text string="CLARINET"><color red="0" blue="0" green="0" /></text></element>
<element name="tone22"><text string="HARP 1"><color red="0" blue="0" green="0" /></text></element>
<element name="tone23"><text string="FLUTE"><color red="0" blue="0" green="0" /></text></element>
<element name="tone24"><text string="HARP 2"><color red="0" blue="0" green="0" /></text></element>
<element name="tone25"><text string="HARP 3"><color red="0" blue="0" green="0" /></text></element>
<element name="tone26"><text string="SHAKUHACHI"><color red="0" blue="0" green="0" /></text></element>
<element name="tone27"><text string="KOTO 1"><color red="0" blue="0" green="0" /></text></element>
<element name="tone28"><text string="WA-WA"><color red="0" blue="0" green="0" /></text></element>
<element name="tone29"><text string="KOTO 2"><color red="0" blue="0" green="0" /></text></element>
<element name="tone30"><text string="TAISHO"><color red="0" blue="0" green="0" /></text></element>
<element name="tone30b"><text string="KOTO"><color red="0" blue="0" green="0" /></text></element>
<element name="tone31"><text string="HORN"><color red="0" blue="0" green="0" /></text></element>
<element name="tone32"><text string="BANJO"><color red="0" blue="0" green="0" /></text></element>
<element name="tone33"><text string="FLUEGELHORN"><color red="0" blue="0" green="0" /></text></element>
<element name="tone34"><text string="MANDOLIN"><color red="0" blue="0" green="0" /></text></element>
<element name="tone35"><text string="TRUMPET"><color red="0" blue="0" green="0" /></text></element>
<element name="tone36"><text string="GUITAR 1"><color red="0" blue="0" green="0" /></text></element>
<element name="tone37"><text string="GUITAR 2"><color red="0" blue="0" green="0" /></text></element>
<element name="tone38"><text string="BRASS ENSEMBLE"><color red="0" blue="0" green="0" /></text></element>
<element name="tone39"><text string="GUITAR 3"><color red="0" blue="0" green="0" /></text></element>
<element name="tone40"><text string="STRING ENSEMBLE"><color red="0" blue="0" green="0" /></text></element>
<element name="tone41"><text string="ELEC."><color red="0" blue="0" green="0" /></text></element>
<element name="tone41b"><text string="GUITAR 1"><color red="0" blue="0" green="0" /></text></element>
<element name="tone42"><text string="ELEC."><color red="0" blue="0" green="0" /></text></element>
<element name="tone42b"><text string="GUITAR 2"><color red="0" blue="0" green="0" /></text></element>
<element name="tone43"><text string="VIOLIN"><color red="0" blue="0" green="0" /></text></element>
<element name="tone44"><text string="ELEC."><color red="0" blue="0" green="0" /></text></element>
<element name="tone44b"><text string="GUITAR 3"><color red="0" blue="0" green="0" /></text></element>
<element name="tone45"><text string="CELLO"><color red="0" blue="0" green="0" /></text></element>
<element name="tone46"><text string="ELEC."><color red="0" blue="0" green="0" /></text></element>
<element name="tone46b"><text string="GUITAR 4"><color red="0" blue="0" green="0" /></text></element>
<element name="tone47"><text string="DOUBLE BASS"><color red="0" blue="0" green="0" /></text></element>
<element name="tone48"><text string="ELEC."><color red="0" blue="0" green="0" /></text></element>
<element name="tone48b"><text string="GUITAR 5"><color red="0" blue="0" green="0" /></text></element>
<element name="tone49"><text string="ELEC. BASS"><color red="0" blue="0" green="0" /></text></element>
<!-- button primitives -->
<element name="button_light" defstate="0">
<rect><color red="0.1" green="0.1" blue="0.1" /><bounds x="0" y="0" width="5" height="10" /></rect>
<rect state="0"><color red="0.8" green="0.8" blue="0.8" /><bounds xc="2.5" yc="5" width="4" height="9" /></rect>
<rect state="1"><color red="0.5" green="0.5" blue="0.5" /><bounds xc="2.5" yc="5" width="4" height="9" /></rect>
</element>
<element name="button_dark" defstate="0">
<rect><color red="0.1" green="0.1" blue="0.1" /><bounds x="0" y="0" width="5" height="10" /></rect>
<rect state="0"><color red="0.3" green="0.3" blue="0.3" /><bounds xc="2.5" yc="5" width="4" height="9" /></rect>
<rect state="1"><color red="0.15" green="0.15" blue="0.15" /><bounds xc="2.5" yc="5" width="4" height="9" /></rect>
</element>
<element name="button_down" defstate="0">
<rect><color red="0.1" green="0.1" blue="0.1" /><bounds x="0" y="0" width="5" height="10" /></rect>
<rect state="0"><color red="0.3" green="0.3" blue="0.3" /><bounds xc="2.5" yc="5" width="4" height="9" /></rect>
<rect state="1"><color red="0.15" green="0.15" blue="0.15" /><bounds xc="2.5" yc="5" width="4" height="9" /></rect>
<image>
<bounds xc="2.5" yc="5" width="3" height="6" />
<data><![CDATA[
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10" version="1.1">
<polygon points="2,2 5,8 8,2" fill="white" stroke="white" stroke-width="0.4" />
</svg>
]]></data>
</image>
</element>
<element name="button_up" defstate="0">
<rect><color red="0.1" green="0.1" blue="0.1" /><bounds x="0" y="0" width="5" height="10" /></rect>
<rect state="0"><color red="0.3" green="0.3" blue="0.3" /><bounds xc="2.5" yc="5" width="4" height="9" /></rect>
<rect state="1"><color red="0.15" green="0.15" blue="0.15" /><bounds xc="2.5" yc="5" width="4" height="9" /></rect>
<image>
<bounds xc="2.5" yc="5" width="3" height="6" />
<data><![CDATA[
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10" version="1.1">
<polygon points="2,8 5,2 8,8" fill="white" stroke="white" stroke-width="0.4" />
</svg>
]]></data>
</image>
</element>
<element name="button_set" defstate="0">
<rect><color red="0.1" green="0.1" blue="0.1" /><bounds x="0" y="0" width="10" height="10" /></rect>
<rect state="0"><color red="1.0" green="0.45" blue="0.0" /><bounds xc="5" yc="5" width="9" height="9" /></rect>
<rect state="1"><color red="0.8" green="0.25" blue="0" /><bounds xc="5" yc="5" width="9" height="9" /></rect>
</element>
<element name="button_icon_up">
<rect><color red="0.7" green="0.7" blue="0.725" /><bounds x="0" y="0" width="5" height="5" /></rect>
<rect><color red="0" green="0" blue="0" /><bounds xc="2.5" y="4" width="5" height="1" /></rect>
<rect><color red="0" green="0" blue="0" /><bounds xc="2.5" y="1" width="3.5" height="4" /></rect>
</element>
<element name="button_icon_down">
<rect><color red="0.7" green="0.7" blue="0.725" /><bounds x="0" y="0" width="5" height="5" /></rect>
<rect><color red="0" green="0" blue="0" /><bounds xc="2.5" y="4" width="5" height="1" /></rect>
<rect><color red="0" green="0" blue="0" /><bounds xc="2.5" y="3" width="3.5" height="2" /></rect>
</element>
<!-- keyboard primitives -->
<element name="keyfill"><rect><color red="0.2" green="0.2" blue="0.2" /></rect></element>
<element name="whitekey" defstate="0">
<rect state="0"><bounds x="0" y="0" width="45" height="504" /><color red="1.0" green="1.0" blue="1.0" /></rect>
<rect state="1"><bounds x="0" y="0" width="45" height="504" /><color red="0.9" green="0.9" blue="0.9" /></rect>
</element>
<element name="whitekey-l" defstate="0">
<rect state="0"><bounds x="0" y="0" width="45" height="332" /><color red="1.0" green="1.0" blue="1.0" /></rect>
<rect state="0"><bounds x="0" y="332" width="79" height="172" /><color red="1.0" green="1.0" blue="1.0" /></rect>
<rect state="1"><bounds x="0" y="0" width="45" height="332" /><color red="0.9" green="0.9" blue="0.9" /></rect>
<rect state="1"><bounds x="0" y="332" width="79" height="172" /><color red="0.9" green="0.9" blue="0.9" /></rect>
</element>
<element name="whitekey-m" defstate="0">
<rect state="0"><bounds x="13" y="0" width="53" height="332" /><color red="1.0" green="1.0" blue="1.0" /></rect>
<rect state="0"><bounds x="0" y="332" width="79" height="172" /><color red="1.0" green="1.0" blue="1.0" /></rect>
<rect state="1"><bounds x="13" y="0" width="53" height="332" /><color red="0.9" green="0.9" blue="0.9" /></rect>
<rect state="1"><bounds x="0" y="332" width="79" height="172" /><color red="0.9" green="0.9" blue="0.9" /></rect>
</element>
<element name="whitekey-lm" defstate="0">
<rect state="0"><bounds x="10" y="0" width="44" height="332" /><color red="1.0" green="1.0" blue="1.0" /></rect>
<rect state="0"><bounds x="0" y="332" width="79" height="172" /><color red="1.0" green="1.0" blue="1.0" /></rect>
<rect state="1"><bounds x="10" y="0" width="44" height="332" /><color red="0.9" green="0.9" blue="0.9" /></rect>
<rect state="1"><bounds x="0" y="332" width="79" height="172" /><color red="0.9" green="0.9" blue="0.9" /></rect>
</element>
<element name="whitekey-rm" defstate="0">
<rect state="0"><bounds x="22" y="0" width="44" height="332" /><color red="1.0" green="1.0" blue="1.0" /></rect>
<rect state="0"><bounds x="0" y="332" width="79" height="172" /><color red="1.0" green="1.0" blue="1.0" /></rect>
<rect state="1"><bounds x="22" y="0" width="44" height="332" /><color red="0.9" green="0.9" blue="0.9" /></rect>
<rect state="1"><bounds x="0" y="332" width="79" height="172" /><color red="0.9" green="0.9" blue="0.9" /></rect>
</element>
<element name="whitekey-r" defstate="0">
<rect state="0"><bounds x="34" y="0" width="45" height="332" /><color red="1.0" green="1.0" blue="1.0" /></rect>
<rect state="0"><bounds x="0" y="332" width="79" height="172" /><color red="1.0" green="1.0" blue="1.0" /></rect>
<rect state="1"><bounds x="34" y="0" width="45" height="332" /><color red="0.9" green="0.9" blue="0.9" /></rect>
<rect state="1"><bounds x="0" y="332" width="79" height="172" /><color red="0.9" green="0.9" blue="0.9" /></rect>
</element>
<element name="blackkey" defstate="0">
<rect state="0"><bounds x="0" y="0" width="44" height="324" /><color red="0.0" green="0.0" blue="0.0" /></rect>
<rect state="0"><bounds x="4" y="0" width="36" height="320" /><color red="0.1" green="0.1" blue="0.1" /></rect>
<rect state="1"><bounds x="0" y="0" width="44" height="324" /><color red="0.1" green="0.1" blue="0.1" /></rect>
<rect state="1"><bounds x="4" y="0" width="36" height="320" /><color red="0.1" green="0.1" blue="0.1" /></rect>
</element>
<!-- left button group -->
<group name="left">
<bounds x="0" y="0" width="80" height="120" />
<element ref="panel_back"><bounds x="0" y="0" width="80" height="120" /></element>
<element ref="line_black"><bounds xc="26" y="25" width="28" height="4" /></element>
<element ref="panel_back"><bounds xc="19.25" y="26" width="12.5" height="4" /></element>
<element ref="panel_back"><bounds xc="32.75" y="26" width="12.5" height="4" /></element>
<element ref="button_back"><bounds x="0" y="35" width="80" height="20" /></element>
<element ref="cancel"><bounds xc="12" y="30" width="15" height="4" /></element>
<element ref="button_dark" inputtag="SWITCH" inputmask="0x01"><bounds xc="12" yc="45" width="9" height="18" /></element>
<element ref="sustain"><bounds xc="26" y="30" width="15" height="4" /></element>
<element ref="button_back" inputtag="SWITCH" inputmask="0x02"><bounds xc="26" yc="45" width="9" height="18" /></element>
<element ref="button_dark" inputtag="KCA" inputmask="0x01"><bounds xc="26" yc="45" width="9" height="18" /></element>
<element ref="reverb"><bounds xc="40" y="30" width="15" height="4" /></element>
<element ref="button_back" inputtag="SWITCH" inputmask="0x04"><bounds xc="40" yc="45" width="9" height="18" /></element>
<element ref="button_dark" inputtag="KCA" inputmask="0x04"><bounds xc="40" yc="45" width="9" height="18" /></element>
<element ref="vibrato"><bounds xc="54" y="30" width="15" height="4" /></element>
<element ref="button_dark" inputtag="KCC" inputmask="0x10"><bounds xc="54" yc="45" width="9" height="18" /></element>
<element ref="stereo"><bounds xc="68" y="26" width="15" height="4" /></element>
<element ref="chorus"><bounds xc="68" y="30" width="15" height="4" /></element>
<element ref="button_dark" inputtag="KC9" inputmask="0x08"><bounds xc="68" yc="45" width="9" height="18" /></element>
<element ref="effect"><bounds xc="40" y="55" width="80" height="4" /></element>
<element ref="tone_memory"><bounds xc="40" y="62" width="80" height="4" /></element>
<element ref="button_back"><bounds x="0" y="70" width="80" height="20" /></element>
<element ref="num1"><bounds xc="19" y="66" width="15" height="4" /></element>
<element ref="button_light" inputtag="KCB" inputmask="0x01"><bounds xc="19" yc="80" width="9" height="16" /></element>
<element ref="num2"><bounds xc="33" y="66" width="15" height="4" /></element>
<element ref="button_light" inputtag="KCB" inputmask="0x02"><bounds xc="33" yc="80" width="9" height="16" /></element>
<element ref="num3"><bounds xc="47" y="66" width="15" height="4" /></element>
<element ref="button_light" inputtag="KCB" inputmask="0x04"><bounds xc="47" yc="80" width="9" height="16" /></element>
<element ref="num4"><bounds xc="61" y="66" width="15" height="4" /></element>
<element ref="button_light" inputtag="KCB" inputmask="0x08"><bounds xc="61" yc="80" width="9" height="16" /></element>
<element ref="mode"><bounds xc="61" y="92" width="15" height="4" /></element>
<element ref="button_set" inputtag="KCB" inputmask="0x10"><bounds xc="61" yc="106" width="16" height="16" /></element>
<!-- these are supposed to be to the right of the button but here they are on the left for better readability -->
<element ref="button_icon_down"><bounds xc="38" yc="102" width="3" height="3" /></element>
<element ref="set"><bounds xc="46" yc="102" width="15" height="4" /></element>
<element ref="button_icon_up"><bounds xc="38" yc="110" width="3" height="3" /></element>
<element ref="play"><bounds xc="46" yc="110" width="15" height="4" /></element>
</group>
<!-- right button group -->
<group name="right">
<element ref="panel_back"><bounds x="0" y="0" width="40" height="120" /></element>
<element ref="tune"><bounds xc="20" y="15" width="40" height="4" /></element>
<element ref="button_back"><bounds x="0" y="20" width="40" height="20" /></element>
<element ref="button_down" inputtag="KC9" inputmask="0x04"><bounds xc="13" yc="30" width="9" height="18" /></element>
<element ref="button_up" inputtag="KC9" inputmask="0x02"><bounds xc="27" yc="30" width="9" height="18" /></element>
</group>
<!-- keyboard group -->
<group name="keyboard">
<element ref="keyfill"><bounds x="72" y="0" width="2396" height="524" /></element>
<!-- octave 1 -->
<element ref="blackkey" inputtag="KC0" inputmask="0x02"><bounds x="130" y="10" width="44" height="324" /></element>
<element ref="blackkey" inputtag="KC0" inputmask="0x08"><bounds x="233" y="10" width="44" height="324" /></element>
<element ref="blackkey" inputtag="KC1" inputmask="0x01"><bounds x="373" y="10" width="44" height="324" /></element>
<element ref="blackkey" inputtag="KC1" inputmask="0x04"><bounds x="467" y="10" width="44" height="324" /></element>
<element ref="blackkey" inputtag="KC1" inputmask="0x10"><bounds x="561" y="10" width="44" height="324" /></element>
<element ref="whitekey-l" inputtag="KC0" inputmask="0x01"><bounds x="82" y="10" width="79" height="504" /></element>
<element ref="whitekey-m" inputtag="KC0" inputmask="0x04"><bounds x="164" y="10" width="79" height="504" /></element>
<element ref="whitekey-r" inputtag="KC0" inputmask="0x10"><bounds x="246" y="10" width="79" height="504" /></element>
<element ref="whitekey-l" inputtag="KC0" inputmask="0x20"><bounds x="328" y="10" width="79" height="504" /></element>
<element ref="whitekey-lm" inputtag="KC1" inputmask="0x02"><bounds x="410" y="10" width="79" height="504" /></element>
<element ref="whitekey-rm" inputtag="KC1" inputmask="0x08"><bounds x="492" y="10" width="79" height="504" /></element>
<element ref="whitekey-r" inputtag="KC1" inputmask="0x20"><bounds x="574" y="10" width="79" height="504" /></element>
<!-- octave 2 -->
<element ref="blackkey" inputtag="KC2" inputmask="0x02"><bounds x="704" y="10" width="44" height="324" /></element>
<element ref="blackkey" inputtag="KC2" inputmask="0x08"><bounds x="807" y="10" width="44" height="324" /></element>
<element ref="blackkey" inputtag="KC3" inputmask="0x01"><bounds x="947" y="10" width="44" height="324" /></element>
<element ref="blackkey" inputtag="KC3" inputmask="0x04"><bounds x="1041" y="10" width="44" height="324" /></element>
<element ref="blackkey" inputtag="KC3" inputmask="0x10"><bounds x="1135" y="10" width="44" height="324" /></element>
<element ref="whitekey-l" inputtag="KC2" inputmask="0x01"><bounds x="656" y="10" width="79" height="504" /></element>
<element ref="whitekey-m" inputtag="KC2" inputmask="0x04"><bounds x="738" y="10" width="79" height="504" /></element>
<element ref="whitekey-r" inputtag="KC2" inputmask="0x10"><bounds x="820" y="10" width="79" height="504" /></element>
<element ref="whitekey-l" inputtag="KC2" inputmask="0x20"><bounds x="902" y="10" width="79" height="504" /></element>
<element ref="whitekey-lm" inputtag="KC3" inputmask="0x02"><bounds x="984" y="10" width="79" height="504" /></element>
<element ref="whitekey-rm" inputtag="KC3" inputmask="0x08"><bounds x="1066" y="10" width="79" height="504" /></element>
<element ref="whitekey-r" inputtag="KC3" inputmask="0x20"><bounds x="1148" y="10" width="79" height="504" /></element>
<!-- octave 3 -->
<element ref="blackkey" inputtag="KC4" inputmask="0x02"><bounds x="1278" y="10" width="44" height="324" /></element>
<element ref="blackkey" inputtag="KC4" inputmask="0x08"><bounds x="1381" y="10" width="44" height="324" /></element>
<element ref="blackkey" inputtag="KC5" inputmask="0x01"><bounds x="1521" y="10" width="44" height="324" /></element>
<element ref="blackkey" inputtag="KC5" inputmask="0x04"><bounds x="1615" y="10" width="44" height="324" /></element>
<element ref="blackkey" inputtag="KC5" inputmask="0x10"><bounds x="1709" y="10" width="44" height="324" /></element>
<element ref="whitekey-l" inputtag="KC4" inputmask="0x01"><bounds x="1230" y="10" width="79" height="504" /></element>
<element ref="whitekey-m" inputtag="KC4" inputmask="0x04"><bounds x="1312" y="10" width="79" height="504" /></element>
<element ref="whitekey-r" inputtag="KC4" inputmask="0x10"><bounds x="1394" y="10" width="79" height="504" /></element>
<element ref="whitekey-l" inputtag="KC4" inputmask="0x20"><bounds x="1476" y="10" width="79" height="504" /></element>
<element ref="whitekey-lm" inputtag="KC5" inputmask="0x02"><bounds x="1558" y="10" width="79" height="504" /></element>
<element ref="whitekey-rm" inputtag="KC5" inputmask="0x08"><bounds x="1640" y="10" width="79" height="504" /></element>
<element ref="whitekey-r" inputtag="KC5" inputmask="0x20"><bounds x="1722" y="10" width="79" height="504" /></element>
<!-- octave 4 -->
<element ref="blackkey" inputtag="KC6" inputmask="0x02"><bounds x="1852" y="10" width="44" height="324" /></element>
<element ref="blackkey" inputtag="KC6" inputmask="0x08"><bounds x="1955" y="10" width="44" height="324" /></element>
<element ref="blackkey" inputtag="KC7" inputmask="0x01"><bounds x="2095" y="10" width="44" height="324" /></element>
<element ref="blackkey" inputtag="KC7" inputmask="0x04"><bounds x="2189" y="10" width="44" height="324" /></element>
<element ref="blackkey" inputtag="KC7" inputmask="0x10"><bounds x="2283" y="10" width="44" height="324" /></element>
<element ref="whitekey-l" inputtag="KC6" inputmask="0x01"><bounds x="1804" y="10" width="79" height="504" /></element>
<element ref="whitekey-m" inputtag="KC6" inputmask="0x04"><bounds x="1886" y="10" width="79" height="504" /></element>
<element ref="whitekey-r" inputtag="KC6" inputmask="0x10"><bounds x="1968" y="10" width="79" height="504" /></element>
<element ref="whitekey-l" inputtag="KC6" inputmask="0x20"><bounds x="2050" y="10" width="79" height="504" /></element>
<element ref="whitekey-lm" inputtag="KC7" inputmask="0x02"><bounds x="2132" y="10" width="79" height="504" /></element>
<element ref="whitekey-rm" inputtag="KC7" inputmask="0x08"><bounds x="2214" y="10" width="79" height="504" /></element>
<element ref="whitekey-r" inputtag="KC7" inputmask="0x20"><bounds x="2296" y="10" width="79" height="504" /></element>
<!-- final key -->
<element ref="whitekey" inputtag="KC8" inputmask="0x01"><bounds x="2378" y="10" width="79" height="504" /></element>
</group>
<group name="key_labels">
<bounds x="0" y="0" width="580" height="20" />
<element ref="tone2"><bounds xc="18" y="2" width="30" height="4" /></element>
<element ref="line_white"><bounds xc="18" y="7" width="0.75" height="13" /></element>
<element ref="tone4"><bounds xc="43" y="2" width="30" height="4" /></element>
<element ref="line_white"><bounds xc="43" y="7" width="0.75" height="13" /></element>
<element ref="tone7"><bounds xc="77" y="2" width="30" height="4" /></element>
<element ref="line_white"><bounds xc="77" y="7" width="0.75" height="13" /></element>
<element ref="tone9"><bounds xc="100" y="2" width="30" height="4" /></element>
<element ref="line_white"><bounds xc="100" y="7" width="0.75" height="13" /></element>
<element ref="tone11"><bounds xc="123" y="2" width="30" height="4" /></element>
<element ref="line_white"><bounds xc="123" y="7" width="0.75" height="13" /></element>
<element ref="tone1"><bounds x="-1" y="15" width="20" height="4" /></element>
<element ref="tone3"><bounds x="20" y="15" width="20" height="4" /></element>
<element ref="tone5"><bounds x="41" y="15" width="20" height="4" /></element>
<element ref="tone6"><bounds x="59" y="15" width="20" height="4" /></element>
<element ref="tone8"><bounds x="79" y="11" width="20" height="4" /></element>
<element ref="tone8b"><bounds x="79" y="15" width="20" height="4" /></element>
<element ref="tone10"><bounds x="101" y="15" width="20" height="4" /></element>
<element ref="tone12"><bounds x="121" y="11" width="20" height="4" /></element>
<element ref="tone12b"><bounds x="121" y="15" width="20" height="4" /></element>
<element ref="tone14"><bounds xc="158" y="2" width="30" height="4" /></element>
<element ref="line_white"><bounds xc="158" y="7" width="0.75" height="13" /></element>
<element ref="tone16"><bounds xc="183" y="2" width="30" height="4" /></element>
<element ref="line_white"><bounds xc="183" y="7" width="0.75" height="13" /></element>
<element ref="tone19"><bounds xc="217" y="2" width="30" height="4" /></element>
<element ref="line_white"><bounds xc="217" y="7" width="0.75" height="13" /></element>
<element ref="tone21"><bounds xc="240" y="2" width="30" height="4" /></element>
<element ref="line_white"><bounds xc="240" y="7" width="0.75" height="13" /></element>
<element ref="tone23"><bounds xc="263" y="2" width="30" height="4" /></element>
<element ref="line_white"><bounds xc="263" y="7" width="0.75" height="13" /></element>
<element ref="tone13"><bounds x="140" y="11" width="20" height="4" /></element>
<element ref="tone13b"><bounds x="140" y="15" width="20" height="4" /></element>
<element ref="tone15"><bounds x="160" y="11" width="20" height="4" /></element>
<element ref="tone15b"><bounds x="160" y="15" width="20" height="4" /></element>
<element ref="tone17"><bounds x="182" y="15" width="20" height="4" /></element>
<element ref="tone18"><bounds x="199" y="15" width="20" height="4" /></element>
<element ref="tone20"><bounds x="220" y="15" width="20" height="4" /></element>
<element ref="tone22"><bounds x="240" y="15" width="20" height="4" /></element>
<element ref="tone24"><bounds x="261" y="15" width="20" height="4" /></element>
<element ref="tone26"><bounds xc="298" y="2" width="30" height="4" /></element>
<element ref="line_white"><bounds xc="298" y="7" width="0.75" height="13" /></element>
<element ref="tone28"><bounds xc="323" y="2" width="30" height="4" /></element>
<element ref="line_white"><bounds xc="323" y="7" width="0.75" height="13" /></element>
<element ref="tone31"><bounds xc="357" y="2" width="30" height="4" /></element>
<element ref="line_white"><bounds xc="357" y="7" width="0.75" height="13" /></element>
<element ref="tone33"><bounds xc="380" y="2" width="30" height="4" /></element>
<element ref="line_white"><bounds xc="380" y="7" width="0.75" height="13" /></element>
<element ref="tone35"><bounds xc="403" y="2" width="30" height="4" /></element>
<element ref="line_white"><bounds xc="403" y="7" width="0.75" height="13" /></element>
<element ref="tone25"><bounds x="280" y="15" width="20" height="4" /></element>
<element ref="tone27"><bounds x="300" y="15" width="20" height="4" /></element>
<element ref="tone29"><bounds x="321" y="15" width="20" height="4" /></element>
<element ref="tone30"><bounds x="339" y="11" width="20" height="4" /></element>
<element ref="tone30b"><bounds x="339" y="15" width="20" height="4" /></element>
<element ref="tone32"><bounds x="360" y="15" width="20" height="4" /></element>
<element ref="tone34"><bounds x="380" y="15" width="20" height="4" /></element>
<element ref="tone36"><bounds x="401" y="15" width="20" height="4" /></element>
<element ref="tone38"><bounds xc="432" y="2" width="30" height="4" /></element>
<element ref="line_white"><bounds xc="438" y="7" width="0.75" height="13" /></element>
<element ref="tone40"><bounds xc="466" y="2" width="30" height="4" /></element>
<element ref="line_white"><bounds xc="463" y="7" width="0.75" height="13" /></element>
<element ref="tone43"><bounds xc="497" y="2" width="30" height="4" /></element>
<element ref="line_white"><bounds xc="497" y="7" width="0.75" height="13" /></element>
<element ref="tone45"><bounds xc="520" y="2" width="30" height="4" /></element>
<element ref="line_white"><bounds xc="520" y="7" width="0.75" height="13" /></element>
<element ref="tone47"><bounds xc="543" y="2" width="30" height="4" /></element>
<element ref="line_white"><bounds xc="543" y="7" width="0.75" height="13" /></element>
<element ref="tone37"><bounds x="419" y="15" width="20" height="4" /></element>
<element ref="tone39"><bounds x="440" y="15" width="20" height="4" /></element>
<element ref="tone41"><bounds x="461" y="11" width="20" height="4" /></element>
<element ref="tone41b"><bounds x="461" y="15" width="20" height="4" /></element>
<element ref="tone42"><bounds x="479" y="11" width="20" height="4" /></element>
<element ref="tone42b"><bounds x="479" y="15" width="20" height="4" /></element>
<element ref="tone44"><bounds x="500" y="11" width="20" height="4" /></element>
<element ref="tone44b"><bounds x="500" y="15" width="20" height="4" /></element>
<element ref="tone46"><bounds x="520" y="11" width="20" height="4" /></element>
<element ref="tone46b"><bounds x="520" y="15" width="20" height="4" /></element>
<element ref="tone48"><bounds x="541" y="11" width="20" height="4" /></element>
<element ref="tone48b"><bounds x="541" y="15" width="20" height="4" /></element>
<element ref="tone49"><bounds x="560" y="15" width="20" height="4" /></element>
</group>
<view name="Keyboard">
<element ref="bg"><bounds x="0" y="0" width="704" height="170" /></element>
<element ref="line"><bounds x="80" y="0" width="1" height="50" /></element>
<element ref="line"><bounds x="663" y="0" width="1" height="50" /></element>
<group ref="left"><bounds x="0" y="50" width="80" height="120" /></group>
<group ref="right"><bounds x="664" y="50" width="40" height="120" /></group>
<element ref="line"><bounds x="0" y="50" width="704" height="1" /></element>
<group ref="key_labels"><bounds x="82" y="30" width="580" height="20" /></group>
<group ref="keyboard"><bounds x="80" y="50" width="584" height="120" /></group>
</view>
</mamelayout>

241
src/mame/layout/ctfk1.lay Normal file
View File

@ -0,0 +1,241 @@
<?xml version="1.0"?>
<!--
license:CC0-1.0
-->
<mamelayout version="2">
<element name="bg"><rect><color red="0.75" green="0.75" blue="0.775" /></rect></element>
<element name="brown"><rect><color red="0.365" green="0.263" blue="0.17" /></rect></element>
<element name="line"><rect><color red="0.55" green="0.55" blue="0.575" /></rect></element>
<element name="line_white"><rect><color red="1" green="1" blue="0.95" /></rect></element>
<element name="button_back">
<rect><bounds x="0" y="0" width="1" height="20" /><color red="0.25" green="0.25" blue="0.275" /></rect>
<rect><bounds x="0" y="1" width="1" height="18" /><color red="0.3" green="0.3" blue="0.325" /></rect>
</element>
<element name="panel_back"><rect><color red="0.7" green="0.7" blue="0.725" /></rect></element>
<element name="top_back">
<disk><bounds x="0" y="0" width="10" height="10" /><color red="0.1" green="0.1" blue="0.1" /></disk>
<disk><bounds x="270" y="0" width="10" height="10" /><color red="0.1" green="0.1" blue="0.1" /></disk>
<disk><bounds x="0" y="35" width="10" height="10" /><color red="0.1" green="0.1" blue="0.1" /></disk>
<disk><bounds x="270" y="35" width="10" height="10" /><color red="0.1" green="0.1" blue="0.1" /></disk>
<rect><bounds x="5" y="0" width="270" height="45" /><color red="0.1" green="0.1" blue="0.1" /></rect>
<rect><bounds x="0" y="5" width="280" height="35" /><color red="0.1" green="0.1" blue="0.1" /></rect>
</element>
<!-- text elements -->
<element name="select"><text string="BASS SELECT"><color red="0" blue="0" green="0" /></text></element>
<element name="foot"><text string="FOOT" align="1"><color red="0" blue="0" green="0" /></text></element>
<element name="auto"><text string="AUTO" align="1"><color red="0" blue="0" green="0" /></text></element>
<element name="mode"><text string="MODE"><color red="0" blue="0" green="0" /></text></element>
<element name="set"><text string="SET" align="1"><color red="0" blue="0" green="0" /></text></element>
<element name="play"><text string="PLAY" align="1"><color red="0" blue="0" green="0" /></text></element>
<element name="sustain"><text string="SUSTAIN"><color red="0" blue="0" green="0" /></text></element>
<element name="on"><text string="ON" align="1"><color red="0" blue="0" green="0" /></text></element>
<element name="off"><text string="OFF" align="1"><color red="0" blue="0" green="0" /></text></element>
<element name="tune"><text string="TUNE" /></element>
<element name="tone1"><text string="ORGAN 1"><color red="0" blue="0" green="0" /></text></element>
<element name="tone2"><text string="ORGAN 4"><color red="0" blue="0" green="0" /></text></element>
<element name="tone3"><text string="ORGAN 2"><color red="0" blue="0" green="0" /></text></element>
<element name="tone4"><text string="TUBA 2"><color red="0" blue="0" green="0" /></text></element>
<element name="tone5"><text string="ORGAN 3"><color red="0" blue="0" green="0" /></text></element>
<element name="tone6"><text string="TUBA 1"><color red="0" blue="0" green="0" /></text></element>
<element name="tone7"><text string="BASSOON 2"><color red="0" blue="0" green="0" /></text></element>
<element name="tone8"><text string="BASSOON 1"><color red="0" blue="0" green="0" /></text></element>
<element name="tone9"><text string="WOOD BASS"><color red="0" blue="0" green="0" /></text></element>
<element name="tone9b"><text string="(ARCO 2)"><color red="0" blue="0" green="0" /></text></element>
<element name="tone10"><text string="WOOD BASS"><color red="0" blue="0" green="0" /></text></element>
<element name="tone10b"><text string="(ARCO 1)"><color red="0" blue="0" green="0" /></text></element>
<element name="tone11"><text string="WOOD BASS"><color red="0" blue="0" green="0" /></text></element>
<element name="tone11b"><text string="(PIZZ 2)"><color red="0" blue="0" green="0" /></text></element>
<element name="tone12"><text string="WOOD BASS"><color red="0" blue="0" green="0" /></text></element>
<element name="tone12b"><text string="(PIZZ 1)"><color red="0" blue="0" green="0" /></text></element>
<element name="tone13"><text string="ELEC. BASS 1"><color red="0" blue="0" green="0" /></text></element>
<element name="tone14"><text string="ELEC. BASS 2"><color red="0" blue="0" green="0" /></text></element>
<element name="tone15"><text string="HARPSICHORD 1"><color red="0" blue="0" green="0" /></text></element>
<element name="tone16"><text string="HARPSICHORD 2"><color red="0" blue="0" green="0" /></text></element>
<element name="tone17"><text string="PIANO"><color red="0" blue="0" green="0" /></text></element>
<element name="tone18"><text string="SYNTH CHIME"><color red="0" blue="0" green="0" /></text></element>
<!-- button primitives -->
<element name="button_down" defstate="0">
<rect><color red="0.1" green="0.1" blue="0.1" /><bounds x="0" y="0" width="5" height="10" /></rect>
<rect state="0"><color red="0.8" green="0.8" blue="0.8" /><bounds xc="2.5" yc="5" width="4" height="9" /></rect>
<rect state="1"><color red="0.5" green="0.5" blue="0.5" /><bounds xc="2.5" yc="5" width="4" height="9" /></rect>
<image>
<bounds xc="2.5" yc="5" width="2" height="5" />
<data><![CDATA[
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10" version="1.1">
<polygon points="2,2 5,8 8,2" fill="white" stroke="white" stroke-width="0.4" />
</svg>
]]></data>
</image>
</element>
<element name="button_up" defstate="0">
<rect><color red="0.1" green="0.1" blue="0.1" /><bounds x="0" y="0" width="5" height="10" /></rect>
<rect state="0"><color red="0.8" green="0.8" blue="0.8" /><bounds xc="2.5" yc="5" width="4" height="9" /></rect>
<rect state="1"><color red="0.5" green="0.5" blue="0.5" /><bounds xc="2.5" yc="5" width="4" height="9" /></rect>
<image>
<bounds xc="2.5" yc="5" width="2" height="5" />
<data><![CDATA[
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10" version="1.1">
<polygon points="2,8 5,2 8,8" fill="white" stroke="white" stroke-width="0.4" />
</svg>
]]></data>
</image>
</element>
<element name="button" defstate="0">
<disk><color red="0.1" green="0.1" blue="0.1" /><bounds x="0" y="0" width="20" height="20" /></disk>
<rect><color red="0.1" green="0.1" blue="0.1" /><bounds x="0" y="10" width="20" height="42" /></rect>
<disk state="0"><color red="0.3" green="0.3" blue="0.3" /><bounds xc="10" y="1" width="18" height="18" /></disk>
<rect state="0"><color red="0.3" green="0.3" blue="0.3" /><bounds xc="10" y="10" width="18" height="40" /></rect>
<disk state="1"><color red="0.15" green="0.15" blue="0.15" /><bounds xc="10" y="1" width="18" height="18" /></disk>
<rect state="1"><color red="0.15" green="0.15" blue="0.15" /><bounds xc="10" y="10" width="18" height="40" /></rect>
</element>
<element name="button_icon_up">
<rect><color red="0.75" green="0.75" blue="0.775" /><bounds x="0" y="0" width="5" height="5" /></rect>
<rect><color red="0" green="0" blue="0" /><bounds xc="2.5" y="4" width="5" height="1" /></rect>
<rect><color red="0" green="0" blue="0" /><bounds xc="2.5" y="1" width="3.5" height="4" /></rect>
</element>
<element name="button_icon_down">
<rect><color red="0.75" green="0.75" blue="0.775" /><bounds x="0" y="0" width="5" height="5" /></rect>
<rect><color red="0" green="0" blue="0" /><bounds xc="2.5" y="4" width="5" height="1" /></rect>
<rect><color red="0" green="0" blue="0" /><bounds xc="2.5" y="3" width="3.5" height="2" /></rect>
</element>
<!-- keyboard primitives -->
<element name="keyfill"><rect><color red="0.08" green="0.08" blue="0.08" /></rect></element>
<element name="whitekey" defstate="0">
<rect><bounds xc="0" y="0" width="5" height="47.5" /><color red="0" green="0" blue="0" /></rect>
<disk><bounds xc="0" yc="47.5" width="5" height="5" /><color red="0" green="0" blue="0" /></disk>
<rect state="0"><bounds xc="0" y="0" width="4.25" height="47.5" /><color red="1" green="1" blue="1" /></rect>
<disk state="0"><bounds xc="0" yc="47.5" width="4.25" height="4.25" /><color red="1" green="1" blue="1" /></disk>
<rect state="1"><bounds xc="0" y="0" width="4.25" height="47.5" /><color red="0.8" green="0.8" blue="0.8" /></rect>
<disk state="1"><bounds xc="0" yc="47.5" width="4.25" height="4.25" /><color red="0.8" green="0.8" blue="0.8" /></disk>
</element>
<element name="blackkey" defstate="0">
<rect><bounds xc="0" y="0" width="5" height="25" /><color red="0" green="0" blue="0" /></rect>
<disk><bounds xc="0" yc="25" width="5" height="5" /><color red="0" green="0" blue="0" /></disk>
<rect state="0"><bounds xc="0" y="0" width="4.25" height="25" /><color red="0.25" green="0.25" blue="0.25" /></rect>
<disk state="0"><bounds xc="0" yc="25" width="4.25" height="4.25" /><color red="0.25" green="0.25" blue="0.25" /></disk>
<rect state="1"><bounds xc="0" y="0" width="4.25" height="25" /><color red="0.2" green="0.2" blue="0.2" /></rect>
<disk state="1"><bounds xc="0" yc="25" width="4.25" height="4.25" /><color red="0.2" green="0.2" blue="0.2" /></disk>
</element>
<!-- button group -->
<group name="buttons">
<element ref="bg"><bounds x="0" y="0" width="660" height="180" /></element>
<element ref="top_back"><bounds x="25" y="25" width="280" height="45" /></element>
<element ref="tune"><bounds xc="270" y="30" width="40" height="9" /></element>
<element ref="button_down" inputtag="KC9" inputmask="0x04"><bounds xc="260" y="42" width="10" height="20" /></element>
<element ref="button_up" inputtag="KC9" inputmask="0x02"><bounds xc="280" y="42" width="10" height="20" /></element>
<element ref="select"><bounds xc="90" y="75" width="60" height="11" /></element>
<element ref="button_icon_down"><bounds x="73" y="87" width="9" height="9" /></element>
<element ref="foot"><bounds x="85" y="87" width="60" height="11" /></element>
<element ref="button_icon_up"><bounds x="73" y="99" width="9" height="9" /></element>
<element ref="auto"><bounds x="85" y="99" width="60" height="11" /></element>
<element ref="button" inputtag="KC9" inputmask="0x10"><bounds xc="90" y="115" width="20" height="52" /></element>
<element ref="mode"><bounds xc="180" y="75" width="60" height="11" /></element>
<element ref="button_icon_down"><bounds x="163" y="87" width="9" height="9" /></element>
<element ref="set"><bounds x="175" y="87" width="60" height="11" /></element>
<element ref="button_icon_up"><bounds x="163" y="99" width="9" height="9" /></element>
<element ref="play"><bounds x="175" y="99" width="60" height="11" /></element>
<element ref="button" inputtag="KCB" inputmask="0x10"><bounds xc="180" y="115" width="20" height="52" /></element>
<element ref="sustain"><bounds xc="270" y="75" width="60" height="11" /></element>
<element ref="button_icon_down"><bounds x="253" y="87" width="9" height="9" /></element>
<element ref="on"><bounds x="265" y="87" width="60" height="11" /></element>
<element ref="button_icon_up"><bounds x="253" y="99" width="9" height="9" /></element>
<element ref="off"><bounds x="265" y="99" width="60" height="11" /></element>
<element ref="button" inputtag="KCA" inputmask="0x01"><bounds xc="270" y="115" width="20" height="52" /></element>
</group>
<!-- keyboard group -->
<group name="keyboard">
<element ref="keyfill"><bounds x="0" y="0" width="660" height="300" /></element>
<element ref="button_back"><bounds x="0" y="100" width="660" height="30" /></element>
<element ref="button_back"><bounds x="0" y="200" width="660" height="30" /></element>
<element ref="whitekey" inputtag="KC0" inputmask="0x01"><bounds xc="30" y="0" width="25" height="245" /></element>
<element ref="blackkey" inputtag="KC0" inputmask="0x02"><bounds xc="60" y="0" width="25" height="125" /></element>
<element ref="whitekey" inputtag="KC0" inputmask="0x04"><bounds xc="90" y="0" width="25" height="245" /></element>
<element ref="blackkey" inputtag="KC0" inputmask="0x08"><bounds xc="120" y="0" width="25" height="125" /></element>
<element ref="whitekey" inputtag="KC0" inputmask="0x10"><bounds xc="150" y="0" width="25" height="245" /></element>
<element ref="whitekey" inputtag="KC0" inputmask="0x20"><bounds xc="210" y="0" width="25" height="245" /></element>
<element ref="blackkey" inputtag="KC1" inputmask="0x01"><bounds xc="240" y="0" width="25" height="125" /></element>
<element ref="whitekey" inputtag="KC1" inputmask="0x02"><bounds xc="270" y="0" width="25" height="245" /></element>
<element ref="blackkey" inputtag="KC1" inputmask="0x04"><bounds xc="300" y="0" width="25" height="125" /></element>
<element ref="whitekey" inputtag="KC1" inputmask="0x08"><bounds xc="330" y="0" width="25" height="245" /></element>
<element ref="blackkey" inputtag="KC1" inputmask="0x10"><bounds xc="360" y="0" width="25" height="125" /></element>
<element ref="whitekey" inputtag="KC1" inputmask="0x20"><bounds xc="390" y="0" width="25" height="245" /></element>
<element ref="whitekey" inputtag="KC2" inputmask="0x01"><bounds xc="450" y="0" width="25" height="245" /></element>
<element ref="blackkey" inputtag="KC2" inputmask="0x02"><bounds xc="480" y="0" width="25" height="125" /></element>
<element ref="whitekey" inputtag="KC2" inputmask="0x04"><bounds xc="510" y="0" width="25" height="245" /></element>
<element ref="blackkey" inputtag="KC2" inputmask="0x08"><bounds xc="540" y="0" width="25" height="125" /></element>
<element ref="whitekey" inputtag="KC2" inputmask="0x10"><bounds xc="570" y="0" width="25" height="245" /></element>
<element ref="whitekey" inputtag="KC2" inputmask="0x20"><bounds xc="630" y="0" width="25" height="245" /></element>
</group>
<group name="key_labels">
<bounds x="0" y="0" width="660" height="40" />
<element ref="tone2"><bounds xc="60" y="11" width="60" height="9" /></element>
<element ref="line_white"><bounds xc="60" y="20" width="1.5" height="20" /></element>
<element ref="tone4"><bounds xc="120" y="11" width="60" height="9" /></element>
<element ref="line_white"><bounds xc="120" y="20" width="1.5" height="20" /></element>
<element ref="tone7"><bounds xc="240" y="11" width="60" height="9" /></element>
<element ref="line_white"><bounds xc="240" y="20" width="1.5" height="20" /></element>
<element ref="tone9"><bounds xc="300" y="3" width="55" height="9" /></element>
<element ref="tone9b"><bounds xc="300" y="11" width="55" height="9" /></element>
<element ref="line_white"><bounds xc="300" y="20" width="1.5" height="20" /></element>
<element ref="tone11"><bounds xc="360" y="3" width="55" height="9" /></element>
<element ref="tone11b"><bounds xc="360" y="11" width="55" height="9" /></element>
<element ref="line_white"><bounds xc="360" y="20" width="1.5" height="20" /></element>
<element ref="tone1"><bounds xc="30" y="30" width="60" height="9" /></element>
<element ref="tone3"><bounds xc="90" y="30" width="60" height="9" /></element>
<element ref="tone5"><bounds xc="150" y="30" width="60" height="9" /></element>
<element ref="tone6"><bounds xc="210" y="30" width="60" height="9" /></element>
<element ref="tone8"><bounds xc="270" y="30" width="60" height="9" /></element>
<element ref="tone10"><bounds xc="330" y="22" width="55" height="9" /></element>
<element ref="tone10b"><bounds xc="330" y="30" width="55" height="9" /></element>
<element ref="tone12"><bounds xc="390" y="22" width="55" height="9" /></element>
<element ref="tone12b"><bounds xc="390" y="30" width="55" height="9" /></element>
<element ref="tone14"><bounds xc="480" y="11" width="60" height="9" /></element>
<element ref="line_white"><bounds xc="480" y="20" width="1.5" height="20" /></element>
<element ref="tone16"><bounds xc="540" y="11" width="60" height="9" /></element>
<element ref="line_white"><bounds xc="540" y="20" width="1.5" height="20" /></element>
<element ref="tone13"><bounds xc="450" y="30" width="60" height="9" /></element>
<element ref="tone15"><bounds xc="510" y="30" width="60" height="9" /></element>
<element ref="tone17"><bounds xc="570" y="30" width="60" height="9" /></element>
<element ref="tone18"><bounds xc="630" y="30" width="60" height="9" /></element>
</group>
<view name="Keyboard">
<element ref="brown"><bounds x="0" y="0" width="690" height="520" /></element>
<group ref="buttons"><bounds xc="345" y="0" width="660" height="180" /></group>
<element ref="panel_back"><bounds xc="345" y="180" width="660" height="40" /></element>
<element ref="line"><bounds xc="345" y="180" width="660" height="3" /></element>
<group ref="key_labels"><bounds xc="345" y="180" width="660" height="40" /></group>
<group ref="keyboard"><bounds xc="345" y="220" width="660" height="300" /></group>
</view>
</mamelayout>

View File

@ -16155,6 +16155,11 @@ casloopy //
cfx9850 // Casio CFX-9850
cfx9850gb // Casio CFX-9850GB Plus
@source:casio/ct8000.cpp
ct8000 //
ctfk1 //
ctmb1 //
@source:casio/ctk2000.cpp
ctk2100 //