├── COPYING ├── LICENSE ├── Makefile ├── README.md ├── debian ├── bzr-builder.manifest ├── changelog ├── compat ├── control ├── copyright ├── rules ├── source │ └── format └── watch ├── libdsd2pcm ├── dither.h ├── dsd_pcm_constants.h ├── dsd_pcm_converter.h ├── dsd_pcm_converter_engine.cpp ├── dsd_pcm_converter_engine.h ├── dsd_pcm_converter_hq.cpp ├── dsd_pcm_converter_hq.h ├── dsd_pcm_converter_multistage.h ├── dsd_pcm_filter_setup.h ├── dsd_pcm_fir.h ├── dsd_pcm_util.h ├── pcm_pcm_fir.h ├── upsampler.cpp └── upsampler.h ├── libdstdec ├── ac_data.cpp ├── ac_data.h ├── coded_table.cpp ├── coded_table.h ├── dst_consts.h ├── dst_decoder.cpp ├── dst_decoder.h ├── dst_decoder_mt.cpp ├── dst_decoder_mt.h ├── dst_defs.h ├── frame_reader.cpp ├── frame_reader.h ├── str_data.cpp └── str_data.h ├── libsacd ├── endianess.h ├── sacd_disc.cpp ├── sacd_disc.h ├── sacd_dsd.h ├── sacd_dsdiff.cpp ├── sacd_dsdiff.h ├── sacd_dsf.cpp ├── sacd_dsf.h ├── sacd_media.cpp ├── sacd_media.h ├── sacd_reader.h ├── scarletbook.cpp ├── scarletbook.h └── version.h ├── main.cpp └── man └── sacd.1 /COPYING: -------------------------------------------------------------------------------- 1 | Copyright 2015-2019 Robert Tari 2 | Copyright 2012 Vladislav Goncharov 3 | Copyright 2011-2016 Maxim V.Anisiutkin 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PNAME=sacd 2 | PNLIB=lib$(PNAME).so.19 3 | 4 | ARCH = $(shell getconf LONG_BIT) 5 | 6 | CXX = g++ 7 | 8 | CXXFLAGS_32 = -msse2 9 | CXXFLAGS_64 = 10 | CXXFLAGS = $(CXXFLAGS_$(ARCH)) -std=c++11 -Wall -O3 11 | #CXXFLAGS += -g -ggdb3 12 | 13 | VPATH = libdstdec:libdsd2pcm:libsacd 14 | 15 | INCLUDE_DIRS = libdstdec libdsd2pcm libsacd 16 | CPPFLAGS = $(foreach includedir,$(INCLUDE_DIRS),-I$(includedir)) 17 | 18 | LIBRARIES = rt pthread 19 | LIBRARY_DIRS = libdstdec libdsd2pcm libsacd 20 | LDFLAGS = $(foreach librarydir,$(LIBRARY_DIRS),-L$(librarydir)) 21 | LDFLAGS += $(foreach library,$(LIBRARIES),-l$(library)) 22 | 23 | PREFIX := /usr 24 | 25 | .PHONY: all clean install 26 | 27 | all: clean $(PNAME) 28 | 29 | str_data: dst_defs.h str_data.h str_data.cpp 30 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c libdstdec/str_data.cpp -o libdstdec/str_data.o 31 | 32 | ac_data: dst_defs.h ac_data.h ac_data.cpp 33 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c libdstdec/ac_data.cpp -o libdstdec/ac_data.o 34 | 35 | coded_table: dst_defs.h coded_table.h coded_table.cpp 36 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c libdstdec/coded_table.cpp -o libdstdec/coded_table.o 37 | 38 | frame_reader: str_data.h coded_table.h frame_reader.h frame_reader.cpp 39 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c libdstdec/frame_reader.cpp -o libdstdec/frame_reader.o 40 | 41 | dst_decoder: str_data.h ac_data.h coded_table.h frame_reader.h dst_decoder.h dst_decoder.cpp 42 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c libdstdec/dst_decoder.cpp -o libdstdec/dst_decoder.o 43 | 44 | dst_decoder_mt: dst_decoder.h dst_decoder_mt.h dst_decoder_mt.cpp 45 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c libdstdec/dst_decoder_mt.cpp -o libdstdec/dst_decoder_mt.o 46 | 47 | dsd_pcm_converter_engine: dsd_pcm_converter_multistage.h dsd_pcm_converter_engine.h dsd_pcm_converter_engine.cpp 48 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c libdsd2pcm/dsd_pcm_converter_engine.cpp -o libdsd2pcm/dsd_pcm_converter_engine.o 49 | 50 | upsampler: dither.h upsampler.h upsampler.cpp 51 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c libdsd2pcm/upsampler.cpp -o libdsd2pcm/upsampler.o 52 | 53 | dsd_pcm_converter_hq: upsampler.h dsd_pcm_converter_hq.h dsd_pcm_converter_hq.cpp 54 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c libdsd2pcm/dsd_pcm_converter_hq.cpp -o libdsd2pcm/dsd_pcm_converter_hq.o 55 | 56 | scarletbook: scarletbook.h scarletbook.cpp 57 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c libsacd/scarletbook.cpp -o libsacd/scarletbook.o 58 | 59 | sacd_disc: endianess.h scarletbook.h sacd_reader.h sacd_disc.h sacd_disc.cpp 60 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c libsacd/sacd_disc.cpp -o libsacd/sacd_disc.o 61 | 62 | sacd_media: scarletbook.h scarletbook.h sacd_media.h sacd_media.cpp 63 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c libsacd/sacd_media.cpp -o libsacd/sacd_media.o 64 | 65 | sacd_dsdiff: scarletbook.h sacd_dsd.h sacd_reader.h endianess.h sacd_dsdiff.h sacd_dsdiff.cpp 66 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c libsacd/sacd_dsdiff.cpp -o libsacd/sacd_dsdiff.o 67 | 68 | sacd_dsf: scarletbook.h sacd_dsd.h sacd_reader.h endianess.h sacd_dsf.h sacd_dsf.cpp 69 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c libsacd/sacd_dsf.cpp -o libsacd/sacd_dsf.o 70 | 71 | main: version.h sacd_reader.h sacd_disc.h sacd_dsdiff.h sacd_dsf.h dsd_pcm_converter_hq.h dsd_pcm_converter_engine.h main.cpp 72 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c main.cpp -o main.o 73 | 74 | $(PNAME): str_data ac_data coded_table frame_reader dst_decoder dst_decoder_mt dsd_pcm_converter_engine upsampler dsd_pcm_converter_hq scarletbook sacd_disc sacd_media sacd_dsdiff sacd_dsf main 75 | $(CXX) $(CXXFLAGS) -o sacd libdsd2pcm/upsampler.o libdsd2pcm/dsd_pcm_converter_hq.o libdsd2pcm/dsd_pcm_converter_engine.o libdstdec/frame_reader.o libdstdec/ac_data.o libdstdec/str_data.o libdstdec/coded_table.o libdstdec/dst_decoder.o libdstdec/dst_decoder_mt.o libsacd/sacd_media.o libsacd/sacd_dsf.o libsacd/sacd_dsdiff.o libsacd/scarletbook.o libsacd/sacd_disc.o main.o $(LDFLAGS) 76 | 77 | shared: str_data ac_data coded_table frame_reader dst_decoder dst_decoder_mt dsd_pcm_converter_engine upsampler dsd_pcm_converter_hq scarletbook sacd_disc sacd_media sacd_dsdiff sacd_dsf main 78 | $(CXX) -shared $(CXXFLAGS) -Wl,-soname,$(PNLIB) -o $(PNLIB) libdsd2pcm/upsampler.o libdsd2pcm/dsd_pcm_converter_hq.o libdsd2pcm/dsd_pcm_converter_engine.o libdstdec/frame_reader.o libdstdec/ac_data.o libdstdec/str_data.o libdstdec/coded_table.o libdstdec/dst_decoder.o libdstdec/dst_decoder_mt.o libsacd/sacd_media.o libsacd/sacd_dsf.o libsacd/sacd_dsdiff.o libsacd/scarletbook.o libsacd/sacd_disc.o $(LDFLAGS) 79 | $(CXX) $(CXXFLAGS) -o $(PNAME) $(PNLIB) main.o $(LDFLAGS) 80 | 81 | clean: 82 | rm -f $(PNAME) $(PNLIB) *.o $(foreach librarydir,$(LIBRARY_DIRS),$(librarydir)/*.o) 83 | 84 | install: sacd 85 | 86 | install -d $(DESTDIR)$(PREFIX)/bin 87 | install -m 0755 $(PNAME) $(DESTDIR)$(PREFIX)/bin 88 | install -d $(DESTDIR)$(PREFIX)/share/man/man1 89 | install -m 0644 ./man/$(PNAME).1 $(DESTDIR)$(PREFIX)/share/man/man1 90 | 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sacd 2 | Robert Tari 3 | https://tari.in/www/software/sacd/ 4 | 5 | ## Description 6 | Super Audio CD decoder. 7 | Converts SACD image files, Philips DSDIFF and Sony DSF files to 24-bit high resolution wave files. Handles both DST and DSD streams. 8 | 9 | ## Usage 10 | 11 | sacd -i infile [-o outdir] [options] 12 | 13 | -i, --infile : Specify the input file (*.iso, *.dsf, *.dff) 14 | -o, --outdir : The folder to write the WAVE files to. If you omit 15 | this, the files will be placed in the input file's 16 | directory 17 | -c, --stdout : Stdout output (for pipe), sample: 18 | sacd -i file.dsf -c | play - 19 | -r, --rate : The output samplerate. 20 | Valid rates are: 88200, 96000, 176400 and 192000. 21 | If you omit this, 96KHz will be used. 22 | -s, --stereo : Only extract the 2-channel area if it exists. 23 | If you omit this, the multichannel area will have priority. 24 | -p, --progress : Display progress to new lines. Use this if you intend 25 | to parse the output through a script. This option only 26 | lists either one progress percentage per line, or one 27 | status/error message. 28 | -h, --help : Show this help message 29 | 30 | -------------------------------------------------------------------------------- /debian/bzr-builder.manifest: -------------------------------------------------------------------------------- 1 | # bzr-builder format 0.3 deb-version {debupstream}.37 2 | lp:sacd revid:robert@tari.in-20190715223949-zxngtgvgo6zfxuvh 3 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | sacd (19.7.16.37~ubuntu19.04.1) disco; urgency=low 2 | 3 | * Auto build. 4 | 5 | -- Robert Tari Mon, 15 Jul 2019 22:42:36 +0000 6 | 7 | sacd (19.7.16) xenial; urgency=medium 8 | 9 | * Make sure aligned_alloc uses multiple of the alignment (LP: #1836478) 10 | 11 | -- Robert Tari Tue, 16 Jul 2019 00:34:52 +0200 12 | 13 | sacd (19.5.3) xenial; urgency=medium 14 | 15 | * Fixed uneven padding error in DSDIFF 16 | * Some code refactoring 17 | 18 | -- Robert Tari Fri, 03 May 2019 12:08:47 +0200 19 | 20 | sacd (19.3.21) xenial; urgency=medium 21 | 22 | * Added option to display source info (LP: #1820971) 23 | * Set 88.2KHz as default 24 | 25 | -- Robert Tari Thu, 21 Mar 2019 21:23:48 +0100 26 | 27 | sacd (18.6.25) xenial; urgency=medium 28 | 29 | * Added option to extract stereo area only 30 | 31 | -- Robert Tari Mon, 25 Jun 2018 01:45:42 +0200 32 | 33 | sacd (18.5.4) xenial; urgency=medium 34 | 35 | * Fixed DSDIFF track length 36 | 37 | -- Robert Tari Fri, 04 May 2018 02:44:24 +0200 38 | 39 | sacd (16.10.03) xenial; urgency=medium 40 | 41 | * Keep edited master chunks by default 42 | 43 | -- Robert Tari Mon, 03 Oct 2016 23:14:52 +0200 44 | 45 | sacd (16.09.21) xenial; urgency=medium 46 | 47 | * Multithreaded pcm converter 48 | * Highest quality 88200*x decoder 49 | * Improved de-clicking 50 | * Reworked float->24bit logic 51 | * Some rework in dsf reader 52 | * Various minor code changes 53 | 54 | -- Robert Tari Wed, 21 Sep 2016 02:33:30 +0200 55 | 56 | sacd (16.06.23) xenial; urgency=medium 57 | 58 | * de-Gibbs removed 59 | 60 | -- Robert Tari Thu, 23 Jun 2016 00:50:52 +0200 61 | 62 | sacd (16.06.07) xenial; urgency=medium 63 | 64 | * Signal when each decoding thread finishes 65 | 66 | -- Robert Tari Tue, 07 Jun 2016 17:39:12 +0200 67 | 68 | sacd (16.03.29) trusty; urgency=medium 69 | 70 | * Updated progress calculations 71 | 72 | -- Robert Tari Tue, 29 Mar 2016 15:59:31 +0200 73 | 74 | sacd (16.03.18) trusty; urgency=medium 75 | 76 | * Updated filenames to display channel count 77 | 78 | -- Robert Tari Mon, 28 Mar 2016 22:31:06 +0200 79 | 80 | sacd (16.03.18) trusty; urgency=medium 81 | 82 | * Updated progress calculations 83 | 84 | -- Robert Tari Fri, 18 Mar 2016 14:49:57 +0100 85 | 86 | sacd (16.03.15) trusty; urgency=medium 87 | 88 | * Updated progress calculations 89 | 90 | -- Robert Tari Tue, 15 Mar 2016 13:25:34 +0100 91 | 92 | sacd (16.03.14) trusty; urgency=medium 93 | 94 | * DSF header bug fix 95 | * Set lower thread limit 96 | * Updated progress calculations 97 | 98 | -- Robert Tari Mon, 14 Mar 2016 21:30:35 +0100 99 | 100 | sacd (16.03.12) trusty; urgency=medium 101 | 102 | * Added support for discs with mismatching track count in 103 | stereo/multichannel areas 104 | 105 | -- Robert Tari Sat, 12 Mar 2016 17:35:25 +0100 106 | 107 | sacd (16.01.25) trusty; urgency=medium 108 | 109 | * Replace unsafe characters in output filename 110 | * Format output filename depending on artist tag presence 111 | 112 | -- Robert Tari Mon, 25 Jan 2016 20:29:19 +0100 113 | 114 | sacd (15.11.21) trusty; urgency=medium 115 | 116 | * Filter out invalid trailing DSD samples 117 | 118 | -- Robert Tari Sun, 22 Nov 2015 00:11:24 +0100 119 | 120 | sacd (15.08.14) trusty; urgency=medium 121 | 122 | * Fixed progress hang when CPU count is greater than track count 123 | 124 | -- Robert Tari Fri, 14 Aug 2015 12:12:53 +0200 125 | 126 | sacd (15.08.04) trusty; urgency=medium 127 | 128 | * Multi-threaded DST decoder 129 | * Lots of code rewrites 130 | 131 | -- Robert Tari Tue, 04 Aug 2015 02:09:22 +0200 132 | 133 | sacd (15.06.29) trusty; urgency=medium 134 | 135 | * Added support for DSD256 and DSD512 136 | * Removed single-precision filters 137 | 138 | -- Robert Tari Mon, 29 Jun 2015 02:42:46 +0200 139 | 140 | sacd (15.06.26) trusty; urgency=medium 141 | 142 | * Added multiple sample rates 143 | 144 | -- Robert Tari Fri, 26 Jun 2015 22:57:26 +0200 145 | 146 | sacd (15.06.15) trusty; urgency=medium 147 | 148 | * Invalid files now do not throw exceptions, but exit gracefully 149 | 150 | -- Robert Tari Mon, 15 Jun 2015 23:22:38 +0200 151 | 152 | sacd (15.06.10) trusty; urgency=medium 153 | 154 | * The -p switch prints a 1-based track index 155 | 156 | -- Robert Tari Wed, 10 Jun 2015 00:37:33 +0200 157 | 158 | sacd (15.06.05) trusty; urgency=medium 159 | 160 | * getopt error handling 161 | * Automated versioning 162 | * Target filenames from metadata 163 | 164 | -- Robert Tari Fri, 05 Jun 2015 23:48:07 +0200 165 | 166 | sacd (15.05.25) trusty; urgency=medium 167 | 168 | * Initial release 169 | 170 | -- Robert Tari Mon, 25 May 2015 22:55:49 +0200 171 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 10 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: sacd 2 | Section: sound 3 | Priority: optional 4 | Maintainer: Robert Tari 5 | Build-Depends: debhelper (>= 10), g++ 6 | Standards-Version: 4.0.1 7 | Homepage: https://tari.in/www/software/sacd/ 8 | 9 | Package: sacd 10 | Architecture: any 11 | Depends: ${shlibs:Depends}, ${misc:Depends} 12 | Description: Super Audio CD decoder 13 | Converts SACD image files, Philips DSDIFF and Sony DSF files to 14 | 24-bit high resolution wave files. Handles both DST and DSD 15 | streams. 16 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: sacd 3 | Upstream-Contact: Robert Tari 4 | Source: https://code.launchpad.net/~robert-tari/sacd/trunk 5 | 6 | Files: * 7 | Copyright: 2015-2019 Robert Tari 8 | 2012 Vladislav Goncharov 9 | 2011-2019 Maxim V.Anisiutkin 10 | License: GPL-3.0+ 11 | 12 | Files: debian/* 13 | Copyright: 2019 Robert Tari 14 | License: GPL-3.0+ 15 | 16 | License: GPL-3.0+ 17 | This program is free software: you can redistribute it and/or modify 18 | it under the terms of the GNU General Public License as published by 19 | the Free Software Foundation, either version 3 of the License, or 20 | (at your option) any later version. 21 | . 22 | This package is distributed in the hope that it will be useful, 23 | but WITHOUT ANY WARRANTY; without even the implied warranty of 24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 | GNU General Public License for more details. 26 | . 27 | You should have received a copy of the GNU General Public License 28 | along with this program. If not, see . 29 | . 30 | On Debian systems, the complete text of the GNU General 31 | Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". 32 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # See debhelper(7) (uncomment to enable) 3 | # output every command that modifies files on the build system. 4 | #DH_VERBOSE = 1 5 | 6 | # see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/* 7 | DPKG_EXPORT_BUILDFLAGS = 1 8 | include /usr/share/dpkg/default.mk 9 | 10 | # see FEATURE AREAS in dpkg-buildflags(1) 11 | #export DEB_BUILD_MAINT_OPTIONS = hardening=+all 12 | 13 | # see ENVIRONMENT in dpkg-buildflags(1) 14 | # package maintainers to append CFLAGS 15 | #export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic 16 | # package maintainers to append LDFLAGS 17 | #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed 18 | 19 | 20 | # main packaging script based on dh7 syntax 21 | %: 22 | dh $@ 23 | 24 | # debmake generated override targets 25 | # This is example for Cmake (See http://bugs.debian.org/641051 ) 26 | #override_dh_auto_configure: 27 | # dh_auto_configure -- \ 28 | # -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /debian/watch: -------------------------------------------------------------------------------- 1 | version=3 2 | http://launchpad.net/sacd/+download .*/sacd-([0-9.]+)\.tar\.gz 3 | -------------------------------------------------------------------------------- /libdsd2pcm/dither.h: -------------------------------------------------------------------------------- 1 | /* 2 | HQ DSD->PCM converter 88.2/96 kHz 3 | Copyright (c) 2015-2016 Robert Tari 4 | Copyright (c) 2012 Vladislav Goncharov 5 | 6 | This file is part of SACD. 7 | 8 | SACD is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | SACD is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with SACD. If not, see . 20 | */ 21 | 22 | #ifndef _dither_h_ 23 | #define _dither_h_ 24 | 25 | // inline dithering implementation 26 | class Dither 27 | { 28 | public: 29 | 30 | Dither(unsigned int n_bits); 31 | Dither& operator=(const Dither &obj); 32 | double processSample(double x) { return (x + m_rand_max * (double)(fast_rand() - (RAND_MAX / 2)) / (double)(RAND_MAX / 2)); } 33 | 34 | protected: 35 | 36 | double m_rand_max; 37 | int m_holdrand; 38 | int fast_rand() { return (((m_holdrand = m_holdrand * 214013L + 2531011L) >> 16) & 0x7fff); } // libc rand() is too slow 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /libdsd2pcm/dsd_pcm_constants.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2016 Robert Tari 3 | Copyright (c) 2011-2015 Maxim V.Anisiutkin 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #pragma once 22 | 23 | #include 24 | 25 | static const int MEM_ALIGN = 64; 26 | 27 | #define DSDxFs1 (44100 * 1) 28 | #define DSDxFs2 (44100 * 2) 29 | #define DSDxFs4 (44100 * 4) 30 | #define DSDxFs8 (44100 * 8) 31 | #define DSDxFs64 (44100 * 64) 32 | #define DSDxFs128 (44100 * 128) 33 | #define DSDxFs256 (44100 * 256) 34 | #define DSDxFs512 (44100 * 512) 35 | #define DSD_SILENCE_BYTE 0x69 36 | #define CTABLES(fir_length) ((fir_length + 7) / 8) 37 | #define DSDPCM_MAX_CHANNELS 6 38 | #define DSDPCM_MAX_FRAMELEN (DSDxFs128 / 75 / 8) 39 | #define DSDPCM_MAX_SAMPLES (DSDPCM_MAX_FRAMELEN * DSDPCM_MAX_CHANNELS) 40 | #define DSDPCM_GAIN_0 8388608 41 | #define DSDFIR1_8_LENGTH 80 42 | #define DSDFIR1_16_LENGTH 160 43 | #define DSDFIR1_64_LENGTH 641 44 | #define PCMFIR2_2_LENGTH 27 45 | #define PCMFIR3_2_LENGTH 151 46 | #define PCMFIR_OFFSET 0x7fffffff 47 | #define PCMFIR_SCALE 31 48 | 49 | const double DSDFIR1_8_COEFS[DSDFIR1_8_LENGTH] = 50 | { 51 | -142, 52 | -651, 53 | -1997, 54 | -4882, 55 | -10198, 56 | -18819, 57 | -31226, 58 | -46942, 59 | -63892, 60 | -77830, 61 | -82099, 62 | -67999, 63 | -26010, 64 | 52003, 65 | 169742, 66 | 323000, 67 | 496497, 68 | 662008, 69 | 778827, 70 | 797438, 71 | 666789, 72 | 344848, 73 | -188729, 74 | -919845, 75 | -1789769, 76 | -2690283, 77 | -3466610, 78 | -3929490, 79 | -3876295, 80 | -3119266, 81 | -1517221, 82 | 994203, 83 | 4379191, 84 | 8490255, 85 | 13072043, 86 | 17781609, 87 | 22223533, 88 | 25995570, 89 | 28738430, 90 | 30182209, 91 | 30182209, 92 | 28738430, 93 | 25995570, 94 | 22223533, 95 | 17781609, 96 | 13072043, 97 | 8490255, 98 | 4379191, 99 | 994203, 100 | -1517221, 101 | -3119266, 102 | -3876295, 103 | -3929490, 104 | -3466610, 105 | -2690283, 106 | -1789769, 107 | -919845, 108 | -188729, 109 | 344848, 110 | 666789, 111 | 797438, 112 | 778827, 113 | 662008, 114 | 496497, 115 | 323000, 116 | 169742, 117 | 52003, 118 | -26010, 119 | -67999, 120 | -82099, 121 | -77830, 122 | -63892, 123 | -46942, 124 | -31226, 125 | -18819, 126 | -10198, 127 | -4882, 128 | -1997, 129 | -651, 130 | -142, 131 | }; 132 | 133 | const double DSDFIR1_16_COEFS[DSDFIR1_16_LENGTH] = 134 | { 135 | -42, 136 | -102, 137 | -220, 138 | -420, 139 | -739, 140 | -1220, 141 | -1914, 142 | -2878, 143 | -4171, 144 | -5851, 145 | -7967, 146 | -10555, 147 | -13625, 148 | -17154, 149 | -21075, 150 | -25266, 151 | -29539, 152 | -33636, 153 | -37219, 154 | -39874, 155 | -41114, 156 | -40390, 157 | -37108, 158 | -30659, 159 | -20450, 160 | -5948, 161 | 13272, 162 | 37474, 163 | 66704, 164 | 100733, 165 | 139006, 166 | 180597, 167 | 224174, 168 | 267987, 169 | 309866, 170 | 347255, 171 | 377263, 172 | 396750, 173 | 402440, 174 | 391067, 175 | 359534, 176 | 305112, 177 | 225636, 178 | 119722, 179 | -13034, 180 | -171854, 181 | -354614, 182 | -557713, 183 | -775985, 184 | -1002675, 185 | -1229481, 186 | -1446662, 187 | -1643229, 188 | -1807208, 189 | -1925973, 190 | -1986643, 191 | -1976541, 192 | -1883674, 193 | -1697253, 194 | -1408195, 195 | -1009619, 196 | -497293, 197 | 129993, 198 | 870122, 199 | 1717463, 200 | 2662800, 201 | 3693381, 202 | 4793111, 203 | 5942870, 204 | 7120962, 205 | 8303674, 206 | 9465936, 207 | 10582054, 208 | 11626490, 209 | 12574667, 210 | 13403753, 211 | 14093414, 212 | 14626488, 213 | 14989568, 214 | 15173448, 215 | 15173448, 216 | 14989568, 217 | 14626488, 218 | 14093414, 219 | 13403753, 220 | 12574667, 221 | 11626490, 222 | 10582054, 223 | 9465936, 224 | 8303674, 225 | 7120962, 226 | 5942870, 227 | 4793111, 228 | 3693381, 229 | 2662800, 230 | 1717463, 231 | 870122, 232 | 129993, 233 | -497293, 234 | -1009619, 235 | -1408195, 236 | -1697253, 237 | -1883674, 238 | -1976541, 239 | -1986643, 240 | -1925973, 241 | -1807208, 242 | -1643229, 243 | -1446662, 244 | -1229481, 245 | -1002675, 246 | -775985, 247 | -557713, 248 | -354614, 249 | -171854, 250 | -13034, 251 | 119722, 252 | 225636, 253 | 305112, 254 | 359534, 255 | 391067, 256 | 402440, 257 | 396750, 258 | 377263, 259 | 347255, 260 | 309866, 261 | 267987, 262 | 224174, 263 | 180597, 264 | 139006, 265 | 100733, 266 | 66704, 267 | 37474, 268 | 13272, 269 | -5948, 270 | -20450, 271 | -30659, 272 | -37108, 273 | -40390, 274 | -41114, 275 | -39874, 276 | -37219, 277 | -33636, 278 | -29539, 279 | -25266, 280 | -21075, 281 | -17154, 282 | -13625, 283 | -10555, 284 | -7967, 285 | -5851, 286 | -4171, 287 | -2878, 288 | -1914, 289 | -1220, 290 | -739, 291 | -420, 292 | -220, 293 | -102, 294 | -42, 295 | }; 296 | 297 | const double DSDFIR1_64_COEFS[DSDFIR1_64_LENGTH] = 298 | { 299 | 1652, 421, 509, 606, 714, 832, 300 | 960, 1098, 1245, 1402, 1567, 1739, 301 | 1917, 2101, 2287, 2475, 2663, 2848, 302 | 3027, 3199, 3359, 3504, 3632, 3738, 303 | 3818, 3869, 3886, 3864, 3800, 3689, 304 | 3525, 3305, 3024, 2677, 2260, 1769, 305 | 1201, 550, -184, -1006, -1918, -2921, 306 | -4016, -5203, -6483, -7854, -9314, -10859, 307 | -12487, -14190, -15965, -17802, -19693, -21630, 308 | -23600, -25591, -27591, -29584, -31555, -33486, 309 | -35359, -37155, -38854, -40434, -41872, -43146, 310 | -44233, -45108, -45747, -46124, -46216, -45997, 311 | -45443, -44530, -43236, -41537, -39414, -36847, 312 | -33818, -30310, -26311, -21809, -16796, -11265, 313 | -5214, 1355, 8439, 16029, 24112, 32673, 314 | 41689, 51136, 60984, 71196, 81734, 92553, 315 | 103603, 114829, 126173, 137571, 148954, 160248, 316 | 171377, 182260, 192811, 202943, 212564, 221581, 317 | 229897, 237417, 244040, 249669, 254204, 257547, 318 | 259603, 260277, 259477, 257115, 253109, 247380, 319 | 239855, 230471, 219167, 205896, 190617, 173299, 320 | 153922, 132479, 108972, 83419, 55848, 26302, 321 | -5160, -38467, -73531, -110251, -148507, -188168, 322 | -229083, -271089, -314005, -357638, -401777, -446201, 323 | -490671, -534940, -578744, -621813, -663865, -704607, 324 | -743744, -780970, -815977, -848454, -878088, -904566, 325 | -927580, -946823, -961997, -972811, -978983, -980246, 326 | -976346, -967047, -952131, -931400, -904681, -871825, 327 | -832710, -787243, -735363, -677041, -612281, -541124, 328 | -463648, -379970, -290244, -194666, -93474, 13055, 329 | 124601, 240802, 361256, 485518, 613104, 743490, 330 | 876114, 1010376, 1145641, 1281243, 1416482, 1550632, 331 | 1682939, 1812627, 1938901, 2060949, 2177947, 2289061, 332 | 2393456, 2490292, 2578736, 2657963, 2727161, 2785536, 333 | 2832319, 2866766, 2888167, 2895851, 2889189, 2867598, 334 | 2830552, 2777578, 2708268, 2622279, 2519340, 2399255, 335 | 2261904, 2107255, 1935358, 1746352, 1540469, 1318035, 336 | 1079470, 825295, 556126, 272680, -24227, -333679, 337 | -654661, -986063, -1326676, -1675201, -2030245, -2390331, 338 | -2753893, -3119291, -3484807, -3848654, -4208980, -4563877, 339 | -4911383, -5249495, -5576169, -5889335, -6186898, -6466754, 340 | -6726792, -6964906, -7179003, -7367014, -7526900, -7656665, 341 | -7754364, -7818111, -7846090, -7836566, -7787889, -7698508, 342 | -7566979, -7391970, -7172272, -6906808, -6594638, -6234967, 343 | -5827149, -5370698, -4865289, -4310766, -3707142, -3054607, 344 | -2353529, -1604454, -808113, 34585, 922545, 1854492, 345 | 2828973, 3844355, 4898832, 5990428, 7117000, 8276247, 346 | 9465711, 10682788, 11924736, 13188679, 14471619, 15770447, 347 | 17081948, 18402817, 19729666, 21059038, 22387418, 23711244, 348 | 25026925, 26330844, 27619383, 28888926, 30135877, 31356675, 349 | 32547801, 33705799, 34827283, 35908952, 36947604, 37940146, 350 | 38883606, 39775147, 40612074, 41391849, 42112096, 42770616, 351 | 43365391, 43894592, 44356589, 44749958, 45073480, 45326155, 352 | 45507200, 45616052, 45652373, 45616052, 45507200, 45326155, 353 | 45073480, 44749958, 44356589, 43894592, 43365391, 42770616, 354 | 42112096, 41391849, 40612074, 39775147, 38883606, 37940146, 355 | 36947604, 35908952, 34827283, 33705799, 32547801, 31356675, 356 | 30135877, 28888926, 27619383, 26330844, 25026925, 23711244, 357 | 22387418, 21059038, 19729666, 18402817, 17081948, 15770447, 358 | 14471619, 13188679, 11924736, 10682788, 9465711, 8276247, 359 | 7117000, 5990428, 4898832, 3844355, 2828973, 1854492, 360 | 922545, 34585, -808113, -1604454, -2353529, -3054607, 361 | -3707142, -4310766, -4865289, -5370698, -5827149, -6234967, 362 | -6594638, -6906808, -7172272, -7391970, -7566979, -7698508, 363 | -7787889, -7836566, -7846090, -7818111, -7754364, -7656665, 364 | -7526900, -7367014, -7179003, -6964906, -6726792, -6466754, 365 | -6186898, -5889335, -5576169, -5249495, -4911383, -4563877, 366 | -4208980, -3848654, -3484807, -3119291, -2753893, -2390331, 367 | -2030245, -1675201, -1326676, -986063, -654661, -333679, 368 | -24227, 272680, 556126, 825295, 1079470, 1318035, 369 | 1540469, 1746352, 1935358, 2107255, 2261904, 2399255, 370 | 2519340, 2622279, 2708268, 2777578, 2830552, 2867598, 371 | 2889189, 2895851, 2888167, 2866766, 2832319, 2785536, 372 | 2727161, 2657963, 2578736, 2490292, 2393456, 2289061, 373 | 2177947, 2060949, 1938901, 1812627, 1682939, 1550632, 374 | 1416482, 1281243, 1145641, 1010376, 876114, 743490, 375 | 613104, 485518, 361256, 240802, 124601, 13055, 376 | -93474, -194666, -290244, -379970, -463648, -541124, 377 | -612281, -677041, -735363, -787243, -832710, -871825, 378 | -904681, -931400, -952131, -967047, -976346, -980246, 379 | -978983, -972811, -961997, -946823, -927580, -904566, 380 | -878088, -848454, -815977, -780970, -743744, -704607, 381 | -663865, -621813, -578744, -534940, -490671, -446201, 382 | -401777, -357638, -314005, -271089, -229083, -188168, 383 | -148507, -110251, -73531, -38467, -5160, 26302, 384 | 55848, 83419, 108972, 132479, 153922, 173299, 385 | 190617, 205896, 219167, 230471, 239855, 247380, 386 | 253109, 257115, 259477, 260277, 259603, 257547, 387 | 254204, 249669, 244040, 237417, 229897, 221581, 388 | 212564, 202943, 192811, 182260, 171377, 160248, 389 | 148954, 137571, 126173, 114829, 103603, 92553, 390 | 81734, 71196, 60984, 51136, 41689, 32673, 391 | 24112, 16029, 8439, 1355, -5214, -11265, 392 | -16796, -21809, -26311, -30310, -33818, -36847, 393 | -39414, -41537, -43236, -44530, -45443, -45997, 394 | -46216, -46124, -45747, -45108, -44233, -43146, 395 | -41872, -40434, -38854, -37155, -35359, -33486, 396 | -31555, -29584, -27591, -25591, -23600, -21630, 397 | -19693, -17802, -15965, -14190, -12487, -10859, 398 | -9314, -7854, -6483, -5203, -4016, -2921, 399 | -1918, -1006, -184, 550, 1201, 1769, 400 | 2260, 2677, 3024, 3305, 3525, 3689, 401 | 3800, 3864, 3886, 3869, 3818, 3738, 402 | 3632, 3504, 3359, 3199, 3027, 2848, 403 | 2663, 2475, 2287, 2101, 1917, 1739, 404 | 1567, 1402, 1245, 1098, 960, 832, 405 | 714, 606, 509, 421, 1652 406 | }; 407 | 408 | const double PCMFIR2_2_COEFS[PCMFIR2_2_LENGTH] = 409 | { 410 | 349146, 411 | 0, 412 | -2503287, 413 | 0, 414 | 10155531, 415 | 0, 416 | -30459917, 417 | 0, 418 | 76750087, 419 | 0, 420 | -185782569, 421 | 0, 422 | 668365690, 423 | 1073741824, 424 | 668365690, 425 | 0, 426 | -185782569, 427 | 0, 428 | 76750087, 429 | 0, 430 | -30459917, 431 | 0, 432 | 10155531, 433 | 0, 434 | -2503287, 435 | 0, 436 | 349146, 437 | }; 438 | 439 | const double PCMFIR3_2_COEFS[PCMFIR3_2_LENGTH] = 440 | { 441 | -5412, 442 | 0, 443 | 10344, 444 | 0, 445 | -19926, 446 | 0, 447 | 35056, 448 | 0, 449 | -57881, 450 | 0, 451 | 91092, 452 | 0, 453 | -138012, 454 | 0, 455 | 202658, 456 | 0, 457 | -289823, 458 | 0, 459 | 405153, 460 | 0, 461 | -555217, 462 | 0, 463 | 747573, 464 | 0, 465 | -990842, 466 | 0, 467 | 1294782, 468 | 0, 469 | -1670364, 470 | 0, 471 | 2129866, 472 | 0, 473 | -2687005, 474 | 0, 475 | 3357104, 476 | 0, 477 | -4157326, 478 | 0, 479 | 5107022, 480 | 0, 481 | -6228238, 482 | 0, 483 | 7546440, 484 | 0, 485 | -9091589, 486 | 0, 487 | 10899739, 488 | 0, 489 | -13015406, 490 | 0, 491 | 15495180, 492 | 0, 493 | -18413298, 494 | 0, 495 | 21870494, 496 | 0, 497 | -26008543, 498 | 0, 499 | 31035142, 500 | 0, 501 | -37268765, 502 | 0, 503 | 45224971, 504 | 0, 505 | -55796870, 506 | 0, 507 | 70676173, 508 | 0, 509 | -93495917, 510 | 0, 511 | 133715464, 512 | 0, 513 | -226044891, 514 | 0, 515 | 682959923, 516 | 1073741824, 517 | 682959923, 518 | 0, 519 | -226044891, 520 | 0, 521 | 133715464, 522 | 0, 523 | -93495917, 524 | 0, 525 | 70676173, 526 | 0, 527 | -55796870, 528 | 0, 529 | 45224971, 530 | 0, 531 | -37268765, 532 | 0, 533 | 31035142, 534 | 0, 535 | -26008543, 536 | 0, 537 | 21870494, 538 | 0, 539 | -18413298, 540 | 0, 541 | 15495180, 542 | 0, 543 | -13015406, 544 | 0, 545 | 10899739, 546 | 0, 547 | -9091589, 548 | 0, 549 | 7546440, 550 | 0, 551 | -6228238, 552 | 0, 553 | 5107022, 554 | 0, 555 | -4157326, 556 | 0, 557 | 3357104, 558 | 0, 559 | -2687005, 560 | 0, 561 | 2129866, 562 | 0, 563 | -1670364, 564 | 0, 565 | 1294782, 566 | 0, 567 | -990842, 568 | 0, 569 | 747573, 570 | 0, 571 | -555217, 572 | 0, 573 | 405153, 574 | 0, 575 | -289823, 576 | 0, 577 | 202658, 578 | 0, 579 | -138012, 580 | 0, 581 | 91092, 582 | 0, 583 | -57881, 584 | 0, 585 | 35056, 586 | 0, 587 | -19926, 588 | 0, 589 | 10344, 590 | 0, 591 | -5412, 592 | }; 593 | -------------------------------------------------------------------------------- /libdsd2pcm/dsd_pcm_converter.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2016 Robert Tari 3 | Copyright (c) 2011-2015 Maxim V.Anisiutkin 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #pragma once 22 | 23 | #include "dsd_pcm_filter_setup.h" 24 | #include "dsd_pcm_fir.h" 25 | #include "pcm_pcm_fir.h" 26 | 27 | enum conv_type_e 28 | { 29 | DSDPCM_CONV_UNKNOWN = -1, 30 | DSDPCM_CONV_MULTISTAGE = 0, 31 | DSDPCM_CONV_DIRECT = 1, 32 | DSDPCM_CONV_USER = 2 33 | }; 34 | 35 | enum declick_side_e 36 | { 37 | DECLICK_NONE = 0, 38 | DECLICK_LEFT = 1, 39 | DECLICK_RIGHT = 2 40 | }; 41 | 42 | class DSDPCMConverter 43 | { 44 | protected: 45 | 46 | int framerate; 47 | int dsd_samplerate; 48 | int pcm_samplerate; 49 | float delay; 50 | double* pcm_temp1; 51 | double* pcm_temp2; 52 | 53 | public: 54 | 55 | DSDPCMConverter() 56 | { 57 | pcm_temp1 = nullptr; 58 | pcm_temp2 = nullptr; 59 | } 60 | 61 | virtual ~DSDPCMConverter() 62 | { 63 | free_pcm_temp1(); 64 | free_pcm_temp2(); 65 | } 66 | 67 | float get_delay() 68 | { 69 | return delay; 70 | } 71 | 72 | virtual void init(DSDPCMFilterSetup& flt_setup, int dsd_samples) = 0; 73 | virtual int convert(uint8_t* dsd_data, double* pcm_data, int dsd_samples) = 0; 74 | 75 | protected: 76 | 77 | void alloc_pcm_temp1(int pcm_samples) 78 | { 79 | free_pcm_temp1(); 80 | pcm_temp1 = (double*)DSDPCMUtil::mem_alloc(pcm_samples * sizeof(double)); 81 | } 82 | 83 | void alloc_pcm_temp2(int pcm_samples) 84 | { 85 | free_pcm_temp2(); 86 | pcm_temp2 = (double*)DSDPCMUtil::mem_alloc(pcm_samples * sizeof(double)); 87 | } 88 | 89 | void free_pcm_temp1() 90 | { 91 | DSDPCMUtil::mem_free(pcm_temp1); 92 | pcm_temp1 = nullptr; 93 | } 94 | 95 | void free_pcm_temp2() 96 | { 97 | DSDPCMUtil::mem_free(pcm_temp2); 98 | pcm_temp2 = nullptr; 99 | } 100 | }; 101 | -------------------------------------------------------------------------------- /libdsd2pcm/dsd_pcm_converter_engine.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2016 Robert Tari 3 | Copyright (c) 2011-2015 Maxim V.Anisiutkin 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #include "dsd_pcm_converter_engine.h" 22 | 23 | static void* ConverterThread(void* threadarg) 24 | { 25 | DSDPCMConverterSlot* slot = reinterpret_cast(threadarg); 26 | 27 | while (1) 28 | { 29 | pthread_mutex_lock(&slot->hMutex); 30 | 31 | while (slot->state != PCM_SLOT_LOADED && slot->state != PCM_SLOT_TERMINATING) 32 | { 33 | pthread_cond_wait(&slot->hEventPut, &slot->hMutex); 34 | } 35 | 36 | if (slot->state == PCM_SLOT_TERMINATING) 37 | { 38 | slot->pcm_samples = 0; 39 | pthread_mutex_unlock(&slot->hMutex); 40 | return 0; 41 | } 42 | 43 | slot->state = PCM_SLOT_RUNNING; 44 | pthread_mutex_unlock(&slot->hMutex); 45 | 46 | slot->pcm_samples = slot->converter->convert(slot->dsd_data, slot->pcm_data, slot->dsd_samples); 47 | 48 | pthread_mutex_lock(&slot->hMutex); 49 | slot->state = PCM_SLOT_READY; 50 | pthread_cond_signal(&slot->hEventGet); 51 | pthread_mutex_unlock(&slot->hMutex); 52 | } 53 | 54 | return 0; 55 | } 56 | 57 | DSDPCMConverterEngine::DSDPCMConverterEngine() 58 | { 59 | channels = 0; 60 | framerate = 0; 61 | dsd_samplerate = 0; 62 | pcm_samplerate = 0; 63 | conv_delay = 0.0f; 64 | convSlots_fp64 = nullptr; 65 | conv_called = false; 66 | 67 | for (int i = 0; i < 256; i++) 68 | { 69 | swap_bits[i] = 0; 70 | 71 | for (int j = 0; j < 8; j++) 72 | { 73 | swap_bits[i] |= ((i >> j) & 1) << (7 - j); 74 | } 75 | } 76 | } 77 | 78 | DSDPCMConverterEngine::~DSDPCMConverterEngine() 79 | { 80 | free(); 81 | 82 | conv_delay = 0.0f; 83 | } 84 | 85 | float DSDPCMConverterEngine::get_delay() 86 | { 87 | return conv_delay; 88 | } 89 | 90 | bool DSDPCMConverterEngine::is_convert_called() 91 | { 92 | return conv_called; 93 | } 94 | 95 | int DSDPCMConverterEngine::init(int channels, int framerate, int dsd_samplerate, int pcm_samplerate) 96 | { 97 | free(); 98 | 99 | this->channels = channels; 100 | this->framerate = framerate; 101 | this->dsd_samplerate = dsd_samplerate; 102 | this->pcm_samplerate = pcm_samplerate; 103 | convSlots_fp64 = init_slots(fltSetup_fp64); 104 | conv_delay = convSlots_fp64[0].converter->get_delay(); 105 | conv_called = false; 106 | 107 | return 0; 108 | } 109 | 110 | int DSDPCMConverterEngine::free() 111 | { 112 | if (convSlots_fp64) 113 | { 114 | free_slots(convSlots_fp64); 115 | convSlots_fp64 = nullptr; 116 | } 117 | 118 | return 0; 119 | } 120 | 121 | int DSDPCMConverterEngine::convert(uint8_t* dsd_data, int dsd_samples, float* pcm_data) 122 | { 123 | int pcm_samples = 0; 124 | 125 | if (!dsd_data) 126 | { 127 | if (convSlots_fp64) 128 | { 129 | pcm_samples = convertR(convSlots_fp64, pcm_data); 130 | } 131 | 132 | return pcm_samples; 133 | } 134 | 135 | if (!conv_called) 136 | { 137 | if (convSlots_fp64) 138 | { 139 | convertL(convSlots_fp64, dsd_data, dsd_samples); 140 | } 141 | 142 | conv_called = true; 143 | } 144 | 145 | if (convSlots_fp64) 146 | { 147 | pcm_samples = convert(convSlots_fp64, dsd_data, dsd_samples, pcm_data); 148 | } 149 | 150 | return pcm_samples; 151 | } 152 | 153 | DSDPCMConverterSlot* DSDPCMConverterEngine::init_slots(DSDPCMFilterSetup& fltSetup) 154 | { 155 | DSDPCMConverterSlot* convSlots = new DSDPCMConverterSlot[channels]; 156 | 157 | int dsd_samples = dsd_samplerate / 8 / framerate; 158 | int pcm_samples = pcm_samplerate / framerate; 159 | int decimation = dsd_samplerate / pcm_samplerate; 160 | 161 | for (int ch = 0; ch < channels; ch++) 162 | { 163 | DSDPCMConverterSlot* slot = &convSlots[ch]; 164 | slot->dsd_data = (uint8_t*)DSDPCMUtil::mem_alloc(dsd_samples * sizeof(uint8_t)); 165 | slot->dsd_samples = dsd_samples; 166 | slot->pcm_data = (double*)DSDPCMUtil::mem_alloc(pcm_samples * sizeof(double)); 167 | slot->pcm_samples = 0; 168 | 169 | DSDPCMConverterMultistage* pConv = nullptr; 170 | 171 | switch (decimation) 172 | { 173 | case 512: 174 | pConv = new DSDPCMConverterMultistage_x512(); 175 | break; 176 | case 256: 177 | pConv = new DSDPCMConverterMultistage_x256(); 178 | break; 179 | case 128: 180 | pConv = new DSDPCMConverterMultistage_x128(); 181 | break; 182 | case 64: 183 | pConv = new DSDPCMConverterMultistage_x64(); 184 | break; 185 | case 32: 186 | pConv = new DSDPCMConverterMultistage_x32(); 187 | break; 188 | case 16: 189 | pConv = new DSDPCMConverterMultistage_x16(); 190 | break; 191 | case 8: 192 | pConv = new DSDPCMConverterMultistage_x8(); 193 | break; 194 | } 195 | 196 | pConv->init(fltSetup, dsd_samples); 197 | slot->converter = pConv; 198 | 199 | pthread_mutex_init(&slot->hMutex, NULL); 200 | pthread_cond_init(&slot->hEventGet, NULL); 201 | pthread_cond_init(&slot->hEventPut, NULL); 202 | pthread_create(&slot->hThread, NULL, ConverterThread, slot); 203 | } 204 | 205 | return convSlots; 206 | } 207 | 208 | void DSDPCMConverterEngine::free_slots(DSDPCMConverterSlot* convSlots) 209 | { 210 | for (int ch = 0; ch < channels; ch++) 211 | { 212 | DSDPCMConverterSlot* slot = &convSlots[ch]; 213 | 214 | // Release worker (decoding) thread for exit 215 | pthread_mutex_lock(&slot->hMutex); 216 | slot->state = PCM_SLOT_TERMINATING; 217 | pthread_cond_signal(&slot->hEventPut); 218 | pthread_mutex_unlock(&slot->hMutex); 219 | 220 | // Wait until worker (decoding) thread exit 221 | pthread_join(slot->hThread, NULL); 222 | pthread_cond_destroy(&slot->hEventGet); 223 | pthread_cond_destroy(&slot->hEventPut); 224 | pthread_mutex_destroy(&slot->hMutex); 225 | 226 | delete slot->converter; 227 | slot->converter = nullptr; 228 | DSDPCMUtil::mem_free(slot->dsd_data); 229 | slot->dsd_data = nullptr; 230 | slot->dsd_samples = 0; 231 | DSDPCMUtil::mem_free(slot->pcm_data); 232 | slot->pcm_data = nullptr; 233 | slot->pcm_samples = 0; 234 | } 235 | 236 | delete[] convSlots; 237 | convSlots = nullptr; 238 | } 239 | 240 | int DSDPCMConverterEngine::convert(DSDPCMConverterSlot* convSlots, uint8_t* dsd_data, int dsd_samples, float* pcm_data) 241 | { 242 | int pcm_samples = 0; 243 | 244 | for (int ch = 0; ch < channels; ch++) 245 | { 246 | DSDPCMConverterSlot* slot = &convSlots[ch]; 247 | slot->dsd_samples = dsd_samples / channels; 248 | 249 | for (int sample = 0; sample < slot->dsd_samples; sample++) 250 | { 251 | slot->dsd_data[sample] = dsd_data[sample * channels + ch]; 252 | } 253 | 254 | // Release worker (decoding) thread on the loaded slot 255 | pthread_mutex_lock(&slot->hMutex); 256 | slot->state = PCM_SLOT_LOADED; 257 | pthread_cond_signal(&slot->hEventPut); 258 | pthread_mutex_unlock(&slot->hMutex); 259 | } 260 | 261 | for (int ch = 0; ch < channels; ch++) 262 | { 263 | DSDPCMConverterSlot* slot = &convSlots[ch]; 264 | 265 | // Wait until worker (decoding) thread is complete 266 | pthread_mutex_lock(&slot->hMutex); 267 | 268 | while (slot->state != PCM_SLOT_READY) 269 | { 270 | pthread_cond_wait(&slot->hEventGet, &slot->hMutex); 271 | } 272 | 273 | pthread_mutex_unlock(&slot->hMutex); 274 | 275 | for (int sample = 0; sample < slot->pcm_samples; sample++) 276 | { 277 | pcm_data[sample * channels + ch] = (float)slot->pcm_data[sample]; 278 | } 279 | 280 | pcm_samples += slot->pcm_samples; 281 | } 282 | 283 | return pcm_samples; 284 | } 285 | 286 | int DSDPCMConverterEngine::convertL(DSDPCMConverterSlot* convSlots, uint8_t* dsd_data, int dsd_samples) 287 | { 288 | for (int ch = 0; ch < channels; ch++) 289 | { 290 | DSDPCMConverterSlot* slot = &convSlots[ch]; 291 | 292 | slot->dsd_samples = dsd_samples / channels; 293 | 294 | for (int sample = 0; sample < slot->dsd_samples; sample++) 295 | { 296 | slot->dsd_data[sample] = swap_bits[dsd_data[(slot->dsd_samples - 1 - sample) * channels + ch]]; 297 | } 298 | 299 | // Release worker (decoding) thread on the loaded slot 300 | pthread_mutex_lock(&convSlots[ch].hMutex); 301 | convSlots[ch].state = PCM_SLOT_LOADED; 302 | pthread_cond_signal(&convSlots[ch].hEventPut); 303 | pthread_mutex_unlock(&convSlots[ch].hMutex); 304 | } 305 | 306 | for (int ch = 0; ch < channels; ch++) 307 | { 308 | DSDPCMConverterSlot* slot = &convSlots[ch]; 309 | 310 | // Wait until worker (decoding) thread is complete 311 | pthread_mutex_lock(&slot->hMutex); 312 | 313 | while (slot->state != PCM_SLOT_READY) 314 | { 315 | pthread_cond_wait(&slot->hEventGet, &slot->hMutex); 316 | } 317 | 318 | pthread_mutex_unlock(&slot->hMutex); 319 | } 320 | 321 | return 0; 322 | } 323 | 324 | int DSDPCMConverterEngine::convertR(DSDPCMConverterSlot* convSlots, float* pcm_data) 325 | { 326 | int pcm_samples = 0; 327 | 328 | for (int ch = 0; ch < channels; ch++) 329 | { 330 | DSDPCMConverterSlot* slot = &convSlots[ch]; 331 | 332 | for (int sample = 0; sample < slot->dsd_samples / 2; sample++) 333 | { 334 | uint8_t temp = slot->dsd_data[slot->dsd_samples - 1 - sample]; 335 | slot->dsd_data[slot->dsd_samples - 1 - sample] = swap_bits[slot->dsd_data[sample]]; 336 | slot->dsd_data[sample] = swap_bits[temp]; 337 | } 338 | 339 | // Release worker (decoding) thread on the loaded slot 340 | pthread_mutex_lock(&convSlots[ch].hMutex); 341 | convSlots[ch].state = PCM_SLOT_LOADED; 342 | pthread_cond_signal(&convSlots[ch].hEventPut); 343 | pthread_mutex_unlock(&convSlots[ch].hMutex); 344 | } 345 | 346 | for (int ch = 0; ch < channels; ch++) 347 | { 348 | DSDPCMConverterSlot* slot = &convSlots[ch]; 349 | 350 | // Wait until worker (decoding) thread is complete 351 | pthread_mutex_lock(&slot->hMutex); 352 | 353 | while (slot->state != PCM_SLOT_READY) 354 | { 355 | pthread_cond_wait(&slot->hEventGet, &slot->hMutex); 356 | } 357 | 358 | pthread_mutex_unlock(&slot->hMutex); 359 | 360 | for (int sample = 0; sample < slot->pcm_samples; sample++) 361 | { 362 | pcm_data[sample * channels + ch] = (float)slot->pcm_data[sample]; 363 | } 364 | 365 | pcm_samples += slot->pcm_samples; 366 | } 367 | 368 | return pcm_samples; 369 | } 370 | -------------------------------------------------------------------------------- /libdsd2pcm/dsd_pcm_converter_engine.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2016 Robert Tari 3 | Copyright (c) 2011-2015 Maxim V.Anisiutkin 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #pragma once 22 | 23 | #include 24 | #include "dsd_pcm_converter_multistage.h" 25 | 26 | enum pcm_slot_state_t {PCM_SLOT_EMPTY, PCM_SLOT_LOADED, PCM_SLOT_RUNNING, PCM_SLOT_READY, PCM_SLOT_TERMINATING}; 27 | 28 | class DSDPCMConverterSlot 29 | { 30 | public: 31 | 32 | uint8_t* dsd_data; 33 | int dsd_samples; 34 | double* pcm_data; 35 | int pcm_samples; 36 | DSDPCMConverter* converter; 37 | pthread_t hThread; 38 | pthread_cond_t hEventGet; 39 | pthread_cond_t hEventPut; 40 | pthread_mutex_t hMutex; 41 | volatile int state; 42 | 43 | DSDPCMConverterSlot() 44 | { 45 | state = PCM_SLOT_EMPTY; 46 | dsd_data = nullptr; 47 | dsd_samples = 0; 48 | pcm_data = nullptr; 49 | pcm_samples = 0; 50 | converter = nullptr; 51 | } 52 | }; 53 | 54 | class DSDPCMConverterEngine 55 | { 56 | 57 | public: 58 | 59 | DSDPCMConverterEngine(); 60 | ~DSDPCMConverterEngine(); 61 | float get_delay(); 62 | bool is_convert_called(); 63 | int init(int channels, int framerate, int dsd_samplerate, int pcm_samplerate); 64 | int free(); 65 | int convert(uint8_t* dsd_data, int dsd_samples, float* pcm_data); 66 | 67 | private: 68 | 69 | int channels; 70 | int framerate; 71 | int dsd_samplerate; 72 | int pcm_samplerate; 73 | float conv_delay; 74 | bool conv_fp64; 75 | bool conv_called; 76 | DSDPCMFilterSetup fltSetup_fp64; 77 | DSDPCMConverterSlot* convSlots_fp64; 78 | uint8_t swap_bits[256]; 79 | 80 | DSDPCMConverterSlot* init_slots(DSDPCMFilterSetup& fltSetup); 81 | void free_slots(DSDPCMConverterSlot* convSlots); 82 | int convert(DSDPCMConverterSlot* convSlots, uint8_t* dsd_data, int dsd_samples, float* pcm_data); 83 | int convertL(DSDPCMConverterSlot* convSlots, uint8_t* dsd_data, int dsd_samples); 84 | int convertR(DSDPCMConverterSlot* convSlots, float* pcm_data); 85 | }; 86 | -------------------------------------------------------------------------------- /libdsd2pcm/dsd_pcm_converter_hq.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-2016 Robert Tari 3 | Copyright 2012 Vladislav Goncharov (HQ DSD->PCM converter 88.2/96 kHz) 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include "dsd_pcm_converter_hq.h" 24 | 25 | dsdpcm_converter_hq::dsdpcm_converter_hq(): m_dither24(24) 26 | { 27 | unsigned int i, j; 28 | 29 | m_decimation = 0; 30 | m_upsampling = 0; 31 | m_nChannels = 0; 32 | m_nDsdSamplerate = 0; 33 | m_nPcmSamplerate = 0; 34 | conv_called = false; 35 | 36 | memset(m_resampler, 0, sizeof(m_resampler)); 37 | 38 | // fill bits_table 39 | for (i = 0; i < 16; i++) 40 | { 41 | for (j = 0; j < 4; j++) 42 | m_bits_table[i][j] = (i & (1 << (3 - j))) ? 1.0 : -1.0; 43 | } 44 | 45 | for (int i = 0; i < 256; i++) 46 | { 47 | swap_bits[i] = 0; 48 | 49 | for (int j = 0; j < 8; j++) 50 | { 51 | swap_bits[i] |= ((i >> j) & 1) << (7 - j); 52 | } 53 | } 54 | } 55 | 56 | dsdpcm_converter_hq::~dsdpcm_converter_hq() 57 | { 58 | int i; 59 | 60 | for (i = 0; i < DSDPCM_MAX_CHANNELS; i++) 61 | { 62 | delete m_resampler[i]; 63 | } 64 | } 65 | 66 | float dsdpcm_converter_hq::get_delay() 67 | { 68 | return (m_resampler[0] != NULL) ? (float)(m_resampler[0]->getFirSize() / 2) / (float)m_decimation : 0; 69 | } 70 | 71 | bool dsdpcm_converter_hq::is_convert_called() 72 | { 73 | return conv_called; 74 | } 75 | 76 | int dsdpcm_converter_hq::init(int channels, int dsd_samplerate, int pcm_samplerate) 77 | { 78 | double *impulse; 79 | int i, taps, sinc_freq, multiplier, divisor; 80 | 81 | this->m_nChannels = channels; 82 | this->m_nDsdSamplerate = dsd_samplerate; 83 | this->m_nPcmSamplerate = pcm_samplerate; 84 | 85 | switch (dsd_samplerate) 86 | { 87 | case DSDxFs64: 88 | { 89 | switch (pcm_samplerate) 90 | { 91 | case 96000: 92 | multiplier = 1; 93 | divisor = 1; 94 | break; 95 | case 192000: 96 | multiplier = 1; 97 | divisor = 2; 98 | break; 99 | default: 100 | return -2; 101 | } 102 | 103 | break; 104 | } 105 | case DSDxFs128: 106 | switch (pcm_samplerate) 107 | { 108 | case 96000: 109 | multiplier = 2; 110 | divisor = 1; 111 | break; 112 | case 192000: 113 | multiplier = 2; 114 | divisor = 2; 115 | break; 116 | default: 117 | return -2; 118 | } 119 | break; 120 | case DSDxFs256: 121 | switch (pcm_samplerate) 122 | { 123 | case 96000: 124 | multiplier = 4; 125 | divisor = 1; 126 | break; 127 | case 192000: 128 | multiplier = 4; 129 | divisor = 2; 130 | break; 131 | default: 132 | return -2; 133 | } 134 | break; 135 | case DSDxFs512: 136 | switch (pcm_samplerate) 137 | { 138 | case 96000: 139 | multiplier = 8; 140 | divisor = 1; 141 | break; 142 | case 192000: 143 | multiplier = 8; 144 | divisor = 2; 145 | break; 146 | default: 147 | return -2; 148 | } 149 | break; 150 | default: 151 | return -1; 152 | break; 153 | } 154 | 155 | // prepare filter 156 | for (i = 0; i < DSDPCM_MAX_CHANNELS; i++) 157 | { 158 | delete m_resampler[i]; 159 | } 160 | 161 | memset(m_resampler, 0, sizeof(m_resampler)); 162 | 163 | // default resampling mode DSD64 -> 96 (5/147 resampling) 164 | // actual resampling mode DSD64 * multiplier -> 96 * divisor (5 * divisor / 147 * multiplier) 165 | m_upsampling = 5 * divisor; 166 | m_decimation = 147 * multiplier; 167 | 168 | // generate filter 169 | taps = 2878 * divisor * multiplier + 1; 170 | sinc_freq = 70 * 5 * divisor * multiplier; // 44.1 * 64 * upsampling / x 171 | impulse = new double[taps]; 172 | 173 | generateFilter(impulse, taps, sinc_freq); 174 | 175 | for (i = 0; i < channels; i++) 176 | m_resampler[i] = new ResamplerNxMx(m_upsampling, m_decimation, impulse, taps); 177 | 178 | delete[] impulse; 179 | 180 | conv_called = false; 181 | 182 | return 0; 183 | } 184 | 185 | int dsdpcm_converter_hq::convert(uint8_t* dsd_data, int dsd_samples, float* pcm_data) 186 | { 187 | int pcm_samples = 0; 188 | 189 | if (!dsd_data) 190 | { 191 | for (int sample = 0; sample < dsd_samples / 2; sample++) 192 | { 193 | uint8_t temp = dsd_data[dsd_samples - 1 - sample]; 194 | dsd_data[dsd_samples - 1 - sample] = swap_bits[dsd_data[sample]]; 195 | dsd_data[sample] = swap_bits[temp]; 196 | } 197 | 198 | return convertResample(dsd_data, dsd_samples, pcm_data); 199 | } 200 | 201 | if (!conv_called) 202 | { 203 | for (int sample = 0; sample < dsd_samples; sample++) 204 | { 205 | dsd_data[sample] = swap_bits[dsd_data[dsd_samples - 1 - sample]]; 206 | } 207 | 208 | convertResample(dsd_data, dsd_samples, pcm_data); 209 | 210 | conv_called = true; 211 | } 212 | 213 | pcm_samples = convertResample(dsd_data, dsd_samples, pcm_data); 214 | 215 | return pcm_samples; 216 | } 217 | 218 | int dsdpcm_converter_hq::convertResample(uint8_t* dsd_data, int dsd_samples, float* pcm_data) 219 | { 220 | if((dsd_samples % m_decimation) != 0) 221 | { 222 | return -1; 223 | } 224 | 225 | int i, pcm_samples, ch, offset, dsd_offset, pcm_offset, j; 226 | double dsd_input[DSDPCM_MAX_CHANNELS][MAX_RESAMPLING_IN + 8], x[DSDPCM_MAX_CHANNELS][MAX_RESAMPLING_OUT]; 227 | uint8_t dsd8bits; 228 | unsigned int x_samples = 1; 229 | 230 | assert((dsd_samples % m_decimation) == 0); 231 | 232 | pcm_samples = (dsd_samples * 8) / m_decimation / m_nChannels * m_upsampling; 233 | dsd_offset = 0; 234 | pcm_offset = 0; 235 | offset = 0; // offset in dsd_input 236 | 237 | // all PCM samples 238 | for (i = 0; i < pcm_samples; i += x_samples) 239 | { 240 | // fill decimation buffer for downsampling 241 | for (; offset < m_decimation; offset += 8) 242 | { 243 | // has padding of 8 elements 244 | // all channels 245 | for (ch = 0; ch < m_nChannels; ch++) 246 | { 247 | dsd8bits = dsd_data[dsd_offset++]; 248 | 249 | // fastfill doubles from bits 250 | memcpy(&dsd_input[ch][offset], m_bits_table[(dsd8bits & 0xf0) >> 4], 4 * sizeof(double)); 251 | memcpy(&dsd_input[ch][offset + 4], m_bits_table[dsd8bits & 0x0f], 4 * sizeof(double)); 252 | } 253 | } 254 | 255 | // now fill pcm samples in all channels!!! 256 | for (ch = 0; ch < m_nChannels; ch++) 257 | { 258 | m_resampler[ch]->processSample(dsd_input[ch], m_decimation, x[ch], &x_samples); 259 | 260 | // shift overfill in channel 261 | memcpy(&dsd_input[ch][0], &dsd_input[ch][m_decimation], (offset - m_decimation) * sizeof(double)); 262 | } 263 | 264 | // shift offset 265 | offset -= m_decimation; 266 | 267 | // and output interleaving samples 268 | for (j = 0; j < (int)x_samples; j++) 269 | { 270 | for (ch = 0; ch < m_nChannels; ch++) 271 | { 272 | // interleave 273 | pcm_data[pcm_offset++] = (float)m_dither24.processSample(x[ch][j]); 274 | } 275 | } 276 | } 277 | 278 | assert(dsd_offset == dsd_samples); 279 | assert(pcm_offset == pcm_samples * m_nChannels); 280 | 281 | conv_called = true; 282 | 283 | return pcm_offset; 284 | } 285 | -------------------------------------------------------------------------------- /libdsd2pcm/dsd_pcm_converter_hq.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-2016 Robert Tari 3 | Copyright 2012 Vladislav Goncharov (HQ DSD->PCM converter 88.2/96 kHz) 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #ifndef _dsdpcm_converter_hq_h_ 22 | #define _dsdpcm_converter_hq_h_ 23 | 24 | #include 25 | #include 26 | #include "upsampler.h" 27 | 28 | #define DSDxFs1 (44100 * 1) 29 | #define DSDxFs2 (44100 * 2) 30 | #define DSDxFs4 (44100 * 4) 31 | #define DSDxFs8 (44100 * 8) 32 | #define DSDxFs64 (44100 * 64) 33 | #define DSDxFs128 (44100 * 128) 34 | #define DSDxFs256 (44100 * 256) 35 | #define DSDxFs512 (44100 * 512) 36 | #define DSDPCM_MAX_CHANNELS 6 37 | 38 | typedef uint8_t dsd_sample_t[DSDPCM_MAX_CHANNELS]; 39 | 40 | class dsdpcm_converter_hq 41 | { 42 | public: 43 | 44 | dsdpcm_converter_hq(); 45 | ~dsdpcm_converter_hq(); 46 | int init(int channels, int dsd_samplerate, int pcm_samplerate); 47 | int convert(uint8_t* dsd_data, int dsd_samples, float* pcm_data); 48 | float get_delay(); 49 | bool is_convert_called(); 50 | 51 | private: 52 | 53 | int m_decimation; 54 | int m_upsampling; 55 | int m_nChannels; 56 | int m_nDsdSamplerate; 57 | int m_nPcmSamplerate; 58 | bool conv_called; 59 | static const int MAX_DECIMATION = 32 * 2; // 64x -> 88.2 (44.1 not supported, 128x not supported) 60 | static const int MAX_RESAMPLING_IN = 147 * 2; // 64x -> 96 (147 -> 5 for 64x -> 96, 128x not supported) 61 | static const int MAX_RESAMPLING_OUT = 5 * 2; // 147 -> 5 for 64x -> 96 62 | ResamplerNxMx *m_resampler[DSDPCM_MAX_CHANNELS]; 63 | Dither m_dither24; 64 | double m_bits_table[16][4]; 65 | uint8_t swap_bits[256]; 66 | int convertResample(uint8_t* dsd_data, int dsd_samples, float* pcm_data); 67 | }; 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /libdsd2pcm/dsd_pcm_converter_multistage.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2016 Robert Tari 3 | Copyright (c) 2011-2015 Maxim V.Anisiutkin 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #pragma once 22 | 23 | #include "dsd_pcm_converter.h" 24 | 25 | class DSDPCMConverterMultistage : public DSDPCMConverter 26 | { 27 | }; 28 | 29 | class DSDPCMConverterMultistage_x512 : public DSDPCMConverterMultistage 30 | { 31 | DSDPCMFir dsd_fir1; 32 | PCMPCMFir pcm_fir2a; 33 | PCMPCMFir pcm_fir2b; 34 | PCMPCMFir pcm_fir2c; 35 | PCMPCMFir pcm_fir2d; 36 | PCMPCMFir pcm_fir3; 37 | 38 | public: 39 | 40 | void init(DSDPCMFilterSetup& flt_setup, int dsd_samples) 41 | { 42 | alloc_pcm_temp1(dsd_samples / 2); 43 | alloc_pcm_temp2(dsd_samples / 4); 44 | dsd_fir1.init(flt_setup.get_fir1_16_ctables(), flt_setup.get_fir1_16_length(), 16); 45 | pcm_fir2a.init(flt_setup.get_fir2_2_coefs(), flt_setup.get_fir2_2_length(), 2); 46 | pcm_fir2b.init(flt_setup.get_fir2_2_coefs(), flt_setup.get_fir2_2_length(), 2); 47 | pcm_fir2c.init(flt_setup.get_fir2_2_coefs(), flt_setup.get_fir2_2_length(), 2); 48 | pcm_fir2d.init(flt_setup.get_fir2_2_coefs(), flt_setup.get_fir2_2_length(), 2); 49 | pcm_fir3.init(flt_setup.get_fir3_2_coefs(), flt_setup.get_fir3_2_length(), 2); 50 | delay = (((dsd_fir1.get_delay() / pcm_fir2a.get_decimation() + pcm_fir2a.get_delay()) / pcm_fir2b.get_decimation() + pcm_fir2b.get_delay()) / pcm_fir2c.get_decimation() + pcm_fir2c.get_delay()) / pcm_fir3.get_decimation() + pcm_fir3.get_delay(); 51 | } 52 | 53 | int convert(uint8_t* dsd_data, double* pcm_data, int dsd_samples) 54 | { 55 | int pcm_samples; 56 | pcm_samples = dsd_fir1.run(dsd_data, pcm_temp1, dsd_samples); 57 | pcm_samples = pcm_fir2a.run(pcm_temp1, pcm_temp2, pcm_samples); 58 | pcm_samples = pcm_fir2b.run(pcm_temp2, pcm_temp1, pcm_samples); 59 | pcm_samples = pcm_fir2c.run(pcm_temp1, pcm_temp2, pcm_samples); 60 | pcm_samples = pcm_fir2d.run(pcm_temp2, pcm_temp1, pcm_samples); 61 | pcm_samples = pcm_fir3.run(pcm_temp1, pcm_data, pcm_samples); 62 | 63 | return pcm_samples; 64 | } 65 | }; 66 | 67 | class DSDPCMConverterMultistage_x256 : public DSDPCMConverterMultistage 68 | { 69 | DSDPCMFir dsd_fir1; 70 | PCMPCMFir pcm_fir2a; 71 | PCMPCMFir pcm_fir2b; 72 | PCMPCMFir pcm_fir2c; 73 | PCMPCMFir pcm_fir3; 74 | 75 | public: 76 | 77 | void init(DSDPCMFilterSetup& flt_setup, int dsd_samples) 78 | { 79 | alloc_pcm_temp1(dsd_samples / 2); 80 | alloc_pcm_temp2(dsd_samples / 4); 81 | dsd_fir1.init(flt_setup.get_fir1_16_ctables(), flt_setup.get_fir1_16_length(), 16); 82 | pcm_fir2a.init(flt_setup.get_fir2_2_coefs(), flt_setup.get_fir2_2_length(), 2); 83 | pcm_fir2b.init(flt_setup.get_fir2_2_coefs(), flt_setup.get_fir2_2_length(), 2); 84 | pcm_fir2c.init(flt_setup.get_fir2_2_coefs(), flt_setup.get_fir2_2_length(), 2); 85 | pcm_fir3.init(flt_setup.get_fir3_2_coefs(), flt_setup.get_fir3_2_length(), 2); 86 | delay = (((dsd_fir1.get_delay() / pcm_fir2a.get_decimation() + pcm_fir2a.get_delay()) / pcm_fir2b.get_decimation() + pcm_fir2b.get_delay()) / pcm_fir2c.get_decimation() + pcm_fir2c.get_delay()) / pcm_fir3.get_decimation() + pcm_fir3.get_delay(); 87 | } 88 | 89 | int convert(uint8_t* dsd_data, double* pcm_data, int dsd_samples) 90 | { 91 | int pcm_samples; 92 | pcm_samples = dsd_fir1.run(dsd_data, pcm_temp1, dsd_samples); 93 | pcm_samples = pcm_fir2a.run(pcm_temp1, pcm_temp2, pcm_samples); 94 | pcm_samples = pcm_fir2b.run(pcm_temp2, pcm_temp1, pcm_samples); 95 | pcm_samples = pcm_fir2c.run(pcm_temp1, pcm_temp2, pcm_samples); 96 | pcm_samples = pcm_fir3.run(pcm_temp2, pcm_data, pcm_samples); 97 | 98 | return pcm_samples; 99 | } 100 | }; 101 | 102 | class DSDPCMConverterMultistage_x128 : public DSDPCMConverterMultistage 103 | { 104 | DSDPCMFir dsd_fir1; 105 | PCMPCMFir pcm_fir2a; 106 | PCMPCMFir pcm_fir2b; 107 | PCMPCMFir pcm_fir3; 108 | 109 | public: 110 | 111 | void init(DSDPCMFilterSetup& flt_setup, int dsd_samples) 112 | { 113 | alloc_pcm_temp1(dsd_samples / 2); 114 | alloc_pcm_temp2(dsd_samples / 4); 115 | dsd_fir1.init(flt_setup.get_fir1_16_ctables(), flt_setup.get_fir1_16_length(), 16); 116 | pcm_fir2a.init(flt_setup.get_fir2_2_coefs(), flt_setup.get_fir2_2_length(), 2); 117 | pcm_fir2b.init(flt_setup.get_fir2_2_coefs(), flt_setup.get_fir2_2_length(), 2); 118 | pcm_fir3.init(flt_setup.get_fir3_2_coefs(), flt_setup.get_fir3_2_length(), 2); 119 | delay = ((dsd_fir1.get_delay() / pcm_fir2a.get_decimation() + pcm_fir2a.get_delay()) / pcm_fir2b.get_decimation() + pcm_fir2b.get_delay()) / pcm_fir3.get_decimation() + pcm_fir3.get_delay(); 120 | } 121 | 122 | int convert(uint8_t* dsd_data, double* pcm_data, int dsd_samples) 123 | { 124 | int pcm_samples; 125 | pcm_samples = dsd_fir1.run(dsd_data, pcm_temp1, dsd_samples); 126 | pcm_samples = pcm_fir2a.run(pcm_temp1, pcm_temp2, pcm_samples); 127 | pcm_samples = pcm_fir2b.run(pcm_temp2, pcm_temp1, pcm_samples); 128 | pcm_samples = pcm_fir3.run(pcm_temp1, pcm_data, pcm_samples); 129 | 130 | return pcm_samples; 131 | } 132 | }; 133 | 134 | class DSDPCMConverterMultistage_x64 : public DSDPCMConverterMultistage 135 | { 136 | DSDPCMFir dsd_fir1; 137 | PCMPCMFir pcm_fir2a; 138 | PCMPCMFir pcm_fir3; 139 | 140 | public: 141 | 142 | void init(DSDPCMFilterSetup& flt_setup, int dsd_samples) 143 | { 144 | alloc_pcm_temp1(dsd_samples / 2); 145 | alloc_pcm_temp2(dsd_samples / 4); 146 | dsd_fir1.init(flt_setup.get_fir1_16_ctables(), flt_setup.get_fir1_16_length(), 16); 147 | pcm_fir2a.init(flt_setup.get_fir2_2_coefs(), flt_setup.get_fir2_2_length(), 2); 148 | pcm_fir3.init(flt_setup.get_fir3_2_coefs(), flt_setup.get_fir3_2_length(), 2); 149 | delay = (dsd_fir1.get_delay() / pcm_fir2a.get_decimation() + pcm_fir2a.get_delay()) / pcm_fir3.get_decimation() + pcm_fir3.get_delay(); 150 | } 151 | 152 | int convert(uint8_t* dsd_data, double* pcm_data, int dsd_samples) 153 | { 154 | int pcm_samples; 155 | pcm_samples = dsd_fir1.run(dsd_data, pcm_temp1, dsd_samples); 156 | pcm_samples = pcm_fir2a.run(pcm_temp1, pcm_temp2, pcm_samples); 157 | pcm_samples = pcm_fir3.run(pcm_temp2, pcm_data, pcm_samples); 158 | 159 | return pcm_samples; 160 | } 161 | }; 162 | 163 | class DSDPCMConverterMultistage_x32 : public DSDPCMConverterMultistage 164 | { 165 | DSDPCMFir dsd_fir1; 166 | PCMPCMFir pcm_fir2a; 167 | PCMPCMFir pcm_fir3; 168 | 169 | public: 170 | 171 | void init(DSDPCMFilterSetup& flt_setup, int dsd_samples) 172 | { 173 | alloc_pcm_temp1(dsd_samples); 174 | alloc_pcm_temp2(dsd_samples / 2); 175 | dsd_fir1.init(flt_setup.get_fir1_8_ctables(), flt_setup.get_fir1_8_length(), 8); 176 | pcm_fir2a.init(flt_setup.get_fir2_2_coefs(), flt_setup.get_fir2_2_length(), 2); 177 | pcm_fir3.init(flt_setup.get_fir3_2_coefs(), flt_setup.get_fir3_2_length(), 2); 178 | delay = (dsd_fir1.get_delay() / pcm_fir2a.get_decimation() + pcm_fir2a.get_delay()) / pcm_fir3.get_decimation() + pcm_fir3.get_delay(); 179 | } 180 | 181 | int convert(uint8_t* dsd_data, double* pcm_data, int dsd_samples) 182 | { 183 | int pcm_samples; 184 | pcm_samples = dsd_fir1.run(dsd_data, pcm_temp1, dsd_samples); 185 | pcm_samples = pcm_fir2a.run(pcm_temp1, pcm_temp2, pcm_samples); 186 | pcm_samples = pcm_fir3.run(pcm_temp2, pcm_data, pcm_samples); 187 | 188 | return pcm_samples; 189 | } 190 | }; 191 | 192 | class DSDPCMConverterMultistage_x16 : public DSDPCMConverterMultistage 193 | { 194 | DSDPCMFir dsd_fir1; 195 | PCMPCMFir pcm_fir3; 196 | 197 | public: 198 | 199 | void init(DSDPCMFilterSetup& flt_setup, int dsd_samples) 200 | { 201 | alloc_pcm_temp1(dsd_samples); 202 | dsd_fir1.init(flt_setup.get_fir1_8_ctables(), flt_setup.get_fir1_8_length(), 8); 203 | pcm_fir3.init(flt_setup.get_fir3_2_coefs(), flt_setup.get_fir3_2_length(), 2); 204 | delay = dsd_fir1.get_delay() / pcm_fir3.get_decimation() + pcm_fir3.get_delay(); 205 | } 206 | 207 | int convert(uint8_t* dsd_data, double* pcm_data, int dsd_samples) 208 | { 209 | int pcm_samples; 210 | pcm_samples = dsd_fir1.run(dsd_data, pcm_temp1, dsd_samples); 211 | pcm_samples = pcm_fir3.run(pcm_temp1, pcm_data, pcm_samples); 212 | 213 | return pcm_samples; 214 | } 215 | }; 216 | 217 | class DSDPCMConverterMultistage_x8 : public DSDPCMConverterMultistage 218 | { 219 | DSDPCMFir dsd_fir1; 220 | 221 | public: 222 | 223 | void init(DSDPCMFilterSetup& flt_setup, int dsd_samples) 224 | { 225 | dsd_fir1.init(flt_setup.get_fir1_8_ctables(), flt_setup.get_fir1_8_length(), 8); 226 | delay = dsd_fir1.get_delay(); 227 | } 228 | 229 | int convert(uint8_t* dsd_data, double* pcm_data, int dsd_samples) 230 | { 231 | int pcm_samples; 232 | pcm_samples = dsd_fir1.run(dsd_data, pcm_data, dsd_samples); 233 | 234 | return pcm_samples; 235 | } 236 | }; 237 | -------------------------------------------------------------------------------- /libdsd2pcm/dsd_pcm_filter_setup.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2016 Robert Tari 3 | Copyright (c) 2011-2015 Maxim V.Anisiutkin 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #pragma once 22 | 23 | #include "dsd_pcm_constants.h" 24 | #include "dsd_pcm_util.h" 25 | 26 | class DSDPCMFilterSetup 27 | { 28 | using ctable_t = double[256]; 29 | ctable_t* dsd_fir1_8_ctables; 30 | ctable_t* dsd_fir1_16_ctables; 31 | ctable_t* dsd_fir1_64_ctables; 32 | double* pcm_fir2_2_coefs; 33 | double* pcm_fir3_2_coefs; 34 | 35 | public: 36 | 37 | DSDPCMFilterSetup() 38 | { 39 | dsd_fir1_8_ctables = nullptr; 40 | dsd_fir1_16_ctables = nullptr; 41 | dsd_fir1_64_ctables = nullptr; 42 | pcm_fir2_2_coefs = nullptr; 43 | pcm_fir3_2_coefs = nullptr; 44 | } 45 | 46 | ~DSDPCMFilterSetup() 47 | { 48 | 49 | DSDPCMUtil::mem_free(pcm_fir2_2_coefs); 50 | DSDPCMUtil::mem_free(pcm_fir3_2_coefs); 51 | } 52 | 53 | static const double NORM_I(const int scale = 0) 54 | { 55 | return (double)1 / (double)((unsigned int)1 << (31 - scale)); 56 | } 57 | 58 | ctable_t* get_fir1_8_ctables() 59 | { 60 | if (!dsd_fir1_8_ctables) 61 | { 62 | dsd_fir1_8_ctables = (ctable_t*)DSDPCMUtil::mem_alloc(CTABLES(DSDFIR1_8_LENGTH) * sizeof(ctable_t)); 63 | set_ctables(DSDFIR1_8_COEFS, DSDFIR1_8_LENGTH, NORM_I(3), dsd_fir1_8_ctables); 64 | } 65 | 66 | return dsd_fir1_8_ctables; 67 | } 68 | 69 | int get_fir1_8_length() 70 | { 71 | return DSDFIR1_8_LENGTH; 72 | } 73 | 74 | ctable_t* get_fir1_16_ctables() 75 | { 76 | if (!dsd_fir1_16_ctables) 77 | { 78 | dsd_fir1_16_ctables = (ctable_t*)DSDPCMUtil::mem_alloc(CTABLES(DSDFIR1_16_LENGTH) * sizeof(ctable_t)); 79 | set_ctables(DSDFIR1_16_COEFS, DSDFIR1_16_LENGTH, NORM_I(3), dsd_fir1_16_ctables); 80 | } 81 | 82 | return dsd_fir1_16_ctables; 83 | } 84 | 85 | int get_fir1_16_length() 86 | { 87 | return DSDFIR1_16_LENGTH; 88 | } 89 | 90 | ctable_t* get_fir1_64_ctables() 91 | { 92 | if (!dsd_fir1_64_ctables) 93 | { 94 | dsd_fir1_64_ctables = (ctable_t*)DSDPCMUtil::mem_alloc(CTABLES(DSDFIR1_64_LENGTH) * sizeof(ctable_t)); 95 | set_ctables(DSDFIR1_64_COEFS, DSDFIR1_64_LENGTH, NORM_I(), dsd_fir1_64_ctables); 96 | } 97 | 98 | return dsd_fir1_64_ctables; 99 | } 100 | 101 | 102 | int get_fir1_64_length() 103 | { 104 | return DSDFIR1_64_LENGTH; 105 | } 106 | 107 | double* get_fir2_2_coefs() 108 | { 109 | if (!pcm_fir2_2_coefs) 110 | { 111 | pcm_fir2_2_coefs = (double*)DSDPCMUtil::mem_alloc(PCMFIR2_2_LENGTH * sizeof(double)); 112 | set_coefs(PCMFIR2_2_COEFS, PCMFIR2_2_LENGTH, NORM_I(), pcm_fir2_2_coefs); 113 | } 114 | 115 | return pcm_fir2_2_coefs; 116 | } 117 | 118 | int get_fir2_2_length() 119 | { 120 | return PCMFIR2_2_LENGTH; 121 | } 122 | 123 | double* get_fir3_2_coefs() 124 | { 125 | if (!pcm_fir3_2_coefs) 126 | { 127 | pcm_fir3_2_coefs = (double*)DSDPCMUtil::mem_alloc(PCMFIR3_2_LENGTH * sizeof(double)); 128 | set_coefs(PCMFIR3_2_COEFS, PCMFIR3_2_LENGTH, NORM_I(), pcm_fir3_2_coefs); 129 | } 130 | 131 | return pcm_fir3_2_coefs; 132 | } 133 | 134 | int get_fir3_2_length() 135 | { 136 | return PCMFIR3_2_LENGTH; 137 | } 138 | 139 | private: 140 | 141 | int set_ctables(const double* fir_coefs, const int fir_length, const double fir_gain, ctable_t* out_ctables) 142 | { 143 | int ctables = CTABLES(fir_length); 144 | 145 | for (int ct = 0; ct < ctables; ct++) 146 | { 147 | int k = fir_length - ct * 8; 148 | 149 | if (k > 8) 150 | { 151 | k = 8; 152 | } 153 | 154 | if (k < 0) 155 | { 156 | k = 0; 157 | } 158 | 159 | for (int i = 0; i < 256; i++) 160 | { 161 | double cvalue = 0.0; 162 | 163 | for (int j = 0; j < k; j++) 164 | { 165 | cvalue += (((i >> (7 - j)) & 1) * 2 - 1) * fir_coefs[fir_length - 1 - (ct * 8 + j)]; 166 | } 167 | 168 | out_ctables[ct][i] = (double)(cvalue * fir_gain); 169 | } 170 | } 171 | 172 | return ctables; 173 | } 174 | 175 | void set_coefs(const double* fir_coefs, const int fir_length, const double fir_gain, double* out_coefs) 176 | { 177 | for (int i = 0; i < fir_length; i++) 178 | { 179 | out_coefs[i] = (double)(fir_coefs[fir_length - 1 - i] * fir_gain); 180 | } 181 | } 182 | }; 183 | -------------------------------------------------------------------------------- /libdsd2pcm/dsd_pcm_fir.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2016 Robert Tari 3 | Copyright (c) 2011-2015 Maxim V.Anisiutkin 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #pragma once 22 | 23 | class DSDPCMFir 24 | { 25 | using ctable_t = double[256]; 26 | ctable_t* fir_ctables; 27 | int fir_order; 28 | int fir_length; 29 | int decimation; 30 | uint8_t* fir_buffer; 31 | int fir_index; 32 | 33 | public: 34 | 35 | DSDPCMFir() 36 | { 37 | fir_ctables = nullptr; 38 | fir_order = 0; 39 | fir_length = 0; 40 | decimation = 0; 41 | fir_buffer = nullptr; 42 | fir_index = 0; 43 | } 44 | 45 | ~DSDPCMFir() 46 | { 47 | free(); 48 | } 49 | 50 | void init(ctable_t* fir_ctables, int fir_length, int decimation) 51 | { 52 | this->fir_ctables = fir_ctables; 53 | this->fir_order = fir_length - 1; 54 | this->fir_length = CTABLES(fir_length); 55 | this->decimation = decimation / 8; 56 | int buf_size = 2 * this->fir_length * sizeof(uint8_t); 57 | this->fir_buffer = (uint8_t*)DSDPCMUtil::mem_alloc(buf_size); 58 | memset(this->fir_buffer, DSD_SILENCE_BYTE, buf_size); 59 | fir_index = 0; 60 | } 61 | 62 | void free() 63 | { 64 | if (fir_buffer) 65 | { 66 | DSDPCMUtil::mem_free(fir_buffer); 67 | fir_buffer = nullptr; 68 | } 69 | } 70 | 71 | int get_decimation() { 72 | return decimation; 73 | } 74 | 75 | float get_delay() 76 | { 77 | return (float)fir_order / 2 / 8 / decimation; 78 | } 79 | 80 | int run(uint8_t* dsd_data, double* pcm_data, int dsd_samples) 81 | { 82 | int pcm_samples = dsd_samples / decimation; 83 | 84 | for (int sample = 0; sample < pcm_samples; sample++) 85 | { 86 | for (int i = 0; i < decimation; i++) 87 | { 88 | fir_buffer[fir_index + fir_length] = fir_buffer[fir_index] = *(dsd_data++); 89 | fir_index = fir_index + 1; 90 | fir_index = fir_index % fir_length; 91 | } 92 | 93 | pcm_data[sample] = (double)0; 94 | 95 | for (int j = 0; j < fir_length; j++) 96 | { 97 | pcm_data[sample] += fir_ctables[j][fir_buffer[fir_index + j]]; 98 | } 99 | } 100 | 101 | return pcm_samples; 102 | } 103 | }; 104 | -------------------------------------------------------------------------------- /libdsd2pcm/dsd_pcm_util.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2019 Robert Tari 3 | Copyright (c) 2011-2015 Maxim V.Anisiutkin 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #pragma once 22 | 23 | #include 24 | #include 25 | #ifndef _ISOC11_SOURCE 26 | #include 27 | #endif 28 | 29 | class DSDPCMUtil 30 | { 31 | 32 | public: 33 | 34 | static void* mem_alloc(size_t size) 35 | { 36 | size_t sizemem = (size_t)MEM_ALIGN * (size_t)((size + MEM_ALIGN - 1) / MEM_ALIGN); 37 | #ifdef _ISOC11_SOURCE 38 | void* memory = aligned_alloc(MEM_ALIGN, sizemem); 39 | #else 40 | void* memory = memalign(MEM_ALIGN, sizemem); 41 | #endif 42 | 43 | if (memory) 44 | { 45 | memset(memory, 0, size); 46 | } 47 | 48 | return memory; 49 | } 50 | 51 | static void mem_free(void* memory) 52 | { 53 | if (memory) 54 | { 55 | free(memory); 56 | } 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /libdsd2pcm/pcm_pcm_fir.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2016 Robert Tari 3 | Copyright (c) 2011-2015 Maxim V.Anisiutkin 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #pragma once 22 | 23 | class PCMPCMFir 24 | { 25 | double* fir_coefs; 26 | int fir_order; 27 | int fir_length; 28 | int decimation; 29 | double* fir_buffer; 30 | int fir_index; 31 | 32 | public: 33 | 34 | PCMPCMFir() 35 | { 36 | fir_coefs = nullptr; 37 | fir_order = 0; 38 | fir_length = 0; 39 | decimation = 0; 40 | fir_buffer = nullptr; 41 | fir_index = 0; 42 | } 43 | 44 | ~PCMPCMFir() 45 | { 46 | free(); 47 | } 48 | 49 | void init(double* fir_coefs, int fir_length, int decimation) 50 | { 51 | this->fir_coefs = fir_coefs; 52 | this->fir_order = fir_length - 1; 53 | this->fir_length = fir_length; 54 | this->decimation = decimation; 55 | int buf_size = 2 * this->fir_length * sizeof(double); 56 | this->fir_buffer = (double*)DSDPCMUtil::mem_alloc(buf_size); 57 | memset(this->fir_buffer, 0, buf_size); 58 | fir_index = 0; 59 | } 60 | 61 | void free() 62 | { 63 | if (fir_buffer) 64 | { 65 | DSDPCMUtil::mem_free(fir_buffer); 66 | fir_buffer = nullptr; 67 | } 68 | } 69 | 70 | int get_decimation() 71 | { 72 | return decimation; 73 | } 74 | 75 | float get_delay() 76 | { 77 | return (float)fir_order / 2 / decimation; 78 | } 79 | 80 | int run(double* pcm_data, double* out_data, int pcm_samples) 81 | { 82 | int out_samples = pcm_samples / decimation; 83 | 84 | for (int sample = 0; sample < out_samples; sample++) 85 | { 86 | for (int i = 0; i < decimation; i++) 87 | { 88 | fir_buffer[fir_index + fir_length] = fir_buffer[fir_index] = *(pcm_data++); 89 | fir_index = fir_index + 1; 90 | fir_index = fir_index % fir_length; 91 | } 92 | 93 | out_data[sample] = (double)0; 94 | 95 | for (int j = 0; j < fir_length; j++) 96 | { 97 | out_data[sample] += fir_coefs[j] * fir_buffer[fir_index + j]; 98 | } 99 | } 100 | 101 | return out_samples; 102 | } 103 | }; 104 | -------------------------------------------------------------------------------- /libdsd2pcm/upsampler.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-2016 Robert Tari 3 | Copyright 2012 Vladislav Goncharov 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #include // SSE2 inlines 22 | #include 23 | #include 24 | #include 25 | #include "upsampler.h" 26 | 27 | // FirHistory 28 | FirHistory::FirHistory(unsigned int fir_size) 29 | { 30 | m_fir_size = fir_size; 31 | 32 | if (m_fir_size > 0) 33 | { 34 | // init history buffer 35 | m_x = new double[m_fir_size * 2 + 10]; // leave space for FIR pad (8 SSE block align + 2 memory align) 36 | m_head = m_fir_size; 37 | 38 | memset(m_x, 0, (m_fir_size * 2 + 10) * sizeof(double)); 39 | 40 | } 41 | else 42 | { 43 | m_x = NULL; 44 | m_head = 0; 45 | } 46 | } 47 | 48 | FirHistory::~FirHistory() 49 | { 50 | delete[] m_x; 51 | } 52 | 53 | FirHistory& FirHistory::operator=(const FirHistory &obj) 54 | { 55 | delete[] m_x; 56 | 57 | m_fir_size = obj.m_fir_size; 58 | 59 | if (m_fir_size > 0) 60 | { 61 | // init history buffer 62 | m_x = new double[m_fir_size * 2 + 10]; // leave space for FIR pad (8 SSE block align + 2 memory align) 63 | m_head = m_fir_size; 64 | 65 | memset(m_x, 0, (m_fir_size * 2 + 10) * sizeof(double)); 66 | 67 | } 68 | else 69 | { 70 | m_x = NULL; 71 | m_head = 0; 72 | } 73 | 74 | return *this; 75 | } 76 | 77 | void FirHistory::pushSample(double x) 78 | { 79 | // push to head 80 | if (m_head == 0) 81 | { 82 | // shift 83 | memcpy(m_x + m_fir_size, m_x, m_fir_size * sizeof(double)); 84 | 85 | m_head = m_fir_size; 86 | } 87 | 88 | m_x[--m_head] = x; 89 | } 90 | 91 | void FirHistory::reset(bool reset_to_1) 92 | { 93 | unsigned int i; 94 | 95 | m_head = m_fir_size; 96 | 97 | if (!reset_to_1) 98 | { 99 | memset(m_x, 0, m_fir_size * 2 * sizeof(double)); 100 | 101 | } 102 | else 103 | { 104 | for (i = 0; i < m_fir_size * 2; i++) 105 | m_x[i] = 1.0; 106 | } 107 | } 108 | 109 | // FirFilter 110 | FirFilter::FirFilter(const double *fir, unsigned int fir_size, bool no_history) : m_x(!no_history ? fir_size : 0) 111 | { 112 | unsigned int i; 113 | 114 | m_org_fir_size = fir_size; 115 | 116 | if ((fir_size % 8) != 0) 117 | m_fir_size = (fir_size / 8 + 1) * 8; // align size! 118 | else 119 | m_fir_size = m_org_fir_size; // already aligned 120 | 121 | m_fir_alloc = new double[m_fir_size + 2]; // reserve some space for pointer align 122 | 123 | // align pointer! 124 | m_fir = (((size_t)m_fir_alloc & 0x0f) == 0) ? m_fir_alloc : (double *)(((size_t)m_fir_alloc & ~0x0f) + 0x10); 125 | 126 | for (i = 0; i < m_fir_size; i++) 127 | m_fir[i] = (i < fir_size) ? fir[i] : 0; 128 | } 129 | 130 | FirFilter::FirFilter() : m_x(0) 131 | { 132 | m_fir = NULL; 133 | m_fir_alloc = NULL; 134 | m_fir_size = 0; 135 | } 136 | 137 | FirFilter::~FirFilter() 138 | { 139 | delete[] m_fir_alloc; 140 | } 141 | 142 | FirFilter& FirFilter::operator=(const FirFilter &obj) 143 | { 144 | delete[] m_fir_alloc; 145 | 146 | m_fir_size = obj.m_fir_size; 147 | m_org_fir_size = obj.m_org_fir_size; 148 | 149 | m_fir_alloc = new double[m_fir_size + 2]; // reserve some space for pointer align 150 | 151 | // align pointer! 152 | m_fir = (((size_t)m_fir_alloc & 0x0f) == 0) ? m_fir_alloc : (double *)(((size_t)m_fir_alloc & ~0x0f) + 0x10); 153 | 154 | memcpy(m_fir, obj.m_fir, m_fir_size * sizeof(double)); 155 | 156 | m_x = obj.m_x; 157 | 158 | return *this; 159 | } 160 | 161 | double FirFilter::processSample(double x) 162 | { 163 | double y; 164 | 165 | m_x.pushSample(x); 166 | 167 | y = fast_convolve(m_x.getBuffer()); 168 | 169 | return y; 170 | } 171 | 172 | void FirFilter::pushSample(double x) 173 | { 174 | m_x.pushSample(x); 175 | } 176 | 177 | // fir must be aligned! fir_size must be %8! 178 | double FirFilter::fast_convolve(double *x) 179 | { 180 | unsigned int i; 181 | double y; 182 | 183 | // convolution 184 | __m128d xy1, xy2, xy3, xy4; 185 | 186 | xy1 = _mm_setzero_pd(); 187 | xy2 = _mm_setzero_pd(); 188 | xy3 = _mm_setzero_pd(); 189 | xy4 = _mm_setzero_pd(); 190 | 191 | for (i = 0; i < m_fir_size; i += 8) 192 | { 193 | xy1 = _mm_add_pd(xy1, _mm_mul_pd(_mm_loadu_pd(x + i), _mm_load_pd(m_fir + i))); 194 | xy2 = _mm_add_pd(xy2, _mm_mul_pd(_mm_loadu_pd(x + i + 2), _mm_load_pd(m_fir + i + 2))); 195 | xy3 = _mm_add_pd(xy3, _mm_mul_pd(_mm_loadu_pd(x + i + 4), _mm_load_pd(m_fir + i + 4))); 196 | xy4 = _mm_add_pd(xy4, _mm_mul_pd(_mm_loadu_pd(x + i + 6), _mm_load_pd(m_fir + i + 6))); 197 | } 198 | 199 | xy1 = _mm_add_pd(_mm_add_pd(xy1, xy2), _mm_add_pd(xy3, xy4)); 200 | 201 | double xy_flt[2]; 202 | 203 | _mm_storeu_pd(xy_flt, xy1); 204 | 205 | y = xy_flt[0] + xy_flt[1]; 206 | 207 | return y; 208 | } 209 | 210 | void FirFilter::reset(bool reset_to_1) 211 | { 212 | m_x.reset(reset_to_1); 213 | } 214 | 215 | // ResamplerNxMx 216 | ResamplerNxMx::ResamplerNxMx(unsigned int nX, unsigned int mX, const double *fir, unsigned int fir_size) : m_x((fir_size % nX) == 0 ? fir_size / nX : fir_size / nX + 1) 217 | { 218 | unsigned int *xfir_size, i, j; 219 | double *xfir; 220 | 221 | m_fir_size = fir_size; 222 | m_xN = nX; 223 | m_xM = mX; 224 | xfir_size = new unsigned int[nX]; 225 | m_flt = new FirFilter[nX]; 226 | 227 | for (i = 0; i < nX; i++) 228 | { 229 | xfir_size[i] = (i < (fir_size % nX)) ? (fir_size / nX + 1) : (fir_size / nX); 230 | 231 | // use max size for alloc 232 | xfir = new double[xfir_size[0]]; 233 | 234 | // fill and pad with zeros 235 | for (j = 0; j < xfir_size[0]; j++) 236 | xfir[j] = j < xfir_size[i] ? fir[i + j * nX] : 0; 237 | 238 | // got filter 239 | m_flt[i] = FirFilter(xfir, xfir_size[i], true); 240 | 241 | delete[] xfir; 242 | } 243 | 244 | delete[] xfir_size; 245 | 246 | m_xN_counter = 0; 247 | } 248 | 249 | ResamplerNxMx::~ResamplerNxMx() 250 | { 251 | delete[] m_flt; 252 | } 253 | 254 | void ResamplerNxMx::processSample(const double *x, unsigned int x_n, double *y, unsigned int *y_n) 255 | { 256 | unsigned int i, offset, x_phase; 257 | 258 | assert((x_n * m_xN) % m_xM == 0); // x_n input samples to integer number of y_n output samples!!! 259 | 260 | offset = 0; 261 | 262 | for (i = 0; i < x_n; i++) 263 | { 264 | // push 1 sample 265 | m_x.pushSample(x[i]); 266 | 267 | // actually we pushed xN samples (xN upsampled) 268 | m_xN_counter += m_xN; 269 | 270 | if (m_xN_counter >= m_xM) 271 | { 272 | // calculate phase to fill m_xM samples 273 | x_phase = m_xN_counter - m_xM; 274 | 275 | // apply phase shift (0 -> 0000x -> (N-1); 1 -> x0000 -> 0; 2 -> 0x000 -> 1) 276 | x_phase = (x_phase + (m_xN - 1)) % m_xN; 277 | 278 | y[offset++] = m_flt[x_phase].fast_convolve(m_x.getBuffer()) * (double)m_xN; 279 | 280 | // leave some zero virtual samples in buffer 281 | m_xN_counter -= m_xM; 282 | } 283 | } 284 | 285 | assert(offset == x_n * m_xN / m_xM); 286 | 287 | *y_n = offset; 288 | } 289 | 290 | void ResamplerNxMx::reset(bool reset_to_1) 291 | { 292 | unsigned int i; 293 | 294 | for (i = 0; i < m_xN; i++) 295 | m_flt[i].reset(); 296 | 297 | m_x.reset(reset_to_1); 298 | 299 | m_xN_counter = 0; 300 | } 301 | 302 | // Dither 303 | Dither::Dither(unsigned int n_bits) 304 | { 305 | static int last_holdrand = 0; 306 | 307 | unsigned int max_value; 308 | 309 | assert(n_bits <= 31); 310 | 311 | max_value = 2 << n_bits; 312 | m_rand_max = 1.0 / (double)max_value; 313 | 314 | m_holdrand = last_holdrand++; 315 | } 316 | 317 | Dither& Dither::operator=(const Dither &obj) 318 | { 319 | m_rand_max = obj.m_rand_max; 320 | 321 | return *this; 322 | } 323 | 324 | // filter generation 325 | void generateFilter(double *impulse, int taps, double sinc_freq) 326 | { 327 | int i, int_sinc_freq; 328 | double x1, y1, x2, y2, y, sum_y, taps_per_pi, center_tap; 329 | 330 | taps_per_pi = sinc_freq / 2.0; 331 | center_tap = (double)(taps - 1) / 2.0; 332 | 333 | int_sinc_freq = (int)floor(sinc_freq); 334 | 335 | if ((double)int_sinc_freq != sinc_freq) 336 | int_sinc_freq = 0; 337 | 338 | sum_y = 0; 339 | 340 | for (i = 0; i < taps; i++) { 341 | 342 | // sinc 343 | x1 = ((double)i - center_tap) / taps_per_pi * M_PI; 344 | y1 = (x1 == 0.0) ? 1.0 : (sin(x1) / x1); 345 | 346 | if (int_sinc_freq != 0 && ((taps - 1) % 2) == 0 && (int_sinc_freq % 2) == 0 && ((i - ((taps - 1) / 2)) % (int_sinc_freq / 2)) == 0) 347 | { 348 | // insert true zero here! 349 | y1 = 0.0; 350 | //y1 = ((double)i == center_tap) ? 1.0 : 0.0; 351 | } 352 | 353 | if ((double)i == center_tap) 354 | y1 = 1.0; 355 | 356 | // windowing (BH7) 357 | x2 = (double)i / (double)(taps - 1); // from [0.0 to 1.0] 358 | y2 = 0.2712203606 - 0.4334446123 * cos(2.0 * M_PI * x2) + 0.21800412 * cos(4.0 * M_PI * x2) - 0.0657853433 * cos(6.0 * M_PI * x2) + 0.0107618673 * cos(8.0 * M_PI * x2) - 0.0007700127 * cos(10.0 * M_PI * x2) + 0.00001368088 * cos(12.0 * M_PI * x2); 359 | y = y1 * y2; 360 | impulse[i] = y; 361 | sum_y += y; 362 | } 363 | 364 | // scale 365 | for (i = 0; i < taps; i++) 366 | impulse[i] /= sum_y; 367 | } 368 | -------------------------------------------------------------------------------- /libdsd2pcm/upsampler.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-2016 Robert Tari 3 | Copyright 2012 Vladislav Goncharov 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #ifndef _upsampler_h_ 22 | #define _upsampler_h_ 23 | 24 | #include "dither.h" 25 | 26 | // history buffer for FIR 27 | class FirHistory 28 | { 29 | public: 30 | FirHistory(unsigned int fir_size); 31 | ~FirHistory(); 32 | FirHistory& operator=(const FirHistory &obj); 33 | void pushSample(double x); 34 | void reset(bool reset_to_1 = false); 35 | double *getBuffer() { return &m_x[m_head]; } 36 | unsigned int getSize() const { return m_fir_size; } 37 | 38 | private: 39 | double *m_x; // [m_fir_size * 2] 40 | unsigned int m_fir_size; 41 | unsigned int m_head; // write to m_x[--head] 42 | }; 43 | 44 | // FIR filter 45 | class FirFilter 46 | { 47 | public: 48 | FirFilter(const double *fir, unsigned int fir_size, bool no_history = false); 49 | FirFilter(); 50 | ~FirFilter(); 51 | FirFilter& operator=(const FirFilter &obj); 52 | double processSample(double x); 53 | void reset(bool reset_to_1 = false); 54 | void pushSample(double x); 55 | double fast_convolve(double *x); 56 | const double *getFir() const { return m_fir; } 57 | unsigned int getFirSize() const { return m_org_fir_size; } 58 | 59 | private: 60 | double *m_fir; // [m_fir_size], aligned 61 | double *m_fir_alloc; 62 | FirHistory m_x; 63 | unsigned int m_fir_size; // aligned 64 | unsigned int m_org_fir_size; 65 | }; 66 | 67 | // Nx/Mx resampler 68 | class ResamplerNxMx 69 | { 70 | public: 71 | ResamplerNxMx(unsigned int nX, unsigned int mX, const double *fir, unsigned int fir_size); 72 | ~ResamplerNxMx(); 73 | void processSample(const double *x, unsigned int x_n, double *y, unsigned int *y_n); 74 | void reset(bool reset_to_1 = false); 75 | unsigned int getFirSize() const { return m_fir_size; } 76 | 77 | private: 78 | unsigned int m_xN; // up^ 79 | unsigned int m_xM; // down_ 80 | unsigned int m_fir_size; 81 | FirFilter *m_flt; // [m_xN] 82 | FirHistory m_x; 83 | unsigned int m_xN_counter; // how many virtually upsampled samples we have in FirHistory? 84 | }; 85 | 86 | // generate windowed sinc impulse response for low-pass filter 87 | void generateFilter(double *impulse, int taps, double sinc_freq); 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /libdstdec/ac_data.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sound-Linux-More/sacd/6cfc988eca603c770788b3fd489b192ae5d264e5/libdstdec/ac_data.cpp -------------------------------------------------------------------------------- /libdstdec/ac_data.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sound-Linux-More/sacd/6cfc988eca603c770788b3fd489b192ae5d264e5/libdstdec/ac_data.h -------------------------------------------------------------------------------- /libdstdec/coded_table.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sound-Linux-More/sacd/6cfc988eca603c770788b3fd489b192ae5d264e5/libdstdec/coded_table.cpp -------------------------------------------------------------------------------- /libdstdec/coded_table.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sound-Linux-More/sacd/6cfc988eca603c770788b3fd489b192ae5d264e5/libdstdec/coded_table.h -------------------------------------------------------------------------------- /libdstdec/dst_consts.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sound-Linux-More/sacd/6cfc988eca603c770788b3fd489b192ae5d264e5/libdstdec/dst_consts.h -------------------------------------------------------------------------------- /libdstdec/dst_decoder.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sound-Linux-More/sacd/6cfc988eca603c770788b3fd489b192ae5d264e5/libdstdec/dst_decoder.cpp -------------------------------------------------------------------------------- /libdstdec/dst_decoder.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sound-Linux-More/sacd/6cfc988eca603c770788b3fd489b192ae5d264e5/libdstdec/dst_decoder.h -------------------------------------------------------------------------------- /libdstdec/dst_decoder_mt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-2016 Robert Tari 3 | Copyright 2011-2015 Maxim V.Anisiutkin 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #include "dst_decoder_mt.h" 22 | 23 | #define DSD_SILENCE_BYTE 0x69 24 | 25 | void* DSTDecoderThread(void* threadarg) 26 | { 27 | frame_slot_t* frame_slot = (frame_slot_t*)threadarg; 28 | 29 | while (1) 30 | { 31 | pthread_mutex_lock(&frame_slot->hMutex); 32 | 33 | while (frame_slot->state != SLOT_LOADED && frame_slot->state != SLOT_TERMINATING) 34 | { 35 | pthread_cond_wait(&frame_slot->hEventPut, &frame_slot->hMutex); 36 | } 37 | 38 | if (frame_slot->state == SLOT_TERMINATING) 39 | { 40 | frame_slot->dsd_data = nullptr; 41 | frame_slot->dst_size = 0; 42 | pthread_mutex_unlock(&frame_slot->hMutex); 43 | return 0; 44 | } 45 | 46 | frame_slot->state = SLOT_RUNNING; 47 | pthread_mutex_unlock(&frame_slot->hMutex); 48 | 49 | bool bError = false; 50 | 51 | try 52 | { 53 | frame_slot->D.decode(frame_slot->dst_data, frame_slot->dst_size * 8, frame_slot->dsd_data); 54 | } 55 | catch (...) 56 | { 57 | bError = true; 58 | frame_slot->D.close(); 59 | frame_slot->D.init(frame_slot->channel_count, frame_slot->samplerate / 44100); 60 | } 61 | 62 | pthread_mutex_lock(&frame_slot->hMutex); 63 | frame_slot->state = bError ? SLOT_READY_WITH_ERROR : SLOT_READY; 64 | pthread_cond_signal(&frame_slot->hEventGet); 65 | pthread_mutex_unlock(&frame_slot->hMutex); 66 | } 67 | 68 | return 0; 69 | } 70 | 71 | dst_decoder_t::dst_decoder_t(int threads) 72 | { 73 | thread_count = threads; 74 | 75 | frame_slots = new frame_slot_t[thread_count]; 76 | 77 | if (!frame_slots) 78 | { 79 | thread_count = 0; 80 | } 81 | 82 | channel_count = 0; 83 | samplerate = 0; 84 | framerate = 0; 85 | slot_nr = 0; 86 | } 87 | 88 | dst_decoder_t::~dst_decoder_t() 89 | { 90 | for (int i = 0; i < thread_count; i++) 91 | { 92 | frame_slot_t* frame_slot = &frame_slots[i]; 93 | 94 | // Release worker (decoding) thread for exit 95 | pthread_mutex_lock(&frame_slot->hMutex); 96 | frame_slot->state = SLOT_TERMINATING; 97 | frame_slot->D.close(); 98 | pthread_cond_signal(&frame_slot->hEventPut); 99 | pthread_mutex_unlock(&frame_slot->hMutex); 100 | 101 | // Wait until worker (decoding) thread exit 102 | pthread_join(frame_slot->hThread, NULL); 103 | pthread_cond_destroy(&frame_slot->hEventGet); 104 | pthread_cond_destroy(&frame_slot->hEventPut); 105 | pthread_mutex_destroy(&frame_slot->hMutex); 106 | } 107 | 108 | delete[] frame_slots; 109 | } 110 | 111 | int dst_decoder_t::init(int channel_count, int samplerate, int framerate) 112 | { 113 | for (int i = 0; i < thread_count; i++) 114 | { 115 | frame_slot_t* frame_slot = &frame_slots[i]; 116 | 117 | if (frame_slot->D.init(channel_count, (samplerate / 44100) / (framerate / 75)) == 0) 118 | { 119 | frame_slot->channel_count = channel_count; 120 | frame_slot->samplerate = samplerate; 121 | frame_slot->framerate = framerate; 122 | frame_slot->dsd_size = (size_t)(samplerate / 8 / framerate * channel_count); 123 | pthread_mutex_init(&frame_slot->hMutex, NULL); 124 | pthread_cond_init(&frame_slot->hEventGet, NULL); 125 | pthread_cond_init(&frame_slot->hEventPut, NULL); 126 | } 127 | else 128 | { 129 | return -1; 130 | } 131 | 132 | pthread_create(&frame_slot->hThread, NULL, DSTDecoderThread, frame_slot); 133 | } 134 | 135 | this->channel_count = channel_count; 136 | this->samplerate = samplerate; 137 | this->framerate = framerate; 138 | this->frame_nr = 0; 139 | 140 | return 0; 141 | } 142 | 143 | int dst_decoder_t::decode(uint8_t* dst_data, size_t dst_size, uint8_t** dsd_data, size_t* dsd_size) 144 | { 145 | // Get current slot 146 | frame_slot_t* frame_slot = &frame_slots[slot_nr]; 147 | 148 | // Allocate encoded frame into the slot 149 | frame_slot->dsd_data = *dsd_data; 150 | frame_slot->dst_data = dst_data; 151 | frame_slot->dst_size = dst_size; 152 | frame_slot->frame_nr = frame_nr; 153 | 154 | // Release worker (decoding) thread on the loaded slot 155 | if (dst_size > 0) 156 | { 157 | pthread_mutex_lock(&frame_slot->hMutex); 158 | frame_slot->state = SLOT_LOADED; 159 | pthread_cond_signal(&frame_slot->hEventPut); 160 | pthread_mutex_unlock(&frame_slot->hMutex); 161 | } 162 | else 163 | { 164 | frame_slot->state = SLOT_EMPTY; 165 | } 166 | 167 | // Advance to the next slot 168 | slot_nr = (slot_nr + 1) % thread_count; 169 | frame_slot = &frame_slots[slot_nr]; 170 | 171 | // Dump decoded frame 172 | if (frame_slot->state != SLOT_EMPTY) 173 | { 174 | pthread_mutex_lock(&frame_slot->hMutex); 175 | 176 | while (frame_slot->state != SLOT_READY) 177 | { 178 | pthread_cond_wait(&frame_slot->hEventGet, &frame_slot->hMutex); 179 | } 180 | 181 | pthread_mutex_unlock(&frame_slot->hMutex); 182 | } 183 | 184 | switch (frame_slot->state) 185 | { 186 | case SLOT_READY: 187 | *dsd_data = frame_slot->dsd_data; 188 | *dsd_size = (size_t)(samplerate / 8 / framerate * channel_count); 189 | break; 190 | case SLOT_READY_WITH_ERROR: 191 | *dsd_data = frame_slot->dsd_data; 192 | *dsd_size = (size_t)(samplerate / 8 / framerate * channel_count); 193 | memset(*dsd_data, DSD_SILENCE_BYTE, *dsd_size); 194 | break; 195 | default: 196 | *dsd_data = nullptr; 197 | *dsd_size = 0; 198 | break; 199 | } 200 | 201 | frame_nr++; 202 | 203 | return 0; 204 | } 205 | -------------------------------------------------------------------------------- /libdstdec/dst_decoder_mt.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Robert Tari 3 | Copyright 2011-2014 Maxim V.Anisiutkin 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #ifndef _DST_DECODER_H_INCLUDED 22 | #define _DST_DECODER_H_INCLUDED 23 | 24 | #include 25 | #include "dst_decoder.h" 26 | 27 | enum slot_state_t {SLOT_EMPTY, SLOT_LOADED, SLOT_RUNNING, SLOT_READY, SLOT_READY_WITH_ERROR, SLOT_TERMINATING}; 28 | 29 | class frame_slot_t 30 | { 31 | public: 32 | 33 | volatile int state; 34 | int frame_nr; 35 | uint8_t* dsd_data; 36 | int dsd_size; 37 | uint8_t* dst_data; 38 | int dst_size; 39 | int channel_count; 40 | int samplerate; 41 | int framerate; 42 | pthread_t hThread; 43 | pthread_cond_t hEventGet; 44 | pthread_cond_t hEventPut; 45 | pthread_mutex_t hMutex; 46 | CDSTDecoder D; 47 | 48 | frame_slot_t() 49 | { 50 | state = SLOT_EMPTY; 51 | dsd_data = nullptr; 52 | dsd_size = 0; 53 | dst_data = nullptr; 54 | dst_size = 0; 55 | channel_count = 0; 56 | samplerate = 0; 57 | framerate = 0; 58 | frame_nr = 0; 59 | } 60 | }; 61 | 62 | class dst_decoder_t 63 | { 64 | frame_slot_t* frame_slots; 65 | int thread_count; 66 | int channel_count; 67 | int samplerate; 68 | int framerate; 69 | uint32_t frame_nr; 70 | 71 | public: 72 | 73 | int slot_nr; 74 | 75 | dst_decoder_t(int threads); 76 | ~dst_decoder_t(); 77 | int init(int channel_count, int samplerate, int framerate); 78 | int decode(uint8_t* dst_data, size_t dst_size, uint8_t** dsd_data, size_t* dsd_size); 79 | }; 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /libdstdec/dst_defs.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sound-Linux-More/sacd/6cfc988eca603c770788b3fd489b192ae5d264e5/libdstdec/dst_defs.h -------------------------------------------------------------------------------- /libdstdec/frame_reader.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sound-Linux-More/sacd/6cfc988eca603c770788b3fd489b192ae5d264e5/libdstdec/frame_reader.cpp -------------------------------------------------------------------------------- /libdstdec/frame_reader.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sound-Linux-More/sacd/6cfc988eca603c770788b3fd489b192ae5d264e5/libdstdec/frame_reader.h -------------------------------------------------------------------------------- /libdstdec/str_data.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sound-Linux-More/sacd/6cfc988eca603c770788b3fd489b192ae5d264e5/libdstdec/str_data.cpp -------------------------------------------------------------------------------- /libdstdec/str_data.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sound-Linux-More/sacd/6cfc988eca603c770788b3fd489b192ae5d264e5/libdstdec/str_data.h -------------------------------------------------------------------------------- /libsacd/endianess.h: -------------------------------------------------------------------------------- 1 | /* 2 | SACD Ripper - http://code.google.com/p/sacd-ripper/ 3 | 4 | Copyright (c) 2010-2011 by respective authors. 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | */ 21 | 22 | #ifndef _ENDIANESS_H_INCLUDED 23 | #define _ENDIANESS_H_INCLUDED 24 | 25 | #define MAKE_MARKER(a, b, c, d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24)) 26 | 27 | #define hton16(x) ((((x) & 0xff00) >> 8) | (((x) & 0x00ff) << 8)) 28 | #define hton32(x) ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) 29 | #define hton64(x) ((((x) & 0xff00000000000000ULL) >> 56) | (((x) & 0x00ff000000000000ULL) >> 40) | (((x) & 0x0000ff0000000000ULL) >> 24) | (((x) & 0x000000ff00000000ULL) >> 8) | (((x) & 0x00000000ff000000ULL) << 8) | (((x) & 0x0000000000ff0000ULL) << 24) | (((x) & 0x000000000000ff00ULL) << 40) | (((x) & 0x00000000000000ffULL) << 56)) 30 | #define SWAP16(x) x = (hton16(x)) 31 | #define SWAP32(x) x = (hton32(x)) 32 | #define SWAP64(x) x = (hton64(x)) 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /libsacd/sacd_disc.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-2019 Robert Tari 3 | Copyright 2011-2019 Maxim V.Anisiutkin 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #ifndef _SACD_DISC_H_INCLUDED 22 | #define _SACD_DISC_H_INCLUDED 23 | 24 | #include 25 | #include "endianess.h" 26 | #include "scarletbook.h" 27 | #include "sacd_reader.h" 28 | 29 | constexpr int SACD_PSN_SIZE = 2064; 30 | 31 | using namespace std; 32 | 33 | typedef struct 34 | { 35 | uint8_t data[1024 * 64]; 36 | int size; 37 | bool started; 38 | int channel_count; 39 | int dst_encoded; 40 | } audio_frame_t; 41 | 42 | class sacd_disc_t : public sacd_reader_t 43 | { 44 | private: 45 | sacd_media_t* m_file; 46 | scarletbook_handle_t m_sb; 47 | area_id_e m_track_area; 48 | uint32_t m_track_start_lsn; 49 | uint32_t m_track_length_lsn; 50 | uint32_t m_track_current_lsn; 51 | uint8_t m_channel_count; 52 | audio_sector_t m_audio_sector; 53 | audio_frame_t m_frame; 54 | int m_packet_info_idx; 55 | uint8_t m_sector_buffer[SACD_PSN_SIZE]; 56 | uint32_t m_sector_size; 57 | int m_sector_bad_reads; 58 | uint8_t* m_buffer; 59 | int m_buffer_offset; 60 | public: 61 | static bool g_is_sacd(const char* p_path); 62 | sacd_disc_t(); 63 | ~sacd_disc_t(); 64 | scarletbook_area_t* get_area(area_id_e area_id); 65 | uint32_t get_track_count(area_id_e area_id = AREA_BOTH); 66 | int get_channels(); 67 | int get_samplerate(); 68 | int get_framerate(); 69 | float getProgress(); 70 | bool is_dst(); 71 | int open(sacd_media_t* p_file); 72 | bool close(); 73 | string set_track(uint32_t track_number, area_id_e area_id = AREA_BOTH, uint32_t offset = 0); 74 | bool read_frame(uint8_t* frame_data, size_t* frame_size, frame_type_e* frame_type); 75 | bool read_blocks_raw(uint32_t lb_start, size_t block_count, uint8_t* data); 76 | void getTrackDetails(uint32_t track_number, area_id_e area_id, TrackDetails* cTrackDetails); 77 | private: 78 | bool read_master_toc(); 79 | bool read_area_toc(int area_idx); 80 | void free_area(scarletbook_area_t* area); 81 | }; 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /libsacd/sacd_dsd.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-2016 Robert Tari 3 | Copyright 2011-2019 Maxim V.Anisiutkin 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #ifndef _SACD_DSD_H_INCLUDED 22 | #define _SACD_DSD_H_INCLUDED 23 | 24 | #include 25 | #include "endianess.h" 26 | 27 | #pragma pack(1) 28 | 29 | class ID 30 | { 31 | char ckID[4]; 32 | 33 | public: 34 | 35 | bool operator == (const char* id) 36 | { 37 | return ckID[0] == id[0] && ckID[1] == id[1] && ckID[2] == id[2] && ckID[3] == id[3]; 38 | } 39 | 40 | bool operator != (const char* id) 41 | { 42 | return !(*this == id); 43 | } 44 | 45 | void set_id(const char* id) 46 | { 47 | ckID[0] = id[0]; 48 | ckID[1] = id[1]; 49 | ckID[2] = id[2]; 50 | ckID[3] = id[3]; 51 | } 52 | }; 53 | 54 | class Chunk : public ID 55 | { 56 | 57 | public: 58 | 59 | uint64_t ckDataSize; 60 | 61 | uint64_t get_size() 62 | { 63 | return hton64(ckDataSize); 64 | } 65 | 66 | void set_size(uint64_t size) 67 | { 68 | ckDataSize = hton64(size); 69 | } 70 | }; 71 | 72 | #pragma pack() 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /libsacd/sacd_dsdiff.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-2019 Robert Tari 3 | Copyright 2011-2019 Maxim V.Anisiutkin 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #include "sacd_dsdiff.h" 22 | 23 | #define MIN(a,b) (((a)<(b))?(a):(b)) 24 | #define MAX(a,b) (((a)>(b))?(a):(b)) 25 | 26 | sacd_dsdiff_t::sacd_dsdiff_t() 27 | { 28 | m_current_subsong = 0; 29 | m_dst_encoded = 0; 30 | } 31 | 32 | sacd_dsdiff_t::~sacd_dsdiff_t() 33 | { 34 | close(); 35 | } 36 | 37 | uint32_t sacd_dsdiff_t::get_track_count(area_id_e area_id) 38 | { 39 | if ((area_id == AREA_TWOCH && m_channel_count == 2) || (area_id == AREA_MULCH && m_channel_count > 2) || area_id == AREA_BOTH) 40 | { 41 | return m_subsong.size(); 42 | } 43 | 44 | return 0; 45 | } 46 | 47 | int sacd_dsdiff_t::get_channels() 48 | { 49 | return m_channel_count; 50 | } 51 | 52 | int sacd_dsdiff_t::get_samplerate() 53 | { 54 | return m_samplerate; 55 | } 56 | 57 | int sacd_dsdiff_t::get_framerate() 58 | { 59 | return m_framerate; 60 | } 61 | 62 | float sacd_dsdiff_t::getProgress() 63 | { 64 | return ((float)(m_file->get_position() - m_current_offset) * 100.0) / (float)m_current_size; 65 | } 66 | 67 | bool sacd_dsdiff_t::is_dst() 68 | { 69 | return m_dst_encoded != 0; 70 | } 71 | 72 | int sacd_dsdiff_t::open(sacd_media_t* p_file) 73 | { 74 | m_file = p_file; 75 | m_dsti_size = 0; 76 | Chunk ck; 77 | ID id; 78 | uint32_t start_mark_count = 0; 79 | id3tags_t t_old; 80 | m_subsong.resize(0); 81 | m_id3tags.resize(0); 82 | 83 | if (!m_file->seek(0)) 84 | { 85 | return 0; 86 | } 87 | 88 | if (!(m_file->read(&ck, sizeof(ck)) == sizeof(ck) && ck == "FRM8")) 89 | { 90 | return 0; 91 | } 92 | 93 | if (!(m_file->read(&id, sizeof(id)) == sizeof(id) && id == "DSD ")) 94 | { 95 | return 0; 96 | } 97 | 98 | m_frm8_size = ck.get_size(); 99 | 100 | while ((uint64_t)m_file->get_position() < m_frm8_size + sizeof(ck)) 101 | { 102 | if (!(m_file->read(&ck, sizeof(ck)) == sizeof(ck))) 103 | { 104 | return 0; 105 | } 106 | 107 | if (ck == "FVER" && ck.get_size() == 4) 108 | { 109 | uint32_t version; 110 | 111 | if (!(m_file->read(&version, sizeof(version)) == sizeof(version))) 112 | { 113 | return 0; 114 | } 115 | 116 | m_version = hton32(version); 117 | } 118 | else if (ck == "PROP") 119 | { 120 | if (!(m_file->read(&id, sizeof(id)) == sizeof(id) && id == "SND ")) 121 | { 122 | return 0; 123 | } 124 | 125 | int64_t id_prop_end = m_file->get_position() - sizeof(id) + ck.get_size(); 126 | 127 | while (m_file->get_position() < id_prop_end) 128 | { 129 | if (!(m_file->read(&ck, sizeof(ck)) == sizeof(ck))) 130 | { 131 | return 0; 132 | } 133 | 134 | if (ck == "FS " && ck.get_size() == 4) 135 | { 136 | uint32_t samplerate; 137 | 138 | if (!(m_file->read(&samplerate, sizeof(samplerate)) == sizeof(samplerate))) 139 | { 140 | return 0; 141 | } 142 | 143 | m_samplerate = hton32(samplerate); 144 | } 145 | else if (ck == "CHNL") 146 | { 147 | uint16_t channel_count; 148 | 149 | if (!(m_file->read(&channel_count, sizeof(channel_count)) == sizeof(channel_count))) 150 | { 151 | return false; 152 | } 153 | 154 | m_channel_count = hton16(channel_count); 155 | 156 | switch (m_channel_count) 157 | { 158 | case 2: 159 | m_loudspeaker_config = 0; 160 | break; 161 | case 5: 162 | m_loudspeaker_config = 3; 163 | break; 164 | case 6: 165 | m_loudspeaker_config = 4; 166 | break; 167 | default: 168 | m_loudspeaker_config = 65535; 169 | break; 170 | } 171 | 172 | m_file->skip(ck.get_size() - sizeof(channel_count)); 173 | } 174 | else if (ck == "CMPR") 175 | { 176 | if (!(m_file->read(&id, sizeof(id)) == sizeof(id))) 177 | { 178 | return 0; 179 | } 180 | 181 | if (id == "DSD ") 182 | { 183 | m_dst_encoded = 0; 184 | } 185 | 186 | if (id == "DST ") 187 | { 188 | m_dst_encoded = 1; 189 | } 190 | 191 | m_file->skip(ck.get_size() - sizeof(id)); 192 | } 193 | else if (ck == "LSCO") 194 | { 195 | uint16_t loudspeaker_config; 196 | 197 | if (!(m_file->read(&loudspeaker_config, sizeof(loudspeaker_config)) == sizeof(loudspeaker_config))) 198 | { 199 | return 0; 200 | } 201 | 202 | m_loudspeaker_config = hton16(loudspeaker_config); 203 | m_file->skip(ck.get_size() - sizeof(loudspeaker_config)); 204 | } 205 | else if (ck == "ID3 ") 206 | { 207 | t_old.index = 0; 208 | t_old.offset = m_file->get_position(); 209 | t_old.id3_value.resize((uint32_t)ck.get_size()); 210 | m_file->read(t_old.id3_value.data(), t_old.id3_value.size()); 211 | } 212 | else 213 | { 214 | m_file->skip(ck.get_size()); 215 | } 216 | 217 | m_file->skip(m_file->get_position() & 1); 218 | } 219 | } 220 | else if (ck == "DSD ") 221 | { 222 | m_data_offset = m_file->get_position(); 223 | m_data_size = ck.get_size(); 224 | m_framerate = 75; 225 | m_frame_size = m_samplerate / 8 * m_channel_count / m_framerate; 226 | m_frame_count = (uint32_t)(m_data_size / m_frame_size); 227 | m_file->skip(ck.get_size()); 228 | subsong_t s; 229 | s.start_time = 0.0; 230 | s.stop_time = (double) m_frame_count / m_framerate; 231 | m_subsong.push_back(s); 232 | } 233 | else if (ck == "DST ") 234 | { 235 | m_data_offset = m_file->get_position(); 236 | m_data_size = ck.get_size(); 237 | 238 | if (!(m_file->read(&ck, sizeof(ck)) == sizeof(ck) && ck == "FRTE" && ck.get_size() == 6)) 239 | { 240 | return 0; 241 | } 242 | 243 | m_data_offset += sizeof(ck) + ck.get_size(); 244 | m_data_size -= sizeof(ck) + ck.get_size(); 245 | m_current_offset = m_data_offset; 246 | m_current_size = m_data_size; 247 | uint32_t frame_count; 248 | 249 | if (!(m_file->read(&frame_count, sizeof(frame_count)) == sizeof(frame_count))) 250 | { 251 | return 0; 252 | } 253 | 254 | m_frame_count = hton32(frame_count); 255 | uint16_t framerate; 256 | 257 | if (!(m_file->read(&framerate, sizeof(framerate)) == sizeof(framerate))) 258 | { 259 | return 0; 260 | } 261 | 262 | m_framerate = hton16(framerate); 263 | m_frame_size = m_samplerate / 8 * m_channel_count / m_framerate; 264 | m_file->seek(m_data_offset + m_data_size); 265 | subsong_t s; 266 | s.start_time = 0.0; 267 | s.stop_time = (double)m_frame_count / m_framerate; 268 | m_subsong.push_back(s); 269 | } 270 | else if (ck == "DSTI") 271 | { 272 | m_dsti_offset = m_file->get_position(); 273 | m_dsti_size = ck.get_size(); 274 | m_file->skip(ck.get_size()); 275 | } 276 | else if (ck == "DIIN") 277 | { 278 | int64_t id_diin_end = m_file->get_position() + ck.get_size(); 279 | 280 | while (m_file->get_position() < id_diin_end) 281 | { 282 | if (!(m_file->read(&ck, sizeof(ck)) == sizeof(ck))) 283 | { 284 | return false; 285 | } 286 | 287 | if (ck == "MARK" && ck.get_size() >= sizeof(Marker)) 288 | { 289 | Marker m; 290 | 291 | if (m_file->read(&m, sizeof(Marker)) == sizeof(Marker)) 292 | { 293 | m.hours = hton16(m.hours); 294 | m.samples = hton32(m.samples); 295 | m.offset = hton32(m.offset); 296 | m.markType = hton16(m.markType); 297 | m.markChannel = hton16(m.markChannel); 298 | m.TrackFlags = hton16(m.TrackFlags); 299 | m.count = hton32(m.count); 300 | 301 | switch (m.markType) 302 | { 303 | case TrackStart: 304 | { 305 | if (start_mark_count > 0) 306 | { 307 | subsong_t s; 308 | m_subsong.push_back(s); 309 | } 310 | 311 | start_mark_count++; 312 | 313 | if (m_subsong.size() > 0) 314 | { 315 | m_subsong[m_subsong.size() - 1].start_time = get_marker_time(m); 316 | m_subsong[m_subsong.size() - 1].stop_time = (double)m_frame_count / m_framerate; 317 | 318 | if (m_subsong.size() - 1 > 0) 319 | { 320 | if (m_subsong[m_subsong.size() - 2].stop_time > m_subsong[m_subsong.size() - 1].start_time) 321 | { 322 | m_subsong[m_subsong.size() - 2].stop_time = m_subsong[m_subsong.size() - 1].start_time; 323 | } 324 | } 325 | } 326 | 327 | break; 328 | } 329 | case TrackStop: 330 | { 331 | if (m_subsong.size() > 0) 332 | { 333 | m_subsong[m_subsong.size() - 1].stop_time = get_marker_time(m); 334 | } 335 | 336 | break; 337 | } 338 | } 339 | } 340 | 341 | m_file->skip(ck.get_size() - sizeof(Marker)); 342 | } 343 | else 344 | { 345 | m_file->skip(ck.get_size()); 346 | } 347 | 348 | m_file->skip(m_file->get_position() & 1); 349 | } 350 | } 351 | else if (ck == "ID3 ") 352 | { 353 | id3tags_t t; 354 | t.index = m_id3tags.size(); 355 | t.offset = m_file->get_position(); 356 | t.id3_value.resize((uint32_t)ck.get_size()); 357 | m_file->read(t.id3_value.data(), t.id3_value.size()); 358 | m_id3tags.push_back(t); 359 | } 360 | else 361 | { 362 | m_file->skip(ck.get_size()); 363 | } 364 | 365 | m_file->skip(m_file->get_position() & 1); 366 | } 367 | 368 | if (m_id3tags.size() == 0) 369 | { 370 | if (t_old.id3_value.size() > 0) 371 | { 372 | m_id3tags.push_back(t_old); 373 | } 374 | } 375 | 376 | m_file->seek(m_data_offset); 377 | 378 | return m_subsong.size(); 379 | } 380 | 381 | bool sacd_dsdiff_t::close() 382 | { 383 | m_current_subsong = 0; 384 | m_subsong.resize(0); 385 | m_id3tags.resize(0); 386 | m_dsti_size = 0; 387 | 388 | return true; 389 | } 390 | 391 | void sacd_dsdiff_t::getTrackDetails(uint32_t track_number, area_id_e area_id, TrackDetails* cTrackDetails) 392 | { 393 | cTrackDetails->strArtist = "Unknown Artist"; 394 | cTrackDetails->strTitle = "Unknown Title"; 395 | cTrackDetails->nChannels = m_channel_count; 396 | } 397 | 398 | string sacd_dsdiff_t::set_track(uint32_t track_number, area_id_e area_id, uint32_t offset) 399 | { 400 | if (track_number < m_subsong.size()) 401 | { 402 | m_current_subsong = track_number; 403 | double t0 = m_subsong[m_current_subsong].start_time; 404 | double t1 = m_subsong[m_current_subsong].stop_time; 405 | uint64_t offset = (uint64_t)(t0 * m_framerate / m_frame_count * m_data_size); 406 | uint64_t size = (uint64_t)(t1 * m_framerate / m_frame_count * m_data_size) - offset; 407 | 408 | if (m_dst_encoded) 409 | { 410 | if (m_dsti_size > 0) 411 | { 412 | if ((uint32_t)(t0 * m_framerate) < (uint32_t)(m_dsti_size / sizeof(DSTFrameIndex) - 1)) 413 | { 414 | m_current_offset = get_dsti_for_frame((uint32_t)(t0 * m_framerate)); 415 | } 416 | else 417 | { 418 | m_current_offset = m_data_offset + offset; 419 | } 420 | 421 | if ((uint32_t)(t1 * m_framerate) < (uint32_t)(m_dsti_size / sizeof(DSTFrameIndex) - 1)) 422 | { 423 | m_current_size = get_dsti_for_frame((uint32_t)(t1 * m_framerate)) - m_current_offset; 424 | } 425 | else 426 | { 427 | m_current_size = size; 428 | } 429 | } 430 | else 431 | { 432 | m_current_offset = m_data_offset + offset; 433 | m_current_size = size; 434 | } 435 | } 436 | else 437 | { 438 | m_current_offset = m_data_offset + (offset / m_frame_size) * m_frame_size; 439 | m_current_size = (size / m_frame_size) * m_frame_size; 440 | } 441 | } 442 | 443 | m_file->seek(m_current_offset); 444 | 445 | return m_file->getFileName(); 446 | } 447 | 448 | bool sacd_dsdiff_t::read_frame(uint8_t* frame_data, size_t* frame_size, frame_type_e* frame_type) 449 | { 450 | if (m_dst_encoded) 451 | { 452 | Chunk ck; 453 | 454 | while ((uint64_t)m_file->get_position() < m_current_offset + m_current_size && m_file->read(&ck, sizeof(ck)) == sizeof(ck)) 455 | { 456 | if (ck == "DSTF" && ck.get_size() <= (uint64_t)*frame_size) 457 | { 458 | if (m_file->read(frame_data, (size_t)ck.get_size()) == ck.get_size()) 459 | { 460 | m_file->skip(ck.get_size() & 1); 461 | *frame_size = (size_t)ck.get_size(); 462 | *frame_type = FRAME_DST; 463 | 464 | return true; 465 | } 466 | 467 | break; 468 | } 469 | else if (ck == "DSTC" && ck.get_size() == 4) 470 | { 471 | uint32_t crc; 472 | 473 | if (ck.get_size() == sizeof(crc)) 474 | { 475 | if (m_file->read(&crc, sizeof(crc)) != sizeof(crc)) 476 | { 477 | break; 478 | } 479 | } 480 | else 481 | { 482 | m_file->skip(ck.get_size()); 483 | m_file->skip(ck.get_size() & 1); 484 | } 485 | } 486 | else 487 | { 488 | m_file->seek(1 - (int)sizeof(ck), SEEK_CUR); 489 | } 490 | } 491 | } 492 | else 493 | { 494 | uint64_t position = m_file->get_position(); 495 | *frame_size = (size_t)MIN((int64_t)m_frame_size, (int64_t)MAX(0, (int64_t)(m_current_offset + m_current_size) - (int64_t)position)); 496 | 497 | if (*frame_size > 0) 498 | { 499 | *frame_size = m_file->read(frame_data, *frame_size); 500 | *frame_size -= *frame_size % m_channel_count; 501 | 502 | if (*frame_size > 0) 503 | { 504 | *frame_type = FRAME_DSD; 505 | return true; 506 | } 507 | } 508 | } 509 | 510 | *frame_type = FRAME_INVALID; 511 | return false; 512 | } 513 | 514 | double sacd_dsdiff_t::get_marker_time(const Marker& m) 515 | { 516 | return (double)m.hours * 60 * 60 + (double)m.minutes * 60 + (double)m.seconds + ((double)m.samples + (double)m.offset) / (double)m_samplerate; 517 | } 518 | 519 | uint64_t sacd_dsdiff_t::get_dsti_for_frame(uint32_t frame_nr) 520 | { 521 | uint64_t cur_offset; 522 | DSTFrameIndex frame_index; 523 | cur_offset = m_file->get_position(); 524 | frame_nr = min(frame_nr, (uint32_t)(m_dsti_size / sizeof(DSTFrameIndex) - 1)); 525 | m_file->seek(m_dsti_offset + frame_nr * sizeof(DSTFrameIndex)); 526 | cur_offset = m_file->get_position(); 527 | m_file->read(&frame_index, sizeof(DSTFrameIndex)); 528 | m_file->seek(cur_offset); 529 | 530 | return hton64(frame_index.offset) - sizeof(Chunk); 531 | } 532 | -------------------------------------------------------------------------------- /libsacd/sacd_dsdiff.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-2019 Robert Tari 3 | Copyright 2011-2019 Maxim V.Anisiutkin 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #ifndef _SACD_DSDIFF_H_INCLUDED 22 | #define _SACD_DSDIFF_H_INCLUDED 23 | 24 | #include 25 | #include 26 | #include "endianess.h" 27 | #include "scarletbook.h" 28 | #include "sacd_reader.h" 29 | #include "sacd_dsd.h" 30 | 31 | #pragma pack(1) 32 | 33 | class FormDSDChunk : public Chunk 34 | { 35 | public: 36 | ID formType; 37 | }; 38 | 39 | class DSTFrameIndex 40 | { 41 | public: 42 | uint64_t offset; 43 | uint32_t length; 44 | }; 45 | 46 | enum MarkType {TrackStart = 0, TrackStop = 1, ProgramStart = 2, Index = 4}; 47 | 48 | class Marker 49 | { 50 | public: 51 | uint16_t hours; 52 | uint8_t minutes; 53 | uint8_t seconds; 54 | uint32_t samples; 55 | int32_t offset; 56 | uint16_t markType; 57 | uint16_t markChannel; 58 | uint16_t TrackFlags; 59 | uint32_t count; 60 | }; 61 | 62 | #pragma pack() 63 | 64 | class subsong_t 65 | { 66 | public: 67 | double start_time; 68 | double stop_time; 69 | }; 70 | 71 | class id3tags_t 72 | { 73 | public: 74 | uint32_t index; 75 | uint64_t offset; 76 | vector id3_value; 77 | }; 78 | 79 | class sacd_dsdiff_t : public sacd_reader_t 80 | { 81 | sacd_media_t* m_file; 82 | uint32_t m_version; 83 | uint32_t m_samplerate; 84 | uint16_t m_channel_count; 85 | int m_loudspeaker_config; 86 | int m_dst_encoded; 87 | uint64_t m_frm8_size; 88 | uint64_t m_dsti_offset; 89 | uint64_t m_dsti_size; 90 | uint64_t m_data_offset; 91 | uint64_t m_data_size; 92 | uint16_t m_framerate; 93 | uint32_t m_frame_size; 94 | uint32_t m_frame_count; 95 | vector m_subsong; 96 | vector m_id3tags; 97 | uint32_t m_current_subsong; 98 | uint64_t m_current_offset; 99 | uint64_t m_current_size; 100 | public: 101 | sacd_dsdiff_t(); 102 | virtual ~sacd_dsdiff_t(); 103 | uint32_t get_track_count(area_id_e area_id = AREA_BOTH); 104 | int get_channels(); 105 | int get_samplerate(); 106 | int get_framerate(); 107 | float getProgress(); 108 | bool is_dst(); 109 | int open(sacd_media_t* p_file); 110 | bool close(); 111 | string set_track(uint32_t track_number, area_id_e area_id = AREA_BOTH, uint32_t offset = 0); 112 | bool read_frame(uint8_t* frame_data, size_t* frame_size, frame_type_e* frame_type); 113 | void getTrackDetails(uint32_t track_number, area_id_e area_id, TrackDetails* cTrackDetails); 114 | private: 115 | double get_marker_time(const Marker& m); 116 | uint64_t get_dsti_for_frame(uint32_t frame_nr); 117 | }; 118 | 119 | #endif 120 | -------------------------------------------------------------------------------- /libsacd/sacd_dsf.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-2019 Robert Tari 3 | Copyright 2011-2019 Maxim V.Anisiutkin 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #include "sacd_dsf.h" 22 | 23 | #define MIN(a,b) (((a)<(b))?(a):(b)) 24 | 25 | sacd_dsf_t::sacd_dsf_t() 26 | { 27 | for (int i = 0; i < 256; i++) 28 | { 29 | swap_bits[i] = 0; 30 | 31 | for (int j = 0; j < 8; j++) 32 | { 33 | swap_bits[i] |= ((i >> j) & 1) << (7 - j); 34 | } 35 | } 36 | } 37 | 38 | sacd_dsf_t::~sacd_dsf_t() 39 | { 40 | close(); 41 | } 42 | 43 | uint32_t sacd_dsf_t::get_track_count(area_id_e area_id) 44 | { 45 | if ((area_id == AREA_TWOCH && m_channel_count <= 2) || (area_id == AREA_MULCH && m_channel_count > 2) || area_id == AREA_BOTH) 46 | { 47 | return 1; 48 | } 49 | 50 | return 0; 51 | } 52 | 53 | int sacd_dsf_t::get_channels() 54 | { 55 | return m_channel_count; 56 | } 57 | 58 | int sacd_dsf_t::get_samplerate() 59 | { 60 | return m_samplerate; 61 | } 62 | 63 | int sacd_dsf_t::get_framerate() 64 | { 65 | return 75; 66 | } 67 | 68 | float sacd_dsf_t::getProgress() 69 | { 70 | return ((float)(m_file->get_position() - m_read_offset) * 100.0) / (float)m_data_size; 71 | } 72 | 73 | bool sacd_dsf_t::is_dst() 74 | { 75 | return false; 76 | } 77 | 78 | int sacd_dsf_t::open(sacd_media_t* p_file) 79 | { 80 | m_file = p_file; 81 | Chunk ck; 82 | FmtDSFChunk fmt; 83 | uint64_t pos; 84 | 85 | if (!(m_file->read(&ck, sizeof(ck)) == sizeof(ck) && ck == "DSD ")) 86 | { 87 | return false; 88 | } 89 | 90 | if (ck.get_size() != hton64((uint64_t)28)) 91 | { 92 | return false; 93 | } 94 | 95 | if (m_file->read(&m_file_size, sizeof(m_file_size)) != sizeof(m_file_size)) 96 | { 97 | return false; 98 | } 99 | 100 | if (m_file->read(&m_id3_offset, sizeof(m_id3_offset)) != sizeof(m_id3_offset)) 101 | { 102 | return false; 103 | } 104 | 105 | pos = m_file->get_position(); 106 | 107 | if (!(m_file->read(&fmt, sizeof(fmt)) == sizeof(fmt) && fmt == "fmt ")) 108 | { 109 | return false; 110 | } 111 | 112 | if (fmt.format_id != 0) { 113 | return false; 114 | } 115 | 116 | switch (fmt.channel_type) 117 | { 118 | case 1: 119 | m_loudspeaker_config = 5; 120 | break; 121 | case 2: 122 | m_loudspeaker_config = 0; 123 | break; 124 | case 3: 125 | m_loudspeaker_config = 6; 126 | break; 127 | case 4: 128 | m_loudspeaker_config = 1; 129 | break; 130 | case 5: 131 | m_loudspeaker_config = 2; 132 | break; 133 | case 6: 134 | m_loudspeaker_config = 3; 135 | break; 136 | case 7: 137 | m_loudspeaker_config = 4; 138 | break; 139 | default: 140 | m_loudspeaker_config = 65535; 141 | break; 142 | } 143 | 144 | if (fmt.channel_count < 1 || fmt.channel_count > 6) 145 | { 146 | return false; 147 | } 148 | 149 | m_channel_count = fmt.channel_count; 150 | m_samplerate = fmt.samplerate; 151 | 152 | switch (fmt.bits_per_sample) 153 | { 154 | case 1: 155 | m_is_lsb = true; 156 | break; 157 | case 8: 158 | m_is_lsb = false; 159 | break; 160 | default: 161 | return false; 162 | break; 163 | } 164 | 165 | m_sample_count = fmt.sample_count; 166 | m_block_size = fmt.block_size; 167 | m_block_offset = m_block_size; 168 | m_block_data_end = 0; 169 | m_file->seek(pos + hton64(fmt.get_size())); 170 | 171 | if (!(m_file->read(&ck, sizeof(ck)) == sizeof(ck) && ck == "data")) 172 | { 173 | return false; 174 | } 175 | 176 | m_block_data.resize(m_channel_count * m_block_size); 177 | m_data_offset = m_file->get_position(); 178 | m_data_end_offset = m_data_offset + ((m_sample_count / 8) * m_channel_count); 179 | m_data_size = hton64(ck.get_size()) - sizeof(ck); 180 | m_read_offset = m_data_offset; 181 | 182 | return 1; 183 | } 184 | 185 | bool sacd_dsf_t::close() 186 | { 187 | return true; 188 | } 189 | 190 | void sacd_dsf_t::getTrackDetails(uint32_t track_number, area_id_e area_id, TrackDetails* cTrackDetails) 191 | { 192 | cTrackDetails->strArtist = "Unknown Artist"; 193 | cTrackDetails->strTitle = "Unknown Title"; 194 | cTrackDetails->nChannels = m_channel_count; 195 | } 196 | 197 | string sacd_dsf_t::set_track(uint32_t track_number, area_id_e area_id, uint32_t offset) 198 | { 199 | if (track_number) 200 | { 201 | return ""; 202 | } 203 | 204 | m_file->seek(m_data_offset); 205 | 206 | return m_file->getFileName(); 207 | } 208 | 209 | bool sacd_dsf_t::read_frame(uint8_t* frame_data, size_t* frame_size, frame_type_e* frame_type) 210 | { 211 | int samples_read = 0; 212 | 213 | for (int i = 0; i < (int)*frame_size / m_channel_count; i++) 214 | { 215 | if (m_block_offset * m_channel_count >= m_block_data_end) 216 | { 217 | m_block_data_end = (int)MIN(m_data_end_offset - m_file->get_position(), m_block_data.size()); 218 | 219 | if (m_block_data_end > 0) 220 | { 221 | m_block_data_end = m_file->read(m_block_data.data(), m_block_data_end); 222 | } 223 | 224 | if (m_block_data_end > 0) 225 | { 226 | m_block_offset = 0; 227 | } 228 | else 229 | { 230 | break; 231 | } 232 | } 233 | 234 | for (int ch = 0; ch < m_channel_count; ch++) 235 | { 236 | uint8_t b = m_block_data.data()[ch * m_block_size + m_block_offset]; 237 | frame_data[i * m_channel_count + ch] = m_is_lsb ? swap_bits[b] : b; 238 | } 239 | 240 | m_block_offset++; 241 | samples_read++; 242 | } 243 | 244 | *frame_size = samples_read * m_channel_count; 245 | *frame_type = samples_read > 0 ? FRAME_DSD : FRAME_INVALID; 246 | 247 | return samples_read > 0; 248 | } 249 | -------------------------------------------------------------------------------- /libsacd/sacd_dsf.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-2019 Robert Tari 3 | Copyright 2011-2019 Maxim V.Anisiutkin 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #ifndef _SACD_DSF_H_INCLUDED 22 | #define _SACD_DSF_H_INCLUDED 23 | 24 | #include 25 | #include 26 | #include "endianess.h" 27 | #include "scarletbook.h" 28 | #include "sacd_reader.h" 29 | #include "sacd_dsd.h" 30 | 31 | #pragma pack(1) 32 | 33 | class FmtDSFChunk : public Chunk 34 | { 35 | public: 36 | uint32_t format_version; 37 | uint32_t format_id; 38 | uint32_t channel_type; 39 | uint32_t channel_count; 40 | uint32_t samplerate; 41 | uint32_t bits_per_sample; 42 | uint64_t sample_count; 43 | uint32_t block_size; 44 | uint32_t reserved; 45 | }; 46 | 47 | #pragma pack() 48 | 49 | class sacd_dsf_t : public sacd_reader_t 50 | { 51 | sacd_media_t* m_file; 52 | int m_samplerate; 53 | int m_channel_count; 54 | int m_loudspeaker_config; 55 | uint64_t m_file_size; 56 | vector m_block_data; 57 | int m_block_size; 58 | int m_block_offset; 59 | int m_block_data_end; 60 | uint64_t m_sample_count; 61 | uint64_t m_data_offset; 62 | uint64_t m_data_size; 63 | uint64_t m_data_end_offset; 64 | uint64_t m_read_offset; 65 | bool m_is_lsb; 66 | uint64_t m_id3_offset; 67 | vector m_id3_data; 68 | uint8_t swap_bits[256]; 69 | public: 70 | sacd_dsf_t(); 71 | ~sacd_dsf_t(); 72 | uint32_t get_track_count(area_id_e area_id = AREA_BOTH); 73 | int get_channels(); 74 | int get_samplerate(); 75 | int get_framerate(); 76 | float getProgress(); 77 | bool is_dst(); 78 | int open(sacd_media_t* p_file); 79 | bool close(); 80 | string set_track(uint32_t track_number, area_id_e area_id = AREA_BOTH, uint32_t offset = 0); 81 | bool read_frame(uint8_t* frame_data, size_t* frame_size, frame_type_e* frame_type); 82 | void getTrackDetails(uint32_t track_number, area_id_e area_id, TrackDetails* cTrackDetails); 83 | }; 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /libsacd/sacd_media.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-2019 Robert Tari 3 | Copyright 2011-2019 Maxim V.Anisiutkin 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include "scarletbook.h" 24 | #include "sacd_media.h" 25 | 26 | #define MIN(a,b) (((a)<(b))?(a):(b)) 27 | 28 | sacd_media_t::sacd_media_t() 29 | { 30 | } 31 | 32 | sacd_media_t::~sacd_media_t() 33 | { 34 | } 35 | 36 | bool sacd_media_t::open(const char* path) 37 | { 38 | try 39 | { 40 | media_file = fopen(path, "r"); 41 | m_strFilePath = path; 42 | return true; 43 | } 44 | catch (...) 45 | { 46 | } 47 | 48 | return false; 49 | } 50 | 51 | bool sacd_media_t::close() 52 | { 53 | fclose(media_file); 54 | 55 | return true; 56 | } 57 | 58 | bool sacd_media_t::seek(int64_t position, int mode) 59 | { 60 | fseek(media_file, position, mode); 61 | return true; 62 | } 63 | 64 | int64_t sacd_media_t::get_position() 65 | { 66 | return ftell(media_file); 67 | } 68 | 69 | size_t sacd_media_t::read(void* data, size_t size) 70 | { 71 | return fread(data, 1, size, media_file); 72 | } 73 | 74 | int64_t sacd_media_t::skip(int64_t bytes) 75 | { 76 | return fseek(media_file, bytes, SEEK_CUR); 77 | } 78 | 79 | string sacd_media_t::getFileName() 80 | { 81 | m_strFilePath = m_strFilePath.substr(m_strFilePath.find_last_of("/") + 1, string::npos); 82 | return m_strFilePath.substr(0, m_strFilePath.find_last_of(".")) + ".wav"; 83 | } 84 | -------------------------------------------------------------------------------- /libsacd/sacd_media.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-2016 Robert Tari 3 | Copyright 2011-2012 Maxim V.Anisiutkin 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #ifndef _SACD_MEDIA_H_INCLUDED 22 | #define _SACD_MEDIA_H_INCLUDED 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | using namespace std; 30 | 31 | class sacd_media_t 32 | { 33 | FILE * media_file; 34 | string m_strFilePath; 35 | public: 36 | sacd_media_t(); 37 | virtual ~sacd_media_t(); 38 | virtual bool open(const char* path); 39 | virtual bool close(); 40 | virtual bool seek(int64_t position, int mode = SEEK_SET); 41 | virtual int64_t get_position(); 42 | virtual size_t read(void* data, size_t size); 43 | virtual int64_t skip(int64_t bytes); 44 | virtual string getFileName(); 45 | }; 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /libsacd/sacd_reader.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-2019 Robert Tari 3 | Copyright 2011-2019 Maxim V.Anisiutkin 4 | 5 | This file is part of SACD. 6 | 7 | SACD is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | SACD is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with SACD. If not, see . 19 | */ 20 | 21 | #ifndef _SACD_READER_H_INCLUDED 22 | #define _SACD_READER_H_INCLUDED 23 | 24 | #include 25 | #include 26 | #include "sacd_media.h" 27 | 28 | using namespace std; 29 | 30 | enum area_id_e {AREA_BOTH = 0, AREA_TWOCH = 1, AREA_MULCH = 2}; 31 | enum frame_type_e {FRAME_DSD = 0, FRAME_DST = 1, FRAME_INVALID = -1}; 32 | enum media_type_t {UNK_TYPE = 0, ISO_TYPE = 1, DSDIFF_TYPE = 2, DSF_TYPE = 3}; 33 | 34 | struct TrackDetails 35 | { 36 | string strArtist; 37 | string strTitle; 38 | int nChannels; 39 | }; 40 | 41 | class sacd_reader_t { 42 | public: 43 | sacd_reader_t() {} 44 | virtual ~sacd_reader_t() {} 45 | virtual int open(sacd_media_t* p_file) = 0; 46 | virtual bool close() = 0; 47 | virtual uint32_t get_track_count(area_id_e area_id = AREA_BOTH) = 0; 48 | virtual int get_channels() = 0; 49 | virtual int get_samplerate() = 0; 50 | virtual int get_framerate() = 0; 51 | virtual float getProgress() = 0; 52 | virtual bool is_dst() = 0; 53 | virtual string set_track(uint32_t track_number, area_id_e area_id = AREA_BOTH, uint32_t offset = 0) = 0; 54 | virtual bool read_frame(uint8_t* frame_data, size_t* frame_size, frame_type_e* frame_type) = 0; 55 | virtual void getTrackDetails(uint32_t track_number, area_id_e area_id, TrackDetails* cTrackDetails) = 0; 56 | }; 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /libsacd/scarletbook.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-2016 Robert Tari 3 | 4 | SACD Ripper - http://code.google.com/p/sacd-ripper/ 5 | Copyright 2010-2011 by respective authors. 6 | 7 | This file is part of SACD. 8 | 9 | SACD is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | SACD is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License 20 | along with SACD. If not, see . 21 | */ 22 | 23 | #include "scarletbook.h" 24 | 25 | const char* character_set[] = 26 | { 27 | "US-ASCII", 28 | "ISO646-JP", 29 | "ISO-8859-1", 30 | "SHIFT_JISX0213", 31 | "KSC5636", 32 | "GB2312", 33 | "BIG5", 34 | "ISO-8859-1" 35 | }; 36 | 37 | const char* album_genre[] = 38 | { 39 | "Not used", 40 | "Not defined", 41 | "Adult Contemporary", 42 | "Alternative Rock", 43 | "Children's Music", 44 | "Classical", 45 | "Contemporary Christian", 46 | "Country", 47 | "Dance", 48 | "Easy Listening", 49 | "Erotic", 50 | "Folk", 51 | "Gospel", 52 | "Hip Hop", 53 | "Jazz", 54 | "Latin", 55 | "Musical", 56 | "New Age", 57 | "Opera", 58 | "Operetta", 59 | "Pop Music", 60 | "RAP", 61 | "Reggae", 62 | "Rock Music", 63 | "Rhythm & Blues", 64 | "Sound Effects", 65 | "Sound Track", 66 | "Spoken Word", 67 | "World Music", 68 | "Blues" 69 | }; 70 | 71 | const char* album_category[] = 72 | { 73 | "Not used", 74 | "General", 75 | "Japanese" 76 | }; 77 | -------------------------------------------------------------------------------- /libsacd/scarletbook.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-2016 Robert Tari 3 | 4 | SACD Ripper - http://code.google.com/p/sacd-ripper/ 5 | Copyright 2010-2011 by respective authors. 6 | 7 | This file is part of SACD. 8 | 9 | SACD is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | SACD is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License 20 | along with SACD. If not, see . 21 | */ 22 | 23 | #ifndef _SCARLETBOOK_H_INCLUDED 24 | #define _SCARLETBOOK_H_INCLUDED 25 | 26 | #include 27 | #include 28 | 29 | using namespace std; 30 | 31 | #define SACD_LSN_SIZE 2048 32 | #define SACD_SAMPLING_FREQUENCY 2822400 33 | #define START_OF_FILE_SYSTEM_AREA 0 34 | #define START_OF_MASTER_TOC 510 35 | #define MASTER_TOC_LEN 10 36 | #define MAX_AREA_TOC_SIZE_LSN 96 37 | #define MAX_LANGUAGE_COUNT 8 38 | #define MAX_CHANNEL_COUNT 6 39 | #define SAMPLES_PER_FRAME 588 40 | #define FRAME_SIZE_64 (SAMPLES_PER_FRAME * 64 / 8) 41 | #define SUPPORTED_VERSION_MAJOR 1 42 | #define SUPPORTED_VERSION_MINOR 20 43 | #define MAX_GENRE_COUNT 29 44 | #define MAX_CATEGORY_COUNT 3 45 | 46 | enum frame_format_t 47 | { 48 | FRAME_FORMAT_DST = 0, 49 | FRAME_FORMAT_DSD_3_IN_14 = 2, 50 | FRAME_FORMAT_DSD_3_IN_16 = 3 51 | }; 52 | 53 | enum character_set_t 54 | { 55 | CHAR_SET_UNKNOWN = 0, 56 | CHAR_SET_ISO646 = 1, // ISO 646 (IRV), no escape sequences allowed 57 | CHAR_SET_ISO8859_1 = 2, // ISO 8859-1, no escape sequences allowed 58 | CHAR_SET_RIS506 = 3, // MusicShiftJIS, per RIS-506 (RIAJ), Music Shift-JIS Kanji 59 | CHAR_SET_KSC5601 = 4, // Korean KSC 5601-1987 60 | CHAR_SET_GB2312 = 5, // Chinese GB 2312-80 61 | CHAR_SET_BIG5 = 6, // Big5 62 | CHAR_SET_ISO8859_1_ESC = 7 // ISO 8859-1, single byte set escape sequences allowed 63 | }; 64 | 65 | // string representation for character sets 66 | extern const char* character_set[]; 67 | 68 | extern const char* album_genre[]; 69 | 70 | enum genre_t 71 | { 72 | GENRE_NOT_USED = 0, // 12 73 | GENRE_NOT_DEFINED = 1, // 12 74 | GENRE_ADULT_CONTEMPORARY = 2, // 12 75 | GENRE_ALTERNATIVE_ROCK = 3, // 40 76 | GENRE_CHILDRENS_MUSIC = 4, // 12 77 | GENRE_CLASSICAL = 5, // 32 78 | GENRE_CONTEMPORARY_CHRISTIAN = 6, // 140 79 | GENRE_COUNTRY = 7, // 2 80 | GENRE_DANCE = 8, // 3 81 | GENRE_EASY_LISTENING = 9, // 98 82 | GENRE_EROTIC = 10, // 12 83 | GENRE_FOLK = 11, // 80 84 | GENRE_GOSPEL = 12, // 38 85 | GENRE_HIP_HOP = 13, // 7 86 | GENRE_JAZZ = 14, // 8 87 | GENRE_LATIN = 15, // 86 88 | GENRE_MUSICAL = 16, // 77 89 | GENRE_NEW_AGE = 17, // 10 90 | GENRE_OPERA = 18, // 103 91 | GENRE_OPERETTA = 19, // 104 92 | GENRE_POP_MUSIC = 20, // 13 93 | GENRE_RAP = 21, // 15 94 | GENRE_REGGAE = 22, // 16 95 | GENRE_ROCK_MUSIC = 23, // 17 96 | GENRE_RHYTHM_AND_BLUES = 24, // 14 97 | GENRE_SOUND_EFFECTS = 25, // 37 98 | GENRE_SOUND_TRACK = 26, // 24 99 | GENRE_SPOKEN_WORD = 27, // 101 100 | GENRE_WORLD_MUSIC = 28, // 12 101 | GENRE_BLUES = 29 // 0 102 | }; 103 | 104 | enum category_t 105 | { 106 | CATEGORY_NOT_USED = 0, 107 | CATEGORY_GENERAL = 1, 108 | CATEGORY_JAPANESE = 2 109 | }; 110 | 111 | extern const char* album_category[]; 112 | 113 | enum track_type_t 114 | { 115 | TRACK_TYPE_TITLE = 0x01, 116 | TRACK_TYPE_PERFORMER = 0x02, 117 | TRACK_TYPE_SONGWRITER = 0x03, 118 | TRACK_TYPE_COMPOSER = 0x04, 119 | TRACK_TYPE_ARRANGER = 0x05, 120 | TRACK_TYPE_MESSAGE = 0x06, 121 | TRACK_TYPE_EXTRA_MESSAGE = 0x07, 122 | TRACK_TYPE_TITLE_PHONETIC = 0x81, 123 | TRACK_TYPE_PERFORMER_PHONETIC = 0x82, 124 | TRACK_TYPE_SONGWRITER_PHONETIC = 0x83, 125 | TRACK_TYPE_COMPOSER_PHONETIC = 0x84, 126 | TRACK_TYPE_ARRANGER_PHONETIC = 0x85, 127 | TRACK_TYPE_MESSAGE_PHONETIC = 0x86, 128 | TRACK_TYPE_EXTRA_MESSAGE_PHONETIC = 0x87 129 | }; 130 | 131 | #pragma pack(1) 132 | 133 | // Common 134 | // The following structures are used in both the Master and area TOCs. 135 | // Genre Information. 136 | typedef struct 137 | { 138 | uint8_t category; // category_t 139 | uint16_t reserved; 140 | uint8_t genre; // genre_t 141 | } 142 | genre_table_t; 143 | 144 | //Language & character set 145 | typedef struct 146 | { 147 | char language_code[2]; // ISO639-2 Language code 148 | uint8_t character_set; // char_set_t, 1 (ISO 646) 149 | uint8_t reserved; 150 | } locale_table_t; 151 | 152 | // Master TOC 153 | // The following structures are needed for Master TOC information. 154 | typedef struct 155 | { 156 | char id[8]; // SACDMTOC 157 | 158 | struct 159 | { 160 | uint8_t major; 161 | uint8_t minor; 162 | } version; // 1.20 / 0x0114 163 | 164 | uint8_t reserved01[6]; 165 | uint16_t album_set_size; 166 | uint16_t album_sequence_number; 167 | uint8_t reserved02[4]; 168 | char album_catalog_number[16]; // 0x00 when empty, else padded with spaces for short strings 169 | genre_table_t album_genre[4]; 170 | uint8_t reserved03[8]; 171 | uint32_t area_1_toc_1_start; 172 | uint32_t area_1_toc_2_start; 173 | uint32_t area_2_toc_1_start; 174 | uint32_t area_2_toc_2_start; 175 | uint8_t disc_type_reserved : 7; 176 | uint8_t disc_type_hybrid : 1; 177 | uint8_t reserved04[3]; 178 | uint16_t area_1_toc_size; 179 | uint16_t area_2_toc_size; 180 | char disc_catalog_number[16]; // 0x00 when empty, else padded with spaces for short strings 181 | genre_table_t disc_genre[4]; 182 | uint16_t disc_date_year; 183 | uint8_t disc_date_month; 184 | uint8_t disc_date_day; 185 | uint8_t reserved05[4]; 186 | uint8_t text_area_count; 187 | uint8_t reserved06[7]; 188 | locale_table_t locales[MAX_LANGUAGE_COUNT]; 189 | } master_toc_t; 190 | 191 | // Master Album Information 192 | typedef struct 193 | { 194 | char id[8]; // SACDText 195 | uint8_t reserved[8]; 196 | uint16_t album_title_position; 197 | uint16_t album_artist_position; 198 | uint16_t album_publisher_position; 199 | uint16_t album_copyright_position; 200 | uint16_t album_title_phonetic_position; 201 | uint16_t album_artist_phonetic_position; 202 | uint16_t album_publisher_phonetic_position; 203 | uint16_t album_copyright_phonetic_position; 204 | uint16_t disc_title_position; 205 | uint16_t disc_artist_position; 206 | uint16_t disc_publisher_position; 207 | uint16_t disc_copyright_position; 208 | uint16_t disc_title_phonetic_position; 209 | uint16_t disc_artist_phonetic_position; 210 | uint16_t disc_publisher_phonetic_position; 211 | uint16_t disc_copyright_phonetic_position; 212 | uint8_t data[2000]; 213 | } master_sacd_text_t; 214 | 215 | typedef struct 216 | { 217 | string album_title; 218 | string album_artist; 219 | string album_publisher; 220 | string album_copyright; 221 | string album_title_phonetic; 222 | string album_artist_phonetic; 223 | string album_publisher_phonetic; 224 | string album_copyright_phonetic; 225 | string disc_title; 226 | string disc_artist; 227 | string disc_publisher; 228 | string disc_copyright; 229 | string disc_title_phonetic; 230 | string disc_artist_phonetic; 231 | string disc_publisher_phonetic; 232 | string disc_copyright_phonetic; 233 | } master_text_t; 234 | 235 | // Unknown Structure 236 | typedef struct 237 | { 238 | char id[8]; // SACD_Man, manufacturer information 239 | uint8_t information[2040]; 240 | } master_man_t; 241 | 242 | // Area TOC 243 | // The following structures are needed for Area TOC information. 244 | typedef struct 245 | { 246 | char id[8]; // TWOCHTOC or MULCHTOC 247 | 248 | struct 249 | { 250 | uint8_t major; 251 | uint8_t minor; 252 | } version; // 1.20 / 0x0114 253 | 254 | uint16_t size; // ex. 40 (total size of TOC) 255 | uint8_t reserved01[4]; 256 | uint32_t max_byte_rate; 257 | uint8_t sample_frequency; // 0x04 = (64 * 44.1 kHz) (physically there can be no other values, or..? :) 258 | uint8_t frame_format : 4; 259 | uint8_t reserved02 : 4; 260 | uint8_t reserved03[10]; 261 | uint8_t channel_count; 262 | uint8_t loudspeaker_config : 5; 263 | uint8_t extra_settings : 3; 264 | uint8_t max_available_channels; 265 | uint8_t area_mute_flags; 266 | uint8_t reserved04[12]; 267 | uint8_t track_attribute : 4; 268 | uint8_t reserved05 : 4; 269 | uint8_t reserved06[15]; 270 | 271 | struct 272 | { 273 | uint8_t minutes; 274 | uint8_t seconds; 275 | uint8_t frames; 276 | } total_playtime; 277 | 278 | uint8_t reserved07; 279 | uint8_t track_offset; 280 | uint8_t track_count; 281 | uint8_t reserved08[2]; 282 | uint32_t track_start; 283 | uint32_t track_end; 284 | uint8_t text_area_count; 285 | uint8_t reserved09[7]; 286 | locale_table_t languages[10]; 287 | uint16_t track_text_offset; 288 | uint16_t index_list_offset; 289 | uint16_t access_list_offset; 290 | uint8_t reserved10[10]; 291 | uint16_t area_description_offset; 292 | uint16_t copyright_offset; 293 | uint16_t area_description_phonetic_offset; 294 | uint16_t copyright_phonetic_offset; 295 | uint8_t data[1896]; 296 | } area_toc_t; 297 | 298 | typedef struct 299 | { 300 | string track_type_title; 301 | string track_type_performer; 302 | string track_type_songwriter; 303 | string track_type_composer; 304 | string track_type_arranger; 305 | string track_type_message; 306 | string track_type_extra_message; 307 | string track_type_title_phonetic; 308 | string track_type_performer_phonetic; 309 | string track_type_songwriter_phonetic; 310 | string track_type_composer_phonetic; 311 | string track_type_arranger_phonetic; 312 | string track_type_message_phonetic; 313 | string track_type_extra_message_phonetic; 314 | } area_track_text_t; 315 | 316 | typedef struct 317 | { 318 | char id[8]; // SACDTTxt, Track Text 319 | uint16_t track_text_position[1]; 320 | } area_text_t; 321 | 322 | typedef struct 323 | { 324 | char country_code[2]; 325 | char owner_code[3]; 326 | char recording_year[2]; 327 | char designation_code[5]; 328 | } isrc_t; 329 | 330 | typedef struct 331 | { 332 | char id[8]; // SACD_IGL, ISRC and Genre List 333 | isrc_t isrc[255]; 334 | uint32_t reserved; 335 | genre_table_t track_genre[255]; 336 | } area_isrc_genre_t; 337 | 338 | typedef struct 339 | { 340 | char id[8]; // SACD_ACC, Access List 341 | uint16_t entry_count; 342 | uint8_t main_step_size; 343 | uint8_t reserved01[5]; 344 | uint8_t main_access_list[6550][5]; 345 | uint8_t reserved02[2]; 346 | uint8_t detailed_access_list[32768]; 347 | } area_access_list_t; 348 | 349 | typedef struct 350 | { 351 | char id[8]; // SACDTRL1 352 | uint32_t track_start_lsn[255]; 353 | uint32_t track_length_lsn[255]; 354 | } area_tracklist_offset_t; 355 | 356 | typedef struct 357 | { 358 | uint8_t minutes; 359 | uint8_t seconds; 360 | uint8_t frames; 361 | uint8_t reserved : 5; 362 | uint8_t extra_use : 3; 363 | } area_tracklist_time_start_t; 364 | 365 | typedef struct 366 | { 367 | uint8_t minutes; 368 | uint8_t seconds; 369 | uint8_t frames; 370 | uint8_t reserved : 3; 371 | uint8_t track_flags_tmf1 : 1; 372 | uint8_t track_flags_tmf2 : 1; 373 | uint8_t track_flags_tmf3 : 1; 374 | uint8_t track_flags_tmf4 : 1; 375 | uint8_t track_flags_ilp : 1; 376 | } area_tracklist_time_duration_t; 377 | 378 | typedef struct 379 | { 380 | char id[8]; // SACDTRL2 381 | area_tracklist_time_start_t start[255]; 382 | area_tracklist_time_duration_t duration[255]; 383 | } area_tracklist_time_t; 384 | 385 | enum audio_packet_data_type_t 386 | { 387 | DATA_TYPE_AUDIO = 2, 388 | DATA_TYPE_SUPPLEMENTARY = 3, 389 | DATA_TYPE_PADDING = 7 390 | }; 391 | 392 | // It's no use to make a little & big endian struct. On little 393 | // endian systems this needs to be filled manually anyway. 394 | typedef struct 395 | { 396 | uint8_t frame_start : 1; 397 | uint8_t reserved : 1; 398 | uint8_t data_type : 3; 399 | uint16_t packet_length : 11; 400 | } audio_packet_info_t; 401 | 402 | #define AUDIO_PACKET_INFO_SIZE 2U 403 | 404 | typedef struct 405 | { 406 | struct 407 | { 408 | uint8_t minutes; 409 | uint8_t seconds; 410 | uint8_t frames; 411 | } timecode; 412 | 413 | // Note: the following byte is only filled 414 | // on DST encoded audio frames 415 | uint8_t channel_bit_3 : 1; 416 | uint8_t channel_bit_2 : 1; 417 | uint8_t sector_count : 5; 418 | uint8_t channel_bit_1 : 1; 419 | } audio_frame_info_t; 420 | 421 | #define AUDIO_FRAME_INFO_SIZE 4U 422 | 423 | typedef struct 424 | { 425 | uint8_t dst_encoded : 1; 426 | uint8_t reserved : 1; 427 | uint8_t frame_info_count : 3; 428 | uint8_t packet_info_count : 3; 429 | } audio_frame_header_t; 430 | 431 | #define AUDIO_SECTOR_HEADER_SIZE 1U 432 | 433 | typedef struct 434 | { 435 | audio_frame_header_t header; 436 | audio_packet_info_t packet[7]; 437 | audio_frame_info_t frame[7]; 438 | } audio_sector_t; 439 | 440 | typedef struct 441 | { 442 | uint8_t* area_data; 443 | area_toc_t* area_toc; 444 | area_tracklist_offset_t* area_tracklist_offset; 445 | area_tracklist_time_t* area_tracklist_time; 446 | area_text_t* area_text; 447 | area_track_text_t area_track_text[255]; // max of 255 supported tracks 448 | area_isrc_genre_t* area_isrc_genre; 449 | string description; 450 | string copyright; 451 | string description_phonetic; 452 | string copyright_phonetic; 453 | } scarletbook_area_t; 454 | 455 | typedef struct 456 | { 457 | void* sacd; // sacd_reader_t 458 | uint8_t* master_data; 459 | master_toc_t* master_toc; 460 | master_man_t* master_man; 461 | master_text_t master_text; 462 | int twoch_area_idx; 463 | int mulch_area_idx; 464 | int area_count; 465 | scarletbook_area_t area[2]; 466 | } scarletbook_handle_t; 467 | 468 | #pragma pack() 469 | #endif 470 | -------------------------------------------------------------------------------- /libsacd/version.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-2019 Robert Tari 3 | 4 | This file is part of SACD. 5 | 6 | SACD is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | SACD is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with SACD. If not, see . 18 | */ 19 | 20 | #define APPVERSION "19.7.16" 21 | 22 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-2019 Robert Tari 3 | Copyright 2012 Vladislav Goncharov 4 | Copyright 2011-2016 Maxim V.Anisiutkin 5 | 6 | This file is part of SACD. 7 | 8 | SACD is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | SACD is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with SACD. If not, see . 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "libsacd/sacd_reader.h" 34 | #include "libsacd/sacd_disc.h" 35 | #include "libsacd/sacd_dsdiff.h" 36 | #include "libsacd/sacd_dsf.h" 37 | #include "libsacd/version.h" 38 | #include "libdsd2pcm/dsd_pcm_converter_hq.h" 39 | #include "libdsd2pcm/dsd_pcm_converter_engine.h" 40 | #include "libdstdec/dst_decoder_mt.h" 41 | 42 | struct TrackInfo 43 | { 44 | int nTrack; 45 | area_id_e nArea; 46 | }; 47 | 48 | int g_nCPUs = 2; 49 | int g_nThreads = 2; 50 | vector g_arrQueue; 51 | pthread_mutex_t g_hMutex = PTHREAD_MUTEX_INITIALIZER; 52 | string g_strOut = ""; 53 | int g_StdOut = 0; 54 | int g_nSampleRate = 88200; 55 | bool g_bProgressLine = false; 56 | int g_nFinished = 0; 57 | area_id_e g_nArea = AREA_MULCH; 58 | 59 | void packageInt(unsigned char * buf, int offset, int num, int bytes) 60 | { 61 | buf[offset + 0] = (unsigned char)(num & 0xff); 62 | buf[offset + 1] = (unsigned char)((num >> 8) & 0xff); 63 | 64 | if (bytes == 4) 65 | { 66 | buf[offset + 2] = (unsigned char) ((num >> 0x10) & 0xff); 67 | buf[offset + 3] = (unsigned char) ((num >> 0x18) & 0xff); 68 | } 69 | } 70 | 71 | string toLower(const string& s) 72 | { 73 | string result; 74 | locale loc; 75 | 76 | for (unsigned int i = 0; i < s.length(); ++i) 77 | { 78 | result += tolower(s.at(i), loc); 79 | } 80 | 81 | return result; 82 | } 83 | 84 | class SACD 85 | { 86 | private: 87 | 88 | sacd_media_t* m_pSacdMedia; 89 | dst_decoder_t* m_pDstDecoder; 90 | vector m_arrDstBuf; 91 | vector m_arrDsdBuf; 92 | vector m_arrPcmBuf; 93 | int m_nDsdBufSize; 94 | int m_nDstBufSize; 95 | dsdpcm_converter_hq* m_pDsdPcmConverter480; 96 | DSDPCMConverterEngine* m_pDsdPcmConverter441; 97 | int m_nDsdSamplerate; 98 | int m_nFramerate; 99 | int m_nPcmOutSamples; 100 | int m_nPcmOutDelta; 101 | 102 | void dsd2pcm(uint8_t* dsd_data, int dsd_samples, float* pcm_data) 103 | { 104 | 105 | if (m_pDsdPcmConverter480) 106 | { 107 | m_pDsdPcmConverter480->convert(dsd_data, dsd_samples, pcm_data); 108 | } 109 | else if (m_pDsdPcmConverter441) 110 | { 111 | m_pDsdPcmConverter441->convert(dsd_data, dsd_samples, pcm_data); 112 | } 113 | } 114 | 115 | void writeData(FILE * pFile, int nOffset, int nSamples) 116 | { 117 | int nFramesIn = nSamples * m_nPcmOutChannels; 118 | int nBytesOut = nFramesIn * 3; 119 | char * pSrc = (char*)(m_arrPcmBuf.data() + nOffset * m_nPcmOutChannels); 120 | char * pDst = new char[nBytesOut]; 121 | float fSample; 122 | int32_t nVal; 123 | int nFrame; 124 | int nOut = 0; 125 | 126 | for (nFrame = 0; nFrame < nFramesIn; nFrame++, pSrc += 4) 127 | { 128 | fSample = *(float*)(pSrc); 129 | fSample = MIN(fSample, 1.0); 130 | fSample = MAX(fSample, -1.0); 131 | fSample *= 8388608.0; 132 | 133 | /*const float DITHER_NOISE = rand() / (float)RAND_MAX - 0.5f; 134 | const float SHAPED_BS[] = { 2.033f, -2.165f, 1.959f, -1.590f, 0.6149f }; 135 | float mTriangleState = 0; 136 | int mPhase = 0; 137 | float mBuffer[8]; 138 | memset(mBuffer, 0, sizeof(float) * 8);*/ 139 | 140 | // 1 RectangleDither 141 | /*fSample = fSample - DITHER_NOISE;*/ 142 | 143 | // 2 TriangleDither 144 | /*float r = DITHER_NOISE; 145 | float result = fSample + r - mTriangleState; 146 | mTriangleState = r; 147 | fSample = result;*/ 148 | 149 | // 3 ShapedDither 150 | /*// Generate triangular dither, +-1 LSB, flat psd 151 | float r = DITHER_NOISE + DITHER_NOISE; 152 | 153 | // test for NaN and do the best we can with it 154 | if(fSample != fSample) 155 | fSample = 0; 156 | 157 | // Run FIR 158 | float xe = fSample + mBuffer[mPhase] * SHAPED_BS[0] 159 | + mBuffer[(mPhase - 1) & 7] * SHAPED_BS[1] 160 | + mBuffer[(mPhase - 2) & 7] * SHAPED_BS[2] 161 | + mBuffer[(mPhase - 3) & 7] * SHAPED_BS[3] 162 | + mBuffer[(mPhase - 4) & 7] * SHAPED_BS[4]; 163 | 164 | // Accumulate FIR and triangular noise 165 | float result = xe + r; 166 | 167 | // Roll buffer and store last error 168 | mPhase = (mPhase + 1) & 7; 169 | mBuffer[mPhase] = xe - lrintf(result); 170 | 171 | fSample = result;*/ 172 | 173 | nVal = lrintf(fSample); 174 | nVal = MIN(nVal, 8388607); 175 | nVal = MAX(nVal, -8388608); 176 | 177 | pDst[nOut++] = nVal; 178 | pDst[nOut++] = nVal >> 8; 179 | pDst[nOut++] = nVal >> 16; 180 | } 181 | 182 | fwrite(pDst, 1, nBytesOut, pFile); 183 | delete [] pDst; 184 | 185 | m_fProgress = m_pSacdReader->getProgress(); 186 | } 187 | 188 | public: 189 | 190 | int m_nTracks; 191 | float m_fProgress; 192 | int m_nPcmOutChannels; 193 | unsigned int m_nPcmOutChannelMap; 194 | sacd_reader_t* m_pSacdReader; 195 | bool m_bTrackCompleted; 196 | 197 | SACD() 198 | { 199 | m_pSacdMedia = nullptr; 200 | m_pSacdReader = nullptr; 201 | m_pDsdPcmConverter441 = nullptr; 202 | m_pDsdPcmConverter480 = nullptr; 203 | m_pDstDecoder = nullptr; 204 | m_fProgress = 0; 205 | m_nTracks = 0; 206 | m_nPcmOutSamples = 0; 207 | m_nPcmOutDelta = 0; 208 | } 209 | 210 | ~SACD() 211 | { 212 | if (m_pSacdReader) 213 | { 214 | delete m_pSacdReader; 215 | } 216 | 217 | if (m_pSacdMedia) 218 | { 219 | delete m_pSacdMedia; 220 | } 221 | 222 | if (m_pDsdPcmConverter441) 223 | { 224 | delete m_pDsdPcmConverter441; 225 | } 226 | else if (m_pDsdPcmConverter480) 227 | { 228 | delete m_pDsdPcmConverter480; 229 | } 230 | 231 | if (m_pDstDecoder) 232 | { 233 | delete m_pDstDecoder; 234 | } 235 | } 236 | 237 | int open(string p_path) 238 | { 239 | string ext = toLower(p_path.substr(p_path.length()-3, 3)); 240 | media_type_t tMediaType = UNK_TYPE; 241 | 242 | if (ext == "iso") 243 | { 244 | tMediaType = ISO_TYPE; 245 | } 246 | else if (ext == "dat") 247 | { 248 | tMediaType = ISO_TYPE; 249 | } 250 | else if (ext == "dff") 251 | { 252 | tMediaType = DSDIFF_TYPE; 253 | } 254 | else if (ext == "dsf") 255 | { 256 | tMediaType = DSF_TYPE; 257 | } 258 | 259 | if (tMediaType == UNK_TYPE) 260 | { 261 | fprintf(stderr, "PANIC: exception_io_unsupported_format\n"); 262 | return 0; 263 | } 264 | 265 | m_pSacdMedia = new sacd_media_t(); 266 | 267 | if (!m_pSacdMedia) 268 | { 269 | fprintf(stderr, "PANIC: exception_overflow\n"); 270 | return 0; 271 | } 272 | 273 | switch (tMediaType) 274 | { 275 | case ISO_TYPE: 276 | m_pSacdReader = new sacd_disc_t; 277 | if (!m_pSacdReader) 278 | { 279 | fprintf(stderr, "PANIC: exception_overflow\n"); 280 | return 0; 281 | } 282 | break; 283 | case DSDIFF_TYPE: 284 | m_pSacdReader = new sacd_dsdiff_t; 285 | if (!m_pSacdReader) 286 | { 287 | fprintf(stderr, "PANIC: exception_overflow\n"); 288 | return 0; 289 | } 290 | break; 291 | case DSF_TYPE: 292 | m_pSacdReader = new sacd_dsf_t; 293 | if (!m_pSacdReader) 294 | { 295 | fprintf(stderr, "PANIC: exception_overflow\n"); 296 | return 0; 297 | } 298 | break; 299 | default: 300 | fprintf(stderr, "PANIC: exception_io_data\n"); 301 | return 0; 302 | break; 303 | } 304 | 305 | if (!m_pSacdMedia->open(p_path.c_str())) 306 | { 307 | fprintf(stderr, "PANIC: exception_io_data\n"); 308 | return 0; 309 | } 310 | 311 | if ((m_nTracks = m_pSacdReader->open(m_pSacdMedia)) == 0) 312 | { 313 | fprintf(stderr, "PANIC: Failed to parse SACD media\n"); 314 | return 0; 315 | } 316 | 317 | return m_nTracks; 318 | } 319 | 320 | string init(uint32_t nSubsong, int g_nSampleRate, area_id_e nArea) 321 | { 322 | if (m_pDsdPcmConverter441) 323 | { 324 | delete m_pDsdPcmConverter441; 325 | m_pDsdPcmConverter441 = nullptr; 326 | } 327 | else if (m_pDsdPcmConverter480) 328 | { 329 | delete m_pDsdPcmConverter480; 330 | m_pDsdPcmConverter480 = nullptr; 331 | } 332 | 333 | if (m_pDstDecoder) 334 | { 335 | delete m_pDstDecoder; 336 | m_pDstDecoder = nullptr; 337 | } 338 | 339 | string strFileName = m_pSacdReader->set_track(nSubsong, nArea, 0); 340 | m_nDsdSamplerate = m_pSacdReader->get_samplerate(); 341 | m_nFramerate = m_pSacdReader->get_framerate(); 342 | m_nPcmOutSamples = g_nSampleRate / m_nFramerate; 343 | m_nPcmOutChannels = m_pSacdReader->get_channels(); 344 | 345 | switch (m_nPcmOutChannels) 346 | { 347 | case 1: 348 | m_nPcmOutChannelMap = 1<<2; 349 | break; 350 | case 2: 351 | m_nPcmOutChannelMap = 1<<0 | 1<<1; 352 | break; 353 | case 3: 354 | m_nPcmOutChannelMap = 1<<0 | 1<<1 | 1<<2; 355 | break; 356 | case 4: 357 | m_nPcmOutChannelMap = 1<<0 | 1<<1 | 1<<4 | 1<<5; 358 | break; 359 | case 5: 360 | m_nPcmOutChannelMap = 1<<0 | 1<<1 | 1<<2 | 1<<4 | 1<<5; 361 | break; 362 | case 6: 363 | m_nPcmOutChannelMap = 1<<0 | 1<<1 | 1<<2 | 1<<3 | 1<<4 | 1<<5; 364 | break; 365 | default: 366 | m_nPcmOutChannelMap = 0; 367 | break; 368 | } 369 | 370 | m_nDstBufSize = m_nDsdBufSize = m_nDsdSamplerate / 8 / m_nFramerate * m_nPcmOutChannels; 371 | m_arrDsdBuf.resize(m_nDsdBufSize * g_nCPUs); 372 | m_arrDstBuf.resize(m_nDstBufSize * g_nCPUs); 373 | m_arrPcmBuf.resize(m_nPcmOutChannels * m_nPcmOutSamples); 374 | 375 | if (g_nSampleRate == 96000 or g_nSampleRate == 192000) 376 | { 377 | m_pDsdPcmConverter480 = new dsdpcm_converter_hq(); 378 | m_pDsdPcmConverter480->init(m_nPcmOutChannels, m_nDsdSamplerate, g_nSampleRate); 379 | } 380 | else 381 | { 382 | m_pDsdPcmConverter441 = new DSDPCMConverterEngine(); 383 | m_pDsdPcmConverter441->init(m_nPcmOutChannels, m_nFramerate, m_nDsdSamplerate, g_nSampleRate); 384 | } 385 | 386 | float fPcmOutDelay = 0.0f; 387 | 388 | if (m_pDsdPcmConverter480) 389 | { 390 | fPcmOutDelay = m_pDsdPcmConverter480->get_delay(); 391 | } 392 | else 393 | { 394 | fPcmOutDelay = m_pDsdPcmConverter441->get_delay(); 395 | } 396 | 397 | m_nPcmOutDelta = (int)(fPcmOutDelay - 0.5f);// + 0.5f originally 398 | 399 | if (m_nPcmOutDelta > m_nPcmOutSamples - 1) 400 | { 401 | m_nPcmOutDelta = m_nPcmOutSamples - 1; 402 | } 403 | 404 | m_bTrackCompleted = false; 405 | 406 | return strFileName; 407 | } 408 | 409 | void fixPcmStream(bool bIsEnd, float* pPcmData, int nPcmSamples) 410 | { 411 | if (!bIsEnd) 412 | { 413 | if (nPcmSamples > 1) 414 | { 415 | for (int ch = 0; ch < m_nPcmOutChannels; ch++) 416 | { 417 | pPcmData[0 * m_nPcmOutChannels + ch] = pPcmData[1 * m_nPcmOutChannels + ch]; 418 | } 419 | } 420 | } 421 | else 422 | { 423 | if (nPcmSamples > 1) 424 | { 425 | for (int ch = 0; ch < m_nPcmOutChannels; ch++) 426 | { 427 | pPcmData[(nPcmSamples - 1) * m_nPcmOutChannels + ch] = pPcmData[(nPcmSamples - 2) * m_nPcmOutChannels + ch]; 428 | } 429 | } 430 | } 431 | } 432 | 433 | bool decode(FILE* pFile) 434 | { 435 | if (m_bTrackCompleted) 436 | { 437 | return true; 438 | } 439 | 440 | uint8_t* pDsdData; 441 | uint8_t* pDstData; 442 | size_t nDsdSize = 0; 443 | size_t nDstSize = 0; 444 | int nThread = 0; 445 | 446 | while (1) 447 | { 448 | nThread = m_pDstDecoder ? m_pDstDecoder->slot_nr : 0; 449 | pDsdData = m_arrDsdBuf.data() + m_nDsdBufSize * nThread; 450 | pDstData = m_arrDstBuf.data() + m_nDstBufSize * nThread; 451 | nDstSize = m_nDstBufSize; 452 | frame_type_e nFrameType; 453 | 454 | if (m_pSacdReader->read_frame(pDstData, &nDstSize, &nFrameType)) 455 | { 456 | if (nDstSize > 0) 457 | { 458 | if (nFrameType == FRAME_INVALID) 459 | { 460 | nDstSize = m_nDstBufSize; 461 | memset(pDstData, DSD_SILENCE_BYTE, nDstSize); 462 | } 463 | 464 | if (nFrameType == FRAME_DST) 465 | { 466 | if (!m_pDstDecoder) 467 | { 468 | m_pDstDecoder = new dst_decoder_t(g_nCPUs); 469 | 470 | if (!m_pDstDecoder || m_pDstDecoder->init(m_nPcmOutChannels, m_nDsdSamplerate, m_nFramerate) != 0) 471 | { 472 | return true; 473 | } 474 | } 475 | 476 | m_pDstDecoder->decode(pDstData, nDstSize, &pDsdData, &nDsdSize); 477 | } 478 | else 479 | { 480 | pDsdData = pDstData; 481 | nDsdSize = nDstSize; 482 | } 483 | 484 | if (nDsdSize > 0) 485 | { 486 | int nRemoveSamples = 0; 487 | 488 | if ((m_pDsdPcmConverter480 && !m_pDsdPcmConverter480->is_convert_called()) || (m_pDsdPcmConverter441 && !m_pDsdPcmConverter441->is_convert_called())) 489 | { 490 | nRemoveSamples = m_nPcmOutDelta; 491 | } 492 | 493 | dsd2pcm(pDsdData, nDsdSize, m_arrPcmBuf.data()); 494 | 495 | if (nRemoveSamples > 0) 496 | { 497 | fixPcmStream(false, m_arrPcmBuf.data() + m_nPcmOutChannels * nRemoveSamples, m_nPcmOutSamples - nRemoveSamples); 498 | } 499 | 500 | writeData(pFile, nRemoveSamples, m_nPcmOutSamples - nRemoveSamples); 501 | 502 | return false; 503 | } 504 | } 505 | } 506 | else 507 | { 508 | break; 509 | } 510 | } 511 | 512 | pDsdData = nullptr; 513 | pDstData = nullptr; 514 | nDstSize = 0; 515 | 516 | if (m_pDstDecoder) 517 | { 518 | m_pDstDecoder->decode(pDstData, nDstSize, &pDsdData, &nDsdSize); 519 | } 520 | 521 | if (nDsdSize > 0) 522 | { 523 | dsd2pcm(pDsdData, nDsdSize, m_arrPcmBuf.data()); 524 | writeData(pFile, 0, m_nPcmOutSamples); 525 | 526 | return false; 527 | } 528 | 529 | if (m_nPcmOutDelta > 0) 530 | { 531 | dsd2pcm(nullptr, 0, m_arrPcmBuf.data()); 532 | fixPcmStream(true, m_arrPcmBuf.data(), m_nPcmOutDelta); 533 | writeData(pFile, 0, m_nPcmOutDelta); 534 | } 535 | 536 | m_bTrackCompleted = true; 537 | 538 | return true; 539 | } 540 | }; 541 | 542 | void * fnProgress (void* threadargs) 543 | { 544 | vector* arrSACD = (vector*)threadargs; 545 | 546 | while(1) 547 | { 548 | float fProgress = 0; 549 | int nTracks = (*arrSACD).at(0)->m_nTracks; 550 | 551 | for (int i = 0; i < g_nThreads; i++) 552 | { 553 | fProgress += (*arrSACD).at(i)->m_fProgress; 554 | } 555 | 556 | fProgress = MAX(((((float)nTracks - (float)MIN(g_nThreads, nTracks) - (float)g_arrQueue.size()) * 100.0) + fProgress) / (float)nTracks, 0); 557 | 558 | if (g_bProgressLine) 559 | { 560 | fprintf(stderr, "PROGRESS\t%.2f\n", fProgress); 561 | } 562 | else 563 | { 564 | fprintf(stderr, "\r%.2f%%", fProgress); 565 | } 566 | 567 | fflush(stdout); 568 | 569 | if (g_nFinished == nTracks) 570 | { 571 | break; 572 | } 573 | 574 | sleep(1); 575 | } 576 | 577 | return 0; 578 | } 579 | 580 | void * fnDecoder (void* threadargs) 581 | { 582 | SACD* pSACD = (SACD*)threadargs; 583 | 584 | while(!g_arrQueue.empty()) 585 | { 586 | pthread_mutex_lock(&g_hMutex); 587 | 588 | TrackInfo cTrackInfo = g_arrQueue.front(); 589 | g_arrQueue.erase(g_arrQueue.begin()); 590 | 591 | pthread_mutex_unlock(&g_hMutex); 592 | 593 | string strOutFile = g_strOut + pSACD->init(cTrackInfo.nTrack, g_nSampleRate, cTrackInfo.nArea); 594 | unsigned int nSize = 0x7fffffff; 595 | unsigned char arrHeader[68]; 596 | unsigned char arrFormat[2] = {0xFE, 0xFF}; 597 | unsigned char arrSubtype[16] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71}; 598 | 599 | memcpy (arrHeader, "RIFF", 4); 600 | packageInt (arrHeader, 4, nSize - 8, 4); 601 | memcpy (arrHeader + 8, "WAVE", 4); 602 | memcpy (arrHeader + 12, "fmt ", 4); 603 | packageInt (arrHeader, 16, 40, 4); 604 | memcpy (arrHeader + 20, arrFormat, 2); 605 | packageInt (arrHeader, 22, pSACD->m_nPcmOutChannels, 2); 606 | packageInt (arrHeader, 24, g_nSampleRate, 4); 607 | packageInt (arrHeader, 28, (g_nSampleRate * 24 * pSACD->m_nPcmOutChannels) / 8, 4); 608 | packageInt (arrHeader, 32, pSACD->m_nPcmOutChannels * 3, 2); 609 | packageInt (arrHeader, 34, 24, 2); 610 | packageInt (arrHeader, 36, 22, 2); 611 | packageInt (arrHeader, 38, 24, 2); 612 | packageInt (arrHeader, 40, pSACD->m_nPcmOutChannelMap, 4); 613 | memcpy (arrHeader + 44, arrSubtype, 16); 614 | memcpy (arrHeader + 60, "data", 4); 615 | packageInt (arrHeader, 64, nSize - 68, 4); 616 | FILE * pFile = (g_StdOut == 0) ? fopen(strOutFile.data(), "wb") : stdout; 617 | if (pFile == NULL) 618 | { 619 | fprintf(stderr, "ERROR: Failed to open %s\n", strOutFile.data()); 620 | return 0; 621 | } 622 | fwrite(arrHeader, 1, 68, pFile); 623 | 624 | bool bDone = false; 625 | 626 | while (!bDone || !pSACD->m_bTrackCompleted) 627 | { 628 | bDone = pSACD->decode(pFile); 629 | } 630 | 631 | nSize = ftell (pFile); 632 | packageInt (arrHeader, 4, nSize - 8, 4); 633 | packageInt (arrHeader, 64, nSize - 68, 4); 634 | fseek (pFile, 0, SEEK_SET); 635 | fwrite (arrHeader, 1, 68, pFile); 636 | fclose(pFile); 637 | 638 | if (g_bProgressLine) 639 | { 640 | fprintf(stderr, "FILE\t%s\t%.2i\t%.2i\n", strOutFile.data(), cTrackInfo.nTrack + 1, pSACD->m_nTracks); 641 | } 642 | 643 | g_nFinished++; 644 | } 645 | 646 | return 0; 647 | } 648 | 649 | int main(int argc, char* argv[]) 650 | { 651 | int nCPUs = sysconf(_SC_NPROCESSORS_ONLN); 652 | 653 | if (nCPUs < 2) 654 | { 655 | nCPUs = thread::hardware_concurrency(); 656 | } 657 | 658 | if (nCPUs > 2) 659 | { 660 | g_nCPUs = nCPUs; 661 | } 662 | 663 | string strIn = ""; 664 | char strPath[PATH_MAX]; 665 | int nOpt; 666 | bool bPrintDetails = false; 667 | bool bPrintHelp = false; 668 | 669 | const char strHelpText[] = 670 | "\n" 671 | "Usage: sacd -i infile [-o outdir] [options]\n\n" 672 | " -i, --infile : Specify the input file (*.iso, *.dsf, *.dff)\n" 673 | " -o, --outdir : The folder to write the WAVE files to. If you omit\n" 674 | " this, the files will be placed in the input file's\n" 675 | " directory\n" 676 | " -c, --stdout : Stdout output (for pipe), sample:\n" 677 | " sacd -i file.dsf -c | play -\n" 678 | " -r, --rate : The output samplerate.\n" 679 | " Valid rates are: 88200, 96000, 176400 and 192000.\n" 680 | " If you omit this, 88.2KHz will be used.\n" 681 | " -s, --stereo : Only extract the 2-channel area if it exists.\n" 682 | " If you omit this, the multichannel area will have priority.\n" 683 | " -p, --progress : Display progress to new lines. Use this if you intend\n" 684 | " to parse the output through a script. This option only\n" 685 | " lists either one progress percentage per line, or one\n" 686 | " status/error message.\n" 687 | " -d, --details : Show detailed information about the input\n" 688 | " -h, --help : Show this help message\n\n"; 689 | 690 | const struct option tOptionsTable[] = 691 | { 692 | {"infile", required_argument, NULL, 'i' }, 693 | {"outdir", required_argument, NULL, 'o' }, 694 | {"stdout", no_argument, NULL, 'c'}, 695 | {"rate", required_argument, NULL, 'r' }, 696 | {"stereo", no_argument, NULL, 's'}, 697 | {"progress", no_argument, NULL, 'p'}, 698 | {"details", no_argument, NULL, 'd'}, 699 | {"help", no_argument, NULL, 'h' }, 700 | { NULL, 0, NULL, 0 } 701 | }; 702 | 703 | while ((nOpt = getopt_long(argc, argv, "i:o:cr:spdh", tOptionsTable, NULL)) >= 0) 704 | { 705 | switch (nOpt) 706 | { 707 | case 'i': 708 | strIn = optarg; 709 | break; 710 | case 'o': 711 | g_strOut = optarg; 712 | break; 713 | case 'c': 714 | g_StdOut = 1; 715 | break; 716 | case 'r': 717 | { 718 | string s = optarg; 719 | 720 | if (s == "88200" || s == "96000" || s == "176400" || s == "192000") 721 | { 722 | g_nSampleRate = stoi(optarg); 723 | } 724 | else 725 | { 726 | fprintf(stderr, "PANIC: Invalid samplerate\n"); 727 | return 0; 728 | } 729 | break; 730 | } 731 | case 's': 732 | g_nArea = AREA_TWOCH; 733 | break; 734 | case 'p': 735 | g_bProgressLine = true; 736 | break; 737 | case 'd': 738 | bPrintDetails = true; 739 | break; 740 | default: 741 | bPrintHelp = true; 742 | break; 743 | } 744 | } 745 | 746 | if (bPrintHelp || argc == 1 || strIn.empty()) 747 | { 748 | fprintf(stderr, g_bProgressLine ? "PANIC: Invalid command-line syntax\n" : strHelpText); 749 | return 0; 750 | } 751 | 752 | struct stat tStat; 753 | 754 | if (stat(strIn.c_str(), &tStat) == -1 || !S_ISREG(tStat.st_mode)) 755 | { 756 | fprintf(stderr, "PANIC: Input file does not exist\n"); 757 | return 0; 758 | } 759 | 760 | strIn = realpath(strIn.data(), strPath); 761 | 762 | if (g_strOut.empty()) 763 | { 764 | g_strOut = strIn.substr(0, strIn.find_last_of("/") + 1); 765 | } 766 | 767 | if (g_strOut.empty() || stat(g_strOut.c_str(), &tStat) == -1 || !S_ISDIR(tStat.st_mode)) 768 | { 769 | fprintf(stderr, "PANIC: Output directory does not exist\n"); 770 | return 0; 771 | } 772 | 773 | g_strOut = realpath(g_strOut.data(), strPath); 774 | 775 | if (g_strOut.compare(g_strOut.size() - 1, 1, "/") != 0) 776 | { 777 | g_strOut += "/"; 778 | } 779 | 780 | SACD * pSacd = new SACD(); 781 | 782 | if (!pSacd->open(strIn)) 783 | { 784 | exit(1); 785 | } 786 | 787 | if (!g_bProgressLine) 788 | { 789 | fprintf(stderr, "\n\nsacd\n----\nCommand-line SACD decoder\nversion %s\n\n", APPVERSION); 790 | } 791 | 792 | int nTwoch = pSacd->m_pSacdReader->get_track_count(AREA_TWOCH); 793 | int nMulch = pSacd->m_pSacdReader->get_track_count(AREA_MULCH); 794 | 795 | if (bPrintDetails) 796 | { 797 | fprintf(stderr, "STEREO AREA TRACK LIST:\n"); 798 | fprintf(stderr, "-----------------------\n\n"); 799 | 800 | if (nTwoch > 0) 801 | { 802 | for (int i = 0; i < nTwoch; i++) 803 | { 804 | TrackDetails cTrackDetails; 805 | 806 | pSacd->m_pSacdReader->getTrackDetails(i, AREA_TWOCH, &cTrackDetails); 807 | 808 | fprintf(stderr, "\nTRACK: %i\n", i + 1); 809 | fprintf(stderr, "ARTIST: %s\n", cTrackDetails.strArtist.data()); 810 | fprintf(stderr, "TITLE: %s\n", cTrackDetails.strTitle.data()); 811 | fprintf(stderr, "CHANNELS: %i\n", cTrackDetails.nChannels); 812 | } 813 | } 814 | else 815 | { 816 | fprintf(stderr, "No tracks.\n\n"); 817 | } 818 | 819 | fprintf(stderr, "\nMULTICHANNEL AREA TRACK LIST:\n"); 820 | fprintf(stderr, "-----------------------------\n"); 821 | 822 | if (nMulch > 0) 823 | { 824 | for (int i = 0; i < nMulch; i++) 825 | { 826 | TrackDetails cTrackDetails; 827 | pSacd->m_pSacdReader->getTrackDetails(i, AREA_MULCH, &cTrackDetails); 828 | 829 | fprintf(stderr, "\nTRACK: %i\n", i + 1); 830 | fprintf(stderr, "ARTIST: %s\n", cTrackDetails.strArtist.data()); 831 | fprintf(stderr, "TITLE: %s\n", cTrackDetails.strTitle.data()); 832 | fprintf(stderr, "CHANNELS: %i\n", cTrackDetails.nChannels); 833 | } 834 | } 835 | else 836 | { 837 | fprintf(stderr, "\nNo tracks.\n\n"); 838 | } 839 | 840 | delete pSacd; 841 | return 0; 842 | } 843 | 844 | bool bWarn = false; 845 | area_id_e nArea; 846 | 847 | if (nMulch > 0 && nTwoch > nMulch && g_nArea != AREA_TWOCH) 848 | { 849 | nArea = AREA_BOTH; 850 | bWarn = true; 851 | } 852 | else if (nTwoch > 0 && nMulch > nTwoch && g_nArea != AREA_TWOCH) 853 | { 854 | nArea = AREA_BOTH; 855 | bWarn = true; 856 | } 857 | else if (nMulch > 0 && g_nArea != AREA_TWOCH) 858 | { 859 | nArea = AREA_MULCH; 860 | } 861 | else if (nTwoch > 0) 862 | { 863 | nArea = AREA_TWOCH; 864 | } 865 | else 866 | { 867 | nArea = AREA_MULCH; 868 | } 869 | 870 | if(nArea == AREA_MULCH || nArea == AREA_BOTH) 871 | { 872 | for (int i = 0; i < nMulch; i++) 873 | { 874 | TrackInfo cTrackInfo = {i, AREA_MULCH}; 875 | g_arrQueue.push_back(cTrackInfo); 876 | } 877 | } 878 | 879 | if(nArea == AREA_TWOCH || nArea == AREA_BOTH) 880 | { 881 | for (int i = 0; i < nTwoch; i++) 882 | { 883 | TrackInfo cTrackInfo = {i, AREA_TWOCH}; 884 | g_arrQueue.push_back(cTrackInfo); 885 | } 886 | } 887 | 888 | if(bWarn) 889 | { 890 | if (g_bProgressLine) 891 | { 892 | fprintf(stderr, "WARNINGThe multichannel and stereo areas have a different track count: extracting both.\n"); 893 | } 894 | else 895 | { 896 | fprintf(stderr, "WARNING: The multichannel and stereo areas have a different track count: extracting both.\n\n"); 897 | } 898 | } 899 | 900 | delete pSacd; 901 | 902 | g_nThreads = MIN(g_nCPUs, (int)g_arrQueue.size()); 903 | 904 | time_t nNow = time(0); 905 | pthread_t hThreadProgress; 906 | vector arrSACD(g_nThreads); 907 | vector arrThreads(g_nThreads); 908 | 909 | for (int i = 0; i < g_nThreads; i++) 910 | { 911 | arrSACD[i] = new SACD(); 912 | arrSACD[i]->open(strIn); 913 | pthread_create(&arrThreads[i], NULL, fnDecoder, arrSACD[i]); 914 | pthread_detach(arrThreads[i]); 915 | } 916 | 917 | pthread_create(&hThreadProgress, NULL, fnProgress, &arrSACD); 918 | pthread_join(hThreadProgress, NULL); 919 | pthread_mutex_destroy(&g_hMutex); 920 | 921 | for (int i = 0; i < g_nThreads; i++) 922 | { 923 | delete arrSACD[i]; 924 | } 925 | 926 | int nSeconds = time(0) - nNow; 927 | 928 | if (g_bProgressLine) 929 | { 930 | fprintf(stderr, "FINISHED\t%d\n", nSeconds); 931 | } 932 | else 933 | { 934 | fprintf(stderr, "\nFinished in %d seconds.\n\n", nSeconds); 935 | } 936 | 937 | return 0; 938 | } 939 | -------------------------------------------------------------------------------- /man/sacd.1: -------------------------------------------------------------------------------- 1 | .TH SACD 1 "08 Nov 2018" "18.6.25" "User Manual" 2 | 3 | .SH NAME 4 | sacd \- Super Audio CD decoder 5 | 6 | .SH SYNOPSIS 7 | sacd \fB-i\fP infile [\fB-o\fP outdir] [options] 8 | 9 | .SH OPTIONS 10 | .TP 11 | -i, --infile 12 | Specify the input file (*.iso, *.dsf, *.dff) 13 | .TP 14 | -o, --outdir 15 | The folder to write the WAVE files to. If you omit 16 | this, the files will be placed in the input file's 17 | directory 18 | .TP 19 | -c, --stdout 20 | Stdout output (for pipe), sample: 21 | .br 22 | sacd -i file.dsf -c | play - 23 | .TP 24 | -r, --rate 25 | The output samplerate. 26 | Valid rates are: 88200, 96000, 176400 and 192000. 27 | If you omit this, 96KHz will be used. 28 | .TP 29 | -p, --progress 30 | Display progress to new lines. Use this if you intend 31 | to parse the output through a script. This option only 32 | lists either one progress percentage per line, or one 33 | status/error message. 34 | .TP 35 | -s, --stereo 36 | Only extract the 2-channel area if it exists. 37 | If you omit this, the multichannel area will have priority. 38 | .TP 39 | -h, --help 40 | Show help message 41 | 42 | .SH AUTHOR 43 | Robert Tari 44 | .br 45 | https://tari.in/www/software/sacd/ 46 | --------------------------------------------------------------------------------