#ifndef YMFM_PCM_H
#define YMFM_PCM_H
#pragma once
#include "ymfm.h"
namespace ymfm
{
class pcm_engine;
struct pcm_cache
{
uint32_t step; uint32_t total_level; uint32_t pan_left; uint32_t pan_right; uint32_t eg_sustain; uint8_t eg_rate[EG_STATES]; uint8_t lfo_step; uint8_t am_depth; uint8_t pm_depth; };
class pcm_registers
{
public:
static constexpr uint32_t OUTPUTS = 4;
static constexpr uint32_t CHANNELS = 24;
static constexpr uint32_t REGISTERS = 0x100;
static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1;
pcm_registers() { }
void save_restore(ymfm_saved_state &state);
void reset();
void cache_channel_data(uint32_t choffs, pcm_cache &cache);
uint8_t read(uint32_t index ) { return m_regdata[index]; }
void write(uint32_t index, uint8_t data) { m_regdata[index] = data; }
uint32_t memory_access_mode() const { return bitfield(m_regdata[0x02], 0); }
uint32_t memory_type() const { return bitfield(m_regdata[0x02], 1); }
uint32_t wave_table_header() const { return bitfield(m_regdata[0x02], 2, 3); }
uint32_t device_id() const { return bitfield(m_regdata[0x02], 5, 3); }
uint32_t memory_address() const { return (bitfield(m_regdata[0x03], 0, 6) << 16) | (m_regdata[0x04] << 8) | m_regdata[0x05]; }
uint32_t memory_data() const { return m_regdata[0x06]; }
uint32_t mix_fm_r() const { return bitfield(m_regdata[0xf8], 3, 3); }
uint32_t mix_fm_l() const { return bitfield(m_regdata[0xf8], 0, 3); }
uint32_t mix_pcm_r() const { return bitfield(m_regdata[0xf9], 3, 3); }
uint32_t mix_pcm_l() const { return bitfield(m_regdata[0xf9], 0, 3); }
uint32_t ch_wave_table_num(uint32_t choffs) const { return m_regdata[choffs + 0x08] | (bitfield(m_regdata[choffs + 0x20], 0) << 8); }
uint32_t ch_fnumber(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x20], 1, 7) | (bitfield(m_regdata[choffs + 0x38], 0, 3) << 7); }
uint32_t ch_pseudo_reverb(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x38], 3); }
uint32_t ch_octave(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x38], 4, 4); }
uint32_t ch_total_level(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x50], 1, 7); }
uint32_t ch_level_direct(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x50], 0); }
uint32_t ch_keyon(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 7); }
uint32_t ch_damp(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 6); }
uint32_t ch_lfo_reset(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 5); }
uint32_t ch_output_channel(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 4); }
uint32_t ch_panpot(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 0, 4); }
uint32_t ch_lfo_speed(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x80], 3, 3); }
uint32_t ch_vibrato(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x80], 0, 3); }
uint32_t ch_attack_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x98], 4, 4); }
uint32_t ch_decay_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x98], 0, 4); }
uint32_t ch_sustain_level(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xb0], 4, 4); }
uint32_t ch_sustain_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xb0], 0, 4); }
uint32_t ch_rate_correction(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xc8], 4, 4); }
uint32_t ch_release_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xc8], 0, 4); }
uint32_t ch_am_depth(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xe0], 0, 3); }
uint32_t memory_address_autoinc()
{
uint32_t result = memory_address();
uint32_t newval = result + 1;
m_regdata[0x05] = newval >> 0;
m_regdata[0x04] = newval >> 8;
m_regdata[0x03] = (newval >> 16) & 0x3f;
return result;
}
private:
uint32_t effective_rate(uint32_t raw, uint32_t correction);
uint8_t m_regdata[REGISTERS]; };
class pcm_channel
{
static constexpr uint8_t KEY_ON = 0x01;
static constexpr uint8_t KEY_PENDING_ON = 0x02;
static constexpr uint8_t KEY_PENDING = 0x04;
static constexpr uint32_t EG_QUIET = 0x200;
public:
using output_data = ymfm_output<pcm_registers::OUTPUTS>;
pcm_channel(pcm_engine &owner, uint32_t choffs);
void save_restore(ymfm_saved_state &state);
void reset();
uint32_t choffs() const { return m_choffs; }
bool prepare();
void clock(uint32_t env_counter);
void output(output_data &output) const;
void keyonoff(bool on);
void load_wavetable();
private:
void start_attack();
void start_release();
void clock_envelope(uint32_t env_counter);
int16_t fetch_sample() const;
uint8_t read_pcm(uint32_t address) const;
uint32_t const m_choffs; uint32_t m_baseaddr; uint32_t m_endpos; uint32_t m_looppos; uint32_t m_curpos; uint32_t m_nextpos; uint32_t m_lfo_counter; envelope_state m_eg_state; uint16_t m_env_attenuation; uint32_t m_total_level; uint8_t m_format; uint8_t m_key_state; pcm_cache m_cache; pcm_registers &m_regs; pcm_engine &m_owner; };
class pcm_engine
{
public:
static constexpr int OUTPUTS = pcm_registers::OUTPUTS;
static constexpr int CHANNELS = pcm_registers::CHANNELS;
static constexpr uint32_t ALL_CHANNELS = pcm_registers::ALL_CHANNELS;
using output_data = pcm_channel::output_data;
pcm_engine(ymfm_interface &intf);
void reset();
void save_restore(ymfm_saved_state &state);
void clock(uint32_t chanmask);
void output(output_data &output, uint32_t chanmask);
uint8_t read(uint32_t regnum);
void write(uint32_t regnum, uint8_t data);
ymfm_interface &intf() { return m_intf; }
pcm_registers ®s() { return m_regs; }
private:
ymfm_interface &m_intf; uint32_t m_env_counter; uint32_t m_modified_channels; uint32_t m_active_channels; uint32_t m_prepare_count; std::unique_ptr<pcm_channel> m_channel[CHANNELS]; pcm_registers m_regs; };
}
#endif