mirror of
https://github.com/TomHarte/CLK.git
synced 2024-10-18 23:23:56 +00:00
Merge pull request #75 from TomHarte/AudioUpdates
Reintroduced a separate thread for audio processing and made various AY fixes
This commit is contained in:
commit
4ba39d13b5
@ -124,7 +124,7 @@ void AY38910::get_samples(unsigned int number_of_samples, int16_t *target)
|
|||||||
if(_envelope_divider) _envelope_divider--;
|
if(_envelope_divider) _envelope_divider--;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_envelope_divider = _envelope_period;
|
_envelope_divider = _envelope_period * 16;
|
||||||
_envelope_position ++;
|
_envelope_position ++;
|
||||||
if(_envelope_position == 32) _envelope_position = _envelope_overflow_masks[_output_registers[13]];
|
if(_envelope_position == 32) _envelope_position = _envelope_overflow_masks[_output_registers[13]];
|
||||||
}
|
}
|
||||||
@ -149,11 +149,12 @@ void AY38910::evaluate_output_volume()
|
|||||||
// The output level for a channel is:
|
// The output level for a channel is:
|
||||||
// 1 if neither tone nor noise is enabled;
|
// 1 if neither tone nor noise is enabled;
|
||||||
// 0 if either tone or noise is enabled and its value is low.
|
// 0 if either tone or noise is enabled and its value is low.
|
||||||
// (which is implemented here with reverse logic, assuming _channel_output and _noise_output are already inverted)
|
// The tone/noise enable bits use inverse logic — 0 = on, 1 = off — permitting the OR logic below.
|
||||||
#define level(c, tb, nb) \
|
#define tone_level(c, tone_bit) (_tone_outputs[c] | (_output_registers[7] >> tone_bit))
|
||||||
(((((_output_registers[7] >> tb)&1)^1) & _tone_outputs[c]) | ((((_output_registers[7] >> nb)&1)^1) & _noise_output)) ^ 1
|
#define noise_level(c, noise_bit) (_noise_output | (_output_registers[7] >> noise_bit))
|
||||||
|
|
||||||
int channel_levels[3] = {
|
#define level(c, tone_bit, noise_bit) tone_level(c, tone_bit) & noise_level(c, noise_bit) & 1
|
||||||
|
const int channel_levels[3] = {
|
||||||
level(0, 0, 3),
|
level(0, 0, 3),
|
||||||
level(1, 1, 4),
|
level(1, 1, 4),
|
||||||
level(2, 2, 5),
|
level(2, 2, 5),
|
||||||
@ -164,7 +165,7 @@ void AY38910::evaluate_output_volume()
|
|||||||
#define channel_volume(c) \
|
#define channel_volume(c) \
|
||||||
((_output_registers[c] >> 4)&1) * envelope_volume + (((_output_registers[c] >> 4)&1)^1) * (_output_registers[c]&0xf)
|
((_output_registers[c] >> 4)&1) * envelope_volume + (((_output_registers[c] >> 4)&1)^1) * (_output_registers[c]&0xf)
|
||||||
|
|
||||||
int volumes[3] = {
|
const int volumes[3] = {
|
||||||
channel_volume(8),
|
channel_volume(8),
|
||||||
channel_volume(9),
|
channel_volume(9),
|
||||||
channel_volume(10)
|
channel_volume(10)
|
||||||
@ -214,12 +215,12 @@ void AY38910::set_register_value(uint8_t value)
|
|||||||
|
|
||||||
case 11:
|
case 11:
|
||||||
_envelope_period = (_envelope_period & ~0xff) | value;
|
_envelope_period = (_envelope_period & ~0xff) | value;
|
||||||
_envelope_divider = _envelope_period;
|
_envelope_divider = _envelope_period * 16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 12:
|
case 12:
|
||||||
_envelope_period = (_envelope_period & 0xff) | (int)(value << 8);
|
_envelope_period = (_envelope_period & 0xff) | (int)(value << 8);
|
||||||
_envelope_divider = _envelope_period;
|
_envelope_divider = _envelope_period * 16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 13:
|
case 13:
|
||||||
@ -235,7 +236,15 @@ void AY38910::set_register_value(uint8_t value)
|
|||||||
|
|
||||||
uint8_t AY38910::get_register_value()
|
uint8_t AY38910::get_register_value()
|
||||||
{
|
{
|
||||||
return _registers[_selected_register];
|
// This table ensures that bits that aren't defined within the AY are returned as 1s
|
||||||
|
// when read. I can't find documentation on this and don't have a machine to test, so
|
||||||
|
// this is provisionally a guess. TODO: investigate.
|
||||||
|
const uint8_t register_masks[16] = {
|
||||||
|
0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0xe0, 0x00,
|
||||||
|
0xe0, 0xe0, 0xe0, 0x00, 0x00, 0xf0, 0x00, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
return _registers[_selected_register] | register_masks[_selected_register];
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t AY38910::get_port_output(bool port_b)
|
uint8_t AY38910::get_port_output(bool port_b)
|
||||||
|
@ -213,6 +213,7 @@ uint8_t Machine::VIA::get_port_input(Port port)
|
|||||||
void Machine::VIA::synchronise()
|
void Machine::VIA::synchronise()
|
||||||
{
|
{
|
||||||
ay8910->run_for_cycles(_cycles_since_ay_update);
|
ay8910->run_for_cycles(_cycles_since_ay_update);
|
||||||
|
ay8910->flush();
|
||||||
_cycles_since_ay_update = 0;
|
_cycles_since_ay_update = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,10 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
#include "../SignalProcessing/Stepper.hpp"
|
#include "../SignalProcessing/Stepper.hpp"
|
||||||
#include "../SignalProcessing/FIRFilter.hpp"
|
#include "../SignalProcessing/FIRFilter.hpp"
|
||||||
#include "../Concurrency/AsyncTaskQueue.hpp"
|
#include "../Concurrency/AsyncTaskQueue.hpp"
|
||||||
@ -87,16 +91,28 @@ class Speaker {
|
|||||||
|
|
||||||
Speaker() : _buffer_in_progress_pointer(0), _requested_number_of_taps(0), _high_frequency_cut_off(-1.0), _queue(new Concurrency::AsyncTaskQueue) {}
|
Speaker() : _buffer_in_progress_pointer(0), _requested_number_of_taps(0), _high_frequency_cut_off(-1.0), _queue(new Concurrency::AsyncTaskQueue) {}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Ensures any deferred processing occurs now.
|
||||||
|
*/
|
||||||
|
void flush()
|
||||||
|
{
|
||||||
|
std::shared_ptr<std::list<std::function<void(void)>>> queued_functions = _queued_functions;
|
||||||
|
_queued_functions.reset();
|
||||||
|
_queue->enqueue([queued_functions] {
|
||||||
|
for(auto function : *queued_functions)
|
||||||
|
{
|
||||||
|
function();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void enqueue(std::function<void(void)> function)
|
void enqueue(std::function<void(void)> function)
|
||||||
{
|
{
|
||||||
function();
|
if(!_queued_functions) _queued_functions.reset(new std::list<std::function<void(void)>>);
|
||||||
// _queue->enqueue(function);
|
_queued_functions->push_back(function);
|
||||||
}
|
|
||||||
void flush()
|
|
||||||
{
|
|
||||||
// _queue->flush();
|
|
||||||
}
|
}
|
||||||
|
std::shared_ptr<std::list<std::function<void(void)>>> _queued_functions;
|
||||||
|
|
||||||
std::unique_ptr<int16_t> _buffer_in_progress;
|
std::unique_ptr<int16_t> _buffer_in_progress;
|
||||||
float _high_frequency_cut_off;
|
float _high_frequency_cut_off;
|
||||||
@ -137,7 +153,7 @@ template <class T> class Filter: public Speaker {
|
|||||||
public:
|
public:
|
||||||
~Filter()
|
~Filter()
|
||||||
{
|
{
|
||||||
flush();
|
_queue->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void run_for_cycles(unsigned int input_cycles)
|
void run_for_cycles(unsigned int input_cycles)
|
||||||
|
Loading…
Reference in New Issue
Block a user