99static constexpr int FR_SIZE = 4;
101static constexpr uint8_t IP_SIZE_SLOWER = 240 / FR_SIZE;
102static constexpr uint8_t IP_SIZE_SLOW = 200 / FR_SIZE;
103static constexpr uint8_t IP_SIZE_NORMAL = 160 / FR_SIZE;
104static constexpr uint8_t IP_SIZE_FAST = 120 / FR_SIZE;
105static constexpr uint8_t IP_SIZE_FASTER = 80 / FR_SIZE;
125static constexpr std::array<uint8_t, 8> VLM5030_speed_table =
142static constexpr std::array<uint16_t, 0x20> energyTable =
144 0, 2, 4, 6, 10, 12, 14, 18,
145 22, 26, 30, 34, 38, 44, 48, 54,
146 62, 68, 76, 84, 94,102,114,124,
147 136,150,164,178,196,214,232,254
151static constexpr std::array<uint8_t, 0x20> pitchTable =
155 23, 24, 25, 26, 27, 28, 29, 30,
156 32, 34, 36, 38, 40, 42, 44, 46,
157 50, 54, 58, 62, 66, 70, 74, 78,
158 86, 94, 102,110,118,126
161static constexpr std::array<int16_t, 64> K1_table = {
162 -24898, -25672, -26446, -27091, -27736, -28252, -28768, -29155,
163 -29542, -29929, -30316, -30574, -30832, -30961, -31219, -31348,
164 -31606, -31735, -31864, -31864, -31993, -32122, -32122, -32251,
165 -32251, -32380, -32380, -32380, -32509, -32509, -32509, -32509,
166 24898, 23995, 22963, 21931, 20770, 19480, 18061, 16642,
167 15093, 13416, 11610, 9804, 7998, 6063, 3999, 1935,
168 0, -1935, -3999, -6063, -7998, -9804, -11610, -13416,
169 -15093, -16642, -18061, -19480, -20770, -21931, -22963, -23995
171static constexpr std::array<int16_t, 32> K2_table = {
172 0, -3096, -6321, -9417, -12513, -15351, -18061, -20770,
173 -23092, -25285, -27220, -28897, -30187, -31348, -32122, -32638,
174 0, 32638, 32122, 31348, 30187, 28897, 27220, 25285,
175 23092, 20770, 18061, 15351, 12513, 9417, 6321, 3096
177static constexpr std::array<int16_t, 16> K3_table = {
178 0, -3999, -8127, -12255, -16384, -20383, -24511, -28639,
179 32638, 28639, 24511, 20383, 16254, 12255, 8127, 3999
181static constexpr std::array<int16_t, 8> K5_table = {
182 0, -8127, -16384, -24511, 32638, 24511, 16254, 8127
185unsigned VLM5030::getBits(
unsigned sBit,
unsigned bits)
const
187 unsigned offset = address + (sBit / 8);
188 unsigned data = rom[(offset + 0) & address_mask] +
189 rom[(offset + 1) & address_mask] * 256;
191 data &= (0xFF >> (8 - bits));
196int VLM5030::parseFrame()
199 old_energy = new_energy;
200 old_pitch = new_pitch;
203 if (uint8_t cmd = rom[address & address_mask];
206 new_energy = new_pitch = 0;
214 int nums = ((cmd >> 2) + 1) * 2;
215 return nums * FR_SIZE;
219 new_pitch = narrow_cast<uint8_t>((pitchTable[getBits(1, 5)] + pitch_offset) & 0xff);
221 new_energy = energyTable[getBits(6, 5)];
224 new_k[9] = K5_table[getBits(11, 3)];
225 new_k[8] = K5_table[getBits(14, 3)];
226 new_k[7] = K5_table[getBits(17, 3)];
227 new_k[6] = K5_table[getBits(20, 3)];
228 new_k[5] = K5_table[getBits(23, 3)];
229 new_k[4] = K5_table[getBits(26, 3)];
230 new_k[3] = K3_table[getBits(29, 4)];
231 new_k[2] = K3_table[getBits(33, 4)];
232 new_k[1] = K2_table[getBits(37, 5)];
233 new_k[0] = K1_table[getBits(42, 6)];
240void VLM5030::generateChannels(std::span<float*> bufs,
unsigned num)
255 if (sample_count == 0) {
261 sample_count = frame_size;
263 if (interp_count == 0) {
265 interp_count = narrow_cast<uint8_t>(parseFrame());
266 if (interp_count == 0) {
268 interp_count = FR_SIZE;
269 sample_count = frame_size;
273 current_energy = old_energy;
274 current_pitch = old_pitch;
277 if (current_energy == 0) {
279 target_pitch = narrow_cast<uint8_t>(current_pitch);
283 target_energy = new_energy;
284 target_pitch = new_pitch;
290 interp_count -= interp_step;
292 int interp_effect = FR_SIZE - (interp_count % FR_SIZE);
293 current_energy = old_energy + (target_energy - old_energy) * interp_effect / FR_SIZE;
295 current_pitch = old_pitch + (target_pitch - old_pitch) * interp_effect / FR_SIZE;
298 current_k[i] = old_k[i] + (target_k[i] - old_k[i]) * interp_effect / FR_SIZE;
301 int current_val = [&] {
302 if (old_energy == 0) {
305 }
else if (old_pitch <= 1) {
308 : -int(current_energy);
311 return (pitch_count == 0) ? int(current_energy) : 0;
316 std::array<int, 11> u;
318 for (
int i = 9; i >= 0; --i) {
319 u[i] = u[i + 1] - ((current_k[i] * x[i]) / 32768);
321 for (
int i = 9; i >= 1; --i) {
322 x[i] = x[i - 1] + ((current_k[i - 1] * u[i - 1]) / 32768);
327 bufs[0][buf_count] = narrow<float>(std::clamp(u[0], -511, 511));
331 if (pitch_count >= current_pitch) {
341 if (sample_count <= num) {
346 sample_count -= narrow<uint8_t>(num);
350 if (sample_count <= num) {
355 sample_count -= narrow<uint8_t>(num);
360 bufs[0][buf_count++] = 0;
365float VLM5030::getAmplificationFactorImpl()
const
367 return 1.0f / (1 << 9);
371void VLM5030::setupParameter(uint8_t param)
379 }
else if (param & 1) {
386 frame_size = VLM5030_speed_table[(param >> 3) & 7];
391 }
else if (param & 0x40) {
405 old_energy = old_pitch = 0;
406 new_energy = new_pitch = 0;
407 current_energy = current_pitch = 0;
408 target_energy = target_pitch = 0;
413 interp_count = sample_count = pitch_count = 0;
416 setupParameter(0x00);
435 setRST((data & 0x01) != 0);
436 setVCU((data & 0x04) != 0);
437 setST ((data & 0x02) != 0);
441void VLM5030::setRST(
bool pin)
446 setupParameter(latch_data);
459void VLM5030::setVCU(
bool pin)
466void VLM5030::setST(
bool pin)
477 vcu_addr_h = narrow<uint16_t>((latch_data << 8) + 0x01);
482 address = (vcu_addr_h & 0xff00) + latch_data;
486 int table = (latch_data & 0xfe) + ((
int(latch_data) & 1) << 8);
487 address = uint16_t((rom[(table + 0) & address_mask] << 8) |
488 (rom[(table + 1) & address_mask] << 0));
491 sample_count = frame_size;
492 interp_count = FR_SIZE;
508static XMLElement* getRomConfig(
509 DeviceConfig& config,
const std::string& name, std::string_view romFilename)
511 auto& doc = config.getXMLDocument();
512 auto* voiceROMconfig = doc.allocateElement(doc.allocateString(name));
514 auto* romElement = voiceROMconfig->setFirstChild(doc.allocateElement(
"rom"));
515 romElement->setFirstChild(doc.allocateElement(
516 "sha1",
"4f36d139ee4baa7d5980f765de9895570ee05f40"))
517 ->setNextSibling(doc.allocateElement(
520 ->setNextSibling(doc.allocateElement(
521 "filename",
"keyboardmaster/voice.rom"));
522 return voiceROMconfig;
525static constexpr auto INPUT_RATE = unsigned(
cstd::round(3579545 / 440.0));
528 std::string_view romFilename,
const DeviceConfig& config)
530 , rom(name_ +
" ROM",
"rom",
DeviceConfig(config, *getRomConfig(const_cast<
DeviceConfig&>(config), name_, romFilename)))
535 assert(rom.
size() != 0);
536 address_mask = narrow<unsigned>(rom.
size() - 1);
546template<
typename Archive>
549 ar.serialize(
"address_mask", address_mask,
550 "frame_size", frame_size,
551 "pitch_offset", pitch_offset,
552 "current_energy", current_energy,
553 "current_pitch", current_pitch,
554 "current_k", current_k,
557 "vcu_addr_h", vcu_addr_h,
560 "target_k", target_k,
561 "old_energy", old_energy,
562 "new_energy", new_energy,
563 "target_energy", target_energy,
564 "old_pitch", old_pitch,
565 "new_pitch", new_pitch,
566 "target_pitch", target_pitch,
567 "interp_step", interp_step,
568 "interp_count", interp_count,
569 "sample_count", sample_count,
570 "pitch_count", pitch_count,
571 "latch_data", latch_data,
572 "parameter", parameter,
void updateStream(EmuTime::param time)
void unregisterSound()
Unregisters this sound device with the Mixer.
void registerSound(const DeviceConfig &config)
Registers this sound device with the Mixer.
void writeData(uint8_t data)
latch control data
bool getBSY(EmuTime::param time) const
get BSY pin level
VLM5030(const std::string &name, static_string_view desc, std::string_view romFilename, const DeviceConfig &config)
void writeControl(uint8_t data, EmuTime::param time)
set RST / VCU / ST pins
void serialize(Archive &ar, unsigned version)
XMLAttribute * setFirstAttribute(XMLAttribute *attribute)
constexpr double round(double x)
string_view stripExtension(string_view path)
Returns the path without extension.
This file implemented 3 utility functions:
constexpr void fill(ForwardRange &&range, const T &value)
auto copy(InputRange &&range, OutputIter out)
bool random_bool()
Return a random boolean value.
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
TemporaryString tmpStrCat(Ts &&... ts)
constexpr auto xrange(T e)