diff --git a/src/internal_modules/roc_audio/sample_spec.cpp b/src/internal_modules/roc_audio/sample_spec.cpp index 214c5b86f..0dd50c48d 100644 --- a/src/internal_modules/roc_audio/sample_spec.cpp +++ b/src/internal_modules/roc_audio/sample_spec.cpp @@ -147,6 +147,10 @@ bool SampleSpec::is_empty() const { && sample_rate_ == 0 && channel_set_.num_channels() == 0; } +bool SampleSpec::is_pcm() const { + return sample_fmt_ == SampleFormat_Pcm && pcm_fmt_ != PcmFormat_Invalid; +} + bool SampleSpec::is_raw() const { return sample_fmt_ == SampleFormat_Pcm && get_pcm_portable_format(pcm_fmt_) == get_pcm_portable_format(Sample_RawFormat); diff --git a/src/internal_modules/roc_audio/sample_spec.h b/src/internal_modules/roc_audio/sample_spec.h index 99450ae07..cd3b48762 100644 --- a/src/internal_modules/roc_audio/sample_spec.h +++ b/src/internal_modules/roc_audio/sample_spec.h @@ -71,6 +71,12 @@ class SampleSpec { //! Check if sample spec has a zero rate, empty channel set, and invalid_format. bool is_empty() const; + //! Check if samples are in PCM format. + //! @returns + //! true if sample_format() is SampleFormat_Pcm and pcm_format() + //! is anything except PcmFormat_Invalid. + bool is_pcm() const; + //! Check if samples are in raw format. //! @returns //! true if sample_format() is SampleFormat_Pcm and pcm_format() diff --git a/src/internal_modules/roc_sndio/backend_dispatcher.cpp b/src/internal_modules/roc_sndio/backend_dispatcher.cpp index 37607c874..c3f314a71 100644 --- a/src/internal_modules/roc_sndio/backend_dispatcher.cpp +++ b/src/internal_modules/roc_sndio/backend_dispatcher.cpp @@ -225,10 +225,12 @@ status::StatusCode BackendDispatcher::open_default_device_(DeviceType device_typ return code; } - roc_log(LogDebug, - "backend dispatcher: got error from driver:" - " driver=%s status=%s", - driver_info.name, status::code_to_str(code)); + if (code != status::StatusNoDriver) { + roc_log(LogDebug, + "backend dispatcher: got error from driver:" + " driver=%s status=%s", + driver_info.name, status::code_to_str(code)); + } } roc_log(LogError, "backend dispatcher: failed to open default %s: status=%s", @@ -265,10 +267,12 @@ status::StatusCode BackendDispatcher::open_device_(DeviceType device_type, return code; } - roc_log(LogDebug, - "backend dispatcher: got error from driver:" - " driver=%s status=%s", - driver_info.name, status::code_to_str(code)); + if (code != status::StatusNoDriver) { + roc_log(LogDebug, + "backend dispatcher: got error from driver:" + " driver=%s status=%s", + driver_info.name, status::code_to_str(code)); + } } } else { for (size_t n = 0; n < BackendMap::instance().num_backends(); n++) { @@ -281,10 +285,12 @@ status::StatusCode BackendDispatcher::open_device_(DeviceType device_type, return code; } - roc_log(LogDebug, - "backend dispatcher: got error from backend:" - " backend=%s status=%s", - backend.name(), status::code_to_str(code)); + if (code != status::StatusNoDriver) { + roc_log(LogDebug, + "backend dispatcher: got error from backend:" + " backend=%s status=%s", + backend.name(), status::code_to_str(code)); + } } } diff --git a/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_backend.cpp b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_backend.cpp index b6d73d189..3265485da 100644 --- a/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_backend.cpp +++ b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_backend.cpp @@ -9,9 +9,9 @@ #include "roc_sndio/sndfile_backend.h" #include "roc_core/log.h" #include "roc_core/scoped_ptr.h" -#include "roc_sndio/sndfile_extension_table.h" #include "roc_sndio/sndfile_sink.h" #include "roc_sndio/sndfile_source.h" +#include "roc_sndio/sndfile_tables.h" #include "roc_status/code_to_str.h" namespace roc { @@ -45,10 +45,11 @@ void SndfileBackend::discover_drivers(core::Array& drive const char* driver = format_info.extension; - for (size_t map_index = 0; map_index < ROC_ARRAY_SIZE(file_type_map); + for (size_t map_index = 0; map_index < ROC_ARRAY_SIZE(sndfile_driver_remap); map_index++) { - if (file_type_map[map_index].format_id == format_info.format) { - driver = file_type_map[map_index].driver_name; + if ((sndfile_driver_remap[map_index].format_mask & SF_FORMAT_TYPEMASK) + == format_info.format) { + driver = sndfile_driver_remap[map_index].driver_name; } } diff --git a/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_extension_table.cpp b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_extension_table.cpp deleted file mode 100644 index d0c6041e7..000000000 --- a/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_extension_table.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2024 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_sndio/sndfile_extension_table.h" -#include "roc_core/macro_helpers.h" - -#include - -namespace roc { -namespace sndio { - -FileMap file_type_map[ROC_ARRAY_SIZE(file_type_map)] = { - { SF_FORMAT_MAT4, "mat4", NULL }, // - { SF_FORMAT_MAT5, "mat5", NULL }, // - { SF_FORMAT_WAV, "wav", "wav" }, // - { SF_FORMAT_NIST, "nist", NULL }, // - { SF_FORMAT_WAVEX, "wavex", NULL }, // -}; - -} // namespace sndio -} // namespace roc diff --git a/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_extension_table.h b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_extension_table.h deleted file mode 100644 index bc1f09b55..000000000 --- a/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_extension_table.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2024 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_extension_table.h -//! @brief Sndfile driver map. - -#ifndef ROC_SNDIO_SNDFILE_EXTENSION_TABLE_H_ -#define ROC_SNDIO_SNDFILE_EXTENSION_TABLE_H_ - -namespace roc { -namespace sndio { - -//! Sndfile driver map. -struct FileMap { - //! SF_FORMAT ID corresponding to the enum value in sndfile.h - int format_id; - //! Name of driver mapped to SF_FORMAT - const char* driver_name; - //! File extension associated with driver and SF_FORMAT if it exists. - const char* file_extension; -}; - -//! Declare the file_type_map as extern -extern FileMap file_type_map[5]; - -} // namespace sndio -} // namespace roc - -#endif // ROC_SNDIO_SNDFILE_EXTENSION_TABLE_H_ diff --git a/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_sink.cpp b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_sink.cpp index b9f8b023c..4857f6892 100644 --- a/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_sink.cpp +++ b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_sink.cpp @@ -11,78 +11,106 @@ #include "roc_core/log.h" #include "roc_core/panic.h" #include "roc_sndio/backend_map.h" -#include "roc_sndio/sndfile_extension_table.h" +#include "roc_sndio/sndfile_tables.h" namespace roc { namespace sndio { namespace { -bool map_to_sub_format(SF_INFO& file_info_, int format_enum) { - // Provides the minimum number of sub formats needed to support all - // possible major formats - int high_to_low_sub_formats[] = { - SF_FORMAT_PCM_24, - SF_FORMAT_PCM_16, - SF_FORMAT_DPCM_16, - }; - - for (size_t format_attempt = 0; - format_attempt < ROC_ARRAY_SIZE(high_to_low_sub_formats); format_attempt++) { - file_info_.format = format_enum | high_to_low_sub_formats[format_attempt]; - - if (sf_format_check(&file_info_)) { - return true; - } +int pcm_2_sf(audio::PcmFormat fmt) { + switch (fmt) { + case audio::PcmFormat_UInt8: + case audio::PcmFormat_UInt8_Le: + case audio::PcmFormat_UInt8_Be: + return SF_FORMAT_PCM_U8 | SF_ENDIAN_FILE; + + case audio::PcmFormat_SInt8: + case audio::PcmFormat_SInt8_Le: + case audio::PcmFormat_SInt8_Be: + return SF_FORMAT_PCM_S8 | SF_ENDIAN_FILE; + + case audio::PcmFormat_SInt16: + return SF_FORMAT_PCM_16 | SF_ENDIAN_FILE; + case audio::PcmFormat_SInt16_Le: + return SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE; + case audio::PcmFormat_SInt16_Be: + return SF_FORMAT_PCM_16 | SF_ENDIAN_BIG; + + case audio::PcmFormat_SInt24: + return SF_FORMAT_PCM_24 | SF_ENDIAN_FILE; + case audio::PcmFormat_SInt24_Le: + return SF_FORMAT_PCM_24 | SF_ENDIAN_LITTLE; + case audio::PcmFormat_SInt24_Be: + return SF_FORMAT_PCM_24 | SF_ENDIAN_BIG; + + case audio::PcmFormat_SInt32: + return SF_FORMAT_PCM_32 | SF_ENDIAN_FILE; + case audio::PcmFormat_SInt32_Le: + return SF_FORMAT_PCM_32 | SF_ENDIAN_LITTLE; + case audio::PcmFormat_SInt32_Be: + return SF_FORMAT_PCM_32 | SF_ENDIAN_BIG; + + case audio::PcmFormat_Float32: + return SF_FORMAT_FLOAT | SF_ENDIAN_FILE; + case audio::PcmFormat_Float32_Le: + return SF_FORMAT_FLOAT | SF_ENDIAN_LITTLE; + case audio::PcmFormat_Float32_Be: + return SF_FORMAT_FLOAT | SF_ENDIAN_BIG; + + case audio::PcmFormat_Float64: + return SF_FORMAT_DOUBLE | SF_ENDIAN_FILE; + case audio::PcmFormat_Float64_Le: + return SF_FORMAT_DOUBLE | SF_ENDIAN_LITTLE; + case audio::PcmFormat_Float64_Be: + return SF_FORMAT_DOUBLE | SF_ENDIAN_BIG; + + default: + break; } - return false; + return 0; } -bool map_to_sndfile(const char** driver, const char* path, SF_INFO& file_info_) { +bool select_major_format(SF_INFO& file_info, const char** driver, const char* path) { roc_panic_if(!driver); roc_panic_if(!path); const char* file_extension = NULL; - const char* dot = strrchr(path, '.'); - if (!dot || dot == path) { - return false; + const char* dot = strrchr(path, '.'); + if (dot && dot != path) { + file_extension = dot; } - file_extension = dot + 1; + int format_mask = 0; - int format_enum = 0; - - // First try to select format by iterating through file_map_index. - if (*driver == NULL) { + // First try to select format by iterating through sndfile_driver_remap. + if (*driver != NULL) { + // If driver is non-NULL, match by driver name. + for (size_t idx = 0; idx < ROC_ARRAY_SIZE(sndfile_driver_remap); idx++) { + if (strcmp(*driver, sndfile_driver_remap[idx].driver_name) == 0) { + format_mask = sndfile_driver_remap[idx].format_mask; + break; + } + } + } else if (file_extension != NULL) { // If driver is NULL, match by file extension. - for (size_t file_map_index = 0; file_map_index < ROC_ARRAY_SIZE(file_type_map); - file_map_index++) { - if (file_type_map[file_map_index].file_extension != NULL) { - if (strcmp(file_extension, file_type_map[file_map_index].file_extension) + for (size_t idx = 0; idx < ROC_ARRAY_SIZE(sndfile_driver_remap); idx++) { + if (sndfile_driver_remap[idx].file_extension != NULL) { + if (strcmp(file_extension, sndfile_driver_remap[idx].file_extension) == 0) { - format_enum = file_type_map[file_map_index].format_id; + format_mask = sndfile_driver_remap[idx].format_mask; *driver = file_extension; break; } } } - } else { - // If driver is non-NULL, match by driver name. - for (size_t file_map_index = 0; file_map_index < ROC_ARRAY_SIZE(file_type_map); - file_map_index++) { - if (strcmp(*driver, file_type_map[file_map_index].driver_name) == 0) { - format_enum = file_type_map[file_map_index].format_id; - break; - } - } } // Then try to select format by iterating through all sndfile formats. - if (format_enum == 0) { - SF_FORMAT_INFO info; - int major_count = 0, format_index = 0; + if (format_mask == 0) { + int major_count = 0; if (int errnum = sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &major_count, sizeof(int))) { roc_panic( @@ -90,7 +118,9 @@ bool map_to_sndfile(const char** driver, const char* path, SF_INFO& file_info_) sf_error_number(errnum)); } - for (format_index = 0; format_index < major_count; format_index++) { + for (int format_index = 0; format_index < major_count; format_index++) { + SF_FORMAT_INFO info; + memset(&info, 0, sizeof(info)); info.format = format_index; if (int errnum = sf_command(NULL, SFC_GET_FORMAT_MAJOR, &info, sizeof(info))) { @@ -98,38 +128,82 @@ bool map_to_sndfile(const char** driver, const char* path, SF_INFO& file_info_) sf_error_number(errnum)); } - if (*driver == NULL) { + if (*driver != NULL) { + // If driver is non-NULL, match by driver name. + if (strcmp(info.extension, *driver) == 0) { + format_mask = info.format; + break; + } + } else if (file_extension != NULL) { // If driver is NULL, match by file extension. if (strcmp(info.extension, file_extension) == 0) { - format_enum = info.format; + format_mask = info.format; *driver = file_extension; break; } - } else { - // If driver is non-NULL, match by driver name. - if (strcmp(info.extension, *driver) == 0) { - format_enum = info.format; - break; - } } } } - if (format_enum == 0) { + if (format_mask == 0) { + roc_log(LogDebug, + "sndfile sink: failed to detect major format: driver=%s path=%s", *driver, + path); return false; } - roc_log(LogDebug, "detected file format type '%s'", *driver); + file_info.format = format_mask; - file_info_.format = format_enum | file_info_.format; + return true; +} - if (sf_format_check(&file_info_)) { - // Format is supported as is. +bool select_sub_format(SF_INFO& file_info, + const char* driver, + const char* path, + int requested_subformat) { + roc_panic_if(!driver); + roc_panic_if(!path); + + const int format_mask = file_info.format; + + // User explicitly requested specific PCM format, map it to sub-format. + if (requested_subformat != 0) { + file_info.format = format_mask | requested_subformat; + + if (sf_format_check(&file_info)) { + return true; + } + + roc_log(LogError, + "sndfile sink: requested format not supported by driver:" + " driver=%s path=%s", + driver, path); + return false; + } + + // User did not request specific PCM format. + // First check if we can work without sub-format. + file_info.format = format_mask; + + if (sf_format_check(&file_info)) { return true; - } else { - // Format may be supported if combined with a subformat. - return map_to_sub_format(file_info_, format_enum); } + + // We can't work without sub-format and should choose one. + for (size_t idx = 0; idx < ROC_ARRAY_SIZE(sndfile_default_subformats); idx++) { + const int sub_format = sndfile_default_subformats[idx]; + + file_info.format = format_mask | sub_format; + + if (sf_format_check(&file_info)) { + return true; + } + } + + roc_log(LogDebug, "sndfile sink: failed to detect sub-format: driver=%s path=%s", + driver, path); + + return false; } } // namespace @@ -138,34 +212,48 @@ SndfileSink::SndfileSink(audio::FrameFactory& frame_factory, core::IArena& arena, const Config& config) : file_(NULL) + , requested_subformat_(0) , init_status_(status::NoStatus) { if (config.latency != 0) { - roc_log(LogError, - "sndfile sink: setting io latency not supported by sndfile backend"); + roc_log(LogError, "sndfile sink: setting io latency not supported by backend"); init_status_ = status::StatusBadConfig; return; } + if (config.sample_spec.sample_format() != audio::SampleFormat_Invalid + && config.sample_spec.sample_format() != audio::SampleFormat_Pcm) { + roc_log(LogError, "sndfile sink: requested format not supported by backend: %s", + audio::sample_spec_to_str(sample_spec_).c_str()); + init_status_ = status::StatusBadConfig; + return; + } + + if (config.sample_spec.is_pcm()) { + // Remember which format to use for file, if requested explicitly. + requested_subformat_ = pcm_2_sf(config.sample_spec.pcm_format()); + if (requested_subformat_ == 0) { + roc_log(LogError, + "sndfile sink: requested format not supported by backend: %s", + audio::sample_spec_to_str(config.sample_spec).c_str()); + init_status_ = status::StatusBadConfig; + return; + } + } + sample_spec_ = config.sample_spec; + // Always request raw samples from pipeline. + // If the user requested different format, we've remembered it above and will + // tell libsndfile to perform conversion to that format when writing file. + sample_spec_.set_sample_format(audio::SampleFormat_Pcm); + sample_spec_.set_pcm_format(audio::Sample_RawFormat); + sample_spec_.use_defaults(audio::Sample_RawFormat, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, audio::ChanMask_Surround_Stereo, 44100); - // TODO(gh-696): map format from sample_spec - if (!sample_spec_.is_raw()) { - roc_log(LogError, "sndfile sink: sample format can be only \"-\" or \"%s\"", - audio::pcm_format_to_str(audio::Sample_RawFormat)); - init_status_ = status::StatusBadConfig; - return; - } - memset(&file_info_, 0, sizeof(file_info_)); - file_info_.format = SF_FORMAT_PCM_32; - file_info_.channels = (int)sample_spec_.num_channels(); - file_info_.samplerate = (int)sample_spec_.sample_rate(); - init_status_ = status::StatusOK; } @@ -259,28 +347,34 @@ status::StatusCode SndfileSink::flush() { } status::StatusCode SndfileSink::open_(const char* driver, const char* path) { - if (!map_to_sndfile(&driver, path, file_info_)) { - roc_log(LogDebug, - "sndfile sink: map_to_sndfile():" - " cannot find valid subtype format for major format type"); + file_info_.channels = (int)sample_spec_.num_channels(); + file_info_.samplerate = (int)sample_spec_.sample_rate(); + + if (!select_major_format(file_info_, &driver, path)) { + roc_log(LogDebug, "sndfile sink: can't detect file format: driver=%s path=%s", + driver, path); + return status::StatusErrFile; + } + + if (!select_sub_format(file_info_, driver, path, requested_subformat_)) { + roc_log(LogDebug, "sndfile sink: can't detect file format: driver=%s path=%s", + driver, path); return status::StatusErrFile; } file_ = sf_open(path, SFM_WRITE, &file_info_); if (!file_) { - roc_log(LogDebug, "sndfile sink: %s, can't open: driver=%s path=%s", + roc_log(LogDebug, "sndfile sink: %s, can't open file: driver=%s path=%s", sf_strerror(file_), driver, path); return status::StatusErrFile; } - if (sf_command(file_, SFC_SET_UPDATE_HEADER_AUTO, NULL, SF_TRUE) == 0) { - roc_log(LogDebug, - "sndfile sink: sf_command(SFC_SET_UPDATE_HEADER_AUTO) returned false"); + if (!sf_command(file_, SFC_SET_UPDATE_HEADER_AUTO, NULL, SF_TRUE)) { + roc_log(LogDebug, "sndfile sink: %s, can't configure driver: driver=%s path=%s", + sf_strerror(file_), driver, path); return status::StatusErrFile; } - sample_spec_.set_sample_rate((size_t)file_info_.samplerate); - roc_log(LogInfo, "sndfile sink: opened output file: %s", audio::sample_spec_to_str(sample_spec_).c_str()); diff --git a/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_sink.h b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_sink.h index 85d1833c0..fa131dda6 100644 --- a/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_sink.h +++ b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_sink.h @@ -80,6 +80,7 @@ class SndfileSink : public ISink, public core::NonCopyable<> { SF_INFO file_info_; audio::SampleSpec sample_spec_; + int requested_subformat_; status::StatusCode init_status_; }; diff --git a/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_source.cpp b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_source.cpp index 22ab82846..f749bda82 100644 --- a/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_source.cpp +++ b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_source.cpp @@ -24,14 +24,13 @@ SndfileSource::SndfileSource(audio::FrameFactory& frame_factory, , path_(arena) , init_status_(status::NoStatus) { if (config.latency != 0) { - roc_log(LogError, - "sndfile source: setting io latency not supported by sndfile backend"); + roc_log(LogError, "sndfile source: setting io latency not supported by backend"); init_status_ = status::StatusBadConfig; return; } if (!config.sample_spec.is_empty()) { - roc_log(LogError, "sndfile source: setting io encoding not supported"); + roc_log(LogError, "sndfile source: setting io encoding not supported by backend"); init_status_ = status::StatusBadConfig; return; } diff --git a/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_tables.cpp b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_tables.cpp new file mode 100644 index 000000000..b2be4c8c7 --- /dev/null +++ b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_tables.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024 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_sndio/sndfile_tables.h" +#include "roc_core/macro_helpers.h" + +#include + +namespace roc { +namespace sndio { + +namespace { + +// These constants are not defined in older versions of libsndfile, +// so we define them explicitly. If libsndfile doesn't actually +// support them, we'll detect it at run-time. +enum { + // ogg + SF_FORMAT_OGG = 0x200000, + SF_FORMAT_VORBIS = 0x0060, + SF_FORMAT_OPUS = 0x0064, + // mpeg + SF_FORMAT_MPEG = 0x230000, + SF_FORMAT_MPEG_LAYER_I = 0x0080, + SF_FORMAT_MPEG_LAYER_II = 0x0081, + SF_FORMAT_MPEG_LAYER_III = 0x0082, +}; + +} // namespace + +SndfileDriverInfo sndfile_driver_remap[ROC_ARRAY_SIZE(sndfile_driver_remap)] = { + { "ogg", ".ogg", SF_FORMAT_OGG }, + { "mp1", ".mp1", SF_FORMAT_MPEG | SF_FORMAT_MPEG_LAYER_I }, + { "mp2", ".mp2", SF_FORMAT_MPEG | SF_FORMAT_MPEG_LAYER_II }, + { "mp3", ".mp3", SF_FORMAT_MPEG | SF_FORMAT_MPEG_LAYER_III }, + { "mat4", NULL, SF_FORMAT_MAT4 }, + { "mat5", NULL, SF_FORMAT_MAT5 }, + { "wav", ".wav", SF_FORMAT_WAV }, + { "nist", NULL, SF_FORMAT_NIST }, + { "wavex", NULL, SF_FORMAT_WAVEX }, +}; + +int sndfile_default_subformats[ROC_ARRAY_SIZE(sndfile_default_subformats)] = { + // at least one of the PCM sub-formats is supported by almost every major format + SF_FORMAT_PCM_24, + SF_FORMAT_PCM_16, + SF_FORMAT_DPCM_16, + // for caf + SF_FORMAT_ULAW, + SF_FORMAT_ALAW, + // for ogg + SF_FORMAT_VORBIS, + SF_FORMAT_OPUS, +}; + +} // namespace sndio +} // namespace roc diff --git a/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_tables.h b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_tables.h new file mode 100644 index 000000000..266268025 --- /dev/null +++ b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_tables.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024 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_tables.h +//! @brief Sndfile tables. + +#ifndef ROC_SNDIO_SNDFILE_TABLES_H_ +#define ROC_SNDIO_SNDFILE_TABLES_H_ + +namespace roc { +namespace sndio { + +//! Sndfile driver meta-data. +struct SndfileDriverInfo { + //! Name of the driver. + const char* driver_name; + //! File extension associated with driver. + const char* file_extension; + //! SF_FORMAT corresponding to the driver. + int format_mask; +}; + +//! Table of sndfile drivers with re-mapped names or file extensions. +//! This table should be checked when we need to guess format from driver +//! name or file extension. +extern SndfileDriverInfo sndfile_driver_remap[9]; + +//! Table of sndfile sub-formats to try when no specific format requested. +//! This list provides the minimum number of sub-formats needed to support +//! all possible major formats. +extern int sndfile_default_subformats[7]; + +} // namespace sndio +} // namespace roc + +#endif // ROC_SNDIO_SNDFILE_TABLES_H_ diff --git a/src/internal_modules/roc_sndio/wav_sink.cpp b/src/internal_modules/roc_sndio/wav_sink.cpp index c78ca74c6..35b7a554d 100644 --- a/src/internal_modules/roc_sndio/wav_sink.cpp +++ b/src/internal_modules/roc_sndio/wav_sink.cpp @@ -22,7 +22,7 @@ WavSink::WavSink(audio::FrameFactory& frame_factory, : output_file_(NULL) , init_status_(status::NoStatus) { if (config.latency != 0) { - roc_log(LogError, "wav sink: setting io latency not supported"); + roc_log(LogError, "wav sink: setting io latency not supported by backend"); init_status_ = status::StatusBadConfig; return; } diff --git a/src/internal_modules/roc_sndio/wav_source.cpp b/src/internal_modules/roc_sndio/wav_source.cpp index c65664630..0a33add55 100644 --- a/src/internal_modules/roc_sndio/wav_source.cpp +++ b/src/internal_modules/roc_sndio/wav_source.cpp @@ -21,13 +21,13 @@ WavSource::WavSource(audio::FrameFactory& frame_factory, , eof_(false) , init_status_(status::NoStatus) { if (config.latency != 0) { - roc_log(LogError, "wav source: setting io latency not supported"); + roc_log(LogError, "wav source: setting io latency not supported by backend"); init_status_ = status::StatusBadConfig; return; } if (!config.sample_spec.is_empty()) { - roc_log(LogError, "wav source: setting io encoding not supported"); + roc_log(LogError, "wav source: setting io encoding not supported by backend"); init_status_ = status::StatusBadConfig; return; }