1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
// This file is part of the 64k demo project.
// Implementation of WAV dump backend for debugging.
#include "wav_dump_backend.h"
#if !defined(STRIP_ALL)
#include "synth.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
WavDumpBackend::WavDumpBackend()
: wav_file_(nullptr),
samples_written_(0),
output_filename_("audio_dump.wav"),
is_active_(false) {
sample_buffer_.resize(kBufferSize);
}
WavDumpBackend::~WavDumpBackend() { shutdown(); }
void WavDumpBackend::set_output_file(const char* filename) {
output_filename_ = filename;
}
void WavDumpBackend::init() {
// Open WAV file for writing
wav_file_ = fopen(output_filename_, "wb");
if (wav_file_ == nullptr) {
fprintf(stderr, "Error: Failed to open WAV file: %s\n", output_filename_);
return;
}
// Write placeholder header (we'll update it in shutdown())
write_wav_header(wav_file_, 0);
samples_written_ = 0;
printf("WAV dump backend initialized: %s\n", output_filename_);
}
void WavDumpBackend::start() {
is_active_ = true;
printf("WAV dump started, rendering audio...\n");
// Render audio in chunks until we reach desired duration
// For debugging, render 60 seconds max
const int max_duration_sec = 60;
const size_t total_samples = kSampleRate * max_duration_sec;
const size_t total_frames = total_samples / kBufferSize;
for (size_t frame = 0; frame < total_frames; ++frame) {
// Render audio from synth
synth_render(sample_buffer_.data(), kBufferSize);
// Convert float to int16 and write to WAV
for (int i = 0; i < kBufferSize; ++i) {
// Clamp to [-1, 1] and convert to 16-bit signed
float sample = sample_buffer_[i];
if (sample > 1.0f) sample = 1.0f;
if (sample < -1.0f) sample = -1.0f;
const int16_t sample_i16 = (int16_t)(sample * 32767.0f);
fwrite(&sample_i16, sizeof(int16_t), 1, wav_file_);
}
samples_written_ += kBufferSize;
// Progress indicator
if (frame % 100 == 0) {
const float progress_sec = (float)samples_written_ / kSampleRate;
printf(" Rendering: %.1fs / %ds\r", progress_sec, max_duration_sec);
fflush(stdout);
}
// Call frame rendering hook
on_frames_rendered(kBufferSize);
}
printf("\nWAV dump complete: %zu samples (%.2f seconds)\n", samples_written_,
(float)samples_written_ / kSampleRate);
is_active_ = false;
}
void WavDumpBackend::shutdown() {
if (wav_file_ != nullptr) {
// Update header with final sample count
update_wav_header();
fclose(wav_file_);
wav_file_ = nullptr;
printf("WAV file written: %s\n", output_filename_);
}
}
void WavDumpBackend::write_wav_header(FILE* file, uint32_t num_samples) {
// WAV file header structure
// Reference: http://soundfile.sapp.org/doc/WaveFormat/
const uint32_t num_channels = 1; // Mono
const uint32_t sample_rate = kSampleRate;
const uint32_t bits_per_sample = 16;
const uint32_t byte_rate = sample_rate * num_channels * bits_per_sample / 8;
const uint16_t block_align = num_channels * bits_per_sample / 8;
const uint32_t data_size = num_samples * num_channels * bits_per_sample / 8;
// RIFF header
fwrite("RIFF", 1, 4, file);
const uint32_t chunk_size = 36 + data_size;
fwrite(&chunk_size, 4, 1, file);
fwrite("WAVE", 1, 4, file);
// fmt subchunk
fwrite("fmt ", 1, 4, file);
const uint32_t subchunk1_size = 16;
fwrite(&subchunk1_size, 4, 1, file);
const uint16_t audio_format = 1; // PCM
fwrite(&audio_format, 2, 1, file);
fwrite(&num_channels, 2, 1, file);
fwrite(&sample_rate, 4, 1, file);
fwrite(&byte_rate, 4, 1, file);
fwrite(&block_align, 2, 1, file);
fwrite(&bits_per_sample, 2, 1, file);
// data subchunk header
fwrite("data", 1, 4, file);
fwrite(&data_size, 4, 1, file);
}
void WavDumpBackend::update_wav_header() {
if (wav_file_ == nullptr) return;
// Seek to beginning and rewrite header with actual sample count
fseek(wav_file_, 0, SEEK_SET);
write_wav_header(wav_file_, samples_written_);
}
#endif /* !defined(STRIP_ALL) */
|