27 return std::max(requested_channels, 2);
33 const float*
const* input_channel_data,
34 int num_input_channels,
38 for (
int channel = 0; channel < channels; ++channel) {
39 block.
channels[channel].assign(num_samples, 0.0f);
40 const int source = channel < num_input_channels ? channel : 0;
41 if (source < num_input_channels && input_channel_data[source]) {
42 std::copy(input_channel_data[source], input_channel_data[source] + num_samples, block.
channels[channel].begin());
54 const int channels =
static_cast<int>(block.
channels.size());
55 result.
peak.assign(channels, 0.0f);
56 result.
rms.assign(channels, 0.0f);
58 for (
int channel = 0; channel < channels; ++channel) {
59 const auto& samples = block.
channels[channel];
60 double squared_sum = 0.0;
63 for (
float sample : samples) {
64 const float abs_sample = std::abs(sample);
65 peak = std::max(peak, abs_sample);
66 squared_sum +=
static_cast<double>(sample) *
static_cast<double>(sample);
67 if (abs_sample >= 1.0f) {
72 result.
peak[channel] = peak;
73 if (!samples.empty()) {
74 result.
rms[channel] =
static_cast<float>(std::sqrt(squared_sum /
static_cast<double>(samples.size())));
82 : sample_rate(new_sample_rate)
83 , samples_per_second(new_samples_per_second)
87 , pending_squared_sum(0.0)
88 , emitted_visual_samples(0)
90 if (sample_rate <= 0 || samples_per_second <= 0) {
91 throw InvalidOptions(
"Audio waveform settings require a valid sample rate and samples-per-second value.");
94 sample_divisor = std::max(1, sample_rate / samples_per_second);
99 std::vector<AudioWaveformChunk> chunks;
106 chunk.
start_time =
static_cast<double>(emitted_visual_samples) /
static_cast<double>(samples_per_second);
108 const int channels =
static_cast<int>(block.
channels.size());
109 const int samples = block.
Samples();
110 for (
int sample_index = 0; sample_index < samples; ++sample_index) {
111 for (
int channel = 0; channel < channels; ++channel) {
112 if (sample_index >=
static_cast<int>(block.
channels[channel].size())) {
115 const float sample = block.
channels[channel][sample_index];
116 pending_max = std::max(pending_max, std::abs(sample));
117 pending_squared_sum +=
static_cast<double>(sample) *
static_cast<double>(sample);
121 if (pending_samples >= sample_divisor) {
122 const double denominator =
static_cast<double>(pending_samples * channels);
123 const float rms = denominator > 0.0
124 ?
static_cast<float>(std::sqrt(pending_squared_sum / denominator))
127 max_samples.push_back(pending_max);
128 rms_samples.push_back(rms);
131 emitted_visual_samples++;
135 pending_squared_sum = 0.0;
140 chunk.
duration =
static_cast<double>(chunk.
max_samples.size()) /
static_cast<double>(samples_per_second);
141 chunks.push_back(std::move(chunk));
159 pending_squared_sum = 0.0;
160 emitted_visual_samples = 0;
168 int64_t frame_number)
170 const int channels =
static_cast<int>(block.
channels.size());
171 const int samples = block.
Samples();
172 auto frame = std::make_shared<Frame>(frame_number, samples, channels);
174 frame->ChannelsLayout(channel_layout);
176 for (
int channel = 0; channel < channels; ++channel) {
177 frame->AddAudio(
true, channel, 0, block.
channels[channel].data(), samples, 1.0f);
184 : settings(new_settings)
186 , waveform_accumulator(nullptr)
188 , is_recording(false)
189 , is_monitoring(false)
190 , writer_should_stop(false)
191 , samples_recorded(0)
193 , next_frame_number(1)
203void AudioRecorder::ValidateSettings()
const
205 if (settings.
path.empty()) {
208 if (settings.
codec.empty()) {
212 throw InvalidSampleRate(
"Audio recorder requires a sample rate of at least 8000 Hz.", settings.
path);
215 throw InvalidChannels(
"Audio recorder requires at least one input channel.", settings.
path);
218 throw InvalidOptions(
"Audio recorder requires a positive audio buffer size.", settings.
path);
221 throw InvalidOptions(
"Audio recorder requires a positive waveform sample rate.", settings.
path);
224 throw InvalidOptions(
"Audio recorder requires a positive maximum queue duration.", settings.
path);
237 device_manager.getAvailableDeviceTypes();
238 device_manager.setCurrentAudioDeviceType(settings.
device_type,
true);
244 juce::String error = OpenDevice(device_channels);
245 if (error.isNotEmpty() && device_channels != settings.
channels) {
246 device_manager.closeAudioDevice();
247 error = OpenDevice(settings.
channels);
249 if (error.isNotEmpty()) {
253 if (
auto* device = device_manager.getCurrentAudioDevice()) {
254 const double actual_rate = device->getCurrentSampleRate();
255 if (actual_rate > 0.0) {
256 settings.
sample_rate =
static_cast<int>(std::llround(actual_rate));
260 waveform_accumulator = std::make_unique<AudioRecorderWaveformAccumulator>(
267juce::String AudioRecorder::OpenDevice(
int device_channels)
269 juce::AudioDeviceManager::AudioDeviceSetup setup;
270 setup.inputChannels.clear();
271 setup.inputChannels.setRange(0, device_channels,
true);
272 setup.outputChannels.clear();
277 return device_manager.initialise(
286void AudioRecorder::OpenWriter()
292 writer = std::make_unique<FFmpegWriter>(settings.
path);
305 writer_should_stop =
false;
307 device_manager.addAudioCallback(
this);
308 writer_thread = std::thread(&AudioRecorder::WriterLoop,
this);
318 if (waveform_accumulator) {
319 waveform_accumulator->Reset();
321 samples_recorded = 0;
323 next_frame_number = 1;
328 if (!is_recording && !writer_thread.joinable()) {
336 is_recording =
false;
337 device_manager.removeAudioCallback(
this);
338 writer_should_stop =
true;
339 queue_condition.notify_all();
341 if (writer_thread.joinable()) {
342 writer_thread.join();
353 if (is_recording || is_monitoring) {
361 is_monitoring =
true;
362 device_manager.addAudioCallback(
this);
367 if (!is_monitoring) {
371 is_monitoring =
false;
372 device_manager.removeAudioCallback(
this);
381 device_manager.closeAudioDevice();
402 return is_monitoring;
418 std::lock_guard<std::mutex> lock(queue_mutex);
425 std::lock_guard<std::mutex> lock(state_mutex);
426 return waveform_accumulator ? waveform_accumulator->Snapshot() :
AudioWaveformData();
431 std::lock_guard<std::mutex> lock(state_mutex);
437 std::lock_guard<std::mutex> lock(state_mutex);
438 level_callback = std::move(callback);
443 std::lock_guard<std::mutex> lock(state_mutex);
444 waveform_callback = std::move(callback);
448 const float*
const* inputChannelData,
449 int numInputChannels,
450 float*
const* outputChannelData,
451 int numOutputChannels,
453 const juce::AudioIODeviceCallbackContext&)
455 for (
int channel = 0; channel < numOutputChannels; ++channel) {
456 if (outputChannelData[channel]) {
457 std::fill(outputChannelData[channel], outputChannelData[channel] + numSamples, 0.0f);
461 if ((!is_recording && !is_monitoring) || numSamples <= 0) {
473 std::lock_guard<std::mutex> lock(state_mutex);
475 level_cb = level_callback;
482 samples_recorded += numSamples;
486 const int64_t max_queue_blocks =
static_cast<int64_t
>(
489 std::lock_guard<std::mutex> lock(queue_mutex);
490 if (
static_cast<int64_t
>(queue.size()) >= max_queue_blocks) {
493 queue.push_back(std::move(block));
494 queue_condition.notify_one();
498 samples_recorded += numSamples;
512 std::unique_lock<std::mutex> lock(queue_mutex);
513 queue_condition.wait(lock, [
this]() {
514 return writer_should_stop || !queue.empty();
521 block = std::move(queue.front());
526void AudioRecorder::WriterLoop()
528 int64_t expected_next_sample = -1;
531 if (!PopBlock(block)) {
532 if (writer_should_stop) {
538 std::vector<AudioWaveformChunk> waveform_chunks;
541 std::lock_guard<std::mutex> lock(state_mutex);
542 if (waveform_accumulator) {
543 waveform_chunks = waveform_accumulator->ProcessBlock(block);
545 waveform_cb = waveform_callback;
549 for (
const auto& chunk : waveform_chunks) {
555 while (expected_next_sample >= 0 && block.
first_sample > expected_next_sample) {
556 const int64_t missing_samples = block.
first_sample - expected_next_sample;
557 const int silence_samples =
static_cast<int>(std::min<int64_t>(
565 std::vector<float>(silence_samples, 0.0f));
569 next_frame_number++));
570 expected_next_sample += silence_samples;
576 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