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

* Re: Review/merge request - RSO/RSF support
  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
  0 siblings, 1 reply; 3+ messages in thread
From: Måns Rullgård @ 2019-05-16 12:16 UTC (permalink / raw)
  To: Jakub Vaněk; +Cc: sox-devel

Jakub Vaněk <linuxtardis@gmail.com> writes:

> 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

Is there a specification, preferably official, for this format available
somewhere?  Could you provide a few samples?

> 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

Please use your actual name in copyright tags.  If non-trivial portions
of the code were written by someone else, they should also be mentioned,
along with some kind of assurance that they approve of the distribution
under the specified licence terms (LGPL).

I will not touch patches lacking clear provenance.

-- 
Måns Rullgård


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

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

* Re: Review/merge request - RSO/RSF support - V2
  2019-05-16 12:16 ` Måns Rullgård
@ 2019-05-17 19:10   ` Jakub Vaněk
  0 siblings, 0 replies; 3+ messages in thread
From: Jakub Vaněk @ 2019-05-17 19:10 UTC (permalink / raw)
  To: Måns Rullgård, sox-devel

Hi,

On Thu, May 16, 2019 at 2:16 PM Måns Rullgård <mans@mansr.com> wrote:
> 
> Jakub Vaněk <linuxtardis@gmail.com> writes:
> 
> > 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
> 
> Is there a specification, preferably official, for this format
> available
> somewhere?  Could you provide a few samples?

I don't think there is an official specification. However, there are
some sources where it is described:

RSO description: https://wiki.multimedia.cx/index.php/RSO
RSF (=RSO) decoder on EV3: https://github.com/mindboards/ev3sources/blob/78ebaf5b6f8fe31cc17aa5dce0f8e4916a4fc072/lms2012/c_sound/source/c_sound.c#L695
RSO encoder in FFmpeg: https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/rsoenc.c

I found the following samples online.

RSO samples:
http://samples.mplayerhq.hu/A-codecs/rso/

RSF samples:
https://github.com/mindboards/ev3sources/tree/master/lms2012/lms2012/doc/graphics/BITMAPS/SOUNDS

> 
> > diff --git a/src/rso.c b/src/rso.c
> > new file mode 100644
> > index
> > 0000000000000000000000000000000000000000..92256d130c63f79d1a8eb6ddf
> > 5db4197796b5f6b
> > --- /dev/null
> > +++ b/src/rso.c
> > @@ -0,0 +1,233 @@
> > +/* (c) 2019 SoX contributors
> 
> Please use your actual name in copyright tags.  If non-trivial
> portions
> of the code were written by someone else, they should also be
> mentioned,
> along with some kind of assurance that they approve of the
> distribution
> under the specified licence terms (LGPL).
> 
> I will not touch patches lacking clear provenance.

The code was written by me, however I took inspiration from AU and
similar formats. I have changed the header so that it lists my address.
I have also added LEGO(R) MINDSTORMS(R) to some places so that it is
clearer what NXT and EV3 are.

New patch is also at https://github.com/JakubVanek/sox-rsf/commit/57dbfa9664d9e3ae2bfb29150934027598e18377.patch

> 
> --
> Måns Rullgård

Jakub Vaněk


From 57dbfa9664d9e3ae2bfb29150934027598e18377 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Van=C4=9Bk?= <linuxtardis@gmail.com>
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                     | 235 ++++++++++++++++++++++++++++++++++
 10 files changed, 256 insertions(+), 1 deletion(-)
 create mode 100644 src/rso.c

diff --git a/FEATURES.in b/FEATURES.in
index 43ab6c69..444a153a 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
+* LEGO® MINDSTORMS® NXT RSO/EV3 RSF files
 * The "null" pseudo-file that reads and writes from/to nowhere
 (:tableend:)
 
diff --git a/msvc10/LibSoX.vcxproj b/msvc10/LibSoX.vcxproj
index b2e9d5a3..d00dbe6a 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 f39449ca..9ac27753 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 362f8d77..11c94234 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 5e53b9f9..4df79e24 100644
--- a/soxformat.7
+++ b/soxformat.7
@@ -652,6 +652,13 @@ and
 .BR sox (1)
 .BR \-d .
 .TP
+\fB.rso\fR, \fB.rsf\fR
+LEGO(R) MINDSTORMS(R) NXT RSO / EV3 RSF (Robot Sound File).
+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 problems on the EV3.
+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 bde10d6a..41a2ee4a 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 d5f6d125..ed547cd2 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 f3efe764..a2e2da51 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 a42ce270..e8341ac0 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 00000000..8332b290
--- /dev/null
+++ b/src/rso.c
@@ -0,0 +1,235 @@
+/* LEGO® MINDSTORMS® NXT RSO and EV3 RSF sound format
+ * (c) 2019 Jakub Vanek <linuxtardis@gmail.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
+ */
+
+#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,
+    "LEGO(R) MINDSTORMS(R) NXT RSO / EV3 RSF", 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).