Line data Source code
1 : #pragma once
2 :
3 : #include "common.h"
4 : #include <fstream>
5 : #include <chrono>
6 : #include <array>
7 : #include <thread>
8 :
9 : namespace Amplitron {
10 :
11 : class AudioEngine;
12 :
13 : class Recorder {
14 : public:
15 : static constexpr int WAVEFORM_SIZE = 512;
16 :
17 : Recorder();
18 : ~Recorder();
19 :
20 : // Start recording to a temporary WAV file
21 : bool start(const std::string& filepath, int sample_rate, int channels = 1);
22 :
23 : // Stop recording and finalize the WAV file
24 : void stop();
25 :
26 : // Pause / resume recording
27 : void pause();
28 : void resume();
29 :
30 : // Write audio samples (called from audio callback)
31 : void write_samples(const float* buffer, int num_samples);
32 :
33 : void write_samples_stereo(const float* left, const float* right, int num_samples);
34 :
35 : // Write metadata JSON sidecar file
36 : void write_metadata(const std::string& wav_path, AudioEngine& engine);
37 :
38 : // Move the recorded temp file to a user-chosen path
39 : bool save_to(const std::string& dest_path);
40 :
41 : // Discard the recorded temp file
42 : void discard();
43 :
44 5205 : bool is_recording() const { return recording_.load(); }
45 18 : bool is_paused() const { return paused_.load(); }
46 21 : bool has_unsaved() const { return has_unsaved_.load(); }
47 : float get_duration() const;
48 52 : int64_t get_samples_written() const { return samples_written_.load(); }
49 9 : int get_channels() const { return channels_; }
50 : const std::string& filepath() const { return filepath_; }
51 :
52 : // Waveform visualization data (lock-free ring buffer of peak values)
53 : void get_waveform(float* out, int count) const;
54 15 : float get_current_peak() const { return current_peak_.load(); }
55 :
56 : // Get default recordings directory
57 : static std::string get_recordings_dir();
58 :
59 : // Generate a timestamped filename
60 : static std::string generate_filename();
61 :
62 : private:
63 : std::ofstream file_;
64 : std::atomic<bool> recording_{false};
65 : std::atomic<bool> paused_{false};
66 : std::atomic<bool> has_unsaved_{false};
67 : std::atomic<int64_t> samples_written_{0};
68 : int sample_rate_ = 48000;
69 : int channels_ = 1;
70 : std::string filepath_;
71 :
72 : std::chrono::steady_clock::time_point start_time_;
73 : float pause_duration_ = 0.0f; // GUI thread only (accessed from pause/resume)
74 : std::chrono::steady_clock::time_point pause_start_;
75 :
76 : // Waveform ring buffer (written from audio thread, read from UI thread)
77 : std::array<std::atomic<float>, WAVEFORM_SIZE> waveform_buf_;
78 : std::atomic<int> waveform_write_pos_{0};
79 : std::atomic<float> current_peak_{0.0f};
80 : int samples_per_bin_ = 0;
81 : int bin_sample_count_ = 0;
82 : float bin_peak_ = 0.0f;
83 :
84 : // WAV header helpers
85 : void write_wav_header();
86 : void finalize_wav_header();
87 :
88 : // Lock-free ring buffer for real-time audio thread -> disk writer thread
89 : static constexpr int RING_BUFFER_SIZE = 48000 * 4;
90 : std::vector<float> ring_buffer_;
91 : std::atomic<int64_t> ring_write_pos_{0};
92 : std::atomic<int64_t> ring_read_pos_{0};
93 :
94 : // Disk writer thread (keeps file I/O off the real-time audio thread)
95 : std::thread writer_thread_;
96 : std::atomic<bool> writer_running_{false};
97 : std::vector<int16_t> pcm_buffer_;
98 :
99 : void writer_thread_func();
100 : };
101 :
102 : } // namespace Amplitron
|