Hi, On Thu, May 16, 2019 at 2:16 PM Måns Rullgård <m...@mansr.com> wrote: > > Jakub Vaněk <linuxtar...@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?= <linuxtar...@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 <linuxtar...@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