forked from roc-streaming/roc-toolkit
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
503 additions
and
50 deletions.
There are no files selected for viewing
40 changes: 40 additions & 0 deletions
40
src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_backend.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
#include <stdio.h> | ||
|
||
#include "roc_core/log.h" | ||
#include "roc_core/macro_helpers.h" | ||
#include "roc_core/scoped_lock.h" | ||
#include "roc_core/scoped_ptr.h" | ||
#include "roc_sndio/sndfile_backend.h" | ||
#include "roc_sndio/sndfile_sink.h" | ||
#include "roc_sndio/sndfile_source.h" | ||
|
||
namespace roc { | ||
namespace sndio { | ||
|
||
namespace { | ||
|
||
|
||
|
||
} | ||
|
||
SndfileBackend::SndfileBackend() : first_created_(false) { | ||
roc_log(LogDebug, "sndfile backend: initializing"); | ||
} | ||
|
||
void SndfileBackend::set_frame_size(core::nanoseconds_t frame_length, const audio::SampleSpec& sample_spec){ | ||
size_t size = sample_spec.ns_2_samples_overall(frame_length); | ||
|
||
if (first_created_) { | ||
roc_panic( | ||
"sox backend:" | ||
" set_frame_size() can be called only before creating first source or sink"); | ||
} | ||
} | ||
|
||
void SndfileBackend::discover_drivers(core::Array<DriverInfo, MaxDrivers>& driver_list){ | ||
|
||
} | ||
|
||
|
||
} | ||
} |
53 changes: 53 additions & 0 deletions
53
src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_backend.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
/* | ||
* Copyright (c) 2023 Roc Streaming authors | ||
* | ||
* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
|
||
//! @file roc_sndio/target_sndfile/roc_sndio/sndfile_backend.h | ||
//! @brief SndFile backend. | ||
|
||
#ifndef ROC_SNDIO_SNDFILE_BACKEND_H_ | ||
#define ROC_SNDIO_SNDFILE_BACKEND_H_ | ||
|
||
#include <sndfile.h> | ||
|
||
#include "roc_audio/sample_spec.h" | ||
#include "roc_core/noncopyable.h" | ||
#include "roc_sndio/ibackend.h" | ||
|
||
namespace roc { | ||
namespace sndio { | ||
|
||
//! SoX backend. | ||
class SndfileBackend : public IBackend, core::NonCopyable<> { | ||
public: | ||
SndfileBackend(); | ||
|
||
//! Set internal SndFile frame size. | ||
//! @remarks | ||
//! Number of samples for all channels. | ||
void set_frame_size(core::nanoseconds_t frame_length, | ||
const audio::SampleSpec& sample_spec); | ||
|
||
//! Append supported drivers to the list. | ||
virtual void discover_drivers(core::Array<DriverInfo, MaxDrivers>& driver_list); | ||
|
||
//! Create and open a sink or source. | ||
virtual IDevice* open_device(DeviceType device_type, | ||
DriverType driver_type, | ||
const char* driver, | ||
const char* path, | ||
const Config& config, | ||
core::IArena& arena); | ||
|
||
private: | ||
bool first_created_; | ||
}; | ||
|
||
} // namespace sndio | ||
} // namespace roc | ||
|
||
#endif // ROC_SNDIO_SNDFILE_BACKEND_H_ |
255 changes: 255 additions & 0 deletions
255
src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_sink.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,255 @@ | ||
/* | ||
* Copyright (c) 2023 Roc Streaming authors | ||
* | ||
* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
|
||
|
||
#include "roc_core/log.h" | ||
#include "roc_core/panic.h" | ||
#include "roc_sndio/backend_map.h" | ||
#include "roc_sndio/sndfile_sink.h" | ||
|
||
namespace roc { | ||
namespace sndio { | ||
|
||
SndfileSink::SndfileSink(core::IArena& arena, const Config& config) | ||
: sndfile_output_(NULL) | ||
, buffer_(arena) | ||
, buffer_size_(0) | ||
, is_file_(false) | ||
, valid_(false) { | ||
BackendMap::instance(); | ||
|
||
if (config.sample_spec.num_channels() == 0) { | ||
roc_log(LogError, "sndfile sink: # of channels is zero"); | ||
return; | ||
} | ||
|
||
if (config.latency != 0) { | ||
roc_log(LogError, "sndfile sink: setting io latency not supported by sndfile backend"); | ||
return; | ||
} | ||
|
||
frame_length_ = config.frame_length; | ||
sample_spec_ = config.sample_spec; | ||
|
||
if (frame_length_ == 0) { | ||
roc_log(LogError, "sndfile sink: frame length is zero"); | ||
return; | ||
} | ||
|
||
memset(&sf_info_out_, 0, sizeof(sf_info_out_)); | ||
sf_info_out_->samplerate = (int)config.sample_spec.sample_rate(); | ||
sf_info_out_->channels = (int)config.sample_spec.num_channels(); | ||
sf_info_out_->format = SF_FORMAT_PCM_32; | ||
|
||
valid_ = true; | ||
} | ||
|
||
SndfileSink::~SndfileSink() { | ||
close_(); | ||
} | ||
|
||
bool SndfileSink::is_valid() const { | ||
return valid_; | ||
} | ||
|
||
bool SndfileSink::open(const char* driver, const char* path) { | ||
roc_panic_if(!valid_); | ||
|
||
roc_log(LogDebug, "sndfile sink: opening: driver=%s path=%s", driver, path); | ||
|
||
if (buffer_.size() != 0 || sndfile_output_) { | ||
roc_panic("sndfile sink: can't call open() more than once"); | ||
} | ||
|
||
if (!open_(driver, path)) { | ||
return false; | ||
} | ||
|
||
if (!setup_buffer_()) { | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
DeviceType SndfileSink::type() const { | ||
return DeviceType_Sink; | ||
} | ||
|
||
DeviceState SndfileSink::state() const { | ||
return DeviceState_Active; | ||
} | ||
|
||
void SndfileSink::pause() { | ||
// no-op | ||
} | ||
|
||
bool SndfileSink::resume() { | ||
return true; | ||
} | ||
|
||
bool SndfileSink::restart() { | ||
return true; | ||
} | ||
|
||
audio::SampleSpec SndfileSink::sample_spec() const { | ||
roc_panic_if(!valid_); | ||
|
||
if (!sndfile_output_) { | ||
roc_panic("sndfile sink: sample_rate(): non-open output file or device"); | ||
} | ||
|
||
if (sf_info_out_->channels == 1) { | ||
return audio::SampleSpec(size_t(sf_info_out_->samplerate), audio::ChanLayout_Surround, | ||
audio::ChanOrder_Smpte, audio::ChanMask_Surround_Mono); | ||
} | ||
|
||
if (sf_info_out_->channels == 2) { | ||
return audio::SampleSpec(size_t(sf_info_out_->samplerate), audio::ChanLayout_Surround, | ||
audio::ChanOrder_Smpte, audio::ChanMask_Surround_Stereo); | ||
} | ||
|
||
roc_panic("sndfile sink: unsupported channel count"); | ||
} | ||
|
||
core::nanoseconds_t SndfileSink::latency() const { | ||
roc_panic_if(!valid_); | ||
|
||
if (!sndfile_output_) { | ||
roc_panic("sndfile sink: latency(): non-open output file or device"); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
bool SndfileSink::has_latency() const { | ||
roc_panic_if(!valid_); | ||
|
||
if (!sndfile_output_) { | ||
roc_panic("sndfile sink: has_latency(): non-open output file or device"); | ||
} | ||
|
||
return false; | ||
} | ||
|
||
bool SndfileSink::has_clock() const { | ||
roc_panic_if(!valid_); | ||
|
||
if (!sndfile_output_) { | ||
roc_panic("sndfile sink: has_clock(): non-open output file or device"); | ||
} | ||
|
||
return !is_file_; | ||
} | ||
|
||
void SndfileSink::write(audio::Frame& frame) { | ||
roc_panic_if(!valid_); | ||
|
||
const audio::sample_t* frame_data = frame.samples(); | ||
size_t frame_size = frame.num_samples(); | ||
|
||
int32_t* buffer_data = buffer_.data(); | ||
size_t buffer_pos = 0; | ||
|
||
|
||
|
||
while (frame_size > 0) { | ||
for (; buffer_pos < buffer_size_ && frame_size > 0; buffer_pos++) { | ||
buffer_data[buffer_pos] = (int32_t)frame_data[buffer_pos]; //?? | ||
frame_data++; | ||
frame_size--; | ||
} | ||
|
||
if (buffer_pos == buffer_size_) { | ||
write_(buffer_data, buffer_pos); | ||
buffer_pos = 0; | ||
} | ||
} | ||
|
||
write_(buffer_data, buffer_pos); | ||
} | ||
|
||
bool SndfileSink::setup_buffer_() { | ||
buffer_size_ = sample_spec_.ns_2_samples_overall(frame_length_); | ||
if (buffer_size_ == 0) { | ||
roc_log(LogError, "sndfile sink: buffer size is zero"); | ||
return false; | ||
} | ||
if (!buffer_.resize(buffer_size_)) { | ||
roc_log(LogError, "sndfile sink: can't allocate sample buffer"); | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
bool SndfileSink::open_(const char* driver, const char* path) { | ||
sndfile_output_ = sf_open(path, SFM_WRITE, sf_info_out_); | ||
if (!sndfile_output_) { | ||
roc_log(LogDebug, "sndfile sink: can't open: driver=%s path=%s", driver, path); | ||
return false; | ||
} | ||
|
||
is_file_ = true; | ||
|
||
unsigned long in_rate = (unsigned long)sf_info_out_->samplerate; | ||
unsigned long out_rate = (unsigned long)sf_info_out_->samplerate; | ||
|
||
if (in_rate != 0 && in_rate != out_rate) { | ||
roc_log(LogError, | ||
"sndfile sink:" | ||
" can't open output file or device with the required sample rate:" | ||
" required_by_output=%lu requested_by_user=%lu", | ||
out_rate, in_rate); | ||
return false; | ||
} | ||
|
||
sample_spec_.set_sample_rate((unsigned long)sf_info_out_->samplerate); | ||
|
||
/* | ||
roc_log(LogInfo, | ||
"sndfile sink:" | ||
" opened: bits=%lu out_rate=%lu in_rate=%lu ch=%lu is_file=%d", | ||
(unsigned long)sndfile_output_->encoding.bits_per_sample, out_rate, in_rate, | ||
(unsigned long)sndfile_output_->signal.channels, (int)is_file_); | ||
*/ | ||
|
||
roc_log(LogInfo, | ||
"sndfile sink:" | ||
" out_rate=%lu in_rate=%lu ch=%lu is_file=%d", | ||
out_rate, in_rate, | ||
(unsigned long)sf_info_out_->channels, (int)is_file_); | ||
|
||
return true; | ||
} | ||
|
||
void SndfileSink::write_(const int32_t * samples, size_t n_samples) { | ||
if (n_samples > 0) { | ||
if (sf_writef_int(sndfile_output_, samples, (sf_count_t)n_samples) != (sf_count_t)n_samples) { | ||
roc_log(LogError, "sndfile sink: failed to write output buffer"); | ||
} | ||
} | ||
} | ||
|
||
void SndfileSink::close_() { | ||
if (!sndfile_output_) { | ||
return; | ||
} | ||
|
||
roc_log(LogDebug, "sndfile sink: closing output"); | ||
|
||
int err = sf_close(sndfile_output_); | ||
if (err != 0) { | ||
roc_panic("sndfile sink: can't close output: %s", sf_error_number(err)); | ||
} | ||
|
||
sndfile_output_ = NULL; | ||
} | ||
|
||
} // namespace sndio | ||
} // namespace roc |
Oops, something went wrong.