sox-devel@lists.sourceforge.net unofficial mirror
 help / color / mirror / code / Atom feed
From: Mans Rullgard <mans@mansr.com>
To: sox-devel@lists.sourceforge.net
Subject: [PATCH 2/6] Add DSF file support
Date: Wed, 16 Sep 2015 15:16:26 +0100	[thread overview]
Message-ID: <1442412990-20764-2-git-send-email-mans@mansr.com> (raw)
In-Reply-To: <1442412990-20764-1-git-send-email-mans@mansr.com>

This adds support for reading and writing DSF files as specified by
http://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf

The 1-bit DSD samples are simply unpacked to sox_sample_t values of
maximum amplitude.  The "rate" filter can be used directly on this to
obtain usable PCM samples.

DSF files may include an ID3v2 metadata tag, which is not handled here.
---
 msvc10/LibSoX.vcxproj         |   1 +
 msvc10/LibSoX.vcxproj.filters |   3 +
 soxformat.7                   |   4 +
 src/Makefile.am               |   2 +-
 src/dsf.c                     | 378 ++++++++++++++++++++++++++++++++++++++++++
 src/formats.h                 |   1 +
 6 files changed, 388 insertions(+), 1 deletion(-)
 create mode 100644 src/dsf.c

diff --git a/msvc10/LibSoX.vcxproj b/msvc10/LibSoX.vcxproj
index b2e9d5a..e257831 100644
--- a/msvc10/LibSoX.vcxproj
+++ b/msvc10/LibSoX.vcxproj
@@ -144,6 +144,7 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="..\src\dsf.c" />
     <ClCompile Include="..\src\example0.c">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
diff --git a/msvc10/LibSoX.vcxproj.filters b/msvc10/LibSoX.vcxproj.filters
index f39449c..6cc951c 100644
--- a/msvc10/LibSoX.vcxproj.filters
+++ b/msvc10/LibSoX.vcxproj.filters
@@ -597,5 +597,8 @@
     <ClCompile Include="..\src\downsample.c">
       <Filter>Effect Sources</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\dsf.c">
+      <Filter>Format Sources</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>
diff --git a/soxformat.7 b/soxformat.7
index 5e53b9f..685b27e 100644
--- a/soxformat.7
+++ b/soxformat.7
@@ -304,6 +304,10 @@ Example containing only 2 stereo samples of silence:
     0.00012481278	0	0
 .EE
 .TP
+.B .dsf
+DSD Stream File.  Format defined by Sony for storing 1-bit DSD data.
+Commonly used for online distribution of audiophile recordings.
+.TP
 \&\fB.dvms\fR, \fB.vms\fR
 Used in Germany to compress speech audio for voice mail.
 A self-describing variant of
diff --git a/src/Makefile.am b/src/Makefile.am
index 7cceaaf..462d46d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -121,7 +121,7 @@ libsox_la_SOURCES += raw-fmt.c s1-fmt.c s2-fmt.c s3-fmt.c \
   lu-fmt.c 8svx.c aiff-fmt.c aifc-fmt.c au.c avr.c cdr.c cvsd-fmt.c \
   dvms-fmt.c dat.c hcom.c htk.c maud.c prc.c sf.c smp.c \
   sounder.c soundtool.c sphere.c tx16w.c voc.c vox-fmt.c ima-fmt.c adpcm.c adpcm.h \
-  ima_rw.c ima_rw.h wav.c wve.c xa.c nulfile.c f4-fmt.c f8-fmt.c gsrt.c
+  ima_rw.c ima_rw.h wav.c wve.c xa.c nulfile.c f4-fmt.c f8-fmt.c gsrt.c dsf.c
 
 libsox_la_LIBADD += @GSM_LIBS@ @LIBGSM_LIBADD@
 libsox_la_LIBADD += @LPC10_LIBS@ @LIBLPC10_LIBADD@
diff --git a/src/dsf.c b/src/dsf.c
new file mode 100644
index 0000000..2f17c8d
--- /dev/null
+++ b/src/dsf.c
@@ -0,0 +1,378 @@
+/* DSF file support
+ *
+ * Copyright (c) 2015 Mans Rullgard <mans@mansr.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/* File format specification available at
+ * http://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
+ */
+
+#include <stdint.h>
+#include "sox_i.h"
+
+struct dsf {
+	uint64_t file_size;
+	uint64_t metadata;
+	uint32_t version;
+	uint32_t format_id;
+	uint32_t chan_type;
+	uint32_t chan_num;
+	uint32_t sfreq;
+	uint32_t bps;
+	uint64_t scount;
+	uint32_t block_size;
+
+	uint32_t block_pos;
+	uint32_t bit_pos;
+	uint8_t *block;
+	uint64_t read_samp;
+};
+
+#define TAG(a, b, c, d) ((a) | (b) << 8 | (c) << 16 | (d) << 24)
+
+#define DSF_TAG  TAG('D', 'S', 'D', ' ')
+#define FMT_TAG  TAG('f', 'm', 't', ' ')
+#define DATA_TAG TAG('d', 'a', 't', 'a')
+
+#define HEADER_SIZE (28 + 52 + 12)
+
+static int dsf_startread(sox_format_t *ft)
+{
+	struct dsf *dsf = ft->priv;
+	uint32_t magic;
+	uint64_t csize;
+	uint32_t v;
+
+	if (lsx_readdw(ft, &magic) || magic != DSF_TAG) {
+		lsx_fail_errno(ft, SOX_EHDR, "DSF signature not found");
+		return SOX_EHDR;
+	}
+
+	if (lsx_readqw(ft, &csize) || csize != 28) {
+		lsx_fail_errno(ft, SOX_EHDR, "invalid DSD chunk size");
+		return SOX_EHDR;
+	}
+
+	lsx_readqw(ft, &dsf->file_size);
+	lsx_readqw(ft, &dsf->metadata);
+
+	if (lsx_readdw(ft, &magic) || magic != FMT_TAG) {
+		lsx_fail_errno(ft, SOX_EHDR, "fmt chunk not found");
+		return SOX_EHDR;
+	}
+
+	if (lsx_readqw(ft, &csize) || csize != 52) {
+		lsx_fail_errno(ft, SOX_EHDR, "invalid fmt chunk size");
+		return SOX_EHDR;
+	}
+
+	if (lsx_readdw(ft, &dsf->version)   ||
+	    lsx_readdw(ft, &dsf->format_id) ||
+	    lsx_readdw(ft, &dsf->chan_type) ||
+	    lsx_readdw(ft, &dsf->chan_num)  ||
+	    lsx_readdw(ft, &dsf->sfreq)     ||
+	    lsx_readdw(ft, &dsf->bps)       ||
+	    lsx_readqw(ft, &dsf->scount)    ||
+	    lsx_readdw(ft, &dsf->block_size))
+		return SOX_EHDR;
+
+	if (lsx_readdw(ft, &v) || v) /* reserved */
+		return SOX_EHDR;
+
+	if (lsx_readdw(ft, &magic) || magic != DATA_TAG) {
+		lsx_fail_errno(ft, SOX_EHDR, "data chunk not found");
+		return SOX_EHDR;
+	}
+
+	if (lsx_readqw(ft, &csize) ||
+	    csize < 12 + dsf->block_size * dsf->chan_num) {
+		lsx_fail_errno(ft, SOX_EHDR, "invalid data chunk size");
+		return SOX_EHDR;
+	}
+
+	if (dsf->version != 1) {
+		lsx_fail_errno(ft, SOX_EHDR, "unknown format version %d",
+			       dsf->version);
+		return SOX_EHDR;
+	}
+
+	if (dsf->format_id != 0) {
+		lsx_fail_errno(ft, SOX_EFMT, "unknown format ID %d",
+			       dsf->format_id);
+		return SOX_EFMT;
+	}
+
+	if (dsf->chan_num < 1 || dsf->chan_num > 6) {
+		lsx_fail_errno(ft, SOX_EHDR, "invalid channel count %d",
+			       dsf->chan_num);
+		return SOX_EHDR;
+	}
+
+	if (dsf->bps != 1) {
+		lsx_fail_errno(ft, SOX_EFMT, "unsupported bit depth %d",
+			       dsf->bps);
+		return SOX_EFMT;
+	}
+
+	dsf->block = lsx_calloc(dsf->chan_num, (size_t)dsf->block_size);
+	if (!dsf->block)
+		return SOX_ENOMEM;
+
+	dsf->block_pos = dsf->block_size;
+
+	ft->signal.rate = dsf->sfreq;
+	ft->signal.channels = dsf->chan_num;
+	ft->signal.precision = 1;
+	ft->signal.length = dsf->scount * dsf->chan_num;
+
+	ft->encoding.encoding = SOX_ENCODING_DSD;
+	ft->encoding.bits_per_sample = 1;
+
+	return SOX_SUCCESS;
+}
+
+static void dsf_read_bits(struct dsf *dsf, sox_sample_t *buf, unsigned len)
+{
+	uint8_t *dsd = dsf->block + dsf->block_pos;
+	unsigned i, j;
+
+	for (i = 0; i < dsf->chan_num; i++) {
+		unsigned d = dsd[i * dsf->block_size];
+
+		for (j = 0; j < len; j++) {
+			buf[i + j * dsf->chan_num] = d & 1 ?
+				SOX_SAMPLE_MAX : -SOX_SAMPLE_MAX;
+			d >>= 1;
+		}
+	}
+}
+
+static size_t dsf_read(sox_format_t *ft, sox_sample_t *buf, size_t len)
+{
+	struct dsf *dsf = ft->priv;
+	uint64_t samp_left = dsf->scount - dsf->read_samp;
+	size_t rsamp = 0;
+
+	len /= dsf->chan_num;
+	len = min(len, samp_left);
+
+	while (len >= 8) {
+		if (dsf->block_pos >= dsf->block_size) {
+			size_t rlen = dsf->chan_num * dsf->block_size;
+			if (lsx_read_b_buf(ft, dsf->block, rlen) < rlen)
+				return rsamp * dsf->chan_num;
+			dsf->block_pos = 0;
+		}
+
+		dsf_read_bits(dsf, buf, 8);
+		buf += 8 * dsf->chan_num;
+		dsf->block_pos++;
+		rsamp += 8;
+		len -= 8;
+	}
+
+	if (len && samp_left < 8) {
+		dsf_read_bits(dsf, buf, (unsigned)len);
+		rsamp += len;
+	}
+
+	dsf->read_samp += rsamp;
+
+	return rsamp * dsf->chan_num;
+}
+
+static int dsf_stopread(sox_format_t *ft)
+{
+	struct dsf *dsf = ft->priv;
+
+	free(dsf->block);
+
+	return SOX_SUCCESS;
+}
+
+static int dsf_writeheader(sox_format_t *ft)
+{
+	struct dsf *dsf = ft->priv;
+	uint64_t data_size = dsf->file_size ? dsf->file_size - HEADER_SIZE : 0;
+
+	if (lsx_writedw(ft, DSF_TAG) ||
+	    lsx_writeqw(ft, (uint64_t)28) ||
+	    lsx_writeqw(ft, dsf->file_size) ||
+	    lsx_writeqw(ft, dsf->metadata) ||
+	    lsx_writedw(ft, FMT_TAG) ||
+	    lsx_writeqw(ft, (uint64_t)52) ||
+	    lsx_writedw(ft, dsf->version) ||
+	    lsx_writedw(ft, dsf->format_id) ||
+	    lsx_writedw(ft, dsf->chan_type) ||
+	    lsx_writedw(ft, dsf->chan_num) ||
+	    lsx_writedw(ft, dsf->sfreq) ||
+	    lsx_writedw(ft, dsf->bps) ||
+	    lsx_writeqw(ft, dsf->scount) ||
+	    lsx_writedw(ft, dsf->block_size) ||
+	    lsx_writedw(ft, 0) || /* reserved */
+	    lsx_writedw(ft, DATA_TAG) ||
+	    lsx_writeqw(ft, data_size + 12))
+		return SOX_EOF;
+
+	return SOX_SUCCESS;
+}
+
+static int dsf_startwrite(sox_format_t *ft)
+{
+	struct dsf *dsf = ft->priv;
+
+	dsf->version = 1;
+	dsf->format_id = 0;
+	dsf->chan_type = ft->signal.channels + (ft->signal.channels > 4);
+	dsf->chan_num = ft->signal.channels;
+	dsf->sfreq = ft->signal.rate;
+	dsf->bps = ft->encoding.bits_per_sample;
+	dsf->block_size = 4096;
+
+	dsf->block = lsx_calloc(dsf->chan_num, (size_t)dsf->block_size);
+	if (!dsf->block)
+		return SOX_ENOMEM;
+
+	return dsf_writeheader(ft);
+}
+
+static int dsf_write_buf(sox_format_t *ft)
+{
+	struct dsf *dsf = ft->priv;
+
+	if (dsf->block_pos == dsf->block_size) {
+		size_t wlen = dsf->chan_num * dsf->block_size;
+		if (lsx_write_b_buf(ft, dsf->block, wlen) < wlen)
+			return SOX_EOF;
+		dsf->block_pos = 0;
+		memset(dsf->block, 0, wlen);
+	}
+
+	return SOX_SUCCESS;
+}
+
+static void dsf_write_bits(struct dsf *dsf, const sox_sample_t *buf,
+			   unsigned start_bit, unsigned len)
+{
+	uint8_t *dsd = dsf->block + dsf->block_pos;
+	unsigned i, j;
+
+	for (i = 0; i < dsf->chan_num; i++) {
+		unsigned d = dsd[i * dsf->block_size];
+
+		for (j = start_bit; j < start_bit + len; j++) {
+			d |= (buf[i + j * dsf->chan_num] > 0) << j;
+		}
+
+		dsd[i * dsf->block_size] = d;
+	}
+}
+
+static size_t dsf_write(sox_format_t *ft, const sox_sample_t *buf, size_t len)
+{
+	struct dsf *dsf = ft->priv;
+	unsigned nchan = dsf->chan_num;
+	size_t wsamp = 0;
+
+	len /= nchan;
+
+	if (dsf->bit_pos) {
+		unsigned pre = min(len, 8 - dsf->bit_pos);
+
+		dsf_write_bits(dsf, buf, dsf->bit_pos, pre);
+		buf += pre * nchan;
+		wsamp += pre;
+		len -= pre;
+		dsf->bit_pos += pre;
+
+		if (dsf->bit_pos == 8) {
+			dsf->block_pos++;
+			dsf->bit_pos = 0;
+			if (dsf_write_buf(ft))
+				return 0;
+		}
+	}
+
+	while (len >= 8) {
+		dsf_write_bits(dsf, buf, 0, 8);
+		buf += 8 * nchan;
+		dsf->block_pos++;
+		wsamp += 8;
+		len -= 8;
+
+		if (dsf_write_buf(ft))
+			return wsamp * nchan;
+	}
+
+	if (len) {
+		dsf_write_bits(dsf, buf, 0, (unsigned)len);
+		wsamp += len;
+		dsf->bit_pos = len;
+	}
+
+	dsf->scount += wsamp;
+
+	return wsamp * nchan;
+}
+
+static int dsf_stopwrite(sox_format_t *ft)
+{
+	struct dsf *dsf = ft->priv;
+	int err = SOX_SUCCESS;
+
+	if (dsf->bit_pos)
+		dsf->block_pos++;
+
+	if (dsf->block_pos) {
+		size_t wlen = dsf->chan_num * dsf->block_size;
+		if (lsx_write_b_buf(ft, dsf->block, wlen) < wlen)
+			err = SOX_EOF;
+	}
+
+	free(dsf->block);
+
+	if (err)
+		return err;
+
+	dsf->file_size = lsx_tell(ft);
+
+	if (lsx_seeki(ft, (off_t)0, SEEK_SET)) {
+		lsx_fail_errno(ft, SOX_EOF,
+			       "error rewinding output to update header");
+		return SOX_EOF;
+	}
+
+	return dsf_writeheader(ft);
+}
+
+LSX_FORMAT_HANDLER(dsf)
+{
+	static char const * const names[] = { "dsf", NULL };
+	static unsigned const write_encodings[] = {
+		SOX_ENCODING_DSD, 1, 0,
+		0 };
+	static sox_format_handler_t const handler = {
+		SOX_LIB_VERSION_CODE,
+		"Container for DSD data",
+		names, SOX_FILE_LIT_END,
+		dsf_startread, dsf_read, dsf_stopread,
+		dsf_startwrite, dsf_write, dsf_stopwrite,
+		NULL, write_encodings, NULL,
+		sizeof(struct dsf)
+	};
+	return &handler;
+}
diff --git a/src/formats.h b/src/formats.h
index a42ce27..4701efd 100644
--- a/src/formats.h
+++ b/src/formats.h
@@ -26,6 +26,7 @@
   FORMAT(cvsd)
   FORMAT(cvu)
   FORMAT(dat)
+  FORMAT(dsf)
   FORMAT(dvms)
   FORMAT(f4)
   FORMAT(f8)
-- 
2.5.2


------------------------------------------------------------------------------
Monitor Your Dynamic Infrastructure at Any Scale With Datadog!
Get real-time metrics from all of your servers, apps and tools
in one place.
SourceForge users - Click here to start your Free Trial of Datadog now!
http://pubads.g.doubleclick.net/gampad/clk?id=241902991&iu=/4140

  reply	other threads:[~2015-09-16 14:16 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-09-16 14:16 [PATCH 1/6] Add SOX_ENCODING_DSD Mans Rullgard
2015-09-16 14:16 ` Mans Rullgard [this message]
2015-09-16 14:16 ` [PATCH 3/6] Add support for reading DSDIFF files Mans Rullgard
2015-09-16 14:16 ` [PATCH 4/6] Add macros for increasing data alignment Mans Rullgard
2015-12-20 12:41   ` Eric Wong
2015-12-20 13:54     ` Måns Rullgård
2015-12-21 10:55       ` Eric Wong
2015-12-21 11:37         ` Måns Rullgård
2015-12-21 18:33           ` Eric Wong
2015-12-21 18:37             ` Måns Rullgård
2015-09-16 14:16 ` [PATCH 5/6] Add a sigma-delta modulator for DSD encoding Mans Rullgard
2015-10-03 22:31   ` Eric Wong
2015-10-03 22:39     ` Måns Rullgård
2015-09-16 14:16 ` [PATCH 6/6] Add DSD over PCM (dop) effect Mans Rullgard
2015-12-19 12:09   ` Eric Wong
2015-12-19 12:14     ` Måns Rullgård
2015-12-20  5:04       ` Eric Wong
2015-12-20 13:44         ` Måns Rullgård

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://lists.sourceforge.net/lists/listinfo/sox-devel

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1442412990-20764-2-git-send-email-mans@mansr.com \
    --to=sox-devel@lists.sourceforge.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/sox.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).