OXYGENHUB
◆ rrdc-perf enhanced-x16-emulator / src / vera_pcm.h
3.7 KiB · Objective-C · last changed chore(): initial commit 25 days ago source blame history raw
1
// Commander X16 Emulator
2
// Copyright (c) 2020 Frank van den Hoef
3
// All rights reserved. License: 2-clause BSD
4
5
#pragma once
6
7
#include <stdint.h>
8
#include <stdbool.h>
9
10
#ifdef __cplusplus
11
extern "C" {
12
#endif
13
14
void    pcm_reset(void);
15
void    pcm_write_ctrl(uint8_t val);
16
uint8_t pcm_read_ctrl(void);
17
void    pcm_write_rate(uint8_t val);
18
uint8_t pcm_read_rate(void);
19
void    pcm_write_fifo(uint8_t val);
20
void    pcm_render(int16_t *buf, unsigned num_samples);
21
bool    pcm_is_fifo_almost_empty(void);
22
23
// ─── Debugger accessors ─────────────────────────────────────────────────────
24
// Read-only views of the VERA PCM channel for the ImGui debugger's PCM panel.
25
// Called from the emulator main thread — the same thread that runs pcm_render()
26
// via audio_render() — so no locking is needed.
27
28
#define PCM_FIFO_SIZE 4096
29
30
struct pcm_debug_state {
31
	uint8_t  ctrl;         // AUDIO_CTRL as stored (bits 5-0; format + volume)
32
	uint8_t  rate;         // effective rate divider after the >128 clamp
33
	bool     loop;
34
	unsigned fifo_cnt;     // bytes queued
35
	unsigned fifo_rdidx;
36
	unsigned fifo_wridx;
37
	unsigned fifo_size;    // PCM_FIFO_SIZE (usable fill is fifo_size - 1)
38
	int16_t  cur_l, cur_r; // last sample fetched from the FIFO
39
	uint8_t  phase;        // 8-bit playback phase accumulator
40
	bool     almost_empty; // the AFLOW IRQ condition (fifo_cnt < 1024)
41
};
42
43
void pcm_debug_get_state(struct pcm_debug_state *out);
44
45
// Copy up to `len` queued FIFO bytes into `dest`, starting `offset` bytes after
46
// the read pointer, without consuming anything. Handles the ring wrap and stops
47
// at the end of the queued data. Returns the number of bytes written.
48
unsigned pcm_debug_peek_fifo(uint8_t *dest, unsigned offset, unsigned len);
49
50
// ─── Scope capture ──────────────────────────────────────────────────────────
51
// Self-arming output capture, same contract as the PSG's (see vera_psg.h):
52
// reading (re)arms it for roughly half a second, so pcm_render() costs nothing
53
// extra while the debugger's Scope tab is hidden.
54
55
#define PCM_SCOPE_SAMPLES 2048
56
#define PCM_SCOPE_L       0
57
#define PCM_SCOPE_R       1
58
#define PCM_SCOPE_STREAMS 2
59
60
unsigned pcm_debug_scope_read(int side, int16_t *dest, unsigned max_samples);
61
62
// Predict the next `num_samples` of playback from the queued FIFO contents,
63
// WITHOUT consuming them. Returns the sample count written to each side.
64
//
65
// For the debugger: scope capture is self-arming and nothing renders audio while
66
// paused, so a scope opened at a breakpoint has an empty ring. The FIFO already
67
// holds the samples that are about to play, so we can show them. Runs the real
68
// pcm_render() over saved-and-restored state, so the rate stepping, FIFO drain
69
// order and loop restart all behave exactly as they will on resume.
70
//
71
// This is a projection of queued data, not captured output; label it as such.
72
//
73
// Implementation note for reviewers: this runs the live FIFO state forward and
74
// then rewinds it, rather than working on a copy. That is safe here because
75
//   * every field pcm_render() touches is saved and restored (the FIFO, its
76
//     three indices, the held sample pair and the rate phase - there is no
77
//     hidden or derived state), and
78
//   * pcm_render() is only ever called from audio_render() on the main thread,
79
//     the same thread as the debugger, and not at all while paused.
80
// Nothing in the audio path observes the intermediate state.
81
unsigned pcm_debug_scope_predict(int16_t *dest_l, int16_t *dest_r, unsigned num_samples);
82
83
#ifdef __cplusplus
84
}
85
#endif