2020-11-05 02:29:44 +00:00
|
|
|
//
|
|
|
|
// Sound.hpp
|
|
|
|
// Clock Signal
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 04/11/2020.
|
|
|
|
// Copyright © 2020 Thomas Harte. All rights reserved.
|
|
|
|
//
|
|
|
|
|
2024-01-17 04:34:46 +00:00
|
|
|
#pragma once
|
2020-11-05 02:29:44 +00:00
|
|
|
|
2020-11-19 02:40:56 +00:00
|
|
|
#include <atomic>
|
|
|
|
|
2020-11-05 02:29:44 +00:00
|
|
|
#include "../../../ClockReceiver/ClockReceiver.hpp"
|
2020-11-18 23:39:11 +00:00
|
|
|
#include "../../../Concurrency/AsyncTaskQueue.hpp"
|
2024-02-09 19:25:40 +00:00
|
|
|
#include "../../../Outputs/Speaker/Implementation/BufferSource.hpp"
|
2020-11-05 02:29:44 +00:00
|
|
|
|
2023-05-10 21:02:18 +00:00
|
|
|
namespace Apple::IIgs::Sound {
|
2020-11-05 02:29:44 +00:00
|
|
|
|
2024-02-09 19:25:40 +00:00
|
|
|
class GLU: public Outputs::Speaker::BufferSource<GLU, false> { // TODO: isn't this stereo?
|
2020-11-05 02:29:44 +00:00
|
|
|
public:
|
2022-07-16 18:41:04 +00:00
|
|
|
GLU(Concurrency::AsyncTaskQueue<false> &audio_queue);
|
2020-11-18 23:39:11 +00:00
|
|
|
|
2020-11-05 02:29:44 +00:00
|
|
|
void set_control(uint8_t);
|
|
|
|
uint8_t get_control();
|
|
|
|
void set_data(uint8_t);
|
|
|
|
uint8_t get_data();
|
|
|
|
void set_address_low(uint8_t);
|
|
|
|
uint8_t get_address_low();
|
|
|
|
void set_address_high(uint8_t);
|
|
|
|
uint8_t get_address_high();
|
|
|
|
|
2020-11-18 23:39:11 +00:00
|
|
|
void run_for(Cycles);
|
2023-09-10 22:00:49 +00:00
|
|
|
Cycles next_sequence_point() const;
|
2020-11-26 21:44:35 +00:00
|
|
|
bool get_interrupt_line();
|
2020-11-18 23:39:11 +00:00
|
|
|
|
|
|
|
// SampleSource.
|
2024-02-12 15:55:52 +00:00
|
|
|
template <Outputs::Speaker::Action action>
|
|
|
|
void apply_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *target);
|
2020-11-18 23:39:11 +00:00
|
|
|
void set_sample_volume_range(std::int16_t range);
|
2024-02-13 15:51:33 +00:00
|
|
|
bool is_zero_level() const { return false; } // TODO.
|
2020-11-18 23:39:11 +00:00
|
|
|
|
2020-11-05 02:29:44 +00:00
|
|
|
private:
|
2022-07-16 18:41:04 +00:00
|
|
|
Concurrency::AsyncTaskQueue<false> &audio_queue_;
|
2020-11-18 23:39:11 +00:00
|
|
|
|
2020-11-05 02:29:44 +00:00
|
|
|
uint16_t address_ = 0;
|
2020-11-18 23:39:11 +00:00
|
|
|
|
2020-11-19 02:40:56 +00:00
|
|
|
// Use a circular buffer for piping memory alterations onto the audio
|
|
|
|
// thread; it would be prohibitive to defer every write individually.
|
|
|
|
//
|
|
|
|
// Assumed: on most modern architectures, an atomic 64-bit read or
|
|
|
|
// write can be achieved locklessly.
|
|
|
|
struct MemoryWrite {
|
|
|
|
uint32_t time;
|
|
|
|
uint16_t address;
|
|
|
|
uint8_t value;
|
2020-11-20 03:21:20 +00:00
|
|
|
bool enabled;
|
2020-11-19 02:40:56 +00:00
|
|
|
};
|
|
|
|
static_assert(sizeof(MemoryWrite) == 8);
|
|
|
|
constexpr static int StoreBufferSize = 16384;
|
|
|
|
|
|
|
|
std::atomic<MemoryWrite> pending_stores_[StoreBufferSize];
|
|
|
|
uint32_t pending_store_read_ = 0, pending_store_read_time_ = 0;
|
|
|
|
uint32_t pending_store_write_ = 0, pending_store_write_time_ = 0;
|
|
|
|
|
|
|
|
// Maintain state both 'locally' (i.e. on the emulation thread) and
|
|
|
|
// 'remotely' (i.e. on the audio thread).
|
2020-11-18 23:39:11 +00:00
|
|
|
struct EnsoniqState {
|
|
|
|
uint8_t ram_[65536];
|
|
|
|
struct Oscillator {
|
|
|
|
uint32_t position;
|
|
|
|
|
|
|
|
// Programmer-set values.
|
|
|
|
uint16_t velocity;
|
|
|
|
uint8_t volume;
|
|
|
|
uint8_t address;
|
|
|
|
uint8_t control;
|
|
|
|
uint8_t table_size;
|
2020-11-20 02:19:27 +00:00
|
|
|
|
2020-11-26 20:07:29 +00:00
|
|
|
// Derived state.
|
2020-11-26 21:24:48 +00:00
|
|
|
uint32_t overflow_mask; // If a non-zero bit gets anywhere into the overflow mask, this channel
|
|
|
|
// has wrapped around. It's a function of table_size.
|
|
|
|
bool interrupt_request = false; // Will be non-zero if this channel would request an interrupt, were
|
|
|
|
// it currently enabled to do so.
|
2020-11-26 20:07:29 +00:00
|
|
|
|
2020-11-20 02:19:27 +00:00
|
|
|
uint8_t sample(uint8_t *ram);
|
|
|
|
int16_t output(uint8_t *ram);
|
2020-11-19 02:52:03 +00:00
|
|
|
} oscillators[32];
|
2020-11-19 00:48:53 +00:00
|
|
|
|
2020-11-26 21:24:48 +00:00
|
|
|
// Some of these aren't actually needed on both threads.
|
2020-12-10 00:46:32 +00:00
|
|
|
uint8_t control = 0;
|
|
|
|
int oscillator_count = 1;
|
2020-11-19 02:52:03 +00:00
|
|
|
|
|
|
|
void set_register(uint16_t address, uint8_t value);
|
2020-11-18 23:39:11 +00:00
|
|
|
} local_, remote_;
|
|
|
|
|
2020-11-19 02:40:56 +00:00
|
|
|
// Functions to update an EnsoniqState; these don't belong to the state itself
|
|
|
|
// because they also access the pending stores (inter alia).
|
2024-02-12 15:55:52 +00:00
|
|
|
template <Outputs::Speaker::Action action>
|
|
|
|
void generate_audio(size_t number_of_samples, Outputs::Speaker::MonoSample *target);
|
2020-11-19 02:40:56 +00:00
|
|
|
void skip_audio(EnsoniqState &state, size_t number_of_samples);
|
2020-11-19 00:48:53 +00:00
|
|
|
|
2020-11-19 02:40:56 +00:00
|
|
|
// Audio-thread state.
|
2020-11-19 00:48:53 +00:00
|
|
|
int16_t output_range_ = 0;
|
2020-11-05 02:29:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
}
|