mirror of
https://github.com/TomHarte/CLK.git
synced 2024-07-06 01:28:57 +00:00
Pushes Mac audio further towards being able to function.
This commit is contained in:
parent
6d985866ee
commit
17635da812
@ -10,41 +10,55 @@
|
|||||||
|
|
||||||
using namespace Apple::Macintosh;
|
using namespace Apple::Macintosh;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const HalfCycles sample_length(704);
|
||||||
|
}
|
||||||
|
|
||||||
Audio::Audio(Concurrency::DeferringAsyncTaskQueue &task_queue) : task_queue_(task_queue) {}
|
Audio::Audio(Concurrency::DeferringAsyncTaskQueue &task_queue) : task_queue_(task_queue) {}
|
||||||
|
|
||||||
// MARK: - Inputs
|
// MARK: - Inputs
|
||||||
|
|
||||||
void Audio::post_sample(uint8_t sample) {
|
void Audio::post_sample(uint8_t sample) {
|
||||||
|
// Grab the read and write pointers, ensure there's room for a new sample and, if not,
|
||||||
|
// drop this one.
|
||||||
const auto write_pointer = sample_queue_.write_pointer.load();
|
const auto write_pointer = sample_queue_.write_pointer.load();
|
||||||
// const auto pointers
|
|
||||||
|
|
||||||
const auto read_pointer = sample_queue_.read_pointer.load();
|
const auto read_pointer = sample_queue_.read_pointer.load();
|
||||||
|
const decltype(write_pointer) next_write_pointer = (write_pointer + 1) % sample_queue_.buffer.size();
|
||||||
|
if(next_write_pointer == read_pointer) return;
|
||||||
|
|
||||||
|
sample_queue_.buffer[write_pointer] = sample;
|
||||||
|
sample_queue_.write_pointer.store(next_write_pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Audio::set_volume(int volume) {
|
void Audio::set_volume(int volume) {
|
||||||
|
// Post the volume change as a deferred event.
|
||||||
task_queue_.defer([=] () {
|
task_queue_.defer([=] () {
|
||||||
volume_ = volume;
|
volume_ = volume;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Audio::set_enabled(bool on) {
|
void Audio::set_enabled(bool on) {
|
||||||
|
// Post the enabled mask change as a deferred event.
|
||||||
task_queue_.defer([=] () {
|
task_queue_.defer([=] () {
|
||||||
is_enabled_ = on;
|
enabled_mask_ = on ? 1 : 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Output generation
|
// MARK: - Output generation
|
||||||
|
|
||||||
bool Audio::is_zero_level() {
|
bool Audio::is_zero_level() {
|
||||||
return !volume_ || !is_enabled_;
|
return !volume_ || !enabled_mask_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Audio::set_sample_volume_range(std::int16_t range) {
|
void Audio::set_sample_volume_range(std::int16_t range) {
|
||||||
|
// Some underflow here doesn't really matter.
|
||||||
volume_multiplier_ = range / 7;
|
volume_multiplier_ = range / 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Audio::get_samples(std::size_t number_of_samples, int16_t *target) {
|
void Audio::get_samples(std::size_t number_of_samples, int16_t *target) {
|
||||||
// if(is_zero_level()) {
|
// TODO.
|
||||||
// memset(target, 0, number_of_samples * sizeof(int16_t));
|
}
|
||||||
// }
|
|
||||||
|
void Audio::skip_samples(std::size_t number_of_samples) {
|
||||||
|
// TODO.
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,9 @@
|
|||||||
#ifndef Audio_hpp
|
#ifndef Audio_hpp
|
||||||
#define Audio_hpp
|
#define Audio_hpp
|
||||||
|
|
||||||
#include "../../../Outputs/Speaker/Implementation/SampleSource.hpp"
|
|
||||||
#include "../../../Concurrency/AsyncTaskQueue.hpp"
|
#include "../../../Concurrency/AsyncTaskQueue.hpp"
|
||||||
#include "../../../ClockReceiver/ClockReceiver.hpp"
|
#include "../../../ClockReceiver/ClockReceiver.hpp"
|
||||||
|
#include "../../../Outputs/Speaker/Implementation/SampleSource.hpp"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
@ -51,6 +51,7 @@ class Audio: public ::Outputs::Speaker::SampleSource {
|
|||||||
|
|
||||||
// to satisfy ::Outputs::Speaker (included via ::Outputs::Filter.
|
// to satisfy ::Outputs::Speaker (included via ::Outputs::Filter.
|
||||||
void get_samples(std::size_t number_of_samples, int16_t *target);
|
void get_samples(std::size_t number_of_samples, int16_t *target);
|
||||||
|
void skip_samples(std::size_t number_of_samples);
|
||||||
bool is_zero_level();
|
bool is_zero_level();
|
||||||
void set_sample_volume_range(std::int16_t range);
|
void set_sample_volume_range(std::int16_t range);
|
||||||
|
|
||||||
@ -61,15 +62,17 @@ class Audio: public ::Outputs::Speaker::SampleSource {
|
|||||||
// written to by another.
|
// written to by another.
|
||||||
struct {
|
struct {
|
||||||
std::array<uint8_t, 2048> buffer;
|
std::array<uint8_t, 2048> buffer;
|
||||||
std::atomic<int> read_pointer, write_pointer;
|
std::atomic<unsigned int> read_pointer, write_pointer;
|
||||||
} sample_queue_;
|
} sample_queue_;
|
||||||
|
|
||||||
// Stateful variables, modified from the audio generation
|
// Stateful variables, modified from the audio generation
|
||||||
// thread only.
|
// thread only.
|
||||||
int volume_ = 0;
|
int volume_ = 0;
|
||||||
bool is_enabled_ = false;
|
int enabled_mask_ = 0;
|
||||||
|
|
||||||
std::int16_t volume_multiplier_ = 0;
|
std::int16_t volume_multiplier_ = 0;
|
||||||
|
|
||||||
|
HalfCycles subcycle_offset_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -236,7 +236,7 @@ class ConcreteMachine:
|
|||||||
// video_.run_for(time_since_video_update_.flush());
|
// video_.run_for(time_since_video_update_.flush());
|
||||||
|
|
||||||
// As above: flush audio after video.
|
// As above: flush audio after video.
|
||||||
audio_.queue.flush();
|
audio_.queue.perform();
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_rom_is_overlay(bool rom_is_overlay) {
|
void set_rom_is_overlay(bool rom_is_overlay) {
|
||||||
|
Loading…
Reference in New Issue
Block a user