From bfbf13ff3cbd31ba7719a24a823cdc097a03826d Mon Sep 17 00:00:00 2001 From: Victor Gaydov <victor@enise.org> Date: Sat, 24 Aug 2024 15:40:38 +0300 Subject: [PATCH] [wip] [api] [cli] gh-608: Formats and sub-formats API --- TODO CLI --- 1. --io-encoding option now has form <format>@<subformat>/<rate>/<channels> E.g.: pcm@s16/44100/stereo (whether sub-format is allowed or required depends on format) 2. --input-format/--output-format options are removed, their function is now handled by <format> field of --io-encoding E.g.: --output file://- --io-encoding wav@s24/48000/stereo or --input file://- --io-encoding wav/-/- 3. --print-supported is updated to discover and list all available sub-formats (divided into logical groups). Docs ---- TODO Internals --------- Introduce concept of format (e.g. PCM, FLAC) and sub-format (e.g. s16). Support formats and sub-formats in all sndio backends. roc_audio: - SampleFormat => Format - Formats: Format_Pcm, Format_Wav, Format_Custom - PcmFormat => PcmSubformat - SampleSpec: set_format(), set_custom_format(), set_pcm_subformat(), set_custom_subformat() - SampleSpec: is_valid() => is_complete() - Sample_RawFormat => PcmSubformat_Raw roc_sndio: - File formats are now *not* drivers. All file formats are handles by special "file://" driver, i.e. URI scheme is now always equal to driver. - Supported file formats and sub-formats are discovered from backends separately from drivers. - For discovery, we use DriverInfo and FormatInfo structs. - IoConfig is empty by default; frame length and latency are zero. - Each backend may use its own defaults for IoConfig. - We can retrieve actually selected config using sample_spec() and frame_length() methods of IDevice. - SoxBackend: remove file support, allow only devices (now we use sndfile for files) - SoxBackend: support PCM format and sub-formats - PulseaudioBackend: support PCM format and sub-formats - SndfileBackend: support formats and sub-formats, map to sndfile major type and sub-type - WavBackend: support WAV format and PCM sub-formats - Unification of sndio backends - Bug-fixes in format handling in sndfile backend --- .fmtignore | 2 +- SConstruct | 2 +- docs/man/roc-copy.1 | 11 +- docs/man/roc-recv.1 | 28 +- docs/man/roc-send.1 | 23 +- docs/sphinx/manuals/roc_copy.rst | 7 +- docs/sphinx/manuals/roc_recv.rst | 32 +- docs/sphinx/manuals/roc_send.rst | 29 +- src/internal_modules/roc_audio/beep_plc.cpp | 4 +- .../roc_audio/builtin_resampler.cpp | 4 +- .../roc_audio/channel_mapper_reader.cpp | 4 +- .../roc_audio/channel_mapper_writer.cpp | 4 +- .../roc_audio/channel_set_format.cpp | 60 +- .../roc_audio/decimation_resampler.cpp | 4 +- .../roc_audio/depacketizer.cpp | 4 +- src/internal_modules/roc_audio/fanout.cpp | 3 +- src/internal_modules/roc_audio/format.cpp | 60 + src/internal_modules/roc_audio/format.h | 95 + src/internal_modules/roc_audio/mixer.cpp | 4 +- src/internal_modules/roc_audio/packetizer.cpp | 4 +- .../roc_audio/pcm_decoder.cpp | 2 +- .../roc_audio/pcm_encoder.cpp | 2 +- src/internal_modules/roc_audio/pcm_format.cpp | 1758 +++++++++-------- src/internal_modules/roc_audio/pcm_mapper.cpp | 14 +- src/internal_modules/roc_audio/pcm_mapper.h | 14 +- .../roc_audio/pcm_mapper_reader.cpp | 12 +- .../roc_audio/pcm_mapper_reader.h | 2 +- .../roc_audio/pcm_mapper_writer.cpp | 12 +- .../roc_audio/pcm_mapper_writer.h | 2 +- .../{pcm_format.h => pcm_subformat.h} | 220 ++- ...pcm_format_gen.py => pcm_subformat_gen.py} | 94 +- .../roc_audio/pcm_subformat_rw.h | 72 + src/internal_modules/roc_audio/plc_reader.cpp | 6 +- .../roc_audio/resampler_reader.cpp | 4 +- .../roc_audio/resampler_writer.cpp | 4 +- src/internal_modules/roc_audio/sample.cpp | 3 +- src/internal_modules/roc_audio/sample.h | 4 +- .../roc_audio/sample_format.cpp | 27 - .../roc_audio/sample_format.h | 39 - .../roc_audio/sample_spec.cpp | 346 +++- src/internal_modules/roc_audio/sample_spec.h | 142 +- .../roc_audio/sample_spec_format.cpp | 11 +- .../roc_audio/sample_spec_parse.rl | 124 +- .../roc_audio/speex_resampler.cpp | 4 +- src/internal_modules/roc_core/backtrace.h | 2 +- src/internal_modules/roc_core/string_list.cpp | 227 ++- src/internal_modules/roc_core/string_list.h | 54 +- .../roc_core/target_posix/roc_core/time.cpp | 4 +- .../roc_dbgio/print_supported.cpp | 213 +- .../target_posix/roc_dbgio/temp_file.cpp | 2 +- src/internal_modules/roc_pipeline/config.h | 2 +- .../roc_pipeline/receiver_loop.cpp | 6 + .../roc_pipeline/receiver_loop.h | 1 + .../roc_pipeline/receiver_session.cpp | 12 +- .../roc_pipeline/receiver_source.cpp | 8 +- .../roc_pipeline/receiver_source.h | 3 + .../roc_pipeline/sender_loop.cpp | 6 + .../roc_pipeline/sender_loop.h | 1 + .../roc_pipeline/sender_session.cpp | 12 +- .../roc_pipeline/sender_sink.cpp | 8 +- .../roc_pipeline/sender_sink.h | 3 + .../roc_pipeline/transcoder_sink.cpp | 12 +- .../roc_pipeline/transcoder_sink.h | 3 + .../roc_pipeline/transcoder_source.cpp | 12 +- .../roc_pipeline/transcoder_source.h | 3 + src/internal_modules/roc_rtp/encoding.cpp | 21 +- src/internal_modules/roc_rtp/encoding.h | 2 +- src/internal_modules/roc_rtp/encoding_map.cpp | 43 +- .../roc_sndio/backend_dispatcher.cpp | 267 ++- .../roc_sndio/backend_dispatcher.h | 24 +- .../roc_sndio/backend_map.cpp | 39 +- src/internal_modules/roc_sndio/backend_map.h | 12 +- src/internal_modules/roc_sndio/driver.cpp | 31 - src/internal_modules/roc_sndio/driver.h | 103 +- src/internal_modules/roc_sndio/ibackend.h | 17 +- src/internal_modules/roc_sndio/idevice.h | 4 + src/internal_modules/roc_sndio/io_config.h | 8 +- src/internal_modules/roc_sndio/io_pump.cpp | 77 +- src/internal_modules/roc_sndio/io_pump.h | 6 +- .../roc_sndio/pulseaudio_backend.cpp | 52 +- .../roc_sndio/pulseaudio_backend.h | 15 +- .../roc_sndio/pulseaudio_device.cpp | 170 +- .../roc_sndio/pulseaudio_device.h | 9 +- .../roc_sndio/sndfile_backend.cpp | 131 +- .../roc_sndio/sndfile_backend.h | 15 +- .../roc_sndio/sndfile_helpers.cpp | 349 ++++ .../roc_sndio/sndfile_helpers.h | 43 + .../target_sndfile/roc_sndio/sndfile_sink.cpp | 313 +-- .../target_sndfile/roc_sndio/sndfile_sink.h | 19 +- .../roc_sndio/sndfile_source.cpp | 119 +- .../target_sndfile/roc_sndio/sndfile_source.h | 16 +- .../roc_sndio/sndfile_tables.cpp | 33 +- .../target_sndfile/roc_sndio/sndfile_tables.h | 38 +- .../target_sox/roc_sndio/sox_backend.cpp | 241 +-- .../target_sox/roc_sndio/sox_backend.h | 18 +- .../target_sox/roc_sndio/sox_sink.cpp | 262 +-- .../roc_sndio/target_sox/roc_sndio/sox_sink.h | 23 +- .../target_sox/roc_sndio/sox_source.cpp | 274 +-- .../target_sox/roc_sndio/sox_source.h | 25 +- .../roc_sndio/wav_backend.cpp | 88 +- src/internal_modules/roc_sndio/wav_backend.h | 15 +- src/internal_modules/roc_sndio/wav_sink.cpp | 195 +- src/internal_modules/roc_sndio/wav_sink.h | 12 +- src/internal_modules/roc_sndio/wav_source.cpp | 154 +- src/internal_modules/roc_sndio/wav_source.h | 13 +- .../roc_status/code_to_str.cpp | 2 + src/internal_modules/roc_status/status_code.h | 12 +- .../examples/basic_receiver_pulseaudio.c | 3 +- .../examples/basic_receiver_wav_file.c | 3 +- .../examples/basic_sender_pulseaudio.c | 3 +- .../examples/basic_sender_sine_wave.c | 3 +- src/public_api/examples/plugin_plc.c | 3 +- .../examples/send_recv_1_sender_2_receivers.c | 6 +- .../examples/send_recv_2_senders_1_receiver.c | 6 +- src/public_api/examples/send_recv_multicast.c | 6 +- src/public_api/examples/send_recv_rtp.c | 6 +- .../examples/send_recv_rtp_rtcp_fec.c | 6 +- src/public_api/include/roc/config.h | 87 +- src/public_api/include/roc/plugin.h | 4 +- src/public_api/src/adapters.cpp | 212 +- src/public_api/src/adapters.h | 6 +- src/tests/public_api/test_context.cpp | 6 +- src/tests/public_api/test_helpers/context.h | 23 +- src/tests/public_api/test_helpers/utils.h | 2 - .../test_loopback_encoder_2_decoder.cpp | 19 +- .../test_loopback_sender_2_receiver.cpp | 180 +- src/tests/public_api/test_plugin_plc.cpp | 25 +- src/tests/public_api/test_receiver.cpp | 3 +- .../public_api/test_receiver_decoder.cpp | 6 +- src/tests/public_api/test_sender.cpp | 3 +- src/tests/public_api/test_sender_encoder.cpp | 3 +- .../roc_audio/test_channel_mapper_reader.cpp | 40 +- .../roc_audio/test_channel_mapper_writer.cpp | 28 +- src/tests/roc_audio/test_channel_set.cpp | 20 +- src/tests/roc_audio/test_depacketizer.cpp | 4 +- src/tests/roc_audio/test_fanout.cpp | 2 +- .../roc_audio/test_frame_encoder_decoder.cpp | 18 +- src/tests/roc_audio/test_freq_estimator.cpp | 2 +- src/tests/roc_audio/test_mixer.cpp | 18 +- src/tests/roc_audio/test_packetizer.cpp | 4 +- src/tests/roc_audio/test_pcm_mapper.cpp | 70 +- .../roc_audio/test_pcm_mapper_reader.cpp | 68 +- .../roc_audio/test_pcm_mapper_writer.cpp | 54 +- src/tests/roc_audio/test_pcm_samples.cpp | 12 +- src/tests/roc_audio/test_plc_reader.cpp | 34 +- src/tests/roc_audio/test_profiler.cpp | 2 +- src/tests/roc_audio/test_resampler.cpp | 46 +- src/tests/roc_audio/test_sample_spec.cpp | 443 +++-- .../test_samples/generate_samples.py | 2 +- .../roc_audio/test_samples/pcm_float32_be.h | 2 +- .../roc_audio/test_samples/pcm_float32_le.h | 2 +- .../roc_audio/test_samples/pcm_sint16_be.h | 2 +- .../roc_audio/test_samples/pcm_sint16_le.h | 2 +- .../roc_audio/test_samples/pcm_sint24_be.h | 2 +- .../roc_audio/test_samples/pcm_sint24_le.h | 2 +- .../roc_audio/test_samples/pcm_sint32_be.h | 2 +- .../roc_audio/test_samples/pcm_sint32_le.h | 2 +- .../roc_audio/test_samples/pcm_sint8_be.h | 2 +- .../roc_audio/test_samples/pcm_sint8_le.h | 2 +- .../roc_audio/test_samples/pcm_uint16_be.h | 2 +- .../roc_audio/test_samples/pcm_uint16_le.h | 2 +- .../roc_audio/test_samples/pcm_uint24_be.h | 2 +- .../roc_audio/test_samples/pcm_uint24_le.h | 2 +- .../roc_audio/test_samples/pcm_uint32_be.h | 2 +- .../roc_audio/test_samples/pcm_uint32_le.h | 2 +- .../roc_audio/test_samples/pcm_uint8_be.h | 2 +- .../roc_audio/test_samples/pcm_uint8_le.h | 2 +- .../roc_audio/test_samples/sample_info.h | 4 +- src/tests/roc_audio/test_watchdog.cpp | 2 +- src/tests/roc_core/test_string_list.cpp | 12 +- src/tests/roc_packet/test_delayed_reader.cpp | 2 +- .../bench_pipeline_loop_contention.cpp | 2 +- .../bench_pipeline_loop_peak_load.cpp | 2 +- .../roc_pipeline/test_helpers/mock_sink.h | 4 + .../roc_pipeline/test_helpers/mock_source.h | 4 + .../test_loopback_sink_2_source.cpp | 26 +- src/tests/roc_pipeline/test_pipeline_loop.cpp | 2 +- .../roc_pipeline/test_receiver_source.cpp | 44 +- src/tests/roc_pipeline/test_sender_sink.cpp | 44 +- .../roc_pipeline/test_transcoder_sink.cpp | 8 +- .../roc_pipeline/test_transcoder_source.cpp | 8 +- src/tests/roc_rtp/test_encoding.cpp | 26 +- src/tests/roc_rtp/test_encoding_map.cpp | 32 +- src/tests/roc_rtp/test_filter.cpp | 2 +- src/tests/roc_rtp/test_link_meter.cpp | 2 +- .../roc_rtp/test_timestamp_extractor.cpp | 4 +- src/tests/roc_rtp/test_timestamp_injector.cpp | 6 +- src/tests/roc_sndio/test_backend_sink.cpp | 136 -- src/tests/roc_sndio/test_backend_source.cpp | 231 --- src/tests/roc_sndio/test_helpers/mock_sink.h | 4 + .../roc_sndio/test_helpers/mock_source.h | 30 +- src/tests/roc_sndio/test_helpers/utils.h | 20 +- src/tests/roc_sndio/test_io_pump.cpp | 70 +- src/tests/roc_sndio/test_sinks.cpp | 429 ++++ src/tests/roc_sndio/test_sources.cpp | 499 +++++ src/tools/roc_copy/cmdline.ggo | 3 +- src/tools/roc_copy/main.cpp | 30 +- src/tools/roc_recv/cmdline.ggo | 5 +- src/tools/roc_recv/main.cpp | 76 +- src/tools/roc_send/cmdline.ggo | 1 - src/tools/roc_send/main.cpp | 44 +- 201 files changed, 6517 insertions(+), 4030 deletions(-) create mode 100644 src/internal_modules/roc_audio/format.cpp create mode 100644 src/internal_modules/roc_audio/format.h rename src/internal_modules/roc_audio/{pcm_format.h => pcm_subformat.h} (71%) rename src/internal_modules/roc_audio/{pcm_format_gen.py => pcm_subformat_gen.py} (92%) create mode 100644 src/internal_modules/roc_audio/pcm_subformat_rw.h delete mode 100644 src/internal_modules/roc_audio/sample_format.cpp delete mode 100644 src/internal_modules/roc_audio/sample_format.h delete mode 100644 src/internal_modules/roc_sndio/driver.cpp create mode 100644 src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_helpers.cpp create mode 100644 src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_helpers.h delete mode 100644 src/tests/roc_sndio/test_backend_sink.cpp delete mode 100644 src/tests/roc_sndio/test_backend_source.cpp create mode 100644 src/tests/roc_sndio/test_sinks.cpp create mode 100644 src/tests/roc_sndio/test_sources.cpp diff --git a/.fmtignore b/.fmtignore index ead144511..84eb45f99 100644 --- a/.fmtignore +++ b/.fmtignore @@ -1,5 +1,5 @@ src/internal_modules/roc_audio/channel_tables.cpp -src/internal_modules/roc_audio/pcm_format.cpp +src/internal_modules/roc_audio/pcm_subformat.cpp src/internal_modules/roc_core/macro_helpers.h src/internal_modules/roc_core/target_libatomic_ops/roc_core/atomic_ops.h src/public_api/include/roc/version.h diff --git a/SConstruct b/SConstruct index 233acf770..ac2e8c93c 100644 --- a/SConstruct +++ b/SConstruct @@ -729,10 +729,10 @@ env['ROC_MODULES'] = [ 'roc_audio', 'roc_rtp', 'roc_rtcp', + 'roc_sdp', 'roc_netio', 'roc_sndio', 'roc_pipeline', - 'roc_sdp', 'roc_ctl', 'roc_node', ] diff --git a/docs/man/roc-copy.1 b/docs/man/roc-copy.1 index 1eaf4454a..e0c0f1603 100644 --- a/docs/man/roc-copy.1 +++ b/docs/man/roc-copy.1 @@ -60,15 +60,12 @@ List supported protocols, formats, etc. .BI \-i\fP,\fB \-\-input\fB= FILE_URI Input file URI .TP -.BI \-\-input\-format\fB= FILE_FORMAT -Force input file format +.BI \-\-input\-encoding\fB= IO_ENCODING +Input file encoding .TP .BI \-o\fP,\fB \-\-output\fB= FILE_URI Output file URI .TP -.BI \-\-output\-format\fB= FILE_FORMAT -Force output file format -.TP .BI \-\-output\-encoding\fB= IO_ENCODING Output file encoding .TP @@ -150,7 +147,7 @@ Convert sample rate to 24\-bit 48k stereo: .sp .nf .ft C -$ roc\-copy \-vv \-\-io\-encoding s24/48000/stereo \-i file:input.wav \-o file:output.wav +$ roc\-copy \-vv \-\-io\-encoding pcm_s24/48000/stereo \-i file:input.wav \-o file:output.wav .ft P .fi .UNINDENT @@ -162,7 +159,7 @@ Same, but drop output results instead of writing to file (useful for benchmarkin .sp .nf .ft C -$ roc\-copy \-vv \-\-io\-encoding s24/48000/stereo \-i file:input.wav +$ roc\-copy \-vv \-\-io\-encoding pcm_s24/48000/stereo \-i file:input.wav .ft P .fi .UNINDENT diff --git a/docs/man/roc-recv.1 b/docs/man/roc-recv.1 index 2501d37fc..ce6287be1 100644 --- a/docs/man/roc-recv.1 +++ b/docs/man/roc-recv.1 @@ -66,9 +66,6 @@ Exit when last connection is closed (default=off) .BI \-o\fP,\fB \-\-output\fB= IO_URI Output file or device URI .TP -.BI \-\-output\-format\fB= FILE_FORMAT -Force output file format -.TP .BI \-\-io\-encoding\fB= IO_ENCODING Output device encoding .TP @@ -82,10 +79,7 @@ Output frame length, TIME units .INDENT 0.0 .TP .BI \-\-backup\fB= IO_URI -Backup file or device URI (used as input when there are no connections) -.TP -.BI \-\-backup\-format\fB= FILE_FORMAT -Force backup file format +Backup file URI (used as input when there are no connections) .UNINDENT .SS Network options .INDENT 0.0 @@ -239,7 +233,7 @@ This option is useful when device supports multiple encodings, or specific file Where: .INDENT 0.0 .IP \(bu 2 -\fBformat\fP defines sample precision and binary representation, e.g. \fBs16_le\fP stands for little\-endian signed 16\-bit integers +\fBformat\fP defines sample precision and binary representation, e.g. \fBpcm_s16_le\fP stands for little\-endian signed 16\-bit integer PCM .IP \(bu 2 \fBrate\fP defines sample rate in Hertz (number of samples per second), e.g. \fB48000\fP .IP \(bu 2 @@ -251,11 +245,11 @@ Any component may be set to special value \fB\-\fP, which means use default valu Examples: .INDENT 0.0 .IP \(bu 2 -\fBs16/44100/mono\fP \-\- 16\-bit native\-endian integers, 44.1KHz, 1 channel +\fBpcm_s16/44100/mono\fP \-\- 16\-bit native\-endian integers, 44.1KHz, 1 channel .IP \(bu 2 -\fBf32_le/48000/stereo\fP \-\- 32\-bit little\-endian floats, 48KHz, 2 channels +\fBpcm_f32_le/48000/stereo\fP \-\- 32\-bit little\-endian floats, 48KHz, 2 channels .IP \(bu 2 -\fBs24_4be/\-/\-\fP \-\- 24\-bit PCM packed into 4\-byte big\-endian frames, default rate and channels +\fBpcm_s24_4be/\-/\-\fP \-\- 24\-bit PCM packed into 4\-byte big\-endian frames, default rate and channels .UNINDENT .sp The list of supported formats and channel layouts can be retrieved using \fB\-\-list\-supported\fP option. @@ -330,7 +324,7 @@ Where: .IP \(bu 2 \fBid\fP is an arbitrary number in range 100..127, which should uniquely identify encoding on all related senders and receivers .IP \(bu 2 -\fBformat\fP defines sample precision and binary representation, e.g. \fBs16_le\fP stands for little\-endian signed 16\-bit integers +\fBformat\fP defines sample precision and binary representation, e.g. \fBpcm_s24\fP stands for 24\-bit signed integer PCM .IP \(bu 2 \fBrate\fP defines sample rate in Hertz (number of samples per second), e.g. \fB48000\fP .IP \(bu 2 @@ -340,9 +334,9 @@ Where: Examples: .INDENT 0.0 .IP \(bu 2 -\fB101:s16_be/44100/mono\fP \-\- 16\-bit big\-endian integers, 44.1KHz, 1 channel +\fB101:pcm_s24/44100/mono\fP \-\- 24\-bit network\-endian integers, 44.1KHz, 1 channel .IP \(bu 2 -\fB102:f32_le/48000/stereo\fP \-\- 32\-bit little\-endian floats, 48KHz, 2 channels +\fB102:pcm_f32/48000/stereo\fP \-\- 32\-bit network\-endian floats, 48KHz, 2 channels .UNINDENT .sp The list of supported formats and channel layouts can be retrieved using \fB\-\-list\-supported\fP option. @@ -650,7 +644,7 @@ Force specific encoding on the output device: .nf .ft C $ roc\-recv \-vv \-s rtp://0.0.0.0:10001 \e - \-\-output alsa://hw:1,0 \-\-io\-encoding s32/48000/stereo + \-\-output alsa://hw:1,0 \-\-io\-encoding pcm_s32/48000/stereo .ft P .fi .UNINDENT @@ -662,7 +656,7 @@ Use specific encoding for network packets: .sp .nf .ft C -$ roc\-send \-vv \-s rtp://192.168.0.3:10001 \-\-packet\-encoding 101:s32/48000/stereo +$ roc\-send \-vv \-s rtp://192.168.0.3:10001 \-\-packet\-encoding 101:pcm_s24/48000/stereo .ft P .fi .UNINDENT @@ -672,7 +666,7 @@ $ roc\-send \-vv \-s rtp://192.168.0.3:10001 \-\-packet\-encoding 101:s32/48000/ .sp .nf .ft C -$ roc\-recv \-vv \-s rtp://0.0.0.0:10001 \-\-packet\-encoding 101:s32/48000/stereo +$ roc\-recv \-vv \-s rtp://0.0.0.0:10001 \-\-packet\-encoding 101:pcm_s24/48000/stereo .ft P .fi .UNINDENT diff --git a/docs/man/roc-send.1 b/docs/man/roc-send.1 index 522409bc0..82c0b9c23 100644 --- a/docs/man/roc-send.1 +++ b/docs/man/roc-send.1 @@ -60,9 +60,6 @@ List supported protocols, formats, etc. .BI \-i\fP,\fB \-\-input\fB= IO_URI Input file or device URI .TP -.BI \-\-input\-format\fB= FILE_FORMAT -Force input file format -.TP .BI \-\-io\-encoding\fB= IO_ENCODING Input device encoding .TP @@ -223,7 +220,7 @@ This option is useful when device supports multiple encodings. Note that I/O enc Where: .INDENT 0.0 .IP \(bu 2 -\fBformat\fP defines sample precision and binary representation, e.g. \fBs16_le\fP stands for little\-endian signed 16\-bit integers +\fBformat\fP defines sample precision and binary representation, e.g. \fBpcm_s16_le\fP stands for little\-endian signed 16\-bit integer PCM .IP \(bu 2 \fBrate\fP defines sample rate in Hertz (number of samples per second), e.g. \fB48000\fP .IP \(bu 2 @@ -235,11 +232,11 @@ Any component may be set to special value \fB\-\fP, which means use default valu Examples: .INDENT 0.0 .IP \(bu 2 -\fBs16/44100/mono\fP \-\- 16\-bit native\-endian integers, 44.1KHz, 1 channel +\fBpcm_s16/44100/mono\fP \-\- 16\-bit native\-endian integers, 44.1KHz, 1 channel .IP \(bu 2 -\fBf32_le/48000/stereo\fP \-\- 32\-bit little\-endian floats, 48KHz, 2 channels +\fBpcm_f32_le/48000/stereo\fP \-\- 32\-bit little\-endian floats, 48KHz, 2 channels .IP \(bu 2 -\fBs24_4be/\-/\-\fP \-\- 24\-bit PCM packed into 4\-byte big\-endian frames, default rate and channels +\fBpcm_s24_4be/\-/\-\fP \-\- 24\-bit PCM packed into 4\-byte big\-endian frames, default rate and channels .UNINDENT .sp The list of supported formats and channel layouts can be retrieved using \fB\-\-list\-supported\fP option. @@ -314,7 +311,7 @@ Where: .IP \(bu 2 \fBid\fP is an arbitrary number in range 100..127, which should uniquely identify encoding on all related senders and receivers .IP \(bu 2 -\fBformat\fP defines sample precision and binary representation, e.g. \fBs16_le\fP stands for little\-endian signed 16\-bit integers +\fBformat\fP defines sample precision and binary representation, e.g. \fBpcm_s24\fP stands for 24\-bit signed integer PCM .IP \(bu 2 \fBrate\fP defines sample rate in Hertz (number of samples per second), e.g. \fB48000\fP .IP \(bu 2 @@ -324,9 +321,9 @@ Where: Examples: .INDENT 0.0 .IP \(bu 2 -\fB101:s16_be/44100/mono\fP \-\- 16\-bit big\-endian integers, 44.1KHz, 1 channel +\fB101:pcm_s24/44100/mono\fP \-\- 24\-bit network\-endian integers, 44.1KHz, 1 channel .IP \(bu 2 -\fB102:f32_le/48000/stereo\fP \-\- 32\-bit little\-endian floats, 48KHz, 2 channels +\fB102:pcm_f32/48000/stereo\fP \-\- 32\-bit network\-endian floats, 48KHz, 2 channels .UNINDENT .sp The list of supported formats and channel layouts can be retrieved using \fB\-\-list\-supported\fP option. @@ -609,7 +606,7 @@ Force specific encoding on the input device: .nf .ft C $ roc\-send \-vv \-s rtp://192.168.0.3:10001 \e - \-\-input alsa://hw:1,0 \-\-io\-encoding s32/48000/stereo + \-\-input alsa://hw:1,0 \-\-io\-encoding pcm_s32/48000/stereo .ft P .fi .UNINDENT @@ -621,7 +618,7 @@ Use specific encoding for network packets: .sp .nf .ft C -$ roc\-send \-vv \-s rtp://192.168.0.3:10001 \-\-packet\-encoding 101:s32/48000/stereo +$ roc\-send \-vv \-s rtp://192.168.0.3:10001 \-\-packet\-encoding 101:pcm_s24/48000/stereo .ft P .fi .UNINDENT @@ -631,7 +628,7 @@ $ roc\-send \-vv \-s rtp://192.168.0.3:10001 \-\-packet\-encoding 101:s32/48000/ .sp .nf .ft C -$ roc\-recv \-vv \-s rtp://0.0.0.0:10001 \-\-packet\-encoding 101:s32/48000/stereo +$ roc\-recv \-vv \-s rtp://0.0.0.0:10001 \-\-packet\-encoding 101:pcm_s24/48000/stereo .ft P .fi .UNINDENT diff --git a/docs/sphinx/manuals/roc_copy.rst b/docs/sphinx/manuals/roc_copy.rst index eefdfba8b..fae7b21aa 100644 --- a/docs/sphinx/manuals/roc_copy.rst +++ b/docs/sphinx/manuals/roc_copy.rst @@ -32,9 +32,8 @@ I/O options ----------- -i, --input=FILE_URI Input file URI ---input-format=FILE_FORMAT Force input file format +--input-encoding=IO_ENCODING Input file encoding -o, --output=FILE_URI Output file URI ---output-format=FILE_FORMAT Force output file format --output-encoding=IO_ENCODING Output file encoding --io-frame-len=TIME I/O frame length, TIME units @@ -101,13 +100,13 @@ Convert sample rate to 24-bit 48k stereo: .. code:: - $ roc-copy -vv --io-encoding s24/48000/stereo -i file:input.wav -o file:output.wav + $ roc-copy -vv --io-encoding pcm_s24/48000/stereo -i file:input.wav -o file:output.wav Same, but drop output results instead of writing to file (useful for benchmarking): .. code:: - $ roc-copy -vv --io-encoding s24/48000/stereo -i file:input.wav + $ roc-copy -vv --io-encoding pcm_s24/48000/stereo -i file:input.wav Input from stdin, output to stdout: diff --git a/docs/sphinx/manuals/roc_recv.rst b/docs/sphinx/manuals/roc_recv.rst index e8adf53ca..42424ab23 100644 --- a/docs/sphinx/manuals/roc_recv.rst +++ b/docs/sphinx/manuals/roc_recv.rst @@ -36,17 +36,15 @@ Operation options Output options -------------- --o, --output=IO_URI Output file or device URI ---output-format=FILE_FORMAT Force output file format ---io-encoding=IO_ENCODING Output device encoding ---io-latency=TIME Output device latency, TIME units ---io-frame-len=TIME Output frame length, TIME units +-o, --output=IO_URI Output file or device URI +--io-encoding=IO_ENCODING Output device encoding +--io-latency=TIME Output device latency, TIME units +--io-frame-len=TIME Output frame length, TIME units Backup input options -------------------- ---backup=IO_URI Backup file or device URI (used as input when there are no connections) ---backup-format=FILE_FORMAT Force backup file format +--backup=IO_URI Backup file URI (used as input when there are no connections) Network options --------------- @@ -150,7 +148,7 @@ This option is useful when device supports multiple encodings, or specific file Where: -* ``format`` defines sample precision and binary representation, e.g. ``s16_le`` stands for little-endian signed 16-bit integers +* ``format`` defines sample precision and binary representation, e.g. ``pcm_s16_le`` stands for little-endian signed 16-bit integer PCM * ``rate`` defines sample rate in Hertz (number of samples per second), e.g. ``48000`` * ``channels`` defines channel layout, e.g. ``mono`` or ``stereo`` @@ -158,9 +156,9 @@ Any component may be set to special value ``-``, which means use default value f Examples: -* ``s16/44100/mono`` -- 16-bit native-endian integers, 44.1KHz, 1 channel -* ``f32_le/48000/stereo`` -- 32-bit little-endian floats, 48KHz, 2 channels -* ``s24_4be/-/-`` -- 24-bit PCM packed into 4-byte big-endian frames, default rate and channels +* ``pcm_s16/44100/mono`` -- 16-bit native-endian integers, 44.1KHz, 1 channel +* ``pcm_f32_le/48000/stereo`` -- 32-bit little-endian floats, 48KHz, 2 channels +* ``pcm_s24_4be/-/-`` -- 24-bit PCM packed into 4-byte big-endian frames, default rate and channels The list of supported formats and channel layouts can be retrieved using ``--list-supported`` option. @@ -226,14 +224,14 @@ Packet encodings Where: * ``id`` is an arbitrary number in range 100..127, which should uniquely identify encoding on all related senders and receivers -* ``format`` defines sample precision and binary representation, e.g. ``s16_le`` stands for little-endian signed 16-bit integers +* ``format`` defines sample precision and binary representation, e.g. ``pcm_s24`` stands for 24-bit signed integer PCM * ``rate`` defines sample rate in Hertz (number of samples per second), e.g. ``48000`` * ``channels`` defines channel layout, e.g. ``mono`` or ``stereo`` Examples: -* ``101:s16_be/44100/mono`` -- 16-bit big-endian integers, 44.1KHz, 1 channel -* ``102:f32_le/48000/stereo`` -- 32-bit little-endian floats, 48KHz, 2 channels +* ``101:pcm_s24/44100/mono`` -- 24-bit network-endian integers, 44.1KHz, 1 channel +* ``102:pcm_f32/48000/stereo`` -- 32-bit network-endian floats, 48KHz, 2 channels The list of supported formats and channel layouts can be retrieved using ``--list-supported`` option. @@ -457,17 +455,17 @@ Force specific encoding on the output device: .. code:: $ roc-recv -vv -s rtp://0.0.0.0:10001 \ - --output alsa://hw:1,0 --io-encoding s32/48000/stereo + --output alsa://hw:1,0 --io-encoding pcm_s32/48000/stereo Use specific encoding for network packets: .. code:: - $ roc-send -vv -s rtp://192.168.0.3:10001 --packet-encoding 101:s32/48000/stereo + $ roc-send -vv -s rtp://192.168.0.3:10001 --packet-encoding 101:pcm_s24/48000/stereo .. code:: - $ roc-recv -vv -s rtp://0.0.0.0:10001 --packet-encoding 101:s32/48000/stereo + $ roc-recv -vv -s rtp://0.0.0.0:10001 --packet-encoding 101:pcm_s24/48000/stereo Select the LDPC-Staircase FEC scheme: diff --git a/docs/sphinx/manuals/roc_send.rst b/docs/sphinx/manuals/roc_send.rst index 694769315..15324e3c7 100644 --- a/docs/sphinx/manuals/roc_send.rst +++ b/docs/sphinx/manuals/roc_send.rst @@ -31,11 +31,10 @@ General options Input options ------------- --i, --input=IO_URI Input file or device URI ---input-format=FILE_FORMAT Force input file format ---io-encoding=IO_ENCODING Input device encoding ---io-latency=TIME Input device latency, TIME units ---io-frame-len=TIME Input frame length, TIME units +-i, --input=IO_URI Input file or device URI +--io-encoding=IO_ENCODING Input device encoding +--io-latency=TIME Input device latency, TIME units +--io-frame-len=TIME Input frame length, TIME units Network options --------------- @@ -135,7 +134,7 @@ This option is useful when device supports multiple encodings. Note that I/O enc Where: -* ``format`` defines sample precision and binary representation, e.g. ``s16_le`` stands for little-endian signed 16-bit integers +* ``format`` defines sample precision and binary representation, e.g. ``pcm_s16_le`` stands for little-endian signed 16-bit integer PCM * ``rate`` defines sample rate in Hertz (number of samples per second), e.g. ``48000`` * ``channels`` defines channel layout, e.g. ``mono`` or ``stereo`` @@ -143,9 +142,9 @@ Any component may be set to special value ``-``, which means use default value f Examples: -* ``s16/44100/mono`` -- 16-bit native-endian integers, 44.1KHz, 1 channel -* ``f32_le/48000/stereo`` -- 32-bit little-endian floats, 48KHz, 2 channels -* ``s24_4be/-/-`` -- 24-bit PCM packed into 4-byte big-endian frames, default rate and channels +* ``pcm_s16/44100/mono`` -- 16-bit native-endian integers, 44.1KHz, 1 channel +* ``pcm_f32_le/48000/stereo`` -- 32-bit little-endian floats, 48KHz, 2 channels +* ``pcm_s24_4be/-/-`` -- 24-bit PCM packed into 4-byte big-endian frames, default rate and channels The list of supported formats and channel layouts can be retrieved using ``--list-supported`` option. @@ -211,14 +210,14 @@ Packet encoding Where: * ``id`` is an arbitrary number in range 100..127, which should uniquely identify encoding on all related senders and receivers -* ``format`` defines sample precision and binary representation, e.g. ``s16_le`` stands for little-endian signed 16-bit integers +* ``format`` defines sample precision and binary representation, e.g. ``pcm_s24`` stands for 24-bit signed integer PCM * ``rate`` defines sample rate in Hertz (number of samples per second), e.g. ``48000`` * ``channels`` defines channel layout, e.g. ``mono`` or ``stereo`` Examples: -* ``101:s16_be/44100/mono`` -- 16-bit big-endian integers, 44.1KHz, 1 channel -* ``102:f32_le/48000/stereo`` -- 32-bit little-endian floats, 48KHz, 2 channels +* ``101:pcm_s24/44100/mono`` -- 24-bit network-endian integers, 44.1KHz, 1 channel +* ``102:pcm_f32/48000/stereo`` -- 32-bit network-endian floats, 48KHz, 2 channels The list of supported formats and channel layouts can be retrieved using ``--list-supported`` option. @@ -432,17 +431,17 @@ Force specific encoding on the input device: .. code:: $ roc-send -vv -s rtp://192.168.0.3:10001 \ - --input alsa://hw:1,0 --io-encoding s32/48000/stereo + --input alsa://hw:1,0 --io-encoding pcm_s32/48000/stereo Use specific encoding for network packets: .. code:: - $ roc-send -vv -s rtp://192.168.0.3:10001 --packet-encoding 101:s32/48000/stereo + $ roc-send -vv -s rtp://192.168.0.3:10001 --packet-encoding 101:pcm_s24/48000/stereo .. code:: - $ roc-recv -vv -s rtp://0.0.0.0:10001 --packet-encoding 101:s32/48000/stereo + $ roc-recv -vv -s rtp://0.0.0.0:10001 --packet-encoding 101:pcm_s24/48000/stereo Select the LDPC-Staircase FEC scheme and a larger block size: diff --git a/src/internal_modules/roc_audio/beep_plc.cpp b/src/internal_modules/roc_audio/beep_plc.cpp index 3a64b13d5..b159a4776 100644 --- a/src/internal_modules/roc_audio/beep_plc.cpp +++ b/src/internal_modules/roc_audio/beep_plc.cpp @@ -20,8 +20,8 @@ BeepPlc::BeepPlc(const PlcConfig& config, : IPlc(arena) , sample_spec_(sample_spec) , signal_pos_(0) { - if (!sample_spec_.is_valid() || !sample_spec_.is_raw()) { - roc_panic("beep plc: required valid sample specs with raw format: spec=%s", + if (!sample_spec_.is_complete() || !sample_spec_.is_raw()) { + roc_panic("beep plc: required complete sample specs with raw format: spec=%s", sample_spec_to_str(sample_spec_).c_str()); } } diff --git a/src/internal_modules/roc_audio/builtin_resampler.cpp b/src/internal_modules/roc_audio/builtin_resampler.cpp index 4dadf4627..142ddfdb7 100644 --- a/src/internal_modules/roc_audio/builtin_resampler.cpp +++ b/src/internal_modules/roc_audio/builtin_resampler.cpp @@ -145,9 +145,9 @@ BuiltinResampler::BuiltinResampler(const ResamplerConfig& config, , qt_dt_(0) , cutoff_freq_(0.9f) , init_status_(status::NoStatus) { - if (!in_spec_.is_valid() || !out_spec_.is_valid() || !in_spec_.is_raw() + if (!in_spec_.is_complete() || !out_spec_.is_complete() || !in_spec_.is_raw() || !out_spec_.is_raw()) { - roc_panic("builtin resampler: required valid sample specs with raw format:" + roc_panic("builtin resampler: required complete sample specs with raw format:" " in_spec=%s out_spec=%s", sample_spec_to_str(in_spec_).c_str(), sample_spec_to_str(out_spec_).c_str()); diff --git a/src/internal_modules/roc_audio/channel_mapper_reader.cpp b/src/internal_modules/roc_audio/channel_mapper_reader.cpp index 80db79be2..f54196aa2 100644 --- a/src/internal_modules/roc_audio/channel_mapper_reader.cpp +++ b/src/internal_modules/roc_audio/channel_mapper_reader.cpp @@ -25,9 +25,9 @@ ChannelMapperReader::ChannelMapperReader(IFrameReader& frame_reader, , in_spec_(in_spec) , out_spec_(out_spec) , init_status_(status::NoStatus) { - if (!in_spec_.is_valid() || !out_spec_.is_valid() || !in_spec_.is_raw() + if (!in_spec_.is_complete() || !out_spec_.is_complete() || !in_spec_.is_raw() || !out_spec_.is_raw()) { - roc_panic("channel mapper reader: required valid sample specs with raw format:" + roc_panic("channel mapper reader: required complete sample specs with raw format:" " in_spec=%s out_spec=%s", sample_spec_to_str(in_spec_).c_str(), sample_spec_to_str(out_spec_).c_str()); diff --git a/src/internal_modules/roc_audio/channel_mapper_writer.cpp b/src/internal_modules/roc_audio/channel_mapper_writer.cpp index 27dc94464..a9eb57c77 100644 --- a/src/internal_modules/roc_audio/channel_mapper_writer.cpp +++ b/src/internal_modules/roc_audio/channel_mapper_writer.cpp @@ -25,9 +25,9 @@ ChannelMapperWriter::ChannelMapperWriter(IFrameWriter& frame_writer, , in_spec_(in_spec) , out_spec_(out_spec) , init_status_(status::NoStatus) { - if (!in_spec_.is_valid() || !out_spec_.is_valid() || !in_spec_.is_raw() + if (!in_spec_.is_complete() || !out_spec_.is_complete() || !in_spec_.is_raw() || !out_spec_.is_raw()) { - roc_panic("channel mapper writer: required valid sample specs with raw format:" + roc_panic("channel mapper writer: required complete sample specs with raw format:" " in_spec=%s out_spec=%s", sample_spec_to_str(in_spec_).c_str(), sample_spec_to_str(out_spec_).c_str()); diff --git a/src/internal_modules/roc_audio/channel_set_format.cpp b/src/internal_modules/roc_audio/channel_set_format.cpp index 02b2e9ec2..2c0efda1c 100644 --- a/src/internal_modules/roc_audio/channel_set_format.cpp +++ b/src/internal_modules/roc_audio/channel_set_format.cpp @@ -20,46 +20,46 @@ void format_channel_set(const ChannelSet& ch_set, core::StringBuilder& bld) { bld.append_str(channel_order_to_str(ch_set.order())); } - bld.append_str(" n_ch="); + bld.append_str(" "); bld.append_uint(ch_set.num_channels(), 10); - if (ch_set.num_channels() != 0) { - if (ch_set.layout() == ChanLayout_Surround) { - bld.append_str(" ch="); + if (ch_set.num_channels() == 0) { + bld.append_str(" none"); + } else if (ch_set.layout() == ChanLayout_Surround) { + bld.append_str(" "); - for (size_t ch = ch_set.first_channel(); ch <= ch_set.last_channel(); ch++) { - if (!ch_set.has_channel(ch)) { - continue; - } - if (ch != ch_set.first_channel()) { - bld.append_str(","); - } - bld.append_str(channel_pos_to_str((ChannelPosition)ch)); + for (size_t ch = ch_set.first_channel(); ch <= ch_set.last_channel(); ch++) { + if (!ch_set.has_channel(ch)) { + continue; + } + if (ch != ch_set.first_channel()) { + bld.append_str(","); } - } else { - bld.append_str(" ch=0x"); + bld.append_str(channel_pos_to_str((ChannelPosition)ch)); + } + } else { + bld.append_str(" 0x"); - size_t last_byte = 0; + size_t last_byte = 0; - for (size_t n = 0; n < ch_set.num_bytes(); n++) { - if (ch_set.byte_at(n) != 0) { - last_byte = n; - } + for (size_t n = 0; n < ch_set.num_bytes(); n++) { + if (ch_set.byte_at(n) != 0) { + last_byte = n; } + } - size_t n = last_byte; - do { - const uint8_t byte = ch_set.byte_at(n); + size_t n = last_byte; + do { + const uint8_t byte = ch_set.byte_at(n); - const uint8_t lo = (byte & 0xf); - const uint8_t hi = ((byte >> 4) & 0xf); + const uint8_t lo = (byte & 0xf); + const uint8_t hi = ((byte >> 4) & 0xf); - if (hi != 0 || n != last_byte) { - bld.append_uint(hi, 16); - } - bld.append_uint(lo, 16); - } while (n-- != 0); - } + if (hi != 0 || n != last_byte) { + bld.append_uint(hi, 16); + } + bld.append_uint(lo, 16); + } while (n-- != 0); } bld.append_str(">"); diff --git a/src/internal_modules/roc_audio/decimation_resampler.cpp b/src/internal_modules/roc_audio/decimation_resampler.cpp index cec39f90d..e2e1ec175 100644 --- a/src/internal_modules/roc_audio/decimation_resampler.cpp +++ b/src/internal_modules/roc_audio/decimation_resampler.cpp @@ -45,9 +45,9 @@ DecimationResampler::DecimationResampler( , decim_count_(0) , report_limiter_(LogReportInterval) , init_status_(status::NoStatus) { - if (!in_spec.is_valid() || !out_spec.is_valid() || !in_spec.is_raw() + if (!in_spec.is_complete() || !out_spec.is_complete() || !in_spec.is_raw() || !out_spec.is_raw()) { - roc_panic("decimation resampler: required valid sample specs with raw format:" + roc_panic("decimation resampler: required complete sample specs with raw format:" " in_spec=%s out_spec=%s", sample_spec_to_str(in_spec).c_str(), sample_spec_to_str(out_spec).c_str()); diff --git a/src/internal_modules/roc_audio/depacketizer.cpp b/src/internal_modules/roc_audio/depacketizer.cpp index f0815230e..e05766c11 100644 --- a/src/internal_modules/roc_audio/depacketizer.cpp +++ b/src/internal_modules/roc_audio/depacketizer.cpp @@ -42,8 +42,8 @@ Depacketizer::Depacketizer(packet::IReader& packet_reader, , rate_limiter_(LogInterval) , dumper_(dumper) , init_status_(status::NoStatus) { - roc_panic_if_msg(!sample_spec_.is_valid() || !sample_spec_.is_raw(), - "depacketizer: required valid sample spec with raw format: %s", + roc_panic_if_msg(!sample_spec_.is_complete() || !sample_spec_.is_raw(), + "depacketizer: required complete sample spec with raw format: %s", sample_spec_to_str(sample_spec_).c_str()); roc_log(LogDebug, "depacketizer: initializing: sample_rate=%lu n_channels=%lu", diff --git a/src/internal_modules/roc_audio/fanout.cpp b/src/internal_modules/roc_audio/fanout.cpp index 0c67a02dd..3c4a715a6 100644 --- a/src/internal_modules/roc_audio/fanout.cpp +++ b/src/internal_modules/roc_audio/fanout.cpp @@ -20,7 +20,8 @@ Fanout::Fanout(const SampleSpec& sample_spec, : outputs_(arena) , sample_spec_(sample_spec) , init_status_(status::NoStatus) { - roc_panic_if_msg(!sample_spec_.is_valid(), "fanout: required valid sample spec: %s", + roc_panic_if_msg(!sample_spec_.is_complete(), + "fanout: required complete sample spec: %s", sample_spec_to_str(sample_spec_).c_str()); init_status_ = status::StatusOK; diff --git a/src/internal_modules/roc_audio/format.cpp b/src/internal_modules/roc_audio/format.cpp new file mode 100644 index 000000000..78bb8f93a --- /dev/null +++ b/src/internal_modules/roc_audio/format.cpp @@ -0,0 +1,60 @@ +/* + * 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_audio/format.h" +#include "roc_core/macro_helpers.h" +#include "roc_core/stddefs.h" + +namespace roc { +namespace audio { + +namespace { + +const FormatTraits formats[] = { + { Format_Pcm, "pcm", + Format_SupportsNetwork | Format_SupportsDevices | Format_SupportsFiles }, + { Format_Wav, "wav", Format_SupportsFiles }, +}; + +} // namespace + +FormatTraits format_traits(Format format) { + for (size_t n = 0; n < ROC_ARRAY_SIZE(formats); n++) { + if (formats[n].id == format) { + return formats[n]; + } + } + + FormatTraits ret; + memset(&ret, 0, sizeof(ret)); + ret.id = Format_Invalid; + return ret; +} + +const char* format_to_str(Format format) { + for (size_t n = 0; n < ROC_ARRAY_SIZE(formats); n++) { + if (formats[n].id == format) { + return formats[n].name; + } + } + + return "invalid"; +} + +Format format_from_str(const char* str) { + for (size_t n = 0; n < ROC_ARRAY_SIZE(formats); n++) { + if (strcmp(formats[n].name, str) == 0) { + return formats[n].id; + } + } + + return Format_Invalid; +} + +} // namespace audio +} // namespace roc diff --git a/src/internal_modules/roc_audio/format.h b/src/internal_modules/roc_audio/format.h new file mode 100644 index 000000000..ea5ce6a6c --- /dev/null +++ b/src/internal_modules/roc_audio/format.h @@ -0,0 +1,95 @@ +/* + * 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_audio/format.h +//! @brief Audio format. + +#ifndef ROC_AUDIO_FORMAT_H_ +#define ROC_AUDIO_FORMAT_H_ + +#include "roc_audio/pcm_subformat.h" + +namespace roc { +namespace audio { + +//! Audio format. +//! Defines representation of samples in memory. +//! Does not define sample depth, rate and channel set. +enum Format { + //! Invalid format. + Format_Invalid, + + //! Interleaved PCM format. + //! @note + //! Can be used for network packets, devices, files. + //! @note + //! This format requires sub-format of type PcmSubformat, which defines + //! sample type, width, and endian. + Format_Pcm, + + //! WAV file. + //! @note + //! Can be used for files. + //! @note + //! This format allows optional sub-format of type PcmSubformat, which defines + //! sample type, width, and endian. However, not every PCM sub-format is + //! supported. If sub-format is omitted, default sub-format is used. + Format_Wav, + + //! Custom opaque format. + //! @remarks + //! Used to specify custom format for file or device via its string + //! name, when we don't have and don't need enum value for it. + Format_Custom, + + //! Maximum enum value. + Format_Max +}; + +//! Audio format flags. +enum FormatFlags { + //! Format can be used for network packets. + Format_SupportsNetwork = (1 << 0), + + //! Format can be used for audio devices. + Format_SupportsDevices = (1 << 1), + + //! Format can be used for audio files. + Format_SupportsFiles = (1 << 2) +}; + +//! Audio format meta-information. +struct FormatTraits { + //! Numeric identifier. + Format id; + + //! String name. + const char* name; + + //! Flags. + unsigned flags; + + //! Check if all given flags are set. + bool has_flags(unsigned mask) const { + return (flags & mask) == mask; + } +}; + +//! Get format traits. +FormatTraits format_traits(Format format); + +//! Get string name of audio format. +const char* format_to_str(Format format); + +//! Get audio format from string name. +Format format_from_str(const char* str); + +} // namespace audio +} // namespace roc + +#endif // ROC_AUDIO_FORMAT_H_ diff --git a/src/internal_modules/roc_audio/mixer.cpp b/src/internal_modules/roc_audio/mixer.cpp index b86506f26..c6c0531c2 100644 --- a/src/internal_modules/roc_audio/mixer.cpp +++ b/src/internal_modules/roc_audio/mixer.cpp @@ -23,8 +23,8 @@ Mixer::Mixer(const SampleSpec& sample_spec, , sample_spec_(sample_spec) , enable_timestamps_(enable_timestamps) , init_status_(status::NoStatus) { - roc_panic_if_msg(!sample_spec_.is_valid() || !sample_spec_.is_raw(), - "mixer: required valid sample spec with raw format: %s", + roc_panic_if_msg(!sample_spec_.is_complete() || !sample_spec_.is_raw(), + "mixer: required complete sample spec with raw format: %s", sample_spec_to_str(sample_spec_).c_str()); in_frame_ = frame_factory_.allocate_frame(0); diff --git a/src/internal_modules/roc_audio/packetizer.cpp b/src/internal_modules/roc_audio/packetizer.cpp index 3a1083562..82618b85f 100644 --- a/src/internal_modules/roc_audio/packetizer.cpp +++ b/src/internal_modules/roc_audio/packetizer.cpp @@ -34,8 +34,8 @@ Packetizer::Packetizer(packet::IWriter& writer, , packet_cts_(0) , capture_ts_(0) , init_status_(status::NoStatus) { - roc_panic_if_msg(!sample_spec_.is_valid() || !sample_spec_.is_raw(), - "packetizer: required valid sample spec with raw format: %s", + roc_panic_if_msg(!sample_spec_.is_complete() || !sample_spec_.is_raw(), + "packetizer: required complete sample spec with raw format: %s", sample_spec_to_str(sample_spec_).c_str()); if (packet_length <= 0 || sample_spec.ns_2_stream_timestamp(packet_length) <= 0) { diff --git a/src/internal_modules/roc_audio/pcm_decoder.cpp b/src/internal_modules/roc_audio/pcm_decoder.cpp index 96f1c732d..4d233b3bd 100644 --- a/src/internal_modules/roc_audio/pcm_decoder.cpp +++ b/src/internal_modules/roc_audio/pcm_decoder.cpp @@ -19,7 +19,7 @@ IFrameDecoder* PcmDecoder::construct(const SampleSpec& sample_spec, core::IArena PcmDecoder::PcmDecoder(const SampleSpec& sample_spec, core::IArena& arena) : IFrameDecoder(arena) - , pcm_mapper_(sample_spec.pcm_format(), Sample_RawFormat) + , pcm_mapper_(sample_spec.pcm_subformat(), PcmSubformat_Raw) , n_chans_(sample_spec.num_channels()) , stream_pos_(0) , stream_avail_(0) diff --git a/src/internal_modules/roc_audio/pcm_encoder.cpp b/src/internal_modules/roc_audio/pcm_encoder.cpp index 49ef31433..020da933c 100644 --- a/src/internal_modules/roc_audio/pcm_encoder.cpp +++ b/src/internal_modules/roc_audio/pcm_encoder.cpp @@ -18,7 +18,7 @@ IFrameEncoder* PcmEncoder::construct(const SampleSpec& sample_spec, core::IArena PcmEncoder::PcmEncoder(const SampleSpec& sample_spec, core::IArena& arena) : IFrameEncoder(arena) - , pcm_mapper_(Sample_RawFormat, sample_spec.pcm_format()) + , pcm_mapper_(PcmSubformat_Raw, sample_spec.pcm_subformat()) , n_chans_(sample_spec.num_channels()) , frame_data_(NULL) , frame_byte_size_(0) diff --git a/src/internal_modules/roc_audio/pcm_format.cpp b/src/internal_modules/roc_audio/pcm_format.cpp index 175fb2670..e8dc14996 100644 --- a/src/internal_modules/roc_audio/pcm_format.cpp +++ b/src/internal_modules/roc_audio/pcm_format.cpp @@ -1,8 +1,9 @@ /* - * THIS FILE IS AUTO-GENERATED USING `pcm_format_gen.py'. DO NOT EDIT! + * THIS FILE IS AUTO-GENERATED USING `pcm_subformat_gen.py'. DO NOT EDIT! */ -#include "roc_audio/pcm_format.h" +#include "roc_audio/pcm_subformat.h" +#include "roc_audio/pcm_subformat_rw.h" #include "roc_core/attributes.h" #include "roc_core/cpu_traits.h" #include "roc_core/stddefs.h" @@ -2151,7 +2152,6 @@ template <> struct pcm_code_converter<PcmCode_Float64, PcmCode_Float64> { } }; - // N-byte native-endian sample template <class T> struct pcm_sample; @@ -2359,54 +2359,6 @@ template <> struct pcm_sample<double> { }; }; -// Write octet at given byte-aligned bit offset -inline void pcm_aligned_write(uint8_t* buffer, size_t& bit_offset, uint8_t arg) { - buffer[bit_offset >> 3] = arg; - bit_offset += 8; -} - -// Read octet at given byte-aligned bit offset -inline uint8_t pcm_aligned_read(const uint8_t* buffer, size_t& bit_offset) { - uint8_t ret = buffer[bit_offset >> 3]; - bit_offset += 8; - return ret; -} - -// Write value (at most 8 bits) at given unaligned bit offset -inline void -pcm_unaligned_write(uint8_t* buffer, size_t& bit_offset, size_t bit_length, uint8_t arg) { - size_t byte_index = (bit_offset >> 3); - size_t bit_index = (bit_offset & 0x7u); - - if (bit_index == 0) { - buffer[byte_index] = 0; - } - - buffer[byte_index] |= uint8_t(uint8_t(arg << (8 - bit_length)) >> bit_index); - - if (bit_index + bit_length > 8) { - buffer[byte_index + 1] = uint8_t(arg << bit_index); - } - - bit_offset += bit_length; -} - -// Read value (at most 8 bits) at given unaligned bit offset -inline uint8_t -pcm_unaligned_read(const uint8_t* buffer, size_t& bit_offset, size_t bit_length) { - size_t byte_index = (bit_offset >> 3); - size_t bit_index = (bit_offset & 0x7u); - - uint8_t ret = uint8_t(uint8_t(buffer[byte_index] << bit_index) >> (8 - bit_length)); - - if (bit_index + bit_length > 8) { - ret |= uint8_t(buffer[byte_index + 1] >> ((8 - bit_index) + (8 - bit_length))); - } - - bit_offset += bit_length; - return ret; -} - // Sample packer / unpacker template <PcmCode, PcmEndian> struct pcm_packer; @@ -4177,14 +4129,14 @@ struct pcm_mapper { // Select mapping function template <PcmCode InCode, PcmEndian InEndian> -PcmMapFn pcm_map_to_raw(PcmFormat raw_format) { +PcmMapFn pcm_map_to_raw(PcmSubformat raw_format) { switch (raw_format) { #if ROC_CPU_ENDIAN == ROC_CPU_BE - case PcmFormat_Float32: - case PcmFormat_Float32_Be: + case PcmSubformat_Float32: + case PcmSubformat_Float32_Be: #else - case PcmFormat_Float32: - case PcmFormat_Float32_Le: + case PcmSubformat_Float32: + case PcmSubformat_Float32_Le: #endif return &pcm_mapper<InCode, InEndian, PcmCode_Float32, PcmEndian_Default>::map; default: @@ -4195,14 +4147,14 @@ PcmMapFn pcm_map_to_raw(PcmFormat raw_format) { // Select mapping function template <PcmCode OutCode, PcmEndian OutEndian> -PcmMapFn pcm_map_from_raw(PcmFormat raw_format) { +PcmMapFn pcm_map_from_raw(PcmSubformat raw_format) { switch (raw_format) { #if ROC_CPU_ENDIAN == ROC_CPU_BE - case PcmFormat_Float32: - case PcmFormat_Float32_Be: + case PcmSubformat_Float32: + case PcmSubformat_Float32_Be: #else - case PcmFormat_Float32: - case PcmFormat_Float32_Le: + case PcmSubformat_Float32: + case PcmSubformat_Float32_Le: #endif return &pcm_mapper<PcmCode_Float32, PcmEndian_Default, OutCode, OutEndian>::map; default: @@ -4214,165 +4166,165 @@ PcmMapFn pcm_map_from_raw(PcmFormat raw_format) { } // namespace // Select mapping function -PcmMapFn pcm_format_mapfn(PcmFormat in_format, PcmFormat out_format) { +PcmMapFn pcm_subformat_mapfn(PcmSubformat in_format, PcmSubformat out_format) { // non-raw to raw switch (in_format) { - case PcmFormat_SInt8: + case PcmSubformat_SInt8: return pcm_map_to_raw<PcmCode_SInt8, PcmEndian_Default>(out_format); - case PcmFormat_SInt8_Be: + case PcmSubformat_SInt8_Be: return pcm_map_to_raw<PcmCode_SInt8, PcmEndian_Big>(out_format); - case PcmFormat_SInt8_Le: + case PcmSubformat_SInt8_Le: return pcm_map_to_raw<PcmCode_SInt8, PcmEndian_Little>(out_format); - case PcmFormat_UInt8: + case PcmSubformat_UInt8: return pcm_map_to_raw<PcmCode_UInt8, PcmEndian_Default>(out_format); - case PcmFormat_UInt8_Be: + case PcmSubformat_UInt8_Be: return pcm_map_to_raw<PcmCode_UInt8, PcmEndian_Big>(out_format); - case PcmFormat_UInt8_Le: + case PcmSubformat_UInt8_Le: return pcm_map_to_raw<PcmCode_UInt8, PcmEndian_Little>(out_format); - case PcmFormat_SInt16: + case PcmSubformat_SInt16: return pcm_map_to_raw<PcmCode_SInt16, PcmEndian_Default>(out_format); - case PcmFormat_SInt16_Be: + case PcmSubformat_SInt16_Be: return pcm_map_to_raw<PcmCode_SInt16, PcmEndian_Big>(out_format); - case PcmFormat_SInt16_Le: + case PcmSubformat_SInt16_Le: return pcm_map_to_raw<PcmCode_SInt16, PcmEndian_Little>(out_format); - case PcmFormat_UInt16: + case PcmSubformat_UInt16: return pcm_map_to_raw<PcmCode_UInt16, PcmEndian_Default>(out_format); - case PcmFormat_UInt16_Be: + case PcmSubformat_UInt16_Be: return pcm_map_to_raw<PcmCode_UInt16, PcmEndian_Big>(out_format); - case PcmFormat_UInt16_Le: + case PcmSubformat_UInt16_Le: return pcm_map_to_raw<PcmCode_UInt16, PcmEndian_Little>(out_format); - case PcmFormat_SInt18: + case PcmSubformat_SInt18: return pcm_map_to_raw<PcmCode_SInt18, PcmEndian_Default>(out_format); - case PcmFormat_SInt18_Be: + case PcmSubformat_SInt18_Be: return pcm_map_to_raw<PcmCode_SInt18, PcmEndian_Big>(out_format); - case PcmFormat_SInt18_Le: + case PcmSubformat_SInt18_Le: return pcm_map_to_raw<PcmCode_SInt18, PcmEndian_Little>(out_format); - case PcmFormat_UInt18: + case PcmSubformat_UInt18: return pcm_map_to_raw<PcmCode_UInt18, PcmEndian_Default>(out_format); - case PcmFormat_UInt18_Be: + case PcmSubformat_UInt18_Be: return pcm_map_to_raw<PcmCode_UInt18, PcmEndian_Big>(out_format); - case PcmFormat_UInt18_Le: + case PcmSubformat_UInt18_Le: return pcm_map_to_raw<PcmCode_UInt18, PcmEndian_Little>(out_format); - case PcmFormat_SInt18_3: + case PcmSubformat_SInt18_3: return pcm_map_to_raw<PcmCode_SInt18_3, PcmEndian_Default>(out_format); - case PcmFormat_SInt18_3_Be: + case PcmSubformat_SInt18_3_Be: return pcm_map_to_raw<PcmCode_SInt18_3, PcmEndian_Big>(out_format); - case PcmFormat_SInt18_3_Le: + case PcmSubformat_SInt18_3_Le: return pcm_map_to_raw<PcmCode_SInt18_3, PcmEndian_Little>(out_format); - case PcmFormat_UInt18_3: + case PcmSubformat_UInt18_3: return pcm_map_to_raw<PcmCode_UInt18_3, PcmEndian_Default>(out_format); - case PcmFormat_UInt18_3_Be: + case PcmSubformat_UInt18_3_Be: return pcm_map_to_raw<PcmCode_UInt18_3, PcmEndian_Big>(out_format); - case PcmFormat_UInt18_3_Le: + case PcmSubformat_UInt18_3_Le: return pcm_map_to_raw<PcmCode_UInt18_3, PcmEndian_Little>(out_format); - case PcmFormat_SInt18_4: + case PcmSubformat_SInt18_4: return pcm_map_to_raw<PcmCode_SInt18_4, PcmEndian_Default>(out_format); - case PcmFormat_SInt18_4_Be: + case PcmSubformat_SInt18_4_Be: return pcm_map_to_raw<PcmCode_SInt18_4, PcmEndian_Big>(out_format); - case PcmFormat_SInt18_4_Le: + case PcmSubformat_SInt18_4_Le: return pcm_map_to_raw<PcmCode_SInt18_4, PcmEndian_Little>(out_format); - case PcmFormat_UInt18_4: + case PcmSubformat_UInt18_4: return pcm_map_to_raw<PcmCode_UInt18_4, PcmEndian_Default>(out_format); - case PcmFormat_UInt18_4_Be: + case PcmSubformat_UInt18_4_Be: return pcm_map_to_raw<PcmCode_UInt18_4, PcmEndian_Big>(out_format); - case PcmFormat_UInt18_4_Le: + case PcmSubformat_UInt18_4_Le: return pcm_map_to_raw<PcmCode_UInt18_4, PcmEndian_Little>(out_format); - case PcmFormat_SInt20: + case PcmSubformat_SInt20: return pcm_map_to_raw<PcmCode_SInt20, PcmEndian_Default>(out_format); - case PcmFormat_SInt20_Be: + case PcmSubformat_SInt20_Be: return pcm_map_to_raw<PcmCode_SInt20, PcmEndian_Big>(out_format); - case PcmFormat_SInt20_Le: + case PcmSubformat_SInt20_Le: return pcm_map_to_raw<PcmCode_SInt20, PcmEndian_Little>(out_format); - case PcmFormat_UInt20: + case PcmSubformat_UInt20: return pcm_map_to_raw<PcmCode_UInt20, PcmEndian_Default>(out_format); - case PcmFormat_UInt20_Be: + case PcmSubformat_UInt20_Be: return pcm_map_to_raw<PcmCode_UInt20, PcmEndian_Big>(out_format); - case PcmFormat_UInt20_Le: + case PcmSubformat_UInt20_Le: return pcm_map_to_raw<PcmCode_UInt20, PcmEndian_Little>(out_format); - case PcmFormat_SInt20_3: + case PcmSubformat_SInt20_3: return pcm_map_to_raw<PcmCode_SInt20_3, PcmEndian_Default>(out_format); - case PcmFormat_SInt20_3_Be: + case PcmSubformat_SInt20_3_Be: return pcm_map_to_raw<PcmCode_SInt20_3, PcmEndian_Big>(out_format); - case PcmFormat_SInt20_3_Le: + case PcmSubformat_SInt20_3_Le: return pcm_map_to_raw<PcmCode_SInt20_3, PcmEndian_Little>(out_format); - case PcmFormat_UInt20_3: + case PcmSubformat_UInt20_3: return pcm_map_to_raw<PcmCode_UInt20_3, PcmEndian_Default>(out_format); - case PcmFormat_UInt20_3_Be: + case PcmSubformat_UInt20_3_Be: return pcm_map_to_raw<PcmCode_UInt20_3, PcmEndian_Big>(out_format); - case PcmFormat_UInt20_3_Le: + case PcmSubformat_UInt20_3_Le: return pcm_map_to_raw<PcmCode_UInt20_3, PcmEndian_Little>(out_format); - case PcmFormat_SInt20_4: + case PcmSubformat_SInt20_4: return pcm_map_to_raw<PcmCode_SInt20_4, PcmEndian_Default>(out_format); - case PcmFormat_SInt20_4_Be: + case PcmSubformat_SInt20_4_Be: return pcm_map_to_raw<PcmCode_SInt20_4, PcmEndian_Big>(out_format); - case PcmFormat_SInt20_4_Le: + case PcmSubformat_SInt20_4_Le: return pcm_map_to_raw<PcmCode_SInt20_4, PcmEndian_Little>(out_format); - case PcmFormat_UInt20_4: + case PcmSubformat_UInt20_4: return pcm_map_to_raw<PcmCode_UInt20_4, PcmEndian_Default>(out_format); - case PcmFormat_UInt20_4_Be: + case PcmSubformat_UInt20_4_Be: return pcm_map_to_raw<PcmCode_UInt20_4, PcmEndian_Big>(out_format); - case PcmFormat_UInt20_4_Le: + case PcmSubformat_UInt20_4_Le: return pcm_map_to_raw<PcmCode_UInt20_4, PcmEndian_Little>(out_format); - case PcmFormat_SInt24: + case PcmSubformat_SInt24: return pcm_map_to_raw<PcmCode_SInt24, PcmEndian_Default>(out_format); - case PcmFormat_SInt24_Be: + case PcmSubformat_SInt24_Be: return pcm_map_to_raw<PcmCode_SInt24, PcmEndian_Big>(out_format); - case PcmFormat_SInt24_Le: + case PcmSubformat_SInt24_Le: return pcm_map_to_raw<PcmCode_SInt24, PcmEndian_Little>(out_format); - case PcmFormat_UInt24: + case PcmSubformat_UInt24: return pcm_map_to_raw<PcmCode_UInt24, PcmEndian_Default>(out_format); - case PcmFormat_UInt24_Be: + case PcmSubformat_UInt24_Be: return pcm_map_to_raw<PcmCode_UInt24, PcmEndian_Big>(out_format); - case PcmFormat_UInt24_Le: + case PcmSubformat_UInt24_Le: return pcm_map_to_raw<PcmCode_UInt24, PcmEndian_Little>(out_format); - case PcmFormat_SInt24_4: + case PcmSubformat_SInt24_4: return pcm_map_to_raw<PcmCode_SInt24_4, PcmEndian_Default>(out_format); - case PcmFormat_SInt24_4_Be: + case PcmSubformat_SInt24_4_Be: return pcm_map_to_raw<PcmCode_SInt24_4, PcmEndian_Big>(out_format); - case PcmFormat_SInt24_4_Le: + case PcmSubformat_SInt24_4_Le: return pcm_map_to_raw<PcmCode_SInt24_4, PcmEndian_Little>(out_format); - case PcmFormat_UInt24_4: + case PcmSubformat_UInt24_4: return pcm_map_to_raw<PcmCode_UInt24_4, PcmEndian_Default>(out_format); - case PcmFormat_UInt24_4_Be: + case PcmSubformat_UInt24_4_Be: return pcm_map_to_raw<PcmCode_UInt24_4, PcmEndian_Big>(out_format); - case PcmFormat_UInt24_4_Le: + case PcmSubformat_UInt24_4_Le: return pcm_map_to_raw<PcmCode_UInt24_4, PcmEndian_Little>(out_format); - case PcmFormat_SInt32: + case PcmSubformat_SInt32: return pcm_map_to_raw<PcmCode_SInt32, PcmEndian_Default>(out_format); - case PcmFormat_SInt32_Be: + case PcmSubformat_SInt32_Be: return pcm_map_to_raw<PcmCode_SInt32, PcmEndian_Big>(out_format); - case PcmFormat_SInt32_Le: + case PcmSubformat_SInt32_Le: return pcm_map_to_raw<PcmCode_SInt32, PcmEndian_Little>(out_format); - case PcmFormat_UInt32: + case PcmSubformat_UInt32: return pcm_map_to_raw<PcmCode_UInt32, PcmEndian_Default>(out_format); - case PcmFormat_UInt32_Be: + case PcmSubformat_UInt32_Be: return pcm_map_to_raw<PcmCode_UInt32, PcmEndian_Big>(out_format); - case PcmFormat_UInt32_Le: + case PcmSubformat_UInt32_Le: return pcm_map_to_raw<PcmCode_UInt32, PcmEndian_Little>(out_format); - case PcmFormat_SInt64: + case PcmSubformat_SInt64: return pcm_map_to_raw<PcmCode_SInt64, PcmEndian_Default>(out_format); - case PcmFormat_SInt64_Be: + case PcmSubformat_SInt64_Be: return pcm_map_to_raw<PcmCode_SInt64, PcmEndian_Big>(out_format); - case PcmFormat_SInt64_Le: + case PcmSubformat_SInt64_Le: return pcm_map_to_raw<PcmCode_SInt64, PcmEndian_Little>(out_format); - case PcmFormat_UInt64: + case PcmSubformat_UInt64: return pcm_map_to_raw<PcmCode_UInt64, PcmEndian_Default>(out_format); - case PcmFormat_UInt64_Be: + case PcmSubformat_UInt64_Be: return pcm_map_to_raw<PcmCode_UInt64, PcmEndian_Big>(out_format); - case PcmFormat_UInt64_Le: + case PcmSubformat_UInt64_Le: return pcm_map_to_raw<PcmCode_UInt64, PcmEndian_Little>(out_format); #if ROC_CPU_ENDIAN != ROC_CPU_BE - case PcmFormat_Float32_Be: + case PcmSubformat_Float32_Be: return pcm_map_to_raw<PcmCode_Float32, PcmEndian_Big>(out_format); #else - case PcmFormat_Float32_Le: + case PcmSubformat_Float32_Le: return pcm_map_to_raw<PcmCode_Float32, PcmEndian_Little>(out_format); #endif - case PcmFormat_Float64: + case PcmSubformat_Float64: return pcm_map_to_raw<PcmCode_Float64, PcmEndian_Default>(out_format); - case PcmFormat_Float64_Be: + case PcmSubformat_Float64_Be: return pcm_map_to_raw<PcmCode_Float64, PcmEndian_Big>(out_format); - case PcmFormat_Float64_Le: + case PcmSubformat_Float64_Le: return pcm_map_to_raw<PcmCode_Float64, PcmEndian_Little>(out_format); default: break; @@ -4380,162 +4332,162 @@ PcmMapFn pcm_format_mapfn(PcmFormat in_format, PcmFormat out_format) { // raw to non-raw switch (out_format) { - case PcmFormat_SInt8: + case PcmSubformat_SInt8: return pcm_map_from_raw<PcmCode_SInt8, PcmEndian_Default>(in_format); - case PcmFormat_SInt8_Be: + case PcmSubformat_SInt8_Be: return pcm_map_from_raw<PcmCode_SInt8, PcmEndian_Big>(in_format); - case PcmFormat_SInt8_Le: + case PcmSubformat_SInt8_Le: return pcm_map_from_raw<PcmCode_SInt8, PcmEndian_Little>(in_format); - case PcmFormat_UInt8: + case PcmSubformat_UInt8: return pcm_map_from_raw<PcmCode_UInt8, PcmEndian_Default>(in_format); - case PcmFormat_UInt8_Be: + case PcmSubformat_UInt8_Be: return pcm_map_from_raw<PcmCode_UInt8, PcmEndian_Big>(in_format); - case PcmFormat_UInt8_Le: + case PcmSubformat_UInt8_Le: return pcm_map_from_raw<PcmCode_UInt8, PcmEndian_Little>(in_format); - case PcmFormat_SInt16: + case PcmSubformat_SInt16: return pcm_map_from_raw<PcmCode_SInt16, PcmEndian_Default>(in_format); - case PcmFormat_SInt16_Be: + case PcmSubformat_SInt16_Be: return pcm_map_from_raw<PcmCode_SInt16, PcmEndian_Big>(in_format); - case PcmFormat_SInt16_Le: + case PcmSubformat_SInt16_Le: return pcm_map_from_raw<PcmCode_SInt16, PcmEndian_Little>(in_format); - case PcmFormat_UInt16: + case PcmSubformat_UInt16: return pcm_map_from_raw<PcmCode_UInt16, PcmEndian_Default>(in_format); - case PcmFormat_UInt16_Be: + case PcmSubformat_UInt16_Be: return pcm_map_from_raw<PcmCode_UInt16, PcmEndian_Big>(in_format); - case PcmFormat_UInt16_Le: + case PcmSubformat_UInt16_Le: return pcm_map_from_raw<PcmCode_UInt16, PcmEndian_Little>(in_format); - case PcmFormat_SInt18: + case PcmSubformat_SInt18: return pcm_map_from_raw<PcmCode_SInt18, PcmEndian_Default>(in_format); - case PcmFormat_SInt18_Be: + case PcmSubformat_SInt18_Be: return pcm_map_from_raw<PcmCode_SInt18, PcmEndian_Big>(in_format); - case PcmFormat_SInt18_Le: + case PcmSubformat_SInt18_Le: return pcm_map_from_raw<PcmCode_SInt18, PcmEndian_Little>(in_format); - case PcmFormat_UInt18: + case PcmSubformat_UInt18: return pcm_map_from_raw<PcmCode_UInt18, PcmEndian_Default>(in_format); - case PcmFormat_UInt18_Be: + case PcmSubformat_UInt18_Be: return pcm_map_from_raw<PcmCode_UInt18, PcmEndian_Big>(in_format); - case PcmFormat_UInt18_Le: + case PcmSubformat_UInt18_Le: return pcm_map_from_raw<PcmCode_UInt18, PcmEndian_Little>(in_format); - case PcmFormat_SInt18_3: + case PcmSubformat_SInt18_3: return pcm_map_from_raw<PcmCode_SInt18_3, PcmEndian_Default>(in_format); - case PcmFormat_SInt18_3_Be: + case PcmSubformat_SInt18_3_Be: return pcm_map_from_raw<PcmCode_SInt18_3, PcmEndian_Big>(in_format); - case PcmFormat_SInt18_3_Le: + case PcmSubformat_SInt18_3_Le: return pcm_map_from_raw<PcmCode_SInt18_3, PcmEndian_Little>(in_format); - case PcmFormat_UInt18_3: + case PcmSubformat_UInt18_3: return pcm_map_from_raw<PcmCode_UInt18_3, PcmEndian_Default>(in_format); - case PcmFormat_UInt18_3_Be: + case PcmSubformat_UInt18_3_Be: return pcm_map_from_raw<PcmCode_UInt18_3, PcmEndian_Big>(in_format); - case PcmFormat_UInt18_3_Le: + case PcmSubformat_UInt18_3_Le: return pcm_map_from_raw<PcmCode_UInt18_3, PcmEndian_Little>(in_format); - case PcmFormat_SInt18_4: + case PcmSubformat_SInt18_4: return pcm_map_from_raw<PcmCode_SInt18_4, PcmEndian_Default>(in_format); - case PcmFormat_SInt18_4_Be: + case PcmSubformat_SInt18_4_Be: return pcm_map_from_raw<PcmCode_SInt18_4, PcmEndian_Big>(in_format); - case PcmFormat_SInt18_4_Le: + case PcmSubformat_SInt18_4_Le: return pcm_map_from_raw<PcmCode_SInt18_4, PcmEndian_Little>(in_format); - case PcmFormat_UInt18_4: + case PcmSubformat_UInt18_4: return pcm_map_from_raw<PcmCode_UInt18_4, PcmEndian_Default>(in_format); - case PcmFormat_UInt18_4_Be: + case PcmSubformat_UInt18_4_Be: return pcm_map_from_raw<PcmCode_UInt18_4, PcmEndian_Big>(in_format); - case PcmFormat_UInt18_4_Le: + case PcmSubformat_UInt18_4_Le: return pcm_map_from_raw<PcmCode_UInt18_4, PcmEndian_Little>(in_format); - case PcmFormat_SInt20: + case PcmSubformat_SInt20: return pcm_map_from_raw<PcmCode_SInt20, PcmEndian_Default>(in_format); - case PcmFormat_SInt20_Be: + case PcmSubformat_SInt20_Be: return pcm_map_from_raw<PcmCode_SInt20, PcmEndian_Big>(in_format); - case PcmFormat_SInt20_Le: + case PcmSubformat_SInt20_Le: return pcm_map_from_raw<PcmCode_SInt20, PcmEndian_Little>(in_format); - case PcmFormat_UInt20: + case PcmSubformat_UInt20: return pcm_map_from_raw<PcmCode_UInt20, PcmEndian_Default>(in_format); - case PcmFormat_UInt20_Be: + case PcmSubformat_UInt20_Be: return pcm_map_from_raw<PcmCode_UInt20, PcmEndian_Big>(in_format); - case PcmFormat_UInt20_Le: + case PcmSubformat_UInt20_Le: return pcm_map_from_raw<PcmCode_UInt20, PcmEndian_Little>(in_format); - case PcmFormat_SInt20_3: + case PcmSubformat_SInt20_3: return pcm_map_from_raw<PcmCode_SInt20_3, PcmEndian_Default>(in_format); - case PcmFormat_SInt20_3_Be: + case PcmSubformat_SInt20_3_Be: return pcm_map_from_raw<PcmCode_SInt20_3, PcmEndian_Big>(in_format); - case PcmFormat_SInt20_3_Le: + case PcmSubformat_SInt20_3_Le: return pcm_map_from_raw<PcmCode_SInt20_3, PcmEndian_Little>(in_format); - case PcmFormat_UInt20_3: + case PcmSubformat_UInt20_3: return pcm_map_from_raw<PcmCode_UInt20_3, PcmEndian_Default>(in_format); - case PcmFormat_UInt20_3_Be: + case PcmSubformat_UInt20_3_Be: return pcm_map_from_raw<PcmCode_UInt20_3, PcmEndian_Big>(in_format); - case PcmFormat_UInt20_3_Le: + case PcmSubformat_UInt20_3_Le: return pcm_map_from_raw<PcmCode_UInt20_3, PcmEndian_Little>(in_format); - case PcmFormat_SInt20_4: + case PcmSubformat_SInt20_4: return pcm_map_from_raw<PcmCode_SInt20_4, PcmEndian_Default>(in_format); - case PcmFormat_SInt20_4_Be: + case PcmSubformat_SInt20_4_Be: return pcm_map_from_raw<PcmCode_SInt20_4, PcmEndian_Big>(in_format); - case PcmFormat_SInt20_4_Le: + case PcmSubformat_SInt20_4_Le: return pcm_map_from_raw<PcmCode_SInt20_4, PcmEndian_Little>(in_format); - case PcmFormat_UInt20_4: + case PcmSubformat_UInt20_4: return pcm_map_from_raw<PcmCode_UInt20_4, PcmEndian_Default>(in_format); - case PcmFormat_UInt20_4_Be: + case PcmSubformat_UInt20_4_Be: return pcm_map_from_raw<PcmCode_UInt20_4, PcmEndian_Big>(in_format); - case PcmFormat_UInt20_4_Le: + case PcmSubformat_UInt20_4_Le: return pcm_map_from_raw<PcmCode_UInt20_4, PcmEndian_Little>(in_format); - case PcmFormat_SInt24: + case PcmSubformat_SInt24: return pcm_map_from_raw<PcmCode_SInt24, PcmEndian_Default>(in_format); - case PcmFormat_SInt24_Be: + case PcmSubformat_SInt24_Be: return pcm_map_from_raw<PcmCode_SInt24, PcmEndian_Big>(in_format); - case PcmFormat_SInt24_Le: + case PcmSubformat_SInt24_Le: return pcm_map_from_raw<PcmCode_SInt24, PcmEndian_Little>(in_format); - case PcmFormat_UInt24: + case PcmSubformat_UInt24: return pcm_map_from_raw<PcmCode_UInt24, PcmEndian_Default>(in_format); - case PcmFormat_UInt24_Be: + case PcmSubformat_UInt24_Be: return pcm_map_from_raw<PcmCode_UInt24, PcmEndian_Big>(in_format); - case PcmFormat_UInt24_Le: + case PcmSubformat_UInt24_Le: return pcm_map_from_raw<PcmCode_UInt24, PcmEndian_Little>(in_format); - case PcmFormat_SInt24_4: + case PcmSubformat_SInt24_4: return pcm_map_from_raw<PcmCode_SInt24_4, PcmEndian_Default>(in_format); - case PcmFormat_SInt24_4_Be: + case PcmSubformat_SInt24_4_Be: return pcm_map_from_raw<PcmCode_SInt24_4, PcmEndian_Big>(in_format); - case PcmFormat_SInt24_4_Le: + case PcmSubformat_SInt24_4_Le: return pcm_map_from_raw<PcmCode_SInt24_4, PcmEndian_Little>(in_format); - case PcmFormat_UInt24_4: + case PcmSubformat_UInt24_4: return pcm_map_from_raw<PcmCode_UInt24_4, PcmEndian_Default>(in_format); - case PcmFormat_UInt24_4_Be: + case PcmSubformat_UInt24_4_Be: return pcm_map_from_raw<PcmCode_UInt24_4, PcmEndian_Big>(in_format); - case PcmFormat_UInt24_4_Le: + case PcmSubformat_UInt24_4_Le: return pcm_map_from_raw<PcmCode_UInt24_4, PcmEndian_Little>(in_format); - case PcmFormat_SInt32: + case PcmSubformat_SInt32: return pcm_map_from_raw<PcmCode_SInt32, PcmEndian_Default>(in_format); - case PcmFormat_SInt32_Be: + case PcmSubformat_SInt32_Be: return pcm_map_from_raw<PcmCode_SInt32, PcmEndian_Big>(in_format); - case PcmFormat_SInt32_Le: + case PcmSubformat_SInt32_Le: return pcm_map_from_raw<PcmCode_SInt32, PcmEndian_Little>(in_format); - case PcmFormat_UInt32: + case PcmSubformat_UInt32: return pcm_map_from_raw<PcmCode_UInt32, PcmEndian_Default>(in_format); - case PcmFormat_UInt32_Be: + case PcmSubformat_UInt32_Be: return pcm_map_from_raw<PcmCode_UInt32, PcmEndian_Big>(in_format); - case PcmFormat_UInt32_Le: + case PcmSubformat_UInt32_Le: return pcm_map_from_raw<PcmCode_UInt32, PcmEndian_Little>(in_format); - case PcmFormat_SInt64: + case PcmSubformat_SInt64: return pcm_map_from_raw<PcmCode_SInt64, PcmEndian_Default>(in_format); - case PcmFormat_SInt64_Be: + case PcmSubformat_SInt64_Be: return pcm_map_from_raw<PcmCode_SInt64, PcmEndian_Big>(in_format); - case PcmFormat_SInt64_Le: + case PcmSubformat_SInt64_Le: return pcm_map_from_raw<PcmCode_SInt64, PcmEndian_Little>(in_format); - case PcmFormat_UInt64: + case PcmSubformat_UInt64: return pcm_map_from_raw<PcmCode_UInt64, PcmEndian_Default>(in_format); - case PcmFormat_UInt64_Be: + case PcmSubformat_UInt64_Be: return pcm_map_from_raw<PcmCode_UInt64, PcmEndian_Big>(in_format); - case PcmFormat_UInt64_Le: + case PcmSubformat_UInt64_Le: return pcm_map_from_raw<PcmCode_UInt64, PcmEndian_Little>(in_format); #if ROC_CPU_ENDIAN != ROC_CPU_BE - case PcmFormat_Float32_Be: + case PcmSubformat_Float32_Be: return pcm_map_from_raw<PcmCode_Float32, PcmEndian_Big>(in_format); #else - case PcmFormat_Float32_Le: + case PcmSubformat_Float32_Le: return pcm_map_from_raw<PcmCode_Float32, PcmEndian_Little>(in_format); #endif - case PcmFormat_Float64: + case PcmSubformat_Float64: return pcm_map_from_raw<PcmCode_Float64, PcmEndian_Default>(in_format); - case PcmFormat_Float64_Be: + case PcmSubformat_Float64_Be: return pcm_map_from_raw<PcmCode_Float64, PcmEndian_Big>(in_format); - case PcmFormat_Float64_Le: + case PcmSubformat_Float64_Le: return pcm_map_from_raw<PcmCode_Float64, PcmEndian_Little>(in_format); default: break; @@ -4543,13 +4495,13 @@ PcmMapFn pcm_format_mapfn(PcmFormat in_format, PcmFormat out_format) { // raw to raw switch (out_format) { - case PcmFormat_Float32: + case PcmSubformat_Float32: return pcm_map_from_raw<PcmCode_Float32, PcmEndian_Default>(in_format); #if ROC_CPU_ENDIAN == ROC_CPU_BE - case PcmFormat_Float32_Be: + case PcmSubformat_Float32_Be: return pcm_map_from_raw<PcmCode_Float32, PcmEndian_Default>(in_format); #else - case PcmFormat_Float32_Le: + case PcmSubformat_Float32_Le: return pcm_map_from_raw<PcmCode_Float32, PcmEndian_Default>(in_format); #endif default: @@ -4560,27 +4512,31 @@ PcmMapFn pcm_format_mapfn(PcmFormat in_format, PcmFormat out_format) { } // Get format traits -PcmTraits pcm_format_traits(PcmFormat format) { +PcmTraits pcm_subformat_traits(PcmSubformat format) { PcmTraits traits; switch (format) { - case PcmFormat_SInt8: + case PcmSubformat_SInt8: + traits.id = PcmSubformat_SInt8; + traits.name = "s8"; traits.bit_width = 8; traits.bit_depth = 8; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsPacked | Pcm_IsAligned; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_SInt8_Be; + traits.portable_alias = PcmSubformat_SInt8_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_SInt8_Le; + traits.portable_alias = PcmSubformat_SInt8_Le; #endif - traits.default_variant = PcmFormat_SInt8; - traits.be_variant = PcmFormat_SInt8_Be; - traits.le_variant = PcmFormat_SInt8_Le; + traits.default_variant = PcmSubformat_SInt8; + traits.be_variant = PcmSubformat_SInt8_Be; + traits.le_variant = PcmSubformat_SInt8_Le; break; - case PcmFormat_SInt8_Be: + case PcmSubformat_SInt8_Be: + traits.id = PcmSubformat_SInt8_Be; + traits.name = "s8_be"; traits.bit_width = 8; traits.bit_depth = 8; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsPacked | Pcm_IsAligned; @@ -4589,13 +4545,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_SInt8_Be; - traits.default_variant = PcmFormat_SInt8; - traits.be_variant = PcmFormat_SInt8_Be; - traits.le_variant = PcmFormat_SInt8_Le; + traits.portable_alias = PcmSubformat_SInt8_Be; + traits.default_variant = PcmSubformat_SInt8; + traits.be_variant = PcmSubformat_SInt8_Be; + traits.le_variant = PcmSubformat_SInt8_Le; break; - case PcmFormat_SInt8_Le: + case PcmSubformat_SInt8_Le: + traits.id = PcmSubformat_SInt8_Le; + traits.name = "s8_le"; traits.bit_width = 8; traits.bit_depth = 8; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsPacked | Pcm_IsAligned; @@ -4604,29 +4562,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_SInt8_Le; - traits.default_variant = PcmFormat_SInt8; - traits.be_variant = PcmFormat_SInt8_Be; - traits.le_variant = PcmFormat_SInt8_Le; + traits.portable_alias = PcmSubformat_SInt8_Le; + traits.default_variant = PcmSubformat_SInt8; + traits.be_variant = PcmSubformat_SInt8_Be; + traits.le_variant = PcmSubformat_SInt8_Le; break; - case PcmFormat_UInt8: + case PcmSubformat_UInt8: + traits.id = PcmSubformat_UInt8; + traits.name = "u8"; traits.bit_width = 8; traits.bit_depth = 8; traits.flags = Pcm_IsInteger | Pcm_IsPacked | Pcm_IsAligned; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_UInt8_Be; + traits.portable_alias = PcmSubformat_UInt8_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_UInt8_Le; + traits.portable_alias = PcmSubformat_UInt8_Le; #endif - traits.default_variant = PcmFormat_UInt8; - traits.be_variant = PcmFormat_UInt8_Be; - traits.le_variant = PcmFormat_UInt8_Le; + traits.default_variant = PcmSubformat_UInt8; + traits.be_variant = PcmSubformat_UInt8_Be; + traits.le_variant = PcmSubformat_UInt8_Le; break; - case PcmFormat_UInt8_Be: + case PcmSubformat_UInt8_Be: + traits.id = PcmSubformat_UInt8_Be; + traits.name = "u8_be"; traits.bit_width = 8; traits.bit_depth = 8; traits.flags = Pcm_IsInteger | Pcm_IsPacked | Pcm_IsAligned; @@ -4635,13 +4597,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_UInt8_Be; - traits.default_variant = PcmFormat_UInt8; - traits.be_variant = PcmFormat_UInt8_Be; - traits.le_variant = PcmFormat_UInt8_Le; + traits.portable_alias = PcmSubformat_UInt8_Be; + traits.default_variant = PcmSubformat_UInt8; + traits.be_variant = PcmSubformat_UInt8_Be; + traits.le_variant = PcmSubformat_UInt8_Le; break; - case PcmFormat_UInt8_Le: + case PcmSubformat_UInt8_Le: + traits.id = PcmSubformat_UInt8_Le; + traits.name = "u8_le"; traits.bit_width = 8; traits.bit_depth = 8; traits.flags = Pcm_IsInteger | Pcm_IsPacked | Pcm_IsAligned; @@ -4650,29 +4614,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_UInt8_Le; - traits.default_variant = PcmFormat_UInt8; - traits.be_variant = PcmFormat_UInt8_Be; - traits.le_variant = PcmFormat_UInt8_Le; + traits.portable_alias = PcmSubformat_UInt8_Le; + traits.default_variant = PcmSubformat_UInt8; + traits.be_variant = PcmSubformat_UInt8_Be; + traits.le_variant = PcmSubformat_UInt8_Le; break; - case PcmFormat_SInt16: + case PcmSubformat_SInt16: + traits.id = PcmSubformat_SInt16; + traits.name = "s16"; traits.bit_width = 16; traits.bit_depth = 16; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsPacked | Pcm_IsAligned; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_SInt16_Be; + traits.portable_alias = PcmSubformat_SInt16_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_SInt16_Le; + traits.portable_alias = PcmSubformat_SInt16_Le; #endif - traits.default_variant = PcmFormat_SInt16; - traits.be_variant = PcmFormat_SInt16_Be; - traits.le_variant = PcmFormat_SInt16_Le; + traits.default_variant = PcmSubformat_SInt16; + traits.be_variant = PcmSubformat_SInt16_Be; + traits.le_variant = PcmSubformat_SInt16_Le; break; - case PcmFormat_SInt16_Be: + case PcmSubformat_SInt16_Be: + traits.id = PcmSubformat_SInt16_Be; + traits.name = "s16_be"; traits.bit_width = 16; traits.bit_depth = 16; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsPacked | Pcm_IsAligned; @@ -4681,13 +4649,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_SInt16_Be; - traits.default_variant = PcmFormat_SInt16; - traits.be_variant = PcmFormat_SInt16_Be; - traits.le_variant = PcmFormat_SInt16_Le; + traits.portable_alias = PcmSubformat_SInt16_Be; + traits.default_variant = PcmSubformat_SInt16; + traits.be_variant = PcmSubformat_SInt16_Be; + traits.le_variant = PcmSubformat_SInt16_Le; break; - case PcmFormat_SInt16_Le: + case PcmSubformat_SInt16_Le: + traits.id = PcmSubformat_SInt16_Le; + traits.name = "s16_le"; traits.bit_width = 16; traits.bit_depth = 16; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsPacked | Pcm_IsAligned; @@ -4696,29 +4666,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_SInt16_Le; - traits.default_variant = PcmFormat_SInt16; - traits.be_variant = PcmFormat_SInt16_Be; - traits.le_variant = PcmFormat_SInt16_Le; + traits.portable_alias = PcmSubformat_SInt16_Le; + traits.default_variant = PcmSubformat_SInt16; + traits.be_variant = PcmSubformat_SInt16_Be; + traits.le_variant = PcmSubformat_SInt16_Le; break; - case PcmFormat_UInt16: + case PcmSubformat_UInt16: + traits.id = PcmSubformat_UInt16; + traits.name = "u16"; traits.bit_width = 16; traits.bit_depth = 16; traits.flags = Pcm_IsInteger | Pcm_IsPacked | Pcm_IsAligned; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_UInt16_Be; + traits.portable_alias = PcmSubformat_UInt16_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_UInt16_Le; + traits.portable_alias = PcmSubformat_UInt16_Le; #endif - traits.default_variant = PcmFormat_UInt16; - traits.be_variant = PcmFormat_UInt16_Be; - traits.le_variant = PcmFormat_UInt16_Le; + traits.default_variant = PcmSubformat_UInt16; + traits.be_variant = PcmSubformat_UInt16_Be; + traits.le_variant = PcmSubformat_UInt16_Le; break; - case PcmFormat_UInt16_Be: + case PcmSubformat_UInt16_Be: + traits.id = PcmSubformat_UInt16_Be; + traits.name = "u16_be"; traits.bit_width = 16; traits.bit_depth = 16; traits.flags = Pcm_IsInteger | Pcm_IsPacked | Pcm_IsAligned; @@ -4727,13 +4701,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_UInt16_Be; - traits.default_variant = PcmFormat_UInt16; - traits.be_variant = PcmFormat_UInt16_Be; - traits.le_variant = PcmFormat_UInt16_Le; + traits.portable_alias = PcmSubformat_UInt16_Be; + traits.default_variant = PcmSubformat_UInt16; + traits.be_variant = PcmSubformat_UInt16_Be; + traits.le_variant = PcmSubformat_UInt16_Le; break; - case PcmFormat_UInt16_Le: + case PcmSubformat_UInt16_Le: + traits.id = PcmSubformat_UInt16_Le; + traits.name = "u16_le"; traits.bit_width = 16; traits.bit_depth = 16; traits.flags = Pcm_IsInteger | Pcm_IsPacked | Pcm_IsAligned; @@ -4742,29 +4718,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_UInt16_Le; - traits.default_variant = PcmFormat_UInt16; - traits.be_variant = PcmFormat_UInt16_Be; - traits.le_variant = PcmFormat_UInt16_Le; + traits.portable_alias = PcmSubformat_UInt16_Le; + traits.default_variant = PcmSubformat_UInt16; + traits.be_variant = PcmSubformat_UInt16_Be; + traits.le_variant = PcmSubformat_UInt16_Le; break; - case PcmFormat_SInt18: + case PcmSubformat_SInt18: + traits.id = PcmSubformat_SInt18; + traits.name = "s18"; traits.bit_width = 18; traits.bit_depth = 18; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsPacked; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_SInt18_Be; + traits.portable_alias = PcmSubformat_SInt18_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_SInt18_Le; + traits.portable_alias = PcmSubformat_SInt18_Le; #endif - traits.default_variant = PcmFormat_SInt18; - traits.be_variant = PcmFormat_SInt18_Be; - traits.le_variant = PcmFormat_SInt18_Le; + traits.default_variant = PcmSubformat_SInt18; + traits.be_variant = PcmSubformat_SInt18_Be; + traits.le_variant = PcmSubformat_SInt18_Le; break; - case PcmFormat_SInt18_Be: + case PcmSubformat_SInt18_Be: + traits.id = PcmSubformat_SInt18_Be; + traits.name = "s18_be"; traits.bit_width = 18; traits.bit_depth = 18; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsPacked; @@ -4773,13 +4753,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_SInt18_Be; - traits.default_variant = PcmFormat_SInt18; - traits.be_variant = PcmFormat_SInt18_Be; - traits.le_variant = PcmFormat_SInt18_Le; + traits.portable_alias = PcmSubformat_SInt18_Be; + traits.default_variant = PcmSubformat_SInt18; + traits.be_variant = PcmSubformat_SInt18_Be; + traits.le_variant = PcmSubformat_SInt18_Le; break; - case PcmFormat_SInt18_Le: + case PcmSubformat_SInt18_Le: + traits.id = PcmSubformat_SInt18_Le; + traits.name = "s18_le"; traits.bit_width = 18; traits.bit_depth = 18; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsPacked; @@ -4788,29 +4770,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_SInt18_Le; - traits.default_variant = PcmFormat_SInt18; - traits.be_variant = PcmFormat_SInt18_Be; - traits.le_variant = PcmFormat_SInt18_Le; + traits.portable_alias = PcmSubformat_SInt18_Le; + traits.default_variant = PcmSubformat_SInt18; + traits.be_variant = PcmSubformat_SInt18_Be; + traits.le_variant = PcmSubformat_SInt18_Le; break; - case PcmFormat_UInt18: + case PcmSubformat_UInt18: + traits.id = PcmSubformat_UInt18; + traits.name = "u18"; traits.bit_width = 18; traits.bit_depth = 18; traits.flags = Pcm_IsInteger | Pcm_IsPacked; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_UInt18_Be; + traits.portable_alias = PcmSubformat_UInt18_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_UInt18_Le; + traits.portable_alias = PcmSubformat_UInt18_Le; #endif - traits.default_variant = PcmFormat_UInt18; - traits.be_variant = PcmFormat_UInt18_Be; - traits.le_variant = PcmFormat_UInt18_Le; + traits.default_variant = PcmSubformat_UInt18; + traits.be_variant = PcmSubformat_UInt18_Be; + traits.le_variant = PcmSubformat_UInt18_Le; break; - case PcmFormat_UInt18_Be: + case PcmSubformat_UInt18_Be: + traits.id = PcmSubformat_UInt18_Be; + traits.name = "u18_be"; traits.bit_width = 18; traits.bit_depth = 18; traits.flags = Pcm_IsInteger | Pcm_IsPacked; @@ -4819,13 +4805,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_UInt18_Be; - traits.default_variant = PcmFormat_UInt18; - traits.be_variant = PcmFormat_UInt18_Be; - traits.le_variant = PcmFormat_UInt18_Le; + traits.portable_alias = PcmSubformat_UInt18_Be; + traits.default_variant = PcmSubformat_UInt18; + traits.be_variant = PcmSubformat_UInt18_Be; + traits.le_variant = PcmSubformat_UInt18_Le; break; - case PcmFormat_UInt18_Le: + case PcmSubformat_UInt18_Le: + traits.id = PcmSubformat_UInt18_Le; + traits.name = "u18_le"; traits.bit_width = 18; traits.bit_depth = 18; traits.flags = Pcm_IsInteger | Pcm_IsPacked; @@ -4834,29 +4822,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_UInt18_Le; - traits.default_variant = PcmFormat_UInt18; - traits.be_variant = PcmFormat_UInt18_Be; - traits.le_variant = PcmFormat_UInt18_Le; + traits.portable_alias = PcmSubformat_UInt18_Le; + traits.default_variant = PcmSubformat_UInt18; + traits.be_variant = PcmSubformat_UInt18_Be; + traits.le_variant = PcmSubformat_UInt18_Le; break; - case PcmFormat_SInt18_3: + case PcmSubformat_SInt18_3: + traits.id = PcmSubformat_SInt18_3; + traits.name = "s18_3"; traits.bit_width = 24; traits.bit_depth = 18; traits.flags = Pcm_IsInteger | Pcm_IsSigned; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_SInt18_3_Be; + traits.portable_alias = PcmSubformat_SInt18_3_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_SInt18_3_Le; + traits.portable_alias = PcmSubformat_SInt18_3_Le; #endif - traits.default_variant = PcmFormat_SInt18_3; - traits.be_variant = PcmFormat_SInt18_3_Be; - traits.le_variant = PcmFormat_SInt18_3_Le; + traits.default_variant = PcmSubformat_SInt18_3; + traits.be_variant = PcmSubformat_SInt18_3_Be; + traits.le_variant = PcmSubformat_SInt18_3_Le; break; - case PcmFormat_SInt18_3_Be: + case PcmSubformat_SInt18_3_Be: + traits.id = PcmSubformat_SInt18_3_Be; + traits.name = "s18_3be"; traits.bit_width = 24; traits.bit_depth = 18; traits.flags = Pcm_IsInteger | Pcm_IsSigned; @@ -4865,13 +4857,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_SInt18_3_Be; - traits.default_variant = PcmFormat_SInt18_3; - traits.be_variant = PcmFormat_SInt18_3_Be; - traits.le_variant = PcmFormat_SInt18_3_Le; + traits.portable_alias = PcmSubformat_SInt18_3_Be; + traits.default_variant = PcmSubformat_SInt18_3; + traits.be_variant = PcmSubformat_SInt18_3_Be; + traits.le_variant = PcmSubformat_SInt18_3_Le; break; - case PcmFormat_SInt18_3_Le: + case PcmSubformat_SInt18_3_Le: + traits.id = PcmSubformat_SInt18_3_Le; + traits.name = "s18_3le"; traits.bit_width = 24; traits.bit_depth = 18; traits.flags = Pcm_IsInteger | Pcm_IsSigned; @@ -4880,29 +4874,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_SInt18_3_Le; - traits.default_variant = PcmFormat_SInt18_3; - traits.be_variant = PcmFormat_SInt18_3_Be; - traits.le_variant = PcmFormat_SInt18_3_Le; + traits.portable_alias = PcmSubformat_SInt18_3_Le; + traits.default_variant = PcmSubformat_SInt18_3; + traits.be_variant = PcmSubformat_SInt18_3_Be; + traits.le_variant = PcmSubformat_SInt18_3_Le; break; - case PcmFormat_UInt18_3: + case PcmSubformat_UInt18_3: + traits.id = PcmSubformat_UInt18_3; + traits.name = "u18_3"; traits.bit_width = 24; traits.bit_depth = 18; traits.flags = Pcm_IsInteger; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_UInt18_3_Be; + traits.portable_alias = PcmSubformat_UInt18_3_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_UInt18_3_Le; + traits.portable_alias = PcmSubformat_UInt18_3_Le; #endif - traits.default_variant = PcmFormat_UInt18_3; - traits.be_variant = PcmFormat_UInt18_3_Be; - traits.le_variant = PcmFormat_UInt18_3_Le; + traits.default_variant = PcmSubformat_UInt18_3; + traits.be_variant = PcmSubformat_UInt18_3_Be; + traits.le_variant = PcmSubformat_UInt18_3_Le; break; - case PcmFormat_UInt18_3_Be: + case PcmSubformat_UInt18_3_Be: + traits.id = PcmSubformat_UInt18_3_Be; + traits.name = "u18_3be"; traits.bit_width = 24; traits.bit_depth = 18; traits.flags = Pcm_IsInteger; @@ -4911,13 +4909,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_UInt18_3_Be; - traits.default_variant = PcmFormat_UInt18_3; - traits.be_variant = PcmFormat_UInt18_3_Be; - traits.le_variant = PcmFormat_UInt18_3_Le; + traits.portable_alias = PcmSubformat_UInt18_3_Be; + traits.default_variant = PcmSubformat_UInt18_3; + traits.be_variant = PcmSubformat_UInt18_3_Be; + traits.le_variant = PcmSubformat_UInt18_3_Le; break; - case PcmFormat_UInt18_3_Le: + case PcmSubformat_UInt18_3_Le: + traits.id = PcmSubformat_UInt18_3_Le; + traits.name = "u18_3le"; traits.bit_width = 24; traits.bit_depth = 18; traits.flags = Pcm_IsInteger; @@ -4926,29 +4926,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_UInt18_3_Le; - traits.default_variant = PcmFormat_UInt18_3; - traits.be_variant = PcmFormat_UInt18_3_Be; - traits.le_variant = PcmFormat_UInt18_3_Le; + traits.portable_alias = PcmSubformat_UInt18_3_Le; + traits.default_variant = PcmSubformat_UInt18_3; + traits.be_variant = PcmSubformat_UInt18_3_Be; + traits.le_variant = PcmSubformat_UInt18_3_Le; break; - case PcmFormat_SInt18_4: + case PcmSubformat_SInt18_4: + traits.id = PcmSubformat_SInt18_4; + traits.name = "s18_4"; traits.bit_width = 32; traits.bit_depth = 18; traits.flags = Pcm_IsInteger | Pcm_IsSigned; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_SInt18_4_Be; + traits.portable_alias = PcmSubformat_SInt18_4_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_SInt18_4_Le; + traits.portable_alias = PcmSubformat_SInt18_4_Le; #endif - traits.default_variant = PcmFormat_SInt18_4; - traits.be_variant = PcmFormat_SInt18_4_Be; - traits.le_variant = PcmFormat_SInt18_4_Le; + traits.default_variant = PcmSubformat_SInt18_4; + traits.be_variant = PcmSubformat_SInt18_4_Be; + traits.le_variant = PcmSubformat_SInt18_4_Le; break; - case PcmFormat_SInt18_4_Be: + case PcmSubformat_SInt18_4_Be: + traits.id = PcmSubformat_SInt18_4_Be; + traits.name = "s18_4be"; traits.bit_width = 32; traits.bit_depth = 18; traits.flags = Pcm_IsInteger | Pcm_IsSigned; @@ -4957,13 +4961,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_SInt18_4_Be; - traits.default_variant = PcmFormat_SInt18_4; - traits.be_variant = PcmFormat_SInt18_4_Be; - traits.le_variant = PcmFormat_SInt18_4_Le; + traits.portable_alias = PcmSubformat_SInt18_4_Be; + traits.default_variant = PcmSubformat_SInt18_4; + traits.be_variant = PcmSubformat_SInt18_4_Be; + traits.le_variant = PcmSubformat_SInt18_4_Le; break; - case PcmFormat_SInt18_4_Le: + case PcmSubformat_SInt18_4_Le: + traits.id = PcmSubformat_SInt18_4_Le; + traits.name = "s18_4le"; traits.bit_width = 32; traits.bit_depth = 18; traits.flags = Pcm_IsInteger | Pcm_IsSigned; @@ -4972,29 +4978,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_SInt18_4_Le; - traits.default_variant = PcmFormat_SInt18_4; - traits.be_variant = PcmFormat_SInt18_4_Be; - traits.le_variant = PcmFormat_SInt18_4_Le; + traits.portable_alias = PcmSubformat_SInt18_4_Le; + traits.default_variant = PcmSubformat_SInt18_4; + traits.be_variant = PcmSubformat_SInt18_4_Be; + traits.le_variant = PcmSubformat_SInt18_4_Le; break; - case PcmFormat_UInt18_4: + case PcmSubformat_UInt18_4: + traits.id = PcmSubformat_UInt18_4; + traits.name = "u18_4"; traits.bit_width = 32; traits.bit_depth = 18; traits.flags = Pcm_IsInteger; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_UInt18_4_Be; + traits.portable_alias = PcmSubformat_UInt18_4_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_UInt18_4_Le; + traits.portable_alias = PcmSubformat_UInt18_4_Le; #endif - traits.default_variant = PcmFormat_UInt18_4; - traits.be_variant = PcmFormat_UInt18_4_Be; - traits.le_variant = PcmFormat_UInt18_4_Le; + traits.default_variant = PcmSubformat_UInt18_4; + traits.be_variant = PcmSubformat_UInt18_4_Be; + traits.le_variant = PcmSubformat_UInt18_4_Le; break; - case PcmFormat_UInt18_4_Be: + case PcmSubformat_UInt18_4_Be: + traits.id = PcmSubformat_UInt18_4_Be; + traits.name = "u18_4be"; traits.bit_width = 32; traits.bit_depth = 18; traits.flags = Pcm_IsInteger; @@ -5003,13 +5013,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_UInt18_4_Be; - traits.default_variant = PcmFormat_UInt18_4; - traits.be_variant = PcmFormat_UInt18_4_Be; - traits.le_variant = PcmFormat_UInt18_4_Le; + traits.portable_alias = PcmSubformat_UInt18_4_Be; + traits.default_variant = PcmSubformat_UInt18_4; + traits.be_variant = PcmSubformat_UInt18_4_Be; + traits.le_variant = PcmSubformat_UInt18_4_Le; break; - case PcmFormat_UInt18_4_Le: + case PcmSubformat_UInt18_4_Le: + traits.id = PcmSubformat_UInt18_4_Le; + traits.name = "u18_4le"; traits.bit_width = 32; traits.bit_depth = 18; traits.flags = Pcm_IsInteger; @@ -5018,29 +5030,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_UInt18_4_Le; - traits.default_variant = PcmFormat_UInt18_4; - traits.be_variant = PcmFormat_UInt18_4_Be; - traits.le_variant = PcmFormat_UInt18_4_Le; + traits.portable_alias = PcmSubformat_UInt18_4_Le; + traits.default_variant = PcmSubformat_UInt18_4; + traits.be_variant = PcmSubformat_UInt18_4_Be; + traits.le_variant = PcmSubformat_UInt18_4_Le; break; - case PcmFormat_SInt20: + case PcmSubformat_SInt20: + traits.id = PcmSubformat_SInt20; + traits.name = "s20"; traits.bit_width = 20; traits.bit_depth = 20; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsPacked; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_SInt20_Be; + traits.portable_alias = PcmSubformat_SInt20_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_SInt20_Le; + traits.portable_alias = PcmSubformat_SInt20_Le; #endif - traits.default_variant = PcmFormat_SInt20; - traits.be_variant = PcmFormat_SInt20_Be; - traits.le_variant = PcmFormat_SInt20_Le; + traits.default_variant = PcmSubformat_SInt20; + traits.be_variant = PcmSubformat_SInt20_Be; + traits.le_variant = PcmSubformat_SInt20_Le; break; - case PcmFormat_SInt20_Be: + case PcmSubformat_SInt20_Be: + traits.id = PcmSubformat_SInt20_Be; + traits.name = "s20_be"; traits.bit_width = 20; traits.bit_depth = 20; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsPacked; @@ -5049,13 +5065,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_SInt20_Be; - traits.default_variant = PcmFormat_SInt20; - traits.be_variant = PcmFormat_SInt20_Be; - traits.le_variant = PcmFormat_SInt20_Le; + traits.portable_alias = PcmSubformat_SInt20_Be; + traits.default_variant = PcmSubformat_SInt20; + traits.be_variant = PcmSubformat_SInt20_Be; + traits.le_variant = PcmSubformat_SInt20_Le; break; - case PcmFormat_SInt20_Le: + case PcmSubformat_SInt20_Le: + traits.id = PcmSubformat_SInt20_Le; + traits.name = "s20_le"; traits.bit_width = 20; traits.bit_depth = 20; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsPacked; @@ -5064,29 +5082,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_SInt20_Le; - traits.default_variant = PcmFormat_SInt20; - traits.be_variant = PcmFormat_SInt20_Be; - traits.le_variant = PcmFormat_SInt20_Le; + traits.portable_alias = PcmSubformat_SInt20_Le; + traits.default_variant = PcmSubformat_SInt20; + traits.be_variant = PcmSubformat_SInt20_Be; + traits.le_variant = PcmSubformat_SInt20_Le; break; - case PcmFormat_UInt20: + case PcmSubformat_UInt20: + traits.id = PcmSubformat_UInt20; + traits.name = "u20"; traits.bit_width = 20; traits.bit_depth = 20; traits.flags = Pcm_IsInteger | Pcm_IsPacked; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_UInt20_Be; + traits.portable_alias = PcmSubformat_UInt20_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_UInt20_Le; + traits.portable_alias = PcmSubformat_UInt20_Le; #endif - traits.default_variant = PcmFormat_UInt20; - traits.be_variant = PcmFormat_UInt20_Be; - traits.le_variant = PcmFormat_UInt20_Le; + traits.default_variant = PcmSubformat_UInt20; + traits.be_variant = PcmSubformat_UInt20_Be; + traits.le_variant = PcmSubformat_UInt20_Le; break; - case PcmFormat_UInt20_Be: + case PcmSubformat_UInt20_Be: + traits.id = PcmSubformat_UInt20_Be; + traits.name = "u20_be"; traits.bit_width = 20; traits.bit_depth = 20; traits.flags = Pcm_IsInteger | Pcm_IsPacked; @@ -5095,13 +5117,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_UInt20_Be; - traits.default_variant = PcmFormat_UInt20; - traits.be_variant = PcmFormat_UInt20_Be; - traits.le_variant = PcmFormat_UInt20_Le; + traits.portable_alias = PcmSubformat_UInt20_Be; + traits.default_variant = PcmSubformat_UInt20; + traits.be_variant = PcmSubformat_UInt20_Be; + traits.le_variant = PcmSubformat_UInt20_Le; break; - case PcmFormat_UInt20_Le: + case PcmSubformat_UInt20_Le: + traits.id = PcmSubformat_UInt20_Le; + traits.name = "u20_le"; traits.bit_width = 20; traits.bit_depth = 20; traits.flags = Pcm_IsInteger | Pcm_IsPacked; @@ -5110,29 +5134,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_UInt20_Le; - traits.default_variant = PcmFormat_UInt20; - traits.be_variant = PcmFormat_UInt20_Be; - traits.le_variant = PcmFormat_UInt20_Le; + traits.portable_alias = PcmSubformat_UInt20_Le; + traits.default_variant = PcmSubformat_UInt20; + traits.be_variant = PcmSubformat_UInt20_Be; + traits.le_variant = PcmSubformat_UInt20_Le; break; - case PcmFormat_SInt20_3: + case PcmSubformat_SInt20_3: + traits.id = PcmSubformat_SInt20_3; + traits.name = "s20_3"; traits.bit_width = 24; traits.bit_depth = 20; traits.flags = Pcm_IsInteger | Pcm_IsSigned; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_SInt20_3_Be; + traits.portable_alias = PcmSubformat_SInt20_3_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_SInt20_3_Le; + traits.portable_alias = PcmSubformat_SInt20_3_Le; #endif - traits.default_variant = PcmFormat_SInt20_3; - traits.be_variant = PcmFormat_SInt20_3_Be; - traits.le_variant = PcmFormat_SInt20_3_Le; + traits.default_variant = PcmSubformat_SInt20_3; + traits.be_variant = PcmSubformat_SInt20_3_Be; + traits.le_variant = PcmSubformat_SInt20_3_Le; break; - case PcmFormat_SInt20_3_Be: + case PcmSubformat_SInt20_3_Be: + traits.id = PcmSubformat_SInt20_3_Be; + traits.name = "s20_3be"; traits.bit_width = 24; traits.bit_depth = 20; traits.flags = Pcm_IsInteger | Pcm_IsSigned; @@ -5141,13 +5169,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_SInt20_3_Be; - traits.default_variant = PcmFormat_SInt20_3; - traits.be_variant = PcmFormat_SInt20_3_Be; - traits.le_variant = PcmFormat_SInt20_3_Le; + traits.portable_alias = PcmSubformat_SInt20_3_Be; + traits.default_variant = PcmSubformat_SInt20_3; + traits.be_variant = PcmSubformat_SInt20_3_Be; + traits.le_variant = PcmSubformat_SInt20_3_Le; break; - case PcmFormat_SInt20_3_Le: + case PcmSubformat_SInt20_3_Le: + traits.id = PcmSubformat_SInt20_3_Le; + traits.name = "s20_3le"; traits.bit_width = 24; traits.bit_depth = 20; traits.flags = Pcm_IsInteger | Pcm_IsSigned; @@ -5156,29 +5186,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_SInt20_3_Le; - traits.default_variant = PcmFormat_SInt20_3; - traits.be_variant = PcmFormat_SInt20_3_Be; - traits.le_variant = PcmFormat_SInt20_3_Le; + traits.portable_alias = PcmSubformat_SInt20_3_Le; + traits.default_variant = PcmSubformat_SInt20_3; + traits.be_variant = PcmSubformat_SInt20_3_Be; + traits.le_variant = PcmSubformat_SInt20_3_Le; break; - case PcmFormat_UInt20_3: + case PcmSubformat_UInt20_3: + traits.id = PcmSubformat_UInt20_3; + traits.name = "u20_3"; traits.bit_width = 24; traits.bit_depth = 20; traits.flags = Pcm_IsInteger; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_UInt20_3_Be; + traits.portable_alias = PcmSubformat_UInt20_3_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_UInt20_3_Le; + traits.portable_alias = PcmSubformat_UInt20_3_Le; #endif - traits.default_variant = PcmFormat_UInt20_3; - traits.be_variant = PcmFormat_UInt20_3_Be; - traits.le_variant = PcmFormat_UInt20_3_Le; + traits.default_variant = PcmSubformat_UInt20_3; + traits.be_variant = PcmSubformat_UInt20_3_Be; + traits.le_variant = PcmSubformat_UInt20_3_Le; break; - case PcmFormat_UInt20_3_Be: + case PcmSubformat_UInt20_3_Be: + traits.id = PcmSubformat_UInt20_3_Be; + traits.name = "u20_3be"; traits.bit_width = 24; traits.bit_depth = 20; traits.flags = Pcm_IsInteger; @@ -5187,13 +5221,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_UInt20_3_Be; - traits.default_variant = PcmFormat_UInt20_3; - traits.be_variant = PcmFormat_UInt20_3_Be; - traits.le_variant = PcmFormat_UInt20_3_Le; + traits.portable_alias = PcmSubformat_UInt20_3_Be; + traits.default_variant = PcmSubformat_UInt20_3; + traits.be_variant = PcmSubformat_UInt20_3_Be; + traits.le_variant = PcmSubformat_UInt20_3_Le; break; - case PcmFormat_UInt20_3_Le: + case PcmSubformat_UInt20_3_Le: + traits.id = PcmSubformat_UInt20_3_Le; + traits.name = "u20_3le"; traits.bit_width = 24; traits.bit_depth = 20; traits.flags = Pcm_IsInteger; @@ -5202,29 +5238,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_UInt20_3_Le; - traits.default_variant = PcmFormat_UInt20_3; - traits.be_variant = PcmFormat_UInt20_3_Be; - traits.le_variant = PcmFormat_UInt20_3_Le; + traits.portable_alias = PcmSubformat_UInt20_3_Le; + traits.default_variant = PcmSubformat_UInt20_3; + traits.be_variant = PcmSubformat_UInt20_3_Be; + traits.le_variant = PcmSubformat_UInt20_3_Le; break; - case PcmFormat_SInt20_4: + case PcmSubformat_SInt20_4: + traits.id = PcmSubformat_SInt20_4; + traits.name = "s20_4"; traits.bit_width = 32; traits.bit_depth = 20; traits.flags = Pcm_IsInteger | Pcm_IsSigned; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_SInt20_4_Be; + traits.portable_alias = PcmSubformat_SInt20_4_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_SInt20_4_Le; + traits.portable_alias = PcmSubformat_SInt20_4_Le; #endif - traits.default_variant = PcmFormat_SInt20_4; - traits.be_variant = PcmFormat_SInt20_4_Be; - traits.le_variant = PcmFormat_SInt20_4_Le; + traits.default_variant = PcmSubformat_SInt20_4; + traits.be_variant = PcmSubformat_SInt20_4_Be; + traits.le_variant = PcmSubformat_SInt20_4_Le; break; - case PcmFormat_SInt20_4_Be: + case PcmSubformat_SInt20_4_Be: + traits.id = PcmSubformat_SInt20_4_Be; + traits.name = "s20_4be"; traits.bit_width = 32; traits.bit_depth = 20; traits.flags = Pcm_IsInteger | Pcm_IsSigned; @@ -5233,13 +5273,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_SInt20_4_Be; - traits.default_variant = PcmFormat_SInt20_4; - traits.be_variant = PcmFormat_SInt20_4_Be; - traits.le_variant = PcmFormat_SInt20_4_Le; + traits.portable_alias = PcmSubformat_SInt20_4_Be; + traits.default_variant = PcmSubformat_SInt20_4; + traits.be_variant = PcmSubformat_SInt20_4_Be; + traits.le_variant = PcmSubformat_SInt20_4_Le; break; - case PcmFormat_SInt20_4_Le: + case PcmSubformat_SInt20_4_Le: + traits.id = PcmSubformat_SInt20_4_Le; + traits.name = "s20_4le"; traits.bit_width = 32; traits.bit_depth = 20; traits.flags = Pcm_IsInteger | Pcm_IsSigned; @@ -5248,29 +5290,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_SInt20_4_Le; - traits.default_variant = PcmFormat_SInt20_4; - traits.be_variant = PcmFormat_SInt20_4_Be; - traits.le_variant = PcmFormat_SInt20_4_Le; + traits.portable_alias = PcmSubformat_SInt20_4_Le; + traits.default_variant = PcmSubformat_SInt20_4; + traits.be_variant = PcmSubformat_SInt20_4_Be; + traits.le_variant = PcmSubformat_SInt20_4_Le; break; - case PcmFormat_UInt20_4: + case PcmSubformat_UInt20_4: + traits.id = PcmSubformat_UInt20_4; + traits.name = "u20_4"; traits.bit_width = 32; traits.bit_depth = 20; traits.flags = Pcm_IsInteger; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_UInt20_4_Be; + traits.portable_alias = PcmSubformat_UInt20_4_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_UInt20_4_Le; + traits.portable_alias = PcmSubformat_UInt20_4_Le; #endif - traits.default_variant = PcmFormat_UInt20_4; - traits.be_variant = PcmFormat_UInt20_4_Be; - traits.le_variant = PcmFormat_UInt20_4_Le; + traits.default_variant = PcmSubformat_UInt20_4; + traits.be_variant = PcmSubformat_UInt20_4_Be; + traits.le_variant = PcmSubformat_UInt20_4_Le; break; - case PcmFormat_UInt20_4_Be: + case PcmSubformat_UInt20_4_Be: + traits.id = PcmSubformat_UInt20_4_Be; + traits.name = "u20_4be"; traits.bit_width = 32; traits.bit_depth = 20; traits.flags = Pcm_IsInteger; @@ -5279,13 +5325,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_UInt20_4_Be; - traits.default_variant = PcmFormat_UInt20_4; - traits.be_variant = PcmFormat_UInt20_4_Be; - traits.le_variant = PcmFormat_UInt20_4_Le; + traits.portable_alias = PcmSubformat_UInt20_4_Be; + traits.default_variant = PcmSubformat_UInt20_4; + traits.be_variant = PcmSubformat_UInt20_4_Be; + traits.le_variant = PcmSubformat_UInt20_4_Le; break; - case PcmFormat_UInt20_4_Le: + case PcmSubformat_UInt20_4_Le: + traits.id = PcmSubformat_UInt20_4_Le; + traits.name = "u20_4le"; traits.bit_width = 32; traits.bit_depth = 20; traits.flags = Pcm_IsInteger; @@ -5294,29 +5342,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_UInt20_4_Le; - traits.default_variant = PcmFormat_UInt20_4; - traits.be_variant = PcmFormat_UInt20_4_Be; - traits.le_variant = PcmFormat_UInt20_4_Le; + traits.portable_alias = PcmSubformat_UInt20_4_Le; + traits.default_variant = PcmSubformat_UInt20_4; + traits.be_variant = PcmSubformat_UInt20_4_Be; + traits.le_variant = PcmSubformat_UInt20_4_Le; break; - case PcmFormat_SInt24: + case PcmSubformat_SInt24: + traits.id = PcmSubformat_SInt24; + traits.name = "s24"; traits.bit_width = 24; traits.bit_depth = 24; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsPacked | Pcm_IsAligned; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_SInt24_Be; + traits.portable_alias = PcmSubformat_SInt24_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_SInt24_Le; + traits.portable_alias = PcmSubformat_SInt24_Le; #endif - traits.default_variant = PcmFormat_SInt24; - traits.be_variant = PcmFormat_SInt24_Be; - traits.le_variant = PcmFormat_SInt24_Le; + traits.default_variant = PcmSubformat_SInt24; + traits.be_variant = PcmSubformat_SInt24_Be; + traits.le_variant = PcmSubformat_SInt24_Le; break; - case PcmFormat_SInt24_Be: + case PcmSubformat_SInt24_Be: + traits.id = PcmSubformat_SInt24_Be; + traits.name = "s24_be"; traits.bit_width = 24; traits.bit_depth = 24; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsPacked | Pcm_IsAligned; @@ -5325,13 +5377,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_SInt24_Be; - traits.default_variant = PcmFormat_SInt24; - traits.be_variant = PcmFormat_SInt24_Be; - traits.le_variant = PcmFormat_SInt24_Le; + traits.portable_alias = PcmSubformat_SInt24_Be; + traits.default_variant = PcmSubformat_SInt24; + traits.be_variant = PcmSubformat_SInt24_Be; + traits.le_variant = PcmSubformat_SInt24_Le; break; - case PcmFormat_SInt24_Le: + case PcmSubformat_SInt24_Le: + traits.id = PcmSubformat_SInt24_Le; + traits.name = "s24_le"; traits.bit_width = 24; traits.bit_depth = 24; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsPacked | Pcm_IsAligned; @@ -5340,29 +5394,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_SInt24_Le; - traits.default_variant = PcmFormat_SInt24; - traits.be_variant = PcmFormat_SInt24_Be; - traits.le_variant = PcmFormat_SInt24_Le; + traits.portable_alias = PcmSubformat_SInt24_Le; + traits.default_variant = PcmSubformat_SInt24; + traits.be_variant = PcmSubformat_SInt24_Be; + traits.le_variant = PcmSubformat_SInt24_Le; break; - case PcmFormat_UInt24: + case PcmSubformat_UInt24: + traits.id = PcmSubformat_UInt24; + traits.name = "u24"; traits.bit_width = 24; traits.bit_depth = 24; traits.flags = Pcm_IsInteger | Pcm_IsPacked | Pcm_IsAligned; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_UInt24_Be; + traits.portable_alias = PcmSubformat_UInt24_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_UInt24_Le; + traits.portable_alias = PcmSubformat_UInt24_Le; #endif - traits.default_variant = PcmFormat_UInt24; - traits.be_variant = PcmFormat_UInt24_Be; - traits.le_variant = PcmFormat_UInt24_Le; + traits.default_variant = PcmSubformat_UInt24; + traits.be_variant = PcmSubformat_UInt24_Be; + traits.le_variant = PcmSubformat_UInt24_Le; break; - case PcmFormat_UInt24_Be: + case PcmSubformat_UInt24_Be: + traits.id = PcmSubformat_UInt24_Be; + traits.name = "u24_be"; traits.bit_width = 24; traits.bit_depth = 24; traits.flags = Pcm_IsInteger | Pcm_IsPacked | Pcm_IsAligned; @@ -5371,13 +5429,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_UInt24_Be; - traits.default_variant = PcmFormat_UInt24; - traits.be_variant = PcmFormat_UInt24_Be; - traits.le_variant = PcmFormat_UInt24_Le; + traits.portable_alias = PcmSubformat_UInt24_Be; + traits.default_variant = PcmSubformat_UInt24; + traits.be_variant = PcmSubformat_UInt24_Be; + traits.le_variant = PcmSubformat_UInt24_Le; break; - case PcmFormat_UInt24_Le: + case PcmSubformat_UInt24_Le: + traits.id = PcmSubformat_UInt24_Le; + traits.name = "u24_le"; traits.bit_width = 24; traits.bit_depth = 24; traits.flags = Pcm_IsInteger | Pcm_IsPacked | Pcm_IsAligned; @@ -5386,29 +5446,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_UInt24_Le; - traits.default_variant = PcmFormat_UInt24; - traits.be_variant = PcmFormat_UInt24_Be; - traits.le_variant = PcmFormat_UInt24_Le; + traits.portable_alias = PcmSubformat_UInt24_Le; + traits.default_variant = PcmSubformat_UInt24; + traits.be_variant = PcmSubformat_UInt24_Be; + traits.le_variant = PcmSubformat_UInt24_Le; break; - case PcmFormat_SInt24_4: + case PcmSubformat_SInt24_4: + traits.id = PcmSubformat_SInt24_4; + traits.name = "s24_4"; traits.bit_width = 32; traits.bit_depth = 24; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsAligned; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_SInt24_4_Be; + traits.portable_alias = PcmSubformat_SInt24_4_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_SInt24_4_Le; + traits.portable_alias = PcmSubformat_SInt24_4_Le; #endif - traits.default_variant = PcmFormat_SInt24_4; - traits.be_variant = PcmFormat_SInt24_4_Be; - traits.le_variant = PcmFormat_SInt24_4_Le; + traits.default_variant = PcmSubformat_SInt24_4; + traits.be_variant = PcmSubformat_SInt24_4_Be; + traits.le_variant = PcmSubformat_SInt24_4_Le; break; - case PcmFormat_SInt24_4_Be: + case PcmSubformat_SInt24_4_Be: + traits.id = PcmSubformat_SInt24_4_Be; + traits.name = "s24_4be"; traits.bit_width = 32; traits.bit_depth = 24; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsAligned; @@ -5417,13 +5481,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_SInt24_4_Be; - traits.default_variant = PcmFormat_SInt24_4; - traits.be_variant = PcmFormat_SInt24_4_Be; - traits.le_variant = PcmFormat_SInt24_4_Le; + traits.portable_alias = PcmSubformat_SInt24_4_Be; + traits.default_variant = PcmSubformat_SInt24_4; + traits.be_variant = PcmSubformat_SInt24_4_Be; + traits.le_variant = PcmSubformat_SInt24_4_Le; break; - case PcmFormat_SInt24_4_Le: + case PcmSubformat_SInt24_4_Le: + traits.id = PcmSubformat_SInt24_4_Le; + traits.name = "s24_4le"; traits.bit_width = 32; traits.bit_depth = 24; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsAligned; @@ -5432,29 +5498,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_SInt24_4_Le; - traits.default_variant = PcmFormat_SInt24_4; - traits.be_variant = PcmFormat_SInt24_4_Be; - traits.le_variant = PcmFormat_SInt24_4_Le; + traits.portable_alias = PcmSubformat_SInt24_4_Le; + traits.default_variant = PcmSubformat_SInt24_4; + traits.be_variant = PcmSubformat_SInt24_4_Be; + traits.le_variant = PcmSubformat_SInt24_4_Le; break; - case PcmFormat_UInt24_4: + case PcmSubformat_UInt24_4: + traits.id = PcmSubformat_UInt24_4; + traits.name = "u24_4"; traits.bit_width = 32; traits.bit_depth = 24; traits.flags = Pcm_IsInteger | Pcm_IsAligned; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_UInt24_4_Be; + traits.portable_alias = PcmSubformat_UInt24_4_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_UInt24_4_Le; + traits.portable_alias = PcmSubformat_UInt24_4_Le; #endif - traits.default_variant = PcmFormat_UInt24_4; - traits.be_variant = PcmFormat_UInt24_4_Be; - traits.le_variant = PcmFormat_UInt24_4_Le; + traits.default_variant = PcmSubformat_UInt24_4; + traits.be_variant = PcmSubformat_UInt24_4_Be; + traits.le_variant = PcmSubformat_UInt24_4_Le; break; - case PcmFormat_UInt24_4_Be: + case PcmSubformat_UInt24_4_Be: + traits.id = PcmSubformat_UInt24_4_Be; + traits.name = "u24_4be"; traits.bit_width = 32; traits.bit_depth = 24; traits.flags = Pcm_IsInteger | Pcm_IsAligned; @@ -5463,13 +5533,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_UInt24_4_Be; - traits.default_variant = PcmFormat_UInt24_4; - traits.be_variant = PcmFormat_UInt24_4_Be; - traits.le_variant = PcmFormat_UInt24_4_Le; + traits.portable_alias = PcmSubformat_UInt24_4_Be; + traits.default_variant = PcmSubformat_UInt24_4; + traits.be_variant = PcmSubformat_UInt24_4_Be; + traits.le_variant = PcmSubformat_UInt24_4_Le; break; - case PcmFormat_UInt24_4_Le: + case PcmSubformat_UInt24_4_Le: + traits.id = PcmSubformat_UInt24_4_Le; + traits.name = "u24_4le"; traits.bit_width = 32; traits.bit_depth = 24; traits.flags = Pcm_IsInteger | Pcm_IsAligned; @@ -5478,29 +5550,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_UInt24_4_Le; - traits.default_variant = PcmFormat_UInt24_4; - traits.be_variant = PcmFormat_UInt24_4_Be; - traits.le_variant = PcmFormat_UInt24_4_Le; + traits.portable_alias = PcmSubformat_UInt24_4_Le; + traits.default_variant = PcmSubformat_UInt24_4; + traits.be_variant = PcmSubformat_UInt24_4_Be; + traits.le_variant = PcmSubformat_UInt24_4_Le; break; - case PcmFormat_SInt32: + case PcmSubformat_SInt32: + traits.id = PcmSubformat_SInt32; + traits.name = "s32"; traits.bit_width = 32; traits.bit_depth = 32; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsPacked | Pcm_IsAligned; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_SInt32_Be; + traits.portable_alias = PcmSubformat_SInt32_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_SInt32_Le; + traits.portable_alias = PcmSubformat_SInt32_Le; #endif - traits.default_variant = PcmFormat_SInt32; - traits.be_variant = PcmFormat_SInt32_Be; - traits.le_variant = PcmFormat_SInt32_Le; + traits.default_variant = PcmSubformat_SInt32; + traits.be_variant = PcmSubformat_SInt32_Be; + traits.le_variant = PcmSubformat_SInt32_Le; break; - case PcmFormat_SInt32_Be: + case PcmSubformat_SInt32_Be: + traits.id = PcmSubformat_SInt32_Be; + traits.name = "s32_be"; traits.bit_width = 32; traits.bit_depth = 32; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsPacked | Pcm_IsAligned; @@ -5509,13 +5585,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_SInt32_Be; - traits.default_variant = PcmFormat_SInt32; - traits.be_variant = PcmFormat_SInt32_Be; - traits.le_variant = PcmFormat_SInt32_Le; + traits.portable_alias = PcmSubformat_SInt32_Be; + traits.default_variant = PcmSubformat_SInt32; + traits.be_variant = PcmSubformat_SInt32_Be; + traits.le_variant = PcmSubformat_SInt32_Le; break; - case PcmFormat_SInt32_Le: + case PcmSubformat_SInt32_Le: + traits.id = PcmSubformat_SInt32_Le; + traits.name = "s32_le"; traits.bit_width = 32; traits.bit_depth = 32; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsPacked | Pcm_IsAligned; @@ -5524,29 +5602,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_SInt32_Le; - traits.default_variant = PcmFormat_SInt32; - traits.be_variant = PcmFormat_SInt32_Be; - traits.le_variant = PcmFormat_SInt32_Le; + traits.portable_alias = PcmSubformat_SInt32_Le; + traits.default_variant = PcmSubformat_SInt32; + traits.be_variant = PcmSubformat_SInt32_Be; + traits.le_variant = PcmSubformat_SInt32_Le; break; - case PcmFormat_UInt32: + case PcmSubformat_UInt32: + traits.id = PcmSubformat_UInt32; + traits.name = "u32"; traits.bit_width = 32; traits.bit_depth = 32; traits.flags = Pcm_IsInteger | Pcm_IsPacked | Pcm_IsAligned; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_UInt32_Be; + traits.portable_alias = PcmSubformat_UInt32_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_UInt32_Le; + traits.portable_alias = PcmSubformat_UInt32_Le; #endif - traits.default_variant = PcmFormat_UInt32; - traits.be_variant = PcmFormat_UInt32_Be; - traits.le_variant = PcmFormat_UInt32_Le; + traits.default_variant = PcmSubformat_UInt32; + traits.be_variant = PcmSubformat_UInt32_Be; + traits.le_variant = PcmSubformat_UInt32_Le; break; - case PcmFormat_UInt32_Be: + case PcmSubformat_UInt32_Be: + traits.id = PcmSubformat_UInt32_Be; + traits.name = "u32_be"; traits.bit_width = 32; traits.bit_depth = 32; traits.flags = Pcm_IsInteger | Pcm_IsPacked | Pcm_IsAligned; @@ -5555,13 +5637,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_UInt32_Be; - traits.default_variant = PcmFormat_UInt32; - traits.be_variant = PcmFormat_UInt32_Be; - traits.le_variant = PcmFormat_UInt32_Le; + traits.portable_alias = PcmSubformat_UInt32_Be; + traits.default_variant = PcmSubformat_UInt32; + traits.be_variant = PcmSubformat_UInt32_Be; + traits.le_variant = PcmSubformat_UInt32_Le; break; - case PcmFormat_UInt32_Le: + case PcmSubformat_UInt32_Le: + traits.id = PcmSubformat_UInt32_Le; + traits.name = "u32_le"; traits.bit_width = 32; traits.bit_depth = 32; traits.flags = Pcm_IsInteger | Pcm_IsPacked | Pcm_IsAligned; @@ -5570,29 +5654,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_UInt32_Le; - traits.default_variant = PcmFormat_UInt32; - traits.be_variant = PcmFormat_UInt32_Be; - traits.le_variant = PcmFormat_UInt32_Le; + traits.portable_alias = PcmSubformat_UInt32_Le; + traits.default_variant = PcmSubformat_UInt32; + traits.be_variant = PcmSubformat_UInt32_Be; + traits.le_variant = PcmSubformat_UInt32_Le; break; - case PcmFormat_SInt64: + case PcmSubformat_SInt64: + traits.id = PcmSubformat_SInt64; + traits.name = "s64"; traits.bit_width = 64; traits.bit_depth = 64; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsPacked | Pcm_IsAligned; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_SInt64_Be; + traits.portable_alias = PcmSubformat_SInt64_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_SInt64_Le; + traits.portable_alias = PcmSubformat_SInt64_Le; #endif - traits.default_variant = PcmFormat_SInt64; - traits.be_variant = PcmFormat_SInt64_Be; - traits.le_variant = PcmFormat_SInt64_Le; + traits.default_variant = PcmSubformat_SInt64; + traits.be_variant = PcmSubformat_SInt64_Be; + traits.le_variant = PcmSubformat_SInt64_Le; break; - case PcmFormat_SInt64_Be: + case PcmSubformat_SInt64_Be: + traits.id = PcmSubformat_SInt64_Be; + traits.name = "s64_be"; traits.bit_width = 64; traits.bit_depth = 64; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsPacked | Pcm_IsAligned; @@ -5601,13 +5689,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_SInt64_Be; - traits.default_variant = PcmFormat_SInt64; - traits.be_variant = PcmFormat_SInt64_Be; - traits.le_variant = PcmFormat_SInt64_Le; + traits.portable_alias = PcmSubformat_SInt64_Be; + traits.default_variant = PcmSubformat_SInt64; + traits.be_variant = PcmSubformat_SInt64_Be; + traits.le_variant = PcmSubformat_SInt64_Le; break; - case PcmFormat_SInt64_Le: + case PcmSubformat_SInt64_Le: + traits.id = PcmSubformat_SInt64_Le; + traits.name = "s64_le"; traits.bit_width = 64; traits.bit_depth = 64; traits.flags = Pcm_IsInteger | Pcm_IsSigned | Pcm_IsPacked | Pcm_IsAligned; @@ -5616,29 +5706,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_SInt64_Le; - traits.default_variant = PcmFormat_SInt64; - traits.be_variant = PcmFormat_SInt64_Be; - traits.le_variant = PcmFormat_SInt64_Le; + traits.portable_alias = PcmSubformat_SInt64_Le; + traits.default_variant = PcmSubformat_SInt64; + traits.be_variant = PcmSubformat_SInt64_Be; + traits.le_variant = PcmSubformat_SInt64_Le; break; - case PcmFormat_UInt64: + case PcmSubformat_UInt64: + traits.id = PcmSubformat_UInt64; + traits.name = "u64"; traits.bit_width = 64; traits.bit_depth = 64; traits.flags = Pcm_IsInteger | Pcm_IsPacked | Pcm_IsAligned; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_UInt64_Be; + traits.portable_alias = PcmSubformat_UInt64_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_UInt64_Le; + traits.portable_alias = PcmSubformat_UInt64_Le; #endif - traits.default_variant = PcmFormat_UInt64; - traits.be_variant = PcmFormat_UInt64_Be; - traits.le_variant = PcmFormat_UInt64_Le; + traits.default_variant = PcmSubformat_UInt64; + traits.be_variant = PcmSubformat_UInt64_Be; + traits.le_variant = PcmSubformat_UInt64_Le; break; - case PcmFormat_UInt64_Be: + case PcmSubformat_UInt64_Be: + traits.id = PcmSubformat_UInt64_Be; + traits.name = "u64_be"; traits.bit_width = 64; traits.bit_depth = 64; traits.flags = Pcm_IsInteger | Pcm_IsPacked | Pcm_IsAligned; @@ -5647,13 +5741,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_UInt64_Be; - traits.default_variant = PcmFormat_UInt64; - traits.be_variant = PcmFormat_UInt64_Be; - traits.le_variant = PcmFormat_UInt64_Le; + traits.portable_alias = PcmSubformat_UInt64_Be; + traits.default_variant = PcmSubformat_UInt64; + traits.be_variant = PcmSubformat_UInt64_Be; + traits.le_variant = PcmSubformat_UInt64_Le; break; - case PcmFormat_UInt64_Le: + case PcmSubformat_UInt64_Le: + traits.id = PcmSubformat_UInt64_Le; + traits.name = "u64_le"; traits.bit_width = 64; traits.bit_depth = 64; traits.flags = Pcm_IsInteger | Pcm_IsPacked | Pcm_IsAligned; @@ -5662,29 +5758,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_UInt64_Le; - traits.default_variant = PcmFormat_UInt64; - traits.be_variant = PcmFormat_UInt64_Be; - traits.le_variant = PcmFormat_UInt64_Le; + traits.portable_alias = PcmSubformat_UInt64_Le; + traits.default_variant = PcmSubformat_UInt64; + traits.be_variant = PcmSubformat_UInt64_Be; + traits.le_variant = PcmSubformat_UInt64_Le; break; - case PcmFormat_Float32: + case PcmSubformat_Float32: + traits.id = PcmSubformat_Float32; + traits.name = "f32"; traits.bit_width = 32; traits.bit_depth = 32; traits.flags = Pcm_IsFloat | Pcm_IsSigned | Pcm_IsPacked | Pcm_IsAligned; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_Float32_Be; + traits.portable_alias = PcmSubformat_Float32_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_Float32_Le; + traits.portable_alias = PcmSubformat_Float32_Le; #endif - traits.default_variant = PcmFormat_Float32; - traits.be_variant = PcmFormat_Float32_Be; - traits.le_variant = PcmFormat_Float32_Le; + traits.default_variant = PcmSubformat_Float32; + traits.be_variant = PcmSubformat_Float32_Be; + traits.le_variant = PcmSubformat_Float32_Le; break; - case PcmFormat_Float32_Be: + case PcmSubformat_Float32_Be: + traits.id = PcmSubformat_Float32_Be; + traits.name = "f32_be"; traits.bit_width = 32; traits.bit_depth = 32; traits.flags = Pcm_IsFloat | Pcm_IsSigned | Pcm_IsPacked | Pcm_IsAligned; @@ -5693,13 +5793,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_Float32_Be; - traits.default_variant = PcmFormat_Float32; - traits.be_variant = PcmFormat_Float32_Be; - traits.le_variant = PcmFormat_Float32_Le; + traits.portable_alias = PcmSubformat_Float32_Be; + traits.default_variant = PcmSubformat_Float32; + traits.be_variant = PcmSubformat_Float32_Be; + traits.le_variant = PcmSubformat_Float32_Le; break; - case PcmFormat_Float32_Le: + case PcmSubformat_Float32_Le: + traits.id = PcmSubformat_Float32_Le; + traits.name = "f32_le"; traits.bit_width = 32; traits.bit_depth = 32; traits.flags = Pcm_IsFloat | Pcm_IsSigned | Pcm_IsPacked | Pcm_IsAligned; @@ -5708,29 +5810,33 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_Float32_Le; - traits.default_variant = PcmFormat_Float32; - traits.be_variant = PcmFormat_Float32_Be; - traits.le_variant = PcmFormat_Float32_Le; + traits.portable_alias = PcmSubformat_Float32_Le; + traits.default_variant = PcmSubformat_Float32; + traits.be_variant = PcmSubformat_Float32_Be; + traits.le_variant = PcmSubformat_Float32_Le; break; - case PcmFormat_Float64: + case PcmSubformat_Float64: + traits.id = PcmSubformat_Float64; + traits.name = "f64"; traits.bit_width = 64; traits.bit_depth = 64; traits.flags = Pcm_IsFloat | Pcm_IsSigned | Pcm_IsPacked | Pcm_IsAligned; #if ROC_CPU_ENDIAN == ROC_CPU_BE traits.flags |= Pcm_IsNative | Pcm_IsBig; - traits.portable_alias = PcmFormat_Float64_Be; + traits.portable_alias = PcmSubformat_Float64_Be; #else traits.flags |= Pcm_IsNative | Pcm_IsLittle; - traits.portable_alias = PcmFormat_Float64_Le; + traits.portable_alias = PcmSubformat_Float64_Le; #endif - traits.default_variant = PcmFormat_Float64; - traits.be_variant = PcmFormat_Float64_Be; - traits.le_variant = PcmFormat_Float64_Le; + traits.default_variant = PcmSubformat_Float64; + traits.be_variant = PcmSubformat_Float64_Be; + traits.le_variant = PcmSubformat_Float64_Le; break; - case PcmFormat_Float64_Be: + case PcmSubformat_Float64_Be: + traits.id = PcmSubformat_Float64_Be; + traits.name = "f64_be"; traits.bit_width = 64; traits.bit_depth = 64; traits.flags = Pcm_IsFloat | Pcm_IsSigned | Pcm_IsPacked | Pcm_IsAligned; @@ -5739,13 +5845,15 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsBig; #endif - traits.portable_alias = PcmFormat_Float64_Be; - traits.default_variant = PcmFormat_Float64; - traits.be_variant = PcmFormat_Float64_Be; - traits.le_variant = PcmFormat_Float64_Le; + traits.portable_alias = PcmSubformat_Float64_Be; + traits.default_variant = PcmSubformat_Float64; + traits.be_variant = PcmSubformat_Float64_Be; + traits.le_variant = PcmSubformat_Float64_Le; break; - case PcmFormat_Float64_Le: + case PcmSubformat_Float64_Le: + traits.id = PcmSubformat_Float64_Le; + traits.name = "f64_le"; traits.bit_width = 64; traits.bit_depth = 64; traits.flags = Pcm_IsFloat | Pcm_IsSigned | Pcm_IsPacked | Pcm_IsAligned; @@ -5754,10 +5862,10 @@ PcmTraits pcm_format_traits(PcmFormat format) { #else traits.flags |= Pcm_IsLittle; #endif - traits.portable_alias = PcmFormat_Float64_Le; - traits.default_variant = PcmFormat_Float64; - traits.be_variant = PcmFormat_Float64_Be; - traits.le_variant = PcmFormat_Float64_Le; + traits.portable_alias = PcmSubformat_Float64_Le; + traits.default_variant = PcmSubformat_Float64; + traits.be_variant = PcmSubformat_Float64_Be; + traits.le_variant = PcmSubformat_Float64_Le; break; default: @@ -5767,163 +5875,163 @@ PcmTraits pcm_format_traits(PcmFormat format) { return traits; } -const char* pcm_format_to_str(PcmFormat format) { +const char* pcm_subformat_to_str(PcmSubformat format) { switch (format) { - case PcmFormat_SInt8: + case PcmSubformat_SInt8: return "s8"; - case PcmFormat_SInt8_Be: + case PcmSubformat_SInt8_Be: return "s8_be"; - case PcmFormat_SInt8_Le: + case PcmSubformat_SInt8_Le: return "s8_le"; - case PcmFormat_UInt8: + case PcmSubformat_UInt8: return "u8"; - case PcmFormat_UInt8_Be: + case PcmSubformat_UInt8_Be: return "u8_be"; - case PcmFormat_UInt8_Le: + case PcmSubformat_UInt8_Le: return "u8_le"; - case PcmFormat_SInt16: + case PcmSubformat_SInt16: return "s16"; - case PcmFormat_SInt16_Be: + case PcmSubformat_SInt16_Be: return "s16_be"; - case PcmFormat_SInt16_Le: + case PcmSubformat_SInt16_Le: return "s16_le"; - case PcmFormat_UInt16: + case PcmSubformat_UInt16: return "u16"; - case PcmFormat_UInt16_Be: + case PcmSubformat_UInt16_Be: return "u16_be"; - case PcmFormat_UInt16_Le: + case PcmSubformat_UInt16_Le: return "u16_le"; - case PcmFormat_SInt18: + case PcmSubformat_SInt18: return "s18"; - case PcmFormat_SInt18_Be: + case PcmSubformat_SInt18_Be: return "s18_be"; - case PcmFormat_SInt18_Le: + case PcmSubformat_SInt18_Le: return "s18_le"; - case PcmFormat_UInt18: + case PcmSubformat_UInt18: return "u18"; - case PcmFormat_UInt18_Be: + case PcmSubformat_UInt18_Be: return "u18_be"; - case PcmFormat_UInt18_Le: + case PcmSubformat_UInt18_Le: return "u18_le"; - case PcmFormat_SInt18_3: + case PcmSubformat_SInt18_3: return "s18_3"; - case PcmFormat_SInt18_3_Be: + case PcmSubformat_SInt18_3_Be: return "s18_3be"; - case PcmFormat_SInt18_3_Le: + case PcmSubformat_SInt18_3_Le: return "s18_3le"; - case PcmFormat_UInt18_3: + case PcmSubformat_UInt18_3: return "u18_3"; - case PcmFormat_UInt18_3_Be: + case PcmSubformat_UInt18_3_Be: return "u18_3be"; - case PcmFormat_UInt18_3_Le: + case PcmSubformat_UInt18_3_Le: return "u18_3le"; - case PcmFormat_SInt18_4: + case PcmSubformat_SInt18_4: return "s18_4"; - case PcmFormat_SInt18_4_Be: + case PcmSubformat_SInt18_4_Be: return "s18_4be"; - case PcmFormat_SInt18_4_Le: + case PcmSubformat_SInt18_4_Le: return "s18_4le"; - case PcmFormat_UInt18_4: + case PcmSubformat_UInt18_4: return "u18_4"; - case PcmFormat_UInt18_4_Be: + case PcmSubformat_UInt18_4_Be: return "u18_4be"; - case PcmFormat_UInt18_4_Le: + case PcmSubformat_UInt18_4_Le: return "u18_4le"; - case PcmFormat_SInt20: + case PcmSubformat_SInt20: return "s20"; - case PcmFormat_SInt20_Be: + case PcmSubformat_SInt20_Be: return "s20_be"; - case PcmFormat_SInt20_Le: + case PcmSubformat_SInt20_Le: return "s20_le"; - case PcmFormat_UInt20: + case PcmSubformat_UInt20: return "u20"; - case PcmFormat_UInt20_Be: + case PcmSubformat_UInt20_Be: return "u20_be"; - case PcmFormat_UInt20_Le: + case PcmSubformat_UInt20_Le: return "u20_le"; - case PcmFormat_SInt20_3: + case PcmSubformat_SInt20_3: return "s20_3"; - case PcmFormat_SInt20_3_Be: + case PcmSubformat_SInt20_3_Be: return "s20_3be"; - case PcmFormat_SInt20_3_Le: + case PcmSubformat_SInt20_3_Le: return "s20_3le"; - case PcmFormat_UInt20_3: + case PcmSubformat_UInt20_3: return "u20_3"; - case PcmFormat_UInt20_3_Be: + case PcmSubformat_UInt20_3_Be: return "u20_3be"; - case PcmFormat_UInt20_3_Le: + case PcmSubformat_UInt20_3_Le: return "u20_3le"; - case PcmFormat_SInt20_4: + case PcmSubformat_SInt20_4: return "s20_4"; - case PcmFormat_SInt20_4_Be: + case PcmSubformat_SInt20_4_Be: return "s20_4be"; - case PcmFormat_SInt20_4_Le: + case PcmSubformat_SInt20_4_Le: return "s20_4le"; - case PcmFormat_UInt20_4: + case PcmSubformat_UInt20_4: return "u20_4"; - case PcmFormat_UInt20_4_Be: + case PcmSubformat_UInt20_4_Be: return "u20_4be"; - case PcmFormat_UInt20_4_Le: + case PcmSubformat_UInt20_4_Le: return "u20_4le"; - case PcmFormat_SInt24: + case PcmSubformat_SInt24: return "s24"; - case PcmFormat_SInt24_Be: + case PcmSubformat_SInt24_Be: return "s24_be"; - case PcmFormat_SInt24_Le: + case PcmSubformat_SInt24_Le: return "s24_le"; - case PcmFormat_UInt24: + case PcmSubformat_UInt24: return "u24"; - case PcmFormat_UInt24_Be: + case PcmSubformat_UInt24_Be: return "u24_be"; - case PcmFormat_UInt24_Le: + case PcmSubformat_UInt24_Le: return "u24_le"; - case PcmFormat_SInt24_4: + case PcmSubformat_SInt24_4: return "s24_4"; - case PcmFormat_SInt24_4_Be: + case PcmSubformat_SInt24_4_Be: return "s24_4be"; - case PcmFormat_SInt24_4_Le: + case PcmSubformat_SInt24_4_Le: return "s24_4le"; - case PcmFormat_UInt24_4: + case PcmSubformat_UInt24_4: return "u24_4"; - case PcmFormat_UInt24_4_Be: + case PcmSubformat_UInt24_4_Be: return "u24_4be"; - case PcmFormat_UInt24_4_Le: + case PcmSubformat_UInt24_4_Le: return "u24_4le"; - case PcmFormat_SInt32: + case PcmSubformat_SInt32: return "s32"; - case PcmFormat_SInt32_Be: + case PcmSubformat_SInt32_Be: return "s32_be"; - case PcmFormat_SInt32_Le: + case PcmSubformat_SInt32_Le: return "s32_le"; - case PcmFormat_UInt32: + case PcmSubformat_UInt32: return "u32"; - case PcmFormat_UInt32_Be: + case PcmSubformat_UInt32_Be: return "u32_be"; - case PcmFormat_UInt32_Le: + case PcmSubformat_UInt32_Le: return "u32_le"; - case PcmFormat_SInt64: + case PcmSubformat_SInt64: return "s64"; - case PcmFormat_SInt64_Be: + case PcmSubformat_SInt64_Be: return "s64_be"; - case PcmFormat_SInt64_Le: + case PcmSubformat_SInt64_Le: return "s64_le"; - case PcmFormat_UInt64: + case PcmSubformat_UInt64: return "u64"; - case PcmFormat_UInt64_Be: + case PcmSubformat_UInt64_Be: return "u64_be"; - case PcmFormat_UInt64_Le: + case PcmSubformat_UInt64_Le: return "u64_le"; - case PcmFormat_Float32: + case PcmSubformat_Float32: return "f32"; - case PcmFormat_Float32_Be: + case PcmSubformat_Float32_Be: return "f32_be"; - case PcmFormat_Float32_Le: + case PcmSubformat_Float32_Le: return "f32_le"; - case PcmFormat_Float64: + case PcmSubformat_Float64: return "f64"; - case PcmFormat_Float64_Be: + case PcmSubformat_Float64_Be: return "f64_be"; - case PcmFormat_Float64_Le: + case PcmSubformat_Float64_Le: return "f64_le"; default: break; @@ -5931,332 +6039,332 @@ const char* pcm_format_to_str(PcmFormat format) { return NULL; } -PcmFormat pcm_format_from_str(const char* str) { +PcmSubformat pcm_subformat_from_str(const char* str) { if (!str) { - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } if (str[0] == 'f') { if (str[1] == '3') { if (str[2] == '2') { if (strcmp(str, "f32") == 0) { - return PcmFormat_Float32; + return PcmSubformat_Float32; } if (strcmp(str, "f32_be") == 0) { - return PcmFormat_Float32_Be; + return PcmSubformat_Float32_Be; } if (strcmp(str, "f32_le") == 0) { - return PcmFormat_Float32_Le; + return PcmSubformat_Float32_Le; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } if (str[1] == '6') { if (str[2] == '4') { if (strcmp(str, "f64") == 0) { - return PcmFormat_Float64; + return PcmSubformat_Float64; } if (strcmp(str, "f64_be") == 0) { - return PcmFormat_Float64_Be; + return PcmSubformat_Float64_Be; } if (strcmp(str, "f64_le") == 0) { - return PcmFormat_Float64_Le; + return PcmSubformat_Float64_Le; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } if (str[0] == 's') { if (str[1] == '1') { if (str[2] == '6') { if (strcmp(str, "s16") == 0) { - return PcmFormat_SInt16; + return PcmSubformat_SInt16; } if (strcmp(str, "s16_be") == 0) { - return PcmFormat_SInt16_Be; + return PcmSubformat_SInt16_Be; } if (strcmp(str, "s16_le") == 0) { - return PcmFormat_SInt16_Le; + return PcmSubformat_SInt16_Le; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } if (str[2] == '8') { if (strcmp(str, "s18") == 0) { - return PcmFormat_SInt18; + return PcmSubformat_SInt18; } if (strcmp(str, "s18_be") == 0) { - return PcmFormat_SInt18_Be; + return PcmSubformat_SInt18_Be; } if (strcmp(str, "s18_le") == 0) { - return PcmFormat_SInt18_Le; + return PcmSubformat_SInt18_Le; } if (strcmp(str, "s18_3") == 0) { - return PcmFormat_SInt18_3; + return PcmSubformat_SInt18_3; } if (strcmp(str, "s18_3be") == 0) { - return PcmFormat_SInt18_3_Be; + return PcmSubformat_SInt18_3_Be; } if (strcmp(str, "s18_3le") == 0) { - return PcmFormat_SInt18_3_Le; + return PcmSubformat_SInt18_3_Le; } if (strcmp(str, "s18_4") == 0) { - return PcmFormat_SInt18_4; + return PcmSubformat_SInt18_4; } if (strcmp(str, "s18_4be") == 0) { - return PcmFormat_SInt18_4_Be; + return PcmSubformat_SInt18_4_Be; } if (strcmp(str, "s18_4le") == 0) { - return PcmFormat_SInt18_4_Le; + return PcmSubformat_SInt18_4_Le; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } if (str[1] == '2') { if (str[2] == '0') { if (strcmp(str, "s20") == 0) { - return PcmFormat_SInt20; + return PcmSubformat_SInt20; } if (strcmp(str, "s20_be") == 0) { - return PcmFormat_SInt20_Be; + return PcmSubformat_SInt20_Be; } if (strcmp(str, "s20_le") == 0) { - return PcmFormat_SInt20_Le; + return PcmSubformat_SInt20_Le; } if (strcmp(str, "s20_3") == 0) { - return PcmFormat_SInt20_3; + return PcmSubformat_SInt20_3; } if (strcmp(str, "s20_3be") == 0) { - return PcmFormat_SInt20_3_Be; + return PcmSubformat_SInt20_3_Be; } if (strcmp(str, "s20_3le") == 0) { - return PcmFormat_SInt20_3_Le; + return PcmSubformat_SInt20_3_Le; } if (strcmp(str, "s20_4") == 0) { - return PcmFormat_SInt20_4; + return PcmSubformat_SInt20_4; } if (strcmp(str, "s20_4be") == 0) { - return PcmFormat_SInt20_4_Be; + return PcmSubformat_SInt20_4_Be; } if (strcmp(str, "s20_4le") == 0) { - return PcmFormat_SInt20_4_Le; + return PcmSubformat_SInt20_4_Le; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } if (str[2] == '4') { if (strcmp(str, "s24") == 0) { - return PcmFormat_SInt24; + return PcmSubformat_SInt24; } if (strcmp(str, "s24_be") == 0) { - return PcmFormat_SInt24_Be; + return PcmSubformat_SInt24_Be; } if (strcmp(str, "s24_le") == 0) { - return PcmFormat_SInt24_Le; + return PcmSubformat_SInt24_Le; } if (strcmp(str, "s24_4") == 0) { - return PcmFormat_SInt24_4; + return PcmSubformat_SInt24_4; } if (strcmp(str, "s24_4be") == 0) { - return PcmFormat_SInt24_4_Be; + return PcmSubformat_SInt24_4_Be; } if (strcmp(str, "s24_4le") == 0) { - return PcmFormat_SInt24_4_Le; + return PcmSubformat_SInt24_4_Le; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } if (str[1] == '3') { if (str[2] == '2') { if (strcmp(str, "s32") == 0) { - return PcmFormat_SInt32; + return PcmSubformat_SInt32; } if (strcmp(str, "s32_be") == 0) { - return PcmFormat_SInt32_Be; + return PcmSubformat_SInt32_Be; } if (strcmp(str, "s32_le") == 0) { - return PcmFormat_SInt32_Le; + return PcmSubformat_SInt32_Le; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } if (str[1] == '6') { if (str[2] == '4') { if (strcmp(str, "s64") == 0) { - return PcmFormat_SInt64; + return PcmSubformat_SInt64; } if (strcmp(str, "s64_be") == 0) { - return PcmFormat_SInt64_Be; + return PcmSubformat_SInt64_Be; } if (strcmp(str, "s64_le") == 0) { - return PcmFormat_SInt64_Le; + return PcmSubformat_SInt64_Le; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } if (str[1] == '8') { if (strcmp(str, "s8") == 0) { - return PcmFormat_SInt8; + return PcmSubformat_SInt8; } if (strcmp(str, "s8_be") == 0) { - return PcmFormat_SInt8_Be; + return PcmSubformat_SInt8_Be; } if (strcmp(str, "s8_le") == 0) { - return PcmFormat_SInt8_Le; + return PcmSubformat_SInt8_Le; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } if (str[0] == 'u') { if (str[1] == '1') { if (str[2] == '6') { if (strcmp(str, "u16") == 0) { - return PcmFormat_UInt16; + return PcmSubformat_UInt16; } if (strcmp(str, "u16_be") == 0) { - return PcmFormat_UInt16_Be; + return PcmSubformat_UInt16_Be; } if (strcmp(str, "u16_le") == 0) { - return PcmFormat_UInt16_Le; + return PcmSubformat_UInt16_Le; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } if (str[2] == '8') { if (strcmp(str, "u18") == 0) { - return PcmFormat_UInt18; + return PcmSubformat_UInt18; } if (strcmp(str, "u18_be") == 0) { - return PcmFormat_UInt18_Be; + return PcmSubformat_UInt18_Be; } if (strcmp(str, "u18_le") == 0) { - return PcmFormat_UInt18_Le; + return PcmSubformat_UInt18_Le; } if (strcmp(str, "u18_3") == 0) { - return PcmFormat_UInt18_3; + return PcmSubformat_UInt18_3; } if (strcmp(str, "u18_3be") == 0) { - return PcmFormat_UInt18_3_Be; + return PcmSubformat_UInt18_3_Be; } if (strcmp(str, "u18_3le") == 0) { - return PcmFormat_UInt18_3_Le; + return PcmSubformat_UInt18_3_Le; } if (strcmp(str, "u18_4") == 0) { - return PcmFormat_UInt18_4; + return PcmSubformat_UInt18_4; } if (strcmp(str, "u18_4be") == 0) { - return PcmFormat_UInt18_4_Be; + return PcmSubformat_UInt18_4_Be; } if (strcmp(str, "u18_4le") == 0) { - return PcmFormat_UInt18_4_Le; + return PcmSubformat_UInt18_4_Le; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } if (str[1] == '2') { if (str[2] == '0') { if (strcmp(str, "u20") == 0) { - return PcmFormat_UInt20; + return PcmSubformat_UInt20; } if (strcmp(str, "u20_be") == 0) { - return PcmFormat_UInt20_Be; + return PcmSubformat_UInt20_Be; } if (strcmp(str, "u20_le") == 0) { - return PcmFormat_UInt20_Le; + return PcmSubformat_UInt20_Le; } if (strcmp(str, "u20_3") == 0) { - return PcmFormat_UInt20_3; + return PcmSubformat_UInt20_3; } if (strcmp(str, "u20_3be") == 0) { - return PcmFormat_UInt20_3_Be; + return PcmSubformat_UInt20_3_Be; } if (strcmp(str, "u20_3le") == 0) { - return PcmFormat_UInt20_3_Le; + return PcmSubformat_UInt20_3_Le; } if (strcmp(str, "u20_4") == 0) { - return PcmFormat_UInt20_4; + return PcmSubformat_UInt20_4; } if (strcmp(str, "u20_4be") == 0) { - return PcmFormat_UInt20_4_Be; + return PcmSubformat_UInt20_4_Be; } if (strcmp(str, "u20_4le") == 0) { - return PcmFormat_UInt20_4_Le; + return PcmSubformat_UInt20_4_Le; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } if (str[2] == '4') { if (strcmp(str, "u24") == 0) { - return PcmFormat_UInt24; + return PcmSubformat_UInt24; } if (strcmp(str, "u24_be") == 0) { - return PcmFormat_UInt24_Be; + return PcmSubformat_UInt24_Be; } if (strcmp(str, "u24_le") == 0) { - return PcmFormat_UInt24_Le; + return PcmSubformat_UInt24_Le; } if (strcmp(str, "u24_4") == 0) { - return PcmFormat_UInt24_4; + return PcmSubformat_UInt24_4; } if (strcmp(str, "u24_4be") == 0) { - return PcmFormat_UInt24_4_Be; + return PcmSubformat_UInt24_4_Be; } if (strcmp(str, "u24_4le") == 0) { - return PcmFormat_UInt24_4_Le; + return PcmSubformat_UInt24_4_Le; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } if (str[1] == '3') { if (str[2] == '2') { if (strcmp(str, "u32") == 0) { - return PcmFormat_UInt32; + return PcmSubformat_UInt32; } if (strcmp(str, "u32_be") == 0) { - return PcmFormat_UInt32_Be; + return PcmSubformat_UInt32_Be; } if (strcmp(str, "u32_le") == 0) { - return PcmFormat_UInt32_Le; + return PcmSubformat_UInt32_Le; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } if (str[1] == '6') { if (str[2] == '4') { if (strcmp(str, "u64") == 0) { - return PcmFormat_UInt64; + return PcmSubformat_UInt64; } if (strcmp(str, "u64_be") == 0) { - return PcmFormat_UInt64_Be; + return PcmSubformat_UInt64_Be; } if (strcmp(str, "u64_le") == 0) { - return PcmFormat_UInt64_Le; + return PcmSubformat_UInt64_Le; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } if (str[1] == '8') { if (strcmp(str, "u8") == 0) { - return PcmFormat_UInt8; + return PcmSubformat_UInt8; } if (strcmp(str, "u8_be") == 0) { - return PcmFormat_UInt8_Be; + return PcmSubformat_UInt8_Be; } if (strcmp(str, "u8_le") == 0) { - return PcmFormat_UInt8_Le; + return PcmSubformat_UInt8_Le; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } } // namespace audio diff --git a/src/internal_modules/roc_audio/pcm_mapper.cpp b/src/internal_modules/roc_audio/pcm_mapper.cpp index 11727952b..041ffcc7a 100644 --- a/src/internal_modules/roc_audio/pcm_mapper.cpp +++ b/src/internal_modules/roc_audio/pcm_mapper.cpp @@ -14,27 +14,27 @@ namespace roc { namespace audio { -PcmMapper::PcmMapper(PcmFormat input_fmt, PcmFormat output_fmt) +PcmMapper::PcmMapper(PcmSubformat input_fmt, PcmSubformat output_fmt) : input_fmt_(input_fmt) , output_fmt_(output_fmt) - , input_traits_(pcm_format_traits(input_fmt)) - , output_traits_(pcm_format_traits(output_fmt)) { + , input_traits_(pcm_subformat_traits(input_fmt)) + , output_traits_(pcm_subformat_traits(output_fmt)) { // To reduce code size, we generate converters only between raw and non-raw formats. // To convert between two non-raw formats, you need a pair of pcm mappers. - roc_panic_if_msg(input_fmt != Sample_RawFormat && output_fmt != Sample_RawFormat, + roc_panic_if_msg(input_fmt != PcmSubformat_Raw && output_fmt != PcmSubformat_Raw, "pcm mapper: either input or output format must be raw"); // This must not happen if checks above passed. - if (!(map_func_ = pcm_format_mapfn(input_fmt, output_fmt))) { + if (!(map_func_ = pcm_subformat_mapfn(input_fmt, output_fmt))) { roc_panic("pcm mapper: unable to select mapping function"); } } -PcmFormat PcmMapper::input_format() const { +PcmSubformat PcmMapper::input_format() const { return input_fmt_; } -PcmFormat PcmMapper::output_format() const { +PcmSubformat PcmMapper::output_format() const { return output_fmt_; } diff --git a/src/internal_modules/roc_audio/pcm_mapper.h b/src/internal_modules/roc_audio/pcm_mapper.h index ebfdb6c23..e6ed5c87a 100644 --- a/src/internal_modules/roc_audio/pcm_mapper.h +++ b/src/internal_modules/roc_audio/pcm_mapper.h @@ -12,7 +12,7 @@ #ifndef ROC_AUDIO_PCM_MAPPER_H_ #define ROC_AUDIO_PCM_MAPPER_H_ -#include "roc_audio/pcm_format.h" +#include "roc_audio/pcm_subformat.h" #include "roc_core/noncopyable.h" #include "roc_core/stddefs.h" @@ -21,17 +21,17 @@ namespace audio { //! PCM format mapper. //! Converts between two PCM formats. -//! Either input or output format must be raw samples (Sample_RawFormat). +//! Either input or output format must be raw samples (PcmSubformat_Raw). class PcmMapper : public core::NonCopyable<> { public: //! Initialize. - PcmMapper(PcmFormat input_fmt, PcmFormat output_fmt); + PcmMapper(PcmSubformat input_fmt, PcmSubformat output_fmt); //! Get input format. - PcmFormat input_format() const; + PcmSubformat input_format() const; //! Get output format. - PcmFormat output_format() const; + PcmSubformat output_format() const; //! Get number of input samples (total for all channels) for given number of bytes. size_t input_sample_count(size_t input_bytes) const; @@ -76,8 +76,8 @@ class PcmMapper : public core::NonCopyable<> { size_t n_samples); private: - const PcmFormat input_fmt_; - const PcmFormat output_fmt_; + const PcmSubformat input_fmt_; + const PcmSubformat output_fmt_; const PcmTraits input_traits_; const PcmTraits output_traits_; diff --git a/src/internal_modules/roc_audio/pcm_mapper_reader.cpp b/src/internal_modules/roc_audio/pcm_mapper_reader.cpp index 57b2fbba2..390c909b9 100644 --- a/src/internal_modules/roc_audio/pcm_mapper_reader.cpp +++ b/src/internal_modules/roc_audio/pcm_mapper_reader.cpp @@ -7,7 +7,7 @@ */ #include "roc_audio/pcm_mapper_reader.h" -#include "roc_audio/sample_format.h" +#include "roc_audio/format.h" #include "roc_audio/sample_spec_to_str.h" #include "roc_core/log.h" #include "roc_core/panic.h" @@ -25,10 +25,9 @@ PcmMapperReader::PcmMapperReader(IFrameReader& frame_reader, , out_spec_(out_spec) , num_ch_(out_spec.num_channels()) , init_status_(status::NoStatus) { - if (!in_spec_.is_valid() || !out_spec_.is_valid() - || in_spec_.sample_format() != SampleFormat_Pcm - || out_spec_.sample_format() != SampleFormat_Pcm) { - roc_panic("pcm mapper reader: required valid sample specs with pcm format:" + if (!in_spec_.is_complete() || !out_spec_.is_complete() + || in_spec_.format() != Format_Pcm || out_spec_.format() != Format_Pcm) { + roc_panic("pcm mapper reader: required complete sample specs with pcm format:" " in_spec=%s out_spec=%s", sample_spec_to_str(in_spec_).c_str(), sample_spec_to_str(out_spec_).c_str()); @@ -49,7 +48,8 @@ PcmMapperReader::PcmMapperReader(IFrameReader& frame_reader, sample_spec_to_str(in_spec_).c_str(), sample_spec_to_str(out_spec_).c_str()); } - mapper_.reset(new (mapper_) PcmMapper(in_spec_.pcm_format(), out_spec_.pcm_format())); + mapper_.reset(new (mapper_) + PcmMapper(in_spec_.pcm_subformat(), out_spec_.pcm_subformat())); if (mapper_->input_bit_count(1) % 8 != 0 || mapper_->output_bit_count(1) % 8 != 0) { roc_panic("pcm mapper reader: unsupported not byte-aligned encoding:" diff --git a/src/internal_modules/roc_audio/pcm_mapper_reader.h b/src/internal_modules/roc_audio/pcm_mapper_reader.h index 6fdfecd07..2f36eed69 100644 --- a/src/internal_modules/roc_audio/pcm_mapper_reader.h +++ b/src/internal_modules/roc_audio/pcm_mapper_reader.h @@ -26,7 +26,7 @@ namespace audio { //! PCM mapper reader. //! Reads frames from nested reader and maps them to another PCM format. //! @remarks -//! - Either input or output format must be raw samples (Sample_RawFormat). +//! - Either input or output format must be raw samples (PcmSubformat_Raw). //! - Both input and output formats must be byte-aligned. class PcmMapperReader : public IFrameReader, public core::NonCopyable<> { public: diff --git a/src/internal_modules/roc_audio/pcm_mapper_writer.cpp b/src/internal_modules/roc_audio/pcm_mapper_writer.cpp index a9ee25584..773d8941c 100644 --- a/src/internal_modules/roc_audio/pcm_mapper_writer.cpp +++ b/src/internal_modules/roc_audio/pcm_mapper_writer.cpp @@ -7,7 +7,7 @@ */ #include "roc_audio/pcm_mapper_writer.h" -#include "roc_audio/sample_format.h" +#include "roc_audio/format.h" #include "roc_audio/sample_spec_to_str.h" #include "roc_core/log.h" #include "roc_core/panic.h" @@ -25,10 +25,9 @@ PcmMapperWriter::PcmMapperWriter(IFrameWriter& frame_writer, , out_spec_(out_spec) , num_ch_(out_spec.num_channels()) , init_status_(status::NoStatus) { - if (!in_spec_.is_valid() || !out_spec_.is_valid() - || in_spec_.sample_format() != SampleFormat_Pcm - || out_spec_.sample_format() != SampleFormat_Pcm) { - roc_panic("pcm mapper writer: required valid sample specs with pcm format:" + if (!in_spec_.is_complete() || !out_spec_.is_complete() + || in_spec_.format() != Format_Pcm || out_spec_.format() != Format_Pcm) { + roc_panic("pcm mapper writer: required complete sample specs with pcm format:" " in_spec=%s out_spec=%s", sample_spec_to_str(in_spec_).c_str(), sample_spec_to_str(out_spec_).c_str()); @@ -49,7 +48,8 @@ PcmMapperWriter::PcmMapperWriter(IFrameWriter& frame_writer, sample_spec_to_str(in_spec_).c_str(), sample_spec_to_str(out_spec_).c_str()); } - mapper_.reset(new (mapper_) PcmMapper(in_spec_.pcm_format(), out_spec_.pcm_format())); + mapper_.reset(new (mapper_) + PcmMapper(in_spec_.pcm_subformat(), out_spec_.pcm_subformat())); if (mapper_->input_bit_count(1) % 8 != 0 || mapper_->output_bit_count(1) % 8 != 0) { roc_panic("pcm mapper writer: unsupported not byte-aligned encoding:" diff --git a/src/internal_modules/roc_audio/pcm_mapper_writer.h b/src/internal_modules/roc_audio/pcm_mapper_writer.h index 661435066..0323a276f 100644 --- a/src/internal_modules/roc_audio/pcm_mapper_writer.h +++ b/src/internal_modules/roc_audio/pcm_mapper_writer.h @@ -26,7 +26,7 @@ namespace audio { //! PCM mapper writer. //! Maps frames to another PCM format and writes them to nested writer. //! @remarks -//! - Either input or output format must be raw samples (Sample_RawFormat). +//! - Either input or output format must be raw samples (PcmSubformat_Raw). //! - Both input and output formats must be byte-aligned. class PcmMapperWriter : public IFrameWriter, public core::NonCopyable<> { public: diff --git a/src/internal_modules/roc_audio/pcm_format.h b/src/internal_modules/roc_audio/pcm_subformat.h similarity index 71% rename from src/internal_modules/roc_audio/pcm_format.h rename to src/internal_modules/roc_audio/pcm_subformat.h index 2ad620d3c..c4fec2aae 100644 --- a/src/internal_modules/roc_audio/pcm_format.h +++ b/src/internal_modules/roc_audio/pcm_subformat.h @@ -6,199 +6,203 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -//! @file roc_audio/pcm_format.h -//! @brief PCM format. +//! @file roc_audio/pcm_subformat.h +//! @brief PCM sub-format. -#ifndef ROC_AUDIO_PCM_FORMAT_H_ -#define ROC_AUDIO_PCM_FORMAT_H_ +#ifndef ROC_AUDIO_PCM_SUBFORMAT_H_ +#define ROC_AUDIO_PCM_SUBFORMAT_H_ #include "roc_core/stddefs.h" namespace roc { namespace audio { -//! PCM format. +//! PCM sub-format. +//! //! Defines PCM sample coding and endian. +//! Used when Format is set to Format_Pcm or other PCM-based format like +//! FLAC, ALAC, WAV, etc. +//! //! "Default endian" means use whatever endian is natural in current context, e.g.: //! - for network packets, big endian (a.k.a. network byte order) is default //! - for processing, native CPU endian is default //! - for WAV files, little endian is default -enum PcmFormat { +enum PcmSubformat { //! Invalid format. - PcmFormat_Invalid, + PcmSubformat_Invalid, //! 8-bit signed integer, default endian. - PcmFormat_SInt8, + PcmSubformat_SInt8, //! 8-bit signed integer, big endian. - PcmFormat_SInt8_Be, + PcmSubformat_SInt8_Be, //! 8-bit signed integer, little endian. - PcmFormat_SInt8_Le, + PcmSubformat_SInt8_Le, //! 8-bit unsigned integer, default endian. - PcmFormat_UInt8, + PcmSubformat_UInt8, //! 8-bit unsigned integer, big endian. - PcmFormat_UInt8_Be, + PcmSubformat_UInt8_Be, //! 8-bit unsigned integer, little endian. - PcmFormat_UInt8_Le, + PcmSubformat_UInt8_Le, //! 16-bit signed integer, default endian. - PcmFormat_SInt16, + PcmSubformat_SInt16, //! 16-bit signed integer, big endian. - PcmFormat_SInt16_Be, + PcmSubformat_SInt16_Be, //! 16-bit signed integer, little endian. - PcmFormat_SInt16_Le, + PcmSubformat_SInt16_Le, //! 16-bit unsigned integer, default endian. - PcmFormat_UInt16, + PcmSubformat_UInt16, //! 16-bit unsigned integer, big endian. - PcmFormat_UInt16_Be, + PcmSubformat_UInt16_Be, //! 16-bit unsigned integer, little endian. - PcmFormat_UInt16_Le, + PcmSubformat_UInt16_Le, //! 18-bit signed integer (2.25 bytes), default endian. - PcmFormat_SInt18, + PcmSubformat_SInt18, //! 18-bit signed integer (2.25 bytes), big endian. - PcmFormat_SInt18_Be, + PcmSubformat_SInt18_Be, //! 18-bit signed integer (2.25 bytes), little endian. - PcmFormat_SInt18_Le, + PcmSubformat_SInt18_Le, //! 18-bit unsigned integer (2.25 bytes), default endian. - PcmFormat_UInt18, + PcmSubformat_UInt18, //! 18-bit unsigned integer (2.25 bytes), big endian. - PcmFormat_UInt18_Be, + PcmSubformat_UInt18_Be, //! 18-bit unsigned integer (2.25 bytes), little endian. - PcmFormat_UInt18_Le, + PcmSubformat_UInt18_Le, //! 18-bit signed integer, in low bits of 3-byte container, default endian. - PcmFormat_SInt18_3, + PcmSubformat_SInt18_3, //! 18-bit signed integer, in low bits of 3-byte container, big endian. - PcmFormat_SInt18_3_Be, + PcmSubformat_SInt18_3_Be, //! 18-bit signed integer, in low bits of 3-byte container, little endian. - PcmFormat_SInt18_3_Le, + PcmSubformat_SInt18_3_Le, //! 18-bit unsigned integer, in low bits of 3-byte container, default endian. - PcmFormat_UInt18_3, + PcmSubformat_UInt18_3, //! 18-bit unsigned integer, in low bits of 3-byte container, big endian. - PcmFormat_UInt18_3_Be, + PcmSubformat_UInt18_3_Be, //! 18-bit unsigned integer, in low bits of 3-byte container, little endian. - PcmFormat_UInt18_3_Le, + PcmSubformat_UInt18_3_Le, //! 18-bit signed integer, in low bits of 4-byte container, default endian. - PcmFormat_SInt18_4, + PcmSubformat_SInt18_4, //! 18-bit signed integer, in low bits of 4-byte container, big endian. - PcmFormat_SInt18_4_Be, + PcmSubformat_SInt18_4_Be, //! 18-bit signed integer, in low bits of 4-byte container, little endian. - PcmFormat_SInt18_4_Le, + PcmSubformat_SInt18_4_Le, //! 18-bit unsigned integer, in low bits of 4-byte container, default endian. - PcmFormat_UInt18_4, + PcmSubformat_UInt18_4, //! 18-bit unsigned integer, in low bits of 4-byte container, big endian. - PcmFormat_UInt18_4_Be, + PcmSubformat_UInt18_4_Be, //! 18-bit unsigned integer, in low bits of 4-byte container, little endian. - PcmFormat_UInt18_4_Le, + PcmSubformat_UInt18_4_Le, //! 20-bit signed integer (2.5 bytes), default endian. - PcmFormat_SInt20, + PcmSubformat_SInt20, //! 20-bit signed integer (2.5 bytes), big endian. - PcmFormat_SInt20_Be, + PcmSubformat_SInt20_Be, //! 20-bit signed integer (2.5 bytes), little endian. - PcmFormat_SInt20_Le, + PcmSubformat_SInt20_Le, //! 20-bit unsigned integer (2.5 bytes), default endian. - PcmFormat_UInt20, + PcmSubformat_UInt20, //! 20-bit unsigned integer (2.5 bytes), big endian. - PcmFormat_UInt20_Be, + PcmSubformat_UInt20_Be, //! 20-bit unsigned integer (2.5 bytes), little endian. - PcmFormat_UInt20_Le, + PcmSubformat_UInt20_Le, //! 20-bit signed integer, in low bits of 3-byte container, default endian. - PcmFormat_SInt20_3, + PcmSubformat_SInt20_3, //! 20-bit signed integer, in low bits of 3-byte container, big endian. - PcmFormat_SInt20_3_Be, + PcmSubformat_SInt20_3_Be, //! 20-bit signed integer, in low bits of 3-byte container, little endian. - PcmFormat_SInt20_3_Le, + PcmSubformat_SInt20_3_Le, //! 20-bit unsigned integer, in low bits of 3-byte container, default endian. - PcmFormat_UInt20_3, + PcmSubformat_UInt20_3, //! 20-bit unsigned integer, in low bits of 3-byte container, big endian. - PcmFormat_UInt20_3_Be, + PcmSubformat_UInt20_3_Be, //! 20-bit unsigned integer, in low bits of 3-byte container, little endian. - PcmFormat_UInt20_3_Le, + PcmSubformat_UInt20_3_Le, //! 20-bit signed integer, in low bits of 4-byte container, default endian. - PcmFormat_SInt20_4, + PcmSubformat_SInt20_4, //! 20-bit signed integer, in low bits of 4-byte container, big endian. - PcmFormat_SInt20_4_Be, + PcmSubformat_SInt20_4_Be, //! 20-bit signed integer, in low bits of 4-byte container, little endian. - PcmFormat_SInt20_4_Le, + PcmSubformat_SInt20_4_Le, //! 20-bit unsigned integer, in low bits of 4-byte container, default endian. - PcmFormat_UInt20_4, + PcmSubformat_UInt20_4, //! 20-bit unsigned integer, in low bits of 4-byte container, big endian. - PcmFormat_UInt20_4_Be, + PcmSubformat_UInt20_4_Be, //! 20-bit unsigned integer, in low bits of 4-byte container, little endian. - PcmFormat_UInt20_4_Le, + PcmSubformat_UInt20_4_Le, //! 24-bit signed integer (3 bytes), default endian. - PcmFormat_SInt24, + PcmSubformat_SInt24, //! 24-bit signed integer (3 bytes), big endian. - PcmFormat_SInt24_Be, + PcmSubformat_SInt24_Be, //! 24-bit signed integer (3 bytes), little endian. - PcmFormat_SInt24_Le, + PcmSubformat_SInt24_Le, //! 24-bit unsigned integer (3 bytes), default endian. - PcmFormat_UInt24, + PcmSubformat_UInt24, //! 24-bit unsigned integer (3 bytes), big endian. - PcmFormat_UInt24_Be, + PcmSubformat_UInt24_Be, //! 24-bit unsigned integer (3 bytes), little endian. - PcmFormat_UInt24_Le, + PcmSubformat_UInt24_Le, //! 24-bit signed integer, in low bits of 4-byte container, default endian. - PcmFormat_SInt24_4, + PcmSubformat_SInt24_4, //! 24-bit signed integer, in low bits of 4-byte container, big endian. - PcmFormat_SInt24_4_Be, + PcmSubformat_SInt24_4_Be, //! 24-bit signed integer, in low bits of 4-byte container, little endian. - PcmFormat_SInt24_4_Le, + PcmSubformat_SInt24_4_Le, //! 24-bit unsigned integer, in low bits of 4-byte container, default endian. - PcmFormat_UInt24_4, + PcmSubformat_UInt24_4, //! 24-bit unsigned integer, in low bits of 4-byte container, big endian. - PcmFormat_UInt24_4_Be, + PcmSubformat_UInt24_4_Be, //! 24-bit unsigned integer, in low bits of 4-byte container, little endian. - PcmFormat_UInt24_4_Le, + PcmSubformat_UInt24_4_Le, //! 32-bit signed integer, default endian. - PcmFormat_SInt32, + PcmSubformat_SInt32, //! 32-bit signed integer, big endian. - PcmFormat_SInt32_Be, + PcmSubformat_SInt32_Be, //! 32-bit signed integer, little endian. - PcmFormat_SInt32_Le, + PcmSubformat_SInt32_Le, //! 32-bit unsigned integer, default endian. - PcmFormat_UInt32, + PcmSubformat_UInt32, //! 32-bit unsigned integer, big endian. - PcmFormat_UInt32_Be, + PcmSubformat_UInt32_Be, //! 32-bit unsigned integer, little endian. - PcmFormat_UInt32_Le, + PcmSubformat_UInt32_Le, //! 64-bit signed integer, default endian. - PcmFormat_SInt64, + PcmSubformat_SInt64, //! 64-bit signed integer, big endian. - PcmFormat_SInt64_Be, + PcmSubformat_SInt64_Be, //! 64-bit signed integer, little endian. - PcmFormat_SInt64_Le, + PcmSubformat_SInt64_Le, //! 64-bit unsigned integer, default endian. - PcmFormat_UInt64, + PcmSubformat_UInt64, //! 64-bit unsigned integer, big endian. - PcmFormat_UInt64_Be, + PcmSubformat_UInt64_Be, //! 64-bit unsigned integer, little endian. - PcmFormat_UInt64_Le, + PcmSubformat_UInt64_Le, //! 32-bit IEEE-754 float in range [-1.0; +1.0], default endian. - PcmFormat_Float32, + PcmSubformat_Float32, //! 32-bit IEEE-754 float in range [-1.0; +1.0], big endian. - PcmFormat_Float32_Be, + PcmSubformat_Float32_Be, //! 32-bit IEEE-754 float in range [-1.0; +1.0], little endian. - PcmFormat_Float32_Le, + PcmSubformat_Float32_Le, //! 64-bit IEEE-754 float in range [-1.0; +1.0], default endian. - PcmFormat_Float64, + PcmSubformat_Float64, //! 64-bit IEEE-754 float in range [-1.0; +1.0], big endian. - PcmFormat_Float64_Be, + PcmSubformat_Float64_Be, //! 64-bit IEEE-754 float in range [-1.0; +1.0], little endian. - PcmFormat_Float64_Le, + PcmSubformat_Float64_Le, //! Maximum enum value. - PcmFormat_Max + PcmSubformat_Max }; //! PCM format flags. @@ -233,41 +237,49 @@ enum PcmFlags { //! PCM format meta-information. struct PcmTraits { + //! Numeric identifier. + PcmSubformat id; + + //! String name. + const char* name; + + //! Flags. + unsigned flags; + //! Number of stored bits per sample in binary form. size_t bit_width; //! Number of significant bits per sample. size_t bit_depth; - //! Flags. - unsigned flags; - //! Same format, but with explicit _Be or _Le suffix. //! If format is default-endian, suffix is added based on current CPU, //! otherwise this field is is same as original format. - PcmFormat portable_alias; + PcmSubformat portable_alias; //! Same format but with removed _Be or _Le suffix. //! May be equal to original format. - PcmFormat default_variant; + PcmSubformat default_variant; //! Same format but with _Le suffix. //! May be equal to original format. - PcmFormat be_variant; + PcmSubformat be_variant; //! Same format but with _Be suffix. //! May be equal to original format. - PcmFormat le_variant; + PcmSubformat le_variant; //! Initialize invalid traits. PcmTraits() - : bit_width(0) - , bit_depth(0) + : id(PcmSubformat_Invalid) + , name(NULL) , flags(0) - , portable_alias(PcmFormat_Invalid) - , default_variant(PcmFormat_Invalid) - , be_variant(PcmFormat_Invalid) - , le_variant(PcmFormat_Invalid) { + , bit_width(0) + , bit_depth(0) + , portable_alias(PcmSubformat_Invalid) + , default_variant(PcmSubformat_Invalid) + , be_variant(PcmSubformat_Invalid) + , le_variant(PcmSubformat_Invalid) { } //! Check if all given flags are set. @@ -284,18 +296,18 @@ typedef void (*PcmMapFn)(const uint8_t* in_data, size_t n_samples); //! Get mapping function for given PCM format pair. -PcmMapFn pcm_format_mapfn(PcmFormat in_format, PcmFormat out_format); +PcmMapFn pcm_subformat_mapfn(PcmSubformat in_format, PcmSubformat out_format); //! Get format traits for given PCM format. -PcmTraits pcm_format_traits(PcmFormat format); +PcmTraits pcm_subformat_traits(PcmSubformat format); //! Get string name of PCM format. -const char* pcm_format_to_str(PcmFormat format); +const char* pcm_subformat_to_str(PcmSubformat format); //! Get PCM format from string name. -PcmFormat pcm_format_from_str(const char* str); +PcmSubformat pcm_subformat_from_str(const char* str); } // namespace audio } // namespace roc -#endif // ROC_AUDIO_PCM_FORMAT_H_ +#endif // ROC_AUDIO_PCM_SUBFORMAT_H_ diff --git a/src/internal_modules/roc_audio/pcm_format_gen.py b/src/internal_modules/roc_audio/pcm_subformat_gen.py similarity index 92% rename from src/internal_modules/roc_audio/pcm_format_gen.py rename to src/internal_modules/roc_audio/pcm_subformat_gen.py index adddd7680..12f0cc30d 100755 --- a/src/internal_modules/roc_audio/pcm_format_gen.py +++ b/src/internal_modules/roc_audio/pcm_subformat_gen.py @@ -89,9 +89,9 @@ def compute_octets(code): # generate enum name for pcm code + endian # e.g.: -# SInt18_3, Default => PcmFormat_SInt18_3 -# SInt18_3, Little => PcmFormat_SInt18_3_Le -# SInt18_3, Big => PcmFormat_SInt18_3_Be +# SInt18_3, Default => PcmSubformat_SInt18_3 +# SInt18_3, Little => PcmSubformat_SInt18_3_Le +# SInt18_3, Big => PcmSubformat_SInt18_3_Be def make_enum_name(code, endian): name = code['code'] @@ -101,13 +101,13 @@ def make_enum_name(code, endian): if endian == 'Big': name += '_Be' - return 'PcmFormat_' + name + return 'PcmSubformat_' + name # generate short string name for pcm code + endian # e.g.: -# SInt18_3, Default => s18_3 -# SInt18_3, Little => s18_3le -# SInt18_3, Big => s18_3be +# SInt18_3, Default => pcm_s18_3 +# SInt18_3, Little => pcm_s18_3le +# SInt18_3, Big => pcm_s18_3be def make_short_name(code, endian): name = code['short_name'] @@ -585,10 +585,11 @@ def nth_chars(codes, prefix=()): template = env.from_string(''' /* - * THIS FILE IS AUTO-GENERATED USING `pcm_format_gen.py'. DO NOT EDIT! + * THIS FILE IS AUTO-GENERATED USING `pcm_subformat_gen.py'. DO NOT EDIT! */ -#include "roc_audio/pcm_format.h" +#include "roc_audio/pcm_subformat.h" +#include "roc_audio/pcm_subformat_rw.h" #include "roc_core/attributes.h" #include "roc_core/cpu_traits.h" #include "roc_core/stddefs.h" @@ -709,7 +710,6 @@ def nth_chars(codes, prefix=()): {% endif %} {% endfor %} {% endfor %} - // N-byte native-endian sample template <class T> struct pcm_sample; @@ -733,54 +733,6 @@ def nth_chars(codes, prefix=()): }; {% endfor %} -// Write octet at given byte-aligned bit offset -inline void pcm_aligned_write(uint8_t* buffer, size_t& bit_offset, uint8_t arg) { - buffer[bit_offset >> 3] = arg; - bit_offset += 8; -} - -// Read octet at given byte-aligned bit offset -inline uint8_t pcm_aligned_read(const uint8_t* buffer, size_t& bit_offset) { - uint8_t ret = buffer[bit_offset >> 3]; - bit_offset += 8; - return ret; -} - -// Write value (at most 8 bits) at given unaligned bit offset -inline void -pcm_unaligned_write(uint8_t* buffer, size_t& bit_offset, size_t bit_length, uint8_t arg) { - size_t byte_index = (bit_offset >> 3); - size_t bit_index = (bit_offset & 0x7u); - - if (bit_index == 0) { - buffer[byte_index] = 0; - } - - buffer[byte_index] |= uint8_t(uint8_t(arg << (8 - bit_length)) >> bit_index); - - if (bit_index + bit_length > 8) { - buffer[byte_index + 1] = uint8_t(arg << bit_index); - } - - bit_offset += bit_length; -} - -// Read value (at most 8 bits) at given unaligned bit offset -inline uint8_t -pcm_unaligned_read(const uint8_t* buffer, size_t& bit_offset, size_t bit_length) { - size_t byte_index = (bit_offset >> 3); - size_t bit_index = (bit_offset & 0x7u); - - uint8_t ret = uint8_t(uint8_t(buffer[byte_index] << bit_index) >> (8 - bit_length)); - - if (bit_index + bit_length > 8) { - ret |= uint8_t(buffer[byte_index + 1] >> ((8 - bit_index) + (8 - bit_length))); - } - - bit_offset += bit_length; - return ret; -} - // Sample packer / unpacker template <PcmCode, PcmEndian> struct pcm_packer; @@ -870,7 +822,7 @@ def nth_chars(codes, prefix=()): // Select mapping function template <PcmCode InCode, PcmEndian InEndian> -PcmMapFn pcm_map_to_raw(PcmFormat raw_format) { +PcmMapFn pcm_map_to_raw(PcmSubformat raw_format) { switch (raw_format) { {% for ocode in CODES %} {% if ocode.is_raw: %} @@ -892,7 +844,7 @@ def nth_chars(codes, prefix=()): // Select mapping function template <PcmCode OutCode, PcmEndian OutEndian> -PcmMapFn pcm_map_from_raw(PcmFormat raw_format) { +PcmMapFn pcm_map_from_raw(PcmSubformat raw_format) { switch (raw_format) { {% for icode in CODES %} {% if icode.is_raw: %} @@ -915,7 +867,7 @@ def nth_chars(codes, prefix=()): } // namespace // Select mapping function -PcmMapFn pcm_format_mapfn(PcmFormat in_format, PcmFormat out_format) { +PcmMapFn pcm_subformat_mapfn(PcmSubformat in_format, PcmSubformat out_format) { // non-raw to raw switch (in_format) { {% for icode in CODES %} @@ -983,13 +935,15 @@ def nth_chars(codes, prefix=()): } // Get format traits -PcmTraits pcm_format_traits(PcmFormat format) { +PcmTraits pcm_subformat_traits(PcmSubformat format) { PcmTraits traits; switch (format) { {% for code in CODES %} {% for endian in ENDIANS %} case {{ make_enum_name(code, endian) }}: + traits.id = {{ make_enum_name(code, endian) }}; + traits.name = "{{ make_short_name(code, endian) }}"; traits.bit_width = {{ code.packed_width }}; traits.bit_depth = {{ code.depth }}; traits.flags = {{ make_format_flags(code) }}; @@ -1030,7 +984,7 @@ def nth_chars(codes, prefix=()): return traits; } -const char* pcm_format_to_str(PcmFormat format) { +const char* pcm_subformat_to_str(PcmSubformat format) { switch (format) { {% for code in CODES %} {% for endian in ENDIANS %} @@ -1044,9 +998,9 @@ def nth_chars(codes, prefix=()): return NULL; } -PcmFormat pcm_format_from_str(const char* str) { +PcmSubformat pcm_subformat_from_str(const char* str) { if (!str) { - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } {% for c0 in nth_chars(CODES) %} if (str[0] == '{{ c0 }}') { @@ -1063,7 +1017,7 @@ def nth_chars(codes, prefix=()): {% endfor %} {% endif %} {% endfor %} - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } {% endfor %} {% for code in CODES %} @@ -1075,13 +1029,13 @@ def nth_chars(codes, prefix=()): {% endfor %} {% endif %} {% endfor %} - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } {% endfor %} - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } {% endfor %} - return PcmFormat_Invalid; + return PcmSubformat_Invalid; } } // namespace audio @@ -1094,5 +1048,5 @@ def nth_chars(codes, prefix=()): os.chdir(os.path.dirname(os.path.abspath(__file__))) -with open('pcm_format.cpp', 'w') as fp: +with open('pcm_subformat.cpp', 'w') as fp: print(text, file=fp) diff --git a/src/internal_modules/roc_audio/pcm_subformat_rw.h b/src/internal_modules/roc_audio/pcm_subformat_rw.h new file mode 100644 index 000000000..023e88131 --- /dev/null +++ b/src/internal_modules/roc_audio/pcm_subformat_rw.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2022 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_audio/pcm_subformat_rw.h +//! @brief PCM sub-format read/write helpers. + +#ifndef ROC_AUDIO_PCM_SUBFORMAT_RW_H_ +#define ROC_AUDIO_PCM_SUBFORMAT_RW_H_ + +#include "roc_audio/pcm_subformat.h" +#include "roc_core/stddefs.h" + +namespace roc { +namespace audio { + +//! Write octet at given byte-aligned bit offset. +inline void pcm_aligned_write(uint8_t* buffer, size_t& bit_offset, uint8_t arg) { + buffer[bit_offset >> 3] = arg; + bit_offset += 8; +} + +//! Read octet at given byte-aligned bit offset. +inline uint8_t pcm_aligned_read(const uint8_t* buffer, size_t& bit_offset) { + const uint8_t ret = buffer[bit_offset >> 3]; + bit_offset += 8; + return ret; +} + +//! Write value (at most 8 bits) at given unaligned bit offset. +inline void +pcm_unaligned_write(uint8_t* buffer, size_t& bit_offset, size_t bit_length, uint8_t arg) { + const size_t byte_index = (bit_offset >> 3); + const size_t bit_index = (bit_offset & 0x7u); + + if (bit_index == 0) { + buffer[byte_index] = 0; + } + + buffer[byte_index] |= uint8_t(uint8_t(arg << (8 - bit_length)) >> bit_index); + + if (bit_index + bit_length > 8) { + buffer[byte_index + 1] = uint8_t(arg << bit_index); + } + + bit_offset += bit_length; +} + +//! Read value (at most 8 bits) at given unaligned bit offset. +inline uint8_t +pcm_unaligned_read(const uint8_t* buffer, size_t& bit_offset, size_t bit_length) { + const size_t byte_index = (bit_offset >> 3); + const size_t bit_index = (bit_offset & 0x7u); + + uint8_t ret = uint8_t(uint8_t(buffer[byte_index] << bit_index) >> (8 - bit_length)); + + if (bit_index + bit_length > 8) { + ret |= uint8_t(buffer[byte_index + 1] >> ((8 - bit_index) + (8 - bit_length))); + } + + bit_offset += bit_length; + return ret; +} + +} // namespace audio +} // namespace roc + +#endif // ROC_AUDIO_PCM_SUBFORMAT_RW_H_ diff --git a/src/internal_modules/roc_audio/plc_reader.cpp b/src/internal_modules/roc_audio/plc_reader.cpp index d1d9336d0..0544aaee9 100644 --- a/src/internal_modules/roc_audio/plc_reader.cpp +++ b/src/internal_modules/roc_audio/plc_reader.cpp @@ -7,7 +7,7 @@ */ #include "roc_audio/plc_reader.h" -#include "roc_audio/sample_format.h" +#include "roc_audio/format.h" #include "roc_audio/sample_spec_to_str.h" #include "roc_core/log.h" #include "roc_core/panic.h" @@ -33,8 +33,8 @@ PlcReader::PlcReader(IFrameReader& frame_reader, , got_first_signal_(false) , sample_spec_(sample_spec) , init_status_(status::NoStatus) { - if (!sample_spec_.is_valid() || sample_spec_.sample_format() != SampleFormat_Pcm) { - roc_panic("plc reader: required valid sample spec with pcm format: spec=%s", + if (!sample_spec_.is_complete() || !sample_spec_.is_pcm()) { + roc_panic("plc reader: required complete sample spec with pcm format: spec=%s", sample_spec_to_str(sample_spec_).c_str()); } if (sample_spec_ != plc_.sample_spec()) { diff --git a/src/internal_modules/roc_audio/resampler_reader.cpp b/src/internal_modules/roc_audio/resampler_reader.cpp index bca675831..34bd0ec5b 100644 --- a/src/internal_modules/roc_audio/resampler_reader.cpp +++ b/src/internal_modules/roc_audio/resampler_reader.cpp @@ -27,9 +27,9 @@ ResamplerReader::ResamplerReader(IFrameReader& frame_reader, , last_in_cts_(0) , scaling_(1.0f) , init_status_(status::NoStatus) { - if (!in_spec_.is_valid() || !out_spec_.is_valid() || !in_spec_.is_raw() + if (!in_spec_.is_complete() || !out_spec_.is_complete() || !in_spec_.is_raw() || !out_spec_.is_raw()) { - roc_panic("resampler reader: required valid sample specs with raw format:" + roc_panic("resampler reader: required complete sample specs with raw format:" " in_spec=%s out_spec=%s", sample_spec_to_str(in_spec_).c_str(), sample_spec_to_str(out_spec_).c_str()); diff --git a/src/internal_modules/roc_audio/resampler_writer.cpp b/src/internal_modules/roc_audio/resampler_writer.cpp index f9945c22b..6a4cedb30 100644 --- a/src/internal_modules/roc_audio/resampler_writer.cpp +++ b/src/internal_modules/roc_audio/resampler_writer.cpp @@ -28,9 +28,9 @@ ResamplerWriter::ResamplerWriter(IFrameWriter& frame_writer, , out_frame_pos_(0) , scaling_(1.f) , init_status_(status::NoStatus) { - if (!in_spec_.is_valid() || !out_spec_.is_valid() || !in_spec_.is_raw() + if (!in_spec_.is_complete() || !out_spec_.is_complete() || !in_spec_.is_raw() || !out_spec_.is_raw()) { - roc_panic("resampler writer: required valid sample specs with raw format:" + roc_panic("resampler writer: required complete sample specs with raw format:" " in_spec=%s out_spec=%s", sample_spec_to_str(in_spec_).c_str(), sample_spec_to_str(out_spec_).c_str()); diff --git a/src/internal_modules/roc_audio/sample.cpp b/src/internal_modules/roc_audio/sample.cpp index bec67f725..a31a241d9 100644 --- a/src/internal_modules/roc_audio/sample.cpp +++ b/src/internal_modules/roc_audio/sample.cpp @@ -8,7 +8,8 @@ #include "roc_audio/sample.h" -const roc::audio::PcmFormat roc::audio::Sample_RawFormat = roc::audio::PcmFormat_Float32; +const roc::audio::PcmSubformat roc::audio::PcmSubformat_Raw = + roc::audio::PcmSubformat_Float32; const roc::audio::sample_t roc::audio::Sample_Min = -1; const roc::audio::sample_t roc::audio::Sample_Max = 1; diff --git a/src/internal_modules/roc_audio/sample.h b/src/internal_modules/roc_audio/sample.h index 14fc41031..2385e098e 100644 --- a/src/internal_modules/roc_audio/sample.h +++ b/src/internal_modules/roc_audio/sample.h @@ -12,7 +12,7 @@ #ifndef ROC_AUDIO_SAMPLE_H_ #define ROC_AUDIO_SAMPLE_H_ -#include "roc_audio/sample_format.h" +#include "roc_audio/format.h" #include "roc_core/stddefs.h" namespace roc { @@ -22,7 +22,7 @@ namespace audio { typedef float sample_t; //! Format description for raw audio samples. -extern const PcmFormat Sample_RawFormat; +extern const PcmSubformat PcmSubformat_Raw; //! Minimum possible value of a raw sample. extern const sample_t Sample_Min; diff --git a/src/internal_modules/roc_audio/sample_format.cpp b/src/internal_modules/roc_audio/sample_format.cpp deleted file mode 100644 index 992213b61..000000000 --- a/src/internal_modules/roc_audio/sample_format.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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_audio/sample_format.h" - -namespace roc { -namespace audio { - -const char* sample_format_to_str(SampleFormat format) { - switch (format) { - case SampleFormat_Pcm: - return "pcm"; - - case SampleFormat_Invalid: - break; - } - - return "invalid"; -} - -} // namespace audio -} // namespace roc diff --git a/src/internal_modules/roc_audio/sample_format.h b/src/internal_modules/roc_audio/sample_format.h deleted file mode 100644 index b449588b0..000000000 --- a/src/internal_modules/roc_audio/sample_format.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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_audio/sample_format.h -//! @brief Sample format. - -#ifndef ROC_AUDIO_SAMPLE_FORMAT_H_ -#define ROC_AUDIO_SAMPLE_FORMAT_H_ - -#include "roc_audio/pcm_format.h" - -namespace roc { -namespace audio { - -//! Sample format. -//! Defines representation of samples in memory. -//! Does not define sample rate and channel set. -enum SampleFormat { - //! Invalid format. - SampleFormat_Invalid, - - //! Interleaved PCM format. - //! What specific PCM coding and endian is used is defined - //! by PcmFormat enum. - SampleFormat_Pcm, -}; - -//! Get string name of sample format. -const char* sample_format_to_str(SampleFormat format); - -} // namespace audio -} // namespace roc - -#endif // ROC_AUDIO_SAMPLE_FORMAT_H_ diff --git a/src/internal_modules/roc_audio/sample_spec.cpp b/src/internal_modules/roc_audio/sample_spec.cpp index 0dd50c48d..5c9a311fc 100644 --- a/src/internal_modules/roc_audio/sample_spec.cpp +++ b/src/internal_modules/roc_audio/sample_spec.cpp @@ -7,8 +7,8 @@ */ #include "roc_audio/sample_spec.h" -#include "roc_audio/pcm_format.h" -#include "roc_audio/sample_format.h" +#include "roc_audio/format.h" +#include "roc_audio/pcm_subformat.h" #include "roc_audio/sample_spec_to_str.h" #include "roc_core/macro_helpers.h" #include "roc_core/panic.h" @@ -62,145 +62,305 @@ core::nanoseconds_t nsamples_2_ns(const float n_samples, const size_t sample_rat return (core::nanoseconds_t)val; } -PcmFormat get_pcm_portable_format(PcmFormat fmt) { - if (fmt == PcmFormat_Invalid) { - return PcmFormat_Invalid; +PcmSubformat get_pcm_portable_format(PcmSubformat fmt) { + if (fmt == PcmSubformat_Invalid) { + return PcmSubformat_Invalid; } - const PcmTraits traits = pcm_format_traits(fmt); + const PcmTraits traits = pcm_subformat_traits(fmt); return traits.portable_alias; } -size_t get_pcm_sample_width(PcmFormat fmt) { - if (fmt == PcmFormat_Invalid) { +size_t get_pcm_sample_width(PcmSubformat fmt) { + if (fmt == PcmSubformat_Invalid) { return 0; } - const PcmTraits traits = pcm_format_traits(fmt); + const PcmTraits traits = pcm_subformat_traits(fmt); return traits.bit_width; } } // namespace SampleSpec::SampleSpec() - : sample_rate_(0) - , sample_fmt_(SampleFormat_Invalid) - , pcm_fmt_(PcmFormat_Invalid) - , pcm_width_(0) + : fmt_(Format_Invalid) + , has_subfmt_(false) + , pcm_subfmt_(PcmSubformat_Invalid) + , pcm_subfmt_width_(0) + , sample_rate_(0) , channel_set_() { + fmt_name_[0] = '\0'; + subfmt_name_[0] = '\0'; } SampleSpec::SampleSpec(const size_t sample_rate, - const PcmFormat pcm_fmt, + const PcmSubformat pcm_fmt, const ChannelSet& channel_set) - : sample_rate_(0) - , sample_fmt_(SampleFormat_Invalid) - , pcm_fmt_(PcmFormat_Invalid) - , pcm_width_(0) + : fmt_(Format_Invalid) + , has_subfmt_(false) + , pcm_subfmt_(PcmSubformat_Invalid) + , pcm_subfmt_width_(0) + , sample_rate_(0) , channel_set_(channel_set) { - set_sample_format(SampleFormat_Pcm); - set_pcm_format(pcm_fmt); + fmt_name_[0] = '\0'; + subfmt_name_[0] = '\0'; + + set_format(Format_Pcm); + set_pcm_subformat(pcm_fmt); set_sample_rate(sample_rate); - roc_panic_if_msg(!is_valid(), "sample spec: attempt to construct invalid spec: %s", + roc_panic_if_msg(!is_complete(), + "sample spec: attempt to construct incomplete spec: %s", sample_spec_to_str(*this).c_str()); } SampleSpec::SampleSpec(const size_t sample_rate, - const PcmFormat pcm_fmt, + const PcmSubformat pcm_fmt, const ChannelLayout channel_layout, ChannelOrder channel_order, const ChannelMask channel_mask) - : sample_rate_(0) - , sample_fmt_(SampleFormat_Invalid) - , pcm_fmt_(PcmFormat_Invalid) - , pcm_width_(0) + : fmt_(Format_Invalid) + , has_subfmt_(false) + , pcm_subfmt_(PcmSubformat_Invalid) + , pcm_subfmt_width_(0) + , sample_rate_(0) , channel_set_(channel_layout, channel_order, channel_mask) { - set_sample_format(SampleFormat_Pcm); - set_pcm_format(pcm_fmt); + fmt_name_[0] = '\0'; + subfmt_name_[0] = '\0'; + + set_format(Format_Pcm); + set_pcm_subformat(pcm_fmt); set_sample_rate(sample_rate); - roc_panic_if_msg(!is_valid(), "sample spec: attempt to construct invalid spec: %s", + roc_panic_if_msg(!is_complete(), + "sample spec: attempt to construct incomplete spec: %s", sample_spec_to_str(*this).c_str()); } bool SampleSpec::operator==(const SampleSpec& other) const { - return sample_fmt_ == other.sample_fmt_ - && (sample_fmt_ != SampleFormat_Pcm - || get_pcm_portable_format(pcm_fmt_) - == get_pcm_portable_format(other.pcm_fmt_)) - && sample_rate_ == other.sample_rate_ && channel_set_ == other.channel_set_; + // format + if (has_format() || other.has_format()) { + if (fmt_ != other.fmt_) { + return false; + } + if (fmt_ == Format_Custom && strcmp(fmt_name_, other.fmt_name_) != 0) { + return false; + } + } + + // sub-format + if (has_subformat() || other.has_subformat()) { + if (pcm_subfmt_ != PcmSubformat_Invalid + || other.pcm_subfmt_ != PcmSubformat_Invalid) { + if (get_pcm_portable_format(pcm_subfmt_) + != get_pcm_portable_format(other.pcm_subfmt_)) { + return false; + } + } else { + if (strcmp(subfmt_name_, other.subfmt_name_) != 0) { + return false; + } + } + } + + // rate, channels + if (sample_rate_ != other.sample_rate_) { + return false; + } + if (channel_set_ != other.channel_set_) { + return false; + } + + return true; } bool SampleSpec::operator!=(const SampleSpec& other) const { return !(*this == other); } -bool SampleSpec::is_valid() const { - return sample_fmt_ != SampleFormat_Invalid - && (sample_fmt_ != SampleFormat_Pcm || pcm_fmt_ != PcmFormat_Invalid) - && sample_rate_ != 0 && channel_set_.is_valid(); +bool SampleSpec::is_complete() const { + // format + if (!has_format()) { + return false; + } + if (!fmt_name_[0]) { + return false; + } + + // sub-format + if (fmt_ == Format_Pcm) { + if (pcm_subfmt_ == PcmSubformat_Invalid) { + return false; + } + } + if (has_subformat()) { + if (!subfmt_name_[0]) { + return false; + } + } + + // rate, channels + if (!has_sample_rate()) { + return false; + } + if (!has_channel_set()) { + return false; + } + + return true; } bool SampleSpec::is_empty() const { - return sample_fmt_ == SampleFormat_Invalid && pcm_fmt_ == PcmFormat_Invalid + return fmt_ == Format_Invalid && !has_subfmt_ && pcm_subfmt_ == PcmSubformat_Invalid && sample_rate_ == 0 && channel_set_.num_channels() == 0; } bool SampleSpec::is_pcm() const { - return sample_fmt_ == SampleFormat_Pcm && pcm_fmt_ != PcmFormat_Invalid; + return fmt_ == Format_Pcm && pcm_subfmt_ != PcmSubformat_Invalid; } bool SampleSpec::is_raw() const { - return sample_fmt_ == SampleFormat_Pcm - && get_pcm_portable_format(pcm_fmt_) == get_pcm_portable_format(Sample_RawFormat); + return fmt_ == Format_Pcm + && get_pcm_portable_format(pcm_subfmt_) + == get_pcm_portable_format(PcmSubformat_Raw); } void SampleSpec::clear() { - sample_fmt_ = SampleFormat_Invalid; - pcm_fmt_ = PcmFormat_Invalid; - pcm_width_ = 0; + fmt_ = Format_Invalid; + fmt_name_[0] = '\0'; + + has_subfmt_ = false; + subfmt_name_[0] = '\0'; + pcm_subfmt_ = PcmSubformat_Invalid; + pcm_subfmt_width_ = 0; + sample_rate_ = 0; channel_set_.clear(); } -void SampleSpec::use_defaults(PcmFormat default_pcm_fmt, +void SampleSpec::use_defaults(Format default_fmt, + PcmSubformat default_pcm_fmt, ChannelLayout default_channel_layout, ChannelOrder default_channel_order, ChannelMask default_channel_mask, size_t default_sample_rate) { - if (sample_fmt_ == SampleFormat_Invalid && default_pcm_fmt != PcmFormat_Invalid) { - set_sample_format(SampleFormat_Pcm); - set_pcm_format(default_pcm_fmt); + if (!has_format() && default_fmt != Format_Invalid) { + set_format(default_fmt); + } + + if (!has_subformat() && default_pcm_fmt != PcmSubformat_Invalid) { + set_pcm_subformat(default_pcm_fmt); + } + + if (!has_sample_rate() && default_sample_rate != 0) { + set_sample_rate(default_sample_rate); } - if (!channel_set_.is_valid() && default_channel_layout != ChanLayout_None) { + + if (!has_channel_set() && default_channel_layout != ChanLayout_None) { channel_set_.set_layout(default_channel_layout); channel_set_.set_order(default_channel_order); channel_set_.set_mask(default_channel_mask); } - if (sample_rate_ == 0 && default_sample_rate != 0) { - set_sample_rate(default_sample_rate); +} + +bool SampleSpec::has_format() const { + return fmt_ != Format_Invalid; +} + +Format SampleSpec::format() const { + return fmt_; +} + +const char* SampleSpec::format_name() const { + return fmt_name_; +} + +void SampleSpec::set_format(Format fmt) { + roc_panic_if_msg(fmt < Format_Invalid || fmt >= Format_Max, + "sample spec: invalid format id"); + + if (fmt_ == fmt) { + return; + } + + fmt_ = fmt; + + if (fmt_ == Format_Invalid || fmt_ == Format_Custom) { + strcpy(fmt_name_, ""); + } else { + strcpy(fmt_name_, format_to_str(fmt)); } } -SampleFormat SampleSpec::sample_format() const { - return sample_fmt_; +bool SampleSpec::set_custom_format(const char* name) { + roc_panic_if_msg(!name, "sample spec: invalid null string"); + + const size_t name_len = strlen(name); + if (name_len == 0 || name_len >= MaxNameLen) { + return false; + } + + fmt_ = Format_Custom; + strcpy(fmt_name_, name); + + return true; } -void SampleSpec::set_sample_format(SampleFormat sample_fmt) { - sample_fmt_ = sample_fmt; +bool SampleSpec::has_subformat() const { + return has_subfmt_; +} + +const char* SampleSpec::subformat_name() const { + return subfmt_name_; +} + +PcmSubformat SampleSpec::pcm_subformat() const { + return pcm_subfmt_; +} + +size_t SampleSpec::pcm_bit_width() const { + return pcm_subfmt_width_; +} + +void SampleSpec::set_pcm_subformat(PcmSubformat pcm_fmt) { + roc_panic_if_msg(pcm_fmt < PcmSubformat_Invalid || pcm_fmt >= PcmSubformat_Max, + "sample spec: invalid pcm format id"); + + if (pcm_subfmt_ == pcm_fmt) { + return; + } + + pcm_subfmt_ = pcm_fmt; + pcm_subfmt_width_ = get_pcm_sample_width(pcm_fmt); + + if (pcm_subfmt_ == PcmSubformat_Invalid) { + has_subfmt_ = false; + strcpy(subfmt_name_, ""); + } else { + has_subfmt_ = true; + strcpy(subfmt_name_, pcm_subformat_to_str(pcm_subfmt_)); + } } -PcmFormat SampleSpec::pcm_format() const { - if (sample_fmt_ != SampleFormat_Pcm) { - return PcmFormat_Invalid; +bool SampleSpec::set_custom_subformat(const char* name) { + roc_panic_if_msg(!name, "sample spec: string is null"); + + const size_t name_len = strlen(name); + if (name_len == 0 || name_len >= MaxNameLen) { + return false; } - return pcm_fmt_; + + pcm_subfmt_ = PcmSubformat_Invalid; + pcm_subfmt_width_ = 0; + + has_subfmt_ = true; + strcpy(subfmt_name_, name); + + return true; } -void SampleSpec::set_pcm_format(PcmFormat pcm_fmt) { - pcm_fmt_ = pcm_fmt; - pcm_width_ = get_pcm_sample_width(pcm_fmt); +bool SampleSpec::has_sample_rate() const { + return sample_rate_ != 0; } size_t SampleSpec::sample_rate() const { @@ -211,6 +371,14 @@ void SampleSpec::set_sample_rate(const size_t sample_rate) { sample_rate_ = sample_rate; } +bool SampleSpec::has_channel_set() const { + return channel_set_.is_valid(); +} + +size_t SampleSpec::num_channels() const { + return channel_set_.num_channels(); +} + const ChannelSet& SampleSpec::channel_set() const { return channel_set_; } @@ -223,12 +391,8 @@ void SampleSpec::set_channel_set(const ChannelSet& channel_set) { channel_set_ = channel_set; } -size_t SampleSpec::num_channels() const { - return channel_set_.num_channels(); -} - size_t SampleSpec::ns_2_samples_per_chan(const core::nanoseconds_t ns_duration) const { - roc_panic_if_msg(!is_valid(), "sample spec: attempt to use invalid spec: %s", + roc_panic_if_msg(!is_complete(), "sample spec: attempt to use incomplete spec: %s", sample_spec_to_str(*this).c_str()); roc_panic_if_msg(ns_duration < 0, "sample spec: duration should not be negative"); @@ -237,21 +401,21 @@ size_t SampleSpec::ns_2_samples_per_chan(const core::nanoseconds_t ns_duration) } core::nanoseconds_t SampleSpec::samples_per_chan_2_ns(const size_t n_samples) const { - roc_panic_if_msg(!is_valid(), "sample spec: attempt to use invalid spec: %s", + roc_panic_if_msg(!is_complete(), "sample spec: attempt to use incomplete spec: %s", sample_spec_to_str(*this).c_str()); return nsamples_2_ns((float)n_samples, sample_rate_); } core::nanoseconds_t SampleSpec::fract_samples_per_chan_2_ns(const float n_samples) const { - roc_panic_if_msg(!is_valid(), "sample spec: attempt to use invalid spec: %s", + roc_panic_if_msg(!is_complete(), "sample spec: attempt to use incomplete spec: %s", sample_spec_to_str(*this).c_str()); return nsamples_2_ns(n_samples, sample_rate_); } size_t SampleSpec::ns_2_samples_overall(const core::nanoseconds_t ns_duration) const { - roc_panic_if_msg(!is_valid(), "sample spec: attempt to use invalid spec: %s", + roc_panic_if_msg(!is_complete(), "sample spec: attempt to use incomplete spec: %s", sample_spec_to_str(*this).c_str()); roc_panic_if_msg(ns_duration < 0, "sample spec: duration should not be negative"); @@ -260,7 +424,7 @@ size_t SampleSpec::ns_2_samples_overall(const core::nanoseconds_t ns_duration) c } core::nanoseconds_t SampleSpec::samples_overall_2_ns(const size_t n_samples) const { - roc_panic_if_msg(!is_valid(), "sample spec: attempt to use invalid spec: %s", + roc_panic_if_msg(!is_complete(), "sample spec: attempt to use incomplete spec: %s", sample_spec_to_str(*this).c_str()); roc_panic_if_msg(n_samples % num_channels() != 0, @@ -270,7 +434,7 @@ core::nanoseconds_t SampleSpec::samples_overall_2_ns(const size_t n_samples) con } core::nanoseconds_t SampleSpec::fract_samples_overall_2_ns(const float n_samples) const { - roc_panic_if_msg(!is_valid(), "sample spec: attempt to use invalid spec: %s", + roc_panic_if_msg(!is_complete(), "sample spec: attempt to use incomplete spec: %s", sample_spec_to_str(*this).c_str()); return nsamples_2_ns(n_samples / num_channels(), sample_rate_); @@ -278,7 +442,7 @@ core::nanoseconds_t SampleSpec::fract_samples_overall_2_ns(const float n_samples packet::stream_timestamp_t SampleSpec::ns_2_stream_timestamp(const core::nanoseconds_t ns_duration) const { - roc_panic_if_msg(!is_valid(), "sample spec: attempt to use invalid spec: %s", + roc_panic_if_msg(!is_complete(), "sample spec: attempt to use incomplete spec: %s", sample_spec_to_str(*this).c_str()); roc_panic_if_msg(ns_duration < 0, "sample spec: duration should not be negative"); @@ -288,7 +452,7 @@ SampleSpec::ns_2_stream_timestamp(const core::nanoseconds_t ns_duration) const { core::nanoseconds_t SampleSpec::stream_timestamp_2_ns(const packet::stream_timestamp_t sts_duration) const { - roc_panic_if_msg(!is_valid(), "sample spec: attempt to use invalid spec: %s", + roc_panic_if_msg(!is_complete(), "sample spec: attempt to use incomplete spec: %s", sample_spec_to_str(*this).c_str()); return nsamples_2_ns((float)sts_duration, sample_rate_); @@ -300,7 +464,7 @@ double SampleSpec::stream_timestamp_2_ms(packet::stream_timestamp_t sts_duration packet::stream_timestamp_diff_t SampleSpec::ns_2_stream_timestamp_delta(const core::nanoseconds_t ns_delta) const { - roc_panic_if_msg(!is_valid(), "sample spec: attempt to use invalid spec: %s", + roc_panic_if_msg(!is_complete(), "sample spec: attempt to use incomplete spec: %s", sample_spec_to_str(*this).c_str()); return ns_2_int_samples<packet::stream_timestamp_diff_t>(ns_delta, sample_rate_, 1); @@ -308,7 +472,7 @@ SampleSpec::ns_2_stream_timestamp_delta(const core::nanoseconds_t ns_delta) cons core::nanoseconds_t SampleSpec::stream_timestamp_delta_2_ns( const packet::stream_timestamp_diff_t sts_delta) const { - roc_panic_if_msg(!is_valid(), "sample spec: attempt to use invalid spec: %s", + roc_panic_if_msg(!is_complete(), "sample spec: attempt to use incomplete spec: %s", sample_spec_to_str(*this).c_str()); return nsamples_2_ns((float)sts_delta, sample_rate_); @@ -320,33 +484,31 @@ SampleSpec::stream_timestamp_delta_2_ms(packet::stream_timestamp_diff_t sts_delt } packet::stream_timestamp_t SampleSpec::bytes_2_stream_timestamp(size_t n_bytes) const { - roc_panic_if_msg(!is_valid(), "sample spec: attempt to use invalid spec: %s", + roc_panic_if_msg(!is_complete(), "sample spec: attempt to use incomplete spec: %s", sample_spec_to_str(*this).c_str()); - roc_panic_if_msg(sample_fmt_ != SampleFormat_Pcm, - "sample spec: sample format is not pcm: %s", + roc_panic_if_msg(fmt_ != Format_Pcm, "sample spec: sample format is not pcm: %s", sample_spec_to_str(*this).c_str()); - roc_panic_if_msg(pcm_width_ % 8 != 0, + roc_panic_if_msg(pcm_subfmt_width_ % 8 != 0, "sample spec: sample width is not byte-aligned: %s", sample_spec_to_str(*this).c_str()); - return n_bytes / (pcm_width_ / 8) / channel_set_.num_channels(); + return n_bytes / (pcm_subfmt_width_ / 8) / channel_set_.num_channels(); } size_t SampleSpec::stream_timestamp_2_bytes(packet::stream_timestamp_t duration) const { - roc_panic_if_msg(!is_valid(), "sample spec: attempt to use invalid spec: %s", + roc_panic_if_msg(!is_complete(), "sample spec: attempt to use incomplete spec: %s", sample_spec_to_str(*this).c_str()); - roc_panic_if_msg(sample_fmt_ != SampleFormat_Pcm, - "sample spec: sample format is not pcm: %s", + roc_panic_if_msg(fmt_ != Format_Pcm, "sample spec: sample format is not pcm: %s", sample_spec_to_str(*this).c_str()); - roc_panic_if_msg(pcm_width_ % 8 != 0, + roc_panic_if_msg(pcm_subfmt_width_ % 8 != 0, "sample spec: sample width is not byte-aligned: %s", sample_spec_to_str(*this).c_str()); - return duration * (pcm_width_ / 8) * channel_set_.num_channels(); + return duration * (pcm_subfmt_width_ / 8) * channel_set_.num_channels(); } core::nanoseconds_t SampleSpec::bytes_2_ns(size_t n_bytes) const { @@ -358,7 +520,7 @@ size_t SampleSpec::ns_2_bytes(core::nanoseconds_t duration) const { } void SampleSpec::validate_frame(Frame& frame) const { - roc_panic_if_msg(!is_valid(), "sample spec: attempt to use invalid spec: %s", + roc_panic_if_msg(!is_complete(), "sample spec: attempt to use incomplete spec: %s", sample_spec_to_str(*this).c_str()); if (frame.num_bytes() == 0) { @@ -399,10 +561,10 @@ void SampleSpec::validate_frame(Frame& frame) const { } bool SampleSpec::is_valid_frame_size(size_t n_bytes) { - roc_panic_if_msg(!is_valid(), "sample spec: attempt to use invalid spec: %s", + roc_panic_if_msg(!is_complete(), "sample spec: attempt to use incomplete spec: %s", sample_spec_to_str(*this).c_str()); - if (sample_fmt_ != SampleFormat_Pcm || pcm_width_ % 8 != 0) { + if (fmt_ != Format_Pcm || pcm_subfmt_width_ % 8 != 0) { return true; } @@ -415,7 +577,7 @@ bool SampleSpec::is_valid_frame_size(size_t n_bytes) { roc_log(LogError, "sample spec: invalid frame buffer size: should be multiple of %u, got %lu" " (%u bytes per sample, %u channels)", - (unsigned)factor, (unsigned long)n_bytes, (unsigned)(pcm_width_ / 8), + (unsigned)factor, (unsigned long)n_bytes, (unsigned)(pcm_subfmt_width_ / 8), (unsigned)num_channels()); return false; @@ -424,7 +586,7 @@ bool SampleSpec::is_valid_frame_size(size_t n_bytes) { packet::stream_timestamp_t SampleSpec::cap_frame_duration(packet::stream_timestamp_t duration, size_t buffer_size) const { - roc_panic_if_msg(!is_valid(), "sample spec: attempt to use invalid spec: %s", + roc_panic_if_msg(!is_complete(), "sample spec: attempt to use incomplete spec: %s", sample_spec_to_str(*this).c_str()); return std::min(duration, bytes_2_stream_timestamp(buffer_size)); diff --git a/src/internal_modules/roc_audio/sample_spec.h b/src/internal_modules/roc_audio/sample_spec.h index cd3b48762..405ae3352 100644 --- a/src/internal_modules/roc_audio/sample_spec.h +++ b/src/internal_modules/roc_audio/sample_spec.h @@ -13,11 +13,11 @@ #define ROC_AUDIO_SAMPLE_SPEC_H_ #include "roc_audio/channel_set.h" +#include "roc_audio/format.h" #include "roc_audio/frame.h" #include "roc_audio/frame_factory.h" -#include "roc_audio/pcm_format.h" +#include "roc_audio/pcm_subformat.h" #include "roc_audio/sample.h" -#include "roc_audio/sample_format.h" #include "roc_core/attributes.h" #include "roc_core/stddefs.h" #include "roc_core/string_builder.h" @@ -28,7 +28,7 @@ namespace roc { namespace audio { //! Sample specification. -//! Describes sample rate and channels. +//! Describes format, rate, and channels. class SampleSpec { public: //! Construct empty specification. @@ -36,17 +36,17 @@ class SampleSpec { //! Construct specification with parameters. //! @note - //! This constructor sets sample_format() to SampleFormat_Pcm. - SampleSpec(size_t sample_rate, PcmFormat pcm_fmt, const ChannelSet& channel_set); + //! This constructor sets format() to Format_Pcm. + SampleSpec(size_t sample_rate, PcmSubformat pcm_fmt, const ChannelSet& channel_set); //! Construct specification with parameters. //! @remarks //! This is a convenient overload for the case when 32-bit mask is enough to //! describe channels. Otherwise, use overload that accepts ChannelSet. //! @note - //! This constructor sets sample_format() to SampleFormat_Pcm. + //! This constructor sets format() to Format_Pcm. SampleSpec(size_t sample_rate, - PcmFormat pcm_fmt, + PcmSubformat pcm_fmt, ChannelLayout channel_layout, ChannelOrder channel_order, ChannelMask channel_mask); @@ -65,22 +65,16 @@ class SampleSpec { //! @name Getters and setters //! @{ - //! Check if sample spec has non-zero rate and valid channel set. - bool is_valid() const; + //! True if all required fields are set and valid. + bool is_complete() const; - //! Check if sample spec has a zero rate, empty channel set, and invalid_format. + //! True if all fields are unset. 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. + //! True if format is PCM and sub-format is valid PCM encoding. bool is_pcm() const; - //! Check if samples are in raw format. - //! @returns - //! true if sample_format() is SampleFormat_Pcm and pcm_format() - //! is Sample_RawFormat (32-bit native-endian floats). + //! True if format is PCM and sub-format is PcmSubformat_Raw. bool is_raw() const; //! Unset all fields. @@ -90,12 +84,75 @@ class SampleSpec { //! @remarks //! Updates only those fields which don't have values, //! with corresponding values provided as arguments. - void use_defaults(PcmFormat default_pcm_fmt, + void use_defaults(Format default_fmt, + PcmSubformat default_pcm_fmt, ChannelLayout default_channel_layout, ChannelOrder default_channel_order, ChannelMask default_channel_mask, size_t default_sample_rate); + //! True format is set to a valid value. + bool has_format() const; + + //! Get format id. + //! @remarks + //! Format and sub-format define how samples are represented in memory. + //! What kind of sub-format is used depends on format, e.g. if format() + //! is Format_Pcm(), pcm_subformat() is used. + Format format() const; + + //! Get format name. + //! @remarks + //! If set_custom_format() was called, returns custom format name. Otherwise, + //! returns string name of format() enum value. + const char* format_name() const; + + //! Set format id. + void set_format(Format sample_fmt); + + //! Store custom format name and set format to Format_Custom. + //! @remarks + //! Custom format and sub-format names are used for file I/O. We can't and don't + //! need to maintain enums for all possible file formats, instead we just forward + //! string format name to the file I/O library. + //! @returns + //! false if name is too long. + ROC_ATTR_NODISCARD bool set_custom_format(const char* name); + + //! True if sub-format is set. + bool has_subformat() const; + + //! Get sub-format name. + //! @remarks + //! If set_custom_subformat() was called, returns custom sub-format name. + //! Otherwise, returns string name of sub-format enum value, e.g. if + //! pmc_subformat() is used, returns its string name. + const char* subformat_name() const; + + //! Get PCM sub-format. + //! @remarks + //! Set only if sub-format is PCM. + PcmSubformat pcm_subformat() const; + + //! Get number of bits in PCM sample. + //! @remarks + //! Set only if sub-format is PCM. + size_t pcm_bit_width() const; + + //! Set PCM sub-format. + void set_pcm_subformat(PcmSubformat pcm_fmt); + + //! Store custom sub-format name. + //! @remarks + //! Custom format and sub-format names are used for file I/O. + //! See comment for set_custom_format() for rationale. + //! @returns + //! false if name is too long. + ROC_ATTR_NODISCARD bool set_custom_subformat(const char* name); + + //! True if rate is set to non-zero value. + bool has_sample_rate() const; + //! Get sample rate. //! @remarks //! Defines sample frequency (number of samples per second). @@ -104,24 +161,13 @@ class SampleSpec { //! Set sample rate. void set_sample_rate(size_t sample_rate); - //! Get sample format. - //! @remarks - //! Defines how samples are represented in memory. - //! When set to SampleFormat_Pcm, pcm_format() defines what exact PCM coding - //! and endian are used. - SampleFormat sample_format() const; + //! True if channel set is valid. + bool has_channel_set() const; - //! Set sample format. - void set_sample_format(SampleFormat sample_fmt); - - //! Get PCM format. + //! Get number enabled channels in channel set. //! @remarks - //! When sample_format() is set to SampleFormat_Pcm, defines what exact PCM coding - //! and endian are used. - PcmFormat pcm_format() const; - - //! Set PCM format. - void set_pcm_format(PcmFormat pcm_fmt); + //! Shorthand for channel_set().num_channels(). + size_t num_channels() const; //! Get channel set. //! @remarks @@ -134,11 +180,6 @@ class SampleSpec { //! Set channel set. void set_channel_set(const ChannelSet& channel_set); - //! Get number enabled channels in channel set. - //! @remarks - //! Shorthand for channel_set().num_channels(). - size_t num_channels() const; - // @} //! @name Convert number of samples @@ -227,22 +268,22 @@ class SampleSpec { //! Convert byte size to stream timestamp. //! @pre - //! sample_format() should be PCM. + //! format() should be PCM. packet::stream_timestamp_t bytes_2_stream_timestamp(size_t n_bytes) const; //! Convert stream timestamp to byte size. //! @pre - //! sample_format() should be PCM. + //! format() should be PCM. size_t stream_timestamp_2_bytes(packet::stream_timestamp_t duration) const; //! Convert byte size to nanosecond duration. //! @pre - //! sample_format() should be PCM. + //! format() should be PCM. core::nanoseconds_t bytes_2_ns(size_t n_bytes) const; //! Convert nanosecond duration to byte size. //! @pre - //! sample_format() should be PCM. + //! format() should be PCM. size_t ns_2_bytes(core::nanoseconds_t duration) const; // @} @@ -266,10 +307,17 @@ class SampleSpec { // @} private: + enum { MaxNameLen = 8 }; + + Format fmt_; + char fmt_name_[MaxNameLen]; + + bool has_subfmt_; + char subfmt_name_[MaxNameLen]; + PcmSubformat pcm_subfmt_; + size_t pcm_subfmt_width_; + size_t sample_rate_; - SampleFormat sample_fmt_; - PcmFormat pcm_fmt_; - size_t pcm_width_; ChannelSet channel_set_; }; diff --git a/src/internal_modules/roc_audio/sample_spec_format.cpp b/src/internal_modules/roc_audio/sample_spec_format.cpp index 76e018c0c..a9fc66532 100644 --- a/src/internal_modules/roc_audio/sample_spec_format.cpp +++ b/src/internal_modules/roc_audio/sample_spec_format.cpp @@ -15,11 +15,14 @@ void format_sample_spec(const SampleSpec& sample_spec, core::StringBuilder& bld) bld.append_str("<sspec rate="); bld.append_uint(sample_spec.sample_rate(), 10); bld.append_str(" fmt=<"); - bld.append_str(sample_format_to_str(sample_spec.sample_format())); - if (sample_spec.sample_format() == SampleFormat_Pcm) { - const char* str = pcm_format_to_str(sample_spec.pcm_format()); + if (sample_spec.has_format()) { + bld.append_str(sample_spec.format_name()); + } else { + bld.append_str("none"); + } + if (sample_spec.has_subformat()) { bld.append_str(" "); - bld.append_str(str ? str : "invalid"); + bld.append_str(sample_spec.subformat_name()); } bld.append_str(">"); bld.append_str(" chset="); diff --git a/src/internal_modules/roc_audio/sample_spec_parse.rl b/src/internal_modules/roc_audio/sample_spec_parse.rl index a38c93576..91a5c5ee5 100644 --- a/src/internal_modules/roc_audio/sample_spec_parse.rl +++ b/src/internal_modules/roc_audio/sample_spec_parse.rl @@ -109,6 +109,50 @@ bool parse_sample_rate(const char* str, size_t str_len, size_t& result) { return true; } +bool parse_format(const char* str, SampleSpec& sample_spec) { + const Format fmt = format_from_str(str); + + if (fmt != Format_Invalid) { + sample_spec.set_format(fmt); + return true; + } + + if (sample_spec.set_custom_format(str)) { + return true; + } + + return false; +} + +bool parse_subformat(const char* str, SampleSpec& sample_spec) { + switch (sample_spec.format()) { + case Format_Pcm: + case Format_Wav: + case Format_Custom: { + const PcmSubformat pcm_fmt = pcm_subformat_from_str(str); + + if (pcm_fmt != PcmSubformat_Invalid) { + // This is PCM sub-format. + sample_spec.set_pcm_subformat(pcm_fmt); + return true; + } + + if (sample_spec.format() != Format_Pcm) { + // This is custom sub-format. Allow only if format is not PCM. + if (sample_spec.set_custom_subformat(str)) { + return true; + } + } + } break; + + case Format_Invalid: + case Format_Max: + break; + } + + return false; +} + bool parse_sample_spec_imp(const char* str, SampleSpec& sample_spec) { if (!str) { roc_log(LogError, "parse sample spec: input string is null"); @@ -142,7 +186,8 @@ bool parse_sample_spec_imp(const char* str, SampleSpec& sample_spec) { action set_surround_mask { ChannelMask ch_mask = 0; if (!parse_surround_mask(start_p, p - start_p, ch_mask)) { - roc_log(LogError, "parse sample spec: invalid channel mask name"); + roc_log(LogError, "parse sample spec: invalid channel mask name '%.*s' in '%s'", + int(p - start_p), start_p, str); return false; } sample_spec.channel_set().set_mask(ch_mask); @@ -151,7 +196,8 @@ bool parse_sample_spec_imp(const char* str, SampleSpec& sample_spec) { action set_surround_channel { ChannelPosition ch_pos = ChanPos_Max; if (!parse_surround_channel(start_p, p - start_p, ch_pos)) { - roc_log(LogError, "parse sample spec: invalid channel name"); + roc_log(LogError, "parse sample spec: invalid channel name '%.*s' in '%s'", + int(p - start_p), start_p, str); return false; } sample_spec.channel_set().toggle_channel(ch_pos, true); @@ -165,8 +211,9 @@ bool parse_sample_spec_imp(const char* str, SampleSpec& sample_spec) { action set_mtr_number { size_t ch_pos = 0; if (!parse_multitrack_channel(start_p, p - start_p, ch_pos)) { - roc_log(LogError, "parse sample spec: invalid channel number," + roc_log(LogError, "parse sample spec: invalid channel number '%.*s' in '%s'," " should be integer in range [0; %d]", + int(p - start_p), start_p, str, (int)ChannelSet::max_channels() - 1); return false; } @@ -175,8 +222,9 @@ bool parse_sample_spec_imp(const char* str, SampleSpec& sample_spec) { action set_mtr_range_begin { if (!parse_multitrack_channel(start_p, p - start_p, mtr_range_begin)) { - roc_log(LogError, "parse sample spec: invalid channel number," + roc_log(LogError, "parse sample spec: invalid channel number '%.*s'," " should be integer in range [0; %d]", + int(p - start_p), start_p, (int)ChannelSet::max_channels() - 1); return false; } @@ -184,8 +232,9 @@ bool parse_sample_spec_imp(const char* str, SampleSpec& sample_spec) { action set_mtr_range_end { if (!parse_multitrack_channel(start_p, p - start_p, mtr_range_end)) { - roc_log(LogError, "parse sample spec: invalid channel number," + roc_log(LogError, "parse sample spec: invalid channel number '%.*s' in '%s'," " should be integer in range [0; %d]", + int(p - start_p), start_p, str, (int)ChannelSet::max_channels() - 1); return false; } @@ -198,7 +247,8 @@ bool parse_sample_spec_imp(const char* str, SampleSpec& sample_spec) { action set_mtr_mask { if (!parse_multitrack_mask(start_p, p - start_p, sample_spec.channel_set())) { - roc_log(LogError, "parse sample spec: invalid channel mask"); + roc_log(LogError, "parse sample spec: invalid channel mask '%.*s' in '%s'", + int(p - start_p), start_p, str); return false; } } @@ -209,21 +259,40 @@ bool parse_sample_spec_imp(const char* str, SampleSpec& sample_spec) { } action set_format { - char str[16] = {}; - strncat(str, start_p, p - start_p); - PcmFormat pcm_fmt = pcm_format_from_str(str); - if (pcm_fmt == PcmFormat_Invalid) { - roc_log(LogError, "parse sample spec: invalid sample format"); + char buf[16] = {}; + if (p - start_p > sizeof(buf) - 1) { + roc_log(LogError, "parse sample spec: invalid format '%.*s' in '%s'", + int(p - start_p), start_p, str); + return false; + } + strncat(buf, start_p, p - start_p); + if (!parse_format(buf, sample_spec)) { + roc_log(LogError, "parse sample spec: invalid format '%.*s' in '%s'", + int(p - start_p), start_p, str); + return false; + } + } + + action set_subformat { + char buf[16] = {}; + if (p - start_p > sizeof(buf) - 1) { + roc_log(LogError, "parse sample spec: invalid subformat '%.*s' in '%s'", + int(p - start_p), start_p, str); + return false; + } + strncat(buf, start_p, p - start_p); + if (!parse_subformat(buf, sample_spec)) { + roc_log(LogError, "parse sample spec: invalid subformat '%.*s' in '%s'", + int(p - start_p), start_p, str); return false; } - sample_spec.set_sample_format(SampleFormat_Pcm); - sample_spec.set_pcm_format(pcm_fmt); } action set_rate { size_t rate = 0; if (!parse_sample_rate(start_p, p - start_p, rate)) { - roc_log(LogError, "parse sample spec: invalid sample rate"); + roc_log(LogError, "parse sample spec: invalid sample rate '%.*s' in '%s'", + int(p - start_p), start_p, str); return false; } sample_spec.set_sample_rate(rate); @@ -248,11 +317,12 @@ bool parse_sample_spec_imp(const char* str, SampleSpec& sample_spec) { mtr = (mtr_mask | mtr_list) %set_mtr; format = [a-z0-9_]+ >start_token %set_format; + subformat = [a-z0-9_]+ >start_token %set_subformat; rate = [0-9]+ >start_token %set_rate; channels_DISABLED = surround | mtr; channels = ('stereo' | 'mono') >start_token %set_surround_mask %set_surround; - main := ( ('-' | format) '/' ('-' | rate) '/' ('-' | channels) ) + main := ( ('-' | format ('@' subformat)?) '/' ('-' | rate) '/' ('-' | channels) ) %{ success = true; } ; @@ -262,18 +332,36 @@ bool parse_sample_spec_imp(const char* str, SampleSpec& sample_spec) { if (!success) { roc_log(LogError, - "parse sample spec: expected 'FORMAT/RATE/CHANNELS', got '%s'", - str); + "parse sample spec: expected '<format>[@<subformat>]/<rate>/<channels>'," + " got '%s'", str); return false; } return true; } +bool validate_sample_spec(const SampleSpec& sample_spec) { + switch (sample_spec.format()) { + case Format_Pcm: + if (!sample_spec.has_subformat()) { + roc_log(LogError, "parse sample spec: subformat required when format is pcm"); + return false; + } + break; + + case Format_Invalid: + case Format_Custom: + case Format_Max: + break; + } + + return true; +} + } // namespace bool parse_sample_spec(const char* str, SampleSpec& result) { - if (!parse_sample_spec_imp(str, result)) { + if (!parse_sample_spec_imp(str, result) || !validate_sample_spec(result)) { result.clear(); return false; } diff --git a/src/internal_modules/roc_audio/target_speexdsp/roc_audio/speex_resampler.cpp b/src/internal_modules/roc_audio/target_speexdsp/roc_audio/speex_resampler.cpp index b62b7e8df..aa35e7c1d 100644 --- a/src/internal_modules/roc_audio/target_speexdsp/roc_audio/speex_resampler.cpp +++ b/src/internal_modules/roc_audio/target_speexdsp/roc_audio/speex_resampler.cpp @@ -60,9 +60,9 @@ SpeexResampler::SpeexResampler(const ResamplerConfig& config, , in_latency_diff_(0) , report_limiter_(LogReportInterval) , init_status_(status::NoStatus) { - if (!in_spec.is_valid() || !out_spec.is_valid() || !in_spec.is_raw() + if (!in_spec.is_complete() || !out_spec.is_complete() || !in_spec.is_raw() || !out_spec.is_raw()) { - roc_panic("speex resampler: required valid sample specs with raw format:" + roc_panic("speex resampler: required complete sample specs with raw format:" " in_spec=%s out_spec=%s", sample_spec_to_str(in_spec).c_str(), sample_spec_to_str(out_spec).c_str()); diff --git a/src/internal_modules/roc_core/backtrace.h b/src/internal_modules/roc_core/backtrace.h index 7c317106b..d404d9285 100644 --- a/src/internal_modules/roc_core/backtrace.h +++ b/src/internal_modules/roc_core/backtrace.h @@ -37,7 +37,7 @@ void print_backtrace_safe(); //! @p demangled_buf and @p demangled_size specify the buffer for demangled name. //! When necessary, this function malloc()s or realloc()s @p demangled_buf and //! updates @p demangled_size accordingly. The buffer may be NULL. The buffer may -//! be resused across several calls. The user should manually free() the buffer +//! be reused across several calls. The user should manually free() the buffer //! when it's not needed anymore. //! @returns //! demangled symbol or NULL if the symbol can't be demangled. diff --git a/src/internal_modules/roc_core/string_list.cpp b/src/internal_modules/roc_core/string_list.cpp index 29176c50b..e27aebb30 100644 --- a/src/internal_modules/roc_core/string_list.cpp +++ b/src/internal_modules/roc_core/string_list.cpp @@ -13,10 +13,37 @@ namespace roc { namespace core { +namespace { + +int strcmp_lexical(const char* a, const char* b) { + return strcmp(a, b); +} + +int strcmp_natural(const char* a, const char* b) { + while (*a && *b) { + if (isdigit(*a) && isdigit(*b)) { + const long ia = strtol(a, const_cast<char**>((const char**)&a), 10); + const long ib = strtol(b, const_cast<char**>((const char**)&b), 10); + if (ia != ib) { + return ia < ib ? -1 : 1; + } + } else { + if (*a != *b) { + return *a < *b ? -1 : 1; + } + a++; + b++; + } + } + return *a < *b ? -1 : *a != *b; +} + +} // namespace + StringList::StringList(IArena& arena) - : data_(arena) - , front_(NULL) - , back_(NULL) + : memory_(arena) + , head_off_(0) + , tail_off_(0) , size_(0) { } @@ -30,7 +57,7 @@ bool StringList::is_empty() const { const char* StringList::front() const { if (size_) { - return front_->str; + return from_offset_(head_off_)->str; } else { return NULL; } @@ -38,7 +65,7 @@ const char* StringList::front() const { const char* StringList::back() const { if (size_) { - return back_->str; + return from_offset_(tail_off_)->str; } else { return NULL; } @@ -51,14 +78,12 @@ const char* StringList::nextof(const char* str) const { check_member_(str); - const Header* str_header = ROC_CONTAINER_OF(const_cast<char*>(str), Header, str); - - if (str_header == back_) { + const Header* curr_header = ROC_CONTAINER_OF(const_cast<char*>(str), Header, str); + if (curr_header == from_offset_(tail_off_)) { return NULL; } - const Header* next_header = - (const Header*)((const char*)str_header + str_header->len); + const Header* next_header = from_offset_(curr_header->next_off); return next_header->str; } @@ -69,23 +94,19 @@ const char* StringList::prevof(const char* str) const { check_member_(str); - const Header* str_header = ROC_CONTAINER_OF(const_cast<char*>(str), Header, str); - - if (str_header == front_) { + const Header* curr_header = ROC_CONTAINER_OF(const_cast<char*>(str), Header, str); + if (curr_header == from_offset_(head_off_)) { return NULL; } - const Footer* prev_footer = (const Footer*)((const char*)str_header - sizeof(Footer)); - const Header* prev_header = - (const Header*)((const char*)str_header - prev_footer->len); - + const Header* prev_header = from_offset_(curr_header->prev_off); return prev_header->str; } void StringList::clear() { - data_.clear(); - front_ = NULL; - back_ = NULL; + memory_.clear(); + head_off_ = 0; + tail_off_ = 0; size_ = 0; } @@ -102,30 +123,38 @@ bool StringList::push_back(const char* str_begin, const char* str_end) { roc_panic("stringlist: invalid range"); } - const size_t str_sz = (size_t)(str_end - str_begin); - const size_t blk_sz = - sizeof(Header) + AlignOps::align_as(str_sz + 1, sizeof(Header)) + sizeof(Footer); + const size_t str_len = size_t(str_end - str_begin); + const size_t blk_len = + sizeof(Header) + AlignOps::align_as(str_len + 1, sizeof(Header)); - if (!grow_(data_.size() + blk_sz)) { + if (!grow_(memory_.size() + blk_len)) { return false; } - - if (!data_.resize(data_.size() + blk_sz)) { + if (!memory_.resize(memory_.size() + blk_len)) { return false; } - front_ = (Header*)(data_.data()); - back_ = (Header*)(data_.data() + data_.size() - blk_sz); - size_++; + const offset_t curr_off = memory_.size() - blk_len; + const offset_t prev_off = tail_off_; - Header* str_header = back_; - str_header->len = (uint32_t)blk_sz; + Header* curr_header = from_offset_(curr_off); + curr_header->prev_off = prev_off; + curr_header->next_off = 0; + curr_header->blk_len = blk_len; - memcpy(str_header->str, str_begin, str_sz); // copy string - str_header->str[str_sz] = '\0'; // add null + if (size_ != 0) { + Header* prev_header = from_offset_(prev_off); + prev_header->next_off = curr_off; + } + + memcpy(curr_header->str, str_begin, str_len); // copy string + curr_header->str[str_len] = '\0'; // add null - Footer* str_footer = (Footer*)((char*)back_ + blk_sz - sizeof(Footer)); - str_footer->len = (uint32_t)blk_sz; + if (size_ == 0) { + head_off_ = curr_off; + } + tail_off_ = curr_off; + size_++; return true; } @@ -135,22 +164,22 @@ bool StringList::pop_back() { roc_panic("stringlist: list is empty"); } - const size_t blk_sz = back_->len; - const Footer* prev_footer = NULL; - if (size_ > 1) { - prev_footer = (const Footer*)((const char*)back_ - sizeof(Footer)); - } + Header* curr_header = from_offset_(tail_off_); + const offset_t prev_off = curr_header->prev_off; - if (!data_.resize(data_.size() - blk_sz)) { + if (!memory_.resize(memory_.size() - curr_header->blk_len)) { return false; } + if (size_ > 1) { + Header* prev_header = from_offset_(prev_off); + prev_header->next_off = 0; + } + size_--; - if (size_) { - back_ = (Header*)(data_.data() + data_.size() - prev_footer->len); - } else { - front_ = NULL; - back_ = NULL; + tail_off_ = prev_off; + if (size_ == 0) { + head_off_ = 0; } return true; @@ -170,33 +199,115 @@ const char* StringList::find(const char* str_begin, const char* str_end) { } if (size_ != 0) { - const size_t str_sz = (size_t)(str_end - str_begin); - const size_t blk_sz = sizeof(Header) - + AlignOps::align_as(str_sz + 1, sizeof(Header)) + sizeof(Footer); + const size_t str_len = size_t(str_end - str_begin); + const size_t blk_len = + sizeof(Header) + AlignOps::align_as(str_len + 1, sizeof(Header)); + + const Header* curr_header = from_offset_(head_off_); + const Header* back_header = from_offset_(tail_off_); - const Header* s_header = front_; for (;;) { - if (s_header->len == blk_sz - && memcmp(s_header->str, str_begin, str_sz) == 0) { - return s_header->str; + if (curr_header->blk_len == blk_len + && memcmp(curr_header->str, str_begin, str_len) == 0 + && curr_header->str[str_len] == '\0') { + return curr_header->str; } - if (s_header == back_) { + if (curr_header == back_header) { break; } - s_header = (const Header*)((const char*)s_header + s_header->len); + curr_header = from_offset_(curr_header->next_off); } } return NULL; } +void StringList::sort(Order order) { + if (size_ < 2) { + return; + } + + int (*compare)(const char* a, const char* b) = + order == OrderLexical ? strcmp_lexical : strcmp_natural; + + for (;;) { + // old good bubble sort + bool swapped = false; + + offset_t curr_off = head_off_; + Header* curr_header = from_offset_(curr_off); + + while (curr_off != tail_off_) { + offset_t next_off = curr_header->next_off; + Header* next_header = from_offset_(next_off); + + const int cmp = compare(curr_header->str, next_header->str); + if (cmp > 0) { + swap_(curr_off, curr_header, next_off, next_header); + swapped = true; + } else { + curr_off = next_off; + curr_header = next_header; + } + } + + if (!swapped) { + break; + } + } +} + +void StringList::swap_(offset_t x_off, + Header* x_header, + offset_t y_off, + Header* y_header) { + offset_t prev_off = x_header->prev_off; + Header* prev_header = from_offset_(prev_off); + + offset_t next_off = y_header->next_off; + Header* next_header = from_offset_(next_off); + + x_header->next_off = next_off; + x_header->prev_off = y_off; + + y_header->next_off = x_off; + y_header->prev_off = prev_off; + + if (x_off == head_off_) { + head_off_ = y_off; + } else { + prev_header->next_off = y_off; + } + + if (y_off == tail_off_) { + tail_off_ = x_off; + } else { + next_header->prev_off = x_off; + } +} + +StringList::offset_t StringList::to_offset_(const Header* header) const { + if (!header) { + return 0; + } + return offset_t((const char*)header - (const char*)memory_.data()); +} + +const StringList::Header* StringList::from_offset_(offset_t off) const { + return (const Header*)(memory_.data() + off); +} + +StringList::Header* StringList::from_offset_(offset_t off) { + return (Header*)(memory_.data() + off); +} + void StringList::check_member_(const char* str) const { if (size_ == 0) { roc_panic("stringlist: list is empty"); } - const char* begin = &data_[0]; - const char* end = &data_[0] + data_.size(); + const char* begin = &memory_[0]; + const char* end = &memory_[0] + memory_.size(); if (str < begin || str >= end) { roc_panic("stringlist: string doesn't belong to the list"); @@ -208,7 +319,7 @@ bool StringList::grow_(size_t new_size) { new_size = MinCapacity; } - return data_.grow_exp(new_size); + return memory_.grow_exp(new_size); } } // namespace core diff --git a/src/internal_modules/roc_core/string_list.h b/src/internal_modules/roc_core/string_list.h index 0703ba4e6..768d269d2 100644 --- a/src/internal_modules/roc_core/string_list.h +++ b/src/internal_modules/roc_core/string_list.h @@ -24,14 +24,15 @@ namespace core { //! Dynamic list of strings. //! //! Strings are stored in a continuous dynamically-growing array. -//! Each string is stored in a block with a header and footer, -//! which both store block length. This allow fast iteration -//! in both directions. +//! Each string is stored in a block with a header which holds offsets to previous +//! and next blocks, forming a linked list. This allows implementing bidirectional +//! iteration and sorting. Using offsets instead of pointers is needed to avoid +//! pointer invalidation after reallocation. //! //! @code -//! ++--------+--------+---------+--------++----------- -//! || Header | string | padding | Footer || Header ... -//! ++--------+--------+---------+--------++----------- +//! ++--------+--------+---------++----------- +//! || Header | string | padding || Header ... +//! ++--------+--------+---------++----------- //! @endcode class StringList : public NonCopyable<> { public: @@ -95,31 +96,54 @@ class StringList : public NonCopyable<> { //! Find string in the list. //! @returns //! the string in the list or NULL if it is not found. - ROC_ATTR_NODISCARD const char* find(const char* str); + const char* find(const char* str); //! Find string in the list. //! @returns //! the string in the list or NULL if it is not found. - ROC_ATTR_NODISCARD const char* find(const char* str_begin, const char* str_end); + const char* find(const char* str_begin, const char* str_end); + + //! String comparison algorithm. + enum Order { + //! Sort in lexicographic order. + //! Assumes ASCII. + OrderLexical, + //! Sort in natural order. + //! Assumes ASCII. + OrderNatural, + }; + + //! Sort list of strings according to specified order. + void sort(Order order); private: enum { MinCapacity = 128 }; + typedef uint32_t offset_t; + struct Header { - uint32_t len; + // offsets of next and previous elements in memory + offset_t next_off; + offset_t prev_off; + // len of this block, including header and padding + offset_t blk_len; + // null-terminated string char str[]; }; - struct Footer { - uint32_t len; - }; + void swap_(offset_t x_off, Header* x_header, offset_t y_off, Header* y_header); + + offset_t to_offset_(const Header* header) const; + const Header* from_offset_(offset_t off) const; + Header* from_offset_(offset_t off); void check_member_(const char* str) const; + bool grow_(size_t size); - core::Array<char> data_; - Header* front_; - Header* back_; + core::Array<char> memory_; + offset_t head_off_; + offset_t tail_off_; size_t size_; }; diff --git a/src/internal_modules/roc_core/target_posix/roc_core/time.cpp b/src/internal_modules/roc_core/target_posix/roc_core/time.cpp index 52e0a53bb..afebdd37b 100644 --- a/src/internal_modules/roc_core/target_posix/roc_core/time.cpp +++ b/src/internal_modules/roc_core/target_posix/roc_core/time.cpp @@ -49,7 +49,7 @@ nanoseconds_t timestamp(clock_t clock) { #else -nanoseconds_t timestamp(clock_t) { +nanoseconds_t timestamp(clock_t clock) { struct timeval tv; if (gettimeofday(&tv, NULL) == -1) { roc_panic("time: gettimeofday(): %s", errno_to_str().c_str()); @@ -77,7 +77,7 @@ void sleep_for(clock_t clock, nanoseconds_t ns) { #else -void sleep_for(clock_t, nanoseconds_t ns) { +void sleep_for(clock_t clock, nanoseconds_t ns) { timespec ts; ts.tv_sec = time_t(ns / 1000000000); ts.tv_nsec = long(ns % 1000000000); diff --git a/src/internal_modules/roc_dbgio/print_supported.cpp b/src/internal_modules/roc_dbgio/print_supported.cpp index 08d6a3f1c..cbea846c8 100644 --- a/src/internal_modules/roc_dbgio/print_supported.cpp +++ b/src/internal_modules/roc_dbgio/print_supported.cpp @@ -10,8 +10,8 @@ #include "roc_address/protocol_map.h" #include "roc_audio/channel_defs.h" #include "roc_audio/channel_tables.h" -#include "roc_audio/pcm_format.h" -#include "roc_audio/sample_format.h" +#include "roc_audio/format.h" +#include "roc_audio/pcm_subformat.h" #include "roc_core/macro_helpers.h" #include "roc_core/printer.h" #include "roc_fec/codec_map.h" @@ -85,7 +85,7 @@ bool print_network_schemes(core::Printer& prn, core::IArena& arena) { } if (n_interface == 0) { - prn.writef("Supported schemes for network endpoints:\n"); + prn.writef("Supported uri schemes for network endpoints: [NET_URI]\n"); } print_interface_protos(prn, interface_array[n_interface], list); @@ -126,28 +126,47 @@ bool print_io_schemes(sndio::BackendDispatcher& backend_dispatcher, return false; } - prn.writef("Supported schemes for audio devices and files:\n"); + prn.writef("Supported uri schemes for io endpoints: [IO_URI]\n"); prn.writef(" (--input, --output)\n"); print_string_list(prn, list, "", "://"); return true; } -bool print_fec_schemes(core::Printer& prn, core::IArena& arena) { - prn.writef("Supported fec encodings:\n"); - prn.writef(" (--fec-encoding)\n"); +bool print_network_formats(core::Printer& prn, core::IArena& arena) { + prn.writef("Supported formats for network packets: [PKT_ENCODING]\n"); + prn.writef(" (--packet-encoding)\n"); - const size_t n_schemes = fec::CodecMap::instance().num_schemes(); + prn.writef(" "); - if (n_schemes == 0) { - prn.writef(" none"); - } else { - prn.writef(" auto"); + for (int fmt = audio::Format_Invalid; fmt < audio::Format_Max; fmt++) { + if (fmt == audio::Format_Invalid) { + continue; + } + const audio::FormatTraits traits = audio::format_traits((audio::Format)fmt); + if (traits.has_flags(audio::Format_SupportsNetwork)) { + prn.writef(" %s", traits.name); + } + } - for (size_t n = 0; n < n_schemes; n++) { - prn.writef( - " %s", - packet::fec_scheme_to_str(fec::CodecMap::instance().nth_scheme(n))); + prn.writef("\n"); + + return true; +} + +bool print_device_formats(core::Printer& prn, core::IArena& arena) { + prn.writef("Supported formats for device io: [IO_ENCODING]\n"); + prn.writef(" (--io-encoding)\n"); + + prn.writef(" "); + + for (int fmt = audio::Format_Invalid; fmt < audio::Format_Max; fmt++) { + if (fmt == audio::Format_Invalid) { + continue; + } + const audio::FormatTraits traits = audio::format_traits((audio::Format)fmt); + if (traits.has_flags(audio::Format_SupportsDevices)) { + prn.writef(" %s", traits.name); } } @@ -166,27 +185,30 @@ bool print_file_formats(sndio::BackendDispatcher& backend_dispatcher, return false; } - prn.writef("Supported formats for audio files:\n"); - prn.writef(" (--input-format, --output-format)\n"); + list.sort(core::StringList::OrderNatural); + + prn.writef("Supported formats for file io: [IO_ENCODING]\n"); + prn.writef(" (--io-encoding)\n"); print_string_list(prn, list, "", ""); return true; } -bool print_pcm_formats(core::Printer& prn, core::IArena& arena) { - prn.writef("Supported sample formats for devices, files, packets:\n"); - prn.writef(" (--io-encoding, --packet-encoding)\n"); +bool print_pcm_subformats(core::Printer& prn, core::IArena& arena) { + prn.writef("Supported pcm sub-formats:" + " [PKT_ENCODING, IO_ENCODING]\n"); + prn.writef(" (--packet-encoding, --io-encoding)\n"); bool first = true; audio::PcmTraits prev_traits, curr_traits; - for (int n = 0; n < audio::PcmFormat_Max; n++) { - const audio::PcmFormat fmt = (audio::PcmFormat)n; - if (fmt == audio::PcmFormat_Invalid) { + for (int n = 0; n < audio::PcmSubformat_Max; n++) { + const audio::PcmSubformat fmt = (audio::PcmSubformat)n; + if (fmt == audio::PcmSubformat_Invalid) { continue; } - curr_traits = pcm_format_traits(fmt); + curr_traits = pcm_subformat_traits(fmt); if (prev_traits.bit_depth != curr_traits.bit_depth || prev_traits.bit_width != curr_traits.bit_width) { @@ -199,11 +221,14 @@ bool print_pcm_formats(core::Printer& prn, core::IArena& arena) { (double)curr_traits.bit_width / 8.); } first = false; + } else if (prev_traits.has_flags(audio::Pcm_IsSigned) + != curr_traits.has_flags(audio::Pcm_IsSigned)) { + prn.writef(" "); } prev_traits = curr_traits; - prn.writef(" %s", pcm_format_to_str(fmt)); + prn.writef(" %s", pcm_subformat_to_str(fmt)); } prn.writef("\n"); @@ -211,14 +236,58 @@ bool print_pcm_formats(core::Printer& prn, core::IArena& arena) { return true; } +bool print_file_subformats(sndio::BackendDispatcher& backend_dispatcher, + core::Printer& prn, + core::IArena& arena) { + core::StringList groups(arena); + core::StringList subformats(arena); + + if (!backend_dispatcher.get_supported_subformat_groups(groups)) { + return false; + } + + bool first = true; + + for (const char* grp = groups.front(); grp != NULL; grp = groups.nextof(grp)) { + if (first) { + first = false; + } else { + prn.writef("\n"); + } + + prn.writef("Supported %s sub-formats:" + " [IO_ENCODING]\n", + grp); + prn.writef(" (--io-encoding)\n"); + + if (!backend_dispatcher.get_supported_subformats(grp, subformats)) { + return false; + } + + subformats.sort(core::StringList::OrderNatural); + + prn.writef(" "); + + for (const char* subfmt = subformats.front(); subfmt != NULL; + subfmt = subformats.nextof(subfmt)) { + prn.writef(" %s", subfmt); + } + + prn.writef("\n"); + } + + return true; +} + bool print_channel_masks(core::Printer& prn, core::IArena& arena) { - prn.writef("Supported channel masks for devices, files, packets:\n"); - prn.writef(" (--io-encoding, --packet-encoding)\n"); + prn.writef("Supported channel masks:" + " [PKT_ENCODING, IO_ENCODING]\n"); + prn.writef(" (--packet-encoding, --io-encoding)\n"); for (size_t i = 0; i < ROC_ARRAY_SIZE(audio::ChanMaskNames); i++) { const audio::ChannelMask ch_mask = audio::ChanMaskNames[i].mask; - // TODO(gh-696): finish surround and remove this. + // TODO(gh-696): finish surround and enable all masks. if (ch_mask != audio::ChanMask_Surround_Mono && ch_mask != audio::ChanMask_Surround_Stereo) { continue; @@ -245,7 +314,8 @@ bool print_channel_masks(core::Printer& prn, core::IArena& arena) { } bool print_channel_names(core::Printer& prn, core::IArena& arena) { - prn.writef("pre-defined channel names:\n"); + prn.writef("Supported surround channels:" + " [PKT_ENCODING, IO_ENCODING]\n"); prn.writef(" front FL FR FC\n"); prn.writef(" side SL SR\n"); @@ -258,6 +328,33 @@ bool print_channel_names(core::Printer& prn, core::IArena& arena) { return true; } +bool print_fec_schemes(core::Printer& prn, core::IArena& arena) { + prn.writef("Supported fec encodings: [FEC_ENCODING]\n"); + prn.writef(" (--fec-encoding)\n"); + + const size_t n_schemes = fec::CodecMap::instance().num_schemes(); + + if (n_schemes == 0) { + prn.writef(" none"); + } else { + prn.writef(" auto"); + + for (size_t n = 0; n < n_schemes; n++) { + prn.writef( + " %s", + packet::fec_scheme_to_str(fec::CodecMap::instance().nth_scheme(n))); + } + } + + prn.writef("\n"); + + return true; +} + +void print_section(core::Printer& prn, const char* section) { + prn.writef("[[ %s ]]\n\n", section); +} + } // namespace bool print_supported(unsigned flags, @@ -273,6 +370,8 @@ bool print_supported(unsigned flags, prn.writef("\n"); } + print_section(prn, "URI schemes"); + if (!print_network_schemes(prn, arena)) { return false; } @@ -288,6 +387,32 @@ bool print_supported(unsigned flags, if (!print_io_schemes(backend_dispatcher, prn, arena)) { return false; } + } + + if (flags & Print_Netio) { + if (first) { + first = false; + } else { + prn.writef("\n"); + } + + print_section(prn, "Formats"); + + if (!print_network_formats(prn, arena)) { + return false; + } + } + + if (flags & Print_Sndio) { + if (first) { + first = false; + } else { + prn.writef("\n"); + } + + if (!print_device_formats(prn, arena)) { + return false; + } prn.writef("\n"); @@ -303,11 +428,33 @@ bool print_supported(unsigned flags, prn.writef("\n"); } - if (!print_pcm_formats(prn, arena)) { + print_section(prn, "Sub-formats"); + + if (!print_pcm_subformats(prn, arena)) { return false; } + } - prn.writef("\n"); + if (flags & Print_Sndio) { + if (first) { + first = false; + } else { + prn.writef("\n"); + } + + if (!print_file_subformats(backend_dispatcher, prn, arena)) { + return false; + } + } + + if (flags & Print_Audio) { + if (first) { + first = false; + } else { + prn.writef("\n"); + } + + print_section(prn, "Channels"); if (!print_channel_masks(prn, arena)) { return false; @@ -321,6 +468,8 @@ bool print_supported(unsigned flags, prn.writef("\n"); } + print_section(prn, "FEC"); + if (!print_fec_schemes(prn, arena)) { return false; } diff --git a/src/internal_modules/roc_dbgio/target_posix/roc_dbgio/temp_file.cpp b/src/internal_modules/roc_dbgio/target_posix/roc_dbgio/temp_file.cpp index 8f50e7bbf..6857d6148 100644 --- a/src/internal_modules/roc_dbgio/target_posix/roc_dbgio/temp_file.cpp +++ b/src/internal_modules/roc_dbgio/target_posix/roc_dbgio/temp_file.cpp @@ -30,7 +30,7 @@ TempFile::TempFile(const char* name) { tempdir = "/tmp"; } - if (snprintf(dir_, sizeof(dir_), "%s/rocXXXXXX", tempdir) < 0) { + if (snprintf(dir_, sizeof(dir_), "%s/roc-XXXXXX", tempdir) < 0) { roc_log(LogError, "temp file: snprintf(): %s", core::errno_to_str().c_str()); return; } diff --git a/src/internal_modules/roc_pipeline/config.h b/src/internal_modules/roc_pipeline/config.h index c0b015695..6530591de 100644 --- a/src/internal_modules/roc_pipeline/config.h +++ b/src/internal_modules/roc_pipeline/config.h @@ -38,7 +38,7 @@ namespace pipeline { //! Default sample specification. static const audio::SampleSpec DefaultSampleSpec(44100, - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, audio::ChanMask_Surround_Stereo); diff --git a/src/internal_modules/roc_pipeline/receiver_loop.cpp b/src/internal_modules/roc_pipeline/receiver_loop.cpp index f26db4955..a6168f9fc 100644 --- a/src/internal_modules/roc_pipeline/receiver_loop.cpp +++ b/src/internal_modules/roc_pipeline/receiver_loop.cpp @@ -157,6 +157,12 @@ audio::SampleSpec ReceiverLoop::sample_spec() const { return source_.sample_spec(); } +core::nanoseconds_t ReceiverLoop::frame_length() const { + core::Mutex::Lock lock(source_mutex_); + + return source_.frame_length(); +} + bool ReceiverLoop::has_state() const { core::Mutex::Lock lock(source_mutex_); diff --git a/src/internal_modules/roc_pipeline/receiver_loop.h b/src/internal_modules/roc_pipeline/receiver_loop.h index 5faeef0f9..44cf3387b 100644 --- a/src/internal_modules/roc_pipeline/receiver_loop.h +++ b/src/internal_modules/roc_pipeline/receiver_loop.h @@ -147,6 +147,7 @@ class ReceiverLoop : public PipelineLoop, private sndio::ISource { virtual sndio::ISink* to_sink(); virtual sndio::ISource* to_source(); virtual audio::SampleSpec sample_spec() const; + virtual core::nanoseconds_t frame_length() const; virtual bool has_state() const; virtual sndio::DeviceState state() const; virtual status::StatusCode pause(); diff --git a/src/internal_modules/roc_pipeline/receiver_session.cpp b/src/internal_modules/roc_pipeline/receiver_session.cpp index ac83aa646..f1c52b109 100644 --- a/src/internal_modules/roc_pipeline/receiver_session.cpp +++ b/src/internal_modules/roc_pipeline/receiver_session.cpp @@ -169,7 +169,7 @@ ReceiverSession::ReceiverSession(const ReceiverSessionConfig& session_config, { const audio::SampleSpec out_spec(pkt_encoding->sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, pkt_encoding->sample_spec.channel_set()); depacketizer_.reset(new (depacketizer_) audio::Depacketizer( @@ -212,11 +212,11 @@ ReceiverSession::ReceiverSession(const ReceiverSessionConfig& session_config, if (pkt_encoding->sample_spec.channel_set() != common_config.output_sample_spec.channel_set()) { const audio::SampleSpec in_spec(pkt_encoding->sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, pkt_encoding->sample_spec.channel_set()); const audio::SampleSpec out_spec(pkt_encoding->sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, common_config.output_sample_spec.channel_set()); channel_mapper_reader_.reset( @@ -232,11 +232,11 @@ ReceiverSession::ReceiverSession(const ReceiverSessionConfig& session_config, || pkt_encoding->sample_spec.sample_rate() != common_config.output_sample_spec.sample_rate()) { const audio::SampleSpec in_spec(pkt_encoding->sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, common_config.output_sample_spec.channel_set()); const audio::SampleSpec out_spec(common_config.output_sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, common_config.output_sample_spec.channel_set()); resampler_.reset(processor_map.new_resampler(session_config.resampler, in_spec, @@ -259,7 +259,7 @@ ReceiverSession::ReceiverSession(const ReceiverSessionConfig& session_config, { const audio::SampleSpec inout_spec( - common_config.output_sample_spec.sample_rate(), audio::Sample_RawFormat, + common_config.output_sample_spec.sample_rate(), audio::PcmSubformat_Raw, common_config.output_sample_spec.channel_set()); latency_monitor_.reset(new (latency_monitor_) audio::LatencyMonitor( diff --git a/src/internal_modules/roc_pipeline/receiver_source.cpp b/src/internal_modules/roc_pipeline/receiver_source.cpp index eba1253c3..21e35863c 100644 --- a/src/internal_modules/roc_pipeline/receiver_source.cpp +++ b/src/internal_modules/roc_pipeline/receiver_source.cpp @@ -49,7 +49,7 @@ ReceiverSource::ReceiverSource(const ReceiverSourceConfig& source_config, { const audio::SampleSpec inout_spec( source_config_.common.output_sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, source_config_.common.output_sample_spec.channel_set()); mixer_.reset(new (mixer_) audio::Mixer(inout_spec, true, frame_factory_, arena)); @@ -62,7 +62,7 @@ ReceiverSource::ReceiverSource(const ReceiverSourceConfig& source_config, if (!source_config_.common.output_sample_spec.is_raw()) { const audio::SampleSpec in_spec( source_config_.common.output_sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, source_config_.common.output_sample_spec.channel_set()); pcm_mapper_.reset(new (pcm_mapper_) audio::PcmMapperReader( @@ -193,6 +193,10 @@ audio::SampleSpec ReceiverSource::sample_spec() const { return source_config_.common.output_sample_spec; } +core::nanoseconds_t ReceiverSource::frame_length() const { + return 0; +} + bool ReceiverSource::has_state() const { return true; } diff --git a/src/internal_modules/roc_pipeline/receiver_source.h b/src/internal_modules/roc_pipeline/receiver_source.h index 5d6d7b1df..21185f825 100644 --- a/src/internal_modules/roc_pipeline/receiver_source.h +++ b/src/internal_modules/roc_pipeline/receiver_source.h @@ -88,6 +88,9 @@ class ReceiverSource : public sndio::ISource, public core::NonCopyable<> { //! Get sample specification of the source. virtual audio::SampleSpec sample_spec() const; + //! Get recommended frame length of the source. + virtual core::nanoseconds_t frame_length() const; + //! Check if the source supports state updates. virtual bool has_state() const; diff --git a/src/internal_modules/roc_pipeline/sender_loop.cpp b/src/internal_modules/roc_pipeline/sender_loop.cpp index eaefb1a35..865774709 100644 --- a/src/internal_modules/roc_pipeline/sender_loop.cpp +++ b/src/internal_modules/roc_pipeline/sender_loop.cpp @@ -157,6 +157,12 @@ audio::SampleSpec SenderLoop::sample_spec() const { return sink_.sample_spec(); } +core::nanoseconds_t SenderLoop::frame_length() const { + core::Mutex::Lock lock(sink_mutex_); + + return sink_.frame_length(); +} + bool SenderLoop::has_state() const { core::Mutex::Lock lock(sink_mutex_); diff --git a/src/internal_modules/roc_pipeline/sender_loop.h b/src/internal_modules/roc_pipeline/sender_loop.h index 681c1b9ad..c8289d9bd 100644 --- a/src/internal_modules/roc_pipeline/sender_loop.h +++ b/src/internal_modules/roc_pipeline/sender_loop.h @@ -145,6 +145,7 @@ class SenderLoop : public PipelineLoop, private sndio::ISink { virtual sndio::ISink* to_sink(); virtual sndio::ISource* to_source(); virtual audio::SampleSpec sample_spec() const; + virtual core::nanoseconds_t frame_length() const; virtual bool has_state() const; virtual sndio::DeviceState state() const; virtual status::StatusCode pause(); diff --git a/src/internal_modules/roc_pipeline/sender_session.cpp b/src/internal_modules/roc_pipeline/sender_session.cpp index d3f83faab..51a5d3c9f 100644 --- a/src/internal_modules/roc_pipeline/sender_session.cpp +++ b/src/internal_modules/roc_pipeline/sender_session.cpp @@ -144,7 +144,7 @@ SenderSession::create_transport_pipeline(SenderEndpoint* source_endpoint, { const audio::SampleSpec in_spec(pkt_encoding->sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, pkt_encoding->sample_spec.channel_set()); packetizer_.reset(new (packetizer_) audio::Packetizer( @@ -159,11 +159,11 @@ SenderSession::create_transport_pipeline(SenderEndpoint* source_endpoint, if (pkt_encoding->sample_spec.channel_set() != sink_config_.input_sample_spec.channel_set()) { const audio::SampleSpec in_spec(pkt_encoding->sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, sink_config_.input_sample_spec.channel_set()); const audio::SampleSpec out_spec(pkt_encoding->sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, pkt_encoding->sample_spec.channel_set()); channel_mapper_writer_.reset( @@ -179,11 +179,11 @@ SenderSession::create_transport_pipeline(SenderEndpoint* source_endpoint, || pkt_encoding->sample_spec.sample_rate() != sink_config_.input_sample_spec.sample_rate()) { const audio::SampleSpec in_spec(sink_config_.input_sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, sink_config_.input_sample_spec.channel_set()); const audio::SampleSpec out_spec(pkt_encoding->sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, sink_config_.input_sample_spec.channel_set()); resampler_.reset(processor_map_.new_resampler(sink_config_.resampler, in_spec, @@ -206,7 +206,7 @@ SenderSession::create_transport_pipeline(SenderEndpoint* source_endpoint, { const audio::SampleSpec inout_spec(sink_config_.input_sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, sink_config_.input_sample_spec.channel_set()); feedback_monitor_.reset(new (feedback_monitor_) audio::FeedbackMonitor( diff --git a/src/internal_modules/roc_pipeline/sender_sink.cpp b/src/internal_modules/roc_pipeline/sender_sink.cpp index 419a65f7d..8ac2b8ed9 100644 --- a/src/internal_modules/roc_pipeline/sender_sink.cpp +++ b/src/internal_modules/roc_pipeline/sender_sink.cpp @@ -48,7 +48,7 @@ SenderSink::SenderSink(const SenderSinkConfig& sink_config, { const audio::SampleSpec inout_spec(sink_config_.input_sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, sink_config_.input_sample_spec.channel_set()); fanout_.reset(new (fanout_) audio::Fanout(inout_spec, frame_factory_, arena_)); @@ -60,7 +60,7 @@ SenderSink::SenderSink(const SenderSinkConfig& sink_config, if (!sink_config_.input_sample_spec.is_raw()) { const audio::SampleSpec out_spec(sink_config_.input_sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, sink_config_.input_sample_spec.channel_set()); pcm_mapper_.reset(new (pcm_mapper_) audio::PcmMapperWriter( @@ -192,6 +192,10 @@ audio::SampleSpec SenderSink::sample_spec() const { return sink_config_.input_sample_spec; } +core::nanoseconds_t SenderSink::frame_length() const { + return 0; +} + bool SenderSink::has_state() const { return true; } diff --git a/src/internal_modules/roc_pipeline/sender_sink.h b/src/internal_modules/roc_pipeline/sender_sink.h index 6bef71e97..37290ad4f 100644 --- a/src/internal_modules/roc_pipeline/sender_sink.h +++ b/src/internal_modules/roc_pipeline/sender_sink.h @@ -87,6 +87,9 @@ class SenderSink : public sndio::ISink, public core::NonCopyable<> { //! Get sample specification of the sink. virtual audio::SampleSpec sample_spec() const; + //! Get recommended frame length of the sink. + virtual core::nanoseconds_t frame_length() const; + //! Check if the sink supports state updates. virtual bool has_state() const; diff --git a/src/internal_modules/roc_pipeline/transcoder_sink.cpp b/src/internal_modules/roc_pipeline/transcoder_sink.cpp index 4109f6257..932bd89f1 100644 --- a/src/internal_modules/roc_pipeline/transcoder_sink.cpp +++ b/src/internal_modules/roc_pipeline/transcoder_sink.cpp @@ -39,11 +39,11 @@ TranscoderSink::TranscoderSink(const TranscoderConfig& config, if (config_.input_sample_spec.channel_set() != config_.output_sample_spec.channel_set()) { const audio::SampleSpec from_spec(config_.output_sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, config_.input_sample_spec.channel_set()); const audio::SampleSpec to_spec(config_.output_sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, config_.output_sample_spec.channel_set()); channel_mapper_writer_.reset( @@ -58,11 +58,11 @@ TranscoderSink::TranscoderSink(const TranscoderConfig& config, if (config_.input_sample_spec.sample_rate() != config_.output_sample_spec.sample_rate()) { const audio::SampleSpec from_spec(config_.input_sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, config_.input_sample_spec.channel_set()); const audio::SampleSpec to_spec(config_.output_sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, config_.input_sample_spec.channel_set()); resampler_.reset(processor_map.new_resampler(config_.resampler, from_spec, @@ -116,6 +116,10 @@ audio::SampleSpec TranscoderSink::sample_spec() const { return config_.output_sample_spec; } +core::nanoseconds_t TranscoderSink::frame_length() const { + return 0; +} + bool TranscoderSink::has_state() const { return false; } diff --git a/src/internal_modules/roc_pipeline/transcoder_sink.h b/src/internal_modules/roc_pipeline/transcoder_sink.h index 6a4388207..ffe991cc9 100644 --- a/src/internal_modules/roc_pipeline/transcoder_sink.h +++ b/src/internal_modules/roc_pipeline/transcoder_sink.h @@ -57,6 +57,9 @@ class TranscoderSink : public sndio::ISink, public core::NonCopyable<> { //! Get sample specification of the sink. virtual audio::SampleSpec sample_spec() const; + //! Get recommended frame length of the sink. + virtual core::nanoseconds_t frame_length() const; + //! Check if the sink supports state updates. virtual bool has_state() const; diff --git a/src/internal_modules/roc_pipeline/transcoder_source.cpp b/src/internal_modules/roc_pipeline/transcoder_source.cpp index 6ee9f6e73..719344db7 100644 --- a/src/internal_modules/roc_pipeline/transcoder_source.cpp +++ b/src/internal_modules/roc_pipeline/transcoder_source.cpp @@ -37,11 +37,11 @@ TranscoderSource::TranscoderSource(const TranscoderConfig& config, if (config_.input_sample_spec.channel_set() != config_.output_sample_spec.channel_set()) { const audio::SampleSpec from_spec(config_.input_sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, config_.input_sample_spec.channel_set()); const audio::SampleSpec to_spec(config_.input_sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, config_.output_sample_spec.channel_set()); channel_mapper_reader_.reset( @@ -56,11 +56,11 @@ TranscoderSource::TranscoderSource(const TranscoderConfig& config, if (config_.input_sample_spec.sample_rate() != config_.output_sample_spec.sample_rate()) { const audio::SampleSpec from_spec(config_.input_sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, config_.output_sample_spec.channel_set()); const audio::SampleSpec to_spec(config_.output_sample_spec.sample_rate(), - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, config_.output_sample_spec.channel_set()); resampler_.reset(processor_map.new_resampler(config_.resampler, from_spec, @@ -114,6 +114,10 @@ audio::SampleSpec TranscoderSource::sample_spec() const { return config_.output_sample_spec; } +core::nanoseconds_t TranscoderSource::frame_length() const { + return 0; +} + bool TranscoderSource::has_state() const { return input_source_.has_state(); } diff --git a/src/internal_modules/roc_pipeline/transcoder_source.h b/src/internal_modules/roc_pipeline/transcoder_source.h index a34d82f27..cd62c1545 100644 --- a/src/internal_modules/roc_pipeline/transcoder_source.h +++ b/src/internal_modules/roc_pipeline/transcoder_source.h @@ -56,6 +56,9 @@ class TranscoderSource : public sndio::ISource, public core::NonCopyable<> { //! Get sample specification of the source. virtual audio::SampleSpec sample_spec() const; + //! Get recommended frame length of the source. + virtual core::nanoseconds_t frame_length() const; + //! Check if the source supports state updates. virtual bool has_state() const; diff --git a/src/internal_modules/roc_rtp/encoding.cpp b/src/internal_modules/roc_rtp/encoding.cpp index 1f84f7e1b..277ea83e4 100644 --- a/src/internal_modules/roc_rtp/encoding.cpp +++ b/src/internal_modules/roc_rtp/encoding.cpp @@ -24,14 +24,16 @@ bool parse_encoding(const char* str, Encoding& result) { if (sep == NULL) { roc_log(LogError, "parse encoding: invalid format: missing separator, expected" - " <id>:<format>/<rate>/<channels>"); + " '<id>:<spec>', got '%s'", + str); return false; } if (!isdigit(*str)) { roc_log(LogError, - "parse encoding: invalid format: not a number, expected" - " <id>:<format>/<rate>/<channels>"); + "parse encoding: invalid id: not a number, expected" + " '<id>:<spec>', got '%s'", + str); return false; } @@ -40,23 +42,22 @@ bool parse_encoding(const char* str, Encoding& result) { if (number == ULONG_MAX || !number_end || number_end != sep) { roc_log(LogError, - "parse encoding: invalid format: not a number, expected" - " <id>:<format>/<rate>/<channels>"); + "parse encoding: invalid id: not a number, expected" + " '<id>:<spec>', got '%s'", + str); return false; } if (number > UINT_MAX) { roc_log(LogError, - "parse encoding: number out of range:" - " value=%lu maximum=%u", + "parse encoding: invalid id: out of range:" + " got=%lu max=%u", number, UINT_MAX); return false; } if (!audio::parse_sample_spec(sep + 1, result.sample_spec)) { - roc_log(LogError, - "parse encoding: invalid format: invalid spec, expected" - " <id>:<format>/<rate>/<channels>"); + roc_log(LogError, "parse encoding: invalid spec"); return false; } diff --git a/src/internal_modules/roc_rtp/encoding.h b/src/internal_modules/roc_rtp/encoding.h index 373ffeb81..8fbf33c3a 100644 --- a/src/internal_modules/roc_rtp/encoding.h +++ b/src/internal_modules/roc_rtp/encoding.h @@ -14,7 +14,7 @@ #include "roc_audio/iframe_decoder.h" #include "roc_audio/iframe_encoder.h" -#include "roc_audio/pcm_format.h" +#include "roc_audio/pcm_subformat.h" #include "roc_audio/sample_spec.h" #include "roc_core/attributes.h" #include "roc_core/iarena.h" diff --git a/src/internal_modules/roc_rtp/encoding_map.cpp b/src/internal_modules/roc_rtp/encoding_map.cpp index 1e63d8bf2..1f1557601 100644 --- a/src/internal_modules/roc_rtp/encoding_map.cpp +++ b/src/internal_modules/roc_rtp/encoding_map.cpp @@ -7,9 +7,9 @@ */ #include "roc_rtp/encoding_map.h" +#include "roc_audio/format.h" #include "roc_audio/pcm_decoder.h" #include "roc_audio/pcm_encoder.h" -#include "roc_audio/sample_format.h" #include "roc_audio/sample_spec_to_str.h" #include "roc_core/panic.h" #include "roc_status/code_to_str.h" @@ -24,7 +24,7 @@ EncodingMap::EncodingMap(core::IArena& arena) Encoding enc; enc.payload_type = PayloadType_L16_Mono; enc.sample_spec = audio::SampleSpec( - 44100, audio::PcmFormat_SInt16_Be, audio::ChanLayout_Surround, + 44100, audio::PcmSubformat_SInt16_Be, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, audio::ChanMask_Surround_Mono); enc.packet_flags = packet::Packet::FlagAudio; @@ -34,7 +34,7 @@ EncodingMap::EncodingMap(core::IArena& arena) Encoding enc; enc.payload_type = PayloadType_L16_Stereo; enc.sample_spec = audio::SampleSpec( - 44100, audio::PcmFormat_SInt16_Be, audio::ChanLayout_Surround, + 44100, audio::PcmSubformat_SInt16_Be, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, audio::ChanMask_Surround_Stereo); enc.packet_flags = packet::Packet::FlagAudio; @@ -80,7 +80,36 @@ status::StatusCode EncodingMap::register_encoding(Encoding enc) { return status::StatusBadArg; } - if (!enc.sample_spec.is_valid()) { + if (enc.sample_spec.format() == audio::Format_Invalid) { + roc_log(LogError, + "encoding map: failed to register encoding:" + " missing format"); + return status::StatusBadArg; + } + + if (enc.sample_spec.format() == audio::Format_Pcm + && enc.sample_spec.pcm_subformat() == audio::PcmSubformat_Invalid) { + roc_log(LogError, + "encoding map: failed to register encoding:" + " missing sub-format"); + return status::StatusBadArg; + } + + if (enc.sample_spec.sample_rate() == 0) { + roc_log(LogError, + "encoding map: failed to register encoding:" + " missing rate"); + return status::StatusBadArg; + } + + if (!enc.sample_spec.channel_set().is_valid()) { + roc_log(LogError, + "encoding map: failed to register encoding:" + " missing channels"); + return status::StatusBadArg; + } + + if (!enc.sample_spec.is_complete()) { roc_log(LogError, "encoding map: failed to register encoding:" " invalid encoding parameters"); @@ -133,8 +162,8 @@ void EncodingMap::resolve_codecs_(Encoding& enc) { return; } - switch (enc.sample_spec.sample_format()) { - case audio::SampleFormat_Pcm: + switch (enc.sample_spec.format()) { + case audio::Format_Pcm: if (!enc.new_encoder) { enc.new_encoder = &audio::PcmEncoder::construct; } @@ -143,7 +172,7 @@ void EncodingMap::resolve_codecs_(Encoding& enc) { } break; - case audio::SampleFormat_Invalid: + default: break; } } diff --git a/src/internal_modules/roc_sndio/backend_dispatcher.cpp b/src/internal_modules/roc_sndio/backend_dispatcher.cpp index d0d2026d2..be1ed4de0 100644 --- a/src/internal_modules/roc_sndio/backend_dispatcher.cpp +++ b/src/internal_modules/roc_sndio/backend_dispatcher.cpp @@ -17,41 +17,28 @@ namespace sndio { namespace { -DriverType select_driver_type(const address::IoUri& uri) { - if (uri.is_file()) { - return DriverType_File; - } else { - return DriverType_Device; +bool match_driver(const DriverInfo& driver_info, + unsigned driver_flags, + const char* driver_name) { + if (driver_name && strcmp(driver_info.driver_name, driver_name) != 0) { + return false; } -} -const char* select_driver_name(const address::IoUri& uri, const char* force_format) { - if (uri.is_file()) { - if (force_format && *force_format) { - // use specific file driver - return force_format; - } - // auto-detect file driver - return NULL; + if ((driver_info.driver_flags & driver_flags) != driver_flags) { + return false; } - // use specific device driver - return uri.scheme(); + return true; } -bool match_driver(const DriverInfo& driver_info, - const char* driver_name, - DriverType driver_type, - unsigned driver_flags) { - if (driver_name != NULL && strcmp(driver_info.name, driver_name) != 0) { - return false; - } - - if (driver_info.type != driver_type) { +bool match_format(const FormatInfo& format_info, + unsigned driver_flags, + const char* format_name) { + if (format_name && strcmp(format_info.format_name, format_name) != 0) { return false; } - if ((driver_info.flags & driver_flags) == 0) { + if ((format_info.driver_flags & driver_flags) != driver_flags) { return false; } @@ -104,20 +91,19 @@ BackendDispatcher::open_default_source(const IoConfig& io_config, } status::StatusCode BackendDispatcher::open_sink(const address::IoUri& uri, - const char* force_format, const IoConfig& io_config, core::ScopedPtr<ISink>& result) { if (!uri.is_valid()) { roc_panic("backend dispatcher: invalid uri"); } - const DriverType driver_type = select_driver_type(uri); - const char* driver_name = select_driver_name(uri, force_format); + const char* driver = uri.scheme(); + const char* path = uri.path(); IDevice* device = NULL; - const status::StatusCode code = open_device_( - DeviceType_Sink, driver_type, driver_name, uri.path(), io_config, &device); + const status::StatusCode code = + open_file_or_device_(DeviceType_Sink, driver, path, io_config, &device); if (code != status::StatusOK) { return code; } @@ -130,20 +116,19 @@ status::StatusCode BackendDispatcher::open_sink(const address::IoUri& uri, } status::StatusCode BackendDispatcher::open_source(const address::IoUri& uri, - const char* force_format, const IoConfig& io_config, core::ScopedPtr<ISource>& result) { if (!uri.is_valid()) { roc_panic("backend dispatcher: invalid uri"); } - const DriverType driver_type = select_driver_type(uri); - const char* driver_name = select_driver_name(uri, force_format); + const char* driver = uri.scheme(); + const char* path = uri.path(); IDevice* device = NULL; - const status::StatusCode code = open_device_( - DeviceType_Source, driver_type, driver_name, uri.path(), io_config, &device); + const status::StatusCode code = + open_file_or_device_(DeviceType_Source, driver, path, io_config, &device); if (code != status::StatusOK) { return code; } @@ -162,36 +147,24 @@ bool BackendDispatcher::get_supported_schemes(core::StringList& result) { for (size_t n = 0; n < BackendMap::instance().num_drivers(); n++) { const DriverInfo& driver_info = BackendMap::instance().nth_driver(n); - // every device driver has its own scheme - if (driver_info.type == DriverType_Device) { - if (result.find(driver_info.name)) { - continue; - } - if (!result.push_back(driver_info.name)) { + if (!result.find(driver_info.driver_name)) { + if (!result.push_back(driver_info.driver_name)) { return false; } } } - // all file drivers has a single "file" scheme - if (!result.push_back("file")) { - return false; - } - return true; } bool BackendDispatcher::get_supported_formats(core::StringList& result) { result.clear(); - for (size_t n = 0; n < BackendMap::instance().num_drivers(); n++) { - const DriverInfo& driver_info = BackendMap::instance().nth_driver(n); + for (size_t n = 0; n < BackendMap::instance().num_formats(); n++) { + const FormatInfo& format_info = BackendMap::instance().nth_format(n); - if (driver_info.type == DriverType_File) { - if (result.find(driver_info.name)) { - continue; - } - if (!result.push_back(driver_info.name)) { + if (!result.find(format_info.format_name)) { + if (!result.push_back(format_info.format_name)) { return false; } } @@ -200,105 +173,209 @@ bool BackendDispatcher::get_supported_formats(core::StringList& result) { return true; } +bool BackendDispatcher::get_supported_subformat_groups(core::StringList& result) { + result.clear(); + + for (size_t n = 0; n < BackendMap::instance().num_backends(); n++) { + IBackend& backend = BackendMap::instance().nth_backend(n); + + if (!backend.discover_subformat_groups(result)) { + return false; + } + } + + return true; +} + +bool BackendDispatcher::get_supported_subformats(const char* group, + core::StringList& result) { + result.clear(); + + for (size_t n = 0; n < BackendMap::instance().num_backends(); n++) { + IBackend& backend = BackendMap::instance().nth_backend(n); + + if (!backend.discover_subformats(group, result)) { + return false; + } + } + + return true; +} + status::StatusCode BackendDispatcher::open_default_device_(DeviceType device_type, const IoConfig& io_config, IDevice** result) { - const unsigned driver_flags = - unsigned(DriverFlag_IsDefault - | (device_type == DeviceType_Sink ? DriverFlag_SupportsSink - : DriverFlag_SupportsSource)); + roc_panic_if(!result); status::StatusCode code = status::StatusNoDriver; + // Try all drivers with Driver_DefaultDevice flag. + const unsigned driver_flags = Driver_Device | Driver_DefaultDevice + | (device_type == DeviceType_Sink ? Driver_SupportsSink : Driver_SupportsSource); + for (size_t n = 0; n < BackendMap::instance().num_drivers(); n++) { const DriverInfo& driver_info = BackendMap::instance().nth_driver(n); - if (!match_driver(driver_info, NULL, DriverType_Device, driver_flags)) { + if (!match_driver(driver_info, driver_flags, NULL)) { continue; } - code = driver_info.backend->open_device(device_type, DriverType_Device, - driver_info.name, "default", io_config, - frame_factory_, arena_, result); + code = driver_info.backend->open_device(device_type, driver_info.driver_name, + "default", io_config, frame_factory_, + arena_, result); if (code == status::StatusOK) { return 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)); + if (code == status::StatusNoDriver) { + continue; } + + break; } - roc_log(LogError, "backend dispatcher: failed to open default %s: status=%s", - device_type_to_str(device_type), status::code_to_str(code)); + roc_log(LogError, "backend dispatcher: failed to open default device: status=%s", + status::code_to_str(code)); return code; } +status::StatusCode BackendDispatcher::open_file_or_device_(DeviceType device_type, + const char* driver, + const char* path, + const IoConfig& io_config, + IDevice** result) { + roc_panic_if(!driver); + roc_panic_if(!path); + roc_panic_if(!result); + + if (strcmp(driver, "file") == 0) { + if (io_config.latency != 0) { + roc_log(LogError, + "backend dispatcher: it's not possible to specify io latency" + " for files"); + return status::StatusBadConfig; + } + + if (device_type == DeviceType_Sink && strcmp(path, "-") == 0 + && !io_config.sample_spec.has_format()) { + roc_log( + LogError, + "backend dispatcher: when output file is \"-\", format must be specified" + " explicitly via io encoding"); + return status::StatusBadConfig; + } + + return open_file_(device_type, driver, path, io_config, result); + } + + return open_device_(device_type, driver, path, io_config, result); +} + status::StatusCode BackendDispatcher::open_device_(DeviceType device_type, - DriverType driver_type, - const char* driver_name, + const char* driver, const char* path, const IoConfig& io_config, IDevice** result) { - const unsigned driver_flags = - (device_type == DeviceType_Sink ? DriverFlag_SupportsSink - : DriverFlag_SupportsSource); + status::StatusCode code = status::StatusNoDriver; + + const unsigned driver_flags = Driver_Device + | (device_type == DeviceType_Sink ? Driver_SupportsSink : Driver_SupportsSource); + + // We're opening device, driver defines device type (pulseaudio, alsa, etc). + // Try backends which support matching driver. + for (size_t n = 0; n < BackendMap::instance().num_drivers(); n++) { + const DriverInfo& driver_info = BackendMap::instance().nth_driver(n); + if (!match_driver(driver_info, driver_flags, driver)) { + continue; + } + + code = driver_info.backend->open_device(device_type, driver, path, io_config, + frame_factory_, arena_, result); + + if (code == status::StatusOK) { + return code; + } + + if (code == status::StatusNoDriver) { + // No error, backend just doesn't support driver. + continue; + } + + break; + } + + roc_log(LogError, + "backend dispatcher: failed to open device:" + " device_type=%s driver=%s path=%s status=%s", + device_type_to_str(device_type), driver, path, status::code_to_str(code)); + + return code; +} + +status::StatusCode BackendDispatcher::open_file_(DeviceType device_type, + const char* driver, + const char* path, + const IoConfig& io_config, + IDevice** result) { status::StatusCode code = status::StatusNoDriver; - if (driver_name != NULL) { - for (size_t n = 0; n < BackendMap::instance().num_drivers(); n++) { - const DriverInfo& driver_info = BackendMap::instance().nth_driver(n); + const unsigned driver_flags = Driver_File + | (device_type == DeviceType_Sink ? Driver_SupportsSink : Driver_SupportsSource); - if (!match_driver(driver_info, driver_name, driver_type, driver_flags)) { + if (io_config.sample_spec.has_format()) { + // We're opening file and format is specified explicitly (wav, flac, etc). + // Try backends which support requested format. + for (size_t n = 0; n < BackendMap::instance().num_formats(); n++) { + const FormatInfo& format_info = BackendMap::instance().nth_format(n); + + if (!match_format(format_info, driver_flags, + io_config.sample_spec.format_name())) { continue; } - code = driver_info.backend->open_device(device_type, driver_type, - driver_info.name, path, io_config, + code = format_info.backend->open_device(device_type, driver, path, io_config, frame_factory_, arena_, result); if (code == status::StatusOK) { return 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)); + if (code == status::StatusNoDriver || code == status::StatusNoFormat) { + // No error, backend just doesn't support driver or format. + continue; } + + break; } } else { + // We're opening file and format is omitted. + // Try all backends. for (size_t n = 0; n < BackendMap::instance().num_backends(); n++) { IBackend& backend = BackendMap::instance().nth_backend(n); - code = backend.open_device(device_type, driver_type, NULL, path, io_config, + code = backend.open_device(device_type, driver, path, io_config, frame_factory_, arena_, result); if (code == status::StatusOK) { return 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)); + if (code == status::StatusNoDriver || code == status::StatusNoFormat) { + // No error, backend just doesn't support driver or format. + continue; } + + break; } } roc_log(LogError, - "backend dispatcher: failed to open %s:" - " driver_type=%s driver_name=%s path=%s status=%s", - device_type_to_str(device_type), driver_type_to_str(driver_type), driver_name, - path, status::code_to_str(code)); + "backend dispatcher: failed to open file:" + " device_type=%s driver=%s path=%s status=%s", + device_type_to_str(device_type), driver, path, status::code_to_str(code)); return code; } diff --git a/src/internal_modules/roc_sndio/backend_dispatcher.h b/src/internal_modules/roc_sndio/backend_dispatcher.h index 002ed0453..a4b4df4e9 100644 --- a/src/internal_modules/roc_sndio/backend_dispatcher.h +++ b/src/internal_modules/roc_sndio/backend_dispatcher.h @@ -47,13 +47,11 @@ class BackendDispatcher : public core::NonCopyable<> { //! Create and open a sink. ROC_ATTR_NODISCARD status::StatusCode open_sink(const address::IoUri& uri, - const char* force_format, const IoConfig& io_config, core::ScopedPtr<ISink>& result); //! Create and open a source. ROC_ATTR_NODISCARD status::StatusCode open_source(const address::IoUri& uri, - const char* force_format, const IoConfig& io_config, core::ScopedPtr<ISource>& result); @@ -63,18 +61,36 @@ class BackendDispatcher : public core::NonCopyable<> { //! Get all supported file formats. ROC_ATTR_NODISCARD bool get_supported_formats(core::StringList& result); + //! Get all groups of sub-formats. + ROC_ATTR_NODISCARD bool get_supported_subformat_groups(core::StringList& result); + + //! Get all sub-formats in group. + ROC_ATTR_NODISCARD bool get_supported_subformats(const char* group, + core::StringList& result); + private: status::StatusCode open_default_device_(DeviceType device_type, const IoConfig& io_config, IDevice** result); + status::StatusCode open_file_or_device_(DeviceType device_type, + const char* driver, + const char* path, + const IoConfig& io_config, + IDevice** result); + status::StatusCode open_device_(DeviceType device_type, - DriverType driver_type, - const char* driver_name, + const char* driver, const char* path, const IoConfig& io_config, IDevice** result); + status::StatusCode open_file_(DeviceType device_type, + const char* driver, + const char* path, + const IoConfig& io_config, + IDevice** result); + audio::FrameFactory frame_factory_; core::IArena& arena_; }; diff --git a/src/internal_modules/roc_sndio/backend_map.cpp b/src/internal_modules/roc_sndio/backend_map.cpp index a55d3b00f..ab4f1858e 100644 --- a/src/internal_modules/roc_sndio/backend_map.cpp +++ b/src/internal_modules/roc_sndio/backend_map.cpp @@ -15,12 +15,15 @@ namespace sndio { BackendMap::BackendMap() : backends_(core::NoopArena) - , drivers_(core::NoopArena) { + , drivers_(core::NoopArena) + , formats_(core::NoopArena) { register_backends_(); - register_drivers_(); + collect_drivers_(); + collect_formats_(); - roc_log(LogDebug, "backend map: initializing: n_backends=%d n_drivers=%d", - (int)backends_.size(), (int)drivers_.size()); + roc_log(LogDebug, + "backend map: initializing: n_backends=%d n_drivers=%d n_formats=%d", + (int)backends_.size(), (int)drivers_.size(), (int)formats_.size()); } size_t BackendMap::num_backends() const { @@ -39,6 +42,14 @@ const DriverInfo& BackendMap::nth_driver(size_t driver_index) const { return drivers_[driver_index]; } +size_t BackendMap::num_formats() const { + return formats_.size(); +} + +const FormatInfo& BackendMap::nth_format(size_t format_index) const { + return formats_[format_index]; +} + void BackendMap::register_backends_() { #ifdef ROC_TARGET_PULSEAUDIO pulseaudio_backend_.reset(new (pulseaudio_backend_) PulseaudioBackend); @@ -59,15 +70,25 @@ void BackendMap::register_backends_() { #endif // ROC_TARGET_SOX } -void BackendMap::register_drivers_() { +void BackendMap::add_backend_(IBackend* backend) { + if (!backends_.push_back(backend)) { + roc_panic("backend map: can't register backend"); + } +} + +void BackendMap::collect_drivers_() { for (size_t n = 0; n < backends_.size(); n++) { - backends_[n]->discover_drivers(drivers_); + if (!backends_[n]->discover_drivers(drivers_)) { + roc_panic("backend map: can't register driver"); + } } } -void BackendMap::add_backend_(IBackend* backend) { - if (!backends_.push_back(backend)) { - roc_panic("backend map: can't register backend"); +void BackendMap::collect_formats_() { + for (size_t n = 0; n < backends_.size(); n++) { + if (!backends_[n]->discover_formats(formats_)) { + roc_panic("backend map: can't register format"); + } } } diff --git a/src/internal_modules/roc_sndio/backend_map.h b/src/internal_modules/roc_sndio/backend_map.h index 766a28f78..a9fe05ac6 100644 --- a/src/internal_modules/roc_sndio/backend_map.h +++ b/src/internal_modules/roc_sndio/backend_map.h @@ -55,16 +55,23 @@ class BackendMap : public core::NonCopyable<> { //! Get driver by index. const DriverInfo& nth_driver(size_t driver_index) const; + //! Get number of file formats available. + size_t num_formats() const; + + //! Get driver by index. + const FormatInfo& nth_format(size_t format_index) const; + private: friend class core::Singleton<BackendMap>; BackendMap(); void register_backends_(); - void register_drivers_(); - void add_backend_(IBackend*); + void collect_drivers_(); + void collect_formats_(); + #ifdef ROC_TARGET_PULSEAUDIO core::Optional<PulseaudioBackend> pulseaudio_backend_; #endif // ROC_TARGET_PULSEAUDIO @@ -81,6 +88,7 @@ class BackendMap : public core::NonCopyable<> { core::Array<IBackend*, MaxBackends> backends_; core::Array<DriverInfo, MaxDrivers> drivers_; + core::Array<FormatInfo, MaxFormats> formats_; }; } // namespace sndio diff --git a/src/internal_modules/roc_sndio/driver.cpp b/src/internal_modules/roc_sndio/driver.cpp deleted file mode 100644 index 463267f8c..000000000 --- a/src/internal_modules/roc_sndio/driver.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2022 Roc 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/driver.h" - -namespace roc { -namespace sndio { - -const char* driver_type_to_str(DriverType type) { - switch (type) { - case DriverType_Device: - return "device"; - - case DriverType_File: - return "file"; - - case DriverType_Invalid: - default: - break; - } - - return "<invalid>"; -} - -} // namespace sndio -} // namespace roc diff --git a/src/internal_modules/roc_sndio/driver.h b/src/internal_modules/roc_sndio/driver.h index ff2406f99..2ba4422a0 100644 --- a/src/internal_modules/roc_sndio/driver.h +++ b/src/internal_modules/roc_sndio/driver.h @@ -7,7 +7,7 @@ */ //! @file roc_sndio/driver.h -//! @brief Driver types. +//! @brief Driver information. #ifndef ROC_SNDIO_DRIVER_H_ #define ROC_SNDIO_DRIVER_H_ @@ -21,71 +21,96 @@ namespace sndio { class IBackend; //! Maximum number of drivers. -static const size_t MaxDrivers = 128; +static const size_t MaxDrivers = 16; -//! Driver type. -enum DriverType { - //! Invalid type. - DriverType_Invalid, - - //! Driver for audio files. - DriverType_File, - - //! Driver for audio devices. - DriverType_Device -}; +//! Maximum number of file formats. +static const size_t MaxFormats = 128; //! Driver flags. enum DriverFlags { + //! This is driver for audio files. + Driver_File = (1 << 0), + + //! This is driver for audio devices. + Driver_Device = (1 << 1), + //! Driver is used if no file or device is specified. - DriverFlag_IsDefault = (1 << 0), + Driver_DefaultDevice = (1 << 2), //! Driver supports sources (input). - DriverFlag_SupportsSource = (1 << 1), + Driver_SupportsSource = (1 << 3), //! Driver supports sinks (output). - DriverFlag_SupportsSink = (1 << 2) + Driver_SupportsSink = (1 << 4) }; -//! Driver information. +//! Information about driver. struct DriverInfo { //! Driver name. - char name[20]; - - //! Driver type. - DriverType type; + char driver_name[12]; //! Driver flags. - unsigned int flags; + unsigned int driver_flags; - //! Backend the driver uses. + //! Associated backend. IBackend* backend; //! Initialize. - DriverInfo() - : type(DriverType_Invalid) - , flags(0) - , backend(NULL) { - strcpy(name, ""); + DriverInfo() { + strcpy(driver_name, ""); + driver_flags = 0; + backend = NULL; } //! Initialize. - DriverInfo(const char* driver_name, - DriverType driver_type, - unsigned int driver_flags, - IBackend* driver_backend) - : type(driver_type) - , flags(driver_flags) - , backend(driver_backend) { - if (!driver_name || strlen(driver_name) > sizeof(name) - 1) { + DriverInfo(const char* p_driver_name, + unsigned int p_driver_flags, + IBackend* p_backend) { + if (!p_driver_name || strlen(p_driver_name) > sizeof(driver_name) - 1) { roc_panic("invalid driver name"); } - strcpy(name, driver_name); + strcpy(driver_name, p_driver_name); + driver_flags = p_driver_flags; + backend = p_backend; } }; -//! Convert driver type to string. -const char* driver_type_to_str(DriverType type); +//! Information about format supported by "file" driver. +struct FormatInfo { + //! Driver name. + char driver_name[12]; + + //! Driver flags. + unsigned int driver_flags; + + //! Format name. + char format_name[12]; + + //! Associated backend. + IBackend* backend; + + //! Initialize. + FormatInfo() { + strcpy(driver_name, ""); + driver_flags = 0; + strcpy(format_name, ""); + backend = NULL; + } + + //! Initialize. + FormatInfo(const char* p_driver_name, + const char* p_format_name, + unsigned int p_driver_flags, + IBackend* p_backend) { + if (!p_format_name || strlen(p_format_name) > sizeof(format_name) - 1) { + roc_panic("invalid format name"); + } + strcpy(driver_name, p_driver_name); + driver_flags = p_driver_flags; + strcpy(format_name, p_format_name); + backend = p_backend; + } +}; } // namespace sndio } // namespace roc diff --git a/src/internal_modules/roc_sndio/ibackend.h b/src/internal_modules/roc_sndio/ibackend.h index 94244f872..9abb1af6d 100644 --- a/src/internal_modules/roc_sndio/ibackend.h +++ b/src/internal_modules/roc_sndio/ibackend.h @@ -16,6 +16,7 @@ #include "roc_core/array.h" #include "roc_core/attributes.h" #include "roc_core/iarena.h" +#include "roc_core/string_list.h" #include "roc_sndio/device_type.h" #include "roc_sndio/driver.h" #include "roc_sndio/idevice.h" @@ -37,12 +38,24 @@ class IBackend { virtual const char* name() const = 0; //! Append supported drivers to the list. - virtual void discover_drivers(core::Array<DriverInfo, MaxDrivers>& driver_list) = 0; + virtual ROC_ATTR_NODISCARD bool + discover_drivers(core::Array<DriverInfo, MaxDrivers>& result) = 0; + + //! Append supported formats to the list. + virtual ROC_ATTR_NODISCARD bool + discover_formats(core::Array<FormatInfo, MaxFormats>& result) = 0; + + //! Append supported groups of sub-formats to the list. + virtual ROC_ATTR_NODISCARD bool + discover_subformat_groups(core::StringList& result) = 0; + + //! Append supported sub-formats of a group to the list. + virtual ROC_ATTR_NODISCARD bool discover_subformats(const char* group, + core::StringList& result) = 0; //! Create and open a sink or source. virtual ROC_ATTR_NODISCARD status::StatusCode open_device(DeviceType device_type, - DriverType driver_type, const char* driver, const char* path, const IoConfig& io_config, diff --git a/src/internal_modules/roc_sndio/idevice.h b/src/internal_modules/roc_sndio/idevice.h index 98ba2e80f..5ba59b141 100644 --- a/src/internal_modules/roc_sndio/idevice.h +++ b/src/internal_modules/roc_sndio/idevice.h @@ -64,6 +64,10 @@ class IDevice : public core::ArenaAllocation { //! Frame written to or read from the device should use this specification. virtual audio::SampleSpec sample_spec() const = 0; + //! Get recommended frame length of the device. + //! Frames written to or read from the device are recommended to have this size. + virtual core::nanoseconds_t frame_length() const = 0; + //! Check if the device supports state updates. //! @remarks //! If true, state() returns current state, and pause() and resume() diff --git a/src/internal_modules/roc_sndio/io_config.h b/src/internal_modules/roc_sndio/io_config.h index ad8e75009..f4aee8a1c 100644 --- a/src/internal_modules/roc_sndio/io_config.h +++ b/src/internal_modules/roc_sndio/io_config.h @@ -20,12 +20,6 @@ namespace roc { namespace sndio { -//! Default frame length. -//! @remarks -//! 10ms is rather high, but works well even on cheap sound cards and CPUs. -//! Usually you can use much lower values. -const core::nanoseconds_t DefaultFrameLength = 10 * core::Millisecond; - //! Sink and source config. struct IoConfig { //! Sample spec @@ -41,7 +35,7 @@ struct IoConfig { IoConfig() : sample_spec() , latency(0) - , frame_length(DefaultFrameLength) { + , frame_length(0) { } }; diff --git a/src/internal_modules/roc_sndio/io_pump.cpp b/src/internal_modules/roc_sndio/io_pump.cpp index f697f465a..64793ae09 100644 --- a/src/internal_modules/roc_sndio/io_pump.cpp +++ b/src/internal_modules/roc_sndio/io_pump.cpp @@ -7,12 +7,19 @@ */ #include "roc_sndio/io_pump.h" +#include "roc_audio/sample_spec_to_str.h" #include "roc_core/log.h" #include "roc_status/code_to_str.h" namespace roc { namespace sndio { +namespace { + +const core::nanoseconds_t DefaultFrameLength = 10 * core::Millisecond; + +} // namespace + IoPump::IoPump(core::IPool& frame_pool, core::IPool& frame_buffer_pool, ISource& source, @@ -26,22 +33,29 @@ IoPump::IoPump(core::IPool& frame_pool, , current_source_(&source) , sink_(sink) , sample_spec_(io_config.sample_spec) - , frame_size_(io_config.sample_spec.ns_2_bytes(io_config.frame_length)) - , frame_duration_(io_config.sample_spec.ns_2_stream_timestamp(io_config.frame_length)) + , frame_size_(0) + , frame_duration_(0) , mode_(mode) , was_active_(false) , stop_(0) + , transferred_bytes_(0) , init_status_(status::NoStatus) { - if (frame_size_ == 0 || frame_duration_ == 0) { - roc_log(LogError, "pump: invalid frame length %lld", - (long long)io_config.frame_length); - init_status_ = status::StatusBadConfig; - return; + if (!io_config.sample_spec.is_complete()) { + roc_panic("io pump: expected complete sample spec: spec=%s", + audio::sample_spec_to_str(io_config.sample_spec).c_str()); } + core::nanoseconds_t frame_len = io_config.frame_length; + if (frame_len == 0) { + frame_len = DefaultFrameLength; + } + + frame_size_ = io_config.sample_spec.ns_2_bytes(frame_len); + frame_duration_ = io_config.sample_spec.ns_2_stream_timestamp(frame_len); + frame_ = frame_factory_.allocate_frame(frame_size_); if (!frame_) { - roc_log(LogError, "pump: can't allocate frame"); + roc_log(LogError, "io pump: can't allocate frame"); init_status_ = status::StatusNoMem; return; } @@ -54,7 +68,7 @@ status::StatusCode IoPump::init_status() const { } status::StatusCode IoPump::run() { - roc_log(LogDebug, "pump: starting main loop"); + roc_log(LogDebug, "io pump: starting main loop"); status::StatusCode code = status::NoStatus; @@ -66,7 +80,10 @@ status::StatusCode IoPump::run() { } if (code == status::StatusFinish) { - code = status::StatusOK; // EOF is fine + // EOF is fine + code = status::StatusOK; + roc_log(LogDebug, "io pump: transferred %.3f MB", + (double)transferred_bytes_ / 1024 * 1024); } if (code == status::StatusOK) { @@ -78,10 +95,10 @@ status::StatusCode IoPump::run() { code = close_code; } - roc_log(LogDebug, "pump: exiting main loop"); + roc_log(LogDebug, "io pump: exiting main loop"); roc_panic_if_msg(code <= status::NoStatus || code >= status::MaxStatus, - "pump: invalid status code %d", code); + "io pump: invalid status code %d", code); return code; } @@ -95,7 +112,7 @@ status::StatusCode IoPump::next_() { // User called stop(). if (stop_) { - roc_log(LogDebug, "pump: got stop request, exiting"); + roc_log(LogDebug, "io pump: got stop request, exiting"); return status::StatusAbort; } @@ -105,17 +122,17 @@ status::StatusCode IoPump::next_() { // inactive first time, we exit. if (mode_ == ModeOneshot && was_active_) { roc_log(LogInfo, - "pump: main source became inactive in oneshot mode, exiting"); + "io pump: main source became inactive in oneshot mode, exiting"); return status::StatusFinish; } // User specified --backup, when main source becomes inactive, we // switch to specified backup source. if (backup_source_) { - roc_log(LogInfo, "pump: main source became inactive, switching to backup"); + roc_log(LogInfo, "io pump: main source became inactive, switching to backup"); if ((code = backup_source_->rewind()) != status::StatusOK) { - roc_log(LogError, "pump: can't rewind backup source: status=%s", + roc_log(LogError, "io pump: can't rewind backup source: status=%s", status::code_to_str(code)); return code; } @@ -128,7 +145,7 @@ status::StatusCode IoPump::next_() { // Main source became active. if (current_source_ != &main_source_ && main_source_.state() == DeviceState_Active) { - roc_log(LogInfo, "pump: main source became active, switching to it"); + roc_log(LogInfo, "io pump: main source became active, switching to it"); if ((code = switch_source_(&main_source_)) != status::StatusOK) { return code; @@ -141,23 +158,23 @@ status::StatusCode IoPump::next_() { if (code == status::StatusFinish) { // EOF from main source causes exit. if (current_source_ == &main_source_) { - roc_log(LogInfo, "pump: got eof from main source, exiting"); + roc_log(LogInfo, "io pump: got eof from main source, exiting"); return code; } // EOF from backup source causes rewind. if (current_source_ == backup_source_) { - roc_log(LogDebug, "pump: got eof from backup source, rewinding"); + roc_log(LogDebug, "io pump: got eof from backup source, rewinding"); if ((code = backup_source_->rewind()) != status::StatusOK) { - roc_log(LogError, "pump: can't rewind backup source: status=%s", + roc_log(LogError, "io pump: can't rewind backup source: status=%s", status::code_to_str(code)); return code; } } } else if (code != status::StatusOK) { // Source or sink failure. - roc_log(LogError, "pump: got error when copying frame: status=%s", + roc_log(LogError, "io pump: got error when copying frame: status=%s", status::code_to_str(code)); return code; } @@ -176,12 +193,12 @@ status::StatusCode IoPump::switch_source_(ISource* new_source) { // Switch from backup to main. if (new_source == &main_source_ && current_source_ != &main_source_) { - roc_log(LogInfo, "pump: switching to main source"); + roc_log(LogInfo, "io pump: switching to main source"); // Pause backup. if (backup_source_ && backup_source_->has_state()) { if ((code = backup_source_->pause()) != status::StatusOK) { - roc_log(LogError, "pump: can't pause backup source: status=%s", + roc_log(LogError, "io pump: can't pause backup source: status=%s", status::code_to_str(code)); return code; } @@ -189,7 +206,7 @@ status::StatusCode IoPump::switch_source_(ISource* new_source) { // Resume main. if ((code = main_source_.resume()) != status::StatusOK) { - roc_log(LogError, "pump: can't resume main source: status=%s", + roc_log(LogError, "io pump: can't resume main source: status=%s", status::code_to_str(code)); return code; } @@ -199,13 +216,13 @@ status::StatusCode IoPump::switch_source_(ISource* new_source) { // Switch from main to backup. if (new_source == backup_source_ && current_source_ != backup_source_) { - roc_log(LogInfo, "pump: switching to backup source"); + roc_log(LogInfo, "io pump: switching to backup source"); roc_panic_if(!backup_source_); // Pause main. if ((code = main_source_.pause()) != status::StatusOK) { - roc_log(LogError, "pump: can't pause main source: status=%s", + roc_log(LogError, "io pump: can't pause main source: status=%s", status::code_to_str(code)); return code; } @@ -213,7 +230,7 @@ status::StatusCode IoPump::switch_source_(ISource* new_source) { // Resume backup. if (backup_source_->has_state()) { if ((code = backup_source_->resume()) != status::StatusOK) { - roc_log(LogError, "pump: can't resume backup source: status=%s", + roc_log(LogError, "io pump: can't resume backup source: status=%s", status::code_to_str(code)); return code; } @@ -279,6 +296,8 @@ status::StatusCode IoPump::transfer_frame_(ISource& source, ISink& sink) { source.reclock(core::timestamp(core::ClockUnix) + playback_latency); } + transferred_bytes_ += frame_->num_bytes(); + return status::StatusOK; } @@ -286,7 +305,7 @@ status::StatusCode IoPump::flush_sink_() { const status::StatusCode code = sink_.flush(); if (code != status::StatusOK) { - roc_log(LogError, "pump: got error when flushing sink: status=%s", + roc_log(LogError, "io pump: got error when flushing sink: status=%s", status::code_to_str(code)); } @@ -301,7 +320,7 @@ status::StatusCode IoPump::close_all_devices_() { if (devices[i]) { status::StatusCode device_code = devices[i]->close(); if (device_code != status::StatusOK) { - roc_log(LogError, "pump: failed to close device: status=%s", + roc_log(LogError, "io pump: failed to close device: status=%s", status::code_to_str(device_code)); if (first_error == status::StatusOK) { first_error = device_code; diff --git a/src/internal_modules/roc_sndio/io_pump.h b/src/internal_modules/roc_sndio/io_pump.h index 9c586b093..6724e2e1a 100644 --- a/src/internal_modules/roc_sndio/io_pump.h +++ b/src/internal_modules/roc_sndio/io_pump.h @@ -83,13 +83,15 @@ class IoPump : public core::NonCopyable<> { const audio::SampleSpec sample_spec_; audio::FramePtr frame_; - const size_t frame_size_; - const packet::stream_timestamp_t frame_duration_; + size_t frame_size_; + packet::stream_timestamp_t frame_duration_; const Mode mode_; bool was_active_; core::Atomic<int> stop_; + uint64_t transferred_bytes_; + status::StatusCode init_status_; }; diff --git a/src/internal_modules/roc_sndio/target_pulseaudio/roc_sndio/pulseaudio_backend.cpp b/src/internal_modules/roc_sndio/target_pulseaudio/roc_sndio/pulseaudio_backend.cpp index 7bb528c78..4320aeb9c 100644 --- a/src/internal_modules/roc_sndio/target_pulseaudio/roc_sndio/pulseaudio_backend.cpp +++ b/src/internal_modules/roc_sndio/target_pulseaudio/roc_sndio/pulseaudio_backend.cpp @@ -24,34 +24,48 @@ const char* PulseaudioBackend::name() const { return "pulseaudio"; } -void PulseaudioBackend::discover_drivers( - core::Array<DriverInfo, MaxDrivers>& driver_list) { - if (!driver_list.push_back(DriverInfo("pulse", DriverType_Device, - DriverFlag_IsDefault | DriverFlag_SupportsSink - | DriverFlag_SupportsSource, - this))) { - roc_panic("pulseaudio backend: can't add driver"); +bool PulseaudioBackend::discover_drivers(core::Array<DriverInfo, MaxDrivers>& result) { + if (!result.push_back(DriverInfo("pulse", + Driver_Device | Driver_DefaultDevice + | Driver_SupportsSink | Driver_SupportsSource, + this))) { + return false; } + return true; +} + +bool PulseaudioBackend::discover_formats(core::Array<FormatInfo, MaxFormats>& result) { + // no formats except pcm + return true; +} + +bool PulseaudioBackend::discover_subformat_groups(core::StringList& result) { + // no sub-formats except pcm + return true; +} + +bool PulseaudioBackend::discover_subformats(const char* group, core::StringList& result) { + // no sub-formats except pcm + return true; } status::StatusCode PulseaudioBackend::open_device(DeviceType device_type, - DriverType driver_type, const char* driver, const char* path, const IoConfig& io_config, audio::FrameFactory& frame_factory, core::IArena& arena, IDevice** result) { - if (driver_type != DriverType_Device) { - return status::StatusNoDriver; - } + roc_panic_if(!driver); + roc_panic_if(!path); - if (driver && strcmp(driver, "pulse") != 0) { + if (strcmp(driver, "pulse") != 0) { + // Not pulse://, go to next backend. return status::StatusNoDriver; } core::ScopedPtr<PulseaudioDevice> device( - new (arena) PulseaudioDevice(frame_factory, arena, io_config, device_type)); + new (arena) PulseaudioDevice(frame_factory, arena, io_config, device_type, path)); if (!device) { roc_log(LogDebug, "pulseaudio backend: can't allocate device: path=%s", path); @@ -59,17 +73,9 @@ status::StatusCode PulseaudioBackend::open_device(DeviceType device_type, } if (device->init_status() != status::StatusOK) { - roc_log(LogDebug, - "pulseaudio backend: can't initialize device: path=%s status=%s", path, - status::code_to_str(device->init_status())); - return device->init_status(); - } - - const status::StatusCode code = device->open(path); - if (code != status::StatusOK) { roc_log(LogDebug, "pulseaudio backend: can't open device: path=%s status=%s", - path, status::code_to_str(code)); - return code; + path, status::code_to_str(device->init_status())); + return device->init_status(); } *result = device.hijack(); diff --git a/src/internal_modules/roc_sndio/target_pulseaudio/roc_sndio/pulseaudio_backend.h b/src/internal_modules/roc_sndio/target_pulseaudio/roc_sndio/pulseaudio_backend.h index d163aa2ce..da3aa69cb 100644 --- a/src/internal_modules/roc_sndio/target_pulseaudio/roc_sndio/pulseaudio_backend.h +++ b/src/internal_modules/roc_sndio/target_pulseaudio/roc_sndio/pulseaudio_backend.h @@ -27,12 +27,23 @@ class PulseaudioBackend : public IBackend, core::NonCopyable<> { virtual const char* name() const; //! Append supported drivers to the list. - virtual void discover_drivers(core::Array<DriverInfo, MaxDrivers>& driver_list); + virtual ROC_ATTR_NODISCARD bool + discover_drivers(core::Array<DriverInfo, MaxDrivers>& result); + + //! Append supported formats to the list. + virtual ROC_ATTR_NODISCARD bool + discover_formats(core::Array<FormatInfo, MaxFormats>& result); + + //! Append supported groups of sub-formats to the list. + virtual ROC_ATTR_NODISCARD bool discover_subformat_groups(core::StringList& result); + + //! Append supported sub-formats of a group to the list. + virtual ROC_ATTR_NODISCARD bool discover_subformats(const char* group, + core::StringList& result); //! Create and open a sink or source. virtual ROC_ATTR_NODISCARD status::StatusCode open_device(DeviceType device_type, - DriverType driver_type, const char* driver, const char* path, const IoConfig& io_config, diff --git a/src/internal_modules/roc_sndio/target_pulseaudio/roc_sndio/pulseaudio_device.cpp b/src/internal_modules/roc_sndio/target_pulseaudio/roc_sndio/pulseaudio_device.cpp index 5cb1e9367..1f252ed21 100644 --- a/src/internal_modules/roc_sndio/target_pulseaudio/roc_sndio/pulseaudio_device.cpp +++ b/src/internal_modules/roc_sndio/target_pulseaudio/roc_sndio/pulseaudio_device.cpp @@ -8,8 +8,8 @@ #include "roc_sndio/pulseaudio_device.h" #include "roc_audio/channel_defs.h" +#include "roc_audio/format.h" #include "roc_audio/sample.h" -#include "roc_audio/sample_format.h" #include "roc_audio/sample_spec_to_str.h" #include "roc_core/log.h" #include "roc_core/panic.h" @@ -27,86 +27,90 @@ const core::nanoseconds_t ReportInterval = 10 * core::Second; // 40ms or 20ms, and sometimes even 10ms const core::nanoseconds_t DefaultLatency = core::Millisecond * 60; +// 10ms is rather high, but works well even on cheap sound cards and CPUs. +// Usually you can use much lower values. +const core::nanoseconds_t DefaultFrameLength = 10 * core::Millisecond; + const core::nanoseconds_t MinTimeout = core::Millisecond * 50; const core::nanoseconds_t MaxTimeout = core::Second * 2; -audio::PcmFormat from_pulse_format(pa_sample_format fmt) { +audio::PcmSubformat from_pulse_format(pa_sample_format fmt) { switch (fmt) { case PA_SAMPLE_U8: - return audio::PcmFormat_UInt8; + return audio::PcmSubformat_UInt8; case PA_SAMPLE_S16LE: - return audio::PcmFormat_SInt16_Le; + return audio::PcmSubformat_SInt16_Le; case PA_SAMPLE_S16BE: - return audio::PcmFormat_SInt16_Be; + return audio::PcmSubformat_SInt16_Be; case PA_SAMPLE_S24LE: - return audio::PcmFormat_SInt24_Le; + return audio::PcmSubformat_SInt24_Le; case PA_SAMPLE_S24BE: - return audio::PcmFormat_SInt24_Be; + return audio::PcmSubformat_SInt24_Be; case PA_SAMPLE_S24_32LE: - return audio::PcmFormat_SInt24_4_Le; + return audio::PcmSubformat_SInt24_4_Le; case PA_SAMPLE_S24_32BE: - return audio::PcmFormat_SInt24_4_Be; + return audio::PcmSubformat_SInt24_4_Be; case PA_SAMPLE_S32LE: - return audio::PcmFormat_SInt32_Le; + return audio::PcmSubformat_SInt32_Le; case PA_SAMPLE_S32BE: - return audio::PcmFormat_SInt32_Be; + return audio::PcmSubformat_SInt32_Be; case PA_SAMPLE_FLOAT32LE: - return audio::PcmFormat_Float32_Le; + return audio::PcmSubformat_Float32_Le; case PA_SAMPLE_FLOAT32BE: - return audio::PcmFormat_Float32_Be; + return audio::PcmSubformat_Float32_Be; default: break; } - return audio::PcmFormat_Invalid; + return audio::PcmSubformat_Invalid; } -pa_sample_format to_pulse_format(audio::PcmFormat fmt) { +pa_sample_format to_pulse_format(audio::PcmSubformat fmt) { switch (fmt) { - case audio::PcmFormat_UInt8: - case audio::PcmFormat_UInt8_Le: - case audio::PcmFormat_UInt8_Be: + case audio::PcmSubformat_UInt8: + case audio::PcmSubformat_UInt8_Le: + case audio::PcmSubformat_UInt8_Be: return PA_SAMPLE_U8; - case audio::PcmFormat_SInt16: + case audio::PcmSubformat_SInt16: return PA_SAMPLE_S16NE; - case audio::PcmFormat_SInt16_Le: + case audio::PcmSubformat_SInt16_Le: return PA_SAMPLE_S16LE; - case audio::PcmFormat_SInt16_Be: + case audio::PcmSubformat_SInt16_Be: return PA_SAMPLE_S16BE; - case audio::PcmFormat_SInt24: + case audio::PcmSubformat_SInt24: return PA_SAMPLE_S24NE; - case audio::PcmFormat_SInt24_Le: + case audio::PcmSubformat_SInt24_Le: return PA_SAMPLE_S24LE; - case audio::PcmFormat_SInt24_Be: + case audio::PcmSubformat_SInt24_Be: return PA_SAMPLE_S24BE; - case audio::PcmFormat_SInt24_4: + case audio::PcmSubformat_SInt24_4: return PA_SAMPLE_S24_32NE; - case audio::PcmFormat_SInt24_4_Le: + case audio::PcmSubformat_SInt24_4_Le: return PA_SAMPLE_S24_32LE; - case audio::PcmFormat_SInt24_4_Be: + case audio::PcmSubformat_SInt24_4_Be: return PA_SAMPLE_S24_32BE; - case audio::PcmFormat_SInt32: + case audio::PcmSubformat_SInt32: return PA_SAMPLE_S32NE; - case audio::PcmFormat_SInt32_Le: + case audio::PcmSubformat_SInt32_Le: return PA_SAMPLE_S32LE; - case audio::PcmFormat_SInt32_Be: + case audio::PcmSubformat_SInt32_Be: return PA_SAMPLE_S32BE; - case audio::PcmFormat_Float32: + case audio::PcmSubformat_Float32: return PA_SAMPLE_FLOAT32NE; - case audio::PcmFormat_Float32_Le: + case audio::PcmSubformat_Float32_Le: return PA_SAMPLE_FLOAT32LE; - case audio::PcmFormat_Float32_Be: + case audio::PcmSubformat_Float32_Be: return PA_SAMPLE_FLOAT32BE; default: @@ -121,7 +125,8 @@ pa_sample_format to_pulse_format(audio::PcmFormat fmt) { PulseaudioDevice::PulseaudioDevice(audio::FrameFactory& frame_factory, core::IArena& arena, const IoConfig& io_config, - DeviceType device_type) + DeviceType device_type, + const char* device) : IDevice(arena) , ISink(arena) , ISource(arena) @@ -148,14 +153,24 @@ PulseaudioDevice::PulseaudioDevice(audio::FrameFactory& frame_factory, , timer_deadline_ns_(0) , rate_limiter_(ReportInterval) , init_status_(status::NoStatus) { - if (sample_spec_.sample_format() != audio::SampleFormat_Invalid - && (sample_spec_.sample_format() != audio::SampleFormat_Pcm - || to_pulse_format(sample_spec_.pcm_format()) == PA_SAMPLE_INVALID)) { + if (io_config.sample_spec.has_format() + && io_config.sample_spec.format() != audio::Format_Pcm) { roc_log(LogError, - "pulseaudio %s: requested sample format not supported by backend:" - " sample_spec=%s", - device_type_to_str(device_type_), - audio::sample_spec_to_str(sample_spec_).c_str()); + "pulseaudio %s: invalid io encoding:" + " <format> '%s' not supported by backend: spec=%s", + device_type_to_str(device_type_), io_config.sample_spec.format_name(), + audio::sample_spec_to_str(io_config.sample_spec).c_str()); + init_status_ = status::StatusBadConfig; + return; + } + + if (io_config.sample_spec.has_subformat() + && to_pulse_format(io_config.sample_spec.pcm_subformat()) == PA_SAMPLE_INVALID) { + roc_log(LogError, + "pulseaudio %s: invalid io encoding:" + " <subformat> '%s' not supported by backend: spec=%s", + device_type_to_str(device_type_), io_config.sample_spec.format_name(), + audio::sample_spec_to_str(io_config.sample_spec).c_str()); init_status_ = status::StatusBadConfig; return; } @@ -176,6 +191,21 @@ PulseaudioDevice::PulseaudioDevice(audio::FrameFactory& frame_factory, timeout_ns_ = MaxTimeout; } + roc_log(LogDebug, "pulseaudio %s: opening device: device=%s", + device_type_to_str(device_type_), device); + + if (device && strcmp(device, "default") != 0) { + device_ = device; + } + + if ((init_status_ = start_mainloop_()) != status::StatusOK) { + return; + } + + if ((init_status_ = open_()) != status::StatusOK) { + return; + } + init_status_ = status::StatusOK; } @@ -191,32 +221,6 @@ status::StatusCode PulseaudioDevice::init_status() const { return init_status_; } -status::StatusCode PulseaudioDevice::open(const char* device) { - if (mainloop_) { - roc_panic("pulseaudio %s: can't call open() twice", - device_type_to_str(device_type_)); - } - - roc_log(LogDebug, "pulseaudio %s: opening device: device=%s", - device_type_to_str(device_type_), device); - - if (device && strcmp(device, "default") != 0) { - device_ = device; - } - - status::StatusCode code = status::NoStatus; - - if ((code = start_mainloop_()) != status::StatusOK) { - return code; - } - - if ((code = open_()) != status::StatusOK) { - return code; - } - - return status::StatusOK; -} - DeviceType PulseaudioDevice::type() const { return device_type_; } @@ -241,6 +245,18 @@ audio::SampleSpec PulseaudioDevice::sample_spec() const { return sample_spec; } +core::nanoseconds_t PulseaudioDevice::frame_length() const { + want_mainloop_(); + + pa_threaded_mainloop_lock(mainloop_); + + const core::nanoseconds_t frame_len = frame_len_ns_; + + pa_threaded_mainloop_unlock(mainloop_); + + return frame_len; +} + bool PulseaudioDevice::has_state() const { return true; } @@ -648,17 +664,17 @@ void PulseaudioDevice::device_info_cb_(pa_context*, } bool PulseaudioDevice::load_device_params_(const pa_sample_spec& device_spec) { - if (sample_spec_.sample_format() == audio::SampleFormat_Invalid) { - audio::PcmFormat fmt = from_pulse_format(device_spec.format); + if (sample_spec_.format() == audio::Format_Invalid) { + audio::PcmSubformat fmt = from_pulse_format(device_spec.format); - if (fmt == audio::PcmFormat_Invalid) { - // We don't support device's native format, so ask pulseaudio - // to do conversion for our native format. - fmt = audio::Sample_RawFormat; + if (fmt == audio::PcmSubformat_Invalid) { + // If don't support device's native format, ask pulseaudio + // to do conversion to our native format. + fmt = audio::PcmSubformat_Raw; } - sample_spec_.set_sample_format(audio::SampleFormat_Pcm); - sample_spec_.set_pcm_format(fmt); + sample_spec_.set_format(audio::Format_Pcm); + sample_spec_.set_pcm_subformat(fmt); } if (sample_spec_.sample_rate() == 0) { @@ -671,7 +687,7 @@ bool PulseaudioDevice::load_device_params_(const pa_sample_spec& device_spec) { sample_spec_.channel_set().set_count(device_spec.channels); } - if (!sample_spec_.is_valid()) { + if (!sample_spec_.is_complete()) { roc_log(LogError, "pulseaudio %s: can't determine device sample spec:" " sample_spec=%s", @@ -717,7 +733,7 @@ bool PulseaudioDevice::load_device_params_(const pa_sample_spec& device_spec) { } bool PulseaudioDevice::init_stream_params_(const pa_sample_spec& device_spec) { - stream_spec_.format = to_pulse_format(sample_spec_.pcm_format()); + stream_spec_.format = to_pulse_format(sample_spec_.pcm_subformat()); stream_spec_.rate = (uint32_t)sample_spec_.sample_rate(); stream_spec_.channels = (uint8_t)sample_spec_.num_channels(); diff --git a/src/internal_modules/roc_sndio/target_pulseaudio/roc_sndio/pulseaudio_device.h b/src/internal_modules/roc_sndio/target_pulseaudio/roc_sndio/pulseaudio_device.h index c3f352c6f..29e0b0a94 100644 --- a/src/internal_modules/roc_sndio/target_pulseaudio/roc_sndio/pulseaudio_device.h +++ b/src/internal_modules/roc_sndio/target_pulseaudio/roc_sndio/pulseaudio_device.h @@ -37,15 +37,13 @@ class PulseaudioDevice : public ISink, public ISource, public core::NonCopyable< PulseaudioDevice(audio::FrameFactory& frame_factory, core::IArena& arena, const IoConfig& io_config, - DeviceType device_type); + DeviceType device_type, + const char* device); ~PulseaudioDevice(); //! Check if the object was successfully constructed. status::StatusCode init_status() const; - //! Open device. - ROC_ATTR_NODISCARD status::StatusCode open(const char* device); - //! Get device type. virtual DeviceType type() const; @@ -58,6 +56,9 @@ class PulseaudioDevice : public ISink, public ISource, public core::NonCopyable< //! Get sample specification of the device. virtual audio::SampleSpec sample_spec() const; + //! Get recommended frame length of the device. + virtual core::nanoseconds_t frame_length() const; + //! Check if the device supports state updates. virtual bool has_state() const; 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 223c0e262..118f51fc1 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 @@ -25,116 +25,145 @@ const char* SndfileBackend::name() const { return "sndfile"; } -void SndfileBackend::discover_drivers(core::Array<DriverInfo, MaxDrivers>& driver_list) { +bool SndfileBackend::discover_drivers(core::Array<DriverInfo, MaxDrivers>& result) { + if (!result.push_back(DriverInfo( + "file", Driver_File | Driver_SupportsSink | Driver_SupportsSource, this))) { + return false; + } + return true; +} + +bool SndfileBackend::discover_formats(core::Array<FormatInfo, MaxFormats>& result) { SF_FORMAT_INFO format_info; - int total_number_of_drivers = 0; + int major_format_count = 0; - if (int err = sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &total_number_of_drivers, + if (int err = sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &major_format_count, sizeof(int))) { - roc_panic("sndfile backend: sf_command(SFC_GET_FORMAT_MAJOR_COUNT) failed: %s", - sf_error_number(err)); + roc_log(LogError, + "sndfile backend: sf_command(SFC_GET_FORMAT_MAJOR_COUNT) failed: %s", + sf_error_number(err)); + return false; } - for (int format_index = 0; format_index < total_number_of_drivers; format_index++) { - format_info.format = format_index; + for (int fmt_index = 0; fmt_index < major_format_count; fmt_index++) { + format_info.format = fmt_index; if (int err = sf_command(NULL, SFC_GET_FORMAT_MAJOR, &format_info, sizeof(format_info))) { - roc_panic("sndfile backend: sf_command(SFC_GET_FORMAT_MAJOR) failed: %s", - sf_error_number(err)); + roc_log(LogError, + "sndfile backend: sf_command(SFC_GET_FORMAT_MAJOR) failed: %s", + sf_error_number(err)); + return false; } - const char* driver = format_info.extension; + // Format name = file extension. + const char* format_name = format_info.extension; - for (size_t map_index = 0; map_index < ROC_ARRAY_SIZE(sndfile_driver_remap); + for (size_t map_index = 0; map_index < ROC_ARRAY_SIZE(sndfile_format_remap); map_index++) { - if ((sndfile_driver_remap[map_index].format_mask & SF_FORMAT_TYPEMASK) + if ((sndfile_format_remap[map_index].format_mask & SF_FORMAT_TYPEMASK) == format_info.format) { - driver = sndfile_driver_remap[map_index].driver_name; + // Some format names are remapped. + format_name = sndfile_format_remap[map_index].name; } } - if (!driver_list.push_back( - DriverInfo(driver, DriverType_File, - DriverFlag_SupportsSource | DriverFlag_SupportsSink, this))) { - roc_panic("sndfile backend: allocation failed"); + if (!result.push_back(FormatInfo( + "file", format_name, + Driver_File | Driver_SupportsSource | Driver_SupportsSink, this))) { + roc_log(LogError, "sndfile backend: allocation failed"); + return false; + } + } + + return true; +} + +bool SndfileBackend::discover_subformat_groups(core::StringList& result) { + for (size_t n = 0; n < ROC_ARRAY_SIZE(sndfile_subformat_map); n++) { + if (result.find(sndfile_subformat_map[n].group)) { + continue; + } + if (!result.push_back(sndfile_subformat_map[n].group)) { + roc_log(LogError, "sndfile backend: allocation failed"); + return false; + } + } + + return true; +} + +bool SndfileBackend::discover_subformats(const char* group, core::StringList& result) { + roc_panic_if(!group); + + for (size_t n = 0; n < ROC_ARRAY_SIZE(sndfile_subformat_map); n++) { + if (strcmp(sndfile_subformat_map[n].group, group) != 0) { + continue; + } + if (result.find(sndfile_subformat_map[n].name)) { + continue; + } + if (!result.push_back(sndfile_subformat_map[n].name)) { + roc_log(LogError, "sndfile backend: allocation failed"); + return false; } } + + return true; } status::StatusCode SndfileBackend::open_device(DeviceType device_type, - DriverType driver_type, const char* driver, const char* path, const IoConfig& io_config, audio::FrameFactory& frame_factory, core::IArena& arena, IDevice** result) { - if (driver_type != DriverType_File) { + roc_panic_if(!driver); + roc_panic_if(!path); + + if (strcmp(driver, "file") != 0) { + // Not file://, go to next backend. return status::StatusNoDriver; } switch (device_type) { case DeviceType_Sink: { core::ScopedPtr<SndfileSink> sink( - new (arena) SndfileSink(frame_factory, arena, io_config)); + new (arena) SndfileSink(frame_factory, arena, io_config, path)); if (!sink) { - roc_log(LogDebug, "sndfile backend: can't allocate sink: driver=%s path=%s", - driver, path); + roc_log(LogDebug, "sndfile backend: can't allocate sink: path=%s", path); return status::StatusNoMem; } if (sink->init_status() != status::StatusOK) { - roc_log(LogDebug, - "sndfile backend: can't initialize sink: driver=%s path=%s status=%s", - driver, path, status::code_to_str(sink->init_status())); + roc_log(LogDebug, "sndfile backend: can't open sink: path=%s status=%s", path, + status::code_to_str(sink->init_status())); return sink->init_status(); } - const status::StatusCode code = sink->open(driver, path); - if (code != status::StatusOK) { - roc_log(LogDebug, - "sndfile backend: can't open sink: driver=%s path=%s status=%s", - driver, path, status::code_to_str(code)); - return code; - } - *result = sink.hijack(); return status::StatusOK; } break; case DeviceType_Source: { core::ScopedPtr<SndfileSource> source( - new (arena) SndfileSource(frame_factory, arena, io_config)); + new (arena) SndfileSource(frame_factory, arena, io_config, path)); if (!source) { - roc_log(LogDebug, "sndfile backend: can't allocate source: driver=%s path=%s", - driver, path); + roc_log(LogDebug, "sndfile backend: can't allocate source: path=%s", path); return status::StatusNoMem; } if (source->init_status() != status::StatusOK) { - roc_log( - LogDebug, - "sndfile backend: can't initialize source: driver=%s path=%s status=%s", - driver, path, status::code_to_str(source->init_status())); + roc_log(LogDebug, "sndfile backend: can't open source: path=%s status=%s", + path, status::code_to_str(source->init_status())); return source->init_status(); } - const status::StatusCode code = source->open(driver, path); - if (code != status::StatusOK) { - roc_log(LogDebug, - "sndfile backend: can't open source: driver=%s path=%s status=%s", - driver, path, status::code_to_str(code)); - return code; - } - *result = source.hijack(); return status::StatusOK; } break; - - default: - break; } roc_panic("sndfile backend: invalid device type"); diff --git a/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_backend.h b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_backend.h index aca7a3198..268193a29 100644 --- a/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_backend.h +++ b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_backend.h @@ -27,12 +27,23 @@ class SndfileBackend : public IBackend, core::NonCopyable<> { virtual const char* name() const; //! Append supported drivers to the list. - virtual void discover_drivers(core::Array<DriverInfo, MaxDrivers>& driver_list); + virtual ROC_ATTR_NODISCARD bool + discover_drivers(core::Array<DriverInfo, MaxDrivers>& result); + + //! Append supported formats to the list. + virtual ROC_ATTR_NODISCARD bool + discover_formats(core::Array<FormatInfo, MaxFormats>& result); + + //! Append supported groups of sub-formats to the list. + virtual ROC_ATTR_NODISCARD bool discover_subformat_groups(core::StringList& result); + + //! Append supported sub-formats of a group to the list. + virtual ROC_ATTR_NODISCARD bool discover_subformats(const char* group, + core::StringList& result); //! Create and open a sink or source. virtual ROC_ATTR_NODISCARD status::StatusCode open_device(DeviceType device_type, - DriverType driver_type, const char* driver, const char* path, const IoConfig& io_config, diff --git a/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_helpers.cpp b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_helpers.cpp new file mode 100644 index 000000000..12b093470 --- /dev/null +++ b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_helpers.cpp @@ -0,0 +1,349 @@ +/* + * 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_helpers.h" +#include "roc_sndio/sndfile_tables.h" + +namespace roc { +namespace sndio { + +namespace { + +int pcm_2_sf(audio::PcmSubformat fmt) { + switch (fmt) { + case audio::PcmSubformat_UInt8: + case audio::PcmSubformat_UInt8_Le: + case audio::PcmSubformat_UInt8_Be: + return SF_FORMAT_PCM_U8 | SF_ENDIAN_FILE; + + case audio::PcmSubformat_SInt8: + case audio::PcmSubformat_SInt8_Le: + case audio::PcmSubformat_SInt8_Be: + return SF_FORMAT_PCM_S8 | SF_ENDIAN_FILE; + + case audio::PcmSubformat_SInt16: + return SF_FORMAT_PCM_16 | SF_ENDIAN_FILE; + case audio::PcmSubformat_SInt16_Le: + return SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE; + case audio::PcmSubformat_SInt16_Be: + return SF_FORMAT_PCM_16 | SF_ENDIAN_BIG; + + case audio::PcmSubformat_SInt24: + return SF_FORMAT_PCM_24 | SF_ENDIAN_FILE; + case audio::PcmSubformat_SInt24_Le: + return SF_FORMAT_PCM_24 | SF_ENDIAN_LITTLE; + case audio::PcmSubformat_SInt24_Be: + return SF_FORMAT_PCM_24 | SF_ENDIAN_BIG; + + case audio::PcmSubformat_SInt32: + return SF_FORMAT_PCM_32 | SF_ENDIAN_FILE; + case audio::PcmSubformat_SInt32_Le: + return SF_FORMAT_PCM_32 | SF_ENDIAN_LITTLE; + case audio::PcmSubformat_SInt32_Be: + return SF_FORMAT_PCM_32 | SF_ENDIAN_BIG; + + case audio::PcmSubformat_Float32: + return SF_FORMAT_FLOAT | SF_ENDIAN_FILE; + case audio::PcmSubformat_Float32_Le: + return SF_FORMAT_FLOAT | SF_ENDIAN_LITTLE; + case audio::PcmSubformat_Float32_Be: + return SF_FORMAT_FLOAT | SF_ENDIAN_BIG; + + case audio::PcmSubformat_Float64: + return SF_FORMAT_DOUBLE | SF_ENDIAN_FILE; + case audio::PcmSubformat_Float64_Le: + return SF_FORMAT_DOUBLE | SF_ENDIAN_LITTLE; + case audio::PcmSubformat_Float64_Be: + return SF_FORMAT_DOUBLE | SF_ENDIAN_BIG; + + default: + break; + } + + return 0; +} + +} // namespace + +status::StatusCode sndfile_select_major_format(SF_INFO& file_info, + audio::SampleSpec& sample_spec, + const char* path) { + roc_panic_if(!path); + + const char* file_extension = NULL; + const char* dot = strrchr(path, '.'); + if (dot && dot != path) { + file_extension = dot; + } + + // First try to select format by iterating through sndfile_driver_remap. + if (sample_spec.has_format()) { + // If format is specified, match by format name. + for (size_t idx = 0; idx < ROC_ARRAY_SIZE(sndfile_format_remap); idx++) { + if (strcmp(sample_spec.format_name(), sndfile_format_remap[idx].name) == 0) { + file_info.format = sndfile_format_remap[idx].format_mask; + return status::StatusOK; + } + } + } else if (file_extension != NULL) { + // If format is omitted, match by file extension. + for (size_t idx = 0; idx < ROC_ARRAY_SIZE(sndfile_format_remap); idx++) { + if (sndfile_format_remap[idx].file_extension != NULL) { + if (strcmp(file_extension, sndfile_format_remap[idx].file_extension) + == 0) { + file_info.format = sndfile_format_remap[idx].format_mask; + if (!sample_spec.set_custom_format(sndfile_format_remap[idx].name)) { + continue; + } + return status::StatusOK; + } + } + } + } + + // Then try to select format by iterating through all sndfile major formats. + int major_format_count = 0; + if (int errnum = sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &major_format_count, + sizeof(int))) { + roc_panic("sndfile backend: sf_command(SFC_GET_FORMAT_MAJOR_COUNT) failed: %s", + sf_error_number(errnum)); + } + + for (int idx = 0; idx < major_format_count; idx++) { + SF_FORMAT_INFO format_info; + memset(&format_info, 0, sizeof(format_info)); + format_info.format = idx; + if (int errnum = sf_command(NULL, SFC_GET_FORMAT_MAJOR, &format_info, + sizeof(format_info))) { + roc_panic("sndfile backend: sf_command(SFC_GET_FORMAT_MAJOR) failed: %s", + sf_error_number(errnum)); + } + + if (sample_spec.has_format()) { + // If format is specified, match by format name. + // Note that format name = file extension. + if (strcmp(format_info.extension, sample_spec.format_name()) == 0) { + file_info.format = format_info.format; + return status::StatusOK; + } + } else if (file_extension != NULL) { + // If format is omitted, match by file extension. + if (strcmp(format_info.extension, file_extension) == 0) { + file_info.format = format_info.format; + if (!sample_spec.set_custom_format(format_info.name)) { + continue; + } + return status::StatusOK; + } + } + } + + if (sample_spec.has_format()) { + roc_log( + LogDebug, + "sndfile backend: requested format '%s' not supported by backend: path=%s", + sample_spec.format_name(), path); + } else { + roc_log(LogDebug, + "sndfile backend: can't detect file format from extension: path=%s", + path); + } + // Not a wav file, go to next backend. + return status::StatusNoFormat; +} + +status::StatusCode sndfile_select_sub_format(SF_INFO& file_info, + audio::SampleSpec& sample_spec, + const char* path) { + roc_panic_if(!path); + + const int format_mask = file_info.format; + + // If sub-format is specified, use it. + if (sample_spec.has_subformat()) { + int subformat_mask = 0; + + if (sample_spec.pcm_subformat() != audio::PcmSubformat_Invalid) { + // PCM sub-formats are mapped by enum. + subformat_mask = pcm_2_sf(sample_spec.pcm_subformat()); + } else { + // Other sub-formats are mapped by string name. + for (size_t idx = 0; idx < ROC_ARRAY_SIZE(sndfile_subformat_map); idx++) { + if (strcmp(sample_spec.subformat_name(), sndfile_subformat_map[idx].name) + == 0) { + subformat_mask = sndfile_subformat_map[idx].format_mask; + break; + } + } + } + + if (subformat_mask != 0) { + file_info.format = format_mask | subformat_mask; + + if (sf_format_check(&file_info)) { + return status::StatusOK; + } + } + + roc_log(LogError, + "sndfile backend: invalid io encoding:" + " <subformat> '%s' not allowed when <format> is '%s'", + sample_spec.subformat_name(), sample_spec.format_name()); + return status::StatusBadConfig; + } + + // For some formats, sub-format must be always specified explicitly. + for (size_t idx = 0; idx < ROC_ARRAY_SIZE(sndfile_explicit_formats); idx++) { + if (file_info.format == sndfile_explicit_formats[idx]) { + roc_log(LogError, + "sndfile backend: invalid io encoding:" + " <subformat> is required when <format> is '%s'", + sample_spec.format_name()); + return status::StatusBadConfig; + } + } + + // If sub-format is omitted, first try if we can work without sub-format. + file_info.format = format_mask; + + if (sf_format_check(&file_info)) { + return status::StatusOK; + } + + // We can't work without sub-format, choose one of the default sub-formats. + for (size_t idx = 0; idx < ROC_ARRAY_SIZE(sndfile_default_subformats); idx++) { + const int subformat_mask = sndfile_default_subformats[idx]; + + file_info.format = format_mask | subformat_mask; + + if (sf_format_check(&file_info)) { + return status::StatusOK; + } + } + + roc_log(LogError, + "sndfile backend: invalid io encoding:" + " <subformat> is required when <format> is '%s'", + sample_spec.format_name()); + return status::StatusBadConfig; +} + +status::StatusCode sndfile_check_input_spec(const SF_INFO& file_info, + const audio::SampleSpec& sample_spec, + const char* path) { + roc_panic_if(!path); + + bool is_explicit = false; + + for (size_t idx = 0; idx < ROC_ARRAY_SIZE(sndfile_explicit_formats); idx++) { + if (file_info.format == sndfile_explicit_formats[idx]) { + is_explicit = true; + break; + } + } + + if (is_explicit) { + if (!sample_spec.has_subformat() || !sample_spec.has_sample_rate() + || !sample_spec.has_channel_set()) { + roc_log(LogError, + "sndfile backend: invalid io encoding: <subformat>, <rate> and" + " <channels> required for input file when <format> is '%s'", + sample_spec.format_name()); + return status::StatusBadConfig; + } + } else { + if (sample_spec.has_subformat() || sample_spec.has_sample_rate() + || sample_spec.has_channel_set()) { + roc_log(LogError, + "sndfile backend: invalid io encoding: <subformat>, <rate> and" + " <channels> not allowed for input file when <format> is '%s'", + sample_spec.format_name()); + return status::StatusBadConfig; + } + } + + return status::StatusOK; +} + +status::StatusCode sndfile_detect_format(const SF_INFO& file_info, + audio::SampleSpec& sample_spec) { + if (!sample_spec.has_format()) { + // First check sndfile_driver_remap. + for (size_t idx = 0; idx < ROC_ARRAY_SIZE(sndfile_format_remap); idx++) { + if ((file_info.format & sndfile_format_remap[idx].format_mask) + == sndfile_format_remap[idx].format_mask) { + if (!sample_spec.set_custom_format(sndfile_format_remap[idx].name)) { + continue; + } + break; + } + } + + // Then check rest major formats. + int major_format_count = 0; + if (int errnum = sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &major_format_count, + sizeof(int))) { + roc_panic( + "sndfile backend: sf_command(SFC_GET_FORMAT_MAJOR_COUNT) failed: %s", + sf_error_number(errnum)); + } + + for (int idx = 0; idx < major_format_count; idx++) { + SF_FORMAT_INFO format_info; + memset(&format_info, 0, sizeof(format_info)); + format_info.format = idx; + if (int errnum = sf_command(NULL, SFC_GET_FORMAT_MAJOR, &format_info, + sizeof(format_info))) { + roc_panic("sndfile backend: sf_command(SFC_GET_FORMAT_MAJOR) failed: %s", + sf_error_number(errnum)); + } + + if ((file_info.format & format_info.format) == format_info.format) { + if (!sample_spec.set_custom_format(format_info.extension)) { + continue; + } + break; + } + } + + if (!sample_spec.has_format()) { + roc_log(LogError, "sndfile backend: can't detect file format"); + return status::StatusErrFile; + } + } + + if (!sample_spec.has_subformat()) { + // First check pcm sub-formats. + for (int subfmt = audio::PcmSubformat_Invalid; subfmt < audio::PcmSubformat_Max; + subfmt++) { + const int subfmt_mask = + pcm_2_sf((audio::PcmSubformat)subfmt) & SF_FORMAT_SUBMASK; + + if ((file_info.format & subfmt_mask) == subfmt_mask) { + sample_spec.set_pcm_subformat((audio::PcmSubformat)subfmt); + break; + } + } + + // Then check rest sub-formats. + for (size_t idx = 0; idx < ROC_ARRAY_SIZE(sndfile_subformat_map); idx++) { + if ((file_info.format & sndfile_subformat_map[idx].format_mask) + == sndfile_subformat_map[idx].format_mask) { + if (!sample_spec.set_custom_subformat(sndfile_subformat_map[idx].name)) { + continue; + } + break; + } + } + } + + return status::StatusOK; +} + +} // namespace sndio +} // namespace roc diff --git a/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_helpers.h b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_helpers.h new file mode 100644 index 000000000..50cc1efb5 --- /dev/null +++ b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_helpers.h @@ -0,0 +1,43 @@ +/* + * 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_helpers.h +//! @brief Sndfile helpers. + +#ifndef ROC_SNDIO_SNDFILE_HELPERS_H_ +#define ROC_SNDIO_SNDFILE_HELPERS_H_ + +#include "roc_audio/sample_spec.h" +#include "roc_core/attributes.h" +#include "roc_status/status_code.h" + +#include <sndfile.h> + +namespace roc { +namespace sndio { + +//! Choose sndfile major format from sample spec and path. +ROC_ATTR_NODISCARD status::StatusCode sndfile_select_major_format( + SF_INFO& file_info, audio::SampleSpec& sample_spec, const char* path); + +//! Choose sndfile sub-format from sample spec and path. +ROC_ATTR_NODISCARD status::StatusCode sndfile_select_sub_format( + SF_INFO& file_info, audio::SampleSpec& sample_spec, const char* path); + +//! Check that requested specification is valid for given input file. +ROC_ATTR_NODISCARD status::StatusCode sndfile_check_input_spec( + const SF_INFO& file_info, const audio::SampleSpec& sample_spec, const char* path); + +//! Detect format and sub-format of opened file and fill sample spec. +ROC_ATTR_NODISCARD status::StatusCode +sndfile_detect_format(const SF_INFO& file_info, audio::SampleSpec& sample_spec); + +} // namespace sndio +} // namespace roc + +#endif // ROC_SNDIO_SNDFILE_HELPERS_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 edf0ab868..65c90d6a3 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,252 +11,32 @@ #include "roc_core/log.h" #include "roc_core/panic.h" #include "roc_sndio/backend_map.h" +#include "roc_sndio/sndfile_helpers.h" #include "roc_sndio/sndfile_tables.h" #include "roc_status/code_to_str.h" namespace roc { namespace sndio { -namespace { - -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 0; -} - -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) { - file_extension = dot; - } - - int format_mask = 0; - - // 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 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_mask = sndfile_driver_remap[idx].format_mask; - *driver = file_extension; - break; - } - } - } - } - - // Then try to select format by iterating through all sndfile formats. - 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( - "sndfile backend: sf_command(SFC_GET_FORMAT_MAJOR_COUNT) failed: %s", - sf_error_number(errnum)); - } - - 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))) { - roc_panic("sndfile backend: sf_command(SFC_GET_FORMAT_MAJOR) failed: %s", - sf_error_number(errnum)); - } - - 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_mask = info.format; - *driver = file_extension; - break; - } - } - } - } - - if (format_mask == 0) { - roc_log(LogDebug, - "sndfile sink: failed to detect major format: driver=%s path=%s", *driver, - path); - return false; - } - - file_info.format = format_mask; - - return true; -} - -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; - } - - // 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 - SndfileSink::SndfileSink(audio::FrameFactory& frame_factory, core::IArena& arena, - const IoConfig& io_config) + const IoConfig& io_config, + const char* path) : IDevice(arena) , ISink(arena) , file_(NULL) - , requested_subformat_(0) , init_status_(status::NoStatus) { - if (io_config.latency != 0) { - roc_log(LogError, "sndfile sink: setting io latency not supported by backend"); - init_status_ = status::StatusBadConfig; - return; - } + file_spec_ = io_config.sample_spec; + file_spec_.use_defaults(audio::Format_Invalid, audio::PcmSubformat_Invalid, + audio::ChanLayout_Surround, audio::ChanOrder_Smpte, + audio::ChanMask_Surround_Stereo, 44100); - if (io_config.sample_spec.sample_format() != audio::SampleFormat_Invalid - && io_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; - } + memset(&file_info_, 0, sizeof(file_info_)); - if (io_config.sample_spec.is_pcm()) { - // Remember which format to use for file, if requested explicitly. - requested_subformat_ = pcm_2_sf(io_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(io_config.sample_spec).c_str()); - init_status_ = status::StatusBadConfig; - return; - } + if ((init_status_ = open_(path)) != status::StatusOK) { + return; } - sample_spec_ = io_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); - - memset(&file_info_, 0, sizeof(file_info_)); - init_status_ = status::StatusOK; } @@ -272,20 +52,6 @@ status::StatusCode SndfileSink::init_status() const { return init_status_; } -status::StatusCode SndfileSink::open(const char* driver, const char* path) { - roc_log(LogDebug, "sndfile sink: opening: driver=%s path=%s", driver, path); - - if (file_) { - roc_panic("sndfile sink: can't call open() more than once"); - } - - if (!path) { - roc_panic("sndfile sink: path is null"); - } - - return open_(driver, path); -} - DeviceType SndfileSink::type() const { return DeviceType_Sink; } @@ -303,7 +69,11 @@ audio::SampleSpec SndfileSink::sample_spec() const { roc_panic("sndfile sink: not opened"); } - return sample_spec_; + return frame_spec_; +} + +core::nanoseconds_t SndfileSink::frame_length() const { + return 0; } bool SndfileSink::has_state() const { @@ -323,6 +93,8 @@ status::StatusCode SndfileSink::write(audio::Frame& frame) { roc_panic("sndfile sink: not opened"); } + frame_spec_.validate_frame(frame); + audio::sample_t* frame_data = frame.raw_samples(); sf_count_t frame_size = (sf_count_t)frame.num_raw_samples(); @@ -355,37 +127,52 @@ void SndfileSink::dispose() { arena().dispose_object(*this); } -status::StatusCode SndfileSink::open_(const char* driver, const char* path) { - file_info_.channels = (int)sample_spec_.num_channels(); - file_info_.samplerate = (int)sample_spec_.sample_rate(); +status::StatusCode SndfileSink::open_(const char* path) { + roc_log(LogDebug, "sndfile sink: opening: path=%s", path); - 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; + file_info_.samplerate = (int)file_spec_.sample_rate(); + file_info_.channels = (int)file_spec_.num_channels(); + + status::StatusCode code = status::NoStatus; + + if ((code = sndfile_select_major_format(file_info_, file_spec_, path)) + != status::StatusOK) { + return code; } - 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; + if ((code = sndfile_select_sub_format(file_info_, file_spec_, path)) + != status::StatusOK) { + return code; } - file_ = sf_open(path, SFM_WRITE, &file_info_); - if (!file_) { - roc_log(LogDebug, "sndfile sink: %s, can't open file: driver=%s path=%s", - sf_strerror(file_), driver, path); + if (!(file_ = sf_open(path, SFM_WRITE, &file_info_))) { + roc_log(LogError, "sndfile sink: can't open output file: %s", + sf_error_number(sf_error(NULL))); return status::StatusErrFile; } 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); + roc_log(LogError, "sndfile sink: can't open output file: %s", sf_strerror(file_)); return status::StatusErrFile; } + if (!file_spec_.has_format() || !file_spec_.has_subformat()) { + if ((code = sndfile_detect_format(file_info_, file_spec_)) != status::StatusOK) { + return code; + } + } + + file_spec_.set_sample_rate((size_t)file_info_.samplerate); + file_spec_.channel_set().set_layout(audio::ChanLayout_Surround); + file_spec_.channel_set().set_order(audio::ChanOrder_Smpte); + file_spec_.channel_set().set_count((size_t)file_info_.channels); + + frame_spec_ = file_spec_; + frame_spec_.set_format(audio::Format_Pcm); + frame_spec_.set_pcm_subformat(audio::PcmSubformat_Raw); + roc_log(LogInfo, "sndfile sink: opened output file: %s", - audio::sample_spec_to_str(sample_spec_).c_str()); + audio::sample_spec_to_str(file_spec_).c_str()); return status::StatusOK; } 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 46efdefb9..18ca1150c 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 @@ -12,8 +12,6 @@ #ifndef ROC_SNDIO_SNDFILE_SINK_H_ #define ROC_SNDIO_SNDFILE_SINK_H_ -#include <sndfile.h> - #include "roc_audio/frame_factory.h" #include "roc_audio/sample_spec.h" #include "roc_core/iarena.h" @@ -21,6 +19,8 @@ #include "roc_sndio/io_config.h" #include "roc_sndio/isink.h" +#include <sndfile.h> + namespace roc { namespace sndio { @@ -33,15 +33,13 @@ class SndfileSink : public ISink, public core::NonCopyable<> { //! Initialize. SndfileSink(audio::FrameFactory& frame_factory, core::IArena& arena, - const IoConfig& io_config); + const IoConfig& io_config, + const char* path); ~SndfileSink(); //! Check if the object was successfully constructed. status::StatusCode init_status() const; - //! Open sink. - ROC_ATTR_NODISCARD status::StatusCode open(const char* driver, const char* path); - //! Get device type. virtual DeviceType type() const; @@ -54,6 +52,9 @@ class SndfileSink : public ISink, public core::NonCopyable<> { //! Get sample specification of the sink. virtual audio::SampleSpec sample_spec() const; + //! Get recommended frame length of the sink. + virtual core::nanoseconds_t frame_length() const; + //! Check if the sink supports state updates. virtual bool has_state() const; @@ -76,14 +77,14 @@ class SndfileSink : public ISink, public core::NonCopyable<> { virtual void dispose(); private: - status::StatusCode open_(const char* driver, const char* path); + status::StatusCode open_(const char* path); status::StatusCode close_(); SNDFILE* file_; SF_INFO file_info_; - audio::SampleSpec sample_spec_; - int requested_subformat_; + audio::SampleSpec frame_spec_; + audio::SampleSpec file_spec_; 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 99166300b..4df5a0ba0 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 @@ -11,6 +11,7 @@ #include "roc_core/log.h" #include "roc_core/panic.h" #include "roc_sndio/backend_map.h" +#include "roc_sndio/sndfile_helpers.h" #include "roc_status/code_to_str.h" namespace roc { @@ -18,27 +19,28 @@ namespace sndio { SndfileSource::SndfileSource(audio::FrameFactory& frame_factory, core::IArena& arena, - const IoConfig& io_config) + const IoConfig& io_config, + const char* path) : IDevice(arena) , ISource(arena) , frame_factory_(frame_factory) , file_(NULL) , path_(arena) , init_status_(status::NoStatus) { - if (io_config.latency != 0) { - roc_log(LogError, "sndfile source: setting io latency not supported by backend"); - init_status_ = status::StatusBadConfig; + file_spec_ = io_config.sample_spec; + + memset(&file_info_, 0, sizeof(file_info_)); + + if (!path_.assign(path)) { + roc_log(LogError, "sndfile source: can't allocate string"); + init_status_ = status::StatusNoMem; return; } - if (!io_config.sample_spec.is_empty()) { - roc_log(LogError, "sndfile source: setting io encoding not supported by backend"); - init_status_ = status::StatusBadConfig; + if ((init_status_ = open_()) != status::StatusOK) { return; } - memset(&file_info_, 0, sizeof(file_info_)); - init_status_ = status::StatusOK; } @@ -54,25 +56,6 @@ status::StatusCode SndfileSource::init_status() const { return init_status_; } -status::StatusCode SndfileSource::open(const char* driver, const char* path) { - roc_log(LogDebug, "sndfile source: opening: driver=%s path=%s", driver, path); - - if (file_) { - roc_panic("sndfile source: can't call open() more than once"); - } - - if (!path) { - roc_panic("sndfile sink: path is null"); - } - - if (!path_.assign(path)) { - roc_log(LogError, "sndfile source: can't allocate string"); - return status::StatusNoMem; - } - - return open_(); -} - DeviceType SndfileSource::type() const { return DeviceType_Source; } @@ -90,7 +73,11 @@ audio::SampleSpec SndfileSource::sample_spec() const { roc_panic("sndfile source: not opened"); } - return sample_spec_; + return frame_spec_; +} + +core::nanoseconds_t SndfileSource::frame_length() const { + return 0; } bool SndfileSource::has_state() const { @@ -134,7 +121,7 @@ status::StatusCode SndfileSource::read(audio::Frame& frame, } if (!frame_factory_.reallocate_frame( - frame, sample_spec_.stream_timestamp_2_bytes(duration))) { + frame, frame_spec_.stream_timestamp_2_bytes(duration))) { return status::StatusNoMem; } @@ -151,11 +138,12 @@ status::StatusCode SndfileSource::read(audio::Frame& frame, } if (n_samples == 0) { + roc_log(LogDebug, "sndfile source: got eof from input file"); return status::StatusFinish; } frame.set_num_raw_samples((size_t)n_samples); - frame.set_duration((size_t)n_samples / sample_spec_.num_channels()); + frame.set_duration((size_t)n_samples / frame_spec_.num_channels()); if (frame.duration() < duration) { return status::StatusPart; @@ -189,28 +177,69 @@ status::StatusCode SndfileSource::seek_(size_t offset) { } status::StatusCode SndfileSource::open_() { - if (file_) { - roc_panic("sndfile source: can't open: already opened"); + roc_log(LogDebug, "sndfile source: opening: path=%s", path_.c_str()); + + file_info_.samplerate = (int)file_spec_.sample_rate(); + file_info_.channels = (int)file_spec_.num_channels(); + + status::StatusCode code = status::NoStatus; + + if (file_spec_.has_format()) { + if ((code = sndfile_select_major_format(file_info_, file_spec_, path_.c_str())) + != status::StatusOK) { + return code; + } } - file_info_.format = 0; + if ((code = sndfile_check_input_spec(file_info_, file_spec_, path_.c_str())) + != status::StatusOK) { + return code; + } - file_ = sf_open(path_.c_str(), SFM_READ, &file_info_); - if (!file_) { - roc_log(LogInfo, "sndfile source: can't open: input=%s, %s", path_.c_str(), - sf_strerror(file_)); + if (file_spec_.has_subformat()) { + if ((code = sndfile_select_sub_format(file_info_, file_spec_, path_.c_str())) + != status::StatusOK) { + return code; + } + } + + const int requested_format = file_info_.format; + + if (!(file_ = sf_open(path_.c_str(), SFM_READ, &file_info_))) { + const int err = sf_error(NULL); + if (err == SF_ERR_UNRECOGNISED_FORMAT || err == SF_ERR_UNSUPPORTED_ENCODING) { + roc_log(LogDebug, "sndfile source: can't recognize input file format"); + return status::StatusNoFormat; + } + roc_log(LogError, "sndfile source: can't open input file: %s", + sf_error_number(err)); return status::StatusErrFile; } - sample_spec_.set_sample_format(audio::SampleFormat_Pcm); - sample_spec_.set_pcm_format(audio::Sample_RawFormat); - sample_spec_.set_sample_rate((size_t)file_info_.samplerate); - sample_spec_.channel_set().set_layout(audio::ChanLayout_Surround); - sample_spec_.channel_set().set_order(audio::ChanOrder_Smpte); - sample_spec_.channel_set().set_count((size_t)file_info_.channels); + if ((file_info_.format & requested_format) != requested_format) { + roc_log(LogError, + "sndfile source: input file doesn't match requested format '%s'", + file_spec_.format_name()); + return status::StatusErrFile; + } + + if (!file_spec_.has_format() || !file_spec_.has_subformat()) { + if ((code = sndfile_detect_format(file_info_, file_spec_)) != status::StatusOK) { + return code; + } + } + + file_spec_.set_sample_rate((size_t)file_info_.samplerate); + file_spec_.channel_set().set_layout(audio::ChanLayout_Surround); + file_spec_.channel_set().set_order(audio::ChanOrder_Smpte); + file_spec_.channel_set().set_count((size_t)file_info_.channels); + + frame_spec_ = file_spec_; + frame_spec_.set_format(audio::Format_Pcm); + frame_spec_.set_pcm_subformat(audio::PcmSubformat_Raw); roc_log(LogInfo, "sndfile source: opened input file: %s", - audio::sample_spec_to_str(sample_spec_).c_str()); + audio::sample_spec_to_str(file_spec_).c_str()); return status::StatusOK; } diff --git a/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_source.h b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_source.h index e84aa9b61..8652a2d92 100644 --- a/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_source.h +++ b/src/internal_modules/roc_sndio/target_sndfile/roc_sndio/sndfile_source.h @@ -12,8 +12,6 @@ #ifndef ROC_SNDIO_SNDFILE_SOURCE_H_ #define ROC_SNDIO_SNDFILE_SOURCE_H_ -#include <sndfile.h> - #include "roc_audio/frame_factory.h" #include "roc_audio/sample_spec.h" #include "roc_core/iarena.h" @@ -22,6 +20,8 @@ #include "roc_sndio/io_config.h" #include "roc_sndio/isource.h" +#include <sndfile.h> + namespace roc { namespace sndio { @@ -34,15 +34,13 @@ class SndfileSource : public ISource, private core::NonCopyable<> { //! Initialize. SndfileSource(audio::FrameFactory& frame_factory, core::IArena& arena, - const IoConfig& io_config); + const IoConfig& io_config, + const char* path); ~SndfileSource(); //! Check if the object was successfully constructed. status::StatusCode init_status() const; - //! Open source. - ROC_ATTR_NODISCARD status::StatusCode open(const char* driver, const char* path); - //! Get device type. virtual DeviceType type() const; @@ -55,6 +53,9 @@ class SndfileSource : public ISource, private core::NonCopyable<> { //! Get sample specification of the source. virtual audio::SampleSpec sample_spec() const; + //! Get recommended frame length of the source. + virtual core::nanoseconds_t frame_length() const; + //! Check if the source supports state updates. virtual bool has_state() const; @@ -90,7 +91,8 @@ class SndfileSource : public ISource, private core::NonCopyable<> { audio::FrameFactory& frame_factory_; - audio::SampleSpec sample_spec_; + audio::SampleSpec frame_spec_; + audio::SampleSpec file_spec_; SNDFILE* file_; SF_INFO file_info_; 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 index 1dbf71cf8..753b07b50 100644 --- 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 @@ -33,7 +33,8 @@ enum { } // namespace -SndfileDriverInfo sndfile_driver_remap[ROC_ARRAY_SIZE(sndfile_driver_remap)] = { +SndfileFormatInfo sndfile_format_remap[ROC_ARRAY_SIZE(sndfile_format_remap)] = { + { "pcm", NULL, SF_FORMAT_RAW }, { "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 }, @@ -44,8 +45,36 @@ SndfileDriverInfo sndfile_driver_remap[ROC_ARRAY_SIZE(sndfile_driver_remap)] = { { "wavex", NULL, SF_FORMAT_WAVEX }, }; +SndfileSubformatInfo sndfile_subformat_map[ROC_ARRAY_SIZE(sndfile_subformat_map)] = { + // lpcm + { "lpcm", "ulaw", SF_FORMAT_ULAW }, + { "lpcm", "alaw", SF_FORMAT_ALAW }, + // dpcm + { "dpcm", "d8", SF_FORMAT_DPCM_8 }, + { "dpcm", "d16", SF_FORMAT_DPCM_16 }, + // adpcm + { "adpcm", "adpcm_ima", SF_FORMAT_IMA_ADPCM }, + { "adpcm", "adpcm_ms", SF_FORMAT_MS_ADPCM }, + { "adpcm", "adpcm_vox", SF_FORMAT_VOX_ADPCM }, + // dwvw + { "dwvw", "dwvw12", SF_FORMAT_DWVW_12 }, + { "dwvw", "dwvw16", SF_FORMAT_DWVW_16 }, + { "dwvw", "dwvw24", SF_FORMAT_DWVW_24 }, + // g72x + { "g72x", "g721_32", SF_FORMAT_G721_32 }, + { "g72x", "g723_24", SF_FORMAT_G723_24 }, + { "g72x", "g723_40", SF_FORMAT_G723_40 }, + // ogg + { "ogg", "vorbis", SF_FORMAT_VORBIS }, + { "ogg", "opus", SF_FORMAT_OPUS }, +}; + +int sndfile_explicit_formats[ROC_ARRAY_SIZE(sndfile_explicit_formats)] = { + SF_FORMAT_RAW, // pcm +}; + 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 + // most major formats supports at least one PCM or DPCM sub-format SF_FORMAT_PCM_24, SF_FORMAT_PCM_16, SF_FORMAT_DPCM_16, 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 index 266268025..b7704f71e 100644 --- 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 @@ -15,22 +15,40 @@ namespace roc { namespace sndio { -//! Sndfile driver meta-data. -struct SndfileDriverInfo { - //! Name of the driver. - const char* driver_name; - //! File extension associated with driver. +//! Sndfile format meta-data. +struct SndfileFormatInfo { + //! Name of the format. + const char* name; + //! File extension associated with the format. 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]; +//! Sndfile driver meta-data. +struct SndfileSubformatInfo { + //! Name of sub-format group. + const char* group; + //! Name of sub-format. + const char* name; + //! SF_FORMAT corresponding to the sub-format. + int format_mask; +}; + +//! Table of sndfile formats with re-mapped names or file extensions. +//! This table is checked when user explicitly specifies format name, +//! or we're trying to guess format from file extension. +extern SndfileFormatInfo sndfile_format_remap[9]; + +//! Table of sndfile sub-formats with mapped string names and divided into groups. +//! This table is checked when user explicitly specifies sub-format name. +extern SndfileSubformatInfo sndfile_subformat_map[15]; + +//! Table of sndfile formats which require explicitly providing sub-format, +//! rate, and channels. +extern int sndfile_explicit_formats[1]; -//! Table of sndfile sub-formats to try when no specific format requested. +//! Table of sndfile sub-formats to try when no specific sub-format requested. //! This list provides the minimum number of sub-formats needed to support //! all possible major formats. extern int sndfile_default_subformats[7]; diff --git a/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_backend.cpp b/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_backend.cpp index 804f6a4ec..4fd248e70 100644 --- a/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_backend.cpp +++ b/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_backend.cpp @@ -31,117 +31,80 @@ const char* default_drivers[] = { }; const char* driver_renames[][2] = { + // device drivers { "waveaudio", "wave" }, { "coreaudio", "core" }, + // file formats + { "anb", "amr" }, }; const char* hidden_drivers[] = { - // raw format aliases - "f4", - "f8", - "s1", - "s2", - "s3", - "s4", - "u1", - "u2", - "u3", - "u4", - "sb", - "sw", - "sl", - "ub", - "uw", - "f32", - "f64", - "s8", - "s16", - "s24", - "s32", - "u8", - "u16", - "u24", - "u32", - // formats already handled by libsndfile - "aif", - "aifc", - "aiff", - "aiffc", - "mat", - "mat4", - "mat5", - "vorbis", - // pseudo-formats - "sndfile", - "null", - "wavpcm", - // unsupported device drivers "ao", "ossdsp", "pulseaudio", }; -bool is_default_driver(const char* driver) { - for (size_t n = 0; n < ROC_ARRAY_SIZE(default_drivers); n++) { - if (strcmp(driver, default_drivers[n]) == 0) { - return true; - } - } - - return false; -} - -const char* map_to_sox_driver(const char* driver) { - if (!driver) { +const char* driver_to_sox(const char* name) { + if (!name) { return NULL; } for (size_t n = 0; n < ROC_ARRAY_SIZE(driver_renames); n++) { - if (strcmp(driver_renames[n][1], driver) == 0) { + if (strcmp(driver_renames[n][1], name) == 0) { return driver_renames[n][0]; } } - return driver; + return name; } -const char* map_from_sox_driver(const char* driver) { - if (!driver) { +const char* driver_from_sox(const char* name) { + if (!name) { return NULL; } for (size_t n = 0; n < ROC_ARRAY_SIZE(driver_renames); n++) { - if (strcmp(driver_renames[n][0], driver) == 0) { + if (strcmp(driver_renames[n][0], name) == 0) { return driver_renames[n][1]; } } - return driver; + return name; } -bool is_driver_hidden(const char* driver) { - // replicate the behavior of display_supported_formats() from sox.c - if (strchr(driver, '/')) { - return true; - } - for (size_t n = 0; n < ROC_ARRAY_SIZE(hidden_drivers); n++) { - if (strcmp(hidden_drivers[n], driver) == 0) { +bool is_default_driver(const char* name) { + for (size_t n = 0; n < ROC_ARRAY_SIZE(default_drivers); n++) { + if (strcmp(name, default_drivers[n]) == 0) { return true; } } return false; } -bool check_handler_type(const sox_format_handler_t* handler, DriverType driver_type) { - if (handler->flags & SOX_FILE_DEVICE) { - if (handler->flags & SOX_FILE_PHONY) { - return false; - } - if (driver_type != DriverType_Device) { - return false; - } - } else { - if (driver_type != DriverType_File) { +bool is_supported_driver(const char* name) { + const sox_format_handler_t* format_handler = sox_write_handler(NULL, name, NULL); + if (format_handler == NULL) { + // not enabled in sox + return false; + } + if (!(format_handler->flags & SOX_FILE_DEVICE)) { + // not device + return false; + } + if (format_handler->flags & SOX_FILE_PHONY) { + // phony device + return false; + } + + if (strchr(name, '/')) { + // replicate the behavior of display_supported_formats() from sox.c + return false; + } + + for (size_t n = 0; n < ROC_ARRAY_SIZE(hidden_drivers); n++) { + // hidden by us + if (strcmp(hidden_drivers[n], name) == 0) { return false; } } + // supported! return true; } @@ -176,8 +139,7 @@ void log_handler(unsigned sox_level, } // namespace -SoxBackend::SoxBackend() - : first_created_(false) { +SoxBackend::SoxBackend() { sox_init(); sox_get_globals()->verbosity = 100; @@ -188,129 +150,120 @@ const char* SoxBackend::name() const { return "sox"; } -void SoxBackend::discover_drivers(core::Array<DriverInfo, MaxDrivers>& driver_list) { +bool SoxBackend::discover_drivers(core::Array<DriverInfo, MaxDrivers>& result) { for (size_t n = 0; n < ROC_ARRAY_SIZE(default_drivers); n++) { - const sox_format_handler_t* handler = - sox_write_handler(NULL, default_drivers[n], NULL); - if (!handler) { + const char* driver = default_drivers[n]; + if (!is_supported_driver(driver)) { continue; } - const char* driver = map_from_sox_driver(default_drivers[n]); - - if (!driver_list.push_back(DriverInfo(driver, DriverType_Device, - DriverFlag_IsDefault - | DriverFlag_SupportsSource - | DriverFlag_SupportsSink, - this))) { - roc_panic("sox backend: can't add driver"); + if (!result.push_back(DriverInfo(driver_from_sox(driver), + Driver_Device | Driver_DefaultDevice + | Driver_SupportsSource + | Driver_SupportsSink, + this))) { + return false; } } const sox_format_tab_t* formats = sox_get_format_fns(); - for (size_t n = 0; formats[n].fn; n++) { - sox_format_handler_t const* handler = formats[n].fn(); - + sox_format_handler_t const* format_handler = formats[n].fn(); char const* const* format_names; - for (format_names = handler->names; *format_names; ++format_names) { - const char* driver = map_from_sox_driver(*format_names); - if (is_driver_hidden(driver) || is_default_driver(driver)) { + for (format_names = format_handler->names; *format_names; ++format_names) { + const char* driver = *format_names; + if (!is_supported_driver(driver) || is_default_driver(driver)) { continue; } - if (!driver_list.push_back(DriverInfo( - driver, - (handler->flags & SOX_FILE_DEVICE) ? DriverType_Device - : DriverType_File, - DriverFlag_SupportsSource | DriverFlag_SupportsSink, this))) { - roc_panic("sox backend: can't add driver"); + if (!result.push_back(DriverInfo( + driver_from_sox(driver), + Driver_Device | Driver_SupportsSource | Driver_SupportsSink, this))) { + return false; } } } + + return true; +} + +bool SoxBackend::discover_formats(core::Array<FormatInfo, MaxFormats>& result) { + // no formats except pcm + return true; +} + +bool SoxBackend::discover_subformat_groups(core::StringList& result) { + // no sub-formats except pcm + return true; +} + +bool SoxBackend::discover_subformats(const char* group, core::StringList& result) { + // no sub-formats except pcm + return true; } status::StatusCode SoxBackend::open_device(DeviceType device_type, - DriverType driver_type, const char* driver, const char* path, const IoConfig& io_config, audio::FrameFactory& frame_factory, core::IArena& arena, IDevice** result) { - first_created_ = true; - - driver = map_to_sox_driver(driver); - - if (driver && is_driver_hidden(driver)) { - roc_log(LogDebug, "sox backend: driver is not supported: driver=%s path=%s", - driver, path); - return status::StatusNoDriver; - } - - const sox_format_handler_t* handler = sox_write_handler(path, driver, NULL); - if (!handler) { - roc_log(LogDebug, "sox backend: driver is not available: driver=%s path=%s", - driver, path); - return status::StatusNoDriver; - } - - if (!check_handler_type(handler, driver_type)) { - roc_log(LogDebug, "sox backend: mismatching driver type: driver=%s path=%s", - driver, path); - return status::StatusNoDriver; + roc_panic_if(!driver); + roc_panic_if(!path); + + if (driver) { + driver = driver_to_sox(driver); + + if (!is_supported_driver(driver)) { + roc_log(LogDebug, + "sox backend sink: requested driver not supported by backend:" + " driver=%s path=%s", + driver, path); + // Try another backend. + return status::StatusNoDriver; + } } switch (device_type) { case DeviceType_Sink: { core::ScopedPtr<SoxSink> sink( - new (arena) SoxSink(frame_factory, arena, io_config, driver_type)); + new (arena) SoxSink(frame_factory, arena, io_config, driver, path)); if (!sink) { - roc_log(LogDebug, "sox backend: can't allocate sink: path=%s", path); + roc_log(LogDebug, "sox backend: can't allocate sink: driver=%s path=%s", + driver, path); return status::StatusNoMem; } if (sink->init_status() != status::StatusOK) { - roc_log(LogDebug, "sox backend: can't initialize sink: path=%s status=%s", - path, status::code_to_str(sink->init_status())); + roc_log(LogDebug, "sox backend: can't open sink: driver=%s path=%s status=%s", + driver, path, status::code_to_str(sink->init_status())); return sink->init_status(); } - const status::StatusCode code = sink->open(driver, path); - if (code != status::StatusOK) { - roc_log(LogDebug, "sox backend: can't open sink: path=%s status=%s", path, - status::code_to_str(code)); - return code; - } - *result = sink.hijack(); return status::StatusOK; } break; case DeviceType_Source: { core::ScopedPtr<SoxSource> source( - new (arena) SoxSource(frame_factory, arena, io_config, driver_type)); + new (arena) SoxSource(frame_factory, arena, io_config, driver, path)); if (!source) { - roc_log(LogDebug, "sox backend: can't allocate source: path=%s", path); + roc_log(LogDebug, "sox backend: can't allocate source: driver=%s path=%s", + driver, path); return status::StatusNoMem; } if (source->init_status() != status::StatusOK) { - roc_log(LogDebug, "sox backend: can't initialize source: path=%s status=%s", + roc_log(LogDebug, + "sox backend: can't open source: driver=%s path=%s status=%s", driver, path, status::code_to_str(source->init_status())); return source->init_status(); } - const status::StatusCode code = source->open(driver, path); - if (code != status::StatusOK) { - roc_log(LogDebug, "sox backend: can't open source: path=%s status=%s", path, - status::code_to_str(code)); - return code; - } - *result = source.hijack(); return status::StatusOK; } break; diff --git a/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_backend.h b/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_backend.h index 0b2442bfa..c939b57a1 100644 --- a/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_backend.h +++ b/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_backend.h @@ -29,21 +29,29 @@ class SoxBackend : public IBackend, core::NonCopyable<> { virtual const char* name() const; //! Append supported drivers to the list. - virtual void discover_drivers(core::Array<DriverInfo, MaxDrivers>& driver_list); + virtual ROC_ATTR_NODISCARD bool + discover_drivers(core::Array<DriverInfo, MaxDrivers>& result); + + //! Append supported formats to the list. + virtual ROC_ATTR_NODISCARD bool + discover_formats(core::Array<FormatInfo, MaxFormats>& result); + + //! Append supported groups of sub-formats to the list. + virtual ROC_ATTR_NODISCARD bool discover_subformat_groups(core::StringList& result); + + //! Append supported sub-formats of a group to the list. + virtual ROC_ATTR_NODISCARD bool discover_subformats(const char* group, + core::StringList& result); //! Create and open a sink or source. virtual ROC_ATTR_NODISCARD status::StatusCode open_device(DeviceType device_type, - DriverType driver_type, const char* driver, const char* path, const IoConfig& io_config, audio::FrameFactory& frame_factory, core::IArena& arena, IDevice** result); - -private: - bool first_created_; }; } // namespace sndio diff --git a/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_sink.cpp b/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_sink.cpp index ad1562617..126ee2632 100644 --- a/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_sink.cpp +++ b/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_sink.cpp @@ -7,6 +7,7 @@ */ #include "roc_sndio/sox_sink.h" +#include "roc_audio/sample_spec_to_str.h" #include "roc_core/log.h" #include "roc_core/panic.h" #include "roc_sndio/backend_map.h" @@ -15,15 +16,21 @@ namespace roc { namespace sndio { +namespace { + +const core::nanoseconds_t DefaultFrameLength = 10 * core::Millisecond; + +} // namespace + SoxSink::SoxSink(audio::FrameFactory& frame_factory, core::IArena& arena, const IoConfig& io_config, - DriverType driver_type) + const char* driver, + const char* path) : IDevice(arena) , ISink(arena) - , driver_type_(driver_type) - , driver_name_(arena) - , output_name_(arena) + , driver_(arena) + , path_(arena) , output_(NULL) , buffer_(arena) , buffer_size_(0) @@ -32,51 +39,89 @@ SoxSink::SoxSink(audio::FrameFactory& frame_factory, BackendMap::instance(); if (io_config.latency != 0) { - roc_log(LogError, "sox sink: setting io latency not supported by sox backend"); + roc_log(LogError, "sox sink: setting io latency not implemented for sox backend"); init_status_ = status::StatusBadConfig; return; } - sample_spec_ = io_config.sample_spec; + if (io_config.sample_spec.has_format() + && io_config.sample_spec.format() != audio::Format_Pcm) { + roc_log(LogError, + "sox sink: invalid io encoding:" + " <format> '%s' not supported by backend: spec=%s", + io_config.sample_spec.format_name(), + audio::sample_spec_to_str(io_config.sample_spec).c_str()); + init_status_ = status::StatusBadConfig; + return; + } - if (driver_type_ == DriverType_File) { - sample_spec_.use_defaults(audio::Sample_RawFormat, audio::ChanLayout_Surround, - audio::ChanOrder_Smpte, audio::ChanMask_Surround_Stereo, - 44100); - } else { - sample_spec_.use_defaults(audio::Sample_RawFormat, audio::ChanLayout_Surround, - audio::ChanOrder_Smpte, audio::ChanMask_Surround_Stereo, - 0); + if (io_config.sample_spec.has_subformat()) { + if (io_config.sample_spec.pcm_subformat() == audio::PcmSubformat_Invalid) { + roc_log(LogError, + "sox sink: invalid io encoding:" + " <subformat> '%s' not supported by backend: spec=%s", + io_config.sample_spec.subformat_name(), + audio::sample_spec_to_str(io_config.sample_spec).c_str()); + init_status_ = status::StatusBadConfig; + return; + } + + const audio::PcmTraits subfmt = + audio::pcm_subformat_traits(io_config.sample_spec.pcm_subformat()); + + if (!subfmt.has_flags(audio::Pcm_IsInteger | audio::Pcm_IsSigned)) { + roc_log(LogError, + "sox sink: invalid io encoding:" + " <subformat> must be signed integer (like s16): spec=%s", + audio::sample_spec_to_str(io_config.sample_spec).c_str()); + init_status_ = status::StatusBadConfig; + return; + } + + if (!subfmt.has_flags(audio::Pcm_IsPacked | audio::Pcm_IsAligned)) { + roc_log(LogError, + "sox sink: invalid io encoding:" + " <subformat> must be packed (like s24, not s24_4) and byte-aligned" + " (like s16, not s18): spec=%s", + audio::sample_spec_to_str(io_config.sample_spec).c_str()); + init_status_ = status::StatusBadConfig; + return; + } + + if (io_config.sample_spec.pcm_subformat() != subfmt.default_variant) { + roc_log(LogError, + "sox sink: invalid io encoding:" + " <subformat> must be default-endian (like s16, not s16_le): spec=%s", + audio::sample_spec_to_str(io_config.sample_spec).c_str()); + init_status_ = status::StatusBadConfig; + return; + } } - if (!sample_spec_.is_raw()) { - roc_log(LogError, "sox sink: sample format can be only \"-\" or \"%s\"", - audio::pcm_format_to_str(audio::Sample_RawFormat)); - init_status_ = status::StatusBadConfig; - return; + out_spec_ = io_config.sample_spec; + if (!out_spec_.has_format()) { + out_spec_.set_format(audio::Format_Pcm); + out_spec_.set_pcm_subformat(audio::PcmSubformat_SInt16); } frame_length_ = io_config.frame_length; - if (frame_length_ == 0) { - roc_log(LogError, "sox sink: frame length is zero"); - init_status_ = status::StatusBadConfig; - return; + frame_length_ = DefaultFrameLength; } - { - audio::SampleSpec spec = sample_spec_; - spec.use_defaults(audio::Sample_RawFormat, audio::ChanLayout_Surround, - audio::ChanOrder_Smpte, audio::ChanMask_Surround_Stereo, 44100); + roc_log(LogDebug, "sox sink: opening: driver=%s path=%s", driver, path); - sox_get_globals()->bufsiz = - spec.ns_2_samples_overall(frame_length_) * sizeof(sox_sample_t); + if ((init_status_ = init_names_(driver, path)) != status::StatusOK) { + return; } - memset(&out_signal_, 0, sizeof(out_signal_)); - out_signal_.rate = (sox_rate_t)sample_spec_.sample_rate(); - out_signal_.channels = (unsigned)sample_spec_.num_channels(); - out_signal_.precision = SOX_SAMPLE_PRECISION; + if ((init_status_ = open_()) != status::StatusOK) { + return; + } + + if ((init_status_ = init_buffer_()) != status::StatusOK) { + return; + } init_status_ = status::StatusOK; } @@ -92,30 +137,6 @@ status::StatusCode SoxSink::init_status() const { return init_status_; } -status::StatusCode SoxSink::open(const char* driver, const char* path) { - roc_log(LogDebug, "sox sink: opening: driver=%s path=%s", driver, path); - - if (buffer_.size() != 0 || output_) { - roc_panic("sox sink: can't call open() more than once"); - } - - status::StatusCode code = status::NoStatus; - - if ((code = init_names_(driver, path)) != status::StatusOK) { - return code; - } - - if ((code = open_()) != status::StatusOK) { - return code; - } - - if ((code = init_buffer_()) != status::StatusOK) { - return code; - } - - return status::StatusOK; -} - DeviceType SoxSink::type() const { return DeviceType_Sink; } @@ -129,15 +150,15 @@ ISource* SoxSink::to_source() { } audio::SampleSpec SoxSink::sample_spec() const { - if (!output_) { - roc_panic("sox sink: not opened"); - } + return frame_spec_; +} - return sample_spec_; +core::nanoseconds_t SoxSink::frame_length() const { + return frame_length_; } bool SoxSink::has_state() const { - return driver_type_ == DriverType_Device; + return true; } DeviceState SoxSink::state() const { @@ -157,14 +178,12 @@ status::StatusCode SoxSink::pause() { roc_panic("sox sink: not opened"); } - roc_log(LogDebug, "sox sink: pausing: driver=%s output=%s", driver_name_.c_str(), - output_name_.c_str()); + roc_log(LogDebug, "sox sink: pausing: driver=%s path=%s", driver_.c_str(), + path_.c_str()); - if (driver_type_ == DriverType_Device) { - const status::StatusCode close_code = close_(); - if (close_code != status::StatusOK) { - return close_code; - } + const status::StatusCode close_code = close_(); + if (close_code != status::StatusOK) { + return close_code; } paused_ = true; @@ -177,8 +196,8 @@ status::StatusCode SoxSink::resume() { return status::StatusOK; } - roc_log(LogDebug, "sox sink: resuming: driver=%s output=%s", driver_name_.c_str(), - output_name_.c_str()); + roc_log(LogDebug, "sox sink: resuming: driver=%s path=%s", driver_.c_str(), + path_.c_str()); if (!output_) { const status::StatusCode code = open_(); @@ -197,10 +216,12 @@ bool SoxSink::has_latency() const { } bool SoxSink::has_clock() const { - return driver_type_ == DriverType_Device; + return true; } status::StatusCode SoxSink::write(audio::Frame& frame) { + frame_spec_.validate_frame(frame); + const audio::sample_t* frame_data = frame.raw_samples(); size_t frame_size = frame.num_raw_samples(); @@ -219,18 +240,18 @@ status::StatusCode SoxSink::write(audio::Frame& frame) { } if (buffer_pos == buffer_size_) { - const status::StatusCode code = write_(buffer_data, buffer_pos); - if (code != status::StatusOK) { - return code; + if (sox_write(output_, buffer_data, buffer_pos) != buffer_pos) { + roc_log(LogError, "sox sink: failed to write output buffer"); + return status::StatusErrDevice; } buffer_pos = 0; } } if (buffer_pos > 0) { - const status::StatusCode code = write_(buffer_data, buffer_pos); - if (code != status::StatusOK) { - return code; + if (sox_write(output_, buffer_data, buffer_pos) != buffer_pos) { + roc_log(LogError, "sox sink: failed to write output buffer"); + return status::StatusErrDevice; } } @@ -238,12 +259,6 @@ status::StatusCode SoxSink::write(audio::Frame& frame) { } status::StatusCode SoxSink::flush() { - if (output_ != NULL && driver_type_ == DriverType_File && output_->fp != NULL) { - if (fflush((FILE*)output_->fp) != 0) { - return status::StatusErrFile; - } - } - return status::StatusOK; } @@ -257,14 +272,14 @@ void SoxSink::dispose() { status::StatusCode SoxSink::init_names_(const char* driver, const char* path) { if (driver) { - if (!driver_name_.assign(driver)) { + if (!driver_.assign(driver)) { roc_log(LogError, "sox sink: can't allocate string"); return status::StatusNoMem; } } if (path) { - if (!output_name_.assign(path)) { + if (!path_.assign(path)) { roc_log(LogError, "sox sink: can't allocate string"); return status::StatusNoMem; } @@ -274,7 +289,7 @@ status::StatusCode SoxSink::init_names_(const char* driver, const char* path) { } status::StatusCode SoxSink::init_buffer_() { - buffer_size_ = sample_spec_.ns_2_samples_overall(frame_length_); + buffer_size_ = frame_spec_.ns_2_samples_overall(frame_length_); if (buffer_size_ == 0) { roc_log(LogError, "sox sink: buffer size is zero"); return status::StatusBadConfig; @@ -288,14 +303,17 @@ status::StatusCode SoxSink::init_buffer_() { } status::StatusCode SoxSink::open_() { - output_ = sox_open_write( - output_name_.is_empty() ? NULL : output_name_.c_str(), &out_signal_, NULL, - driver_name_.is_empty() ? NULL : driver_name_.c_str(), NULL, NULL); + memset(&out_signal_, 0, sizeof(out_signal_)); + out_signal_.rate = (sox_rate_t)out_spec_.sample_rate(); + out_signal_.channels = (unsigned)out_spec_.num_channels(); + out_signal_.precision = (unsigned)out_spec_.pcm_bit_width(); + + output_ = sox_open_write(path_.is_empty() ? NULL : path_.c_str(), &out_signal_, NULL, + driver_.is_empty() ? NULL : driver_.c_str(), NULL, NULL); if (!output_) { - roc_log(LogDebug, "sox sink: can't open: driver=%s path=%s", driver_name_.c_str(), - output_name_.c_str()); - return driver_type_ == DriverType_File ? status::StatusErrFile - : status::StatusErrDevice; + roc_log(LogDebug, "sox sink: can't open: driver=%s path=%s", driver_.c_str(), + path_.c_str()); + return status::StatusErrDevice; } const unsigned long requested_rate = (unsigned long)out_signal_.rate; @@ -304,11 +322,10 @@ status::StatusCode SoxSink::open_() { if (requested_rate != 0 && requested_rate != actual_rate) { roc_log(LogError, "sox sink:" - " can't open output file or device with the requested sample rate:" - " required_by_output=%lu requested_by_user=%lu", + " can't open output device with the requested sample rate:" + " supported=%lu requested=%lu", actual_rate, requested_rate); - return driver_type_ == DriverType_File ? status::StatusErrFile - : status::StatusErrDevice; + return status::StatusErrDevice; } const unsigned long requested_chans = (unsigned long)out_signal_.channels; @@ -317,35 +334,35 @@ status::StatusCode SoxSink::open_() { if (requested_chans != 0 && requested_chans != actual_chans) { roc_log(LogError, "sox sink:" - " can't open output file or device with the requested channel count:" - " required_by_output=%lu requested_by_user=%lu", + " can't open output device with the requested channel count:" + " supported=%lu requested=%lu", actual_chans, requested_chans); - return driver_type_ == DriverType_File ? status::StatusErrFile - : status::StatusErrDevice; + return status::StatusErrDevice; } - sample_spec_.set_sample_rate(actual_rate); - sample_spec_.channel_set().set_layout(audio::ChanLayout_Surround); - sample_spec_.channel_set().set_order(audio::ChanOrder_Smpte); - sample_spec_.channel_set().set_count(actual_chans); + const unsigned long requested_bits = (unsigned long)out_signal_.precision; + const unsigned long actual_bits = (unsigned long)output_->signal.precision; + + if (requested_bits != 0 && requested_bits != actual_bits) { + roc_log(LogError, + "sox sink:" + " can't open output device with the requested subformat:" + " supported=s%lu requested=s%lu", + actual_bits, requested_bits); + return status::StatusErrDevice; + } - roc_log(LogInfo, - "sox sink: opened output:" - " bits=%lu rate=%lu req_rate=%lu chans=%lu req_chans=%lu is_file=%d", - (unsigned long)output_->encoding.bits_per_sample, actual_rate, requested_rate, - actual_chans, requested_chans, (int)(driver_type_ == DriverType_File)); + out_spec_.set_sample_rate(actual_rate); + out_spec_.channel_set().set_layout(audio::ChanLayout_Surround); + out_spec_.channel_set().set_order(audio::ChanOrder_Smpte); + out_spec_.channel_set().set_count(actual_chans); - return status::StatusOK; -} + frame_spec_ = out_spec_; + frame_spec_.set_format(audio::Format_Pcm); + frame_spec_.set_pcm_subformat(audio::PcmSubformat_Raw); -status::StatusCode SoxSink::write_(const sox_sample_t* samples, size_t n_samples) { - if (n_samples > 0) { - if (sox_write(output_, samples, n_samples) != n_samples) { - roc_log(LogError, "sox sink: failed to write output buffer"); - return driver_type_ == DriverType_File ? status::StatusErrFile - : status::StatusErrDevice; - } - } + roc_log(LogInfo, "sox sink: opened output device: %s", + audio::sample_spec_to_str(out_spec_).c_str()); return status::StatusOK; } @@ -362,8 +379,7 @@ status::StatusCode SoxSink::close_() { if (err != SOX_SUCCESS) { roc_log(LogError, "sox sink: can't close output: %s", sox_strerror(err)); - return driver_type_ == DriverType_File ? status::StatusErrFile - : status::StatusErrDevice; + return status::StatusErrDevice; } return status::StatusOK; diff --git a/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_sink.h b/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_sink.h index be4215ca9..b99cc7e19 100644 --- a/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_sink.h +++ b/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_sink.h @@ -28,23 +28,22 @@ namespace sndio { //! SoX sink. //! @remarks -//! Writes samples to output file or device. -//! Supports multiple drivers for different file types and audio systems. +//! Writes samples to output device. +//! Supports multiple drivers for different audio systems. +//! Does not support files. class SoxSink : public ISink, public core::NonCopyable<> { public: //! Initialize. SoxSink(audio::FrameFactory& frame_factory, core::IArena& arena, const IoConfig& io_config, - DriverType driver_type); + const char* driver, + const char* path); ~SoxSink(); //! Check if the object was successfully constructed. status::StatusCode init_status() const; - //! Open sink. - ROC_ATTR_NODISCARD status::StatusCode open(const char* driver, const char* path); - //! Get device type. virtual DeviceType type() const; @@ -57,6 +56,9 @@ class SoxSink : public ISink, public core::NonCopyable<> { //! Get sample specification of the sink. virtual audio::SampleSpec sample_spec() const; + //! Get recommended frame length of the sink. + virtual core::nanoseconds_t frame_length() const; + //! Check if the sink supports state updates. virtual bool has_state() const; @@ -95,9 +97,8 @@ class SoxSink : public ISink, public core::NonCopyable<> { status::StatusCode write_(const sox_sample_t* samples, size_t n_samples); status::StatusCode close_(); - const DriverType driver_type_; - core::StringBuffer driver_name_; - core::StringBuffer output_name_; + core::StringBuffer driver_; + core::StringBuffer path_; sox_format_t* output_; sox_signalinfo_t out_signal_; @@ -105,7 +106,9 @@ class SoxSink : public ISink, public core::NonCopyable<> { core::Array<sox_sample_t> buffer_; size_t buffer_size_; core::nanoseconds_t frame_length_; - audio::SampleSpec sample_spec_; + + audio::SampleSpec frame_spec_; + audio::SampleSpec out_spec_; bool paused_; diff --git a/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_source.cpp b/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_source.cpp index 8164626c7..2269d151f 100644 --- a/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_source.cpp +++ b/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_source.cpp @@ -7,6 +7,7 @@ */ #include "roc_sndio/sox_source.h" +#include "roc_audio/sample_spec_to_str.h" #include "roc_core/log.h" #include "roc_core/panic.h" #include "roc_sndio/backend_map.h" @@ -15,72 +16,114 @@ namespace roc { namespace sndio { +namespace { + +const core::nanoseconds_t DefaultFrameLength = 10 * core::Millisecond; + +} // namespace + SoxSource::SoxSource(audio::FrameFactory& frame_factory, core::IArena& arena, const IoConfig& io_config, - DriverType driver_type) + const char* driver, + const char* path) : IDevice(arena) , ISource(arena) , frame_factory_(frame_factory) - , driver_type_(driver_type) - , driver_name_(arena) - , input_name_(arena) + , driver_(arena) + , path_(arena) , buffer_(arena) , buffer_size_(0) , input_(NULL) - , eof_(false) , paused_(false) , init_status_(status::NoStatus) { BackendMap::instance(); if (io_config.latency != 0) { - roc_log(LogError, "sox source: setting io latency not supported by sox backend"); + roc_log(LogError, + "sox source: setting io latency not implemented for sox backend"); init_status_ = status::StatusBadConfig; return; } - sample_spec_ = io_config.sample_spec; + if (io_config.sample_spec.has_format() + && io_config.sample_spec.format() != audio::Format_Pcm) { + roc_log(LogError, + "sox source: invalid io encoding:" + " <format> '%s' not supported by backend: spec=%s", + io_config.sample_spec.format_name(), + audio::sample_spec_to_str(io_config.sample_spec).c_str()); + init_status_ = status::StatusBadConfig; + return; + } - if (driver_type_ == DriverType_File) { - if (!sample_spec_.is_empty()) { - roc_log(LogError, "sox source: setting io encoding for files not supported"); + if (io_config.sample_spec.has_subformat()) { + if (io_config.sample_spec.pcm_subformat() == audio::PcmSubformat_Invalid) { + roc_log(LogError, + "sox source: invalid io encoding:" + " <subformat> '%s' not supported by backend: spec=%s", + io_config.sample_spec.subformat_name(), + audio::sample_spec_to_str(io_config.sample_spec).c_str()); init_status_ = status::StatusBadConfig; return; } - } else { - sample_spec_.use_defaults(audio::Sample_RawFormat, audio::ChanLayout_Surround, - audio::ChanOrder_Smpte, audio::ChanMask_Surround_Stereo, - 0); - if (!sample_spec_.is_raw()) { - roc_log(LogError, "sox sink: sample format can be only \"-\" or \"%s\"", - audio::pcm_format_to_str(audio::Sample_RawFormat)); + const audio::PcmTraits subfmt = + audio::pcm_subformat_traits(io_config.sample_spec.pcm_subformat()); + + if (!subfmt.has_flags(audio::Pcm_IsInteger | audio::Pcm_IsSigned)) { + roc_log(LogError, + "sox source: invalid io encoding:" + " <subformat> must be signed integer (like s16): spec=%s", + audio::sample_spec_to_str(io_config.sample_spec).c_str()); + init_status_ = status::StatusBadConfig; + return; + } + + if (!subfmt.has_flags(audio::Pcm_IsPacked | audio::Pcm_IsAligned)) { + roc_log(LogError, + "sox source: invalid io encoding:" + " <subformat> must be packed (like s24, not s24_4) and byte-aligned" + " (like s16, not s18): spec=%s", + audio::sample_spec_to_str(io_config.sample_spec).c_str()); + init_status_ = status::StatusBadConfig; + return; + } + + if (io_config.sample_spec.pcm_subformat() != subfmt.default_variant) { + roc_log(LogError, + "sox source: invalid io encoding:" + " <subformat> must be default-endian (like s16, not s16_le): spec=%s", + audio::sample_spec_to_str(io_config.sample_spec).c_str()); init_status_ = status::StatusBadConfig; return; } } - frame_length_ = io_config.frame_length; + in_spec_ = io_config.sample_spec; + if (!in_spec_.has_format()) { + in_spec_.set_format(audio::Format_Pcm); + in_spec_.set_pcm_subformat(audio::PcmSubformat_SInt16); + } + frame_length_ = io_config.frame_length; if (frame_length_ == 0) { - roc_log(LogError, "sox source: frame length is zero"); - init_status_ = status::StatusBadConfig; - return; + frame_length_ = DefaultFrameLength; } - { - audio::SampleSpec spec = sample_spec_; - spec.use_defaults(audio::Sample_RawFormat, audio::ChanLayout_Surround, - audio::ChanOrder_Smpte, audio::ChanMask_Surround_Stereo, 44100); + roc_log(LogDebug, "sox source: opening: driver=%s path=%s", driver, path); - sox_get_globals()->bufsiz = - spec.ns_2_samples_overall(frame_length_) * sizeof(sox_sample_t); + if ((init_status_ = init_names_(driver, path)) != status::StatusOK) { + return; } - memset(&in_signal_, 0, sizeof(in_signal_)); - in_signal_.rate = (sox_rate_t)sample_spec_.sample_rate(); - in_signal_.channels = (unsigned)sample_spec_.num_channels(); - in_signal_.precision = SOX_SAMPLE_PRECISION; + if ((init_status_ = open_()) != status::StatusOK) { + return; + } + + if ((init_status_ = init_buffer_()) != status::StatusOK) { + return; + } init_status_ = status::StatusOK; } @@ -97,30 +140,6 @@ status::StatusCode SoxSource::init_status() const { return init_status_; } -status::StatusCode SoxSource::open(const char* driver, const char* path) { - roc_log(LogInfo, "sox source: opening: driver=%s path=%s", driver, path); - - if (buffer_.size() != 0 || input_) { - roc_panic("sox source: can't call open() more than once"); - } - - status::StatusCode code = status::NoStatus; - - if ((code = init_names_(driver, path)) != status::StatusOK) { - return code; - } - - if ((code = open_()) != status::StatusOK) { - return code; - } - - if ((code = init_buffer_()) != status::StatusOK) { - return code; - } - - return status::StatusOK; -} - DeviceType SoxSource::type() const { return DeviceType_Source; } @@ -134,15 +153,15 @@ ISource* SoxSource::to_source() { } audio::SampleSpec SoxSource::sample_spec() const { - if (!input_ && !paused_) { - roc_panic("sox source: not opened"); - } + return frame_spec_; +} - return sample_spec_; +core::nanoseconds_t SoxSource::frame_length() const { + return frame_length_; } bool SoxSource::has_state() const { - return driver_type_ == DriverType_Device; + return true; } DeviceState SoxSource::state() const { @@ -162,14 +181,12 @@ status::StatusCode SoxSource::pause() { roc_panic("sox source: not opened"); } - roc_log(LogDebug, "sox source: pausing: driver=%s input=%s", driver_name_.c_str(), - input_name_.c_str()); + roc_log(LogDebug, "sox source: pausing: driver=%s path=%s", driver_.c_str(), + path_.c_str()); - if (driver_type_ == DriverType_Device) { - const status::StatusCode close_code = close_(); - if (close_code != status::StatusOK) { - return close_code; - } + const status::StatusCode close_code = close_(); + if (close_code != status::StatusOK) { + return close_code; } paused_ = true; @@ -182,8 +199,8 @@ status::StatusCode SoxSource::resume() { return status::StatusOK; } - roc_log(LogDebug, "sox source: resuming: driver=%s input=%s", driver_name_.c_str(), - input_name_.c_str()); + roc_log(LogDebug, "sox source: resuming: driver=%s path=%s", driver_.c_str(), + path_.c_str()); if (!input_) { const status::StatusCode code = open_(); @@ -202,36 +219,26 @@ bool SoxSource::has_latency() const { } bool SoxSource::has_clock() const { - return driver_type_ == DriverType_Device; + return true; } status::StatusCode SoxSource::rewind() { - roc_log(LogDebug, "sox source: rewinding: driver=%s input=%s", driver_name_.c_str(), - input_name_.c_str()); - - if (driver_type_ == DriverType_File && !eof_) { - const status::StatusCode code = seek_(0); - if (code != status::StatusOK) { - return code; - } - } else { - sample_spec_.clear(); + roc_log(LogDebug, "sox source: rewinding: driver=%s path=%s", driver_.c_str(), + path_.c_str()); - if (input_) { - const status::StatusCode close_code = close_(); - if (close_code != status::StatusOK) { - return close_code; - } + if (input_) { + const status::StatusCode close_code = close_(); + if (close_code != status::StatusOK) { + return close_code; } + } - const status::StatusCode code = open_(); - if (code != status::StatusOK) { - return code; - } + const status::StatusCode code = open_(); + if (code != status::StatusOK) { + return code; } paused_ = false; - eof_ = false; return status::StatusOK; } @@ -244,15 +251,15 @@ status::StatusCode SoxSource::read(audio::Frame& frame, packet::stream_timestamp_t duration, audio::FrameReadMode mode) { if (!input_ && !paused_) { - roc_panic("sox source: read: non-open input file or device"); + roc_panic("sox source: read: non-open input device"); } - if (paused_ || eof_) { + if (paused_) { return status::StatusFinish; } if (!frame_factory_.reallocate_frame( - frame, sample_spec_.stream_timestamp_2_bytes(duration))) { + frame, frame_spec_.stream_timestamp_2_bytes(duration))) { return status::StatusNoMem; } @@ -278,7 +285,6 @@ status::StatusCode SoxSource::read(audio::Frame& frame, n_samples = sox_read(input_, buffer_data, n_samples); if (n_samples == 0) { roc_log(LogDebug, "sox source: got eof from sox"); - eof_ = true; break; } @@ -296,7 +302,7 @@ status::StatusCode SoxSource::read(audio::Frame& frame, } frame.set_num_raw_samples(frame_size); - frame.set_duration(frame_size / sample_spec_.num_channels()); + frame.set_duration(frame_size / frame_spec_.num_channels()); if (frame.duration() < duration) { return status::StatusPart; @@ -315,14 +321,14 @@ void SoxSource::dispose() { status::StatusCode SoxSource::init_names_(const char* driver, const char* path) { if (driver) { - if (!driver_name_.assign(driver)) { + if (!driver_.assign(driver)) { roc_log(LogError, "sox source: can't allocate string"); return status::StatusNoMem; } } if (path) { - if (!input_name_.assign(path)) { + if (!path_.assign(path)) { roc_log(LogError, "sox source: can't allocate string"); return status::StatusNoMem; } @@ -332,7 +338,7 @@ status::StatusCode SoxSource::init_names_(const char* driver, const char* path) } status::StatusCode SoxSource::init_buffer_() { - buffer_size_ = sample_spec_.ns_2_samples_overall(frame_length_); + buffer_size_ = in_spec_.ns_2_samples_overall(frame_length_); if (buffer_size_ == 0) { roc_log(LogError, "sox source: buffer size is zero"); return status::StatusBadConfig; @@ -347,18 +353,17 @@ status::StatusCode SoxSource::init_buffer_() { } status::StatusCode SoxSource::open_() { - if (input_) { - roc_panic("sox source: already opened"); - } + memset(&in_signal_, 0, sizeof(in_signal_)); + in_signal_.rate = (sox_rate_t)in_spec_.sample_rate(); + in_signal_.channels = (unsigned)in_spec_.num_channels(); + in_signal_.precision = (unsigned)in_spec_.pcm_bit_width(); - input_ = - sox_open_read(input_name_.is_empty() ? NULL : input_name_.c_str(), &in_signal_, - NULL, driver_name_.is_empty() ? NULL : driver_name_.c_str()); + input_ = sox_open_read(path_.is_empty() ? NULL : path_.c_str(), &in_signal_, NULL, + driver_.is_empty() ? NULL : driver_.c_str()); if (!input_) { - roc_log(LogInfo, "sox source: can't open: driver=%s input=%s", - driver_name_.c_str(), input_name_.c_str()); - return driver_type_ == DriverType_Device ? status::StatusErrDevice - : status::StatusErrFile; + roc_log(LogInfo, "sox source: can't open: driver=%s path=%s", driver_.c_str(), + path_.c_str()); + return status::StatusErrDevice; } const unsigned long requested_rate = (unsigned long)in_signal_.rate; @@ -367,11 +372,10 @@ status::StatusCode SoxSource::open_() { if (requested_rate != 0 && requested_rate != actual_rate) { roc_log(LogError, "sox source:" - " can't open input file or device with the requested sample rate:" + " can't open input device with the requested sample rate:" " required_by_input=%lu requested_by_user=%lu", actual_rate, requested_rate); - return driver_type_ == DriverType_Device ? status::StatusErrDevice - : status::StatusErrFile; + return status::StatusErrDevice; } const unsigned long requested_chans = (unsigned long)in_signal_.channels; @@ -380,38 +384,35 @@ status::StatusCode SoxSource::open_() { if (requested_chans != 0 && requested_chans != actual_chans) { roc_log(LogError, "sox source:" - " can't open input file or device with the requested channel count:" + " can't open input device with the requested channel count:" " required_by_input=%lu requested_by_user=%lu", actual_chans, requested_chans); - return driver_type_ == DriverType_Device ? status::StatusErrDevice - : status::StatusErrFile; + return status::StatusErrDevice; } - sample_spec_.set_sample_format(audio::SampleFormat_Pcm); - sample_spec_.set_pcm_format(audio::Sample_RawFormat); - sample_spec_.set_sample_rate(actual_rate); - sample_spec_.channel_set().set_layout(audio::ChanLayout_Surround); - sample_spec_.channel_set().set_order(audio::ChanOrder_Smpte); - sample_spec_.channel_set().set_count(actual_chans); + const unsigned long requested_bits = (unsigned long)in_signal_.precision; + const unsigned long actual_bits = (unsigned long)input_->signal.precision; - roc_log(LogInfo, - "sox source: opened input:" - " bits=%lu rate=%lu req_rate=%lu chans=%lu req_chans=%lu is_file=%d", - (unsigned long)input_->encoding.bits_per_sample, actual_rate, requested_rate, - actual_chans, requested_chans, (int)(driver_type_ == DriverType_File)); + if (requested_bits != 0 && requested_bits != actual_bits) { + roc_log(LogError, + "sox source:" + " can't open input device with the requested subformat:" + " supported=s%lu requested=s%lu", + actual_bits, requested_bits); + return status::StatusErrDevice; + } - return status::StatusOK; -} + in_spec_.set_sample_rate(actual_rate); + in_spec_.channel_set().set_layout(audio::ChanLayout_Surround); + in_spec_.channel_set().set_order(audio::ChanOrder_Smpte); + in_spec_.channel_set().set_count(actual_chans); -status::StatusCode SoxSource::seek_(uint64_t offset) { - roc_log(LogDebug, "sox source: resetting position to %lu", (unsigned long)offset); + frame_spec_ = in_spec_; + frame_spec_.set_format(audio::Format_Pcm); + frame_spec_.set_pcm_subformat(audio::PcmSubformat_Raw); - const int err = sox_seek(input_, offset, SOX_SEEK_SET); - if (err != SOX_SUCCESS) { - roc_log(LogError, "sox source: can't reset position to %lu: %s", - (unsigned long)offset, sox_strerror(err)); - return status::StatusErrFile; - } + roc_log(LogInfo, "sox source: input output %s", + audio::sample_spec_to_str(in_spec_).c_str()); return status::StatusOK; } @@ -428,8 +429,7 @@ status::StatusCode SoxSource::close_() { if (err != SOX_SUCCESS) { roc_log(LogError, "sox source: can't close input: %s", sox_strerror(err)); - return driver_type_ == DriverType_File ? status::StatusErrFile - : status::StatusErrDevice; + return status::StatusErrDevice; } return status::StatusOK; diff --git a/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_source.h b/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_source.h index c183beb73..7cbf82bce 100644 --- a/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_source.h +++ b/src/internal_modules/roc_sndio/target_sox/roc_sndio/sox_source.h @@ -29,23 +29,22 @@ namespace sndio { //! SoX source. //! @remarks -//! Reads samples from input file or device. -//! Supports multiple drivers for different file types and audio systems. +//! Reads samples from input device. +//! Supports multiple drivers for different audio systems. +//! Does not support files. class SoxSource : public ISource, private core::NonCopyable<> { public: //! Initialize. SoxSource(audio::FrameFactory& frame_factory, core::IArena& arena, const IoConfig& io_config, - DriverType driver_type); + const char* driver, + const char* path); ~SoxSource(); //! Check if the object was successfully constructed. status::StatusCode init_status() const; - //! Open sink. - ROC_ATTR_NODISCARD status::StatusCode open(const char* driver, const char* path); - //! Get device type. virtual DeviceType type() const; @@ -58,6 +57,9 @@ class SoxSource : public ISource, private core::NonCopyable<> { //! Get sample specification of the source. virtual audio::SampleSpec sample_spec() const; + //! Get recommended frame length of the source. + virtual core::nanoseconds_t frame_length() const; + //! Check if the source supports state updates. virtual bool has_state() const; @@ -99,24 +101,23 @@ class SoxSource : public ISource, private core::NonCopyable<> { status::StatusCode init_buffer_(); status::StatusCode open_(); - status::StatusCode seek_(uint64_t offset); status::StatusCode close_(); audio::FrameFactory& frame_factory_; - const DriverType driver_type_; - core::StringBuffer driver_name_; - core::StringBuffer input_name_; + core::StringBuffer driver_; + core::StringBuffer path_; core::Array<sox_sample_t> buffer_; size_t buffer_size_; core::nanoseconds_t frame_length_; - audio::SampleSpec sample_spec_; + + audio::SampleSpec frame_spec_; + audio::SampleSpec in_spec_; sox_format_t* input_; sox_signalinfo_t in_signal_; - bool eof_; bool paused_; status::StatusCode init_status_; diff --git a/src/internal_modules/roc_sndio/wav_backend.cpp b/src/internal_modules/roc_sndio/wav_backend.cpp index d33ef3340..cff29aeee 100644 --- a/src/internal_modules/roc_sndio/wav_backend.cpp +++ b/src/internal_modules/roc_sndio/wav_backend.cpp @@ -19,60 +19,59 @@ namespace roc { namespace sndio { -namespace { +WavBackend::WavBackend() { +} -bool has_suffix(const char* str, const char* suffix) { - size_t len_str = strlen(str); - size_t len_suffix = strlen(suffix); - if (len_suffix > len_str) { +const char* WavBackend::name() const { + return "wav"; +} + +bool WavBackend::discover_drivers(core::Array<DriverInfo, MaxDrivers>& result) { + if (!result.push_back(DriverInfo( + "file", Driver_File | Driver_SupportsSink | Driver_SupportsSource, this))) { return false; } - return strncmp(str + len_str - len_suffix, suffix, len_suffix) == 0; + return true; } -} // namespace - -WavBackend::WavBackend() { +bool WavBackend::discover_formats(core::Array<FormatInfo, MaxFormats>& result) { + if (!result.push_back(FormatInfo( + "file", "wav", Driver_File | Driver_SupportsSink | Driver_SupportsSource, + this))) { + return false; + } + return true; } -const char* WavBackend::name() const { - return "wav"; +bool WavBackend::discover_subformat_groups(core::StringList& result) { + // no sub-formats except pcm + return true; } -void WavBackend::discover_drivers(core::Array<DriverInfo, MaxDrivers>& driver_list) { - if (!driver_list.push_back( - DriverInfo("wav", DriverType_File, - DriverFlag_SupportsSink | DriverFlag_SupportsSource, this))) { - roc_panic("wav backend: can't add driver"); - } +bool WavBackend::discover_subformats(const char* group, core::StringList& result) { + // no sub-formats except pcm + return true; } status::StatusCode WavBackend::open_device(DeviceType device_type, - DriverType driver_type, const char* driver, const char* path, const IoConfig& io_config, audio::FrameFactory& frame_factory, core::IArena& arena, IDevice** result) { - if (driver_type != DriverType_File) { - return status::StatusNoDriver; - } + roc_panic_if(!driver); + roc_panic_if(!path); - if (driver) { - if (strcmp(driver, "wav") != 0) { - return status::StatusNoDriver; - } - } else { - if (!has_suffix(path, ".wav")) { - return status::StatusNoDriver; - } + if (strcmp(driver, "file") != 0) { + // Not file://, go to next backend. + return status::StatusNoDriver; } switch (device_type) { case DeviceType_Sink: { core::ScopedPtr<WavSink> sink(new (arena) - WavSink(frame_factory, arena, io_config)); + WavSink(frame_factory, arena, io_config, path)); if (!sink) { roc_log(LogDebug, "wav backend: can't allocate sink: path=%s", path); @@ -80,16 +79,9 @@ status::StatusCode WavBackend::open_device(DeviceType device_type, } if (sink->init_status() != status::StatusOK) { - roc_log(LogDebug, "wav backend: can't initialize sink: path=%s status=%s", - path, status::code_to_str(sink->init_status())); - return sink->init_status(); - } - - const status::StatusCode code = sink->open(path); - if (code != status::StatusOK) { roc_log(LogDebug, "wav backend: can't open sink: path=%s status=%s", path, - status::code_to_str(code)); - return code; + status::code_to_str(sink->init_status())); + return sink->init_status(); } *result = sink.hijack(); @@ -97,8 +89,8 @@ status::StatusCode WavBackend::open_device(DeviceType device_type, } break; case DeviceType_Source: { - core::ScopedPtr<WavSource> source(new (arena) - WavSource(frame_factory, arena, io_config)); + core::ScopedPtr<WavSource> source( + new (arena) WavSource(frame_factory, arena, io_config, path)); if (!source) { roc_log(LogDebug, "wav backend: can't allocate source: path=%s", path); @@ -106,24 +98,14 @@ status::StatusCode WavBackend::open_device(DeviceType device_type, } if (source->init_status() != status::StatusOK) { - roc_log(LogDebug, "wav backend: can't initialize source: path=%s status=%s", - path, status::code_to_str(source->init_status())); - return source->init_status(); - } - - const status::StatusCode code = source->open(path); - if (code != status::StatusOK) { roc_log(LogDebug, "wav backend: can't open source: path=%s status=%s", path, - status::code_to_str(code)); - return code; + status::code_to_str(source->init_status())); + return source->init_status(); } *result = source.hijack(); return status::StatusOK; } break; - - default: - break; } roc_panic("wav backend: invalid device type"); diff --git a/src/internal_modules/roc_sndio/wav_backend.h b/src/internal_modules/roc_sndio/wav_backend.h index 72cae1a24..15f671dde 100644 --- a/src/internal_modules/roc_sndio/wav_backend.h +++ b/src/internal_modules/roc_sndio/wav_backend.h @@ -27,12 +27,23 @@ class WavBackend : public IBackend, core::NonCopyable<> { virtual const char* name() const; //! Append supported drivers to the list. - virtual void discover_drivers(core::Array<DriverInfo, MaxDrivers>& driver_list); + virtual ROC_ATTR_NODISCARD bool + discover_drivers(core::Array<DriverInfo, MaxDrivers>& result); + + //! Append supported formats to the list. + virtual ROC_ATTR_NODISCARD bool + discover_formats(core::Array<FormatInfo, MaxFormats>& result); + + //! Append supported groups of sub-formats to the list. + virtual ROC_ATTR_NODISCARD bool discover_subformat_groups(core::StringList& result); + + //! Append supported sub-formats of a group to the list. + virtual ROC_ATTR_NODISCARD bool discover_subformats(const char* group, + core::StringList& result); //! Create and open a sink or source. virtual ROC_ATTR_NODISCARD status::StatusCode open_device(DeviceType device_type, - DriverType driver_type, const char* driver, const char* path, const IoConfig& io_config, diff --git a/src/internal_modules/roc_sndio/wav_sink.cpp b/src/internal_modules/roc_sndio/wav_sink.cpp index 478c93e83..793e6cc86 100644 --- a/src/internal_modules/roc_sndio/wav_sink.cpp +++ b/src/internal_modules/roc_sndio/wav_sink.cpp @@ -7,8 +7,8 @@ */ #include "roc_sndio/wav_sink.h" -#include "roc_audio/pcm_format.h" -#include "roc_audio/sample_format.h" +#include "roc_audio/format.h" +#include "roc_audio/pcm_subformat.h" #include "roc_audio/sample_spec_to_str.h" #include "roc_core/endian_ops.h" #include "roc_core/log.h" @@ -18,71 +18,118 @@ namespace roc { namespace sndio { +namespace { + +bool has_extension(const char* path, const char* ext) { + size_t path_len = strlen(path); + size_t ext_len = strlen(ext); + if (ext_len > path_len) { + return false; + } + return strncmp(path + path_len - ext_len, ext, ext_len) == 0; +} + +} // namespace + WavSink::WavSink(audio::FrameFactory& frame_factory, core::IArena& arena, - const IoConfig& io_config) + const IoConfig& io_config, + const char* path) : IDevice(arena) , ISink(arena) , output_file_(NULL) , is_first_(true) , init_status_(status::NoStatus) { - if (io_config.latency != 0) { - roc_log(LogError, "wav sink: setting io latency not supported by backend"); - init_status_ = status::StatusBadConfig; - return; + if (io_config.sample_spec.has_format()) { + if (io_config.sample_spec.format() != audio::Format_Wav) { + roc_log(LogDebug, + "wav sink: requested format '%s' not supported by backend: spec=%s", + io_config.sample_spec.format_name(), + audio::sample_spec_to_str(io_config.sample_spec).c_str()); + // Not a wav file, go to next backend. + init_status_ = status::StatusNoFormat; + return; + } + } else { + if (!has_extension(path, ".wav")) { + roc_log( + LogDebug, + "wav sink: requested file extension not supported by backend: path=%s", + path); + // Not a wav file, go to next backend. + init_status_ = status::StatusNoFormat; + return; + } } - sample_spec_ = io_config.sample_spec; - - sample_spec_.use_defaults(audio::Sample_RawFormat, audio::ChanLayout_Surround, - audio::ChanOrder_Smpte, audio::ChanMask_Surround_Stereo, - 44100); + if (io_config.sample_spec.has_subformat()) { + if (io_config.sample_spec.pcm_subformat() == audio::PcmSubformat_Invalid) { + roc_log(LogDebug, + "wav sink: invalid io encoding:" + " <subformat> must be pcm (like s16 or f32): spec=%s", + audio::sample_spec_to_str(io_config.sample_spec).c_str()); + init_status_ = status::StatusBadConfig; + return; + } - if (!sample_spec_.is_pcm()) { - roc_log(LogError, "wav sink: unsupported format: must be pcm: spec=%s", - audio::sample_spec_to_str(sample_spec_).c_str()); - init_status_ = status::StatusBadConfig; - return; - } + const audio::PcmTraits subfmt = + audio::pcm_subformat_traits(io_config.sample_spec.pcm_subformat()); + + if (!subfmt.has_flags(audio::Pcm_IsSigned)) { + roc_log(LogError, + "wav sink: invalid io encoding:" + " <subformat> must be float (like f32) or signed integer (like s16): " + "spec=%s", + audio::sample_spec_to_str(io_config.sample_spec).c_str()); + init_status_ = status::StatusBadConfig; + return; + } - const audio::PcmTraits fmt = audio::pcm_format_traits(sample_spec_.pcm_format()); + if (!subfmt.has_flags(audio::Pcm_IsPacked | audio::Pcm_IsAligned)) { + roc_log(LogError, + "wav sink: invalid io encoding:" + " <subformat> must be packed (like s24, not s24_4) and byte-aligned" + " (like s16, not s18): spec=%s", + audio::sample_spec_to_str(io_config.sample_spec).c_str()); + init_status_ = status::StatusBadConfig; + return; + } - if (!fmt.has_flags(audio::Pcm_IsSigned)) { - roc_log(LogError, "wav sink: unsupported format: must be signed: spec=%s", - audio::sample_spec_to_str(sample_spec_).c_str()); - init_status_ = status::StatusBadConfig; - return; + if (io_config.sample_spec.pcm_subformat() != subfmt.default_variant + && io_config.sample_spec.pcm_subformat() != subfmt.le_variant) { + roc_log(LogError, + "wav sink: invalid io encoding:" + " <subformat> must be default-endian (like s16) or little-endian" + " (like s16_le): spec=%s", + audio::sample_spec_to_str(io_config.sample_spec).c_str()); + init_status_ = status::StatusBadConfig; + return; + } } - if (!fmt.has_flags(audio::Pcm_IsPacked | audio::Pcm_IsAligned)) { - roc_log(LogError, - "wav sink: unsupported format: must be packed and byte-aligned: spec=%s", - audio::sample_spec_to_str(sample_spec_).c_str()); - init_status_ = status::StatusBadConfig; - return; - } + file_spec_ = io_config.sample_spec; + file_spec_.use_defaults(audio::Format_Wav, audio::PcmSubformat_Raw, + audio::ChanLayout_Surround, audio::ChanOrder_Smpte, + audio::ChanMask_Surround_Stereo, 44100); - // WAV format is always little-endian. - if (sample_spec_.pcm_format() != fmt.default_variant - && sample_spec_.pcm_format() != fmt.le_variant) { - roc_log(LogError, - "wav sink: sample format must be default-endian (like s16) or" - " little-endian (like s16_le): spec=%s", - audio::sample_spec_to_str(sample_spec_).c_str()); - init_status_ = status::StatusBadConfig; - return; - } + const audio::PcmTraits subfmt = + audio::pcm_subformat_traits(file_spec_.pcm_subformat()); - if (sample_spec_.pcm_format() == fmt.default_variant) { - sample_spec_.set_pcm_format(fmt.le_variant); + frame_spec_ = file_spec_; + frame_spec_.set_format(audio::Format_Pcm); + if (frame_spec_.pcm_subformat() == subfmt.default_variant) { + frame_spec_.set_pcm_subformat(subfmt.le_variant); } const uint16_t fmt_code = - fmt.has_flags(audio::Pcm_IsInteger) ? WAV_FORMAT_PCM : WAV_FORMAT_IEEE_FLOAT; + subfmt.has_flags(audio::Pcm_IsInteger) ? WAV_FORMAT_PCM : WAV_FORMAT_IEEE_FLOAT; - header_.reset(new (header_) - WavHeader(fmt_code, fmt.bit_width, sample_spec_.sample_rate(), - sample_spec_.num_channels())); + header_.reset(new (header_) WavHeader( + fmt_code, subfmt.bit_width, file_spec_.sample_rate(), file_spec_.num_channels())); + + if ((init_status_ = open_(path)) != status::StatusOK) { + return; + } init_status_ = status::StatusOK; } @@ -98,10 +145,6 @@ status::StatusCode WavSink::init_status() const { return init_status_; } -status::StatusCode WavSink::open(const char* path) { - return open_(path); -} - DeviceType WavSink::type() const { return DeviceType_Sink; } @@ -119,7 +162,11 @@ audio::SampleSpec WavSink::sample_spec() const { roc_panic("wav sink: not opened"); } - return sample_spec_; + return frame_spec_; +} + +core::nanoseconds_t WavSink::frame_length() const { + return 0; } bool WavSink::has_state() const { @@ -139,6 +186,8 @@ status::StatusCode WavSink::write(audio::Frame& frame) { roc_panic("wav sink: not opened"); } + frame_spec_.validate_frame(frame); + if (is_first_) { const WavHeader::WavHeaderData& wav_header = header_->update_and_get_header(0); if (fwrite(&wav_header, sizeof(wav_header), 1, output_file_) != 1) { @@ -204,24 +253,20 @@ void WavSink::dispose() { } status::StatusCode WavSink::open_(const char* path) { - if (output_file_) { - roc_panic("wav sink: already opened"); - } + roc_log(LogDebug, "wav sink: opening: path=%s", path); - output_file_ = fopen(path, "w"); - - if (!output_file_) { - roc_log(LogDebug, "wav sink: can't open output file: %s", - core::errno_to_str(errno).c_str()); - return status::StatusErrFile; + if (strcmp(path, "-") == 0) { + output_file_ = stdout; + } else { + if (!(output_file_ = fopen(path, "wb"))) { + roc_log(LogDebug, "wav sink: can't open output file: %s", + core::errno_to_str(errno).c_str()); + return status::StatusErrFile; + } } - roc_log(LogInfo, - "wav sink: opened output file:" - " path=%s out_bits=%lu out_rate=%lu out_ch=%lu", - path, (unsigned long)header_->bits_per_sample(), - (unsigned long)header_->sample_rate(), - (unsigned long)header_->num_channels()); + roc_log(LogInfo, "wav sink: opened output file: %s", + audio::sample_spec_to_str(file_spec_).c_str()); return status::StatusOK; } @@ -233,13 +278,17 @@ status::StatusCode WavSink::close_() { roc_log(LogDebug, "wav sink: closing output file"); - const int err = fclose(output_file_); - output_file_ = NULL; + if (output_file_ == stdout) { + output_file_ = NULL; + } else { + const int err = fclose(output_file_); + output_file_ = NULL; - if (err != 0) { - roc_log(LogError, "wav sink: can't properly close output file: %s", - core::errno_to_str(errno).c_str()); - return status::StatusErrFile; + if (err != 0) { + roc_log(LogError, "wav sink: can't properly close output file: %s", + core::errno_to_str(errno).c_str()); + return status::StatusErrFile; + } } return status::StatusOK; diff --git a/src/internal_modules/roc_sndio/wav_sink.h b/src/internal_modules/roc_sndio/wav_sink.h index d9f1b1882..dec0b5e67 100644 --- a/src/internal_modules/roc_sndio/wav_sink.h +++ b/src/internal_modules/roc_sndio/wav_sink.h @@ -30,15 +30,13 @@ class WavSink : public ISink, public core::NonCopyable<> { //! Initialize. WavSink(audio::FrameFactory& frame_factory, core::IArena& arena, - const IoConfig& io_config); + const IoConfig& io_config, + const char* path); ~WavSink(); //! Check if the object was successfully constructed. status::StatusCode init_status() const; - //! Open sink. - ROC_ATTR_NODISCARD status::StatusCode open(const char* device); - //! Get device type. virtual DeviceType type() const; @@ -51,6 +49,9 @@ class WavSink : public ISink, public core::NonCopyable<> { //! Get sample specification of the sink. virtual audio::SampleSpec sample_spec() const; + //! Get recommended frame length of the sink. + virtual core::nanoseconds_t frame_length() const; + //! Check if the sink supports state updates. virtual bool has_state() const; @@ -76,7 +77,8 @@ class WavSink : public ISink, public core::NonCopyable<> { status::StatusCode open_(const char* path); status::StatusCode close_(); - audio::SampleSpec sample_spec_; + audio::SampleSpec frame_spec_; + audio::SampleSpec file_spec_; FILE* output_file_; core::Optional<WavHeader> header_; diff --git a/src/internal_modules/roc_sndio/wav_source.cpp b/src/internal_modules/roc_sndio/wav_source.cpp index f4dba94f4..2724e59fc 100644 --- a/src/internal_modules/roc_sndio/wav_source.cpp +++ b/src/internal_modules/roc_sndio/wav_source.cpp @@ -7,6 +7,7 @@ */ #include "roc_sndio/wav_source.h" +#include "roc_audio/sample_spec_to_str.h" #include "roc_core/log.h" #include "roc_core/panic.h" #include "roc_status/code_to_str.h" @@ -14,24 +15,71 @@ namespace roc { namespace sndio { +namespace { + +bool has_extension(const char* path, const char* ext) { + size_t path_len = strlen(path); + size_t ext_len = strlen(ext); + if (ext_len > path_len) { + return false; + } + return strncmp(path + path_len - ext_len, ext, ext_len) == 0; +} + +size_t file_read(void* file, void* buf, size_t bufsz) { + return fread(buf, 1, bufsz, (FILE*)file); +} + +drwav_bool32 file_seek(void* file, int offset, drwav_seek_origin origin) { + return fseek((FILE*)file, offset, + origin == drwav_seek_origin_current ? SEEK_CUR : SEEK_SET) + == 0; +} + +} // namespace + WavSource::WavSource(audio::FrameFactory& frame_factory, core::IArena& arena, - const IoConfig& io_config) + const IoConfig& io_config, + const char* path) : IDevice(arena) , ISource(arena) , frame_factory_(frame_factory) - , file_opened_(false) + , input_file_(NULL) , eof_(false) , init_status_(status::NoStatus) { - if (io_config.latency != 0) { - roc_log(LogError, "wav source: setting io latency not supported by backend"); + if (io_config.sample_spec.has_format()) { + if (io_config.sample_spec.format() != audio::Format_Wav) { + roc_log(LogDebug, + "wav source: requested format '%s' not supported by backend: spec=%s", + io_config.sample_spec.format_name(), + audio::sample_spec_to_str(io_config.sample_spec).c_str()); + // Not a wav file, go to next backend. + init_status_ = status::StatusNoFormat; + return; + } + } else { + if (!has_extension(path, ".wav")) { + roc_log( + LogDebug, + "wav source: requested file extension not supported by backend: path=%s", + path); + // Not a wav file, go to next backend. + init_status_ = status::StatusNoFormat; + return; + } + } + + if (io_config.sample_spec.has_subformat() || io_config.sample_spec.has_sample_rate() + || io_config.sample_spec.has_channel_set()) { + roc_log(LogError, + "wav source: invalid io encoding: <subformat>, <rate> and <channels>" + " not allowed for input file when <format> is 'wav', set them to \"-\""); init_status_ = status::StatusBadConfig; return; } - if (!io_config.sample_spec.is_empty()) { - roc_log(LogError, "wav source: setting io encoding not supported by backend"); - init_status_ = status::StatusBadConfig; + if ((init_status_ = open_(path)) != status::StatusOK) { return; } @@ -50,10 +98,6 @@ status::StatusCode WavSource::init_status() const { return init_status_; } -status::StatusCode WavSource::open(const char* path) { - return open_(path); -} - DeviceType WavSource::type() const { return DeviceType_Source; } @@ -67,13 +111,17 @@ ISource* WavSource::to_source() { } audio::SampleSpec WavSource::sample_spec() const { - if (!file_opened_) { + if (!input_file_) { roc_panic("wav source: not opened"); } return sample_spec_; } +core::nanoseconds_t WavSource::frame_length() const { + return 0; +} + bool WavSource::has_state() const { return false; } @@ -89,11 +137,11 @@ bool WavSource::has_clock() const { status::StatusCode WavSource::rewind() { roc_log(LogDebug, "wav source: rewinding"); - if (!file_opened_) { + if (!input_file_) { roc_panic("wav source: not opened"); } - if (!drwav_seek_to_pcm_frame(&wav_, 0)) { + if (!drwav_seek_to_pcm_frame(&wav_decoder_, 0)) { roc_log(LogError, "wav source: seek failed"); return status::StatusErrFile; } @@ -110,7 +158,7 @@ void WavSource::reclock(core::nanoseconds_t timestamp) { status::StatusCode WavSource::read(audio::Frame& frame, packet::stream_timestamp_t duration, audio::FrameReadMode mode) { - if (!file_opened_) { + if (!input_file_) { roc_panic("wav source: not opened"); } @@ -132,9 +180,15 @@ status::StatusCode WavSource::read(audio::Frame& frame, while (frame_left != 0) { size_t n_samples = frame_left; - n_samples = - drwav_read_pcm_frames_f32(&wav_, n_samples / wav_.channels, frame_data) - * wav_.channels; + n_samples = drwav_read_pcm_frames_f32( + &wav_decoder_, n_samples / wav_decoder_.channels, frame_data) + * wav_decoder_.channels; + + if (ferror(input_file_)) { + roc_log(LogError, "wav source: can't read input file: %s", + core::errno_to_str(errno).c_str()); + return status::StatusErrFile; + } if (n_samples == 0) { roc_log(LogDebug, "wav source: got eof from input file"); @@ -152,7 +206,8 @@ status::StatusCode WavSource::read(audio::Frame& frame, } frame.set_num_raw_samples(frame_size); - frame.set_duration(frame_size / sample_spec_.num_channels()); + frame.set_duration( + packet::stream_timestamp_t(frame_size / sample_spec_.num_channels())); if (frame.duration() < duration) { return status::StatusPart; @@ -170,48 +225,65 @@ void WavSource::dispose() { } status::StatusCode WavSource::open_(const char* path) { - if (file_opened_) { - roc_panic("wav source: already opened"); + roc_log(LogDebug, "wav source: opening: path=%s", path); + + if (strcmp(path, "-") == 0) { + input_file_ = stdin; + } else { + if (!(input_file_ = fopen(path, "rb"))) { + roc_log(LogError, "wav source: can't open input file: %s", + core::errno_to_str(errno).c_str()); + return status::StatusErrFile; + } } - if (!drwav_init_file(&wav_, path, NULL)) { - roc_log(LogDebug, "wav sink: can't open input file: %s", - core::errno_to_str(errno).c_str()); - return status::StatusErrFile; + if (!drwav_init(&wav_decoder_, &file_read, &file_seek, input_file_, NULL)) { + roc_log(LogDebug, "wav source: can't recognize input file format"); + if (input_file_ != stdin) { + fclose(input_file_); + } + input_file_ = NULL; + return status::StatusNoFormat; } - roc_log(LogInfo, - "wav source: opened input file:" - " path=%s in_bits=%lu in_rate=%lu in_ch=%lu", - path, (unsigned long)wav_.bitsPerSample, (unsigned long)wav_.sampleRate, - (unsigned long)wav_.channels); - - sample_spec_.set_sample_rate((size_t)wav_.sampleRate); - sample_spec_.set_sample_format(audio::SampleFormat_Pcm); - sample_spec_.set_pcm_format(audio::Sample_RawFormat); + sample_spec_.set_format(audio::Format_Pcm); + sample_spec_.set_pcm_subformat(audio::PcmSubformat_Raw); + sample_spec_.set_sample_rate((size_t)wav_decoder_.sampleRate); sample_spec_.channel_set().set_layout(audio::ChanLayout_Surround); sample_spec_.channel_set().set_order(audio::ChanOrder_Smpte); - sample_spec_.channel_set().set_count((size_t)wav_.channels); + sample_spec_.channel_set().set_count((size_t)wav_decoder_.channels); - file_opened_ = true; + roc_log(LogInfo, "wav source: opened input file: %s", + audio::sample_spec_to_str(sample_spec_).c_str()); return status::StatusOK; } status::StatusCode WavSource::close_() { - if (!file_opened_) { + if (!input_file_) { return status::StatusOK; } - roc_log(LogInfo, "sndfile source: closing input file"); + roc_log(LogInfo, "wav source: closing input file"); - file_opened_ = false; - - if (drwav_uninit(&wav_) != DRWAV_SUCCESS) { + if (drwav_uninit(&wav_decoder_) != DRWAV_SUCCESS) { roc_log(LogError, "wav source: can't properly close input file"); return status::StatusErrFile; } + if (input_file_ == stdin) { + input_file_ = NULL; + } else { + const int err = fclose(input_file_); + input_file_ = NULL; + + if (err != 0) { + roc_log(LogError, "wav source: can't properly close input file: %s", + core::errno_to_str(errno).c_str()); + return status::StatusErrFile; + } + } + return status::StatusOK; } diff --git a/src/internal_modules/roc_sndio/wav_source.h b/src/internal_modules/roc_sndio/wav_source.h index 10f957dcb..e447aa4c8 100644 --- a/src/internal_modules/roc_sndio/wav_source.h +++ b/src/internal_modules/roc_sndio/wav_source.h @@ -31,15 +31,13 @@ class WavSource : public ISource, private core::NonCopyable<> { //! Initialize. WavSource(audio::FrameFactory& frame_factory, core::IArena& arena, - const IoConfig& io_config); + const IoConfig& io_config, + const char* path); ~WavSource(); //! Check if the object was successfully constructed. status::StatusCode init_status() const; - //! Open source. - ROC_ATTR_NODISCARD status::StatusCode open(const char* device); - //! Get device type. virtual DeviceType type() const; @@ -52,6 +50,9 @@ class WavSource : public ISource, private core::NonCopyable<> { //! Get sample specification of the source. virtual audio::SampleSpec sample_spec() const; + //! Get recommended frame length of the source. + virtual core::nanoseconds_t frame_length() const; + //! Check if the source supports state updates. virtual bool has_state() const; @@ -87,8 +88,8 @@ class WavSource : public ISource, private core::NonCopyable<> { audio::SampleSpec sample_spec_; - drwav wav_; - bool file_opened_; + FILE* input_file_; + drwav wav_decoder_; bool eof_; status::StatusCode init_status_; diff --git a/src/internal_modules/roc_status/code_to_str.cpp b/src/internal_modules/roc_status/code_to_str.cpp index da490582e..46313c0b8 100644 --- a/src/internal_modules/roc_status/code_to_str.cpp +++ b/src/internal_modules/roc_status/code_to_str.cpp @@ -34,6 +34,8 @@ const char* code_to_str(StatusCode code) { return "NoRoute"; case StatusNoDriver: return "NoDriver"; + case StatusNoFormat: + return "NoFormat"; case StatusNoPlugin: return "NoPlugin"; case StatusErrDevice: diff --git a/src/internal_modules/roc_status/status_code.h b/src/internal_modules/roc_status/status_code.h index 33c3d27b3..af955fc00 100644 --- a/src/internal_modules/roc_status/status_code.h +++ b/src/internal_modules/roc_status/status_code.h @@ -86,11 +86,19 @@ enum StatusCode { //! @remarks //! Indicates that there is no suitable driver to open sink or source. //! @note + //! Example: we're trying to open a pulseaudio device using a backend + //! that supports only alsa devices. + StatusNoDriver, + + //! Unsupported format. + //! @remarks + //! Indicates that the format or sub-format requested is not supported. + //! @note //! Example: we're trying to open an mp3 file using a backend that //! supports only wav files. - StatusNoDriver, + StatusNoFormat, - //! No plugin found. + //! Unusable or missing plugin. //! @remarks //! Indicates that plugin lookup or initialization failed. //! @note diff --git a/src/public_api/examples/basic_receiver_pulseaudio.c b/src/public_api/examples/basic_receiver_pulseaudio.c index 8160324bb..a529d4a2f 100644 --- a/src/public_api/examples/basic_receiver_pulseaudio.c +++ b/src/public_api/examples/basic_receiver_pulseaudio.c @@ -66,8 +66,9 @@ int main() { memset(&receiver_config, 0, sizeof(receiver_config)); /* Setup frame format that we want to read from receiver. */ + receiver_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + receiver_config.frame_encoding.bits = 32; receiver_config.frame_encoding.rate = MY_SAMPLE_RATE; - receiver_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; receiver_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; /* Use user-provided clock. diff --git a/src/public_api/examples/basic_receiver_wav_file.c b/src/public_api/examples/basic_receiver_wav_file.c index 224e91693..b9490c98c 100644 --- a/src/public_api/examples/basic_receiver_wav_file.c +++ b/src/public_api/examples/basic_receiver_wav_file.c @@ -113,8 +113,9 @@ int main() { memset(&receiver_config, 0, sizeof(receiver_config)); /* Setup frame format that we want to read from receiver. */ + receiver_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + receiver_config.frame_encoding.bits = 32; receiver_config.frame_encoding.rate = MY_SAMPLE_RATE; - receiver_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; receiver_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; /* Turn on internal CPU timer. diff --git a/src/public_api/examples/basic_sender_pulseaudio.c b/src/public_api/examples/basic_sender_pulseaudio.c index 31845b169..600001773 100644 --- a/src/public_api/examples/basic_sender_pulseaudio.c +++ b/src/public_api/examples/basic_sender_pulseaudio.c @@ -67,8 +67,9 @@ int main() { memset(&sender_config, 0, sizeof(sender_config)); /* Setup frame format that we want to write to sender. */ + sender_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + sender_config.frame_encoding.bits = 32; sender_config.frame_encoding.rate = MY_SAMPLE_RATE; - sender_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; sender_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; /* Setup network packets format that sender should generate. */ diff --git a/src/public_api/examples/basic_sender_sine_wave.c b/src/public_api/examples/basic_sender_sine_wave.c index fb9235dac..484732b82 100644 --- a/src/public_api/examples/basic_sender_sine_wave.c +++ b/src/public_api/examples/basic_sender_sine_wave.c @@ -81,8 +81,9 @@ int main() { memset(&sender_config, 0, sizeof(sender_config)); /* Setup frame format that we want to write to sender. */ + sender_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + sender_config.frame_encoding.bits = 32; sender_config.frame_encoding.rate = MY_SAMPLE_RATE; - sender_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; sender_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; /* Setup network packets format that sender should generate. */ diff --git a/src/public_api/examples/plugin_plc.c b/src/public_api/examples/plugin_plc.c index 5c65fc731..6fa2bb135 100644 --- a/src/public_api/examples/plugin_plc.c +++ b/src/public_api/examples/plugin_plc.c @@ -202,8 +202,9 @@ int main() { /* Setup frame encoding that we read from receiver. * Note that this encoding is different from the encoding used by PLC plugin. */ + receiver_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + receiver_config.frame_encoding.bits = 32; receiver_config.frame_encoding.rate = MY_SAMPLE_RATE; - receiver_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; receiver_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; /* Enable PLC plugin. */ diff --git a/src/public_api/examples/send_recv_1_sender_2_receivers.c b/src/public_api/examples/send_recv_1_sender_2_receivers.c index 30255655c..c1a19eeb0 100644 --- a/src/public_api/examples/send_recv_1_sender_2_receivers.c +++ b/src/public_api/examples/send_recv_1_sender_2_receivers.c @@ -85,8 +85,9 @@ static void* receiver_loop(void* arg) { roc_receiver_config receiver_config; memset(&receiver_config, 0, sizeof(receiver_config)); + receiver_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + receiver_config.frame_encoding.bits = 32; receiver_config.frame_encoding.rate = MY_SAMPLE_RATE; - receiver_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; receiver_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; /* Make read operation blocking as we don't have our own clock. */ @@ -176,8 +177,9 @@ static void sender_loop() { roc_sender_config sender_config; memset(&sender_config, 0, sizeof(sender_config)); + sender_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + sender_config.frame_encoding.bits = 32; sender_config.frame_encoding.rate = MY_SAMPLE_RATE; - sender_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; sender_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; sender_config.fec_encoding = ROC_FEC_ENCODING_RS8M; diff --git a/src/public_api/examples/send_recv_2_senders_1_receiver.c b/src/public_api/examples/send_recv_2_senders_1_receiver.c index 530e3c690..e69db3f7b 100644 --- a/src/public_api/examples/send_recv_2_senders_1_receiver.c +++ b/src/public_api/examples/send_recv_2_senders_1_receiver.c @@ -68,8 +68,9 @@ static void receiver_loop() { roc_receiver_config receiver_config; memset(&receiver_config, 0, sizeof(receiver_config)); + receiver_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + receiver_config.frame_encoding.bits = 32; receiver_config.frame_encoding.rate = MY_SAMPLE_RATE; - receiver_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; receiver_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; /* Make read operation blocking as we don't have our own clock. */ @@ -179,8 +180,9 @@ static void* sender_loop(void* arg) { roc_sender_config sender_config; memset(&sender_config, 0, sizeof(sender_config)); + sender_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + sender_config.frame_encoding.bits = 32; sender_config.frame_encoding.rate = MY_SAMPLE_RATE; - sender_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; sender_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; sender_config.packet_encoding = ROC_PACKET_ENCODING_AVP_L16_STEREO; diff --git a/src/public_api/examples/send_recv_multicast.c b/src/public_api/examples/send_recv_multicast.c index 5fafc44e1..990474c7e 100644 --- a/src/public_api/examples/send_recv_multicast.c +++ b/src/public_api/examples/send_recv_multicast.c @@ -68,8 +68,9 @@ static void* receiver_loop(void* arg) { roc_receiver_config receiver_config; memset(&receiver_config, 0, sizeof(receiver_config)); + receiver_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + receiver_config.frame_encoding.bits = 32; receiver_config.frame_encoding.rate = MY_SAMPLE_RATE; - receiver_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; receiver_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; /* Make read operation blocking as we don't have our own clock. */ @@ -193,8 +194,9 @@ static void sender_loop() { roc_sender_config sender_config; memset(&sender_config, 0, sizeof(sender_config)); + sender_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + sender_config.frame_encoding.bits = 32; sender_config.frame_encoding.rate = MY_SAMPLE_RATE; - sender_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; sender_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; sender_config.fec_encoding = ROC_FEC_ENCODING_RS8M; diff --git a/src/public_api/examples/send_recv_rtp.c b/src/public_api/examples/send_recv_rtp.c index 9e861aebe..fcac001f6 100644 --- a/src/public_api/examples/send_recv_rtp.c +++ b/src/public_api/examples/send_recv_rtp.c @@ -47,8 +47,9 @@ static void* receiver_loop(void* arg) { roc_receiver_config receiver_config; memset(&receiver_config, 0, sizeof(receiver_config)); + receiver_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + receiver_config.frame_encoding.bits = 32; receiver_config.frame_encoding.rate = MY_SAMPLE_RATE; - receiver_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; receiver_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; /* Make read operation blocking as we don't have our own clock. */ @@ -102,8 +103,9 @@ static void sender_loop(roc_context* context) { roc_sender_config sender_config; memset(&sender_config, 0, sizeof(sender_config)); + sender_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + sender_config.frame_encoding.bits = 32; sender_config.frame_encoding.rate = MY_SAMPLE_RATE; - sender_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; sender_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; /* Disable FEC as we want to use bare RTP. */ diff --git a/src/public_api/examples/send_recv_rtp_rtcp_fec.c b/src/public_api/examples/send_recv_rtp_rtcp_fec.c index 981ccb2fb..514200194 100644 --- a/src/public_api/examples/send_recv_rtp_rtcp_fec.c +++ b/src/public_api/examples/send_recv_rtp_rtcp_fec.c @@ -48,8 +48,9 @@ static void* receiver_loop(void* arg) { roc_receiver_config receiver_config; memset(&receiver_config, 0, sizeof(receiver_config)); + receiver_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + receiver_config.frame_encoding.bits = 32; receiver_config.frame_encoding.rate = MY_SAMPLE_RATE; - receiver_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; receiver_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; /* Make read operation blocking as we don't have our own clock. */ @@ -130,8 +131,9 @@ static void sender_loop(roc_context* context) { roc_sender_config sender_config; memset(&sender_config, 0, sizeof(sender_config)); + sender_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + sender_config.frame_encoding.bits = 32; sender_config.frame_encoding.rate = MY_SAMPLE_RATE; - sender_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; sender_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; /* Enable Reed-Solomon FEC scheme because we use ROC_PROTO_RTP_RS8M_SOURCE diff --git a/src/public_api/include/roc/config.h b/src/public_api/include/roc/config.h index d06a1a385..333d4689e 100644 --- a/src/public_api/include/roc/config.h +++ b/src/public_api/include/roc/config.h @@ -315,21 +315,65 @@ typedef enum roc_fec_encoding { } roc_fec_encoding; /** Sample format. - * Defines how each sample is represented. - * Does not define channels layout and sample rate. + * Defines how samples are encoded into binary form. + * Doesn't define sample width, sample rate, and channels layout - these parameters + * are configured separately via \ref roc_media_encoding. */ typedef enum roc_format { - /** PCM floats. - * Uncompressed samples coded as 32-bit native-endian floats in range [-1; 1]. - * Channels are interleaved, e.g. two channels are encoded as "L R L R ...". + /** Uncompressed interleaved PCM IEEE-754 floats. + * + * Multiple channels are interleaved, e.g. two channels are encoded as "L R L R ...". + * + * Endianess depends on the context when format is applied: roc_frame uses + * native-endian, network packets uses endian defined by the protocol. + * + * Supported bit widths: 32, 64. + * Supported rates: any. + * Supported channels: any. + */ + ROC_FORMAT_PCM_IEEE_FLOAT = 10, + + /** Uncompressed interleaved PCM signed integers in 2's complement notation. + * + * Multiple channels are interleaved, e.g. two channels are encoded as "L R L R ...". + * + * Endianess depends on the context when format is applied: roc_frame uses + * native-endian, network packets uses endian defined by the protocol. + * + * Supported bit widths: 8, 16, 24, 32, 64. + * Supported rates: any. + * Supported channels: any. + */ + ROC_FORMAT_PCM_SIGNED_INT = 11, + + /** Uncompressed interleaved PCM unsigned integers. + * + * Multiple channels are interleaved, e.g. two channels are encoded as "L R L R ...". + * + * Endianess depends on the context when format is applied: roc_frame uses + * native-endian, network packets uses endian defined by the protocol. + * + * Supported bit widths: 8, 16, 24, 32, 64. + * Supported rates: any. + * Supported channels: any. */ - ROC_FORMAT_PCM_FLOAT32 = 1 + ROC_FORMAT_PCM_UNSIGNED_INT = 12, } roc_format; /** Channel layout. * Defines number of channels and meaning of each channel. */ typedef enum roc_channel_layout { + /** Mono. + * One channel with monophonic sound. + */ + ROC_CHANNEL_LAYOUT_MONO = 1, + + /** Stereo. + * Two channels: left, right. + */ + ROC_CHANNEL_LAYOUT_STEREO = 2, + /** Multi-track audio. * * In multitrack layout, stream contains multiple channels which represent @@ -339,35 +383,34 @@ typedef enum roc_channel_layout { * The number of channels is arbitrary and is defined by \c tracks field of * \ref roc_media_encoding struct. */ - ROC_CHANNEL_LAYOUT_MULTITRACK = 1, - - /** Mono. - * One channel with monophonic sound. - */ - ROC_CHANNEL_LAYOUT_MONO = 2, - - /** Stereo. - * Two channels: left, right. - */ - ROC_CHANNEL_LAYOUT_STEREO = 3, + ROC_CHANNEL_LAYOUT_MULTITRACK = 4 } roc_channel_layout; /** Media encoding. * Defines format and parameters of samples encoded in frames or packets. */ typedef struct roc_media_encoding { + /** Sample format. + * Defines sample binary representation. + * May place limitations to allowed rates, bits, and channels. + */ + roc_format format; + + /** Sample bit depth. + * Defines number of bits per sample (e.g. 16). + * Allowed values may be limited by \c format. + */ + unsigned int bits; + /** Sample frequency. * Defines number of samples per channel per second (e.g. 44100). + * Allowed values may be limited by \c format. */ unsigned int rate; - /** Sample format. - * Defines sample precision and encoding. - */ - roc_format format; - /** Channel layout. * Defines number of channels and meaning of each channel. + * Allowed values may be limited by \c format. */ roc_channel_layout channels; diff --git a/src/public_api/include/roc/plugin.h b/src/public_api/include/roc/plugin.h index b65d27507..d86c7f06f 100644 --- a/src/public_api/include/roc/plugin.h +++ b/src/public_api/include/roc/plugin.h @@ -104,8 +104,8 @@ enum { * arbitrary values, unless it's known that only certain packet encoding * may be used by sender * - * - \c format is always \ref ROC_FORMAT_PCM_FLOAT32, PLC plugin doesn't - * need to support other formats + * - \c format is always \ref ROC_FORMAT_PCM_IEEE_FLOAT and \c bits is 32. + * PLC plugin doesn't need to support other formats. * * **Registration** * diff --git a/src/public_api/src/adapters.cpp b/src/public_api/src/adapters.cpp index 7bccf5ad8..1291835d6 100644 --- a/src/public_api/src/adapters.cpp +++ b/src/public_api/src/adapters.cpp @@ -13,7 +13,7 @@ #include "roc_address/interface.h" #include "roc_audio/channel_defs.h" #include "roc_audio/freq_estimator.h" -#include "roc_audio/pcm_format.h" +#include "roc_audio/pcm_subformat.h" #include "roc_audio/resampler_config.h" #include "roc_core/attributes.h" #include "roc_core/log.h" @@ -311,6 +311,10 @@ ROC_ATTR_NO_SANITIZE_UB bool sample_spec_from_user(audio::SampleSpec& out, const roc_media_encoding& in, bool is_network) { + if (!sample_format_from_user(out, in, is_network)) { + return false; + } + if (in.rate != 0) { out.set_sample_rate(in.rate); } else { @@ -320,13 +324,6 @@ bool sample_spec_from_user(audio::SampleSpec& out, return false; } - if (!sample_format_from_user(out, in.format, is_network)) { - roc_log(LogError, - "bad configuration: invalid roc_media_encoding.format:" - " should be valid enum value"); - return false; - } - if (in.channels != 0) { if (in.channels == ROC_CHANNEL_LAYOUT_MULTITRACK) { if (in.tracks == 0) { @@ -371,17 +368,19 @@ bool sample_spec_from_user(audio::SampleSpec& out, bool sample_spec_to_user(roc_media_encoding& out, const audio::SampleSpec& in) { memset(&out, 0, sizeof(out)); - if (!in.is_valid()) { + if (!in.is_complete()) { + roc_log(LogError, "bad configuration: invalid sample spec"); return false; } - out.rate = (unsigned int)in.sample_rate(); - - if (!sample_format_to_user(out.format, in)) { + if (!sample_format_to_user(out, in)) { return false; } + out.rate = (unsigned int)in.sample_rate(); + if (!channel_set_to_user(out.channels, out.tracks, in.channel_set())) { + roc_log(LogError, "bad configuration: unsupported channel set"); return false; } @@ -389,38 +388,187 @@ bool sample_spec_to_user(roc_media_encoding& out, const audio::SampleSpec& in) { } ROC_ATTR_NO_SANITIZE_UB -bool sample_format_from_user(audio::SampleSpec& out, roc_format in, bool is_network) { - switch (enum_from_user(in)) { - case ROC_FORMAT_PCM_FLOAT32: - out.set_sample_format(audio::SampleFormat_Pcm); - // TODO(gh-608): use PcmFormat_Float32_Be instead of PcmFormat_SInt16_Be - out.set_pcm_format(is_network ? audio::PcmFormat_SInt16_Be - : audio::PcmFormat_Float32); - return true; - } +bool sample_format_from_user(audio::SampleSpec& out, + const roc_media_encoding& in, + bool is_network) { + out.set_format(audio::Format_Invalid); + out.set_pcm_subformat(audio::PcmSubformat_Invalid); + + switch (enum_from_user(in.format)) { + case ROC_FORMAT_PCM_IEEE_FLOAT: { + out.set_format(audio::Format_Pcm); + + switch (in.bits) { + case 32: + out.set_pcm_subformat(audio::PcmSubformat_Float32); + break; + case 64: + out.set_pcm_subformat(audio::PcmSubformat_Float64); + break; + default: + roc_log(LogError, + "bad configuration: invalid roc_media_encoding.bits:" + " ROC_FORMAT_PCM_IEEE_FLOAT doesn't support specified bits count: %u", + (unsigned)in.bits); + return false; + } - return false; -} + break; + } break; // ROC_FORMAT_PCM_IEEE_FLOAT + + case ROC_FORMAT_PCM_SIGNED_INT: { + out.set_format(audio::Format_Pcm); + + switch (in.bits) { + case 8: + out.set_pcm_subformat(audio::PcmSubformat_SInt8); + break; + case 16: + out.set_pcm_subformat(audio::PcmSubformat_SInt16); + break; + case 24: + out.set_pcm_subformat(audio::PcmSubformat_SInt24); + break; + case 32: + out.set_pcm_subformat(audio::PcmSubformat_SInt32); + break; + case 64: + out.set_pcm_subformat(audio::PcmSubformat_SInt64); + break; + default: + roc_log(LogError, + "bad configuration: invalid roc_media_encoding.bits:" + " ROC_FORMAT_PCM_SIGNED_INT doesn't support specified bits count: %u", + (unsigned)in.bits); + return false; + } -bool sample_format_to_user(roc_format& out, const audio::SampleSpec& in) { - if (in.sample_format() != audio::SampleFormat_Pcm) { - return false; + break; + } break; // ROC_FORMAT_PCM_SIGNED_INT + + case ROC_FORMAT_PCM_UNSIGNED_INT: { + out.set_format(audio::Format_Pcm); + + switch (in.bits) { + case 8: + out.set_pcm_subformat(audio::PcmSubformat_UInt8); + break; + case 16: + out.set_pcm_subformat(audio::PcmSubformat_UInt16); + break; + case 24: + out.set_pcm_subformat(audio::PcmSubformat_UInt24); + break; + case 32: + out.set_pcm_subformat(audio::PcmSubformat_UInt32); + break; + case 64: + out.set_pcm_subformat(audio::PcmSubformat_UInt64); + break; + default: + roc_log( + LogError, + "bad configuration: invalid roc_media_encoding.bits:" + " ROC_FORMAT_PCM_UNSIGNED_INT doesn't support specified bits count: %u", + (unsigned)in.bits); + return false; + } + + break; + } break; // ROC_FORMAT_PCM_SIGNED_INT } - const audio::PcmTraits traits = audio::pcm_format_traits(in.pcm_format()); - if (!traits.has_flags(audio::Pcm_IsNative)) { + if (out.format() == audio::Format_Invalid) { + roc_log(LogError, + "bad configuration: invalid roc_media_encoding.format:" + " should be enum value"); return false; } - switch (traits.default_variant) { - case audio::PcmFormat_Float32: - out = ROC_FORMAT_PCM_FLOAT32; - return true; + if (out.is_pcm() && is_network) { + // Switch to big endian. + const audio::PcmTraits traits = audio::pcm_subformat_traits(out.pcm_subformat()); + out.set_pcm_subformat(traits.be_variant); + } + + return true; +} + +bool sample_format_to_user(roc_media_encoding& out, const audio::SampleSpec& in) { + switch (in.format()) { + case audio::Format_Pcm: { + const audio::PcmTraits traits = audio::pcm_subformat_traits(in.pcm_subformat()); + if (!traits.has_flags(audio::Pcm_IsNative)) { + roc_log(LogError, "bad configuration: unsupported pcm endian"); + return false; + } + + switch (traits.default_variant) { + case audio::PcmSubformat_SInt8: + out.format = ROC_FORMAT_PCM_SIGNED_INT; + out.bits = 8; + return true; + case audio::PcmSubformat_UInt8: + out.format = ROC_FORMAT_PCM_UNSIGNED_INT; + out.bits = 8; + return true; + + case audio::PcmSubformat_SInt16: + out.format = ROC_FORMAT_PCM_SIGNED_INT; + out.bits = 16; + return true; + case audio::PcmSubformat_UInt16: + out.format = ROC_FORMAT_PCM_UNSIGNED_INT; + out.bits = 16; + return true; + + case audio::PcmSubformat_SInt24: + out.format = ROC_FORMAT_PCM_SIGNED_INT; + out.bits = 24; + return true; + case audio::PcmSubformat_UInt24: + out.format = ROC_FORMAT_PCM_UNSIGNED_INT; + out.bits = 24; + return true; + + case audio::PcmSubformat_SInt32: + out.format = ROC_FORMAT_PCM_SIGNED_INT; + out.bits = 32; + return true; + case audio::PcmSubformat_UInt32: + out.format = ROC_FORMAT_PCM_UNSIGNED_INT; + out.bits = 32; + return true; + + case audio::PcmSubformat_SInt64: + out.format = ROC_FORMAT_PCM_SIGNED_INT; + out.bits = 64; + return true; + case audio::PcmSubformat_UInt64: + out.format = ROC_FORMAT_PCM_UNSIGNED_INT; + out.bits = 64; + return true; + + case audio::PcmSubformat_Float32: + out.format = ROC_FORMAT_PCM_IEEE_FLOAT; + out.bits = 32; + return true; + case audio::PcmSubformat_Float64: + out.format = ROC_FORMAT_PCM_IEEE_FLOAT; + out.bits = 64; + return true; + + default: + roc_log(LogError, "bad configuration: unsupported pcm format"); + return false; + } + } break; // audio::SampleFormat_Pcm default: break; } + roc_log(LogError, "bad configuration: unsupported sample format"); return false; } diff --git a/src/public_api/src/adapters.h b/src/public_api/src/adapters.h index ea52dc6ee..5e78ba509 100644 --- a/src/public_api/src/adapters.h +++ b/src/public_api/src/adapters.h @@ -38,8 +38,10 @@ bool sample_spec_from_user(audio::SampleSpec& out, bool is_network); bool sample_spec_to_user(roc_media_encoding& out, const audio::SampleSpec& in); -bool sample_format_from_user(audio::SampleSpec& out, roc_format in, bool is_network); -bool sample_format_to_user(roc_format& out, const audio::SampleSpec& in); +bool sample_format_from_user(audio::SampleSpec& out, + const roc_media_encoding& in, + bool is_network); +bool sample_format_to_user(roc_media_encoding& out, const audio::SampleSpec& in); bool channel_set_from_user(audio::ChannelSet& out, roc_channel_layout in_layout, diff --git a/src/tests/public_api/test_context.cpp b/src/tests/public_api/test_context.cpp index 284cd3955..43b9cb7a3 100644 --- a/src/tests/public_api/test_context.cpp +++ b/src/tests/public_api/test_context.cpp @@ -55,8 +55,9 @@ TEST(context, reference_counting) { { roc_sender_config sender_config; memset(&sender_config, 0, sizeof(sender_config)); + sender_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + sender_config.frame_encoding.bits = 32; sender_config.frame_encoding.rate = 44100; - sender_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; sender_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; sender_config.packet_encoding = ROC_PACKET_ENCODING_AVP_L16_STEREO; @@ -69,8 +70,9 @@ TEST(context, reference_counting) { { roc_receiver_config receiver_config; memset(&receiver_config, 0, sizeof(receiver_config)); + receiver_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + receiver_config.frame_encoding.bits = 32; receiver_config.frame_encoding.rate = 44100; - receiver_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; receiver_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; roc_receiver* receiver = NULL; CHECK(roc_receiver_open(context, &receiver_config, &receiver) == 0); diff --git a/src/tests/public_api/test_helpers/context.h b/src/tests/public_api/test_helpers/context.h index 1edf1417e..c3e211d7c 100644 --- a/src/tests/public_api/test_helpers/context.h +++ b/src/tests/public_api/test_helpers/context.h @@ -44,11 +44,28 @@ class Context : public core::NonCopyable<> { return ctx_; } - void register_multitrack_encoding(int encoding_id, unsigned num_tracks) { + void register_custom_encoding(int encoding_id, + roc_format format, + unsigned bits, + unsigned rate, + roc_channel_layout channels) { roc_media_encoding encoding; memset(&encoding, 0, sizeof(encoding)); - encoding.rate = SampleRate; - encoding.format = ROC_FORMAT_PCM_FLOAT32; + encoding.format = format; + encoding.bits = bits; + encoding.rate = rate; + encoding.channels = channels; + + CHECK(roc_context_register_encoding(ctx_, encoding_id, &encoding) == 0); + } + + void + register_multitrack_encoding(int encoding_id, unsigned rate, unsigned num_tracks) { + roc_media_encoding encoding; + memset(&encoding, 0, sizeof(encoding)); + encoding.format = ROC_FORMAT_PCM_SIGNED_INT; + encoding.bits = 16; + encoding.rate = rate; encoding.channels = ROC_CHANNEL_LAYOUT_MULTITRACK; encoding.tracks = num_tracks; diff --git a/src/tests/public_api/test_helpers/utils.h b/src/tests/public_api/test_helpers/utils.h index f571e72ba..bf5edc08d 100644 --- a/src/tests/public_api/test_helpers/utils.h +++ b/src/tests/public_api/test_helpers/utils.h @@ -18,8 +18,6 @@ namespace { enum { MaxBufSize = 5120, - SampleRate = 44100, - SourcePackets = 10, RepairPackets = 7, diff --git a/src/tests/public_api/test_loopback_encoder_2_decoder.cpp b/src/tests/public_api/test_loopback_encoder_2_decoder.cpp index b527a9287..206f48558 100644 --- a/src/tests/public_api/test_loopback_encoder_2_decoder.cpp +++ b/src/tests/public_api/test_loopback_encoder_2_decoder.cpp @@ -26,6 +26,7 @@ namespace api { namespace { enum { + SampleRate = 44100, NoFlags = 0, FlagLosses = (1 << 0), }; @@ -46,23 +47,23 @@ TEST_GROUP(loopback_encoder_2_decoder) { CHECK(context); memset(&sender_conf, 0, sizeof(sender_conf)); - sender_conf.frame_encoding.rate = test::SampleRate; - sender_conf.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; + sender_conf.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + sender_conf.frame_encoding.bits = 32; + sender_conf.frame_encoding.rate = SampleRate; sender_conf.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; sender_conf.packet_encoding = ROC_PACKET_ENCODING_AVP_L16_STEREO; - sender_conf.packet_length = - test::PacketSamples * 1000000000ull / test::SampleRate; + sender_conf.packet_length = test::PacketSamples * 1000000000ull / SampleRate; sender_conf.clock_source = ROC_CLOCK_SOURCE_INTERNAL; memset(&receiver_conf, 0, sizeof(receiver_conf)); - receiver_conf.frame_encoding.rate = test::SampleRate; - receiver_conf.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; + receiver_conf.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + receiver_conf.frame_encoding.bits = 32; + receiver_conf.frame_encoding.rate = SampleRate; receiver_conf.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; receiver_conf.clock_source = ROC_CLOCK_SOURCE_INTERNAL; receiver_conf.latency_tuner_profile = ROC_LATENCY_TUNER_PROFILE_INTACT; - receiver_conf.target_latency = test::Latency * 1000000000ull / test::SampleRate; - receiver_conf.no_playback_timeout = - test::Timeout * 1000000000ull / test::SampleRate; + receiver_conf.target_latency = test::Latency * 1000000000ull / SampleRate; + receiver_conf.no_playback_timeout = test::Timeout * 1000000000ull / SampleRate; } void teardown() { diff --git a/src/tests/public_api/test_loopback_sender_2_receiver.cpp b/src/tests/public_api/test_loopback_sender_2_receiver.cpp index bac067904..939b722be 100644 --- a/src/tests/public_api/test_loopback_sender_2_receiver.cpp +++ b/src/tests/public_api/test_loopback_sender_2_receiver.cpp @@ -31,11 +31,12 @@ TEST_GROUP(loopback_sender_2_receiver) { sample_step = 1. / 32768.; } - void init_config(unsigned flags, unsigned frame_chans, unsigned packet_chans, - int encoding_id = 0) { + void init_config(unsigned flags, unsigned sample_rate, unsigned frame_chans, + unsigned packet_chans, int encoding_id = 0) { memset(&sender_conf, 0, sizeof(sender_conf)); - sender_conf.frame_encoding.rate = test::SampleRate; - sender_conf.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; + sender_conf.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + sender_conf.frame_encoding.bits = 32; + sender_conf.frame_encoding.rate = sample_rate; if (flags & test::FlagMultitrack) { sender_conf.frame_encoding.channels = ROC_CHANNEL_LAYOUT_MULTITRACK; @@ -67,8 +68,7 @@ TEST_GROUP(loopback_sender_2_receiver) { sender_conf.packet_encoding = (roc_packet_encoding)encoding_id; } - sender_conf.packet_length = - test::PacketSamples * 1000000000ull / test::SampleRate; + sender_conf.packet_length = test::PacketSamples * 1000000000ull / sample_rate; sender_conf.clock_source = ROC_CLOCK_SOURCE_INTERNAL; if (flags & test::FlagRS8M) { @@ -84,8 +84,9 @@ TEST_GROUP(loopback_sender_2_receiver) { } memset(&receiver_conf, 0, sizeof(receiver_conf)); - receiver_conf.frame_encoding.rate = test::SampleRate; - receiver_conf.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; + receiver_conf.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + receiver_conf.frame_encoding.bits = 32; + receiver_conf.frame_encoding.rate = sample_rate; if (flags & test::FlagMultitrack) { receiver_conf.frame_encoding.channels = ROC_CHANNEL_LAYOUT_MULTITRACK; @@ -105,9 +106,8 @@ TEST_GROUP(loopback_sender_2_receiver) { receiver_conf.clock_source = ROC_CLOCK_SOURCE_INTERNAL; receiver_conf.latency_tuner_profile = ROC_LATENCY_TUNER_PROFILE_INTACT; - receiver_conf.target_latency = test::Latency * 1000000000ull / test::SampleRate; - receiver_conf.no_playback_timeout = - test::Timeout * 1000000000ull / test::SampleRate; + receiver_conf.target_latency = test::Latency * 1000000000ull / sample_rate; + receiver_conf.no_playback_timeout = test::Timeout * 1000000000ull / sample_rate; } bool is_rs8m_supported() { @@ -120,9 +120,9 @@ TEST_GROUP(loopback_sender_2_receiver) { }; TEST(loopback_sender_2_receiver, bare_rtp) { - enum { Flags = 0, FrameChans = 2, PacketChans = 2 }; + enum { Flags = 0, SampleRate = 44100, FrameChans = 2, PacketChans = 2 }; - init_config(Flags, FrameChans, PacketChans); + init_config(Flags, SampleRate, FrameChans, PacketChans); test::Context context; @@ -143,9 +143,9 @@ TEST(loopback_sender_2_receiver, bare_rtp) { } TEST(loopback_sender_2_receiver, rtp_rtcp) { - enum { Flags = test::FlagRTCP, FrameChans = 2, PacketChans = 2 }; + enum { Flags = test::FlagRTCP, SampleRate = 44100, FrameChans = 2, PacketChans = 2 }; - init_config(Flags, FrameChans, PacketChans); + init_config(Flags, SampleRate, FrameChans, PacketChans); test::Context context; @@ -170,9 +170,9 @@ TEST(loopback_sender_2_receiver, rs8m_without_losses) { return; } - enum { Flags = test::FlagRS8M, FrameChans = 2, PacketChans = 2 }; + enum { Flags = test::FlagRS8M, SampleRate = 44100, FrameChans = 2, PacketChans = 2 }; - init_config(Flags, FrameChans, PacketChans); + init_config(Flags, SampleRate, FrameChans, PacketChans); test::Context context; @@ -199,11 +199,12 @@ TEST(loopback_sender_2_receiver, rs8m_with_losses) { enum { Flags = test::FlagRS8M | test::FlagLoseSomePkts, + SampleRate = 44100, FrameChans = 2, PacketChans = 2 }; - init_config(Flags, FrameChans, PacketChans); + init_config(Flags, SampleRate, FrameChans, PacketChans); test::Context context; @@ -233,9 +234,9 @@ TEST(loopback_sender_2_receiver, ldpc_without_losses) { return; } - enum { Flags = test::FlagLDPC, FrameChans = 2, PacketChans = 2 }; + enum { Flags = test::FlagLDPC, SampleRate = 44100, FrameChans = 2, PacketChans = 2 }; - init_config(Flags, FrameChans, PacketChans); + init_config(Flags, SampleRate, FrameChans, PacketChans); test::Context context; @@ -262,11 +263,12 @@ TEST(loopback_sender_2_receiver, ldpc_with_losses) { enum { Flags = test::FlagLDPC | test::FlagLoseSomePkts, + SampleRate = 44100, FrameChans = 2, PacketChans = 2 }; - init_config(Flags, FrameChans, PacketChans); + init_config(Flags, SampleRate, FrameChans, PacketChans); test::Context context; @@ -292,9 +294,9 @@ TEST(loopback_sender_2_receiver, ldpc_with_losses) { } TEST(loopback_sender_2_receiver, separate_context) { - enum { Flags = 0, FrameChans = 2, PacketChans = 2 }; + enum { Flags = 0, SampleRate = 44100, FrameChans = 2, PacketChans = 2 }; - init_config(Flags, FrameChans, PacketChans); + init_config(Flags, SampleRate, FrameChans, PacketChans); test::Context recv_context, send_context; @@ -315,9 +317,9 @@ TEST(loopback_sender_2_receiver, separate_context) { } TEST(loopback_sender_2_receiver, multiple_senders_one_receiver_sequential) { - enum { Flags = 0, FrameChans = 2, PacketChans = 2 }; + enum { Flags = 0, SampleRate = 44100, FrameChans = 2, PacketChans = 2 }; - init_config(Flags, FrameChans, PacketChans); + init_config(Flags, SampleRate, FrameChans, PacketChans); test::Context context; @@ -350,9 +352,16 @@ TEST(loopback_sender_2_receiver, multiple_senders_one_receiver_sequential) { } TEST(loopback_sender_2_receiver, sender_slots) { - enum { Flags = 0, FrameChans = 2, PacketChans = 2, Slot1 = 1, Slot2 = 2 }; + enum { + Flags = 0, + SampleRate = 44100, + FrameChans = 2, + PacketChans = 2, + Slot1 = 1, + Slot2 = 2 + }; - init_config(Flags, FrameChans, PacketChans); + init_config(Flags, SampleRate, FrameChans, PacketChans); test::Context context; @@ -384,9 +393,16 @@ TEST(loopback_sender_2_receiver, sender_slots) { } TEST(loopback_sender_2_receiver, receiver_slots_sequential) { - enum { Flags = 0, FrameChans = 2, PacketChans = 2, Slot1 = 1, Slot2 = 2 }; + enum { + Flags = 0, + SampleRate = 44100, + FrameChans = 2, + PacketChans = 2, + Slot1 = 1, + Slot2 = 2 + }; - init_config(Flags, FrameChans, PacketChans); + init_config(Flags, SampleRate, FrameChans, PacketChans); test::Context context; @@ -420,9 +436,9 @@ TEST(loopback_sender_2_receiver, receiver_slots_sequential) { } TEST(loopback_sender_2_receiver, mono) { - enum { Flags = 0, FrameChans = 1, PacketChans = 1 }; + enum { Flags = 0, SampleRate = 44100, FrameChans = 1, PacketChans = 1 }; - init_config(Flags, FrameChans, PacketChans); + init_config(Flags, SampleRate, FrameChans, PacketChans); test::Context context; @@ -443,9 +459,9 @@ TEST(loopback_sender_2_receiver, mono) { } TEST(loopback_sender_2_receiver, stereo_mono_stereo) { - enum { Flags = 0, FrameChans = 2, PacketChans = 1 }; + enum { Flags = 0, SampleRate = 44100, FrameChans = 2, PacketChans = 1 }; - init_config(Flags, FrameChans, PacketChans); + init_config(Flags, SampleRate, FrameChans, PacketChans); test::Context context; @@ -466,9 +482,9 @@ TEST(loopback_sender_2_receiver, stereo_mono_stereo) { } TEST(loopback_sender_2_receiver, mono_stereo_mono) { - enum { Flags = 0, FrameChans = 1, PacketChans = 2 }; + enum { Flags = 0, SampleRate = 44100, FrameChans = 1, PacketChans = 2 }; - init_config(Flags, FrameChans, PacketChans); + init_config(Flags, SampleRate, FrameChans, PacketChans); test::Context context; @@ -488,19 +504,93 @@ TEST(loopback_sender_2_receiver, mono_stereo_mono) { sender.join(); } +TEST(loopback_sender_2_receiver, custom_encoding) { + enum { + Flags = 0, + SampleRate = 48000, + BitWidth = 24, + FrameChans = 1, + PacketChans = 2, + EncodingID = 100 + }; + + init_config(Flags, SampleRate, FrameChans, PacketChans, EncodingID); + + test::Context context; + + context.register_custom_encoding(EncodingID, ROC_FORMAT_PCM_UNSIGNED_INT, BitWidth, + SampleRate, ROC_CHANNEL_LAYOUT_STEREO); + + test::Receiver receiver(context, receiver_conf, sample_step, FrameChans, + test::FrameSamples, Flags); + + receiver.bind(); + + test::Sender sender(context, sender_conf, sample_step, FrameChans, test::FrameSamples, + Flags); + + sender.connect(receiver.source_endpoint(), NULL, NULL); + + CHECK(sender.start()); + receiver.receive(); + sender.stop(); + sender.join(); +} + +TEST(loopback_sender_2_receiver, custom_encoding_separate_contextx) { + enum { + Flags = 0, + SampleRate = 48000, + BitWidth = 24, + FrameChans = 1, + PacketChans = 2, + EncodingID = 100 + }; + + init_config(Flags, SampleRate, FrameChans, PacketChans, EncodingID); + + test::Context recv_context; + + recv_context.register_custom_encoding(EncodingID, ROC_FORMAT_PCM_UNSIGNED_INT, + BitWidth, SampleRate, + ROC_CHANNEL_LAYOUT_STEREO); + + test::Receiver receiver(recv_context, receiver_conf, sample_step, FrameChans, + test::FrameSamples, Flags); + + receiver.bind(); + + test::Context send_context; + + send_context.register_custom_encoding(EncodingID, ROC_FORMAT_PCM_UNSIGNED_INT, + BitWidth, SampleRate, + ROC_CHANNEL_LAYOUT_STEREO); + + test::Sender sender(send_context, sender_conf, sample_step, FrameChans, + test::FrameSamples, Flags); + + sender.connect(receiver.source_endpoint(), NULL, NULL); + + CHECK(sender.start()); + receiver.receive(); + sender.stop(); + sender.join(); +} + TEST(loopback_sender_2_receiver, multitrack) { enum { Flags = test::FlagMultitrack, + SampleRate = 44100, FrameChans = 4, PacketChans = 4, EncodingID = 100 }; - init_config(Flags, FrameChans, PacketChans, EncodingID); + init_config(Flags, SampleRate, FrameChans, PacketChans, EncodingID); test::Context context; - context.register_multitrack_encoding(EncodingID, PacketChans); + context.register_multitrack_encoding(EncodingID, SampleRate, PacketChans); test::Receiver receiver(context, receiver_conf, sample_step, FrameChans, test::FrameSamples, Flags); @@ -521,17 +611,18 @@ TEST(loopback_sender_2_receiver, multitrack) { TEST(loopback_sender_2_receiver, multitrack_separate_contexts) { enum { Flags = test::FlagMultitrack, + SampleRate = 44100, FrameChans = 4, PacketChans = 4, EncodingID = 100 }; - init_config(Flags, FrameChans, PacketChans, EncodingID); + init_config(Flags, SampleRate, FrameChans, PacketChans, EncodingID); test::Context recv_context, send_context; - recv_context.register_multitrack_encoding(EncodingID, PacketChans); - send_context.register_multitrack_encoding(EncodingID, PacketChans); + recv_context.register_multitrack_encoding(EncodingID, SampleRate, PacketChans); + send_context.register_multitrack_encoding(EncodingID, SampleRate, PacketChans); test::Receiver receiver(recv_context, receiver_conf, sample_step, FrameChans, test::FrameSamples, Flags); @@ -553,12 +644,13 @@ TEST(loopback_sender_2_receiver, multitrack_separate_contexts) { TEST(loopback_sender_2_receiver, metrics_measurements) { enum { Flags = test::FlagNonStrict | test::FlagInfinite | test::FlagRTCP, + SampleRate = 44100, FrameChans = 2, PacketChans = 2, MaxSess = 10 }; - init_config(Flags, FrameChans, PacketChans); + init_config(Flags, SampleRate, FrameChans, PacketChans); test::Context context; @@ -651,12 +743,13 @@ TEST(loopback_sender_2_receiver, metrics_measurements) { TEST(loopback_sender_2_receiver, metrics_connections) { enum { Flags = test::FlagNonStrict | test::FlagInfinite | test::FlagRTCP, + SampleRate = 44100, FrameChans = 2, PacketChans = 2, MaxSess = 10 }; - init_config(Flags, FrameChans, PacketChans); + init_config(Flags, SampleRate, FrameChans, PacketChans); test::Context context; @@ -757,6 +850,7 @@ TEST(loopback_sender_2_receiver, metrics_connections) { TEST(loopback_sender_2_receiver, metrics_connections_slots) { enum { Flags = test::FlagNonStrict | test::FlagInfinite | test::FlagRTCP, + SampleRate = 44100, FrameChans = 2, PacketChans = 2, MaxSess = 10, @@ -764,7 +858,7 @@ TEST(loopback_sender_2_receiver, metrics_connections_slots) { Slot2 = 2 }; - init_config(Flags, FrameChans, PacketChans); + init_config(Flags, SampleRate, FrameChans, PacketChans); test::Context context; diff --git a/src/tests/public_api/test_plugin_plc.cpp b/src/tests/public_api/test_plugin_plc.cpp index 3f4cf52e1..9c8e6142d 100644 --- a/src/tests/public_api/test_plugin_plc.cpp +++ b/src/tests/public_api/test_plugin_plc.cpp @@ -26,6 +26,7 @@ namespace api { namespace { enum { + SampleRate = 44100, Magic = 123456789, NumChans = 2, LookaheadSamples = 10, @@ -75,8 +76,9 @@ void* test_plc_new(roc_plugin_plc* plugin, const roc_media_encoding* encoding) { roc_panic_if_not(plugin); roc_panic_if_not(encoding); - roc_panic_if_not(encoding->format == ROC_FORMAT_PCM_FLOAT32); - roc_panic_if_not(encoding->rate == test::SampleRate); + roc_panic_if_not(encoding->format == ROC_FORMAT_PCM_IEEE_FLOAT); + roc_panic_if_not(encoding->bits == 32); + roc_panic_if_not(encoding->rate == SampleRate); roc_panic_if_not(encoding->channels == ROC_CHANNEL_LAYOUT_STEREO); return new TestPlc((TestPlugin*)plugin); @@ -182,13 +184,13 @@ TEST_GROUP(plugin_plc) { void setup() { memset(&sender_conf, 0, sizeof(sender_conf)); - sender_conf.frame_encoding.rate = test::SampleRate; - sender_conf.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; + sender_conf.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + sender_conf.frame_encoding.bits = 32; + sender_conf.frame_encoding.rate = SampleRate; sender_conf.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; sender_conf.packet_encoding = ROC_PACKET_ENCODING_AVP_L16_STEREO; - sender_conf.packet_length = - test::PacketSamples * 1000000000ull / test::SampleRate; + sender_conf.packet_length = test::PacketSamples * 1000000000ull / SampleRate; sender_conf.fec_encoding = ROC_FEC_ENCODING_RS8M; sender_conf.fec_block_source_packets = test::SourcePackets; @@ -197,8 +199,9 @@ TEST_GROUP(plugin_plc) { sender_conf.clock_source = ROC_CLOCK_SOURCE_INTERNAL; memset(&receiver_conf, 0, sizeof(receiver_conf)); - receiver_conf.frame_encoding.rate = test::SampleRate; - receiver_conf.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; + receiver_conf.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + receiver_conf.frame_encoding.bits = 32; + receiver_conf.frame_encoding.rate = SampleRate; receiver_conf.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; receiver_conf.clock_source = ROC_CLOCK_SOURCE_INTERNAL; @@ -207,11 +210,11 @@ TEST_GROUP(plugin_plc) { receiver_conf.plc_backend = (roc_plc_backend)PluginID; receiver_conf.latency_tuner_profile = ROC_LATENCY_TUNER_PROFILE_INTACT; - receiver_conf.target_latency = test::Latency * 1000000000ull / test::SampleRate; + receiver_conf.target_latency = test::Latency * 1000000000ull / SampleRate; receiver_conf.latency_tolerance = - test::Latency * 1000000000ull / test::SampleRate * 10000; + test::Latency * 1000000000ull / SampleRate * 10000; receiver_conf.no_playback_timeout = - test::Timeout * 1000000000ull / test::SampleRate * 10000; + test::Timeout * 1000000000ull / SampleRate * 10000; } bool is_rs8m_supported() { diff --git a/src/tests/public_api/test_receiver.cpp b/src/tests/public_api/test_receiver.cpp index a655f2a47..6e30de2f4 100644 --- a/src/tests/public_api/test_receiver.cpp +++ b/src/tests/public_api/test_receiver.cpp @@ -30,8 +30,9 @@ TEST_GROUP(receiver) { CHECK(context); memset(&receiver_config, 0, sizeof(receiver_config)); + receiver_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + receiver_config.frame_encoding.bits = 32; receiver_config.frame_encoding.rate = 44100; - receiver_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; receiver_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; } diff --git a/src/tests/public_api/test_receiver_decoder.cpp b/src/tests/public_api/test_receiver_decoder.cpp index 10718abf5..78714eff5 100644 --- a/src/tests/public_api/test_receiver_decoder.cpp +++ b/src/tests/public_api/test_receiver_decoder.cpp @@ -32,13 +32,15 @@ TEST_GROUP(receiver_decoder) { CHECK(context); memset(&receiver_config, 0, sizeof(receiver_config)); + receiver_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + receiver_config.frame_encoding.bits = 32; receiver_config.frame_encoding.rate = 44100; - receiver_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; receiver_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; memset(&sender_config, 0, sizeof(sender_config)); + sender_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + sender_config.frame_encoding.bits = 32; sender_config.frame_encoding.rate = 44100; - sender_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; sender_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; sender_config.packet_encoding = ROC_PACKET_ENCODING_AVP_L16_STEREO; sender_config.fec_encoding = ROC_FEC_ENCODING_DISABLE; diff --git a/src/tests/public_api/test_sender.cpp b/src/tests/public_api/test_sender.cpp index bd811dc59..db5c426c8 100644 --- a/src/tests/public_api/test_sender.cpp +++ b/src/tests/public_api/test_sender.cpp @@ -30,8 +30,9 @@ TEST_GROUP(sender) { CHECK(context); memset(&sender_config, 0, sizeof(sender_config)); + sender_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + sender_config.frame_encoding.bits = 32; sender_config.frame_encoding.rate = 44100; - sender_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; sender_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; sender_config.packet_encoding = ROC_PACKET_ENCODING_AVP_L16_STEREO; sender_config.fec_encoding = ROC_FEC_ENCODING_DISABLE; diff --git a/src/tests/public_api/test_sender_encoder.cpp b/src/tests/public_api/test_sender_encoder.cpp index 16c1e014f..d4932e911 100644 --- a/src/tests/public_api/test_sender_encoder.cpp +++ b/src/tests/public_api/test_sender_encoder.cpp @@ -30,8 +30,9 @@ TEST_GROUP(sender_encoder) { CHECK(context); memset(&sender_config, 0, sizeof(sender_config)); + sender_config.frame_encoding.format = ROC_FORMAT_PCM_IEEE_FLOAT; + sender_config.frame_encoding.bits = 32; sender_config.frame_encoding.rate = 44100; - sender_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32; sender_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO; sender_config.packet_encoding = ROC_PACKET_ENCODING_AVP_L16_STEREO; sender_config.fec_encoding = ROC_FEC_ENCODING_DISABLE; diff --git a/src/tests/roc_audio/test_channel_mapper_reader.cpp b/src/tests/roc_audio/test_channel_mapper_reader.cpp index b16004449..e3272756b 100644 --- a/src/tests/roc_audio/test_channel_mapper_reader.cpp +++ b/src/tests/roc_audio/test_channel_mapper_reader.cpp @@ -115,9 +115,9 @@ TEST_GROUP(channel_mapper_reader) {}; TEST(channel_mapper_reader, small_read_upmix) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec in_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); - const SampleSpec out_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec out_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); const core::nanoseconds_t start_ts = 1000000; @@ -146,9 +146,9 @@ TEST(channel_mapper_reader, small_read_upmix) { TEST(channel_mapper_reader, small_read_downmix) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec in_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); - const SampleSpec out_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec out_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); const core::nanoseconds_t start_cts = 1000000; @@ -177,9 +177,9 @@ TEST(channel_mapper_reader, small_read_downmix) { TEST(channel_mapper_reader, small_read_no_cts) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec in_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); - const SampleSpec out_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec out_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); test::MockReader mock_reader(frame_factory, in_spec); @@ -205,9 +205,9 @@ TEST(channel_mapper_reader, small_read_no_cts) { // Request big frame when upmixing. // Duration is capped so that both input and output frames could fit max size. TEST(channel_mapper_reader, big_read_upmix) { - const SampleSpec in_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); - const SampleSpec out_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec out_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); const core::nanoseconds_t start_cts = 1000000; @@ -247,9 +247,9 @@ TEST(channel_mapper_reader, big_read_upmix) { // Request big frame when downmixing. // Duration is capped so that both input and output frames could fit max size. TEST(channel_mapper_reader, big_read_downmix) { - const SampleSpec in_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); - const SampleSpec out_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec out_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); const core::nanoseconds_t start_cts = 1000000; @@ -289,9 +289,9 @@ TEST(channel_mapper_reader, big_read_downmix) { // Same as above, but input frames don't have CTS // (because we don't call enable_timestamps). TEST(channel_mapper_reader, big_read_no_cts) { - const SampleSpec in_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); - const SampleSpec out_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec out_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); test::MockReader mock_reader(frame_factory, in_spec); @@ -324,9 +324,9 @@ TEST(channel_mapper_reader, big_read_no_cts) { TEST(channel_mapper_reader, forward_mode) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec in_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); - const SampleSpec out_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec out_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); test::MockReader mock_reader(frame_factory, in_spec); @@ -352,9 +352,9 @@ TEST(channel_mapper_reader, forward_mode) { TEST(channel_mapper_reader, forward_error) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec in_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); - const SampleSpec out_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec out_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); test::MockReader mock_reader(frame_factory, in_spec); @@ -378,9 +378,9 @@ TEST(channel_mapper_reader, forward_error) { TEST(channel_mapper_reader, forward_partial) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec in_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); - const SampleSpec out_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec out_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); test::MockReader mock_reader(frame_factory, in_spec); @@ -402,9 +402,9 @@ TEST(channel_mapper_reader, forward_partial) { TEST(channel_mapper_reader, preallocated_buffer) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec in_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); - const SampleSpec out_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec out_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); const size_t buffer_list[] = { diff --git a/src/tests/roc_audio/test_channel_mapper_writer.cpp b/src/tests/roc_audio/test_channel_mapper_writer.cpp index 2408a5607..b440bb963 100644 --- a/src/tests/roc_audio/test_channel_mapper_writer.cpp +++ b/src/tests/roc_audio/test_channel_mapper_writer.cpp @@ -98,9 +98,9 @@ TEST_GROUP(channel_mapper_writer) {}; TEST(channel_mapper_writer, small_write_upmix) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec in_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); - const SampleSpec out_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec out_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); test::MockWriter mock_writer; @@ -128,9 +128,9 @@ TEST(channel_mapper_writer, small_write_upmix) { TEST(channel_mapper_writer, small_write_downmix) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec in_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); - const SampleSpec out_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec out_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); test::MockWriter mock_writer; @@ -158,9 +158,9 @@ TEST(channel_mapper_writer, small_write_downmix) { TEST(channel_mapper_writer, small_write_no_cts) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec in_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); - const SampleSpec out_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec out_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); test::MockWriter mock_writer; @@ -187,9 +187,9 @@ TEST(channel_mapper_writer, small_write_no_cts) { // Write big frame when upmixing. // It should be split into multiple writes to fit maximum size. TEST(channel_mapper_writer, big_write_upmix) { - const SampleSpec in_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); - const SampleSpec out_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec out_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); test::MockWriter mock_writer; @@ -222,9 +222,9 @@ TEST(channel_mapper_writer, big_write_upmix) { // Write big frame when downmixing. // It should be split into multiple writes to fit maximum size. TEST(channel_mapper_writer, big_write_downmix) { - const SampleSpec in_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); - const SampleSpec out_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec out_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); test::MockWriter mock_writer; @@ -256,9 +256,9 @@ TEST(channel_mapper_writer, big_write_downmix) { // Same as above, but input frames don't have CTS. TEST(channel_mapper_writer, big_write_no_cts) { - const SampleSpec in_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); - const SampleSpec out_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec out_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); test::MockWriter mock_writer; @@ -288,9 +288,9 @@ TEST(channel_mapper_writer, big_write_no_cts) { TEST(channel_mapper_writer, forward_error) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec in_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); - const SampleSpec out_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec out_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); test::MockWriter mock_writer; diff --git a/src/tests/roc_audio/test_channel_set.cpp b/src/tests/roc_audio/test_channel_set.cpp index d1a3ec252..a345b745c 100644 --- a/src/tests/roc_audio/test_channel_set.cpp +++ b/src/tests/roc_audio/test_channel_set.cpp @@ -567,44 +567,42 @@ TEST(channel_set, to_string) { { ChannelSet ch_set; - STRCMP_EQUAL("<none n_ch=0>", channel_set_to_str(ch_set).c_str()); + STRCMP_EQUAL("<none 0 none>", channel_set_to_str(ch_set).c_str()); } { ChannelSet ch_set; ch_set.set_layout(ChanLayout_Surround); ch_set.set_order(ChanOrder_Smpte); - STRCMP_EQUAL("<surround smpte n_ch=0>", channel_set_to_str(ch_set).c_str()); + STRCMP_EQUAL("<surround smpte 0 none>", channel_set_to_str(ch_set).c_str()); } { ChannelSet ch_set(ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); - STRCMP_EQUAL("<surround smpte n_ch=1 ch=FC>", channel_set_to_str(ch_set).c_str()); + STRCMP_EQUAL("<surround smpte 1 FC>", channel_set_to_str(ch_set).c_str()); } { ChannelSet ch_set(ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); - STRCMP_EQUAL("<surround smpte n_ch=2 ch=FL,FR>", - channel_set_to_str(ch_set).c_str()); + STRCMP_EQUAL("<surround smpte 2 FL,FR>", channel_set_to_str(ch_set).c_str()); } { ChannelSet ch_set(ChanLayout_Surround, ChanOrder_Alsa, ChanMask_Surround_Stereo); - STRCMP_EQUAL("<surround alsa n_ch=2 ch=FL,FR>", - channel_set_to_str(ch_set).c_str()); + STRCMP_EQUAL("<surround alsa 2 FL,FR>", channel_set_to_str(ch_set).c_str()); } { ChannelSet ch_set; ch_set.set_layout(ChanLayout_Multitrack); - STRCMP_EQUAL("<multitrack n_ch=0>", channel_set_to_str(ch_set).c_str()); + STRCMP_EQUAL("<multitrack 0 none>", channel_set_to_str(ch_set).c_str()); } { ChannelSet ch_set; ch_set.set_layout(ChanLayout_Multitrack); ch_set.set_range(0, 7); - STRCMP_EQUAL("<multitrack n_ch=8 ch=0xFF>", channel_set_to_str(ch_set).c_str()); + STRCMP_EQUAL("<multitrack 8 0xFF>", channel_set_to_str(ch_set).c_str()); } { ChannelSet ch_set; @@ -614,7 +612,7 @@ TEST(channel_set, to_string) { ch_set.toggle_channel(5, true); ch_set.toggle_channel(7, true); - STRCMP_EQUAL("<multitrack n_ch=4 ch=0xAC>", channel_set_to_str(ch_set).c_str()); + STRCMP_EQUAL("<multitrack 4 0xAC>", channel_set_to_str(ch_set).c_str()); } { ChannelSet ch_set; @@ -624,7 +622,7 @@ TEST(channel_set, to_string) { ch_set.toggle_channel(85, true); ch_set.toggle_channel(87, true); - STRCMP_EQUAL("<multitrack n_ch=4 ch=0xA00000000000000000000C>", + STRCMP_EQUAL("<multitrack 4 0xA00000000000000000000C>", channel_set_to_str(ch_set).c_str()); } } diff --git a/src/tests/roc_audio/test_depacketizer.cpp b/src/tests/roc_audio/test_depacketizer.cpp index 14f0f8a22..d24edd48c 100644 --- a/src/tests/roc_audio/test_depacketizer.cpp +++ b/src/tests/roc_audio/test_depacketizer.cpp @@ -37,10 +37,10 @@ enum { }; const SampleSpec frame_spec( - SampleRate, Sample_RawFormat, ChanLayout_Surround, ChanOrder_Smpte, ChMask); + SampleRate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); const SampleSpec packet_spec( - SampleRate, PcmFormat_SInt16_Be, ChanLayout_Surround, ChanOrder_Smpte, ChMask); + SampleRate, PcmSubformat_SInt16_Be, ChanLayout_Surround, ChanOrder_Smpte, ChMask); const core::nanoseconds_t NsPerPacket = packet_spec.samples_per_chan_2_ns(SamplesPerPacket); diff --git a/src/tests/roc_audio/test_fanout.cpp b/src/tests/roc_audio/test_fanout.cpp index 6f494d5c3..86c396be5 100644 --- a/src/tests/roc_audio/test_fanout.cpp +++ b/src/tests/roc_audio/test_fanout.cpp @@ -23,7 +23,7 @@ namespace { enum { BufSz = 100, MaxSz = 500 }; const SampleSpec sample_spec(44100, - Sample_RawFormat, + PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); diff --git a/src/tests/roc_audio/test_frame_encoder_decoder.cpp b/src/tests/roc_audio/test_frame_encoder_decoder.cpp index 8ceca9173..5b2d310db 100644 --- a/src/tests/roc_audio/test_frame_encoder_decoder.cpp +++ b/src/tests/roc_audio/test_frame_encoder_decoder.cpp @@ -11,7 +11,7 @@ #include "roc_audio/frame_factory.h" #include "roc_audio/pcm_decoder.h" #include "roc_audio/pcm_encoder.h" -#include "roc_audio/pcm_format.h" +#include "roc_audio/pcm_subformat.h" #include "roc_core/heap_arena.h" #include "roc_core/scoped_ptr.h" @@ -51,25 +51,25 @@ IFrameEncoder* new_encoder(size_t id) { switch (id) { case Codec_PCM_SInt16_1ch: return new (arena) - PcmEncoder(SampleSpec(SampleRate, PcmFormat_SInt16_Be, ChanLayout_Surround, + PcmEncoder(SampleSpec(SampleRate, PcmSubformat_SInt16_Be, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono), arena); case Codec_PCM_SInt16_2ch: return new (arena) - PcmEncoder(SampleSpec(SampleRate, PcmFormat_SInt16_Be, ChanLayout_Surround, + PcmEncoder(SampleSpec(SampleRate, PcmSubformat_SInt16_Be, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo), arena); case Codec_PCM_SInt24_1ch: return new (arena) - PcmEncoder(SampleSpec(SampleRate, PcmFormat_SInt24_Be, ChanLayout_Surround, + PcmEncoder(SampleSpec(SampleRate, PcmSubformat_SInt24_Be, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono), arena); case Codec_PCM_SInt24_2ch: return new (arena) - PcmEncoder(SampleSpec(SampleRate, PcmFormat_SInt24_Be, ChanLayout_Surround, + PcmEncoder(SampleSpec(SampleRate, PcmSubformat_SInt24_Be, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo), arena); @@ -84,25 +84,25 @@ IFrameDecoder* new_decoder(size_t id) { switch (id) { case Codec_PCM_SInt16_1ch: return new (arena) - PcmDecoder(SampleSpec(SampleRate, PcmFormat_SInt16_Be, ChanLayout_Surround, + PcmDecoder(SampleSpec(SampleRate, PcmSubformat_SInt16_Be, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono), arena); case Codec_PCM_SInt16_2ch: return new (arena) - PcmDecoder(SampleSpec(SampleRate, PcmFormat_SInt16_Be, ChanLayout_Surround, + PcmDecoder(SampleSpec(SampleRate, PcmSubformat_SInt16_Be, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo), arena); case Codec_PCM_SInt24_1ch: return new (arena) - PcmDecoder(SampleSpec(SampleRate, PcmFormat_SInt24_Be, ChanLayout_Surround, + PcmDecoder(SampleSpec(SampleRate, PcmSubformat_SInt24_Be, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono), arena); case Codec_PCM_SInt24_2ch: return new (arena) - PcmDecoder(SampleSpec(SampleRate, PcmFormat_SInt24_Be, ChanLayout_Surround, + PcmDecoder(SampleSpec(SampleRate, PcmSubformat_SInt24_Be, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo), arena); diff --git a/src/tests/roc_audio/test_freq_estimator.cpp b/src/tests/roc_audio/test_freq_estimator.cpp index 4fe53e53d..12df71863 100644 --- a/src/tests/roc_audio/test_freq_estimator.cpp +++ b/src/tests/roc_audio/test_freq_estimator.cpp @@ -24,7 +24,7 @@ const LatencyTunerProfile profile_list[] = { LatencyTunerProfile_Responsive, LatencyTunerProfile_Gradual }; const SampleSpec sample_spec(44100, - Sample_RawFormat, + PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); diff --git a/src/tests/roc_audio/test_mixer.cpp b/src/tests/roc_audio/test_mixer.cpp index 61b05a7df..64ba00d17 100644 --- a/src/tests/roc_audio/test_mixer.cpp +++ b/src/tests/roc_audio/test_mixer.cpp @@ -22,7 +22,7 @@ namespace { enum { BufSz = 100, MaxBufSz = 500, SampleRate = 44100 }; const SampleSpec sample_spec(SampleRate, - Sample_RawFormat, + PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); @@ -333,7 +333,7 @@ TEST(mixer, clamp) { TEST(mixer, cts_one_reader) { // BufSz samples per second - const SampleSpec sample_spec(BufSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(BufSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); const core::nanoseconds_t start_ts = 1000000000000; @@ -362,7 +362,7 @@ TEST(mixer, cts_one_reader) { TEST(mixer, cts_two_readers) { // BufSz samples per second - const SampleSpec sample_spec(BufSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(BufSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); const core::nanoseconds_t start_ts1 = 2000000000000; const core::nanoseconds_t start_ts2 = 1000000000000; @@ -400,7 +400,7 @@ TEST(mixer, cts_two_readers) { TEST(mixer, cts_partial) { // BufSz samples per second - const SampleSpec sample_spec(BufSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(BufSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); const core::nanoseconds_t start_ts1 = 2000000000000; const core::nanoseconds_t start_ts2 = 1000000000000; @@ -445,7 +445,7 @@ TEST(mixer, cts_partial) { TEST(mixer, cts_prevent_overflow) { // BufSz samples per second - const SampleSpec sample_spec(BufSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(BufSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); const core::nanoseconds_t start_ts1 = 9000000000000000000ll; const core::nanoseconds_t start_ts2 = 9100000000000000000ll; @@ -487,7 +487,7 @@ TEST(mixer, cts_prevent_overflow) { } TEST(mixer, cts_disabled) { - const SampleSpec sample_spec(BufSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(BufSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); const core::nanoseconds_t start_ts = 1000000000000; @@ -735,7 +735,7 @@ TEST(mixer, soft_read_partial_end_two_readers) { // Soft reads and capture timestamps. TEST(mixer, soft_read_cts) { // BufSz samples per second - const SampleSpec sample_spec(BufSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(BufSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); const core::nanoseconds_t start_ts = 1000000000000; @@ -766,7 +766,7 @@ TEST(mixer, soft_read_cts) { // Soft reads and capture timestamps. TEST(mixer, soft_read_cts_partial) { // BufSz samples per second - const SampleSpec sample_spec(BufSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(BufSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); const core::nanoseconds_t start_ts = 1000000000000; @@ -798,7 +798,7 @@ TEST(mixer, soft_read_cts_partial) { // Same as above, but there are two readers, and one returns StatusDrain. TEST(mixer, soft_read_cts_two_readers) { // BufSz samples per second - const SampleSpec sample_spec(BufSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(BufSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); const core::nanoseconds_t start_ts1 = 2000000000000; const core::nanoseconds_t start_ts2 = 1000000000000; diff --git a/src/tests/roc_audio/test_packetizer.cpp b/src/tests/roc_audio/test_packetizer.cpp index af4dd03d0..61e071525 100644 --- a/src/tests/roc_audio/test_packetizer.cpp +++ b/src/tests/roc_audio/test_packetizer.cpp @@ -44,10 +44,10 @@ const core::nanoseconds_t PacketDuration = SamplesPerPacket * core::Second / Sam const core::nanoseconds_t Now = 1691499037871419405; const SampleSpec frame_spec( - SampleRate, Sample_RawFormat, ChanLayout_Surround, ChanOrder_Smpte, ChMask); + SampleRate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); const SampleSpec packet_spec( - SampleRate, PcmFormat_SInt16_Be, ChanLayout_Surround, ChanOrder_Smpte, ChMask); + SampleRate, PcmSubformat_SInt16_Be, ChanLayout_Surround, ChanOrder_Smpte, ChMask); core::HeapArena arena; packet::PacketFactory packet_factory(arena, MaxBufSize); diff --git a/src/tests/roc_audio/test_pcm_mapper.cpp b/src/tests/roc_audio/test_pcm_mapper.cpp index 073e78932..e14552225 100644 --- a/src/tests/roc_audio/test_pcm_mapper.cpp +++ b/src/tests/roc_audio/test_pcm_mapper.cpp @@ -29,8 +29,8 @@ void map(const void* input, size_t in_bytes, size_t out_bytes, size_t n_samples, - PcmFormat in_fmt, - PcmFormat out_fmt) { + PcmSubformat in_fmt, + PcmSubformat out_fmt) { PcmMapper mapper(in_fmt, out_fmt); UNSIGNED_LONGS_EQUAL(n_samples, mapper.input_sample_count(in_bytes)); @@ -108,7 +108,7 @@ TEST(pcm_mapper, raw_to_raw) { sample_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - Sample_RawFormat, Sample_RawFormat); + PcmSubformat_Raw, PcmSubformat_Raw); compare(expected_output, actual_output, NumSamples); } @@ -126,7 +126,7 @@ TEST(pcm_mapper, raw_to_int8) { int8_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - Sample_RawFormat, PcmFormat_SInt8); + PcmSubformat_Raw, PcmSubformat_SInt8); compare(expected_output, actual_output, NumSamples); } @@ -144,7 +144,7 @@ TEST(pcm_mapper, int8_to_raw) { sample_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - PcmFormat_SInt8, Sample_RawFormat); + PcmSubformat_SInt8, PcmSubformat_Raw); compare(expected_output, actual_output, NumSamples); } @@ -162,7 +162,7 @@ TEST(pcm_mapper, raw_to_int16) { int16_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - Sample_RawFormat, PcmFormat_SInt16); + PcmSubformat_Raw, PcmSubformat_SInt16); compare(expected_output, actual_output, NumSamples); } @@ -180,7 +180,7 @@ TEST(pcm_mapper, int16_to_raw) { sample_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - PcmFormat_SInt16, Sample_RawFormat); + PcmSubformat_SInt16, PcmSubformat_Raw); compare(expected_output, actual_output, NumSamples); } @@ -208,7 +208,7 @@ TEST(pcm_mapper, raw_to_int32) { int32_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - Sample_RawFormat, PcmFormat_SInt32); + PcmSubformat_Raw, PcmSubformat_SInt32); compare(expected_output, actual_output, NumSamples); } @@ -238,7 +238,7 @@ TEST(pcm_mapper, int32_to_raw) { float actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - PcmFormat_SInt32, Sample_RawFormat); + PcmSubformat_SInt32, PcmSubformat_Raw); compare(expected_output, actual_output, NumSamples); } @@ -264,7 +264,7 @@ TEST(pcm_mapper, raw_to_int64) { int64_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - Sample_RawFormat, PcmFormat_SInt64); + PcmSubformat_Raw, PcmSubformat_SInt64); compare(expected_output, actual_output, NumSamples); } @@ -290,7 +290,7 @@ TEST(pcm_mapper, int64_to_raw) { sample_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - PcmFormat_SInt64, Sample_RawFormat); + PcmSubformat_SInt64, PcmSubformat_Raw); compare(expected_output, actual_output, NumSamples); } @@ -308,7 +308,7 @@ TEST(pcm_mapper, raw_to_float32) { float actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - Sample_RawFormat, PcmFormat_Float32); + PcmSubformat_Raw, PcmSubformat_Float32); compare(expected_output, actual_output, NumSamples); } @@ -326,7 +326,7 @@ TEST(pcm_mapper, float32_to_raw) { sample_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - PcmFormat_Float32, Sample_RawFormat); + PcmSubformat_Float32, PcmSubformat_Raw); compare(expected_output, actual_output, NumSamples); } @@ -344,7 +344,7 @@ TEST(pcm_mapper, raw_to_float64) { double actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - Sample_RawFormat, PcmFormat_Float64); + PcmSubformat_Raw, PcmSubformat_Float64); compare(expected_output, actual_output, NumSamples); } @@ -362,7 +362,7 @@ TEST(pcm_mapper, float64_to_raw) { sample_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - PcmFormat_Float64, Sample_RawFormat); + PcmSubformat_Float64, PcmSubformat_Raw); compare(expected_output, actual_output, NumSamples); } @@ -392,7 +392,7 @@ TEST(pcm_mapper, raw_to_uint16) { uint16_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - Sample_RawFormat, PcmFormat_UInt16); + PcmSubformat_Raw, PcmSubformat_UInt16); compare(expected_output, actual_output, NumSamples); } @@ -418,7 +418,7 @@ TEST(pcm_mapper, uint16_to_raw) { sample_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - PcmFormat_UInt16, Sample_RawFormat); + PcmSubformat_UInt16, PcmSubformat_Raw); compare(expected_output, actual_output, NumSamples); } @@ -446,7 +446,7 @@ TEST(pcm_mapper, raw_to_uint32) { uint32_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - Sample_RawFormat, PcmFormat_UInt32); + PcmSubformat_Raw, PcmSubformat_UInt32); compare(expected_output, actual_output, NumSamples); } @@ -476,7 +476,7 @@ TEST(pcm_mapper, uint32_to_raw) { sample_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - PcmFormat_UInt32, Sample_RawFormat); + PcmSubformat_UInt32, PcmSubformat_Raw); compare(expected_output, actual_output, NumSamples); } @@ -501,7 +501,7 @@ TEST(pcm_mapper, raw_to_int16be) { uint8_t actual_output[NumOutputBytes] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - Sample_RawFormat, PcmFormat_SInt16_Be); + PcmSubformat_Raw, PcmSubformat_SInt16_Be); compare(expected_output, actual_output, NumOutputBytes); } @@ -526,7 +526,7 @@ TEST(pcm_mapper, raw_to_int16le) { uint8_t actual_output[NumOutputBytes] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - Sample_RawFormat, PcmFormat_SInt16_Le); + PcmSubformat_Raw, PcmSubformat_SInt16_Le); compare(expected_output, actual_output, NumOutputBytes); } @@ -548,7 +548,7 @@ TEST(pcm_mapper, int16be_to_raw) { sample_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - PcmFormat_SInt16_Be, Sample_RawFormat); + PcmSubformat_SInt16_Be, PcmSubformat_Raw); compare(expected_output, actual_output, NumSamples); } @@ -570,7 +570,7 @@ TEST(pcm_mapper, int16le_to_raw) { sample_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - PcmFormat_SInt16_Le, Sample_RawFormat); + PcmSubformat_SInt16_Le, PcmSubformat_Raw); compare(expected_output, actual_output, NumSamples); } @@ -595,7 +595,7 @@ TEST(pcm_mapper, raw_to_int18b4be) { uint8_t actual_output[NumOutputBytes] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - Sample_RawFormat, PcmFormat_SInt18_4_Be); + PcmSubformat_Raw, PcmSubformat_SInt18_4_Be); compare(expected_output, actual_output, NumOutputBytes); } @@ -620,7 +620,7 @@ TEST(pcm_mapper, int18b4be_to_raw) { sample_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - PcmFormat_SInt18_4_Be, Sample_RawFormat); + PcmSubformat_SInt18_4_Be, PcmSubformat_Raw); compare(expected_output, actual_output, NumSamples); } @@ -645,7 +645,7 @@ TEST(pcm_mapper, raw_to_int20b3be) { uint8_t actual_output[NumOutputBytes] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - Sample_RawFormat, PcmFormat_SInt20_3_Be); + PcmSubformat_Raw, PcmSubformat_SInt20_3_Be); compare(expected_output, actual_output, NumOutputBytes); } @@ -667,7 +667,7 @@ TEST(pcm_mapper, int20b3be_to_raw) { sample_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - PcmFormat_SInt20_3_Be, Sample_RawFormat); + PcmSubformat_SInt20_3_Be, PcmSubformat_Raw); compare(expected_output, actual_output, NumSamples); } @@ -692,7 +692,7 @@ TEST(pcm_mapper, raw_to_int20b4be) { uint8_t actual_output[NumOutputBytes] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - Sample_RawFormat, PcmFormat_SInt20_4_Be); + PcmSubformat_Raw, PcmSubformat_SInt20_4_Be); compare(expected_output, actual_output, NumOutputBytes); } @@ -714,7 +714,7 @@ TEST(pcm_mapper, int20b4be_to_raw) { sample_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - PcmFormat_SInt20_4_Be, Sample_RawFormat); + PcmSubformat_SInt20_4_Be, PcmSubformat_Raw); compare(expected_output, actual_output, NumSamples); } @@ -739,7 +739,7 @@ TEST(pcm_mapper, raw_to_int24be) { uint8_t actual_output[NumOutputBytes] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - Sample_RawFormat, PcmFormat_SInt24_Be); + PcmSubformat_Raw, PcmSubformat_SInt24_Be); compare(expected_output, actual_output, NumOutputBytes); } @@ -761,7 +761,7 @@ TEST(pcm_mapper, int24be_to_raw) { sample_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - PcmFormat_SInt24_Be, Sample_RawFormat); + PcmSubformat_SInt24_Be, PcmSubformat_Raw); compare(expected_output, actual_output, NumSamples); } @@ -786,7 +786,7 @@ TEST(pcm_mapper, raw_to_int24b4be) { uint8_t actual_output[NumOutputBytes] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - Sample_RawFormat, PcmFormat_SInt24_4_Be); + PcmSubformat_Raw, PcmSubformat_SInt24_4_Be); compare(expected_output, actual_output, NumOutputBytes); } @@ -808,7 +808,7 @@ TEST(pcm_mapper, int24b4be_to_raw) { sample_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - PcmFormat_SInt24_4_Be, Sample_RawFormat); + PcmSubformat_SInt24_4_Be, PcmSubformat_Raw); compare(expected_output, actual_output, NumSamples); } @@ -843,7 +843,7 @@ TEST(pcm_mapper, raw_to_int20be) { uint8_t actual_output[NumOutputBytes] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - Sample_RawFormat, PcmFormat_SInt20_Be); + PcmSubformat_Raw, PcmSubformat_SInt20_Be); compare(expected_output, actual_output, NumOutputBytes); } @@ -875,7 +875,7 @@ TEST(pcm_mapper, int20be_to_raw) { sample_t actual_output[NumSamples] = {}; map(input, actual_output, sizeof(input), sizeof(actual_output), NumSamples, - PcmFormat_SInt20_Be, Sample_RawFormat); + PcmSubformat_SInt20_Be, PcmSubformat_Raw); compare(expected_output, actual_output, NumSamples); } diff --git a/src/tests/roc_audio/test_pcm_mapper_reader.cpp b/src/tests/roc_audio/test_pcm_mapper_reader.cpp index 977da14fe..15947a285 100644 --- a/src/tests/roc_audio/test_pcm_mapper_reader.cpp +++ b/src/tests/roc_audio/test_pcm_mapper_reader.cpp @@ -8,8 +8,8 @@ #include <CppUTest/TestHarness.h> -#include "roc_audio/pcm_format.h" #include "roc_audio/pcm_mapper_reader.h" +#include "roc_audio/pcm_subformat.h" #include "roc_core/heap_arena.h" #include "roc_core/macro_helpers.h" #include "roc_core/time.h" @@ -206,9 +206,9 @@ TEST_GROUP(pcm_mapper_reader) {}; TEST(pcm_mapper_reader, mono_raw_to_raw) { enum { FrameSz = MaxBytes / 10 }; - const SampleSpec in_spec(Rate, Sample_RawFormat, ChanLayout_Surround, ChanOrder_Smpte, + const SampleSpec in_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); - const SampleSpec out_spec(Rate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec out_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); CountReader<sample_t> count_reader(in_spec, 0.001f); @@ -231,9 +231,9 @@ TEST(pcm_mapper_reader, mono_raw_to_raw) { TEST(pcm_mapper_reader, mono_s16_to_raw) { enum { FrameSz = MaxBytes / 10 }; - const SampleSpec in_spec(Rate, PcmFormat_SInt16, ChanLayout_Surround, ChanOrder_Smpte, - ChanMask_Surround_Mono); - const SampleSpec out_spec(Rate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(Rate, PcmSubformat_SInt16, ChanLayout_Surround, + ChanOrder_Smpte, ChanMask_Surround_Mono); + const SampleSpec out_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); CountReader<int16_t> count_reader(in_spec, 100); @@ -256,9 +256,9 @@ TEST(pcm_mapper_reader, mono_s16_to_raw) { TEST(pcm_mapper_reader, mono_raw_to_s16) { enum { FrameSz = MaxBytes / 10 }; - const SampleSpec in_spec(Rate, Sample_RawFormat, ChanLayout_Surround, ChanOrder_Smpte, + const SampleSpec in_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); - const SampleSpec out_spec(Rate, PcmFormat_SInt16, ChanLayout_Surround, + const SampleSpec out_spec(Rate, PcmSubformat_SInt16, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); CountReader<sample_t> count_reader(in_spec, 0.001f); @@ -281,9 +281,9 @@ TEST(pcm_mapper_reader, mono_raw_to_s16) { TEST(pcm_mapper_reader, stereo_s16_to_raw) { enum { FrameSz = MaxBytes / 10 }; - const SampleSpec in_spec(Rate, PcmFormat_SInt16, ChanLayout_Surround, ChanOrder_Smpte, - ChanMask_Surround_Stereo); - const SampleSpec out_spec(Rate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(Rate, PcmSubformat_SInt16, ChanLayout_Surround, + ChanOrder_Smpte, ChanMask_Surround_Stereo); + const SampleSpec out_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); CountReader<int16_t> count_reader(in_spec, 100); @@ -306,9 +306,9 @@ TEST(pcm_mapper_reader, stereo_s16_to_raw) { TEST(pcm_mapper_reader, stereo_raw_to_s16) { enum { FrameSz = MaxBytes / 10 }; - const SampleSpec in_spec(Rate, Sample_RawFormat, ChanLayout_Surround, ChanOrder_Smpte, + const SampleSpec in_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); - const SampleSpec out_spec(Rate, PcmFormat_SInt16, ChanLayout_Surround, + const SampleSpec out_spec(Rate, PcmSubformat_SInt16, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); CountReader<sample_t> count_reader(in_spec, 0.001f); @@ -336,9 +336,9 @@ TEST(pcm_mapper_reader, big_read_s16_to_raw) { MaxFrameSz = MaxBytes / ROC_MAX(sizeof(int16_t), sizeof(sample_t)) }; - const SampleSpec in_spec(Rate, PcmFormat_SInt16, ChanLayout_Surround, ChanOrder_Smpte, - ChanMask_Surround_Mono); - const SampleSpec out_spec(Rate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(Rate, PcmSubformat_SInt16, ChanLayout_Surround, + ChanOrder_Smpte, ChanMask_Surround_Mono); + const SampleSpec out_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); CountReader<int16_t> count_reader(in_spec, 10); @@ -370,9 +370,9 @@ TEST(pcm_mapper_reader, big_read_raw_to_s16) { MaxFrameSz = MaxBytes / ROC_MAX(sizeof(int16_t), sizeof(sample_t)) }; - const SampleSpec in_spec(Rate, Sample_RawFormat, ChanLayout_Surround, ChanOrder_Smpte, + const SampleSpec in_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); - const SampleSpec out_spec(Rate, PcmFormat_SInt16, ChanLayout_Surround, + const SampleSpec out_spec(Rate, PcmSubformat_SInt16, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); CountReader<sample_t> count_reader(in_spec, 0.001f); @@ -401,9 +401,9 @@ TEST(pcm_mapper_reader, big_read_raw_to_s16) { TEST(pcm_mapper_reader, forward_flags) { enum { MaxFrameSz = MaxBytes / sizeof(sample_t) }; - const SampleSpec in_spec(Rate, Sample_RawFormat, ChanLayout_Surround, ChanOrder_Smpte, + const SampleSpec in_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); - const SampleSpec out_spec(Rate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec out_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); MetaReader meta_reader(in_spec); @@ -428,9 +428,9 @@ TEST(pcm_mapper_reader, forward_flags) { TEST(pcm_mapper_reader, forward_capture_timestamp) { enum { MaxFrameSz = MaxBytes / sizeof(sample_t) }; - const SampleSpec in_spec(Rate, Sample_RawFormat, ChanLayout_Surround, ChanOrder_Smpte, + const SampleSpec in_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); - const SampleSpec out_spec(Rate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec out_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); MetaReader meta_reader(in_spec); @@ -455,9 +455,9 @@ TEST(pcm_mapper_reader, forward_capture_timestamp) { TEST(pcm_mapper_reader, forward_mode) { enum { FrameSz = MaxBytes / 10 }; - const SampleSpec in_spec(Rate, PcmFormat_SInt16, ChanLayout_Surround, ChanOrder_Smpte, - ChanMask_Surround_Mono); - const SampleSpec out_spec(Rate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(Rate, PcmSubformat_SInt16, ChanLayout_Surround, + ChanOrder_Smpte, ChanMask_Surround_Mono); + const SampleSpec out_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); MetaReader meta_reader(in_spec); @@ -481,9 +481,9 @@ TEST(pcm_mapper_reader, forward_mode) { TEST(pcm_mapper_reader, forward_error) { enum { FrameSz = MaxBytes / 10 }; - const SampleSpec in_spec(Rate, PcmFormat_SInt16, ChanLayout_Surround, ChanOrder_Smpte, - ChanMask_Surround_Mono); - const SampleSpec out_spec(Rate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(Rate, PcmSubformat_SInt16, ChanLayout_Surround, + ChanOrder_Smpte, ChanMask_Surround_Mono); + const SampleSpec out_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); MetaReader meta_reader(in_spec); @@ -507,9 +507,9 @@ TEST(pcm_mapper_reader, forward_error) { TEST(pcm_mapper_reader, forward_partial) { enum { FrameSz = MaxBytes / 10 }; - const SampleSpec in_spec(Rate, PcmFormat_SInt16, ChanLayout_Surround, ChanOrder_Smpte, - ChanMask_Surround_Mono); - const SampleSpec out_spec(Rate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(Rate, PcmSubformat_SInt16, ChanLayout_Surround, + ChanOrder_Smpte, ChanMask_Surround_Mono); + const SampleSpec out_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); CountReader<int16_t> count_reader(in_spec, 100); @@ -531,9 +531,9 @@ TEST(pcm_mapper_reader, forward_partial) { TEST(pcm_mapper_reader, preallocated_buffer) { enum { FrameSz = MaxBytes / 10 }; - const SampleSpec in_spec(Rate, PcmFormat_SInt16, ChanLayout_Surround, ChanOrder_Smpte, - ChanMask_Surround_Mono); - const SampleSpec out_spec(Rate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(Rate, PcmSubformat_SInt16, ChanLayout_Surround, + ChanOrder_Smpte, ChanMask_Surround_Mono); + const SampleSpec out_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); const size_t buffer_list[] = { diff --git a/src/tests/roc_audio/test_pcm_mapper_writer.cpp b/src/tests/roc_audio/test_pcm_mapper_writer.cpp index f0fce4849..e453f1478 100644 --- a/src/tests/roc_audio/test_pcm_mapper_writer.cpp +++ b/src/tests/roc_audio/test_pcm_mapper_writer.cpp @@ -8,8 +8,8 @@ #include <CppUTest/TestHarness.h> -#include "roc_audio/pcm_format.h" #include "roc_audio/pcm_mapper_writer.h" +#include "roc_audio/pcm_subformat.h" #include "roc_core/heap_arena.h" #include "roc_core/macro_helpers.h" #include "roc_core/time.h" @@ -191,9 +191,9 @@ TEST_GROUP(pcm_mapper_writer) {}; TEST(pcm_mapper_writer, mono_raw_to_raw) { enum { FrameSz = MaxBytes / 10 }; - const SampleSpec in_spec(Rate, Sample_RawFormat, ChanLayout_Surround, ChanOrder_Smpte, + const SampleSpec in_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); - const SampleSpec out_spec(Rate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec out_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); BufferWriter<sample_t> buf_writer(out_spec); @@ -214,9 +214,9 @@ TEST(pcm_mapper_writer, mono_raw_to_raw) { TEST(pcm_mapper_writer, mono_s16_to_raw) { enum { FrameSz = MaxBytes / 10 }; - const SampleSpec in_spec(Rate, PcmFormat_SInt16, ChanLayout_Surround, ChanOrder_Smpte, - ChanMask_Surround_Mono); - const SampleSpec out_spec(Rate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(Rate, PcmSubformat_SInt16, ChanLayout_Surround, + ChanOrder_Smpte, ChanMask_Surround_Mono); + const SampleSpec out_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); BufferWriter<sample_t> buf_writer(out_spec); @@ -237,9 +237,9 @@ TEST(pcm_mapper_writer, mono_s16_to_raw) { TEST(pcm_mapper_writer, mono_raw_to_s16) { enum { FrameSz = MaxBytes / 10 }; - const SampleSpec in_spec(Rate, Sample_RawFormat, ChanLayout_Surround, ChanOrder_Smpte, + const SampleSpec in_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); - const SampleSpec out_spec(Rate, PcmFormat_SInt16, ChanLayout_Surround, + const SampleSpec out_spec(Rate, PcmSubformat_SInt16, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); BufferWriter<int16_t> buf_writer(out_spec); @@ -260,9 +260,9 @@ TEST(pcm_mapper_writer, mono_raw_to_s16) { TEST(pcm_mapper_writer, stereo_s16_to_raw) { enum { FrameSz = MaxBytes / 10 }; - const SampleSpec in_spec(Rate, PcmFormat_SInt16, ChanLayout_Surround, ChanOrder_Smpte, - ChanMask_Surround_Stereo); - const SampleSpec out_spec(Rate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(Rate, PcmSubformat_SInt16, ChanLayout_Surround, + ChanOrder_Smpte, ChanMask_Surround_Stereo); + const SampleSpec out_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); BufferWriter<sample_t> buf_writer(out_spec); @@ -283,9 +283,9 @@ TEST(pcm_mapper_writer, stereo_s16_to_raw) { TEST(pcm_mapper_writer, stereo_raw_to_s16) { enum { FrameSz = MaxBytes / 10 }; - const SampleSpec in_spec(Rate, Sample_RawFormat, ChanLayout_Surround, ChanOrder_Smpte, + const SampleSpec in_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); - const SampleSpec out_spec(Rate, PcmFormat_SInt16, ChanLayout_Surround, + const SampleSpec out_spec(Rate, PcmSubformat_SInt16, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); BufferWriter<int16_t> buf_writer(out_spec); @@ -308,9 +308,9 @@ TEST(pcm_mapper_writer, stereo_raw_to_s16) { TEST(pcm_mapper_writer, big_write_s16_to_raw) { enum { IterCount = 20, SplitCount = 5, MaxFrameSz = MaxBytes / sizeof(sample_t) }; - const SampleSpec in_spec(Rate, PcmFormat_SInt16, ChanLayout_Surround, ChanOrder_Smpte, - ChanMask_Surround_Mono); - const SampleSpec out_spec(Rate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(Rate, PcmSubformat_SInt16, ChanLayout_Surround, + ChanOrder_Smpte, ChanMask_Surround_Mono); + const SampleSpec out_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); BufferWriter<sample_t> buf_writer(out_spec); @@ -335,9 +335,9 @@ TEST(pcm_mapper_writer, big_write_s16_to_raw) { TEST(pcm_mapper_writer, big_write_raw_to_s16) { enum { IterCount = 20, SplitCount = 5, MaxFrameSz = MaxBytes / sizeof(int16_t) }; - const SampleSpec in_spec(Rate, Sample_RawFormat, ChanLayout_Surround, ChanOrder_Smpte, + const SampleSpec in_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); - const SampleSpec out_spec(Rate, PcmFormat_SInt16, ChanLayout_Surround, + const SampleSpec out_spec(Rate, PcmSubformat_SInt16, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); BufferWriter<int16_t> buf_writer(out_spec); @@ -363,9 +363,9 @@ TEST(pcm_mapper_writer, big_write_raw_to_s16) { TEST(pcm_mapper_writer, forward_flags) { enum { MaxFrameSz = MaxBytes / sizeof(int16_t) }; - const SampleSpec in_spec(Rate, PcmFormat_SInt16, ChanLayout_Surround, ChanOrder_Smpte, - ChanMask_Surround_Mono); - const SampleSpec out_spec(Rate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(Rate, PcmSubformat_SInt16, ChanLayout_Surround, + ChanOrder_Smpte, ChanMask_Surround_Mono); + const SampleSpec out_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); MetaWriter meta_writer(out_spec); @@ -391,9 +391,9 @@ TEST(pcm_mapper_writer, forward_flags) { TEST(pcm_mapper_writer, forward_capture_timestamp) { enum { MaxFrameSz = MaxBytes / sizeof(int16_t) }; - const SampleSpec in_spec(Rate, PcmFormat_SInt16, ChanLayout_Surround, ChanOrder_Smpte, - ChanMask_Surround_Mono); - const SampleSpec out_spec(Rate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(Rate, PcmSubformat_SInt16, ChanLayout_Surround, + ChanOrder_Smpte, ChanMask_Surround_Mono); + const SampleSpec out_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); MetaWriter meta_writer(out_spec); @@ -419,9 +419,9 @@ TEST(pcm_mapper_writer, forward_capture_timestamp) { TEST(pcm_mapper_writer, forward_error) { enum { FrameSz = MaxBytes / 10 }; - const SampleSpec in_spec(Rate, PcmFormat_SInt16, ChanLayout_Surround, ChanOrder_Smpte, - ChanMask_Surround_Mono); - const SampleSpec out_spec(Rate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec in_spec(Rate, PcmSubformat_SInt16, ChanLayout_Surround, + ChanOrder_Smpte, ChanMask_Surround_Mono); + const SampleSpec out_spec(Rate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); MetaWriter meta_writer(out_spec); diff --git a/src/tests/roc_audio/test_pcm_samples.cpp b/src/tests/roc_audio/test_pcm_samples.cpp index ef8a9d162..37c1ce902 100644 --- a/src/tests/roc_audio/test_pcm_samples.cpp +++ b/src/tests/roc_audio/test_pcm_samples.cpp @@ -59,8 +59,8 @@ TEST(pcm_samples, decode) { for (size_t idx = 0; idx < ROC_ARRAY_SIZE(test_samples); idx++) { roc_log(LogDebug, "mapping %s to raw samples", test_samples[idx]->name); - PcmFormat in_fmt = test_samples[idx]->format; - PcmFormat out_fmt = Sample_RawFormat; + PcmSubformat in_fmt = test_samples[idx]->format; + PcmSubformat out_fmt = PcmSubformat_Raw; PcmMapper mapper(in_fmt, out_fmt); @@ -103,8 +103,8 @@ TEST(pcm_samples, encode_decode) { { // encode roc_log(LogDebug, "mapping raw samples to %s", test_samples[idx]->name); - PcmFormat in_fmt = Sample_RawFormat; - PcmFormat out_fmt = test_samples[idx]->format; + PcmSubformat in_fmt = PcmSubformat_Raw; + PcmSubformat out_fmt = test_samples[idx]->format; PcmMapper mapper(in_fmt, out_fmt); @@ -135,8 +135,8 @@ TEST(pcm_samples, encode_decode) { { // decode roc_log(LogDebug, "mapping %s to raw samples", test_samples[idx]->name); - PcmFormat in_fmt = test_samples[idx]->format; - PcmFormat out_fmt = Sample_RawFormat; + PcmSubformat in_fmt = test_samples[idx]->format; + PcmSubformat out_fmt = PcmSubformat_Raw; PcmMapper mapper(in_fmt, out_fmt); diff --git a/src/tests/roc_audio/test_plc_reader.cpp b/src/tests/roc_audio/test_plc_reader.cpp index 42fef37a6..6c8cbdce2 100644 --- a/src/tests/roc_audio/test_plc_reader.cpp +++ b/src/tests/roc_audio/test_plc_reader.cpp @@ -385,7 +385,7 @@ TEST_GROUP(plc_reader) { TEST(plc_reader, small_read) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec sample_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); test::MockReader mock_reader(frame_factory, sample_spec); @@ -414,7 +414,7 @@ TEST(plc_reader, small_read) { TEST(plc_reader, big_read) { enum { FrameSz = MaxSz * 2 }; - const SampleSpec sample_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); test::MockReader mock_reader(frame_factory, sample_spec); @@ -443,7 +443,7 @@ TEST(plc_reader, big_read) { TEST(plc_reader, initial_gap) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec sample_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); test::MockReader mock_reader(frame_factory, sample_spec); @@ -496,7 +496,7 @@ TEST(plc_reader, initial_gap) { TEST(plc_reader, readahead_disabled) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec sample_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); test::MockReader mock_reader(frame_factory, sample_spec); @@ -568,7 +568,7 @@ TEST(plc_reader, readahead_disabled) { TEST(plc_reader, readahead_enabled) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec sample_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); test::MockReader mock_reader(frame_factory, sample_spec); @@ -675,7 +675,7 @@ TEST(plc_reader, readahead_enabled) { TEST(plc_reader, readahead_drained) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec sample_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); test::MockReader mock_reader(frame_factory, sample_spec); @@ -749,7 +749,7 @@ TEST(plc_reader, readahead_drained) { TEST(plc_reader, readahead_partial) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec sample_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); test::MockReader mock_reader(frame_factory, sample_spec); @@ -826,7 +826,7 @@ TEST(plc_reader, readahead_partial) { TEST(plc_reader, soft_reads) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec sample_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); test::MockReader mock_reader(frame_factory, sample_spec); @@ -967,7 +967,7 @@ TEST(plc_reader, variable_frame_sizes) { LookaheadSz = 13, }; - const SampleSpec sample_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); test::MockReader mock_reader(frame_factory, sample_spec); @@ -1130,7 +1130,7 @@ TEST(plc_reader, variable_frame_sizes) { TEST(plc_reader, without_cts) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec sample_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); test::MockReader mock_reader(frame_factory, sample_spec); @@ -1198,7 +1198,7 @@ TEST(plc_reader, without_cts) { TEST(plc_reader, with_cts) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec sample_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); test::MockReader mock_reader(frame_factory, sample_spec); @@ -1272,7 +1272,7 @@ TEST(plc_reader, with_cts) { TEST(plc_reader, non_raw_format) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec int_spec(MaxSz, PcmFormat_SInt16, ChanLayout_Surround, + const SampleSpec int_spec(MaxSz, PcmSubformat_SInt16, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); IntReader<int16_t> int_reader(int_spec); @@ -1354,7 +1354,7 @@ TEST(plc_reader, non_raw_format) { TEST(plc_reader, supported_backends) { enum { FrameSz = MaxSz / 2, NumFrames = 5, NumIters = 10 }; - const SampleSpec sample_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); for (size_t n_back = 0; n_back < n_supported_backends; n_back++) { @@ -1401,7 +1401,7 @@ TEST(plc_reader, supported_backends) { TEST(plc_reader, forward_mode) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec sample_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); test::MockReader mock_reader(frame_factory, sample_spec); @@ -1429,7 +1429,7 @@ TEST(plc_reader, forward_mode) { TEST(plc_reader, forward_error) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec sample_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); test::MockReader mock_reader(frame_factory, sample_spec); @@ -1457,7 +1457,7 @@ TEST(plc_reader, forward_error) { TEST(plc_reader, forward_partial) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec sample_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); test::MockReader mock_reader(frame_factory, sample_spec); @@ -1481,7 +1481,7 @@ TEST(plc_reader, forward_partial) { TEST(plc_reader, preallocated_buffer) { enum { FrameSz = MaxSz / 2 }; - const SampleSpec sample_spec(MaxSz, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(MaxSz, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Mono); const size_t buffer_list[] = { diff --git a/src/tests/roc_audio/test_profiler.cpp b/src/tests/roc_audio/test_profiler.cpp index bf3b0f03d..0402466a8 100644 --- a/src/tests/roc_audio/test_profiler.cpp +++ b/src/tests/roc_audio/test_profiler.cpp @@ -31,7 +31,7 @@ const int SampleRate = 5000; // 50 samples / chunk const int ChannelMask = 0x1; const SampleSpec sample_spec( - SampleRate, Sample_RawFormat, ChanLayout_Surround, ChanOrder_Smpte, ChannelMask); + SampleRate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChannelMask); const ProfilerConfig profiler_config(50 * core::Millisecond, 10 * core::Millisecond); diff --git a/src/tests/roc_audio/test_resampler.cpp b/src/tests/roc_audio/test_resampler.cpp index a48f11dee..f31248490 100644 --- a/src/tests/roc_audio/test_resampler.cpp +++ b/src/tests/roc_audio/test_resampler.cpp @@ -403,10 +403,10 @@ TEST(resampler, supported_scalings) { const ResamplerBackend backend = supported_backends[n_back]; const SampleSpec in_spec = - SampleSpec(supported_rates[n_irate], Sample_RawFormat, + SampleSpec(supported_rates[n_irate], PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); const SampleSpec out_spec = - SampleSpec(supported_rates[n_orate], Sample_RawFormat, + SampleSpec(supported_rates[n_orate], PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); core::SharedPtr<IResampler> resampler = @@ -460,10 +460,10 @@ TEST(resampler, invalid_scalings) { const ResamplerBackend backend = supported_backends[n_back]; const SampleSpec in_spec = - SampleSpec(supported_rates[n_irate], Sample_RawFormat, + SampleSpec(supported_rates[n_irate], PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); const SampleSpec out_spec = - SampleSpec(supported_rates[n_orate], Sample_RawFormat, + SampleSpec(supported_rates[n_orate], PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); core::SharedPtr<IResampler> resampler = processor_map.new_resampler( @@ -509,10 +509,10 @@ TEST(resampler, scaling_trend) { const ResamplerBackend backend = supported_backends[n_back]; const SampleSpec in_spec = - SampleSpec(supported_rates[n_irate], Sample_RawFormat, + SampleSpec(supported_rates[n_irate], PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); const SampleSpec out_spec = - SampleSpec(supported_rates[n_orate], Sample_RawFormat, + SampleSpec(supported_rates[n_orate], PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); const float scaling = supported_scalings[n_scale]; @@ -587,7 +587,7 @@ TEST(resampler, upscale_downscale_mono) { const ResamplerBackend backend = supported_backends[n_back]; const ResamplerProfile profile = ResamplerProfile_High; - const SampleSpec sample_spec(SampleRate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(SampleRate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); for (size_t n_dir = 0; n_dir < ROC_ARRAY_SIZE(supported_dirs); n_dir++) { @@ -655,7 +655,7 @@ TEST(resampler, upscale_downscale_stereo) { const ResamplerBackend backend = supported_backends[n_back]; const ResamplerProfile profile = ResamplerProfile_High; - const SampleSpec sample_spec(SampleRate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(SampleRate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); for (size_t n_dir = 0; n_dir < ROC_ARRAY_SIZE(supported_dirs); n_dir++) { @@ -733,10 +733,10 @@ TEST(resampler, reader_timestamp_passthrough) { const ResamplerBackend backend = supported_backends[n_back]; const SampleSpec in_spec = - SampleSpec(supported_rates[n_irate], Sample_RawFormat, + SampleSpec(supported_rates[n_irate], PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); const SampleSpec out_spec = - SampleSpec(supported_rates[n_orate], Sample_RawFormat, + SampleSpec(supported_rates[n_orate], PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); core::SharedPtr<IResampler> resampler = processor_map.new_resampler( @@ -845,10 +845,10 @@ TEST(resampler, writer_timestamp_passthrough) { const ResamplerBackend backend = supported_backends[n_back]; const SampleSpec in_spec = - SampleSpec(supported_rates[n_irate], Sample_RawFormat, + SampleSpec(supported_rates[n_irate], PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); const SampleSpec out_spec = - SampleSpec(supported_rates[n_orate], Sample_RawFormat, + SampleSpec(supported_rates[n_orate], PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); core::SharedPtr<IResampler> resampler = processor_map.new_resampler( @@ -955,10 +955,10 @@ TEST(resampler, reader_timestamp_zero_or_small) { const ResamplerBackend backend = supported_backends[n_back]; const SampleSpec in_spec = - SampleSpec(supported_rates[n_irate], Sample_RawFormat, + SampleSpec(supported_rates[n_irate], PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); const SampleSpec out_spec = - SampleSpec(supported_rates[n_orate], Sample_RawFormat, + SampleSpec(supported_rates[n_orate], PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); core::SharedPtr<IResampler> resampler = processor_map.new_resampler( @@ -1033,10 +1033,10 @@ TEST(resampler, writer_timestamp_zero_or_small) { const ResamplerBackend backend = supported_backends[n_back]; const SampleSpec in_spec = - SampleSpec(supported_rates[n_irate], Sample_RawFormat, + SampleSpec(supported_rates[n_irate], PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); const SampleSpec out_spec = - SampleSpec(supported_rates[n_orate], Sample_RawFormat, + SampleSpec(supported_rates[n_orate], PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); core::SharedPtr<IResampler> resampler = processor_map.new_resampler( @@ -1098,7 +1098,7 @@ TEST(resampler, reader_big_frame) { const ResamplerBackend backend = supported_backends[n_back]; const ResamplerProfile profile = ResamplerProfile_High; - const SampleSpec sample_spec(SampleRate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(SampleRate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); test::MockReader input_reader(frame_factory, sample_spec); @@ -1137,7 +1137,7 @@ TEST(resampler, writer_big_frame) { const ResamplerBackend backend = supported_backends[n_back]; const ResamplerProfile profile = ResamplerProfile_High; - const SampleSpec sample_spec(SampleRate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(SampleRate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); test::MockWriter output_writer; @@ -1179,7 +1179,7 @@ TEST(resampler, reader_forward_mode) { const ResamplerBackend backend = supported_backends[n_back]; const ResamplerProfile profile = ResamplerProfile_High; - const SampleSpec sample_spec(SampleRate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(SampleRate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); test::MockReader input_reader(frame_factory, sample_spec); @@ -1224,7 +1224,7 @@ TEST(resampler, reader_forward_error) { const ResamplerBackend backend = supported_backends[n_back]; const ResamplerProfile profile = ResamplerProfile_High; - const SampleSpec sample_spec(SampleRate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(SampleRate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); test::MockReader input_reader(frame_factory, sample_spec); @@ -1268,7 +1268,7 @@ TEST(resampler, writer_forward_error) { const ResamplerBackend backend = supported_backends[n_back]; const ResamplerProfile profile = ResamplerProfile_High; - const SampleSpec sample_spec(SampleRate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(SampleRate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); test::MockWriter output_writer; @@ -1311,7 +1311,7 @@ TEST(resampler, reader_process_partial) { const ResamplerBackend backend = supported_backends[n_back]; const ResamplerProfile profile = ResamplerProfile_High; - const SampleSpec sample_spec(SampleRate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(SampleRate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); test::MockReader input_reader(frame_factory, sample_spec); @@ -1358,7 +1358,7 @@ TEST(resampler, reader_preallocated_buffer) { const ResamplerBackend backend = supported_backends[n_back]; const ResamplerProfile profile = ResamplerProfile_High; - const SampleSpec sample_spec(SampleRate, Sample_RawFormat, + const SampleSpec sample_spec(SampleRate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); test::MockReader input_reader(frame_factory, sample_spec); diff --git a/src/tests/roc_audio/test_sample_spec.cpp b/src/tests/roc_audio/test_sample_spec.cpp index 893b2abf3..b2caa2587 100644 --- a/src/tests/roc_audio/test_sample_spec.cpp +++ b/src/tests/roc_audio/test_sample_spec.cpp @@ -10,9 +10,9 @@ #include <CppUTest/UtestMacros.h> #include "roc_audio/channel_defs.h" -#include "roc_audio/pcm_format.h" +#include "roc_audio/format.h" +#include "roc_audio/pcm_subformat.h" #include "roc_audio/sample.h" -#include "roc_audio/sample_format.h" #include "roc_audio/sample_spec.cpp" #include "roc_core/cpu_traits.h" #include "roc_core/macro_helpers.h" @@ -29,7 +29,7 @@ TEST(sample_spec, ns_2_nsamples) { for (size_t numChans = 1; numChans < ChanPos_Max; ++numChans) { const SampleSpec sample_spec = - SampleSpec((size_t)SampleRate, Sample_RawFormat, ChanLayout_Surround, + SampleSpec((size_t)SampleRate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChannelMask(((uint64_t)1 << numChans) - 1)); // num_channels @@ -85,7 +85,7 @@ TEST(sample_spec, nsamples_2_ns) { for (size_t numChans = 1; numChans < ChanPos_Max; ++numChans) { const SampleSpec sample_spec = - SampleSpec((size_t)SampleRate, Sample_RawFormat, ChanLayout_Surround, + SampleSpec((size_t)SampleRate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChannelMask(((uint64_t)1 << numChans) - 1)); const core::nanoseconds_t sampling_period = @@ -133,7 +133,7 @@ TEST(sample_spec, saturation) { #if ROC_CPU_BITS == 32 { const SampleSpec sample_spec = - SampleSpec(1000000000, Sample_RawFormat, ChanLayout_Surround, ChanOrder_Smpte, + SampleSpec(1000000000, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); // ns_2_samples_per_chan @@ -143,7 +143,7 @@ TEST(sample_spec, saturation) { #endif { const SampleSpec sample_spec = - SampleSpec(1000000000, Sample_RawFormat, ChanLayout_Surround, ChanOrder_Smpte, + SampleSpec(1000000000, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); // ns_2_samples_overall @@ -154,7 +154,7 @@ TEST(sample_spec, saturation) { #if ROC_CPU_BITS == 64 { const SampleSpec sample_spec = - SampleSpec(1, Sample_RawFormat, ChanLayout_Surround, ChanOrder_Smpte, + SampleSpec(1, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); // samples_per_chan_2_ns @@ -177,7 +177,7 @@ TEST(sample_spec, saturation) { #endif { const SampleSpec sample_spec = - SampleSpec(1, Sample_RawFormat, ChanLayout_Surround, ChanOrder_Smpte, + SampleSpec(1, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); // ns_2_stream_timestamp_delta @@ -197,7 +197,7 @@ TEST(sample_spec, bytes) { const size_t NumChans = 2; const size_t SampleSize = sizeof(sample_t); - const SampleSpec sample_spec(SampleRate, Sample_RawFormat, ChanLayout_Surround, + const SampleSpec sample_spec(SampleRate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo); // bytes_2_stream_timestamp @@ -221,8 +221,9 @@ TEST(sample_spec, bytes) { const size_t NumChans = 2; const size_t SampleSize = 3; // 24 bits - const SampleSpec sample_spec(SampleRate, PcmFormat_SInt24_Be, ChanLayout_Surround, - ChanOrder_Smpte, ChanMask_Surround_Stereo); + const SampleSpec sample_spec(SampleRate, PcmSubformat_SInt24_Be, + ChanLayout_Surround, ChanOrder_Smpte, + ChanMask_Surround_Stereo); // bytes_2_stream_timestamp CHECK_EQUAL(111, @@ -242,31 +243,50 @@ TEST(sample_spec, bytes) { } } -TEST(sample_spec, clear) { +TEST(sample_spec, empty_complete) { SampleSpec sample_spec; - // sample spec is invalid - CHECK(!sample_spec.is_valid()); + // sample spec is empty + CHECK(!sample_spec.is_complete()); + CHECK(sample_spec.is_empty()); CHECK_EQUAL(0, sample_spec.sample_rate()); - CHECK_EQUAL(SampleFormat_Invalid, sample_spec.sample_format()); - CHECK_EQUAL(PcmFormat_Invalid, sample_spec.pcm_format()); + CHECK_EQUAL(Format_Invalid, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_Invalid, sample_spec.pcm_subformat()); CHECK_EQUAL(ChanLayout_None, sample_spec.channel_set().layout()); CHECK_EQUAL(ChanOrder_None, sample_spec.channel_set().order()); CHECK_EQUAL(0, sample_spec.channel_set().num_channels()); // set all fields sample_spec.set_sample_rate(44100); - sample_spec.set_sample_format(SampleFormat_Pcm); - sample_spec.set_pcm_format(PcmFormat_Float32); + CHECK(!sample_spec.is_complete()); + CHECK(!sample_spec.is_empty()); + + sample_spec.set_format(Format_Pcm); + CHECK(!sample_spec.is_complete()); + CHECK(!sample_spec.is_empty()); + + sample_spec.set_pcm_subformat(PcmSubformat_Float32); + CHECK(!sample_spec.is_complete()); + CHECK(!sample_spec.is_empty()); + sample_spec.channel_set().set_layout(ChanLayout_Surround); + CHECK(!sample_spec.is_complete()); + CHECK(!sample_spec.is_empty()); + sample_spec.channel_set().set_order(ChanOrder_Smpte); + CHECK(!sample_spec.is_complete()); + CHECK(!sample_spec.is_empty()); + sample_spec.channel_set().set_mask(ChanMask_Surround_Stereo); + CHECK(sample_spec.is_complete()); + CHECK(!sample_spec.is_empty()); - // sample spec is valid - CHECK(sample_spec.is_valid()); + // sample spec is complete + CHECK(sample_spec.is_complete()); + CHECK(!sample_spec.is_empty()); CHECK_EQUAL(44100, sample_spec.sample_rate()); - CHECK_EQUAL(SampleFormat_Pcm, sample_spec.sample_format()); - CHECK_EQUAL(PcmFormat_Float32, sample_spec.pcm_format()); + CHECK_EQUAL(Format_Pcm, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_Float32, sample_spec.pcm_subformat()); CHECK_EQUAL(ChanLayout_Surround, sample_spec.channel_set().layout()); CHECK_EQUAL(ChanOrder_Smpte, sample_spec.channel_set().order()); CHECK(sample_spec.channel_set().is_equal(ChanMask_Surround_Stereo)); @@ -274,47 +294,68 @@ TEST(sample_spec, clear) { // clear all fields sample_spec.clear(); - // sample spec is invalid - CHECK(!sample_spec.is_valid()); + // sample spec is empty + CHECK(!sample_spec.is_complete()); + CHECK(sample_spec.is_empty()); CHECK_EQUAL(0, sample_spec.sample_rate()); - CHECK_EQUAL(SampleFormat_Invalid, sample_spec.sample_format()); - CHECK_EQUAL(PcmFormat_Invalid, sample_spec.pcm_format()); + CHECK_EQUAL(Format_Invalid, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_Invalid, sample_spec.pcm_subformat()); CHECK_EQUAL(ChanLayout_None, sample_spec.channel_set().layout()); CHECK_EQUAL(ChanOrder_None, sample_spec.channel_set().order()); CHECK_EQUAL(0, sample_spec.channel_set().num_channels()); } -TEST(sample_spec, is_raw) { +TEST(sample_spec, pcm_raw) { { // empty SampleSpec sample_spec; - CHECK(!sample_spec.is_valid()); + CHECK(!sample_spec.is_complete()); + CHECK(sample_spec.is_empty()); + CHECK(!sample_spec.is_pcm()); CHECK(!sample_spec.is_raw()); } - { // incomplete + { // incomplete (pcm, raw) SampleSpec sample_spec; - sample_spec.set_sample_format(SampleFormat_Pcm); - sample_spec.set_pcm_format(PcmFormat_Float32); - CHECK(!sample_spec.is_valid()); + sample_spec.set_format(Format_Pcm); + sample_spec.set_pcm_subformat(PcmSubformat_Float32); + CHECK(!sample_spec.is_complete()); + CHECK(!sample_spec.is_empty()); + CHECK(sample_spec.is_pcm()); CHECK(sample_spec.is_raw()); } - { // complete + { // complete (pcm, raw) SampleSpec sample_spec; - sample_spec.set_sample_format(SampleFormat_Pcm); - sample_spec.set_pcm_format(PcmFormat_Float32); + sample_spec.set_format(Format_Pcm); + sample_spec.set_pcm_subformat(PcmSubformat_Float32); sample_spec.set_sample_rate(44100); sample_spec.set_channel_set( ChannelSet(ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo)); - CHECK(sample_spec.is_valid()); + CHECK(sample_spec.is_complete()); + CHECK(!sample_spec.is_empty()); + CHECK(sample_spec.is_pcm()); CHECK(sample_spec.is_raw()); } - { // pcm format mismatch + { // format mismatch (non-pcm) + SampleSpec sample_spec; + sample_spec.set_format(Format_Wav); + sample_spec.set_pcm_subformat(PcmSubformat_Float32); + sample_spec.set_sample_rate(44100); + sample_spec.set_channel_set( + ChannelSet(ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo)); + CHECK(sample_spec.is_complete()); + CHECK(!sample_spec.is_empty()); + CHECK(!sample_spec.is_pcm()); + CHECK(!sample_spec.is_raw()); + } + { // sub-format mismatch (pcm, non-raw) SampleSpec sample_spec; - sample_spec.set_sample_format(SampleFormat_Pcm); - sample_spec.set_pcm_format(PcmFormat_Float32_Be); + sample_spec.set_format(Format_Pcm); + sample_spec.set_pcm_subformat(PcmSubformat_Float32_Be); sample_spec.set_sample_rate(44100); sample_spec.set_channel_set( ChannelSet(ChanLayout_Surround, ChanOrder_Smpte, ChanMask_Surround_Stereo)); - CHECK(sample_spec.is_valid()); + CHECK(sample_spec.is_complete()); + CHECK(!sample_spec.is_empty()); + CHECK(sample_spec.is_pcm()); CHECK(!sample_spec.is_raw()); } } @@ -322,26 +363,26 @@ TEST(sample_spec, is_raw) { TEST(sample_spec, parse_rate) { { // 44.1Khz SampleSpec sample_spec; - CHECK(parse_sample_spec("s16/44100/stereo", sample_spec)); + CHECK(parse_sample_spec("pcm@s16/44100/stereo", sample_spec)); - CHECK(sample_spec.is_valid()); + CHECK(sample_spec.is_complete()); CHECK_EQUAL(44100, sample_spec.sample_rate()); - CHECK_EQUAL(SampleFormat_Pcm, sample_spec.sample_format()); - CHECK_EQUAL(PcmFormat_SInt16, sample_spec.pcm_format()); + CHECK_EQUAL(Format_Pcm, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_SInt16, sample_spec.pcm_subformat()); CHECK_EQUAL(ChanLayout_Surround, sample_spec.channel_set().layout()); CHECK_EQUAL(ChanOrder_Smpte, sample_spec.channel_set().order()); CHECK(sample_spec.channel_set().is_equal(ChanMask_Surround_Stereo)); } { // 48Khz SampleSpec sample_spec; - CHECK(parse_sample_spec("s16/48000/stereo", sample_spec)); + CHECK(parse_sample_spec("pcm@s16/48000/stereo", sample_spec)); - CHECK(sample_spec.is_valid()); + CHECK(sample_spec.is_complete()); CHECK_EQUAL(48000, sample_spec.sample_rate()); - CHECK_EQUAL(SampleFormat_Pcm, sample_spec.sample_format()); - CHECK_EQUAL(PcmFormat_SInt16, sample_spec.pcm_format()); + CHECK_EQUAL(Format_Pcm, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_SInt16, sample_spec.pcm_subformat()); CHECK_EQUAL(ChanLayout_Surround, sample_spec.channel_set().layout()); CHECK_EQUAL(ChanOrder_Smpte, sample_spec.channel_set().order()); CHECK(sample_spec.channel_set().is_equal(ChanMask_Surround_Stereo)); @@ -349,83 +390,123 @@ TEST(sample_spec, parse_rate) { } TEST(sample_spec, parse_format) { - { // uint8 + { // format: pcm, subformat: uint8 SampleSpec sample_spec; - CHECK(parse_sample_spec("u8/44100/stereo", sample_spec)); + CHECK(parse_sample_spec("pcm@u8/44100/stereo", sample_spec)); - CHECK(sample_spec.is_valid()); + CHECK(sample_spec.is_complete()); CHECK_EQUAL(44100, sample_spec.sample_rate()); - CHECK_EQUAL(SampleFormat_Pcm, sample_spec.sample_format()); - CHECK_EQUAL(PcmFormat_UInt8, sample_spec.pcm_format()); + CHECK_EQUAL(Format_Pcm, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_UInt8, sample_spec.pcm_subformat()); CHECK_EQUAL(ChanLayout_Surround, sample_spec.channel_set().layout()); CHECK_EQUAL(ChanOrder_Smpte, sample_spec.channel_set().order()); CHECK(sample_spec.channel_set().is_equal(ChanMask_Surround_Stereo)); + + STRCMP_EQUAL("pcm", sample_spec.format_name()); + STRCMP_EQUAL("u8", sample_spec.subformat_name()); } - { // sint18_4_le + { // format: pcm, subformat: sint18_4_le SampleSpec sample_spec; - CHECK(parse_sample_spec("s18_4le/48000/stereo", sample_spec)); + CHECK(parse_sample_spec("pcm@s18_4le/48000/stereo", sample_spec)); - CHECK(sample_spec.is_valid()); + CHECK(sample_spec.is_complete()); CHECK_EQUAL(48000, sample_spec.sample_rate()); - CHECK_EQUAL(SampleFormat_Pcm, sample_spec.sample_format()); - CHECK_EQUAL(PcmFormat_SInt18_4_Le, sample_spec.pcm_format()); + CHECK_EQUAL(Format_Pcm, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_SInt18_4_Le, sample_spec.pcm_subformat()); CHECK_EQUAL(ChanLayout_Surround, sample_spec.channel_set().layout()); CHECK_EQUAL(ChanOrder_Smpte, sample_spec.channel_set().order()); CHECK(sample_spec.channel_set().is_equal(ChanMask_Surround_Stereo)); + + STRCMP_EQUAL("pcm", sample_spec.format_name()); + STRCMP_EQUAL("s18_4le", sample_spec.subformat_name()); } - { // float32 + { // format: wav, subformat: float32 SampleSpec sample_spec; - CHECK(parse_sample_spec("f32/48000/stereo", sample_spec)); + CHECK(parse_sample_spec("wav@f32/48000/stereo", sample_spec)); - CHECK(sample_spec.is_valid()); + CHECK(sample_spec.is_complete()); CHECK_EQUAL(48000, sample_spec.sample_rate()); - CHECK_EQUAL(SampleFormat_Pcm, sample_spec.sample_format()); - CHECK_EQUAL(PcmFormat_Float32, sample_spec.pcm_format()); + CHECK_EQUAL(Format_Wav, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_Float32, sample_spec.pcm_subformat()); CHECK_EQUAL(ChanLayout_Surround, sample_spec.channel_set().layout()); CHECK_EQUAL(ChanOrder_Smpte, sample_spec.channel_set().order()); CHECK(sample_spec.channel_set().is_equal(ChanMask_Surround_Stereo)); + + STRCMP_EQUAL("wav", sample_spec.format_name()); + STRCMP_EQUAL("f32", sample_spec.subformat_name()); + } + { // format: custom, subformat: float64 + SampleSpec sample_spec; + CHECK(parse_sample_spec("test@f64/48000/stereo", sample_spec)); + + CHECK(sample_spec.is_complete()); + + CHECK_EQUAL(48000, sample_spec.sample_rate()); + CHECK_EQUAL(Format_Custom, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_Float64, sample_spec.pcm_subformat()); + CHECK_EQUAL(ChanLayout_Surround, sample_spec.channel_set().layout()); + CHECK_EQUAL(ChanOrder_Smpte, sample_spec.channel_set().order()); + CHECK(sample_spec.channel_set().is_equal(ChanMask_Surround_Stereo)); + + STRCMP_EQUAL("test", sample_spec.format_name()); + STRCMP_EQUAL("f64", sample_spec.subformat_name()); + } + { // format: custom, subformat: custom + SampleSpec sample_spec; + CHECK(parse_sample_spec("xxx@yyy/48000/stereo", sample_spec)); + + CHECK(sample_spec.is_complete()); + + CHECK_EQUAL(48000, sample_spec.sample_rate()); + CHECK_EQUAL(Format_Custom, sample_spec.format()); + CHECK_EQUAL(ChanLayout_Surround, sample_spec.channel_set().layout()); + CHECK_EQUAL(ChanOrder_Smpte, sample_spec.channel_set().order()); + CHECK(sample_spec.channel_set().is_equal(ChanMask_Surround_Stereo)); + + STRCMP_EQUAL("xxx", sample_spec.format_name()); + STRCMP_EQUAL("yyy", sample_spec.subformat_name()); } } IGNORE_TEST(sample_spec, parse_channels) { { // surround stereo SampleSpec sample_spec; - CHECK(parse_sample_spec("s16/48000/stereo", sample_spec)); + CHECK(parse_sample_spec("pcm@s16/48000/stereo", sample_spec)); - CHECK(sample_spec.is_valid()); + CHECK(sample_spec.is_complete()); CHECK_EQUAL(48000, sample_spec.sample_rate()); - CHECK_EQUAL(SampleFormat_Pcm, sample_spec.sample_format()); - CHECK_EQUAL(PcmFormat_SInt16, sample_spec.pcm_format()); + CHECK_EQUAL(Format_Pcm, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_SInt16, sample_spec.pcm_subformat()); CHECK_EQUAL(ChanLayout_Surround, sample_spec.channel_set().layout()); CHECK_EQUAL(ChanOrder_Smpte, sample_spec.channel_set().order()); CHECK(sample_spec.channel_set().is_equal(ChanMask_Surround_Stereo)); } { // surround 5.1.2 SampleSpec sample_spec; - CHECK(parse_sample_spec("s16/48000/surround5.1.2", sample_spec)); + CHECK(parse_sample_spec("pcm@s16/48000/surround5.1.2", sample_spec)); - CHECK(sample_spec.is_valid()); + CHECK(sample_spec.is_complete()); CHECK_EQUAL(48000, sample_spec.sample_rate()); - CHECK_EQUAL(SampleFormat_Pcm, sample_spec.sample_format()); - CHECK_EQUAL(PcmFormat_SInt16, sample_spec.pcm_format()); + CHECK_EQUAL(Format_Pcm, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_SInt16, sample_spec.pcm_subformat()); CHECK_EQUAL(ChanLayout_Surround, sample_spec.channel_set().layout()); CHECK_EQUAL(ChanOrder_Smpte, sample_spec.channel_set().order()); CHECK(sample_spec.channel_set().is_equal(ChanMask_Surround_5_1_2)); } { // surround channel list SampleSpec sample_spec; - CHECK(parse_sample_spec("s16/48000/FL,FC,FR", sample_spec)); + CHECK(parse_sample_spec("pcm@s16/48000/FL,FC,FR", sample_spec)); - CHECK(sample_spec.is_valid()); + CHECK(sample_spec.is_complete()); CHECK_EQUAL(48000, sample_spec.sample_rate()); - CHECK_EQUAL(SampleFormat_Pcm, sample_spec.sample_format()); - CHECK_EQUAL(PcmFormat_SInt16, sample_spec.pcm_format()); + CHECK_EQUAL(Format_Pcm, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_SInt16, sample_spec.pcm_subformat()); CHECK_EQUAL(ChanLayout_Surround, sample_spec.channel_set().layout()); CHECK_EQUAL(ChanOrder_Smpte, sample_spec.channel_set().order()); @@ -436,13 +517,13 @@ IGNORE_TEST(sample_spec, parse_channels) { } { // multitrack channel list SampleSpec sample_spec; - CHECK(parse_sample_spec("s16/48000/1,2,3", sample_spec)); + CHECK(parse_sample_spec("pcm@s16/48000/1,2,3", sample_spec)); - CHECK(sample_spec.is_valid()); + CHECK(sample_spec.is_complete()); CHECK_EQUAL(48000, sample_spec.sample_rate()); - CHECK_EQUAL(SampleFormat_Pcm, sample_spec.sample_format()); - CHECK_EQUAL(PcmFormat_SInt16, sample_spec.pcm_format()); + CHECK_EQUAL(Format_Pcm, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_SInt16, sample_spec.pcm_subformat()); CHECK_EQUAL(ChanLayout_Multitrack, sample_spec.channel_set().layout()); CHECK_EQUAL(ChanOrder_None, sample_spec.channel_set().order()); @@ -453,13 +534,13 @@ IGNORE_TEST(sample_spec, parse_channels) { } { // multitrack channel range SampleSpec sample_spec; - CHECK(parse_sample_spec("s16/48000/1-3", sample_spec)); + CHECK(parse_sample_spec("pcm@s16/48000/1-3", sample_spec)); - CHECK(sample_spec.is_valid()); + CHECK(sample_spec.is_complete()); CHECK_EQUAL(48000, sample_spec.sample_rate()); - CHECK_EQUAL(SampleFormat_Pcm, sample_spec.sample_format()); - CHECK_EQUAL(PcmFormat_SInt16, sample_spec.pcm_format()); + CHECK_EQUAL(Format_Pcm, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_SInt16, sample_spec.pcm_subformat()); CHECK_EQUAL(ChanLayout_Multitrack, sample_spec.channel_set().layout()); CHECK_EQUAL(ChanOrder_None, sample_spec.channel_set().order()); @@ -470,13 +551,13 @@ IGNORE_TEST(sample_spec, parse_channels) { } { // multitrack channel list and range SampleSpec sample_spec; - CHECK(parse_sample_spec("s16/48000/1,3-5,7", sample_spec)); + CHECK(parse_sample_spec("pcm@s16/48000/1,3-5,7", sample_spec)); - CHECK(sample_spec.is_valid()); + CHECK(sample_spec.is_complete()); CHECK_EQUAL(48000, sample_spec.sample_rate()); - CHECK_EQUAL(SampleFormat_Pcm, sample_spec.sample_format()); - CHECK_EQUAL(PcmFormat_SInt16, sample_spec.pcm_format()); + CHECK_EQUAL(Format_Pcm, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_SInt16, sample_spec.pcm_subformat()); CHECK_EQUAL(ChanLayout_Multitrack, sample_spec.channel_set().layout()); CHECK_EQUAL(ChanOrder_None, sample_spec.channel_set().order()); @@ -489,13 +570,13 @@ IGNORE_TEST(sample_spec, parse_channels) { } { // multitrack mask (zero) SampleSpec sample_spec; - CHECK(parse_sample_spec("s16/48000/0x00", sample_spec)); + CHECK(parse_sample_spec("pcm@s16/48000/0x00", sample_spec)); - CHECK(!sample_spec.is_valid()); + CHECK(!sample_spec.is_complete()); CHECK_EQUAL(48000, sample_spec.sample_rate()); - CHECK_EQUAL(SampleFormat_Pcm, sample_spec.sample_format()); - CHECK_EQUAL(PcmFormat_SInt16, sample_spec.pcm_format()); + CHECK_EQUAL(Format_Pcm, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_SInt16, sample_spec.pcm_subformat()); CHECK_EQUAL(ChanLayout_Multitrack, sample_spec.channel_set().layout()); CHECK_EQUAL(ChanOrder_None, sample_spec.channel_set().order()); CHECK_EQUAL(0, sample_spec.num_channels()); @@ -503,13 +584,13 @@ IGNORE_TEST(sample_spec, parse_channels) { { // multitrack mask (short) SampleSpec sample_spec; // 0xAC = 10101100 - CHECK(parse_sample_spec("s16/48000/0xAC", sample_spec)); + CHECK(parse_sample_spec("pcm@s16/48000/0xAC", sample_spec)); - CHECK(sample_spec.is_valid()); + CHECK(sample_spec.is_complete()); CHECK_EQUAL(48000, sample_spec.sample_rate()); - CHECK_EQUAL(SampleFormat_Pcm, sample_spec.sample_format()); - CHECK_EQUAL(PcmFormat_SInt16, sample_spec.pcm_format()); + CHECK_EQUAL(Format_Pcm, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_SInt16, sample_spec.pcm_subformat()); CHECK_EQUAL(ChanLayout_Multitrack, sample_spec.channel_set().layout()); CHECK_EQUAL(ChanOrder_None, sample_spec.channel_set().order()); @@ -522,13 +603,13 @@ IGNORE_TEST(sample_spec, parse_channels) { { // multitrack mask (long) SampleSpec sample_spec; // 1010, 80 zero bits, 1100 - CHECK(parse_sample_spec("s16/48000/0xA00000000000000000000C", sample_spec)); + CHECK(parse_sample_spec("pcm@s16/48000/0xA00000000000000000000C", sample_spec)); - CHECK(sample_spec.is_valid()); + CHECK(sample_spec.is_complete()); CHECK_EQUAL(48000, sample_spec.sample_rate()); - CHECK_EQUAL(SampleFormat_Pcm, sample_spec.sample_format()); - CHECK_EQUAL(PcmFormat_SInt16, sample_spec.pcm_format()); + CHECK_EQUAL(Format_Pcm, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_SInt16, sample_spec.pcm_subformat()); CHECK_EQUAL(ChanLayout_Multitrack, sample_spec.channel_set().layout()); CHECK_EQUAL(ChanOrder_None, sample_spec.channel_set().order()); @@ -545,37 +626,63 @@ TEST(sample_spec, parse_defaults) { SampleSpec sample_spec; CHECK(parse_sample_spec("-/44100/stereo", sample_spec)); - CHECK(!sample_spec.is_valid()); + CHECK(!sample_spec.is_complete()); + + CHECK_EQUAL(44100, sample_spec.sample_rate()); + CHECK_EQUAL(Format_Invalid, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_Invalid, sample_spec.pcm_subformat()); + CHECK_EQUAL(ChanLayout_Surround, sample_spec.channel_set().layout()); + CHECK_EQUAL(ChanOrder_Smpte, sample_spec.channel_set().order()); + CHECK(sample_spec.channel_set().is_equal(ChanMask_Surround_Stereo)); + } + { // no subformat (wav format) + SampleSpec sample_spec; + CHECK(parse_sample_spec("wav/44100/stereo", sample_spec)); + + CHECK(sample_spec.is_complete()); + + CHECK_EQUAL(44100, sample_spec.sample_rate()); + CHECK_EQUAL(Format_Wav, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_Invalid, sample_spec.pcm_subformat()); + CHECK_EQUAL(ChanLayout_Surround, sample_spec.channel_set().layout()); + CHECK_EQUAL(ChanOrder_Smpte, sample_spec.channel_set().order()); + CHECK(sample_spec.channel_set().is_equal(ChanMask_Surround_Stereo)); + } + { // no subformat (custom format) + SampleSpec sample_spec; + CHECK(parse_sample_spec("test/44100/stereo", sample_spec)); + + CHECK(sample_spec.is_complete()); CHECK_EQUAL(44100, sample_spec.sample_rate()); - CHECK_EQUAL(SampleFormat_Invalid, sample_spec.sample_format()); - CHECK_EQUAL(PcmFormat_Invalid, sample_spec.pcm_format()); + CHECK_EQUAL(Format_Custom, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_Invalid, sample_spec.pcm_subformat()); CHECK_EQUAL(ChanLayout_Surround, sample_spec.channel_set().layout()); CHECK_EQUAL(ChanOrder_Smpte, sample_spec.channel_set().order()); CHECK(sample_spec.channel_set().is_equal(ChanMask_Surround_Stereo)); } { // no rate SampleSpec sample_spec; - CHECK(parse_sample_spec("s16/-/stereo", sample_spec)); + CHECK(parse_sample_spec("pcm@s16/-/stereo", sample_spec)); - CHECK(!sample_spec.is_valid()); + CHECK(!sample_spec.is_complete()); CHECK_EQUAL(0, sample_spec.sample_rate()); - CHECK_EQUAL(SampleFormat_Pcm, sample_spec.sample_format()); - CHECK_EQUAL(PcmFormat_SInt16, sample_spec.pcm_format()); + CHECK_EQUAL(Format_Pcm, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_SInt16, sample_spec.pcm_subformat()); CHECK_EQUAL(ChanLayout_Surround, sample_spec.channel_set().layout()); CHECK_EQUAL(ChanOrder_Smpte, sample_spec.channel_set().order()); CHECK(sample_spec.channel_set().is_equal(ChanMask_Surround_Stereo)); } { // no channels SampleSpec sample_spec; - CHECK(parse_sample_spec("s16/48000/-", sample_spec)); + CHECK(parse_sample_spec("pcm@s16/48000/-", sample_spec)); - CHECK(!sample_spec.is_valid()); + CHECK(!sample_spec.is_complete()); CHECK_EQUAL(48000, sample_spec.sample_rate()); - CHECK_EQUAL(SampleFormat_Pcm, sample_spec.sample_format()); - CHECK_EQUAL(PcmFormat_SInt16, sample_spec.pcm_format()); + CHECK_EQUAL(Format_Pcm, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_SInt16, sample_spec.pcm_subformat()); CHECK_EQUAL(ChanLayout_None, sample_spec.channel_set().layout()); CHECK_EQUAL(ChanOrder_None, sample_spec.channel_set().order()); CHECK_EQUAL(0, sample_spec.channel_set().num_channels()); @@ -584,17 +691,65 @@ TEST(sample_spec, parse_defaults) { SampleSpec sample_spec; CHECK(parse_sample_spec("-/-/-", sample_spec)); - CHECK(!sample_spec.is_valid()); + CHECK(!sample_spec.is_complete()); CHECK_EQUAL(0, sample_spec.sample_rate()); - CHECK_EQUAL(SampleFormat_Invalid, sample_spec.sample_format()); - CHECK_EQUAL(PcmFormat_Invalid, sample_spec.pcm_format()); + CHECK_EQUAL(Format_Invalid, sample_spec.format()); + CHECK_EQUAL(PcmSubformat_Invalid, sample_spec.pcm_subformat()); CHECK_EQUAL(ChanLayout_None, sample_spec.channel_set().layout()); CHECK_EQUAL(ChanOrder_None, sample_spec.channel_set().order()); CHECK_EQUAL(0, sample_spec.channel_set().num_channels()); } } +TEST(sample_spec, has_field) { + { // all fields + SampleSpec sample_spec; + CHECK(parse_sample_spec("pcm@s16/44100/stereo", sample_spec)); + + CHECK(sample_spec.has_format()); + CHECK(sample_spec.has_subformat()); + CHECK(sample_spec.has_sample_rate()); + CHECK(sample_spec.has_channel_set()); + } + { // no sub-format + SampleSpec sample_spec; + CHECK(parse_sample_spec("wav/44100/stereo", sample_spec)); + + CHECK(sample_spec.has_format()); + CHECK(!sample_spec.has_subformat()); + CHECK(sample_spec.has_sample_rate()); + CHECK(sample_spec.has_channel_set()); + } + { // no rate + SampleSpec sample_spec; + CHECK(parse_sample_spec("pcm@s16/-/stereo", sample_spec)); + + CHECK(sample_spec.has_format()); + CHECK(sample_spec.has_subformat()); + CHECK(!sample_spec.has_sample_rate()); + CHECK(sample_spec.has_channel_set()); + } + { // no channels + SampleSpec sample_spec; + CHECK(parse_sample_spec("pcm@s16/44100/-", sample_spec)); + + CHECK(sample_spec.has_format()); + CHECK(sample_spec.has_subformat()); + CHECK(sample_spec.has_sample_rate()); + CHECK(!sample_spec.has_channel_set()); + } + { // no fields + SampleSpec sample_spec; + CHECK(parse_sample_spec("-/-/-", sample_spec)); + + CHECK(!sample_spec.has_format()); + CHECK(!sample_spec.has_subformat()); + CHECK(!sample_spec.has_sample_rate()); + CHECK(!sample_spec.has_channel_set()); + } +} + TEST(sample_spec, parse_errors) { SampleSpec sample_spec; @@ -604,40 +759,50 @@ TEST(sample_spec, parse_errors) { CHECK(!parse_sample_spec("//", sample_spec)); CHECK(!parse_sample_spec("///", sample_spec)); CHECK(!parse_sample_spec("/48000/stereo", sample_spec)); - CHECK(!parse_sample_spec("s16//stereo", sample_spec)); - CHECK(!parse_sample_spec("s16/48000/", sample_spec)); - CHECK(!parse_sample_spec("/s16/48000/stereo", sample_spec)); - CHECK(!parse_sample_spec("s16/48000/stereo/", sample_spec)); + CHECK(!parse_sample_spec("pcm@/48000/stereo", sample_spec)); + CHECK(!parse_sample_spec("@s16/48000/stereo", sample_spec)); + CHECK(!parse_sample_spec("@/48000/stereo", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16//stereo", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/48000/", sample_spec)); + CHECK(!parse_sample_spec("/pcm@s16/48000/stereo", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/48000/stereo/", sample_spec)); + CHECK(!parse_sample_spec("pcm@/48000/stereo", sample_spec)); + CHECK(!parse_sample_spec("@s16/48000/stereo", sample_spec)); } { // bad rate - CHECK(!parse_sample_spec("s16/0/stereo", sample_spec)); - CHECK(!parse_sample_spec("s16/-1/stereo", sample_spec)); - CHECK(!parse_sample_spec("s16/bad/stereo", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/0/stereo", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/-1/stereo", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/bad/stereo", sample_spec)); } { // bad format - CHECK(!parse_sample_spec("s77/44100/stereo", sample_spec)); - CHECK(!parse_sample_spec("xxx/44100/stereo", sample_spec)); + CHECK(!parse_sample_spec("!!!@s16/44100/stereo", sample_spec)); + CHECK(!parse_sample_spec("!!!/44100/stereo", sample_spec)); + } + { // bad subformat + CHECK(!parse_sample_spec("pcm@s77/44100/stereo", sample_spec)); + CHECK(!parse_sample_spec("pcm@xxx/44100/stereo", sample_spec)); + CHECK(!parse_sample_spec("pcm/44100/stereo", sample_spec)); } { // bad surround - CHECK(!parse_sample_spec("s16/44100/bad", sample_spec)); - CHECK(!parse_sample_spec("s16/44100/BAD,BAD", sample_spec)); - CHECK(!parse_sample_spec("s16/44100/stereo,", sample_spec)); - CHECK(!parse_sample_spec("s16/44100/FL,FR,", sample_spec)); - CHECK(!parse_sample_spec("s16/44100/,FL,FR", sample_spec)); - CHECK(!parse_sample_spec("s16/44100/FL,,FR", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/44100/bad", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/44100/BAD,BAD", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/44100/stereo,", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/44100/FL,FR,", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/44100/,FL,FR", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/44100/FL,,FR", sample_spec)); } { // bad multitrack - CHECK(!parse_sample_spec("s16/44100/1,2,", sample_spec)); - CHECK(!parse_sample_spec("s16/44100/,1,2", sample_spec)); - CHECK(!parse_sample_spec("s16/44100/1,,2", sample_spec)); - CHECK(!parse_sample_spec("s16/44100/1-", sample_spec)); - CHECK(!parse_sample_spec("s16/44100/-2", sample_spec)); - CHECK(!parse_sample_spec("s16/44100/1--2", sample_spec)); - CHECK(!parse_sample_spec("s16/44100/10000", sample_spec)); - CHECK(!parse_sample_spec("s16/44100/10000-20000", sample_spec)); - CHECK(!parse_sample_spec("s16/44100/0x", sample_spec)); - CHECK(!parse_sample_spec("s16/44100/0XF", sample_spec)); - CHECK(!parse_sample_spec("s16/44100/0xZZ", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/44100/1,2,", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/44100/,1,2", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/44100/1,,2", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/44100/1-", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/44100/-2", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/44100/1--2", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/44100/10000", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/44100/10000-20000", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/44100/0x", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/44100/0XF", sample_spec)); + CHECK(!parse_sample_spec("pcm@s16/44100/0xZZ", sample_spec)); } } diff --git a/src/tests/roc_audio/test_samples/generate_samples.py b/src/tests/roc_audio/test_samples/generate_samples.py index d23626d3b..5f6842bf3 100755 --- a/src/tests/roc_audio/test_samples/generate_samples.py +++ b/src/tests/roc_audio/test_samples/generate_samples.py @@ -4,7 +4,7 @@ import subprocess def format_name(encoding, endian): - return 'PcmFormat_' + \ + return 'PcmSubformat_' + \ encoding['pcm_encoding'] + '_' + endian['pcm_endian'] def format_array(array, maxlen=8, indent=1): diff --git a/src/tests/roc_audio/test_samples/pcm_float32_be.h b/src/tests/roc_audio/test_samples/pcm_float32_be.h index 92cc19d89..66881163e 100644 --- a/src/tests/roc_audio/test_samples/pcm_float32_be.h +++ b/src/tests/roc_audio/test_samples/pcm_float32_be.h @@ -14,7 +14,7 @@ namespace test { static SampleInfo sample_pcm_float32_be = { /* name */ "pcm_float32_be", - /* format */ PcmFormat_Float32_Be, + /* format */ PcmSubformat_Float32_Be, /* num_samples */ 240, /* samples */ { diff --git a/src/tests/roc_audio/test_samples/pcm_float32_le.h b/src/tests/roc_audio/test_samples/pcm_float32_le.h index b02b75231..28ac25597 100644 --- a/src/tests/roc_audio/test_samples/pcm_float32_le.h +++ b/src/tests/roc_audio/test_samples/pcm_float32_le.h @@ -14,7 +14,7 @@ namespace test { static SampleInfo sample_pcm_float32_le = { /* name */ "pcm_float32_le", - /* format */ PcmFormat_Float32_Le, + /* format */ PcmSubformat_Float32_Le, /* num_samples */ 240, /* samples */ { diff --git a/src/tests/roc_audio/test_samples/pcm_sint16_be.h b/src/tests/roc_audio/test_samples/pcm_sint16_be.h index 1963e0c55..86d6cf0de 100644 --- a/src/tests/roc_audio/test_samples/pcm_sint16_be.h +++ b/src/tests/roc_audio/test_samples/pcm_sint16_be.h @@ -14,7 +14,7 @@ namespace test { static SampleInfo sample_pcm_sint16_be = { /* name */ "pcm_sint16_be", - /* format */ PcmFormat_SInt16_Be, + /* format */ PcmSubformat_SInt16_Be, /* num_samples */ 240, /* samples */ { diff --git a/src/tests/roc_audio/test_samples/pcm_sint16_le.h b/src/tests/roc_audio/test_samples/pcm_sint16_le.h index 50d20e0ed..ddb07462c 100644 --- a/src/tests/roc_audio/test_samples/pcm_sint16_le.h +++ b/src/tests/roc_audio/test_samples/pcm_sint16_le.h @@ -14,7 +14,7 @@ namespace test { static SampleInfo sample_pcm_sint16_le = { /* name */ "pcm_sint16_le", - /* format */ PcmFormat_SInt16_Le, + /* format */ PcmSubformat_SInt16_Le, /* num_samples */ 240, /* samples */ { diff --git a/src/tests/roc_audio/test_samples/pcm_sint24_be.h b/src/tests/roc_audio/test_samples/pcm_sint24_be.h index e0302f5a1..3c4f85080 100644 --- a/src/tests/roc_audio/test_samples/pcm_sint24_be.h +++ b/src/tests/roc_audio/test_samples/pcm_sint24_be.h @@ -14,7 +14,7 @@ namespace test { static SampleInfo sample_pcm_sint24_be = { /* name */ "pcm_sint24_be", - /* format */ PcmFormat_SInt24_Be, + /* format */ PcmSubformat_SInt24_Be, /* num_samples */ 240, /* samples */ { diff --git a/src/tests/roc_audio/test_samples/pcm_sint24_le.h b/src/tests/roc_audio/test_samples/pcm_sint24_le.h index 2b3d74b50..8e82b81cb 100644 --- a/src/tests/roc_audio/test_samples/pcm_sint24_le.h +++ b/src/tests/roc_audio/test_samples/pcm_sint24_le.h @@ -14,7 +14,7 @@ namespace test { static SampleInfo sample_pcm_sint24_le = { /* name */ "pcm_sint24_le", - /* format */ PcmFormat_SInt24_Le, + /* format */ PcmSubformat_SInt24_Le, /* num_samples */ 240, /* samples */ { diff --git a/src/tests/roc_audio/test_samples/pcm_sint32_be.h b/src/tests/roc_audio/test_samples/pcm_sint32_be.h index 9aac503cd..4c6499421 100644 --- a/src/tests/roc_audio/test_samples/pcm_sint32_be.h +++ b/src/tests/roc_audio/test_samples/pcm_sint32_be.h @@ -14,7 +14,7 @@ namespace test { static SampleInfo sample_pcm_sint32_be = { /* name */ "pcm_sint32_be", - /* format */ PcmFormat_SInt32_Be, + /* format */ PcmSubformat_SInt32_Be, /* num_samples */ 240, /* samples */ { diff --git a/src/tests/roc_audio/test_samples/pcm_sint32_le.h b/src/tests/roc_audio/test_samples/pcm_sint32_le.h index 59cd59eb1..2a0227f8b 100644 --- a/src/tests/roc_audio/test_samples/pcm_sint32_le.h +++ b/src/tests/roc_audio/test_samples/pcm_sint32_le.h @@ -14,7 +14,7 @@ namespace test { static SampleInfo sample_pcm_sint32_le = { /* name */ "pcm_sint32_le", - /* format */ PcmFormat_SInt32_Le, + /* format */ PcmSubformat_SInt32_Le, /* num_samples */ 240, /* samples */ { diff --git a/src/tests/roc_audio/test_samples/pcm_sint8_be.h b/src/tests/roc_audio/test_samples/pcm_sint8_be.h index e6ebde2bb..f7d8c8e9c 100644 --- a/src/tests/roc_audio/test_samples/pcm_sint8_be.h +++ b/src/tests/roc_audio/test_samples/pcm_sint8_be.h @@ -14,7 +14,7 @@ namespace test { static SampleInfo sample_pcm_sint8_be = { /* name */ "pcm_sint8_be", - /* format */ PcmFormat_SInt8_Be, + /* format */ PcmSubformat_SInt8_Be, /* num_samples */ 240, /* samples */ { diff --git a/src/tests/roc_audio/test_samples/pcm_sint8_le.h b/src/tests/roc_audio/test_samples/pcm_sint8_le.h index a34c6a063..2393a41cc 100644 --- a/src/tests/roc_audio/test_samples/pcm_sint8_le.h +++ b/src/tests/roc_audio/test_samples/pcm_sint8_le.h @@ -14,7 +14,7 @@ namespace test { static SampleInfo sample_pcm_sint8_le = { /* name */ "pcm_sint8_le", - /* format */ PcmFormat_SInt8_Le, + /* format */ PcmSubformat_SInt8_Le, /* num_samples */ 240, /* samples */ { diff --git a/src/tests/roc_audio/test_samples/pcm_uint16_be.h b/src/tests/roc_audio/test_samples/pcm_uint16_be.h index 349a5da65..7aba88274 100644 --- a/src/tests/roc_audio/test_samples/pcm_uint16_be.h +++ b/src/tests/roc_audio/test_samples/pcm_uint16_be.h @@ -14,7 +14,7 @@ namespace test { static SampleInfo sample_pcm_uint16_be = { /* name */ "pcm_uint16_be", - /* format */ PcmFormat_UInt16_Be, + /* format */ PcmSubformat_UInt16_Be, /* num_samples */ 240, /* samples */ { diff --git a/src/tests/roc_audio/test_samples/pcm_uint16_le.h b/src/tests/roc_audio/test_samples/pcm_uint16_le.h index 612fb080e..e419214da 100644 --- a/src/tests/roc_audio/test_samples/pcm_uint16_le.h +++ b/src/tests/roc_audio/test_samples/pcm_uint16_le.h @@ -14,7 +14,7 @@ namespace test { static SampleInfo sample_pcm_uint16_le = { /* name */ "pcm_uint16_le", - /* format */ PcmFormat_UInt16_Le, + /* format */ PcmSubformat_UInt16_Le, /* num_samples */ 240, /* samples */ { diff --git a/src/tests/roc_audio/test_samples/pcm_uint24_be.h b/src/tests/roc_audio/test_samples/pcm_uint24_be.h index 9f9d82505..b9af31eaf 100644 --- a/src/tests/roc_audio/test_samples/pcm_uint24_be.h +++ b/src/tests/roc_audio/test_samples/pcm_uint24_be.h @@ -14,7 +14,7 @@ namespace test { static SampleInfo sample_pcm_uint24_be = { /* name */ "pcm_uint24_be", - /* format */ PcmFormat_UInt24_Be, + /* format */ PcmSubformat_UInt24_Be, /* num_samples */ 240, /* samples */ { diff --git a/src/tests/roc_audio/test_samples/pcm_uint24_le.h b/src/tests/roc_audio/test_samples/pcm_uint24_le.h index ffb82bc60..a96343bae 100644 --- a/src/tests/roc_audio/test_samples/pcm_uint24_le.h +++ b/src/tests/roc_audio/test_samples/pcm_uint24_le.h @@ -14,7 +14,7 @@ namespace test { static SampleInfo sample_pcm_uint24_le = { /* name */ "pcm_uint24_le", - /* format */ PcmFormat_UInt24_Le, + /* format */ PcmSubformat_UInt24_Le, /* num_samples */ 240, /* samples */ { diff --git a/src/tests/roc_audio/test_samples/pcm_uint32_be.h b/src/tests/roc_audio/test_samples/pcm_uint32_be.h index 4fc6e9354..c4cc020fd 100644 --- a/src/tests/roc_audio/test_samples/pcm_uint32_be.h +++ b/src/tests/roc_audio/test_samples/pcm_uint32_be.h @@ -14,7 +14,7 @@ namespace test { static SampleInfo sample_pcm_uint32_be = { /* name */ "pcm_uint32_be", - /* format */ PcmFormat_UInt32_Be, + /* format */ PcmSubformat_UInt32_Be, /* num_samples */ 240, /* samples */ { diff --git a/src/tests/roc_audio/test_samples/pcm_uint32_le.h b/src/tests/roc_audio/test_samples/pcm_uint32_le.h index afa25310b..7c78b5f50 100644 --- a/src/tests/roc_audio/test_samples/pcm_uint32_le.h +++ b/src/tests/roc_audio/test_samples/pcm_uint32_le.h @@ -14,7 +14,7 @@ namespace test { static SampleInfo sample_pcm_uint32_le = { /* name */ "pcm_uint32_le", - /* format */ PcmFormat_UInt32_Le, + /* format */ PcmSubformat_UInt32_Le, /* num_samples */ 240, /* samples */ { diff --git a/src/tests/roc_audio/test_samples/pcm_uint8_be.h b/src/tests/roc_audio/test_samples/pcm_uint8_be.h index 6dddf94fe..5247a53e0 100644 --- a/src/tests/roc_audio/test_samples/pcm_uint8_be.h +++ b/src/tests/roc_audio/test_samples/pcm_uint8_be.h @@ -14,7 +14,7 @@ namespace test { static SampleInfo sample_pcm_uint8_be = { /* name */ "pcm_uint8_be", - /* format */ PcmFormat_UInt8_Be, + /* format */ PcmSubformat_UInt8_Be, /* num_samples */ 240, /* samples */ { diff --git a/src/tests/roc_audio/test_samples/pcm_uint8_le.h b/src/tests/roc_audio/test_samples/pcm_uint8_le.h index 16a515b09..baa56af9f 100644 --- a/src/tests/roc_audio/test_samples/pcm_uint8_le.h +++ b/src/tests/roc_audio/test_samples/pcm_uint8_le.h @@ -14,7 +14,7 @@ namespace test { static SampleInfo sample_pcm_uint8_le = { /* name */ "pcm_uint8_le", - /* format */ PcmFormat_UInt8_Le, + /* format */ PcmSubformat_UInt8_Le, /* num_samples */ 240, /* samples */ { diff --git a/src/tests/roc_audio/test_samples/sample_info.h b/src/tests/roc_audio/test_samples/sample_info.h index fda8150c1..5ec2be167 100644 --- a/src/tests/roc_audio/test_samples/sample_info.h +++ b/src/tests/roc_audio/test_samples/sample_info.h @@ -9,7 +9,7 @@ #ifndef ROC_AUDIO_TEST_SAMPLES_SAMPLE_INFO_H_ #define ROC_AUDIO_TEST_SAMPLES_SAMPLE_INFO_H_ -#include "roc_audio/pcm_format.h" +#include "roc_audio/pcm_subformat.h" #include "roc_core/stddefs.h" namespace roc { @@ -21,7 +21,7 @@ struct SampleInfo { const char* name; - PcmFormat format; + PcmSubformat format; size_t num_samples; float samples[MaxSamples]; diff --git a/src/tests/roc_audio/test_watchdog.cpp b/src/tests/roc_audio/test_watchdog.cpp index acc192603..39afffddd 100644 --- a/src/tests/roc_audio/test_watchdog.cpp +++ b/src/tests/roc_audio/test_watchdog.cpp @@ -36,7 +36,7 @@ enum { const sample_t magic_sample = 42; const SampleSpec sample_spec( - SampleRate, Sample_RawFormat, ChanLayout_Surround, ChanOrder_Smpte, ChMask); + SampleRate, PcmSubformat_Raw, ChanLayout_Surround, ChanOrder_Smpte, ChMask); core::HeapArena arena; FrameFactory frame_factory(arena, MaxBufSize * sizeof(sample_t)); diff --git a/src/tests/roc_core/test_string_list.cpp b/src/tests/roc_core/test_string_list.cpp index a006e14b7..fc97d806d 100644 --- a/src/tests/roc_core/test_string_list.cpp +++ b/src/tests/roc_core/test_string_list.cpp @@ -255,15 +255,15 @@ TEST(string_list, exponential_growth) { int num_reallocs = 0; int expected_reallocs[] = { - 1, 1, 1, // - 2, 2, 2, // - 3, 3, 3, 3, 3, 3, // - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // - 5, 5, 5, 5, 5, 5, 5 // + 1, 1, // + 2, 2, 2, // + 3, 3, 3, 3, 3, // + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // + 5, 5, 5, 5, 5 // }; for (size_t n = 0; n < ROC_ARRAY_SIZE(expected_reallocs); n++) { - CHECK(sl.push_back("123456789abcdef,123456789abcdef")); + CHECK(sl.push_back("123456789abcd,123456789abcd")); if (prev_front != sl.front()) { num_reallocs++; diff --git a/src/tests/roc_packet/test_delayed_reader.cpp b/src/tests/roc_packet/test_delayed_reader.cpp index 2dbb57203..d566ee98f 100644 --- a/src/tests/roc_packet/test_delayed_reader.cpp +++ b/src/tests/roc_packet/test_delayed_reader.cpp @@ -25,7 +25,7 @@ enum { SampleRate = 1000, NumSamples = 100, NumPackets = 30, MaxBufSize = 100 }; const core::nanoseconds_t NsPerSample = core::Second / SampleRate; const audio::SampleSpec sample_spec(SampleRate, - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, audio::ChanMask_Surround_Stereo); diff --git a/src/tests/roc_pipeline/bench_pipeline_loop_contention.cpp b/src/tests/roc_pipeline/bench_pipeline_loop_contention.cpp index 793e9f0f1..9b9036da3 100644 --- a/src/tests/roc_pipeline/bench_pipeline_loop_contention.cpp +++ b/src/tests/roc_pipeline/bench_pipeline_loop_contention.cpp @@ -55,7 +55,7 @@ class NoopPipeline : public PipelineLoop, : PipelineLoop(*this, config, audio::SampleSpec(SampleRate, - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, Chans), diff --git a/src/tests/roc_pipeline/bench_pipeline_loop_peak_load.cpp b/src/tests/roc_pipeline/bench_pipeline_loop_peak_load.cpp index e685d663e..6495782d8 100644 --- a/src/tests/roc_pipeline/bench_pipeline_loop_peak_load.cpp +++ b/src/tests/roc_pipeline/bench_pipeline_loop_peak_load.cpp @@ -279,7 +279,7 @@ class TestPipeline : public PipelineLoop, : PipelineLoop(*this, config, audio::SampleSpec(SampleRate, - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, Chans), diff --git a/src/tests/roc_pipeline/test_helpers/mock_sink.h b/src/tests/roc_pipeline/test_helpers/mock_sink.h index f7206cd91..aa0fcec5b 100644 --- a/src/tests/roc_pipeline/test_helpers/mock_sink.h +++ b/src/tests/roc_pipeline/test_helpers/mock_sink.h @@ -47,6 +47,10 @@ class MockSink : public sndio::ISink, public core::NonCopyable<> { return audio::SampleSpec(); } + core::nanoseconds_t frame_length() const { + return 0; + } + virtual bool has_state() const { return false; } diff --git a/src/tests/roc_pipeline/test_helpers/mock_source.h b/src/tests/roc_pipeline/test_helpers/mock_source.h index c145faef8..65c42207e 100644 --- a/src/tests/roc_pipeline/test_helpers/mock_source.h +++ b/src/tests/roc_pipeline/test_helpers/mock_source.h @@ -53,6 +53,10 @@ class MockSource : public sndio::ISource { return audio::SampleSpec(); } + core::nanoseconds_t frame_length() const { + return 0; + } + virtual bool has_state() const { return true; } diff --git a/src/tests/roc_pipeline/test_loopback_sink_2_source.cpp b/src/tests/roc_pipeline/test_loopback_sink_2_source.cpp index a0587279e..4c47930ec 100644 --- a/src/tests/roc_pipeline/test_loopback_sink_2_source.cpp +++ b/src/tests/roc_pipeline/test_loopback_sink_2_source.cpp @@ -50,10 +50,10 @@ namespace { const audio::ChannelMask Chans_Mono = audio::ChanMask_Surround_Mono; const audio::ChannelMask Chans_Stereo = audio::ChanMask_Surround_Stereo; -const audio::PcmFormat Format_Raw = audio::Sample_RawFormat; -const audio::PcmFormat Format_S16_Be = audio::PcmFormat_SInt16_Be; -const audio::PcmFormat Format_S16_Ne = audio::PcmFormat_SInt16; -const audio::PcmFormat Format_S32_Ne = audio::PcmFormat_SInt32; +const audio::PcmSubformat Format_Raw = audio::PcmSubformat_Raw; +const audio::PcmSubformat Format_S16_Be = audio::PcmSubformat_SInt16_Be; +const audio::PcmSubformat Format_S16_Ne = audio::PcmSubformat_SInt16; +const audio::PcmSubformat Format_S32_Ne = audio::PcmSubformat_SInt32; const rtp::PayloadType PayloadType_Ch1 = rtp::PayloadType_L16_Mono; const rtp::PayloadType PayloadType_Ch2 = rtp::PayloadType_L16_Stereo; @@ -244,14 +244,14 @@ class PacketProxy : core::NonCopyable<> { }; SenderSinkConfig make_sender_config(int flags, - audio::PcmFormat frame_format, + audio::PcmSubformat frame_format, audio::ChannelMask frame_channels, audio::ChannelMask packet_channels) { SenderSinkConfig config; + config.input_sample_spec.set_format(audio::Format_Pcm); + config.input_sample_spec.set_pcm_subformat(frame_format); config.input_sample_spec.set_sample_rate(SampleRate); - config.input_sample_spec.set_sample_format(audio::SampleFormat_Pcm); - config.input_sample_spec.set_pcm_format(frame_format); config.input_sample_spec.channel_set().set_layout(audio::ChanLayout_Surround); config.input_sample_spec.channel_set().set_order(audio::ChanOrder_Smpte); config.input_sample_spec.channel_set().set_mask(frame_channels); @@ -291,14 +291,14 @@ SenderSinkConfig make_sender_config(int flags, return config; } -ReceiverSourceConfig make_receiver_config(audio::PcmFormat frame_format, +ReceiverSourceConfig make_receiver_config(audio::PcmSubformat frame_format, audio::ChannelMask frame_channels, audio::ChannelMask packet_channels) { ReceiverSourceConfig config; + config.common.output_sample_spec.set_format(audio::Format_Pcm); + config.common.output_sample_spec.set_pcm_subformat(frame_format); config.common.output_sample_spec.set_sample_rate(SampleRate); - config.common.output_sample_spec.set_sample_format(audio::SampleFormat_Pcm); - config.common.output_sample_spec.set_pcm_format(frame_format); config.common.output_sample_spec.channel_set().set_layout(audio::ChanLayout_Surround); config.common.output_sample_spec.channel_set().set_order(audio::ChanOrder_Smpte); config.common.output_sample_spec.channel_set().set_mask(frame_channels); @@ -356,7 +356,7 @@ bool is_fec_supported(int flags) { void write_samples(test::FrameWriter& frame_writer, size_t n_samples, - audio::PcmFormat frame_format, + audio::PcmSubformat frame_format, const audio::SampleSpec& sample_spec, core::nanoseconds_t base_cts) { if (frame_format == Format_Raw) { @@ -373,7 +373,7 @@ void write_samples(test::FrameWriter& frame_writer, void read_samples(test::FrameReader& frame_reader, size_t n_samples, size_t n_sessions, - audio::PcmFormat frame_format, + audio::PcmSubformat frame_format, const audio::SampleSpec& sample_spec, core::nanoseconds_t base_cts) { if (frame_format == Format_Raw) { @@ -473,7 +473,7 @@ void check_metrics(ReceiverSlot& receiver, void send_receive(int flags, size_t num_sessions, - audio::PcmFormat frame_format, + audio::PcmSubformat frame_format, audio::ChannelMask frame_channels, audio::ChannelMask packet_channels) { packet::FifoQueue sender_outbound_queue; diff --git a/src/tests/roc_pipeline/test_pipeline_loop.cpp b/src/tests/roc_pipeline/test_pipeline_loop.cpp index f20fad2dd..3a070a0c5 100644 --- a/src/tests/roc_pipeline/test_pipeline_loop.cpp +++ b/src/tests/roc_pipeline/test_pipeline_loop.cpp @@ -59,7 +59,7 @@ core::SlabPool<core::Buffer> big_frame_buffer_pool("big_frame_buffer_pool", audio::FrameFactory big_frame_factory(frame_pool, big_frame_buffer_pool); const audio::SampleSpec sample_spec(SampleRate, - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, Chans); diff --git a/src/tests/roc_pipeline/test_receiver_source.cpp b/src/tests/roc_pipeline/test_receiver_source.cpp index 784c6a894..5186fa901 100644 --- a/src/tests/roc_pipeline/test_receiver_source.cpp +++ b/src/tests/roc_pipeline/test_receiver_source.cpp @@ -48,10 +48,10 @@ namespace { const audio::ChannelMask Chans_Mono = audio::ChanMask_Surround_Mono; const audio::ChannelMask Chans_Stereo = audio::ChanMask_Surround_Stereo; -const audio::PcmFormat Format_Raw = audio::Sample_RawFormat; -const audio::PcmFormat Format_S16_Be = audio::PcmFormat_SInt16_Be; -const audio::PcmFormat Format_S16_Ne = audio::PcmFormat_SInt16; -const audio::PcmFormat Format_S32_Ne = audio::PcmFormat_SInt32; +const audio::PcmSubformat Format_Raw = audio::PcmSubformat_Raw; +const audio::PcmSubformat Format_S16_Be = audio::PcmSubformat_SInt16_Be; +const audio::PcmSubformat Format_S16_Ne = audio::PcmSubformat_SInt16; +const audio::PcmSubformat Format_S32_Ne = audio::PcmSubformat_SInt32; const rtp::PayloadType PayloadType_Ch1 = rtp::PayloadType_L16_Mono; const rtp::PayloadType PayloadType_Ch2 = rtp::PayloadType_L16_Stereo; @@ -310,19 +310,19 @@ TEST_GROUP(receiver_source) { } void init_with_specs(int output_sample_rate, audio::ChannelMask output_channels, - audio::PcmFormat output_format, int packet_sample_rate, + audio::PcmSubformat output_format, int packet_sample_rate, audio::ChannelMask packet_channels, - audio::PcmFormat packet_format) { + audio::PcmSubformat packet_format) { + output_sample_spec.set_format(audio::Format_Pcm); + output_sample_spec.set_pcm_subformat(output_format); output_sample_spec.set_sample_rate((size_t)output_sample_rate); - output_sample_spec.set_sample_format(audio::SampleFormat_Pcm); - output_sample_spec.set_pcm_format(output_format); output_sample_spec.channel_set().set_layout(audio::ChanLayout_Surround); output_sample_spec.channel_set().set_order(audio::ChanOrder_Smpte); output_sample_spec.channel_set().set_mask(output_channels); + packet_sample_spec.set_format(audio::Format_Pcm); + packet_sample_spec.set_pcm_subformat(packet_format); packet_sample_spec.set_sample_rate((size_t)packet_sample_rate); - packet_sample_spec.set_sample_format(audio::SampleFormat_Pcm); - packet_sample_spec.set_pcm_format(packet_format); packet_sample_spec.channel_set().set_layout(audio::ChanLayout_Surround); packet_sample_spec.channel_set().set_order(audio::ChanOrder_Smpte); packet_sample_spec.channel_set().set_mask(packet_channels); @@ -2941,8 +2941,8 @@ TEST(receiver_source, big_read) { TEST(receiver_source, channel_mapping_stereo_to_mono) { enum { Rate = SampleRate, OutputChans = Chans_Mono, PacketChans = Chans_Stereo }; - const audio::PcmFormat OutputFormat = Format_Raw; - const audio::PcmFormat PacketFormat = Format_S16_Be; + const audio::PcmSubformat OutputFormat = Format_Raw; + const audio::PcmSubformat PacketFormat = Format_S16_Be; init_with_specs(Rate, OutputChans, OutputFormat, Rate, PacketChans, PacketFormat); @@ -2980,8 +2980,8 @@ TEST(receiver_source, channel_mapping_stereo_to_mono) { TEST(receiver_source, channel_mapping_mono_to_stereo) { enum { Rate = SampleRate, OutputChans = Chans_Stereo, PacketChans = Chans_Mono }; - const audio::PcmFormat OutputFormat = Format_Raw; - const audio::PcmFormat PacketFormat = Format_S16_Be; + const audio::PcmSubformat OutputFormat = Format_Raw; + const audio::PcmSubformat PacketFormat = Format_S16_Be; init_with_specs(Rate, OutputChans, OutputFormat, Rate, PacketChans, PacketFormat); @@ -3019,8 +3019,8 @@ TEST(receiver_source, channel_mapping_mono_to_stereo) { TEST(receiver_source, sample_rate_mapping) { enum { OutputRate = 48000, PacketRate = 44100, Chans = Chans_Stereo }; - const audio::PcmFormat OutputFormat = Format_Raw; - const audio::PcmFormat PacketFormat = Format_S16_Be; + const audio::PcmSubformat OutputFormat = Format_Raw; + const audio::PcmSubformat PacketFormat = Format_S16_Be; init_with_specs(OutputRate, Chans, OutputFormat, PacketRate, Chans, PacketFormat); @@ -3060,8 +3060,8 @@ TEST(receiver_source, sample_rate_mapping) { TEST(receiver_source, format_mapping_s16) { enum { Rate = SampleRate, Chans = Chans_Stereo }; - const audio::PcmFormat OutputFormat = Format_S16_Ne; - const audio::PcmFormat PacketFormat = Format_S16_Be; + const audio::PcmSubformat OutputFormat = Format_S16_Ne; + const audio::PcmSubformat PacketFormat = Format_S16_Be; init_with_specs(Rate, Chans, OutputFormat, Rate, Chans, PacketFormat); @@ -3098,8 +3098,8 @@ TEST(receiver_source, format_mapping_s16) { TEST(receiver_source, format_mapping_s32) { enum { Rate = SampleRate, Chans = Chans_Stereo }; - const audio::PcmFormat OutputFormat = Format_S32_Ne; - const audio::PcmFormat PacketFormat = Format_S16_Be; + const audio::PcmSubformat OutputFormat = Format_S32_Ne; + const audio::PcmSubformat PacketFormat = Format_S16_Be; init_with_specs(Rate, Chans, OutputFormat, Rate, Chans, PacketFormat); @@ -3319,8 +3319,8 @@ TEST(receiver_source, timestamp_mapping_remixing) { PacketChans = Chans_Mono }; - const audio::PcmFormat OutputFormat = Format_S16_Ne; - const audio::PcmFormat PacketFormat = Format_S16_Be; + const audio::PcmSubformat OutputFormat = Format_S16_Ne; + const audio::PcmSubformat PacketFormat = Format_S16_Be; init_with_specs(OutputRate, OutputChans, OutputFormat, PacketRate, PacketChans, PacketFormat); diff --git a/src/tests/roc_pipeline/test_sender_sink.cpp b/src/tests/roc_pipeline/test_sender_sink.cpp index dcb5e84be..ef53f6679 100644 --- a/src/tests/roc_pipeline/test_sender_sink.cpp +++ b/src/tests/roc_pipeline/test_sender_sink.cpp @@ -44,10 +44,10 @@ namespace { const audio::ChannelMask Chans_Mono = audio::ChanMask_Surround_Mono; const audio::ChannelMask Chans_Stereo = audio::ChanMask_Surround_Stereo; -const audio::PcmFormat Format_Raw = audio::Sample_RawFormat; -const audio::PcmFormat Format_S16_Be = audio::PcmFormat_SInt16_Be; -const audio::PcmFormat Format_S16_Ne = audio::PcmFormat_SInt16; -const audio::PcmFormat Format_S32_Ne = audio::PcmFormat_SInt32; +const audio::PcmSubformat Format_Raw = audio::PcmSubformat_Raw; +const audio::PcmSubformat Format_S16_Be = audio::PcmSubformat_SInt16_Be; +const audio::PcmSubformat Format_S16_Ne = audio::PcmSubformat_SInt16; +const audio::PcmSubformat Format_S32_Ne = audio::PcmSubformat_SInt32; const rtp::PayloadType PayloadType_Ch1 = rtp::PayloadType_L16_Mono; const rtp::PayloadType PayloadType_Ch2 = rtp::PayloadType_L16_Stereo; @@ -171,19 +171,19 @@ TEST_GROUP(sender_sink) { } void init_with_specs(int input_sample_rate, audio::ChannelMask input_channels, - audio::PcmFormat input_format, int packet_sample_rate, + audio::PcmSubformat input_format, int packet_sample_rate, audio::ChannelMask packet_channels, - audio::PcmFormat packet_format) { + audio::PcmSubformat packet_format) { + input_sample_spec.set_format(audio::Format_Pcm); + input_sample_spec.set_pcm_subformat(input_format); input_sample_spec.set_sample_rate((size_t)input_sample_rate); - input_sample_spec.set_sample_format(audio::SampleFormat_Pcm); - input_sample_spec.set_pcm_format(input_format); input_sample_spec.channel_set().set_layout(audio::ChanLayout_Surround); input_sample_spec.channel_set().set_order(audio::ChanOrder_Smpte); input_sample_spec.channel_set().set_mask(input_channels); + packet_sample_spec.set_format(audio::Format_Pcm); + packet_sample_spec.set_pcm_subformat(packet_format); packet_sample_spec.set_sample_rate((size_t)packet_sample_rate); - packet_sample_spec.set_sample_format(audio::SampleFormat_Pcm); - packet_sample_spec.set_pcm_format(packet_format); packet_sample_spec.channel_set().set_layout(audio::ChanLayout_Surround); packet_sample_spec.channel_set().set_order(audio::ChanOrder_Smpte); packet_sample_spec.channel_set().set_mask(packet_channels); @@ -308,8 +308,8 @@ TEST(sender_sink, frame_size_large) { TEST(sender_sink, channel_mapping_stereo_to_mono) { enum { Rate = SampleRate, InputChans = Chans_Stereo, PacketChans = Chans_Mono }; - const audio::PcmFormat InputFormat = Format_Raw; - const audio::PcmFormat PacketFormat = Format_S16_Be; + const audio::PcmSubformat InputFormat = Format_Raw; + const audio::PcmSubformat PacketFormat = Format_S16_Be; init_with_specs(Rate, InputChans, InputFormat, Rate, PacketChans, PacketFormat); @@ -343,8 +343,8 @@ TEST(sender_sink, channel_mapping_stereo_to_mono) { TEST(sender_sink, channel_mapping_mono_to_stereo) { enum { Rate = SampleRate, InputChans = Chans_Mono, PacketChans = Chans_Stereo }; - const audio::PcmFormat InputFormat = Format_Raw; - const audio::PcmFormat PacketFormat = Format_S16_Be; + const audio::PcmSubformat InputFormat = Format_Raw; + const audio::PcmSubformat PacketFormat = Format_S16_Be; init_with_specs(Rate, InputChans, InputFormat, Rate, PacketChans, PacketFormat); @@ -378,8 +378,8 @@ TEST(sender_sink, channel_mapping_mono_to_stereo) { TEST(sender_sink, sample_rate_mapping) { enum { InputRate = 48000, PacketRate = 44100, Chans = Chans_Stereo }; - const audio::PcmFormat InputFormat = Format_Raw; - const audio::PcmFormat PacketFormat = Format_S16_Be; + const audio::PcmSubformat InputFormat = Format_Raw; + const audio::PcmSubformat PacketFormat = Format_S16_Be; init_with_specs(InputRate, Chans, InputFormat, PacketRate, Chans, PacketFormat); @@ -413,8 +413,8 @@ TEST(sender_sink, sample_rate_mapping) { TEST(sender_sink, format_mapping_s16) { enum { Rate = SampleRate, Chans = Chans_Stereo }; - const audio::PcmFormat InputFormat = Format_S16_Ne; - const audio::PcmFormat PacketFormat = Format_S16_Be; + const audio::PcmSubformat InputFormat = Format_S16_Ne; + const audio::PcmSubformat PacketFormat = Format_S16_Be; init_with_specs(Rate, Chans, InputFormat, Rate, Chans, PacketFormat); @@ -447,8 +447,8 @@ TEST(sender_sink, format_mapping_s16) { TEST(sender_sink, format_mapping_s32) { enum { Rate = SampleRate, Chans = Chans_Stereo }; - const audio::PcmFormat InputFormat = Format_S32_Ne; - const audio::PcmFormat PacketFormat = Format_S16_Be; + const audio::PcmSubformat InputFormat = Format_S32_Ne; + const audio::PcmSubformat PacketFormat = Format_S16_Be; init_with_specs(Rate, Chans, InputFormat, Rate, Chans, PacketFormat); @@ -519,8 +519,8 @@ TEST(sender_sink, timestamp_mapping_remixing) { PacketChans = Chans_Mono }; - const audio::PcmFormat InputFormat = Format_S16_Ne; - const audio::PcmFormat PacketFormat = Format_S16_Be; + const audio::PcmSubformat InputFormat = Format_S16_Ne; + const audio::PcmSubformat PacketFormat = Format_S16_Be; init_with_specs(InputRate, InputChans, InputFormat, PacketRate, PacketChans, PacketFormat); diff --git a/src/tests/roc_pipeline/test_transcoder_sink.cpp b/src/tests/roc_pipeline/test_transcoder_sink.cpp index cf7b8b5a8..889274114 100644 --- a/src/tests/roc_pipeline/test_transcoder_sink.cpp +++ b/src/tests/roc_pipeline/test_transcoder_sink.cpp @@ -63,16 +63,16 @@ TEST_GROUP(transcoder_sink) { void init(int input_sample_rate, audio::ChannelMask input_channels, int output_sample_rate, audio::ChannelMask output_channels) { + input_sample_spec.set_format(audio::Format_Pcm); + input_sample_spec.set_pcm_subformat(audio::PcmSubformat_Raw); input_sample_spec.set_sample_rate((size_t)input_sample_rate); - input_sample_spec.set_sample_format(audio::SampleFormat_Pcm); - input_sample_spec.set_pcm_format(audio::Sample_RawFormat); input_sample_spec.channel_set().set_layout(audio::ChanLayout_Surround); input_sample_spec.channel_set().set_order(audio::ChanOrder_Smpte); input_sample_spec.channel_set().set_mask(input_channels); + output_sample_spec.set_format(audio::Format_Pcm); + output_sample_spec.set_pcm_subformat(audio::PcmSubformat_Raw); output_sample_spec.set_sample_rate((size_t)output_sample_rate); - output_sample_spec.set_sample_format(audio::SampleFormat_Pcm); - output_sample_spec.set_pcm_format(audio::Sample_RawFormat); output_sample_spec.channel_set().set_layout(audio::ChanLayout_Surround); output_sample_spec.channel_set().set_order(audio::ChanOrder_Smpte); output_sample_spec.channel_set().set_mask(output_channels); diff --git a/src/tests/roc_pipeline/test_transcoder_source.cpp b/src/tests/roc_pipeline/test_transcoder_source.cpp index 6013503e7..3d90358fa 100644 --- a/src/tests/roc_pipeline/test_transcoder_source.cpp +++ b/src/tests/roc_pipeline/test_transcoder_source.cpp @@ -74,16 +74,16 @@ TEST_GROUP(transcoder_source) { } void init(audio::ChannelMask input_channels, audio::ChannelMask output_channels) { + input_sample_spec.set_format(audio::Format_Pcm); + input_sample_spec.set_pcm_subformat(audio::PcmSubformat_Raw); input_sample_spec.set_sample_rate(SampleRate); - input_sample_spec.set_sample_format(audio::SampleFormat_Pcm); - input_sample_spec.set_pcm_format(audio::Sample_RawFormat); input_sample_spec.channel_set().set_layout(audio::ChanLayout_Surround); input_sample_spec.channel_set().set_order(audio::ChanOrder_Smpte); input_sample_spec.channel_set().set_mask(input_channels); + output_sample_spec.set_format(audio::Format_Pcm); + output_sample_spec.set_pcm_subformat(audio::PcmSubformat_Raw); output_sample_spec.set_sample_rate(SampleRate); - output_sample_spec.set_sample_format(audio::SampleFormat_Pcm); - output_sample_spec.set_pcm_format(audio::Sample_RawFormat); output_sample_spec.channel_set().set_layout(audio::ChanLayout_Surround); output_sample_spec.channel_set().set_order(audio::ChanOrder_Smpte); output_sample_spec.channel_set().set_mask(output_channels); diff --git a/src/tests/roc_rtp/test_encoding.cpp b/src/tests/roc_rtp/test_encoding.cpp index ab2674c04..924a222d2 100644 --- a/src/tests/roc_rtp/test_encoding.cpp +++ b/src/tests/roc_rtp/test_encoding.cpp @@ -17,13 +17,13 @@ TEST_GROUP(encoding) {}; IGNORE_TEST(encoding, parse) { Encoding enc; - CHECK(parse_encoding("101:s18/48000/surround4.1", enc)); + CHECK(parse_encoding("101:pcm@s18/48000/surround4.1", enc)); CHECK_EQUAL(101, enc.payload_type); - CHECK(enc.sample_spec.is_valid()); - CHECK_EQUAL(audio::SampleFormat_Pcm, enc.sample_spec.sample_format()); - CHECK_EQUAL(audio::PcmFormat_SInt18, enc.sample_spec.pcm_format()); + CHECK(enc.sample_spec.is_complete()); + CHECK_EQUAL(audio::Format_Pcm, enc.sample_spec.format()); + CHECK_EQUAL(audio::PcmSubformat_SInt18, enc.sample_spec.pcm_subformat()); CHECK_EQUAL(48000, enc.sample_spec.sample_rate()); CHECK_EQUAL(5, enc.sample_spec.num_channels()); @@ -35,20 +35,20 @@ IGNORE_TEST(encoding, parse) { TEST(encoding, parse_errors) { Encoding enc; - CHECK(!parse_encoding(":s16/44100/stereo", enc)); - CHECK(!parse_encoding("101,s16/44100/stereo", enc)); + CHECK(!parse_encoding(":pcm@s16/44100/stereo", enc)); + CHECK(!parse_encoding("101,pcm@s16/44100/stereo", enc)); CHECK(!parse_encoding("101:", enc)); - CHECK(!parse_encoding("101:s16/44100/bad", enc)); + CHECK(!parse_encoding("101:pcm@s16/44100/bad", enc)); CHECK(!parse_encoding(":", enc)); CHECK(!parse_encoding("", enc)); CHECK(!parse_encoding("::", enc)); - CHECK(!parse_encoding("101::s16/44100/stereo", enc)); - CHECK(!parse_encoding("xxx:s16/44100/stereo", enc)); - CHECK(!parse_encoding("-101:s16/44100/stereo", enc)); - CHECK(!parse_encoding("+101:s16/44100/stereo", enc)); - CHECK(!parse_encoding("101.2:s16/44100/stereo", enc)); + CHECK(!parse_encoding("101::pcm@s16/44100/stereo", enc)); + CHECK(!parse_encoding("xxx:pcm@s16/44100/stereo", enc)); + CHECK(!parse_encoding("-101:pcm@s16/44100/stereo", enc)); + CHECK(!parse_encoding("+101:pcm@s16/44100/stereo", enc)); + CHECK(!parse_encoding("101.2:pcm@s16/44100/stereo", enc)); - CHECK(parse_encoding("101:s16/44100/stereo", enc)); + CHECK(parse_encoding("101:pcm@s16/44100/stereo", enc)); } } // namespace rtp diff --git a/src/tests/roc_rtp/test_encoding_map.cpp b/src/tests/roc_rtp/test_encoding_map.cpp index efe72a23e..a9ba8efff 100644 --- a/src/tests/roc_rtp/test_encoding_map.cpp +++ b/src/tests/roc_rtp/test_encoding_map.cpp @@ -10,7 +10,7 @@ #include "roc_audio/pcm_decoder.h" #include "roc_audio/pcm_encoder.h" -#include "roc_audio/pcm_format.h" +#include "roc_audio/pcm_subformat.h" #include "roc_core/heap_arena.h" #include "roc_rtp/encoding_map.h" @@ -35,9 +35,9 @@ TEST(encoding_map, find_by_pt) { LONGS_EQUAL(PayloadType_L16_Mono, enc->payload_type); - CHECK(enc->sample_spec.is_valid()); + CHECK(enc->sample_spec.is_complete()); CHECK(enc->sample_spec - == audio::SampleSpec(44100, audio::PcmFormat_SInt16_Be, + == audio::SampleSpec(44100, audio::PcmSubformat_SInt16_Be, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, audio::ChanMask_Surround_Mono)); @@ -53,9 +53,9 @@ TEST(encoding_map, find_by_pt) { LONGS_EQUAL(PayloadType_L16_Stereo, enc->payload_type); - CHECK(enc->sample_spec.is_valid()); + CHECK(enc->sample_spec.is_complete()); CHECK(enc->sample_spec - == audio::SampleSpec(44100, audio::PcmFormat_SInt16_Be, + == audio::SampleSpec(44100, audio::PcmSubformat_SInt16_Be, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, audio::ChanMask_Surround_Stereo)); @@ -71,7 +71,7 @@ TEST(encoding_map, find_by_spec) { { const Encoding* enc = enc_map.find_by_spec(audio::SampleSpec( - 48000, audio::PcmFormat_SInt16_Be, audio::ChanLayout_Surround, + 48000, audio::PcmSubformat_SInt16_Be, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, audio::ChanMask_Surround_Mono)); CHECK(!enc); @@ -79,7 +79,7 @@ TEST(encoding_map, find_by_spec) { { const Encoding* enc = enc_map.find_by_spec(audio::SampleSpec( - 44100, audio::PcmFormat_SInt16_Be, audio::ChanLayout_Surround, + 44100, audio::PcmSubformat_SInt16_Be, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, audio::ChanMask_Surround_Mono)); CHECK(enc); @@ -89,7 +89,7 @@ TEST(encoding_map, find_by_spec) { { const Encoding* enc = enc_map.find_by_spec(audio::SampleSpec( - 44100, audio::PcmFormat_SInt16_Be, audio::ChanLayout_Surround, + 44100, audio::PcmSubformat_SInt16_Be, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, audio::ChanMask_Surround_Stereo)); CHECK(enc); @@ -105,9 +105,9 @@ TEST(encoding_map, add_encoding) { Encoding enc; enc.payload_type = (PayloadType)100; enc.packet_flags = packet::Packet::FlagAudio; - enc.sample_spec = - audio::SampleSpec(48000, audio::PcmFormat_SInt32, audio::ChanLayout_Surround, - audio::ChanOrder_Smpte, audio::ChanMask_Surround_Stereo); + enc.sample_spec = audio::SampleSpec( + 48000, audio::PcmSubformat_SInt32, audio::ChanLayout_Surround, + audio::ChanOrder_Smpte, audio::ChanMask_Surround_Stereo); enc.new_encoder = &audio::PcmEncoder::construct; enc.new_decoder = &audio::PcmDecoder::construct; @@ -121,7 +121,7 @@ TEST(encoding_map, add_encoding) { LONGS_EQUAL(100, enc->payload_type); CHECK(enc->sample_spec - == audio::SampleSpec(48000, audio::PcmFormat_SInt32, + == audio::SampleSpec(48000, audio::PcmSubformat_SInt32, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, audio::ChanMask_Surround_Stereo)); @@ -132,15 +132,15 @@ TEST(encoding_map, add_encoding) { } { - const Encoding* enc = enc_map.find_by_spec( - audio::SampleSpec(48000, audio::PcmFormat_SInt32, audio::ChanLayout_Surround, - audio::ChanOrder_Smpte, audio::ChanMask_Surround_Stereo)); + const Encoding* enc = enc_map.find_by_spec(audio::SampleSpec( + 48000, audio::PcmSubformat_SInt32, audio::ChanLayout_Surround, + audio::ChanOrder_Smpte, audio::ChanMask_Surround_Stereo)); CHECK(enc); LONGS_EQUAL(100, enc->payload_type); CHECK(enc->sample_spec - == audio::SampleSpec(48000, audio::PcmFormat_SInt32, + == audio::SampleSpec(48000, audio::PcmSubformat_SInt32, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, audio::ChanMask_Surround_Stereo)); diff --git a/src/tests/roc_rtp/test_filter.cpp b/src/tests/roc_rtp/test_filter.cpp index 822bbd32c..6a11edf19 100644 --- a/src/tests/roc_rtp/test_filter.cpp +++ b/src/tests/roc_rtp/test_filter.cpp @@ -37,7 +37,7 @@ enum { }; const audio::SampleSpec payload_spec(SampleRate, - audio::PcmFormat_SInt16_Be, + audio::PcmSubformat_SInt16_Be, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, ChMask); diff --git a/src/tests/roc_rtp/test_link_meter.cpp b/src/tests/roc_rtp/test_link_meter.cpp index b00f1e010..06e951622 100644 --- a/src/tests/roc_rtp/test_link_meter.cpp +++ b/src/tests/roc_rtp/test_link_meter.cpp @@ -41,7 +41,7 @@ packet::PacketFactory packet_factory(arena, PacketSz); EncodingMap encoding_map(arena); audio::SampleSpec sample_spec(SampleRate, - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, ChMask); diff --git a/src/tests/roc_rtp/test_timestamp_extractor.cpp b/src/tests/roc_rtp/test_timestamp_extractor.cpp index f87e3947c..2117245b6 100644 --- a/src/tests/roc_rtp/test_timestamp_extractor.cpp +++ b/src/tests/roc_rtp/test_timestamp_extractor.cpp @@ -46,7 +46,7 @@ TEST_GROUP(timestamp_extractor) {}; TEST(timestamp_extractor, single_write) { // 1 second = 1000 samples const audio::SampleSpec sample_spec = - audio::SampleSpec(1000, audio::Sample_RawFormat, audio::ChanLayout_Surround, + audio::SampleSpec(1000, audio::PcmSubformat_Raw, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, 0x1); const core::nanoseconds_t cts = 1691499037871419405; @@ -84,7 +84,7 @@ TEST(timestamp_extractor, single_write) { TEST(timestamp_extractor, forward_error) { // 1 second = 1000 samples const audio::SampleSpec sample_spec = - audio::SampleSpec(1000, audio::Sample_RawFormat, audio::ChanLayout_Surround, + audio::SampleSpec(1000, audio::PcmSubformat_Raw, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, 0x1); const status::StatusCode status_list[] = { diff --git a/src/tests/roc_rtp/test_timestamp_injector.cpp b/src/tests/roc_rtp/test_timestamp_injector.cpp index 0525676ca..ed20c6c27 100644 --- a/src/tests/roc_rtp/test_timestamp_injector.cpp +++ b/src/tests/roc_rtp/test_timestamp_injector.cpp @@ -69,7 +69,7 @@ TEST(timestamp_injector, negative_and_positive_dn) { const float sample_rate = 48000.; const audio::SampleSpec sample_spec = - audio::SampleSpec((size_t)sample_rate, audio::Sample_RawFormat, + audio::SampleSpec((size_t)sample_rate, audio::PcmSubformat_Raw, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, ChMask); packet::stream_timestamp_t rtp_ts = 2222; @@ -120,7 +120,7 @@ TEST(timestamp_injector, fetch_peek) { }; const audio::SampleSpec sample_spec = - audio::SampleSpec(SampleRate, audio::Sample_RawFormat, audio::ChanLayout_Surround, + audio::SampleSpec(SampleRate, audio::PcmSubformat_Raw, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, ChMask); packet::FifoQueue queue; @@ -167,7 +167,7 @@ TEST(timestamp_injector, forward_error) { }; const audio::SampleSpec sample_spec = - audio::SampleSpec(SampleRate, audio::Sample_RawFormat, audio::ChanLayout_Surround, + audio::SampleSpec(SampleRate, audio::PcmSubformat_Raw, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, ChMask); const status::StatusCode status_list[] = { diff --git a/src/tests/roc_sndio/test_backend_sink.cpp b/src/tests/roc_sndio/test_backend_sink.cpp deleted file mode 100644 index abc59d1e7..000000000 --- a/src/tests/roc_sndio/test_backend_sink.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2015 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 <CppUTest/TestHarness.h> - -#include "test_helpers/utils.h" - -#include "roc_core/heap_arena.h" -#include "roc_core/scoped_ptr.h" -#include "roc_dbgio/temp_file.h" -#include "roc_sndio/backend_map.h" -#include "roc_sndio/io_pump.h" - -namespace roc { -namespace sndio { - -namespace { - -enum { FrameSize = 500, SampleRate = 48000 }; - -const audio::SampleSpec sample_spec(SampleRate, - audio::Sample_RawFormat, - audio::ChanLayout_Surround, - audio::ChanOrder_Smpte, - audio::ChanMask_Surround_Stereo); - -const core::nanoseconds_t frame_duration = FrameSize * core::Second - / core::nanoseconds_t(sample_spec.sample_rate() * sample_spec.num_channels()); - -core::HeapArena arena; -audio::FrameFactory frame_factory(arena, FrameSize * sizeof(audio::sample_t)); - -} // namespace - -TEST_GROUP(backend_sink) { - IoConfig sink_config; - - void setup() { - sink_config.sample_spec = sample_spec; - sink_config.frame_length = frame_duration; - } -}; - -TEST(backend_sink, open) { - for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); - n_backend++) { - IBackend& backend = BackendMap::instance().nth_backend(n_backend); - if (!test::backend_supports_format(backend, arena, "wav")) { - continue; - } - - dbgio::TempFile file("test.wav"); - - core::ScopedPtr<ISink> backend_sink; - test::expect_open_sink(status::StatusOK, backend, frame_factory, arena, - DriverType_File, NULL, file.path(), sink_config, - backend_sink); - - test::expect_specs_equal(backend.name(), sink_config.sample_spec, - backend_sink->sample_spec()); - - CHECK(!backend_sink->has_state()); - CHECK(!backend_sink->has_latency()); - CHECK(!backend_sink->has_clock()); - LONGS_EQUAL(status::StatusOK, backend_sink->close()); - } -} - -// Open fails because file doesn't exist. -TEST(backend_sink, open_bad_file) { - for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); - n_backend++) { - IBackend& backend = BackendMap::instance().nth_backend(n_backend); - if (!test::backend_supports_format(backend, arena, "wav")) { - continue; - } - - core::ScopedPtr<ISink> backend_sink; - test::expect_open_sink(status::StatusErrFile, backend, frame_factory, arena, - DriverType_File, NULL, "/bad/file.wav", sink_config, - backend_sink); - } -} - -// Open fails because of invalid sndio::Config. -TEST(backend_sink, open_bad_config) { - for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); - n_backend++) { - IBackend& backend = BackendMap::instance().nth_backend(n_backend); - if (!test::backend_supports_format(backend, arena, "wav")) { - continue; - } - - dbgio::TempFile file("test.wav"); - - IoConfig bad_config = sink_config; - bad_config.sample_spec.set_pcm_format(audio::PcmFormat_SInt18_3_Be); - - core::ScopedPtr<ISink> backend_sink; - test::expect_open_sink(status::StatusBadConfig, backend, frame_factory, arena, - DriverType_File, NULL, file.path(), bad_config, - backend_sink); - } -} - -// If config is empty, open uses default values. -TEST(backend_sink, open_default_config) { - for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); - n_backend++) { - IBackend& backend = BackendMap::instance().nth_backend(n_backend); - if (!test::backend_supports_format(backend, arena, "wav")) { - continue; - } - - dbgio::TempFile file("test.wav"); - - IoConfig default_config = sink_config; - default_config.sample_spec.clear(); - - core::ScopedPtr<ISink> backend_sink; - test::expect_open_sink(status::StatusOK, backend, frame_factory, arena, - DriverType_File, NULL, file.path(), default_config, - backend_sink); - - CHECK(backend_sink->sample_spec().is_valid()); - LONGS_EQUAL(status::StatusOK, backend_sink->close()); - } -} - -} // namespace sndio -} // namespace roc diff --git a/src/tests/roc_sndio/test_backend_source.cpp b/src/tests/roc_sndio/test_backend_source.cpp deleted file mode 100644 index c133ccd6c..000000000 --- a/src/tests/roc_sndio/test_backend_source.cpp +++ /dev/null @@ -1,231 +0,0 @@ -/* - * 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 <CppUTest/TestHarness.h> - -#include "test_helpers/mock_source.h" -#include "test_helpers/utils.h" - -#include "roc_core/heap_arena.h" -#include "roc_core/scoped_ptr.h" -#include "roc_core/slab_pool.h" -#include "roc_dbgio/temp_file.h" -#include "roc_sndio/backend_map.h" -#include "roc_sndio/io_pump.h" - -namespace roc { -namespace sndio { - -namespace { - -enum { MaxBufSize = 8192, FrameSize = 500, SampleRate = 48000 }; - -const audio::SampleSpec sample_spec(SampleRate, - audio::Sample_RawFormat, - audio::ChanLayout_Surround, - audio::ChanOrder_Smpte, - audio::ChanMask_Surround_Stereo); - -const core::nanoseconds_t frame_duration = FrameSize * core::Second - / core::nanoseconds_t(sample_spec.sample_rate() * sample_spec.num_channels()); - -core::HeapArena arena; - -core::SlabPool<audio::Frame> frame_pool("frame_pool", arena); -core::SlabPool<core::Buffer> - frame_buffer_pool("frame_buffer_pool", - arena, - sizeof(core::Buffer) + MaxBufSize * sizeof(audio::sample_t)); - -audio::FrameFactory frame_factory(frame_pool, frame_buffer_pool); - -void write_wav(IBackend& backend, - const IoConfig& config, - const char* path, - size_t num_samples) { - test::MockSource mock_source(frame_factory, config.sample_spec, arena); - mock_source.add(num_samples * sample_spec.num_channels()); - - IDevice* backend_device = NULL; - LONGS_EQUAL(status::StatusOK, - backend.open_device(DeviceType_Sink, DriverType_File, NULL, path, config, - frame_factory, arena, &backend_device)); - CHECK(backend_device != NULL); - core::ScopedPtr<ISink> backend_sink(backend_device->to_sink()); - CHECK(backend_sink != NULL); - - IoPump pump(frame_pool, frame_buffer_pool, mock_source, NULL, *backend_sink, config, - IoPump::ModeOneshot); - LONGS_EQUAL(status::StatusOK, pump.init_status()); - LONGS_EQUAL(status::StatusOK, pump.run()); -} - -void expect_read(status::StatusCode expected_code, - ISource& source, - audio::Frame& frame, - packet::stream_timestamp_t requested_samples) { - const status::StatusCode code = - source.read(frame, requested_samples, audio::ModeHard); - - LONGS_EQUAL(expected_code, code); -} - -} // namespace - -TEST_GROUP(backend_source) { - IoConfig sink_config; - IoConfig source_config; - - void setup() { - sink_config.sample_spec = sample_spec; - sink_config.frame_length = frame_duration; - - source_config.sample_spec = audio::SampleSpec(); - source_config.frame_length = frame_duration; - } -}; - -TEST(backend_source, open) { - for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); - n_backend++) { - IBackend& backend = BackendMap::instance().nth_backend(n_backend); - if (!test::backend_supports_format(backend, arena, "wav")) { - continue; - } - - dbgio::TempFile file("test.wav"); - write_wav(backend, sink_config, file.path(), MaxBufSize * 10); - - core::ScopedPtr<ISource> backend_source; - test::expect_open_source(status::StatusOK, backend, frame_factory, arena, - DriverType_File, NULL, file.path(), source_config, - backend_source); - - test::expect_specs_equal(backend.name(), sink_config.sample_spec, - backend_source->sample_spec()); - - CHECK(!backend_source->has_state()); - CHECK(!backend_source->has_latency()); - CHECK(!backend_source->has_clock()); - LONGS_EQUAL(status::StatusOK, backend_source->close()); - } -} - -// Open fails because file doesn't exist. -TEST(backend_source, open_bad_file) { - for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); - n_backend++) { - IBackend& backend = BackendMap::instance().nth_backend(n_backend); - if (!test::backend_supports_format(backend, arena, "wav")) { - continue; - } - - core::ScopedPtr<ISource> backend_source; - test::expect_open_source(status::StatusErrFile, backend, frame_factory, arena, - DriverType_File, NULL, "/bad/file.wav", source_config, - backend_source); - } -} - -// Open fails because of invalid sndio::Config. -TEST(backend_source, open_bad_config) { - for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); - n_backend++) { - IBackend& backend = BackendMap::instance().nth_backend(n_backend); - if (!test::backend_supports_format(backend, arena, "wav")) { - continue; - } - - dbgio::TempFile file("test.wav"); - write_wav(backend, sink_config, file.path(), MaxBufSize * 10); - - IoConfig bad_config = source_config; - bad_config.sample_spec.set_sample_rate(SampleRate); - - core::ScopedPtr<ISource> backend_source; - test::expect_open_source(status::StatusBadConfig, backend, frame_factory, arena, - DriverType_File, NULL, file.path(), bad_config, - backend_source); - } -} - -// Rewind and read same frame again. -TEST(backend_source, rewind) { - for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); - n_backend++) { - IBackend& backend = BackendMap::instance().nth_backend(n_backend); - if (!test::backend_supports_format(backend, arena, "wav")) { - continue; - } - - dbgio::TempFile file("test.wav"); - write_wav(backend, sink_config, file.path(), MaxBufSize * 10); - - core::ScopedPtr<ISource> backend_source; - test::expect_open_source(status::StatusOK, backend, frame_factory, arena, - DriverType_File, "wav", file.path(), source_config, - backend_source); - - audio::FramePtr frame1 = frame_factory.allocate_frame_no_buffer(); - CHECK(frame1); - expect_read(status::StatusOK, *backend_source, *frame1, FrameSize); - - // rewind - LONGS_EQUAL(status::StatusOK, backend_source->rewind()); - - audio::FramePtr frame2 = frame_factory.allocate_frame_no_buffer(); - CHECK(frame2); - expect_read(status::StatusOK, *backend_source, *frame2, FrameSize); - - LONGS_EQUAL(FrameSize * sample_spec.num_channels(), frame1->num_raw_samples()); - LONGS_EQUAL(FrameSize * sample_spec.num_channels(), frame2->num_raw_samples()); - - if (memcmp(frame1->raw_samples(), frame2->raw_samples(), - frame1->num_raw_samples() * sizeof(audio::sample_t)) - != 0) { - FAIL("frames should be equal"); - } - LONGS_EQUAL(status::StatusOK, backend_source->close()); - } -} - -// Read until EOF, rewind, repeat. -TEST(backend_source, rewind_after_eof) { - for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); - n_backend++) { - IBackend& backend = BackendMap::instance().nth_backend(n_backend); - if (!test::backend_supports_format(backend, arena, "wav")) { - continue; - } - - dbgio::TempFile file("test.wav"); - write_wav(backend, sink_config, file.path(), FrameSize * 2); - - core::ScopedPtr<ISource> backend_source; - test::expect_open_source(status::StatusOK, backend, frame_factory, arena, - DriverType_File, "wav", file.path(), source_config, - backend_source); - - audio::FramePtr frame = frame_factory.allocate_frame_no_buffer(); - CHECK(frame); - - for (int i = 0; i < 10; i++) { - expect_read(status::StatusOK, *backend_source, *frame, FrameSize); - expect_read(status::StatusOK, *backend_source, *frame, FrameSize); - expect_read(status::StatusFinish, *backend_source, *frame, FrameSize); - - // rewind - LONGS_EQUAL(status::StatusOK, backend_source->rewind()); - } - LONGS_EQUAL(status::StatusOK, backend_source->close()); - } -} - -} // namespace sndio - -} // namespace roc diff --git a/src/tests/roc_sndio/test_helpers/mock_sink.h b/src/tests/roc_sndio/test_helpers/mock_sink.h index 5ad9b4f67..fbfd042a0 100644 --- a/src/tests/roc_sndio/test_helpers/mock_sink.h +++ b/src/tests/roc_sndio/test_helpers/mock_sink.h @@ -41,6 +41,10 @@ class MockSink : public ISink { return audio::SampleSpec(); } + core::nanoseconds_t frame_length() const { + return 0; + } + virtual bool has_state() const { return true; } diff --git a/src/tests/roc_sndio/test_helpers/mock_source.h b/src/tests/roc_sndio/test_helpers/mock_source.h index 2c127753f..465cbec2e 100644 --- a/src/tests/roc_sndio/test_helpers/mock_source.h +++ b/src/tests/roc_sndio/test_helpers/mock_source.h @@ -46,7 +46,11 @@ class MockSource : public ISource { } virtual audio::SampleSpec sample_spec() const { - return audio::SampleSpec(); + return sample_spec_; + } + + core::nanoseconds_t frame_length() const { + return 0; } virtual bool has_state() const { @@ -97,24 +101,24 @@ class MockSource : public ISource { frame, sample_spec_.stream_timestamp_2_bytes(duration))); frame.set_raw(true); - frame.set_duration(duration); - size_t ns = frame.num_raw_samples(); - if (ns > size_ - pos_) { - ns = size_ - pos_; + size_t n_samples = frame.num_raw_samples(); + if (n_samples > size_ - pos_) { + n_samples = size_ - pos_; } - if (ns > 0) { - memcpy(frame.raw_samples(), samples_ + pos_, ns * sizeof(audio::sample_t)); - pos_ += ns; + if (n_samples == 0) { + return status::StatusFinish; } - if (ns < frame.num_raw_samples()) { - memset(frame.raw_samples() + ns, 0, - (frame.num_raw_samples() - ns) * sizeof(audio::sample_t)); - } + memcpy(frame.raw_samples(), samples_ + pos_, n_samples * sizeof(audio::sample_t)); + pos_ += n_samples; - return status::StatusOK; + frame.set_num_raw_samples(n_samples); + frame.set_duration((packet::stream_timestamp_t)n_samples + / sample_spec_.num_channels()); + + return frame.duration() == duration ? status::StatusOK : status::StatusPart; } virtual status::StatusCode close() { diff --git a/src/tests/roc_sndio/test_helpers/utils.h b/src/tests/roc_sndio/test_helpers/utils.h index bd5f71a63..08617ca13 100644 --- a/src/tests/roc_sndio/test_helpers/utils.h +++ b/src/tests/roc_sndio/test_helpers/utils.h @@ -22,10 +22,10 @@ namespace test { namespace { bool backend_supports_format(IBackend& backend, core::IArena& arena, const char* format) { - core::Array<DriverInfo, MaxDrivers> driver_list(arena); - backend.discover_drivers(driver_list); - for (size_t n = 0; n < driver_list.size(); n++) { - if (strcmp(driver_list[n].name, format) == 0) { + core::Array<FormatInfo, MaxFormats> format_list(arena); + CHECK(backend.discover_formats(format_list)); + for (size_t n = 0; n < format_list.size(); n++) { + if (strcmp(format_list[n].format_name, format) == 0) { return true; } } @@ -36,15 +36,13 @@ void expect_open_sink(status::StatusCode expected_code, IBackend& backend, audio::FrameFactory& frame_factory, core::IArena& arena, - DriverType driver_type, const char* driver, const char* path, const IoConfig& config, core::ScopedPtr<ISink>& result) { IDevice* device = NULL; - const status::StatusCode code = - backend.open_device(DeviceType_Sink, driver_type, driver, path, config, - frame_factory, arena, &device); + const status::StatusCode code = backend.open_device( + DeviceType_Sink, driver, path, config, frame_factory, arena, &device); if (code != expected_code) { char buf[1024] = {}; @@ -70,15 +68,13 @@ void expect_open_source(status::StatusCode expected_code, IBackend& backend, audio::FrameFactory& frame_factory, core::IArena& arena, - DriverType driver_type, const char* driver, const char* path, const IoConfig& config, core::ScopedPtr<ISource>& result) { IDevice* device = NULL; - const status::StatusCode code = - backend.open_device(DeviceType_Source, driver_type, driver, path, config, - frame_factory, arena, &device); + const status::StatusCode code = backend.open_device( + DeviceType_Source, driver, path, config, frame_factory, arena, &device); if (code != expected_code) { char buf[1024] = {}; diff --git a/src/tests/roc_sndio/test_io_pump.cpp b/src/tests/roc_sndio/test_io_pump.cpp index c539a1427..cb04bb640 100644 --- a/src/tests/roc_sndio/test_io_pump.cpp +++ b/src/tests/roc_sndio/test_io_pump.cpp @@ -28,7 +28,7 @@ namespace { enum { FrameSize = 512, SampleRate = 48000 }; const audio::SampleSpec sample_spec(SampleRate, - audio::Sample_RawFormat, + audio::PcmSubformat_Raw, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, audio::ChanMask_Surround_Stereo); @@ -61,7 +61,7 @@ TEST_GROUP(io_pump) { } }; -TEST(io_pump, write_read) { +IGNORE_TEST(io_pump, write_read) { enum { NumSamples = FrameSize * 10 }; for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); @@ -77,14 +77,13 @@ TEST(io_pump, write_read) { mock_source.add(NumSamples); { - // open sink - core::ScopedPtr<ISink> backend_sink; + // open file sink + core::ScopedPtr<ISink> file_sink; test::expect_open_sink(status::StatusOK, backend, frame_factory, arena, - DriverType_File, "wav", file.path(), sink_config, - backend_sink); + "file", file.path(), sink_config, file_sink); - // copy from mock source to sink - IoPump pump(frame_pool, frame_buffer_pool, mock_source, NULL, *backend_sink, + // copy from mock source to file sink + IoPump pump(frame_pool, frame_buffer_pool, mock_source, NULL, *file_sink, sink_config, IoPump::ModeOneshot); LONGS_EQUAL(status::StatusOK, pump.init_status()); LONGS_EQUAL(status::StatusOK, pump.run()); @@ -92,15 +91,14 @@ TEST(io_pump, write_read) { CHECK(mock_source.num_returned() >= NumSamples - FrameSize); } - // open source - core::ScopedPtr<ISource> backend_source; - test::expect_open_source(status::StatusOK, backend, frame_factory, arena, - DriverType_File, "wav", file.path(), source_config, - backend_source); + // open file source + core::ScopedPtr<ISource> file_source; + test::expect_open_source(status::StatusOK, backend, frame_factory, arena, "file", + file.path(), source_config, file_source); - // copy from source to mock sink + // copy from file source to mock sink test::MockSink mock_sink(arena); - IoPump pump(frame_pool, frame_buffer_pool, *backend_source, NULL, mock_sink, + IoPump pump(frame_pool, frame_buffer_pool, *file_source, NULL, mock_sink, sink_config, IoPump::ModePermanent); LONGS_EQUAL(status::StatusOK, pump.init_status()); LONGS_EQUAL(status::StatusOK, pump.run()); @@ -110,7 +108,7 @@ TEST(io_pump, write_read) { } } -TEST(io_pump, write_overwrite_read) { +IGNORE_TEST(io_pump, write_overwrite_read) { enum { NumSamples = FrameSize * 10 }; for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); @@ -126,14 +124,13 @@ TEST(io_pump, write_overwrite_read) { mock_source.add(NumSamples); { - // open sink - core::ScopedPtr<ISink> backend_sink; - test::expect_open_sink(status::StatusOK, backend, frame_factory, arena, - DriverType_File, "wav", file.path(), sink_config, - backend_sink); + // open file sink + core::ScopedPtr<ISink> file_sink; + test::expect_open_sink(status::StatusOK, backend, frame_factory, arena, "wav", + file.path(), sink_config, file_sink); - // copy from mock source to sink - IoPump pump(frame_pool, frame_buffer_pool, mock_source, NULL, *backend_sink, + // copy from mock source to file sink + IoPump pump(frame_pool, frame_buffer_pool, mock_source, NULL, *file_sink, sink_config, IoPump::ModeOneshot); LONGS_EQUAL(status::StatusOK, pump.init_status()); LONGS_EQUAL(status::StatusOK, pump.run()); @@ -146,14 +143,13 @@ TEST(io_pump, write_overwrite_read) { CHECK(num_returned1 >= NumSamples - FrameSize); { - // open sink - core::ScopedPtr<ISink> backend_sink; - test::expect_open_sink(status::StatusOK, backend, frame_factory, arena, - DriverType_File, "wav", file.path(), sink_config, - backend_sink); + // open file sink + core::ScopedPtr<ISink> file_sink; + test::expect_open_sink(status::StatusOK, backend, frame_factory, arena, "wav", + file.path(), sink_config, file_sink); - // copy next samples from mock source to sink, overwriting file - IoPump pump(frame_pool, frame_buffer_pool, mock_source, NULL, *backend_sink, + // copy next samples from mock source to file sink, overwriting file + IoPump pump(frame_pool, frame_buffer_pool, mock_source, NULL, *file_sink, sink_config, IoPump::ModeOneshot); LONGS_EQUAL(status::StatusOK, pump.init_status()); LONGS_EQUAL(status::StatusOK, pump.run()); @@ -162,15 +158,14 @@ TEST(io_pump, write_overwrite_read) { size_t num_returned2 = mock_source.num_returned() - num_returned1; CHECK(num_returned1 >= NumSamples - FrameSize); - // open source - core::ScopedPtr<ISource> backend_source; - test::expect_open_source(status::StatusOK, backend, frame_factory, arena, - DriverType_File, "wav", file.path(), source_config, - backend_source); + // open file source + core::ScopedPtr<ISource> file_source; + test::expect_open_source(status::StatusOK, backend, frame_factory, arena, "wav", + file.path(), source_config, file_source); - // copy from source to mock sink + // copy from file source to mock sink test::MockSink mock_sink(arena); - IoPump pump(frame_pool, frame_buffer_pool, *backend_source, NULL, mock_sink, + IoPump pump(frame_pool, frame_buffer_pool, *file_source, NULL, mock_sink, sink_config, IoPump::ModePermanent); LONGS_EQUAL(status::StatusOK, pump.init_status()); LONGS_EQUAL(status::StatusOK, pump.run()); @@ -179,5 +174,6 @@ TEST(io_pump, write_overwrite_read) { mock_sink.check(num_returned1, num_returned2); } } + } // namespace sndio } // namespace roc diff --git a/src/tests/roc_sndio/test_sinks.cpp b/src/tests/roc_sndio/test_sinks.cpp new file mode 100644 index 000000000..c2034e972 --- /dev/null +++ b/src/tests/roc_sndio/test_sinks.cpp @@ -0,0 +1,429 @@ +/* + * Copyright (c) 2015 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 <CppUTest/TestHarness.h> + +#include "test_helpers/mock_sink.h" +#include "test_helpers/utils.h" + +#include "roc_core/heap_arena.h" +#include "roc_core/macro_helpers.h" +#include "roc_core/scoped_ptr.h" +#include "roc_dbgio/temp_file.h" +#include "roc_sndio/backend_map.h" +#include "roc_sndio/isource.h" + +namespace roc { +namespace sndio { + +namespace { + +enum { FrameSize = 500 }; + +core::HeapArena arena; +audio::FrameFactory frame_factory(arena, FrameSize * sizeof(audio::sample_t)); + +void read_wav(IBackend& backend, const audio::SampleSpec& frame_spec, const char* path) { + const core::nanoseconds_t frame_len = FrameSize * core::Second + / core::nanoseconds_t(frame_spec.sample_rate() * frame_spec.num_channels()); + + test::MockSink mock_sink(arena); + + IoConfig source_config; + source_config.sample_spec = audio::SampleSpec(); + source_config.frame_length = frame_len; + + IDevice* source_device = NULL; + LONGS_EQUAL(status::StatusOK, + backend.open_device(DeviceType_Source, "file", path, source_config, + frame_factory, arena, &source_device)); + CHECK(source_device != NULL); + + core::ScopedPtr<ISource> source(source_device->to_source()); + CHECK(source != NULL); + + for (;;) { + audio::FramePtr frame = + frame_factory.allocate_frame(frame_spec.ns_2_bytes(frame_len)); + + const status::StatusCode code = source->read( + *frame, frame_spec.ns_2_stream_timestamp(frame_len), audio::ModeHard); + + CHECK(code == status::StatusOK || code == status::StatusPart + || code == status::StatusFinish); + + if (code == status::StatusFinish) { + break; + } + + LONGS_EQUAL(status::StatusOK, mock_sink.write(*frame)); + } +} + +audio::ChannelSet make_channel_set(audio::ChannelMask chans) { + audio::ChannelSet ch_set; + ch_set.set_layout(audio::ChanLayout_Surround); + ch_set.set_order(audio::ChanOrder_Smpte); + ch_set.set_mask(chans); + + return ch_set; +} + +IoConfig make_config(const audio::SampleSpec& file_spec, + const audio::SampleSpec& frame_spec) { + IoConfig config; + config.sample_spec = file_spec; + config.frame_length = FrameSize * core::Second + / core::nanoseconds_t(frame_spec.sample_rate() * frame_spec.num_channels()); + + return config; +} + +} // namespace + +TEST_GROUP(sinks) {}; + +// Don't specify output spec. +TEST(sinks, empty_spec) { + audio::SampleSpec file_spec; + file_spec.clear(); + + audio::SampleSpec frame_spec; + frame_spec.set_format(audio::Format_Pcm); + frame_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + frame_spec.set_sample_rate(44100); + frame_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); + n_backend++) { + IBackend& backend = BackendMap::instance().nth_backend(n_backend); + if (!test::backend_supports_format(backend, arena, "wav")) { + continue; + } + + dbgio::TempFile file("test.wav"); + + core::ScopedPtr<ISink> sink; + test::expect_open_sink(status::StatusOK, backend, frame_factory, arena, "file", + file.path(), make_config(file_spec, frame_spec), sink); + + audio::SampleSpec actual_spec = sink->sample_spec(); + CHECK(actual_spec.pcm_subformat() != audio::PcmSubformat_Invalid); + actual_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + + test::expect_specs_equal(backend.name(), frame_spec, actual_spec); + + CHECK(!sink->has_state()); + CHECK(!sink->has_latency()); + CHECK(!sink->has_clock()); + LONGS_EQUAL(status::StatusOK, sink->close()); + } +} + +// Specify complete spec. +TEST(sinks, complete_spec) { + audio::SampleSpec file_spec; + file_spec.set_format(audio::Format_Wav); + file_spec.set_pcm_subformat(audio::PcmSubformat_SInt16); + file_spec.set_sample_rate(48000); + file_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Mono)); + + audio::SampleSpec frame_spec; + frame_spec.set_format(audio::Format_Pcm); + frame_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + frame_spec.set_sample_rate(48000); + frame_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Mono)); + + for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); + n_backend++) { + IBackend& backend = BackendMap::instance().nth_backend(n_backend); + if (!test::backend_supports_format(backend, arena, "wav")) { + continue; + } + + dbgio::TempFile file("test.wav"); + + core::ScopedPtr<ISink> sink; + test::expect_open_sink(status::StatusOK, backend, frame_factory, arena, "file", + file.path(), make_config(file_spec, frame_spec), sink); + + audio::SampleSpec actual_spec = sink->sample_spec(); + CHECK(actual_spec.pcm_subformat() != audio::PcmSubformat_Invalid); + if (actual_spec.pcm_subformat() == audio::PcmSubformat_SInt16_Le) { + actual_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + } + + test::expect_specs_equal(backend.name(), frame_spec, actual_spec); + + CHECK(!sink->has_state()); + CHECK(!sink->has_latency()); + CHECK(!sink->has_clock()); + LONGS_EQUAL(status::StatusOK, sink->close()); + } +} + +// Specify only format. +TEST(sinks, explicit_format) { + audio::SampleSpec file_spec; + file_spec.set_format(audio::Format_Wav); + + audio::SampleSpec frame_spec; + frame_spec.set_format(audio::Format_Pcm); + frame_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + frame_spec.set_sample_rate(44100); + frame_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); + n_backend++) { + IBackend& backend = BackendMap::instance().nth_backend(n_backend); + if (!test::backend_supports_format(backend, arena, "wav")) { + continue; + } + + dbgio::TempFile file("test.wav"); + + core::ScopedPtr<ISink> sink; + test::expect_open_sink(status::StatusOK, backend, frame_factory, arena, "file", + file.path(), make_config(file_spec, frame_spec), sink); + + audio::SampleSpec actual_spec = sink->sample_spec(); + CHECK(actual_spec.pcm_subformat() != audio::PcmSubformat_Invalid); + actual_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + + test::expect_specs_equal(backend.name(), frame_spec, actual_spec); + + CHECK(!sink->has_state()); + CHECK(!sink->has_latency()); + CHECK(!sink->has_clock()); + LONGS_EQUAL(status::StatusOK, sink->close()); + } +} + +// Specify only format and sub-format. +TEST(sinks, explicit_format_and_subformat) { + audio::SampleSpec file_spec; + file_spec.set_format(audio::Format_Wav); + file_spec.set_pcm_subformat(audio::PcmSubformat_SInt16); + + audio::SampleSpec frame_spec; + frame_spec.set_format(audio::Format_Pcm); + frame_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + frame_spec.set_sample_rate(44100); + frame_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); + n_backend++) { + IBackend& backend = BackendMap::instance().nth_backend(n_backend); + if (!test::backend_supports_format(backend, arena, "wav")) { + continue; + } + + dbgio::TempFile file("test.wav"); + + core::ScopedPtr<ISink> sink; + test::expect_open_sink(status::StatusOK, backend, frame_factory, arena, "file", + file.path(), make_config(file_spec, frame_spec), sink); + + audio::SampleSpec actual_spec = sink->sample_spec(); + CHECK(actual_spec.pcm_subformat() != audio::PcmSubformat_Invalid); + if (actual_spec.pcm_subformat() == audio::PcmSubformat_SInt16_Le) { + actual_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + } + + test::expect_specs_equal(backend.name(), frame_spec, actual_spec); + + CHECK(!sink->has_state()); + CHECK(!sink->has_latency()); + CHECK(!sink->has_clock()); + LONGS_EQUAL(status::StatusOK, sink->close()); + } +} + +// Specify only sample rate. +TEST(sinks, explicit_rate) { + audio::SampleSpec file_spec; + file_spec.set_sample_rate(48000); + + audio::SampleSpec frame_spec; + frame_spec.set_format(audio::Format_Pcm); + frame_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + frame_spec.set_sample_rate(48000); + frame_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); + n_backend++) { + IBackend& backend = BackendMap::instance().nth_backend(n_backend); + if (!test::backend_supports_format(backend, arena, "wav")) { + continue; + } + + dbgio::TempFile file("test.wav"); + + core::ScopedPtr<ISink> sink; + test::expect_open_sink(status::StatusOK, backend, frame_factory, arena, "file", + file.path(), make_config(file_spec, frame_spec), sink); + + audio::SampleSpec actual_spec = sink->sample_spec(); + CHECK(actual_spec.pcm_subformat() != audio::PcmSubformat_Invalid); + actual_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + + test::expect_specs_equal(backend.name(), frame_spec, actual_spec); + + CHECK(!sink->has_state()); + CHECK(!sink->has_latency()); + CHECK(!sink->has_clock()); + LONGS_EQUAL(status::StatusOK, sink->close()); + } +} + +// Specify only channel set. +TEST(sinks, explicit_channels) { + audio::SampleSpec file_spec; + file_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Mono)); + + audio::SampleSpec frame_spec; + frame_spec.set_format(audio::Format_Pcm); + frame_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + frame_spec.set_sample_rate(44100); + frame_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Mono)); + + for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); + n_backend++) { + IBackend& backend = BackendMap::instance().nth_backend(n_backend); + if (!test::backend_supports_format(backend, arena, "wav")) { + continue; + } + + dbgio::TempFile file("test.wav"); + + core::ScopedPtr<ISink> sink; + test::expect_open_sink(status::StatusOK, backend, frame_factory, arena, "file", + file.path(), make_config(file_spec, frame_spec), sink); + + audio::SampleSpec actual_spec = sink->sample_spec(); + CHECK(actual_spec.pcm_subformat() != audio::PcmSubformat_Invalid); + actual_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + + test::expect_specs_equal(backend.name(), frame_spec, actual_spec); + + CHECK(!sink->has_state()); + CHECK(!sink->has_latency()); + CHECK(!sink->has_clock()); + LONGS_EQUAL(status::StatusOK, sink->close()); + } +} + +// Directory doesn't exist. +TEST(sinks, bad_file_path) { + audio::SampleSpec file_spec; + file_spec.clear(); + + audio::SampleSpec frame_spec; + frame_spec.set_format(audio::Format_Pcm); + frame_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + frame_spec.set_sample_rate(44100); + frame_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); + n_backend++) { + IBackend& backend = BackendMap::instance().nth_backend(n_backend); + if (!test::backend_supports_format(backend, arena, "wav")) { + continue; + } + + core::ScopedPtr<ISink> sink; + test::expect_open_sink(status::StatusErrFile, backend, frame_factory, arena, + "file", "/bad/file.wav", + make_config(file_spec, frame_spec), sink); + } +} + +// Unknown file extension. +TEST(sinks, bad_file_extension) { + audio::SampleSpec file_spec; + file_spec.clear(); + + audio::SampleSpec frame_spec; + frame_spec.set_format(audio::Format_Pcm); + frame_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + frame_spec.set_sample_rate(44100); + frame_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); + n_backend++) { + IBackend& backend = BackendMap::instance().nth_backend(n_backend); + if (!test::backend_supports_format(backend, arena, "wav")) { + continue; + } + + dbgio::TempFile file("test.bad_ext"); + + core::ScopedPtr<ISink> sink; + test::expect_open_sink(status::StatusNoFormat, backend, frame_factory, arena, + "file", file.path(), make_config(file_spec, frame_spec), + sink); + } +} + +// Format not supported by backend. +TEST(sinks, bad_format) { + audio::SampleSpec file_spec; + CHECK(file_spec.set_custom_format("bad_fmt")); + + audio::SampleSpec frame_spec; + frame_spec.set_format(audio::Format_Pcm); + frame_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + frame_spec.set_sample_rate(44100); + frame_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); + n_backend++) { + IBackend& backend = BackendMap::instance().nth_backend(n_backend); + if (!test::backend_supports_format(backend, arena, "wav")) { + continue; + } + + dbgio::TempFile file("test.wav"); + + core::ScopedPtr<ISink> sink; + test::expect_open_sink(status::StatusNoFormat, backend, frame_factory, arena, + "file", file.path(), make_config(file_spec, frame_spec), + sink); + } +} + +// Sub-format not allowed by format. +TEST(sinks, bad_subformat) { + audio::SampleSpec file_spec; + file_spec.set_format(audio::Format_Wav); + file_spec.set_pcm_subformat(audio::PcmSubformat_SInt18_3_Be); + + audio::SampleSpec frame_spec; + frame_spec.set_format(audio::Format_Pcm); + frame_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + frame_spec.set_sample_rate(44100); + frame_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); + n_backend++) { + IBackend& backend = BackendMap::instance().nth_backend(n_backend); + if (!test::backend_supports_format(backend, arena, "wav")) { + continue; + } + + dbgio::TempFile file("test.wav"); + + core::ScopedPtr<ISink> sink; + test::expect_open_sink(status::StatusBadConfig, backend, frame_factory, arena, + "file", file.path(), make_config(file_spec, frame_spec), + sink); + } +} + +} // namespace sndio +} // namespace roc diff --git a/src/tests/roc_sndio/test_sources.cpp b/src/tests/roc_sndio/test_sources.cpp new file mode 100644 index 000000000..20fadd538 --- /dev/null +++ b/src/tests/roc_sndio/test_sources.cpp @@ -0,0 +1,499 @@ +/* + * 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 <CppUTest/TestHarness.h> + +#include "test_helpers/mock_source.h" +#include "test_helpers/utils.h" + +#include "roc_core/heap_arena.h" +#include "roc_core/macro_helpers.h" +#include "roc_core/scoped_ptr.h" +#include "roc_core/slab_pool.h" +#include "roc_dbgio/temp_file.h" +#include "roc_sndio/backend_map.h" +#include "roc_sndio/isink.h" + +namespace roc { +namespace sndio { + +namespace { + +enum { MaxBufSize = 8192, FrameSize = 500 }; + +core::HeapArena arena; + +core::SlabPool<audio::Frame> frame_pool("frame_pool", arena); +core::SlabPool<core::Buffer> + frame_buffer_pool("frame_buffer_pool", + arena, + sizeof(core::Buffer) + MaxBufSize * sizeof(audio::sample_t)); + +audio::FrameFactory frame_factory(frame_pool, frame_buffer_pool); + +void write_wav(IBackend& backend, + const audio::SampleSpec& file_write_spec, + const audio::SampleSpec& frame_spec, + const char* path, + size_t num_samples) { + const core::nanoseconds_t frame_len = FrameSize * core::Second + / core::nanoseconds_t(frame_spec.sample_rate() * frame_spec.num_channels()); + + test::MockSource mock_source(frame_factory, frame_spec, arena); + mock_source.add(num_samples * file_write_spec.num_channels()); + + IoConfig sink_config; + sink_config.sample_spec = file_write_spec; + sink_config.frame_length = frame_len; + + IDevice* sink_device = NULL; + LONGS_EQUAL(status::StatusOK, + backend.open_device(DeviceType_Sink, "file", path, sink_config, + frame_factory, arena, &sink_device)); + CHECK(sink_device != NULL); + + core::ScopedPtr<ISink> sink(sink_device->to_sink()); + CHECK(sink != NULL); + + for (;;) { + audio::FramePtr frame = + frame_factory.allocate_frame(frame_spec.ns_2_bytes(frame_len)); + + const status::StatusCode code = mock_source.read( + *frame, frame_spec.ns_2_stream_timestamp(frame_len), audio::ModeHard); + + CHECK(code == status::StatusOK || code == status::StatusPart + || code == status::StatusFinish); + + if (code == status::StatusFinish) { + break; + } + + LONGS_EQUAL(status::StatusOK, sink->write(*frame)); + } +} + +void expect_read(status::StatusCode expected_code, + ISource& source, + audio::Frame& frame, + packet::stream_timestamp_t requested_samples) { + const status::StatusCode code = + source.read(frame, requested_samples, audio::ModeHard); + + LONGS_EQUAL(expected_code, code); +} + +audio::ChannelSet make_channel_set(audio::ChannelMask chans) { + audio::ChannelSet ch_set; + ch_set.set_layout(audio::ChanLayout_Surround); + ch_set.set_order(audio::ChanOrder_Smpte); + ch_set.set_mask(chans); + + return ch_set; +} + +IoConfig make_config(const audio::SampleSpec& file_read_spec, + const audio::SampleSpec& frame_spec) { + IoConfig config; + config.sample_spec = file_read_spec; + config.frame_length = FrameSize * core::Second + / core::nanoseconds_t(frame_spec.sample_rate() * frame_spec.num_channels()); + + return config; +} + +} // namespace + +TEST_GROUP(sources) {}; + +// Don't specify input spec. +TEST(sources, empty_spec) { + audio::SampleSpec file_read_spec; + file_read_spec.clear(); + + audio::SampleSpec file_write_spec; + file_write_spec.set_format(audio::Format_Wav); + file_write_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + file_write_spec.set_sample_rate(44100); + file_write_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + audio::SampleSpec frame_spec; + frame_spec.set_format(audio::Format_Pcm); + frame_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + frame_spec.set_sample_rate(44100); + frame_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); + n_backend++) { + IBackend& backend = BackendMap::instance().nth_backend(n_backend); + if (!test::backend_supports_format(backend, arena, "wav")) { + continue; + } + + dbgio::TempFile file("test.wav"); + write_wav(backend, file_write_spec, frame_spec, file.path(), MaxBufSize * 10); + + core::ScopedPtr<ISource> source; + test::expect_open_source(status::StatusOK, backend, frame_factory, arena, "file", + file.path(), make_config(file_read_spec, frame_spec), + source); + + audio::SampleSpec actual_spec = source->sample_spec(); + test::expect_specs_equal(backend.name(), frame_spec, actual_spec); + + CHECK(!source->has_state()); + CHECK(!source->has_latency()); + CHECK(!source->has_clock()); + LONGS_EQUAL(status::StatusOK, source->close()); + } +} + +// Specify only format. +TEST(sources, explicit_format) { + audio::SampleSpec file_read_spec; + file_read_spec.set_format(audio::Format_Wav); + + audio::SampleSpec file_write_spec; + file_write_spec.set_format(audio::Format_Wav); + file_write_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + file_write_spec.set_sample_rate(44100); + file_write_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + audio::SampleSpec frame_spec; + frame_spec.set_format(audio::Format_Pcm); + frame_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + frame_spec.set_sample_rate(44100); + frame_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); + n_backend++) { + IBackend& backend = BackendMap::instance().nth_backend(n_backend); + if (!test::backend_supports_format(backend, arena, "wav")) { + continue; + } + + dbgio::TempFile file("test.wav"); + write_wav(backend, file_write_spec, frame_spec, file.path(), MaxBufSize * 10); + + core::ScopedPtr<ISource> source; + test::expect_open_source(status::StatusOK, backend, frame_factory, arena, "file", + file.path(), make_config(file_read_spec, frame_spec), + source); + + audio::SampleSpec actual_spec = source->sample_spec(); + test::expect_specs_equal(backend.name(), frame_spec, actual_spec); + + CHECK(!source->has_state()); + CHECK(!source->has_latency()); + CHECK(!source->has_clock()); + LONGS_EQUAL(status::StatusOK, source->close()); + } +} + +// File with non-default rate and channels. +TEST(sources, non_default_file) { + audio::SampleSpec file_read_spec; + file_read_spec.clear(); + + audio::SampleSpec file_write_spec; + file_write_spec.set_format(audio::Format_Wav); + file_write_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + file_write_spec.set_sample_rate(48000); + file_write_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Mono)); + + audio::SampleSpec frame_spec; + frame_spec.set_format(audio::Format_Pcm); + frame_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + frame_spec.set_sample_rate(48000); + frame_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Mono)); + + for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); + n_backend++) { + IBackend& backend = BackendMap::instance().nth_backend(n_backend); + if (!test::backend_supports_format(backend, arena, "wav")) { + continue; + } + + dbgio::TempFile file("test.wav"); + write_wav(backend, file_write_spec, frame_spec, file.path(), MaxBufSize * 10); + + core::ScopedPtr<ISource> source; + test::expect_open_source(status::StatusOK, backend, frame_factory, arena, "file", + file.path(), make_config(file_read_spec, frame_spec), + source); + + audio::SampleSpec actual_spec = source->sample_spec(); + test::expect_specs_equal(backend.name(), frame_spec, actual_spec); + + CHECK(!source->has_state()); + CHECK(!source->has_latency()); + CHECK(!source->has_clock()); + LONGS_EQUAL(status::StatusOK, source->close()); + } +} + +// File doesn't exist. +TEST(sources, bad_file_path) { + audio::SampleSpec file_read_spec; + file_read_spec.clear(); + + audio::SampleSpec frame_spec; + frame_spec.set_format(audio::Format_Pcm); + frame_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + frame_spec.set_sample_rate(44100); + frame_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); + n_backend++) { + IBackend& backend = BackendMap::instance().nth_backend(n_backend); + if (!test::backend_supports_format(backend, arena, "wav")) { + continue; + } + + core::ScopedPtr<ISource> source; + test::expect_open_source(status::StatusErrFile, backend, frame_factory, arena, + "file", "/bad/file.wav", + make_config(file_read_spec, frame_spec), source); + } +} + +// Unknown file extension. +TEST(sources, bad_file_extension) { + audio::SampleSpec file_read_spec; + file_read_spec.clear(); + + audio::SampleSpec frame_spec; + frame_spec.set_format(audio::Format_Pcm); + frame_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + frame_spec.set_sample_rate(44100); + frame_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); + n_backend++) { + IBackend& backend = BackendMap::instance().nth_backend(n_backend); + if (!test::backend_supports_format(backend, arena, "wav")) { + continue; + } + + dbgio::TempFile file("test.bad_ext"); + + core::ScopedPtr<ISource> source; + test::expect_open_source(status::StatusNoFormat, backend, frame_factory, arena, + "file", file.path(), + make_config(file_read_spec, frame_spec), source); + } +} + +// File is not a valid WAV file. +TEST(sources, bad_file_contents) { + audio::SampleSpec file_read_spec; + file_read_spec.clear(); + + audio::SampleSpec frame_spec; + frame_spec.set_format(audio::Format_Pcm); + frame_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + frame_spec.set_sample_rate(44100); + frame_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); + n_backend++) { + IBackend& backend = BackendMap::instance().nth_backend(n_backend); + if (!test::backend_supports_format(backend, arena, "wav")) { + continue; + } + + dbgio::TempFile file("test.wav"); + + core::ScopedPtr<ISource> source; + test::expect_open_source(status::StatusNoFormat, backend, frame_factory, arena, + "file", file.path(), + make_config(file_read_spec, frame_spec), source); + } +} + +// Unsupported format. +TEST(sources, bad_format) { + audio::SampleSpec file_read_spec; + CHECK(file_read_spec.set_custom_format("bad_fmt")); + + audio::SampleSpec file_write_spec; + file_write_spec.set_format(audio::Format_Wav); + file_write_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + file_write_spec.set_sample_rate(44100); + file_write_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + audio::SampleSpec frame_spec; + frame_spec.set_format(audio::Format_Pcm); + frame_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + frame_spec.set_sample_rate(44100); + frame_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); + n_backend++) { + IBackend& backend = BackendMap::instance().nth_backend(n_backend); + if (!test::backend_supports_format(backend, arena, "wav")) { + continue; + } + + dbgio::TempFile file("test.wav"); + write_wav(backend, file_write_spec, frame_spec, file.path(), MaxBufSize * 10); + + core::ScopedPtr<ISource> source; + test::expect_open_source(status::StatusNoFormat, backend, frame_factory, arena, + "file", file.path(), + make_config(file_read_spec, frame_spec), source); + } +} + +// Invalid config. +TEST(sources, bad_config) { + audio::SampleSpec file_read_specs[3]; + // explicit sub-format not allowed + file_read_specs[0].set_format(audio::Format_Wav); + file_read_specs[0].set_pcm_subformat(audio::PcmSubformat_Raw); + // explicit rate not allowed + file_read_specs[1].set_sample_rate(44100); + // explicit channels not allowed + file_read_specs[2].set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + audio::SampleSpec file_write_spec; + file_write_spec.set_format(audio::Format_Wav); + file_write_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + file_write_spec.set_sample_rate(44100); + file_write_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + audio::SampleSpec frame_spec; + frame_spec.set_format(audio::Format_Pcm); + frame_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + frame_spec.set_sample_rate(44100); + frame_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + for (size_t n_spec = 0; n_spec < ROC_ARRAY_SIZE(file_read_specs); n_spec++) { + for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); + n_backend++) { + IBackend& backend = BackendMap::instance().nth_backend(n_backend); + if (!test::backend_supports_format(backend, arena, "wav")) { + continue; + } + + dbgio::TempFile file("test.wav"); + write_wav(backend, file_write_spec, frame_spec, file.path(), MaxBufSize * 10); + + core::ScopedPtr<ISource> source; + test::expect_open_source( + status::StatusBadConfig, backend, frame_factory, arena, "file", + file.path(), make_config(file_read_specs[n_spec], frame_spec), source); + } + } +} + +// Rewind and read same frame again. +TEST(sources, rewind) { + audio::SampleSpec file_read_spec; + file_read_spec.clear(); + + audio::SampleSpec file_write_spec; + file_write_spec.set_format(audio::Format_Wav); + file_write_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + file_write_spec.set_sample_rate(44100); + file_write_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + audio::SampleSpec frame_spec; + frame_spec.set_format(audio::Format_Pcm); + frame_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + frame_spec.set_sample_rate(44100); + frame_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); + n_backend++) { + IBackend& backend = BackendMap::instance().nth_backend(n_backend); + if (!test::backend_supports_format(backend, arena, "wav")) { + continue; + } + + dbgio::TempFile file("test.wav"); + write_wav(backend, file_write_spec, frame_spec, file.path(), MaxBufSize * 10); + + core::ScopedPtr<ISource> source; + test::expect_open_source(status::StatusOK, backend, frame_factory, arena, "file", + file.path(), make_config(file_read_spec, frame_spec), + source); + + audio::FramePtr frame1 = frame_factory.allocate_frame_no_buffer(); + CHECK(frame1); + expect_read(status::StatusOK, *source, *frame1, FrameSize); + + // rewind + LONGS_EQUAL(status::StatusOK, source->rewind()); + + audio::FramePtr frame2 = frame_factory.allocate_frame_no_buffer(); + CHECK(frame2); + expect_read(status::StatusOK, *source, *frame2, FrameSize); + + LONGS_EQUAL(FrameSize * frame_spec.num_channels(), frame1->num_raw_samples()); + LONGS_EQUAL(FrameSize * frame_spec.num_channels(), frame2->num_raw_samples()); + + if (memcmp(frame1->raw_samples(), frame2->raw_samples(), + frame1->num_raw_samples() * sizeof(audio::sample_t)) + != 0) { + FAIL("frames should be equal"); + } + LONGS_EQUAL(status::StatusOK, source->close()); + } +} + +// Read until EOF, rewind, repeat. +TEST(sources, rewind_after_eof) { + audio::SampleSpec file_read_spec; + file_read_spec.clear(); + + audio::SampleSpec file_write_spec; + file_write_spec.set_format(audio::Format_Wav); + file_write_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + file_write_spec.set_sample_rate(44100); + file_write_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + audio::SampleSpec frame_spec; + frame_spec.set_format(audio::Format_Pcm); + frame_spec.set_pcm_subformat(audio::PcmSubformat_Raw); + frame_spec.set_sample_rate(44100); + frame_spec.set_channel_set(make_channel_set(audio::ChanMask_Surround_Stereo)); + + for (size_t n_backend = 0; n_backend < BackendMap::instance().num_backends(); + n_backend++) { + IBackend& backend = BackendMap::instance().nth_backend(n_backend); + if (!test::backend_supports_format(backend, arena, "wav")) { + continue; + } + + dbgio::TempFile file("test.wav"); + write_wav(backend, file_write_spec, frame_spec, file.path(), FrameSize * 2); + + core::ScopedPtr<ISource> source; + test::expect_open_source(status::StatusOK, backend, frame_factory, arena, "file", + file.path(), make_config(file_read_spec, frame_spec), + source); + + audio::FramePtr frame = frame_factory.allocate_frame_no_buffer(); + CHECK(frame); + + for (int i = 0; i < 10; i++) { + expect_read(status::StatusOK, *source, *frame, FrameSize); + expect_read(status::StatusOK, *source, *frame, FrameSize); + expect_read(status::StatusFinish, *source, *frame, FrameSize); + + // rewind + LONGS_EQUAL(status::StatusOK, source->rewind()); + } + LONGS_EQUAL(status::StatusOK, source->close()); + } +} + +} // namespace sndio + +} // namespace roc diff --git a/src/tools/roc_copy/cmdline.ggo b/src/tools/roc_copy/cmdline.ggo index f1c094228..d087ff7e4 100644 --- a/src/tools/roc_copy/cmdline.ggo +++ b/src/tools/roc_copy/cmdline.ggo @@ -11,10 +11,9 @@ option "list-supported" L "List supported protocols, formats, etc." optional section "I/O options" option "input" i "Input file URI" typestr="FILE_URI" string optional - option "input-format" - "Force input file format" typestr="FILE_FORMAT" string optional + option "input-encoding" - "Input file encoding" typestr="IO_ENCODING" string optional option "output" o "Output file URI" typestr="FILE_URI" string optional - option "output-format" - "Force output file format" typestr="FILE_FORMAT" string optional option "output-encoding" - "Output file encoding" typestr="IO_ENCODING" string optional option "io-frame-len" - "I/O frame length, TIME units" typestr="TIME" string optional diff --git a/src/tools/roc_copy/main.cpp b/src/tools/roc_copy/main.cpp index 5067681fb..f1d1555c6 100644 --- a/src/tools/roc_copy/main.cpp +++ b/src/tools/roc_copy/main.cpp @@ -120,8 +120,9 @@ bool build_transcoder_config(const gengetopt_args_info& args, size_t compute_max_frame_size(const sndio::IoConfig& io_config) { audio::SampleSpec spec = io_config.sample_spec; - spec.use_defaults(audio::Sample_RawFormat, audio::ChanLayout_Surround, - audio::ChanOrder_Smpte, audio::ChanMask_Surround_7_1_4, 48000); + spec.use_defaults(audio::Format_Pcm, audio::PcmSubformat_Raw, + audio::ChanLayout_Surround, audio::ChanOrder_Smpte, + audio::ChanMask_Surround_7_1_4, 48000); return spec.ns_2_samples_overall(io_config.frame_length) * sizeof(audio::sample_t); } @@ -142,11 +143,6 @@ bool parse_input_uri(const gengetopt_args_info& args, address::IoUri& input_uri) return false; } - if (!args.input_format_given && input_uri.is_special_file()) { - roc_log(LogError, "--input-format should be specified if --input is \"-\""); - return false; - } - return true; } @@ -161,9 +157,11 @@ bool parse_output_uri(const gengetopt_args_info& args, address::IoUri& output_ur return false; } - if (!args.output_format_given && output_uri.is_special_file()) { - roc_log(LogError, "--output-format should be specified if --output is \"-\""); - return false; + if (output_uri.is_special_file()) { + if (!args.output_encoding_given) { + roc_log(LogError, "--output-encoding is required when --output is \"-\""); + return false; + } } return true; @@ -172,10 +170,9 @@ bool parse_output_uri(const gengetopt_args_info& args, address::IoUri& output_ur bool open_input_source(sndio::BackendDispatcher& backend_dispatcher, const sndio::IoConfig& io_config, const address::IoUri& input_uri, - const char* input_format, core::ScopedPtr<sndio::ISource>& input_source) { const status::StatusCode code = - backend_dispatcher.open_source(input_uri, input_format, io_config, input_source); + backend_dispatcher.open_source(input_uri, io_config, input_source); if (code != status::StatusOK) { roc_log(LogError, "can't open --input file or device: status=%s", @@ -194,10 +191,9 @@ bool open_input_source(sndio::BackendDispatcher& backend_dispatcher, bool open_output_sink(sndio::BackendDispatcher& backend_dispatcher, const sndio::IoConfig& io_config, const address::IoUri& output_uri, - const char* output_format, core::ScopedPtr<sndio::ISink>& output_sink) { const status::StatusCode code = - backend_dispatcher.open_sink(output_uri, output_format, io_config, output_sink); + backend_dispatcher.open_sink(output_uri, io_config, output_sink); if (code != status::StatusOK) { roc_log(LogError, "can't open --output file or device: status=%s", @@ -258,12 +254,12 @@ int main(int argc, char** argv) { } core::ScopedPtr<sndio::ISource> input_source; - if (!open_input_source(backend_dispatcher, input_config, input_uri, - args.input_format_arg, input_source)) { + if (!open_input_source(backend_dispatcher, input_config, input_uri, input_source)) { return 1; } input_config.sample_spec = input_source->sample_spec(); + input_config.frame_length = input_source->frame_length(); sndio::IoConfig output_config; if (!build_output_config(args, input_config, output_config)) { @@ -280,7 +276,7 @@ int main(int argc, char** argv) { core::ScopedPtr<sndio::ISink> output_sink; if (args.output_given) { if (!open_output_sink(backend_dispatcher, output_config, output_uri, - args.output_format_arg, output_sink)) { + output_sink)) { return 1; } output_config.sample_spec = output_sink->sample_spec(); diff --git a/src/tools/roc_recv/cmdline.ggo b/src/tools/roc_recv/cmdline.ggo index 8a12ab6f8..2224e44ed 100644 --- a/src/tools/roc_recv/cmdline.ggo +++ b/src/tools/roc_recv/cmdline.ggo @@ -15,7 +15,6 @@ section "Operation options" section "Output options" option "output" o "Output file or device URI" typestr="IO_URI" string optional - option "output-format" - "Force output file format" typestr="FILE_FORMAT" string optional option "io-encoding" - "Output device encoding" typestr="IO_ENCODING" string optional option "io-latency" - "Output device latency, TIME units" typestr="TIME"string optional @@ -24,10 +23,8 @@ section "Output options" section "Backup input options" option "backup" - - "Backup file or device URI (used as input when there are no connections)" + "Backup file URI (used as input when there are no connections)" typestr="IO_URI" string optional - option "backup-format" - "Force backup file format" - typestr="FILE_FORMAT" string optional section "Network options" diff --git a/src/tools/roc_recv/main.cpp b/src/tools/roc_recv/main.cpp index a39f9394c..70da9886c 100644 --- a/src/tools/roc_recv/main.cpp +++ b/src/tools/roc_recv/main.cpp @@ -105,10 +105,15 @@ bool build_context_config(const gengetopt_args_info& args, } } else { audio::SampleSpec spec = io_config.sample_spec; - spec.use_defaults(audio::Sample_RawFormat, audio::ChanLayout_Surround, - audio::ChanOrder_Smpte, audio::ChanMask_Surround_7_1_4, 48000); + spec.use_defaults(audio::Format_Pcm, audio::PcmSubformat_Raw, + audio::ChanLayout_Surround, audio::ChanOrder_Smpte, + audio::ChanMask_Surround_7_1_4, 48000); + core::nanoseconds_t len = io_config.frame_length; + if (len == 0) { + len = 10 * core::Millisecond; + } context_config.max_frame_size = - spec.ns_2_samples_overall(io_config.frame_length) * sizeof(audio::sample_t); + spec.ns_2_samples_overall(len) * sizeof(audio::sample_t); } return true; @@ -293,13 +298,6 @@ bool build_receiver_config(const gengetopt_args_info& args, roc_log(LogError, "invalid --max-latency: should be > 0"); return false; } - if (receiver_config.session_defaults.latency.min_target_latency - > receiver_config.session_defaults.latency.max_target_latency) { - roc_log( - LogError, - "incorrect --max-latency: should be greater or equal to --min-latency"); - return false; - } } if (args.no_play_timeout_given) { @@ -337,7 +335,7 @@ bool build_receiver_config(const gengetopt_args_info& args, receiver_config.common.enable_cpu_clock = !output_sink.has_clock(); receiver_config.common.output_sample_spec = output_sink.sample_spec(); - if (!receiver_config.common.output_sample_spec.is_valid()) { + if (!receiver_config.common.output_sample_spec.is_complete()) { roc_log(LogError, "can't detect output encoding, try to set it" " explicitly with --io-encoding option"); @@ -353,18 +351,11 @@ bool parse_output_uri(const gengetopt_args_info& args, address::IoUri& output_ur roc_log(LogError, "invalid --output file or device URI"); return false; } - } - - if (args.output_format_given) { - if (output_uri.is_valid() && !output_uri.is_file()) { - roc_log(LogError, - "--output-format can't be used if --output is not a file URI"); - return false; - } - } else { if (output_uri.is_special_file()) { - roc_log(LogError, "--output-format should be specified if --output is \"-\""); - return false; + if (!args.io_encoding_given) { + roc_log(LogError, "--io-encoding is required when --output is \"-\""); + return false; + } } } @@ -374,11 +365,10 @@ bool parse_output_uri(const gengetopt_args_info& args, address::IoUri& output_ur bool open_output_sink(sndio::BackendDispatcher& backend_dispatcher, const sndio::IoConfig& io_config, const address::IoUri& output_uri, - const char* output_format, core::ScopedPtr<sndio::ISink>& output_sink) { if (output_uri.is_valid()) { - const status::StatusCode code = backend_dispatcher.open_sink( - output_uri, output_format, io_config, output_sink); + const status::StatusCode code = + backend_dispatcher.open_sink(output_uri, io_config, output_sink); if (code != status::StatusOK) { roc_log(LogError, "can't open --output file or device: status=%s", @@ -401,21 +391,18 @@ bool open_output_sink(sndio::BackendDispatcher& backend_dispatcher, bool parse_backup_uri(const gengetopt_args_info& args, address::IoUri& backup_uri) { if (!address::parse_io_uri(args.backup_arg, backup_uri)) { - roc_log(LogError, "invalid --backup file or device URI"); + roc_log(LogError, "invalid --backup URI: bad format"); return false; } - if (args.backup_format_given) { - if (backup_uri.is_valid() && !backup_uri.is_file()) { - roc_log(LogError, - "--backup-format can't be used if --backup is not a file URI"); - return false; - } - } else { - if (backup_uri.is_special_file()) { - roc_log(LogError, "--backup-format should be specified if --backup is \"-\""); - return false; - } + if (!backup_uri.is_file()) { + roc_log(LogError, "invalid --backup URI: should be file"); + return false; + } + + if (backup_uri.is_special_file()) { + roc_log(LogError, "invalid --backup URI: can't be \"-\""); + return false; } return true; @@ -424,10 +411,9 @@ bool parse_backup_uri(const gengetopt_args_info& args, address::IoUri& backup_ur bool open_backup_source(sndio::BackendDispatcher& backend_dispatcher, const sndio::IoConfig& io_config, const address::IoUri& backup_uri, - const char* backup_format, core::ScopedPtr<sndio::ISource>& backup_source) { - const status::StatusCode code = backend_dispatcher.open_source( - backup_uri, backup_format, io_config, backup_source); + const status::StatusCode code = + backend_dispatcher.open_source(backup_uri, io_config, backup_source); if (code != status::StatusOK) { roc_log(LogError, "can't open --backup file or device: status=%s", @@ -452,11 +438,11 @@ bool open_backup_transcoder( transcoder_config.input_sample_spec = audio::SampleSpec(backup_source.sample_spec().sample_rate(), - receiver_config.common.output_sample_spec.pcm_format(), + receiver_config.common.output_sample_spec.pcm_subformat(), receiver_config.common.output_sample_spec.channel_set()); transcoder_config.output_sample_spec = audio::SampleSpec(receiver_config.common.output_sample_spec.sample_rate(), - receiver_config.common.output_sample_spec.pcm_format(), + receiver_config.common.output_sample_spec.pcm_subformat(), receiver_config.common.output_sample_spec.channel_set()); backup_transcoder.reset(new (context.arena()) pipeline::TranscoderSource( @@ -665,12 +651,12 @@ int main(int argc, char** argv) { } core::ScopedPtr<sndio::ISink> output_sink; - if (!open_output_sink(backend_dispatcher, io_config, output_uri, - args.output_format_arg, output_sink)) { + if (!open_output_sink(backend_dispatcher, io_config, output_uri, output_sink)) { return 1; } io_config.sample_spec = output_sink->sample_spec(); + io_config.frame_length = output_sink->frame_length(); pipeline::ReceiverSourceConfig receiver_config; if (!build_receiver_config(args, receiver_config, context, *output_sink)) { @@ -687,7 +673,7 @@ int main(int argc, char** argv) { } if (!open_backup_source(backend_dispatcher, io_config, backup_uri, - args.backup_format_arg, backup_source)) { + backup_source)) { return 1; } diff --git a/src/tools/roc_send/cmdline.ggo b/src/tools/roc_send/cmdline.ggo index 8359db854..6e16b454a 100644 --- a/src/tools/roc_send/cmdline.ggo +++ b/src/tools/roc_send/cmdline.ggo @@ -11,7 +11,6 @@ option "list-supported" L "List supported protocols, formats, etc." optional section "Input options" option "input" i "Input file or device URI" typestr="IO_URI" string optional - option "input-format" - "Force input file format" typestr="FILE_FORMAT" string optional option "io-encoding" - "Input device encoding" typestr="IO_ENCODING" string optional option "io-latency" - "Input device latency, TIME units" typestr="TIME"string optional diff --git a/src/tools/roc_send/main.cpp b/src/tools/roc_send/main.cpp index 7b8879318..f759efebe 100644 --- a/src/tools/roc_send/main.cpp +++ b/src/tools/roc_send/main.cpp @@ -93,10 +93,15 @@ bool build_context_config(const gengetopt_args_info& args, } } else { audio::SampleSpec spec = io_config.sample_spec; - spec.use_defaults(audio::Sample_RawFormat, audio::ChanLayout_Surround, - audio::ChanOrder_Smpte, audio::ChanMask_Surround_7_1_4, 48000); - context_config.max_packet_size = packet::Packet::approx_size( - spec.ns_2_samples_overall(io_config.frame_length)); + spec.use_defaults(audio::Format_Pcm, audio::PcmSubformat_Raw, + audio::ChanLayout_Surround, audio::ChanOrder_Smpte, + audio::ChanMask_Surround_7_1_4, 48000); + core::nanoseconds_t len = io_config.frame_length; + if (len == 0) { + len = 10 * core::Millisecond; + } + context_config.max_packet_size = + packet::Packet::approx_size(spec.ns_2_samples_overall(len)); } if (args.max_frame_size_given) { @@ -349,13 +354,6 @@ bool build_sender_config(const gengetopt_args_info& args, roc_log(LogError, "invalid --max-latency: should be > 0"); return false; } - if (sender_config.latency.min_target_latency - > sender_config.latency.max_target_latency) { - roc_log( - LogError, - "incorrect --max-latency: should be greater or equal to --min-latency"); - return false; - } } sender_config.enable_profiling = args.prof_flag; @@ -367,7 +365,7 @@ bool build_sender_config(const gengetopt_args_info& args, sender_config.enable_cpu_clock = !input_source.has_clock(); sender_config.input_sample_spec = input_source.sample_spec(); - if (!sender_config.input_sample_spec.is_valid()) { + if (!sender_config.input_sample_spec.is_complete()) { roc_log(LogError, "can't detect input encoding, try to set it " "explicitly with --io-encoding option"); @@ -385,30 +383,16 @@ bool parse_input_uri(const gengetopt_args_info& args, address::IoUri& input_uri) } } - if (args.input_format_given) { - if (input_uri.is_valid() && !input_uri.is_file()) { - roc_log(LogError, - "--input-format can't be used if --input is not a file URI"); - return false; - } - } else { - if (input_uri.is_special_file()) { - roc_log(LogError, "--input-format should be specified if --input is \"-\""); - return false; - } - } - return true; } bool open_input_source(sndio::BackendDispatcher& backend_dispatcher, const sndio::IoConfig& io_config, const address::IoUri& input_uri, - const char* input_format, core::ScopedPtr<sndio::ISource>& input_source) { if (input_uri.is_valid()) { - const status::StatusCode code = backend_dispatcher.open_source( - input_uri, input_format, io_config, input_source); + const status::StatusCode code = + backend_dispatcher.open_source(input_uri, io_config, input_source); if (code != status::StatusOK) { roc_log(LogError, "can't open --input file or device: status=%s", @@ -605,12 +589,12 @@ int main(int argc, char** argv) { } core::ScopedPtr<sndio::ISource> input_source; - if (!open_input_source(backend_dispatcher, io_config, input_uri, - args.input_format_arg, input_source)) { + if (!open_input_source(backend_dispatcher, io_config, input_uri, input_source)) { return 1; } io_config.sample_spec = input_source->sample_spec(); + io_config.frame_length = input_source->frame_length(); pipeline::SenderSinkConfig sender_config; if (!build_sender_config(args, sender_config, context, *input_source)) {