2016-12-03 18:39:46 +00:00
|
|
|
//
|
|
|
|
// Speaker.cpp
|
|
|
|
// Clock Signal
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 03/12/2016.
|
2018-05-13 19:19:52 +00:00
|
|
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
2016-12-03 18:39:46 +00:00
|
|
|
//
|
|
|
|
|
2017-12-18 02:26:06 +00:00
|
|
|
#include "TIASound.hpp"
|
2016-12-03 18:39:46 +00:00
|
|
|
|
|
|
|
using namespace Atari2600;
|
|
|
|
|
2022-07-16 18:41:04 +00:00
|
|
|
Atari2600::TIASound::TIASound(Concurrency::AsyncTaskQueue<false> &audio_queue) :
|
2017-12-18 02:26:06 +00:00
|
|
|
audio_queue_(audio_queue),
|
2016-12-03 18:39:46 +00:00
|
|
|
poly4_counter_{0x00f, 0x00f},
|
|
|
|
poly5_counter_{0x01f, 0x01f},
|
|
|
|
poly9_counter_{0x1ff, 0x1ff}
|
|
|
|
{}
|
|
|
|
|
2017-12-18 02:26:06 +00:00
|
|
|
void Atari2600::TIASound::set_volume(int channel, uint8_t volume) {
|
2022-07-14 20:39:26 +00:00
|
|
|
audio_queue_.enqueue([target = &volume_[channel], volume]() {
|
2020-02-15 04:39:08 +00:00
|
|
|
*target = volume & 0xf;
|
2016-12-03 18:39:46 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-12-18 02:26:06 +00:00
|
|
|
void Atari2600::TIASound::set_divider(int channel, uint8_t divider) {
|
2022-07-14 20:39:26 +00:00
|
|
|
audio_queue_.enqueue([this, channel, divider]() {
|
2016-12-03 18:39:46 +00:00
|
|
|
divider_[channel] = divider & 0x1f;
|
|
|
|
divider_counter_[channel] = 0;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-12-18 02:26:06 +00:00
|
|
|
void Atari2600::TIASound::set_control(int channel, uint8_t control) {
|
2022-07-14 20:39:26 +00:00
|
|
|
audio_queue_.enqueue([target = &control_[channel], control]() {
|
2020-02-15 04:39:08 +00:00
|
|
|
*target = control & 0xf;
|
2016-12-03 18:39:46 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#define advance_poly4(c) poly4_counter_[channel] = (poly4_counter_[channel] >> 1) | (((poly4_counter_[channel] << 3) ^ (poly4_counter_[channel] << 2))&0x008)
|
|
|
|
#define advance_poly5(c) poly5_counter_[channel] = (poly5_counter_[channel] >> 1) | (((poly5_counter_[channel] << 4) ^ (poly5_counter_[channel] << 2))&0x010)
|
|
|
|
#define advance_poly9(c) poly9_counter_[channel] = (poly9_counter_[channel] >> 1) | (((poly9_counter_[channel] << 4) ^ (poly9_counter_[channel] << 8))&0x100)
|
|
|
|
|
2024-02-12 15:55:52 +00:00
|
|
|
template <Outputs::Speaker::Action action>
|
|
|
|
void Atari2600::TIASound::apply_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *target) {
|
2017-03-24 01:59:16 +00:00
|
|
|
for(unsigned int c = 0; c < number_of_samples; c++) {
|
2024-02-12 15:55:52 +00:00
|
|
|
Outputs::Speaker::MonoSample output = 0;
|
2017-03-24 01:59:16 +00:00
|
|
|
for(int channel = 0; channel < 2; channel++) {
|
2016-12-03 18:39:46 +00:00
|
|
|
divider_counter_[channel] ++;
|
2017-04-16 01:18:00 +00:00
|
|
|
int divider_value = divider_counter_[channel] / (38 / CPUTicksPerAudioTick);
|
2016-12-03 18:39:46 +00:00
|
|
|
int level = 0;
|
2017-03-24 01:59:16 +00:00
|
|
|
switch(control_[channel]) {
|
2016-12-03 18:39:46 +00:00
|
|
|
case 0x0: case 0xb: // constant 1
|
|
|
|
level = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x4: case 0x5: // div2 tone
|
2017-04-16 01:18:00 +00:00
|
|
|
level = (divider_value / (divider_[channel]+1))&1;
|
2016-12-03 18:39:46 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xc: case 0xd: // div6 tone
|
2017-04-16 01:18:00 +00:00
|
|
|
level = (divider_value / ((divider_[channel]+1)*3))&1;
|
2016-12-03 18:39:46 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x6: case 0xa: // div31 tone
|
2017-04-16 01:18:00 +00:00
|
|
|
level = (divider_value / (divider_[channel]+1))%30 <= 18;
|
2016-12-03 18:39:46 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xe: // div93 tone
|
2017-04-16 01:18:00 +00:00
|
|
|
level = (divider_value / ((divider_[channel]+1)*3))%30 <= 18;
|
2016-12-03 18:39:46 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x1: // 4-bit poly
|
|
|
|
level = poly4_counter_[channel]&1;
|
2017-04-16 01:18:00 +00:00
|
|
|
if(divider_value == divider_[channel]+1) {
|
2016-12-03 18:39:46 +00:00
|
|
|
divider_counter_[channel] = 0;
|
|
|
|
advance_poly4(channel);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x2: // 4-bit poly div31
|
|
|
|
level = poly4_counter_[channel]&1;
|
2017-04-16 01:18:00 +00:00
|
|
|
if(divider_value%(30*(divider_[channel]+1)) == 18) {
|
2016-12-03 18:39:46 +00:00
|
|
|
advance_poly4(channel);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x3: // 5/4-bit poly
|
|
|
|
level = output_state_[channel];
|
2017-04-16 01:18:00 +00:00
|
|
|
if(divider_value == divider_[channel]+1) {
|
2017-03-24 01:59:16 +00:00
|
|
|
if(poly5_counter_[channel]&1) {
|
2016-12-03 18:39:46 +00:00
|
|
|
output_state_[channel] = poly4_counter_[channel]&1;
|
|
|
|
advance_poly4(channel);
|
|
|
|
}
|
|
|
|
advance_poly5(channel);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x7: case 0x9: // 5-bit poly
|
|
|
|
level = poly5_counter_[channel]&1;
|
2017-04-16 01:18:00 +00:00
|
|
|
if(divider_value == divider_[channel]+1) {
|
2016-12-03 18:39:46 +00:00
|
|
|
divider_counter_[channel] = 0;
|
|
|
|
advance_poly5(channel);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xf: // 5-bit poly div6
|
|
|
|
level = poly5_counter_[channel]&1;
|
2017-04-16 01:18:00 +00:00
|
|
|
if(divider_value == (divider_[channel]+1)*3) {
|
2016-12-03 18:39:46 +00:00
|
|
|
divider_counter_[channel] = 0;
|
|
|
|
advance_poly5(channel);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x8: // 9-bit poly
|
|
|
|
level = poly9_counter_[channel]&1;
|
2017-04-16 01:18:00 +00:00
|
|
|
if(divider_value == divider_[channel]+1) {
|
2016-12-03 18:39:46 +00:00
|
|
|
divider_counter_[channel] = 0;
|
|
|
|
advance_poly9(channel);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-02-12 15:55:52 +00:00
|
|
|
output += (volume_[channel] * per_channel_volume_ * level) >> 4;
|
2016-12-03 18:39:46 +00:00
|
|
|
}
|
2024-02-12 15:55:52 +00:00
|
|
|
Outputs::Speaker::apply<action>(target[c], output);
|
2016-12-03 18:39:46 +00:00
|
|
|
}
|
|
|
|
}
|
2024-02-12 15:55:52 +00:00
|
|
|
template void Atari2600::TIASound::apply_samples<Outputs::Speaker::Action::Mix>(std::size_t, Outputs::Speaker::MonoSample *);
|
|
|
|
template void Atari2600::TIASound::apply_samples<Outputs::Speaker::Action::Store>(std::size_t, Outputs::Speaker::MonoSample *);
|
|
|
|
template void Atari2600::TIASound::apply_samples<Outputs::Speaker::Action::Ignore>(std::size_t, Outputs::Speaker::MonoSample *);
|
2018-03-09 18:23:18 +00:00
|
|
|
|
|
|
|
void Atari2600::TIASound::set_sample_volume_range(std::int16_t range) {
|
|
|
|
per_channel_volume_ = range / 2;
|
|
|
|
}
|