sox-devel@lists.sourceforge.net unofficial mirror
 help / color / mirror / code / Atom feed
* Review/merge request - RSO/RSF support
@ 2019-05-15 22:30 Jakub Vaněk
  2019-05-16 12:16 ` Måns Rullgård
  0 siblings, 1 reply; 3+ messages in thread
From: Jakub Vaněk @ 2019-05-15 22:30 UTC (permalink / raw)
  To: sox-devel

Hi all,

I've created a patch for supporting for the NXT RSO / EV3 RSF (Robot
Sound File) format. It is a format used on the stock firmware of the
LEGO NXT and EV3 programmable bricks. I thought that it would be nice
to have native support in SoX.

The format is capable of holding U8 PCM or ADPCM samples. Currently
decoding of both formats is implemented. However for encoding I have
disabled the ADPCM support, because the created files sound very wrong
on the real brick. U8 PCM mode on the other hand works well.

RSO support is implemented also in FFmpeg, just the RSF alias isn't.

I am posting the exported commit below. However, it is also available
from GitHub: https://github.com/JakubVanek/sox-rsf/commit/f906852451a2efc5b1129c7b34bfc378d3f4da62.patch

Regards,

Jakub Vanek

From f906852451a2efc5b1129c7b34bfc378d3f4da62 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Van=C4=9Bk?= <vanek.jakub4@seznam.cz>
Date: Wed, 15 May 2019 22:33:52 +0200
Subject: [PATCH] rso: add support for NXT RSO / EV3 RSF format

---
 FEATURES.in                   |   1 +
 msvc10/LibSoX.vcxproj         |   1 +
 msvc10/LibSoX.vcxproj.filters |   3 +
 msvc9/LibSoX.vcproj           |   4 +
 soxformat.7                   |   7 +
 src/CMakeLists.txt            |   1 +
 src/Makefile.am               |   2 +-
 src/formats.c                 |   2 +
 src/formats.h                 |   1 +
 src/rso.c                     | 233 ++++++++++++++++++++++++++++++++++
 10 files changed, 254 insertions(+), 1 deletion(-)
 create mode 100644 src/rso.c

diff --git a/FEATURES.in b/FEATURES.in
index 43ab6c69bbe233f3cc45c90e9ae97f5d3d549428..7c1f33a9f26850fb81190e37ae461da7b2d9537a
100644
--- a/FEATURES.in
+++ b/FEATURES.in
@@ -42,6 +42,7 @@ The current release handles the following audio file formats:
 * Maxis XA Audio files
 ** EA ADPCM (read support only, for now)
 * Pseudo formats that allow direct playing/recording from most audio devices
+* NXT RSO/EV3 RSF (Robot Sound File) files
 * The "null" pseudo-file that reads and writes from/to nowhere
 (:tableend:)

diff --git a/msvc10/LibSoX.vcxproj b/msvc10/LibSoX.vcxproj
index b2e9d5a3583f12d5cb9af2fdd5015d55e6eefa92..d00dbe6a551b9dc81968ac88db857ee11f299dfa
100644
--- a/msvc10/LibSoX.vcxproj
+++ b/msvc10/LibSoX.vcxproj
@@ -281,6 +281,7 @@
     <ClCompile Include="..\src\prc.c" />
     <ClCompile Include="..\src\pvf.c" />
     <ClCompile Include="..\src\raw-fmt.c" />
+    <ClCompile Include="..\src\rso.c" />
     <ClCompile Include="..\src\s1-fmt.c" />
     <ClCompile Include="..\src\s2-fmt.c" />
     <ClCompile Include="..\src\s3-fmt.c" />
diff --git a/msvc10/LibSoX.vcxproj.filters b/msvc10/LibSoX.vcxproj.filters
index f39449cadd1d22d207fc57c12f0ab2c08c6c6240..9ac27753686059c9511b7aee6e30171ec7d8e083
100644
--- a/msvc10/LibSoX.vcxproj.filters
+++ b/msvc10/LibSoX.vcxproj.filters
@@ -435,6 +435,9 @@
     <ClCompile Include="..\src\raw-fmt.c">
       <Filter>Format Sources</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\rso.c">
+      <Filter>Format Sources</Filter>
+    </ClCompile>
     <ClCompile Include="..\src\s1-fmt.c">
       <Filter>Format Sources</Filter>
     </ClCompile>
diff --git a/msvc9/LibSoX.vcproj b/msvc9/LibSoX.vcproj
index 362f8d772daa9501e061600e9725009fc331fdfe..11c9423478a02072436225262502c2714ce4f485
100644
--- a/msvc9/LibSoX.vcproj
+++ b/msvc9/LibSoX.vcproj
@@ -869,6 +869,10 @@
                 RelativePath="..\src\au.c"
                 >
             </File>
+            <File
+                RelativePath="..\src\rso.c"
+                >
+            </File>
             <File
                 RelativePath="..\src\avr.c"
                 >
diff --git a/soxformat.7 b/soxformat.7
index 5e53b9f9600d94dec11f445b379938afb2227409..9f60eac0ed3317225647132da218a1044da5ad77
100644
--- a/soxformat.7
+++ b/soxformat.7
@@ -652,6 +652,13 @@ and
 .BR sox (1)
 .BR \-d .
 .TP
+\fB.rso\fR, \fB.rsf\fR
+NXT RSO / EV3 Robot Sound Files. A simple file format used in the stock
+NXT and EV3 firmwares.
+The format can store either U8 or IMA ADPCM samples. However only the U8
+mode is enabled, as the IMA ADPCM mode has some problems.
+Also, only the 8000 Hz sample rate and a single channel are supported
+.TP
 .B .txw
 Yamaha TX-16W sampler.
 A file format from a Yamaha sampling keyboard which wrote IBM-PC
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index bde10d6a96c8dd4c95260c2158ac9901f51a0604..41a2ee4a51391ebc9e572e4422078386e1299c7a
100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -111,6 +111,7 @@ set(formats_srcs
   prc
   raw
   raw-fmt
+  rso
   s1-fmt
   s2-fmt
   s3-fmt
diff --git a/src/Makefile.am b/src/Makefile.am
index d5f6d125fb2822600eb56eab2a10f3eade28ecc7..ed547cd2555563bff3c15ab402eee67c2da36488
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 rso.c

 libsox_la_LIBADD += @GSM_LIBS@ @LIBGSM_LIBADD@
 libsox_la_LIBADD += @LPC10_LIBS@ @LIBLPC10_LIBADD@
diff --git a/src/formats.c b/src/formats.c
index f3efe764cbb2adb8514bec4c4d98ffa4f12daffa..a2e2da51a939b4b6fdbf2b4e73875adf6638c170
100644
--- a/src/formats.c
+++ b/src/formats.c
@@ -88,6 +88,8 @@ static char const * auto_detect_format(sox_format_t
* ft, char const * ext)
   CHECK(sf    , 0, 0, ""     , 0,  4, "\144\243\004\0")
   CHECK(sox   , 0, 0, ""     , 0,  4, ".SoX")
   CHECK(sox   , 0, 0, ""     , 0,  4, "XoS.")
+  CHECK(rso   , 0, 0, ""     , 0,  2, "\x01\x00")
+  CHECK(rso   , 0, 0, ""     , 0,  2, "\x01\x01")

   if (ext && !strcasecmp(ext, "snd"))
   CHECK(sndr  , 7, 1, ""     , 0,  2, "\0")
diff --git a/src/formats.h b/src/formats.h
index a42ce2703929ccf6f210089706f5924e612ccc33..e8341ac0863cadecc1f089163efc7cd286f3be01
100644
--- a/src/formats.h
+++ b/src/formats.h
@@ -62,6 +62,7 @@
   FORMAT(wav)
   FORMAT(wve)
   FORMAT(xa)
+  FORMAT(rso)

 /*--------------------- Plugin or static format handlers
---------------------*/

diff --git a/src/rso.c b/src/rso.c
new file mode 100644
index 0000000000000000000000000000000000000000..92256d130c63f79d1a8eb6ddf5db4197796b5f6b
--- /dev/null
+++ b/src/rso.c
@@ -0,0 +1,233 @@
+/* (c) 2019 SoX contributors
+ *
+ * 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
+ */
+
+#include "sox_i.h"
+#include "adpcms.h"
+#include "sox.h"
+#include "stdio.h"
+
+typedef struct {
+    adpcm_io_t adpcm;
+    sox_bool is_adpcm;
+} priv_t;
+
+#define RSO_MAGIC_U8     0x0100
+#define RSO_MAGIC_ADPCM  0x0101
+#define RSO_LENGTH_EMPTY 0x0000
+#define RSO_MODE_DEFAULT 0x0000
+
+enum {
+    LENGTH_ERROR = -1,
+    LENGTH_READY = 0,
+    LENGTH_MISSING = 1
+};
+
+static int lsx_rso_calc_length(sox_format_t *ft, priv_t *priv,
uint16_t *for_header) {
+    sox_uint64_t samples = ft->olength ? ft->olength : ft->signal.length;
+    sox_uint64_t length = 0;
+
+    if (samples) {
+        if (priv->is_adpcm) {
+            length = (samples + 1) / 2;
+        } else {
+            length = samples;
+        }
+        if (length > 65535) {
+            return LENGTH_ERROR;
+        } else {
+            *for_header = (uint16_t)length;
+            return LENGTH_READY;
+        }
+    } else {
+        return LENGTH_MISSING;
+    }
+}
+
+static int lsx_rso_startread(sox_format_t *ft) {
+    priv_t *priv = (priv_t*) ft->priv;
+    uint16_t hdr_magic = 0;
+    uint16_t hdr_bytes = 0;
+    uint16_t hdr_rate = 0;
+    uint16_t hdr_mode = 0;
+
+    sox_encoding_t sample_type;
+    uint64_t sample_count;
+    unsigned bits_per_sample;
+    int rval;
+
+    if (lsx_readw(ft, &hdr_magic) != SOX_SUCCESS ||
+        lsx_readw(ft, &hdr_bytes) != SOX_SUCCESS ||
+        lsx_readw(ft, &hdr_rate) != SOX_SUCCESS ||
+        lsx_readw(ft, &hdr_mode) != SOX_SUCCESS) {
+        return SOX_EOF;
+    }
+
+    if (hdr_magic == RSO_MAGIC_U8) {
+        priv->is_adpcm = sox_false;
+
+        sample_count = hdr_bytes * 1;
+        sample_type = SOX_ENCODING_UNSIGNED;
+        bits_per_sample = 8;
+
+    } else if (hdr_magic == RSO_MAGIC_ADPCM) {
+        priv->is_adpcm = sox_true;
+
+        sample_count = hdr_bytes * 2;
+        sample_type = SOX_ENCODING_IMA_ADPCM;
+        bits_per_sample = 4;
+
+    } else {
+        lsx_fail_errno(ft, SOX_EHDR, "`%s': unknown sample format",
ft->filename);
+        return SOX_EOF;
+    }
+
+    rval = lsx_check_read_params(ft,
+                                 1 /* channels */,
+                                 1.0 * hdr_rate,
+                                 sample_type,
+                                 bits_per_sample,
+                                 sample_count,
+                                 sox_true /* verify length */);
+    if (rval != SOX_SUCCESS) {
+        return rval;
+    }
+
+    if (priv->is_adpcm) {
+        return lsx_adpcm_ima_start(ft, &priv->adpcm);
+    } else {
+        return lsx_rawstartread(ft);
+    }
+}
+
+static size_t lsx_rso_read(sox_format_t *ft, sox_sample_t *buf, size_t len) {
+    priv_t *priv = (priv_t*) ft->priv;
+
+    if (priv->is_adpcm) {
+        return lsx_adpcm_read(ft, &priv->adpcm, buf, len);
+    } else {
+        return lsx_rawread(ft, buf, len);
+    }
+}
+
+static int lsx_rso_stopread(sox_format_t *ft) {
+    priv_t *priv = (priv_t*) ft->priv;
+
+    if (priv->is_adpcm) {
+        return lsx_adpcm_stopread(ft, &priv->adpcm);
+    }
+
+    return SOX_SUCCESS;
+}
+
+
+static int lsx_rso_startwrite(sox_format_t *ft) {
+    priv_t *priv = (priv_t*) ft->priv;
+    uint16_t magic;
+
+    if (ft->encoding.encoding == SOX_ENCODING_UNSIGNED) {
+        priv->is_adpcm = sox_false;
+        magic = RSO_MAGIC_U8;
+
+    } else if (ft->encoding.encoding == SOX_ENCODING_IMA_ADPCM) {
+        priv->is_adpcm = sox_true;
+        magic = RSO_MAGIC_ADPCM;
+
+    } else {
+        lsx_fail_errno(ft, SOX_EFMT, "`%s': sample encoding is not
supported", ft->filename);
+        return SOX_EOF;
+    }
+
+    if (lsx_writew(ft, magic)                     != SOX_SUCCESS ||
+        lsx_writew(ft, RSO_LENGTH_EMPTY)          != SOX_SUCCESS ||
+        lsx_writew(ft, (uint16_t)ft->signal.rate) != SOX_SUCCESS ||
+        lsx_writew(ft, RSO_MODE_DEFAULT)          != SOX_SUCCESS) {
+        return SOX_EOF;
+    }
+
+    ft->data_start = lsx_tell(ft);
+
+    if (priv->is_adpcm) {
+        return lsx_adpcm_ima_start(ft, &priv->adpcm);
+    } else {
+        return lsx_rawstartwrite(ft);
+    }
+}
+
+static size_t lsx_rso_write(sox_format_t *ft, const sox_sample_t
*buf, size_t len) {
+    priv_t *priv = (priv_t*) ft->priv;
+
+    if (priv->is_adpcm) {
+        return lsx_adpcm_write(ft, &priv->adpcm, buf, len);
+    } else {
+        return lsx_rawwrite(ft, buf, len);
+    }
+}
+
+static int lsx_rso_stopwrite(sox_format_t *ft) {
+    priv_t *priv = (priv_t*) ft->priv;
+    off_t target = (off_t)ft->data_start - 6;
+    uint16_t hdr_bytes = 0;
+
+    if (priv->is_adpcm) {
+        if (lsx_adpcm_stopwrite(ft, &priv->adpcm) != SOX_SUCCESS) {
+            lsx_warn("IMA ADPCM stop failed");
+            return SOX_EOF;
+        }
+    }
+
+    if (ft->seekable) {
+        int rval = lsx_rso_calc_length(ft, priv, &hdr_bytes);
+
+        if (rval == LENGTH_ERROR) {
+            lsx_warn("`%s': output file is too long, length header
will be truncated", ft->filename);
+            hdr_bytes = 0xFFFF;
+
+        } else if (rval == LENGTH_MISSING) {
+            lsx_warn("`%s': file length not available, file header
will indicate zero length", ft->filename);
+            hdr_bytes = 0x0000;
+        }
+
+        if (lsx_seeki(ft, target, SEEK_SET)  != SOX_SUCCESS ||
+            lsx_writew(ft, hdr_bytes)        != SOX_SUCCESS) {
+            lsx_warn("`%s': seek or header write failed", ft->filename);
+            return SOX_EOF;
+        }
+    } else {
+        lsx_warn("`%s': seeking is not possible, the output file will
have broken length header", ft->filename);
+    }
+
+    return SOX_SUCCESS;
+}
+
+LSX_FORMAT_HANDLER(rso)
+{
+  static char const * const names[] = {"rso", "rsf", NULL};
+  static unsigned const write_encodings[] = {
+      SOX_ENCODING_UNSIGNED, 8, 0,
+//    SOX_ENCODING_IMA_ADPCM, 4, 0, // playback broken on EV3?
+      0};
+  static sox_rate_t const sample_rates[] = {
+      8000.0, 0.0
+  };
+  static sox_format_handler_t handler = {SOX_LIB_VERSION_CODE,
+    "NXT RSO / EV3 Robot Sound File", names, SOX_FILE_MONO | SOX_FILE_BIG_END,
+    lsx_rso_startread, lsx_rso_read, lsx_rso_stopread,
+    lsx_rso_startwrite, lsx_rso_write, lsx_rso_stopwrite,
+    lsx_rawseek, write_encodings, sample_rates, sizeof(priv_t)
+  };
+  return &handler;
+}
-- 
2.17.1


_______________________________________________
SoX-devel mailing list
SoX-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sox-devel

^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2019-05-17 19:10 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-05-15 22:30 Review/merge request - RSO/RSF support Jakub Vaněk
2019-05-16 12:16 ` Måns Rullgård
2019-05-17 19:10   ` Review/merge request - RSO/RSF support - V2 Jakub Vaněk

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).