mirror of
https://github.com/TomHarte/CLK.git
synced 2025-08-15 14:27:29 +00:00
Merge pull request #754 from TomHarte/AYVolume
Introduces more correct AY volume levels.
This commit is contained in:
@@ -71,13 +71,19 @@ AY38910::AY38910(Personality personality, Concurrency::DeferringAsyncTaskQueue &
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AY38910::set_sample_volume_range(std::int16_t range) {
|
void AY38910::set_sample_volume_range(std::int16_t range) {
|
||||||
// set up volume lookup table
|
// Set up volume lookup table; the function below is based on a combination of the graph
|
||||||
|
// from the YM's datasheet, showing a clear power curve, and fitting that to observed
|
||||||
|
// values reported elsewhere.
|
||||||
const float max_volume = float(range) / 3.0f; // As there are three channels.
|
const float max_volume = float(range) / 3.0f; // As there are three channels.
|
||||||
constexpr float root_two = 1.414213562373095f;
|
constexpr float root_two = 1.414213562373095f;
|
||||||
for(int v = 0; v < 32; v++) {
|
for(int v = 0; v < 32; v++) {
|
||||||
volumes_[v] = int(max_volume / powf(root_two, float(v ^ 0x1f) / 2.0f));
|
volumes_[v] = int(max_volume / powf(root_two, float(v ^ 0x1f) / 3.18f));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tie level 0 to silence.
|
||||||
|
for(int v = 31; v >= 0; --v) {
|
||||||
|
volumes_[v] -= volumes_[0];
|
||||||
}
|
}
|
||||||
volumes_[0] = 0; // Tie level 0 to silence.
|
|
||||||
evaluate_output_volume();
|
evaluate_output_volume();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +191,7 @@ void AY38910::evaluate_output_volume() {
|
|||||||
#undef channel_volume
|
#undef channel_volume
|
||||||
|
|
||||||
// Mix additively.
|
// Mix additively.
|
||||||
output_volume_ = static_cast<int16_t>(
|
output_volume_ = int16_t(
|
||||||
volumes_[volumes[0]] * channel_levels[0] +
|
volumes_[volumes[0]] * channel_levels[0] +
|
||||||
volumes_[volumes[1]] * channel_levels[1] +
|
volumes_[volumes[1]] * channel_levels[1] +
|
||||||
volumes_[volumes[2]] * channel_levels[2]
|
volumes_[volumes[2]] * channel_levels[2]
|
||||||
@@ -220,7 +226,7 @@ void AY38910::set_register_value(uint8_t value) {
|
|||||||
int channel = selected_register >> 1;
|
int channel = selected_register >> 1;
|
||||||
|
|
||||||
if(selected_register & 1)
|
if(selected_register & 1)
|
||||||
tone_periods_[channel] = (tone_periods_[channel] & 0xff) | static_cast<uint16_t>((value&0xf) << 8);
|
tone_periods_[channel] = (tone_periods_[channel] & 0xff) | uint16_t((value&0xf) << 8);
|
||||||
else
|
else
|
||||||
tone_periods_[channel] = (tone_periods_[channel] & ~0xff) | value;
|
tone_periods_[channel] = (tone_periods_[channel] & ~0xff) | value;
|
||||||
}
|
}
|
||||||
@@ -235,7 +241,7 @@ void AY38910::set_register_value(uint8_t value) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 12:
|
case 12:
|
||||||
envelope_period_ = (envelope_period_ & 0xff) | static_cast<int>(value << 8);
|
envelope_period_ = (envelope_period_ & 0xff) | int(value << 8);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 13:
|
case 13:
|
||||||
@@ -331,15 +337,15 @@ uint8_t AY38910::get_data_output() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AY38910::set_control_lines(ControlLines control_lines) {
|
void AY38910::set_control_lines(ControlLines control_lines) {
|
||||||
switch(static_cast<int>(control_lines)) {
|
switch(int(control_lines)) {
|
||||||
default: control_state_ = Inactive; break;
|
default: control_state_ = Inactive; break;
|
||||||
|
|
||||||
case static_cast<int>(BDIR | BC2 | BC1):
|
case int(BDIR | BC2 | BC1):
|
||||||
case BDIR:
|
case BDIR:
|
||||||
case BC1: control_state_ = LatchAddress; break;
|
case BC1: control_state_ = LatchAddress; break;
|
||||||
|
|
||||||
case static_cast<int>(BC2 | BC1): control_state_ = Read; break;
|
case int(BC2 | BC1): control_state_ = Read; break;
|
||||||
case static_cast<int>(BDIR | BC2): control_state_ = Write; break;
|
case int(BDIR | BC2): control_state_ = Write; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
update_bus();
|
update_bus();
|
||||||
|
@@ -84,7 +84,7 @@ class MachineDocument:
|
|||||||
}
|
}
|
||||||
|
|
||||||
override func close() {
|
override func close() {
|
||||||
machine.stop()
|
machine?.stop()
|
||||||
|
|
||||||
activityPanel?.setIsVisible(false)
|
activityPanel?.setIsVisible(false)
|
||||||
activityPanel = nil
|
activityPanel = nil
|
||||||
|
@@ -770,11 +770,7 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
// Grab the time now and, therefore, the amount of time since the timer last fired
|
// Grab the time now and, therefore, the amount of time since the timer last fired
|
||||||
// (capped at half a second).
|
// (capped at half a second).
|
||||||
const auto timeNow = Time::nanos_now();
|
const auto timeNow = Time::nanos_now();
|
||||||
const auto duration = timeNow - lastTime;
|
const auto duration = std::min(timeNow - lastTime, Time::Nanos(10'000'000'000 / TICKS));
|
||||||
if(duration > Time::Nanos(500'000'000)) {
|
|
||||||
lastTime = timeNow;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CGSize pixelSize;
|
CGSize pixelSize;
|
||||||
BOOL splitAndSync = NO;
|
BOOL splitAndSync = NO;
|
||||||
|
@@ -131,8 +131,7 @@ struct MachineRunner {
|
|||||||
// now, as there's obviously been some sort of substantial time glitch.
|
// now, as there's obviously been some sort of substantial time glitch.
|
||||||
const auto time_now = Time::nanos_now();
|
const auto time_now = Time::nanos_now();
|
||||||
if(time_now - last_time_ > Time::Nanos(500'000'000)) {
|
if(time_now - last_time_ > Time::Nanos(500'000'000)) {
|
||||||
last_time_ = time_now;
|
last_time_ = time_now - Time::Nanos(500'000'000);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto vsync_time = vsync_time_.load();
|
const auto vsync_time = vsync_time_.load();
|
||||||
|
Reference in New Issue
Block a user