2019-06-01 18:39:40 +00:00
|
|
|
//
|
|
|
|
// Audio.hpp
|
|
|
|
// Clock Signal
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 31/05/2019.
|
|
|
|
// Copyright © 2019 Thomas Harte. All rights reserved.
|
|
|
|
//
|
|
|
|
|
2024-01-17 04:34:46 +00:00
|
|
|
#pragma once
|
2019-06-01 18:39:40 +00:00
|
|
|
|
|
|
|
#include "../../../Concurrency/AsyncTaskQueue.hpp"
|
|
|
|
#include "../../../ClockReceiver/ClockReceiver.hpp"
|
2024-02-09 19:25:40 +00:00
|
|
|
#include "../../../Outputs/Speaker/Implementation/BufferSource.hpp"
|
2019-06-01 18:39:40 +00:00
|
|
|
|
|
|
|
#include <array>
|
|
|
|
#include <atomic>
|
|
|
|
|
2023-05-10 21:02:18 +00:00
|
|
|
namespace Apple::Macintosh {
|
2019-06-01 18:39:40 +00:00
|
|
|
|
|
|
|
/*!
|
2019-07-17 18:41:36 +00:00
|
|
|
Implements the Macintosh's audio output hardware.
|
|
|
|
|
|
|
|
Designed to be clocked at half the rate of the real hardware — i.e.
|
|
|
|
a shade less than 4Mhz.
|
2019-06-01 18:39:40 +00:00
|
|
|
*/
|
2024-02-09 19:25:40 +00:00
|
|
|
class Audio: public ::Outputs::Speaker::BufferSource<Audio, false> {
|
2019-06-01 18:39:40 +00:00
|
|
|
public:
|
2022-07-16 18:41:04 +00:00
|
|
|
Audio(Concurrency::AsyncTaskQueue<false> &task_queue);
|
2019-06-01 18:39:40 +00:00
|
|
|
|
|
|
|
/*!
|
|
|
|
Macintosh audio is (partly) sourced by the same scanning
|
|
|
|
hardware as the video; each line it collects an additional
|
|
|
|
word of memory, half of which is used for audio output.
|
|
|
|
|
|
|
|
Use this method to add a newly-collected sample to the queue.
|
|
|
|
*/
|
|
|
|
void post_sample(uint8_t sample);
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Macintosh audio also separately receives an output volume
|
|
|
|
level, in the range 0 to 7.
|
|
|
|
|
|
|
|
Use this method to set the current output volume.
|
|
|
|
*/
|
|
|
|
void set_volume(int volume);
|
|
|
|
|
|
|
|
/*!
|
|
|
|
A further factor in audio output is the on-off toggle.
|
|
|
|
*/
|
|
|
|
void set_enabled(bool on);
|
|
|
|
|
|
|
|
// to satisfy ::Outputs::Speaker (included via ::Outputs::Filter.
|
|
|
|
void get_samples(std::size_t number_of_samples, int16_t *target);
|
2020-05-09 21:57:21 +00:00
|
|
|
bool is_zero_level() const;
|
2019-06-01 18:39:40 +00:00
|
|
|
void set_sample_volume_range(std::int16_t range);
|
|
|
|
|
|
|
|
private:
|
2022-07-16 18:41:04 +00:00
|
|
|
Concurrency::AsyncTaskQueue<false> &task_queue_;
|
2019-06-01 18:39:40 +00:00
|
|
|
|
|
|
|
// A queue of fetched samples; read from by one thread,
|
|
|
|
// written to by another.
|
|
|
|
struct {
|
2020-07-29 02:21:52 +00:00
|
|
|
std::array<std::atomic<uint8_t>, 740> buffer;
|
2019-07-17 18:41:36 +00:00
|
|
|
size_t read_pointer = 0, write_pointer = 0;
|
2019-06-01 18:39:40 +00:00
|
|
|
} sample_queue_;
|
|
|
|
|
2019-07-17 18:41:36 +00:00
|
|
|
// Emulator-thread stateful variables, to avoid work posting
|
|
|
|
// deferral updates if possible.
|
|
|
|
int posted_volume_ = 0;
|
|
|
|
int posted_enable_mask_ = 0;
|
|
|
|
|
2019-06-01 18:39:40 +00:00
|
|
|
// Stateful variables, modified from the audio generation
|
|
|
|
// thread only.
|
|
|
|
int volume_ = 0;
|
2019-06-01 19:18:27 +00:00
|
|
|
int enabled_mask_ = 0;
|
2019-07-17 18:54:06 +00:00
|
|
|
std::int16_t output_volume_ = 0;
|
2019-06-01 18:39:40 +00:00
|
|
|
|
|
|
|
std::int16_t volume_multiplier_ = 0;
|
2019-06-01 21:29:57 +00:00
|
|
|
std::size_t subcycle_offset_ = 0;
|
2019-07-17 18:54:06 +00:00
|
|
|
void set_volume_multiplier();
|
2019-06-01 18:39:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
}
|