356 lines
11 KiB
C++
Executable File
356 lines
11 KiB
C++
Executable File
/*
|
|
* (C) 2011 Dirk W. Hoffmann. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "C64.h"
|
|
|
|
ReSID::ReSID()
|
|
{
|
|
name = "ReSID";
|
|
|
|
debug(2, " Creating ReSID at address %p...\n", this);
|
|
|
|
sid = new SID();
|
|
|
|
// Register snapshot items
|
|
SnapshotItem items[] = {
|
|
|
|
// Configuration items
|
|
{ &chipModel, sizeof(chipModel), KEEP_ON_RESET },
|
|
{ &sampleRate, sizeof(sampleRate), KEEP_ON_RESET },
|
|
{ &samplingMethod, sizeof(samplingMethod), KEEP_ON_RESET },
|
|
{ &cpuFrequency, sizeof(cpuFrequency), KEEP_ON_RESET },
|
|
{ &audioFilter, sizeof(audioFilter), KEEP_ON_RESET },
|
|
{ &externalAudioFilter, sizeof(externalAudioFilter), KEEP_ON_RESET },
|
|
{ &volume, sizeof(volume), KEEP_ON_RESET },
|
|
{ &targetVolume, sizeof(targetVolume), KEEP_ON_RESET },
|
|
|
|
// ReSID state
|
|
{ st.sid_register, sizeof(st.sid_register), KEEP_ON_RESET },
|
|
{ &st.bus_value, sizeof(st.bus_value), KEEP_ON_RESET },
|
|
{ &st.bus_value_ttl, sizeof(st.bus_value_ttl), KEEP_ON_RESET },
|
|
{ &st.accumulator[0], sizeof(st.accumulator[0]), KEEP_ON_RESET },
|
|
{ &st.accumulator[1], sizeof(st.accumulator[1]), KEEP_ON_RESET },
|
|
{ &st.accumulator[2], sizeof(st.accumulator[2]), KEEP_ON_RESET },
|
|
{ &st.shift_register[0], sizeof(&st.shift_register[0]), KEEP_ON_RESET },
|
|
{ &st.shift_register[1], sizeof(&st.shift_register[1]), KEEP_ON_RESET },
|
|
{ &st.shift_register[2], sizeof(&st.shift_register[2]), KEEP_ON_RESET },
|
|
{ &st.rate_counter[0], sizeof(st.rate_counter[0]), KEEP_ON_RESET },
|
|
{ &st.rate_counter[1], sizeof(st.rate_counter[1]), KEEP_ON_RESET },
|
|
{ &st.rate_counter[2], sizeof(st.rate_counter[2]), KEEP_ON_RESET },
|
|
{ &st.rate_counter_period[0], sizeof(st.rate_counter_period[0]), KEEP_ON_RESET },
|
|
{ &st.rate_counter_period[1], sizeof(st.rate_counter_period[1]), KEEP_ON_RESET },
|
|
{ &st.rate_counter_period[2], sizeof(st.rate_counter_period[2]), KEEP_ON_RESET },
|
|
{ &st.exponential_counter[0], sizeof(st.exponential_counter[0]), KEEP_ON_RESET },
|
|
{ &st.exponential_counter[1], sizeof(st.exponential_counter[1]), KEEP_ON_RESET },
|
|
{ &st.exponential_counter[2], sizeof(st.exponential_counter[2]), KEEP_ON_RESET },
|
|
{ &st.exponential_counter_period[0],sizeof(st.exponential_counter_period[0]), KEEP_ON_RESET },
|
|
{ &st.exponential_counter_period[1],sizeof(st.exponential_counter_period[1]), KEEP_ON_RESET },
|
|
{ &st.exponential_counter_period[2],sizeof(st.exponential_counter_period[2]), KEEP_ON_RESET },
|
|
{ &st.envelope_counter[0], sizeof(st.envelope_counter[0]), KEEP_ON_RESET },
|
|
{ &st.envelope_counter[1], sizeof(st.envelope_counter[1]), KEEP_ON_RESET },
|
|
{ &st.envelope_counter[2], sizeof(st.envelope_counter[2]), KEEP_ON_RESET },
|
|
{ &st.envelope_state[0], sizeof(st.envelope_state[0]), KEEP_ON_RESET },
|
|
{ &st.envelope_state[1], sizeof(st.envelope_state[1]), KEEP_ON_RESET },
|
|
{ &st.envelope_state[2], sizeof(st.envelope_state[2]), KEEP_ON_RESET },
|
|
{ &st.hold_zero[0], sizeof(st.hold_zero[0]), KEEP_ON_RESET },
|
|
{ &st.hold_zero[1], sizeof(st.hold_zero[1]), KEEP_ON_RESET },
|
|
{ &st.hold_zero[2], sizeof(st.hold_zero[2]), KEEP_ON_RESET },
|
|
|
|
{ NULL, 0, 0 }};
|
|
|
|
registerSnapshotItems(items, sizeof(items));
|
|
|
|
// Set default values
|
|
setChipModel(MOS6581);
|
|
|
|
cpuFrequency = PAL_CYCLES_PER_FRAME * PAL_REFRESH_RATE;
|
|
samplingMethod = SAMPLE_FAST;
|
|
sampleRate = 44100;
|
|
sid->set_sampling_parameters(cpuFrequency, samplingMethod, sampleRate);
|
|
|
|
setAudioFilter(false);
|
|
setExternalAudioFilter(false);
|
|
|
|
volume = 100000;
|
|
targetVolume = 100000;
|
|
}
|
|
|
|
ReSID::~ReSID()
|
|
{
|
|
delete sid;
|
|
}
|
|
|
|
void
|
|
ReSID::reset()
|
|
{
|
|
VirtualComponent::reset();
|
|
clearRingbuffer();
|
|
sid->reset();
|
|
}
|
|
|
|
void
|
|
ReSID::setChipModel(chip_model model)
|
|
{
|
|
switch (model) {
|
|
case MOS6581:
|
|
debug(2, "Plugging in MOS6581\n");
|
|
break;
|
|
case MOS8580:
|
|
debug(2, "Plugging in MOS8580\n");
|
|
break;
|
|
default:
|
|
warn("Unknown chip model. Using MOS8580\n");
|
|
model = MOS8580;
|
|
}
|
|
|
|
chipModel = model;
|
|
sid->set_chip_model(model);
|
|
}
|
|
|
|
void
|
|
ReSID::setAudioFilter(bool enable)
|
|
{
|
|
audioFilter = enable;
|
|
sid->enable_filter(enable);
|
|
}
|
|
|
|
void
|
|
ReSID::setExternalAudioFilter(bool enable)
|
|
{
|
|
externalAudioFilter = enable;
|
|
sid->enable_external_filter(enable);
|
|
}
|
|
|
|
void
|
|
ReSID::setSamplingMethod(sampling_method method)
|
|
{
|
|
switch (method) {
|
|
case SAMPLE_FAST:
|
|
debug(2, "Using sample method SAMPLE_FAST\n");
|
|
break;
|
|
case SAMPLE_INTERPOLATE:
|
|
debug(2, "Using sample method SAMPLE_INTERPOLATE\n");
|
|
break;
|
|
case SAMPLE_RESAMPLE_INTERPOLATE:
|
|
debug(2, "Using sample method SAMPLE_RESAMPLE_INTERPOLATE\n");
|
|
break;
|
|
case SAMPLE_RESAMPLE_FAST:
|
|
debug(2, "Using sample method SAMPLE_RESAMPLE_FAST\n");
|
|
break;
|
|
default:
|
|
warn("Unknown sample method. Using SAMPLE_FAST\n");
|
|
method = SAMPLE_FAST;
|
|
}
|
|
|
|
samplingMethod = method;
|
|
sid->set_sampling_parameters(cpuFrequency, samplingMethod, sampleRate);
|
|
}
|
|
|
|
void
|
|
ReSID::setSampleRate(uint32_t sr)
|
|
{
|
|
sampleRate = sr;
|
|
sid->set_sampling_parameters(cpuFrequency, samplingMethod, sampleRate);
|
|
}
|
|
|
|
void
|
|
ReSID::setClockFrequency(uint32_t frequency)
|
|
{
|
|
cpuFrequency = frequency;
|
|
sid->set_sampling_parameters(cpuFrequency, samplingMethod, sampleRate);
|
|
}
|
|
|
|
|
|
void
|
|
ReSID::loadFromBuffer(uint8_t **buffer)
|
|
{
|
|
VirtualComponent::loadFromBuffer(buffer);
|
|
|
|
// reset();
|
|
clearRingbuffer();
|
|
|
|
/*
|
|
setChipModel(chipModel);
|
|
setSampleRate(sampleRate);
|
|
setSamplingMethod(samplingMethod);
|
|
setAudioFilter(audioFilter);
|
|
setExternalAudioFilter(externalAudioFilter);
|
|
setClockFrequency(cpuFrequency);
|
|
*/
|
|
|
|
// Push state to reSID
|
|
sid->write_state(st);
|
|
}
|
|
|
|
void
|
|
ReSID::saveToBuffer(uint8_t **buffer)
|
|
{
|
|
// Pull state from reSID
|
|
st = sid->read_state();
|
|
|
|
VirtualComponent::saveToBuffer(buffer);
|
|
}
|
|
|
|
uint8_t
|
|
ReSID::peek(uint16_t addr)
|
|
{
|
|
return sid->read(addr);
|
|
}
|
|
|
|
void
|
|
ReSID::poke(uint16_t addr, uint8_t value)
|
|
{
|
|
// addr &= 0x1F;
|
|
sid->write(addr, value);
|
|
}
|
|
|
|
void
|
|
ReSID::execute(int elapsedCycles)
|
|
{
|
|
short buf[2049];
|
|
int buflength = 2048;
|
|
int delta_t = elapsedCycles;
|
|
int bufindex = 0;
|
|
|
|
// TODO: Can't we write directly into Core Audios ringbuffer (for Speedup)?
|
|
|
|
// Let reSID compute some sound samples
|
|
while (delta_t) {
|
|
bufindex += sid->clock(delta_t, buf + bufindex, buflength - bufindex);
|
|
// if (delta_t != 0) debug(2, "delta_t = %d\n", delta_t);
|
|
}
|
|
|
|
// Write samples into ringbuffer (output is silenced in warp mode)
|
|
|
|
// float volume = c64->getWarp() ? 0.0f : 0.000005f;
|
|
for (int i = 0; i < bufindex; i++) {
|
|
writeData((float)buf[i]);
|
|
}
|
|
|
|
// fprintf(stderr,"wrote %d samples\n", bufindex);
|
|
}
|
|
|
|
void
|
|
ReSID::run()
|
|
{
|
|
clearRingbuffer();
|
|
}
|
|
|
|
void
|
|
ReSID::halt()
|
|
{
|
|
clearRingbuffer();
|
|
}
|
|
|
|
void
|
|
ReSID::clearRingbuffer()
|
|
{
|
|
debug(4,"Clearing ringbuffer\n");
|
|
|
|
// Reset ringbuffer contents
|
|
for (unsigned i = 0; i < bufferSize; i++) {
|
|
ringBuffer[i] = 0.0f;
|
|
}
|
|
|
|
// Reset read pointer and put write pointer somewhat ahead
|
|
readPtr = 0;
|
|
alignWritePtr();
|
|
}
|
|
|
|
float
|
|
ReSID::readData()
|
|
{
|
|
readDataCnt++;
|
|
|
|
// Check for buffer underflow
|
|
if (readPtr == writePtr)
|
|
debug(4, "SID RINGBUFFER UNDERFLOW (%ld)\n", readPtr);
|
|
|
|
// Read sound sample
|
|
float value = ringBuffer[readPtr];
|
|
|
|
// Adjust volume
|
|
if (volume != targetVolume) {
|
|
if (volume < targetVolume) {
|
|
volume += MIN(volumeDelta, targetVolume - volume);
|
|
} else {
|
|
volume -= MIN(volumeDelta, volume - targetVolume);
|
|
}
|
|
}
|
|
value = (volume <= 0) ? 0.0f : value * (float)volume / 100000.0f;
|
|
|
|
// Advance read pointer
|
|
readPtr++;
|
|
if (readPtr == bufferSize)
|
|
readPtr = 0;
|
|
|
|
return value;
|
|
}
|
|
|
|
inline void
|
|
ReSID::writeData(float data)
|
|
{
|
|
writeDataCnt++;
|
|
|
|
// Check for buffer overflow
|
|
if (readPtr == writePtr) {
|
|
|
|
debug(4, "SID RINGBUFFER OVERFLOW (%ld)\n", writePtr);
|
|
|
|
if (!c64->getWarp()) // In real-time mode, we put the write ptr somewhat ahead of the read ptr
|
|
alignWritePtr();
|
|
else
|
|
return; // In warp mode, we don't advance the write ptr to avoid crack noises
|
|
}
|
|
|
|
#if 0
|
|
// Adjust volume
|
|
if (volume != targetVolume) {
|
|
if (volume < targetVolume) {
|
|
volume += MIN(volumeDelta, targetVolume - volume);
|
|
} else {
|
|
volume -= MIN(volumeDelta, volume - targetVolume);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Write sound sample
|
|
// float scale = (volume <= 0) ? 0.0f : 0.000005f * (float)volume / 100000.0f;
|
|
float scale = 0.000005f;
|
|
ringBuffer[writePtr] = data * scale;
|
|
|
|
// Advance write pointer
|
|
writePtr++;
|
|
if (writePtr == bufferSize)
|
|
writePtr = 0;
|
|
}
|
|
|
|
|
|
void
|
|
ReSID::dumpState()
|
|
{
|
|
msg("SID\n");
|
|
msg("---\n\n");
|
|
msg(" Sample rate : %d\n", sampleRate);
|
|
msg(" CPU frequency : %d\n", cpuFrequency);
|
|
msg(" Buffer size : %d\n", bufferSize);
|
|
msg("\n");
|
|
}
|
|
|
|
|