32 const int channels =
static_cast<int>(block.
channels.size());
33 result.
peak.assign(channels, 0.0f);
34 result.
rms.assign(channels, 0.0f);
36 for (
int channel = 0; channel < channels; ++channel) {
37 const auto& samples = block.
channels[channel];
38 double squared_sum = 0.0;
41 for (
float sample : samples) {
42 const float abs_sample = std::abs(sample);
43 peak = std::max(peak, abs_sample);
44 squared_sum +=
static_cast<double>(sample) *
static_cast<double>(sample);
45 if (abs_sample >= 1.0f) {
50 result.
peak[channel] = peak;
51 if (!samples.empty()) {
52 result.
rms[channel] =
static_cast<float>(std::sqrt(squared_sum /
static_cast<double>(samples.size())));
60 : sample_rate(new_sample_rate)
61 , samples_per_second(new_samples_per_second)
65 , pending_squared_sum(0.0)
66 , emitted_visual_samples(0)
68 if (sample_rate <= 0 || samples_per_second <= 0) {
69 throw InvalidOptions(
"Audio waveform settings require a valid sample rate and samples-per-second value.");
72 sample_divisor = std::max(1, sample_rate / samples_per_second);
77 std::vector<AudioWaveformChunk> chunks;
84 chunk.
start_time =
static_cast<double>(emitted_visual_samples) /
static_cast<double>(samples_per_second);
86 const int channels =
static_cast<int>(block.
channels.size());
87 const int samples = block.
Samples();
88 for (
int sample_index = 0; sample_index < samples; ++sample_index) {
89 for (
int channel = 0; channel < channels; ++channel) {
90 if (sample_index >=
static_cast<int>(block.
channels[channel].size())) {
93 const float sample = block.
channels[channel][sample_index];
94 pending_max = std::max(pending_max, std::abs(sample));
95 pending_squared_sum +=
static_cast<double>(sample) *
static_cast<double>(sample);
99 if (pending_samples >= sample_divisor) {
100 const double denominator =
static_cast<double>(pending_samples * channels);
101 const float rms = denominator > 0.0
102 ?
static_cast<float>(std::sqrt(pending_squared_sum / denominator))
105 max_samples.push_back(pending_max);
106 rms_samples.push_back(rms);
109 emitted_visual_samples++;
113 pending_squared_sum = 0.0;
118 chunk.
duration =
static_cast<double>(chunk.
max_samples.size()) /
static_cast<double>(samples_per_second);
119 chunks.push_back(std::move(chunk));
137 pending_squared_sum = 0.0;
138 emitted_visual_samples = 0;
146 int64_t frame_number)
148 const int channels =
static_cast<int>(block.
channels.size());
149 const int samples = block.
Samples();
150 auto frame = std::make_shared<Frame>(frame_number, samples, channels);
152 frame->ChannelsLayout(channel_layout);
154 for (
int channel = 0; channel < channels; ++channel) {
155 frame->AddAudio(
true, channel, 0, block.
channels[channel].data(), samples, 1.0f);
162 : settings(new_settings)
164 , waveform_accumulator(nullptr)
166 , is_recording(false)
167 , is_monitoring(false)
168 , writer_should_stop(false)
169 , samples_recorded(0)
171 , next_frame_number(1)
181 void AudioRecorder::ValidateSettings()
const
183 if (settings.
path.empty()) {
186 if (settings.
codec.empty()) {
190 throw InvalidSampleRate(
"Audio recorder requires a sample rate of at least 8000 Hz.", settings.
path);
193 throw InvalidChannels(
"Audio recorder requires at least one input channel.", settings.
path);
196 throw InvalidOptions(
"Audio recorder requires a positive audio buffer size.", settings.
path);
199 throw InvalidOptions(
"Audio recorder requires a positive waveform sample rate.", settings.
path);
202 throw InvalidOptions(
"Audio recorder requires a positive maximum queue duration.", settings.
path);
213 device_manager.setCurrentAudioDeviceType(settings.
device_type,
true);
216 juce::AudioDeviceManager::AudioDeviceSetup setup;
217 setup.inputChannels.clear();
218 for (
int channel = 0; channel < settings.
channels; ++channel) {
219 setup.inputChannels.setBit(channel);
221 setup.outputChannels.clear();
226 const juce::String error = device_manager.initialise(
233 if (error.isNotEmpty()) {
237 if (
auto* device = device_manager.getCurrentAudioDevice()) {
238 const double actual_rate = device->getCurrentSampleRate();
239 if (actual_rate > 0.0) {
240 settings.
sample_rate =
static_cast<int>(std::llround(actual_rate));
244 waveform_accumulator = std::make_unique<AudioRecorderWaveformAccumulator>(
251 void AudioRecorder::OpenWriter()
257 writer = std::make_unique<FFmpegWriter>(settings.
path);
270 writer_should_stop =
false;
272 device_manager.addAudioCallback(
this);
273 writer_thread = std::thread(&AudioRecorder::WriterLoop,
this);
283 if (waveform_accumulator) {
284 waveform_accumulator->Reset();
286 samples_recorded = 0;
288 next_frame_number = 1;
293 if (!is_recording && !writer_thread.joinable()) {
301 is_recording =
false;
302 device_manager.removeAudioCallback(
this);
303 writer_should_stop =
true;
304 queue_condition.notify_all();
306 if (writer_thread.joinable()) {
307 writer_thread.join();
318 if (is_recording || is_monitoring) {
326 is_monitoring =
true;
327 device_manager.addAudioCallback(
this);
332 if (!is_monitoring) {
336 is_monitoring =
false;
337 device_manager.removeAudioCallback(
this);
346 device_manager.closeAudioDevice();
367 return is_monitoring;
383 std::lock_guard<std::mutex> lock(queue_mutex);
390 std::lock_guard<std::mutex> lock(state_mutex);
391 return waveform_accumulator ? waveform_accumulator->Snapshot() :
AudioWaveformData();
396 std::lock_guard<std::mutex> lock(state_mutex);
402 std::lock_guard<std::mutex> lock(state_mutex);
403 level_callback = std::move(callback);
408 std::lock_guard<std::mutex> lock(state_mutex);
409 waveform_callback = std::move(callback);
413 const float*
const* inputChannelData,
414 int numInputChannels,
415 float*
const* outputChannelData,
416 int numOutputChannels,
418 const juce::AudioIODeviceCallbackContext&)
420 for (
int channel = 0; channel < numOutputChannels; ++channel) {
421 if (outputChannelData[channel]) {
422 std::fill(outputChannelData[channel], outputChannelData[channel] + numSamples, 0.0f);
426 if ((!is_recording && !is_monitoring) || numSamples <= 0) {
435 for (
int channel = 0; channel < settings.
channels; ++channel) {
436 block.
channels[channel].assign(numSamples, 0.0f);
437 if (channel < numInputChannels && inputChannelData[channel]) {
438 std::copy(inputChannelData[channel], inputChannelData[channel] + numSamples, block.
channels[channel].begin());
445 std::lock_guard<std::mutex> lock(state_mutex);
447 level_cb = level_callback;
454 samples_recorded += numSamples;
458 const int64_t max_queue_blocks =
static_cast<int64_t
>(
461 std::lock_guard<std::mutex> lock(queue_mutex);
462 if (
static_cast<int64_t
>(queue.size()) >= max_queue_blocks) {
465 queue.push_back(std::move(block));
466 queue_condition.notify_one();
470 samples_recorded += numSamples;
484 std::unique_lock<std::mutex> lock(queue_mutex);
485 queue_condition.wait(lock, [
this]() {
486 return writer_should_stop || !queue.empty();
493 block = std::move(queue.front());
498 void AudioRecorder::WriterLoop()
500 int64_t expected_next_sample = -1;
503 if (!PopBlock(block)) {
504 if (writer_should_stop) {
510 std::vector<AudioWaveformChunk> waveform_chunks;
513 std::lock_guard<std::mutex> lock(state_mutex);
514 if (waveform_accumulator) {
515 waveform_chunks = waveform_accumulator->ProcessBlock(block);
517 waveform_cb = waveform_callback;
521 for (
const auto& chunk : waveform_chunks) {
527 while (expected_next_sample >= 0 && block.
first_sample > expected_next_sample) {
528 const int64_t missing_samples = block.
first_sample - expected_next_sample;
529 const int silence_samples =
static_cast<int>(std::min<int64_t>(
537 std::vector<float>(silence_samples, 0.0f));
541 next_frame_number++));
542 expected_next_sample += silence_samples;
548 next_frame_number++));
Header file for audio recording classes.
Header file for all Exception classes.
Header file for FFmpegWriter class.
Header file for Frame class.
AudioLevelData ProcessBlock(const AudioRecorderBlock &block) const
AudioRecorder(const AudioRecorderSettings &settings)
void SetWaveformCallback(std::function< void(const AudioWaveformChunk &)> callback)
void SetLevelCallback(std::function< void(const AudioLevelData &)> callback)
~AudioRecorder() override
AudioRecorderStats GetStats() const
bool IsMonitoring() const
void audioDeviceAboutToStart(juce::AudioIODevice *device) override
openshot::AudioWaveformData GetWaveformSnapshot() const
void audioDeviceStopped() override
AudioLevelData GetLevelSnapshot() const
void audioDeviceIOCallbackWithContext(const float *const *inputChannelData, int numInputChannels, float *const *outputChannelData, int numOutputChannels, int numSamples, const juce::AudioIODeviceCallbackContext &context) override
static std::shared_ptr< openshot::Frame > CreateFrame(const AudioRecorderBlock &block, openshot::ChannelLayout channel_layout, int64_t frame_number)
Exception when an invalid # of audio channels are detected.
Exception when invalid encoding options are used.
Exception when invalid sample rate is detected during encoding.
This namespace is the default namespace for all code in the openshot library.
ChannelLayout
This enumeration determines the audio channel layout (such as stereo, mono, 5 point surround,...
std::vector< float > peak
std::vector< std::vector< float > > channels
int waveform_samples_per_second
openshot::ChannelLayout channel_layout