summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcbdev <cb@cbcdn.com>2023-09-24 13:14:53 +0200
committercbdev <cb@cbcdn.com>2023-09-24 13:14:53 +0200
commit1a5f7b4f013e7294d40cd8539ae6d9e16ddf423c (patch)
tree32be7e9d81d9fa490e6ca6824d74c25d92092825
downloadwavextract-1a5f7b4f013e7294d40cd8539ae6d9e16ddf423c.tar.gz
wavextract-1a5f7b4f013e7294d40cd8539ae6d9e16ddf423c.tar.bz2
wavextract-1a5f7b4f013e7294d40cd8539ae6d9e16ddf423c.zip
Initial version for unpacking 16bit little-endian multitrack WAV
-rw-r--r--LICENSE.txt22
-rw-r--r--Makefile14
-rw-r--r--wavextract.c180
3 files changed, 216 insertions, 0 deletions
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..5827d3c
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,22 @@
+Copyright (c) 2021, cbdev/FJS
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..3c88a9d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,14 @@
+CFLAGS = -Wall -Wpedantic
+
+all: wavextract
+
+wavextract: wavextract.c
+
+wavextract.exe: export CC = x86_64-w64-mingw32-gcc
+wavextract.exe: wavextract.c
+ $(CC) $(CFLAGS) $(LDFLAGS) $< $(OBJS) $(LDLIBS) -o $@
+
+clean:
+ $(RM) *.o
+ $(RM) wavextract
+ $(RM) wavextract.exe
diff --git a/wavextract.c b/wavextract.c
new file mode 100644
index 0000000..4192f64
--- /dev/null
+++ b/wavextract.c
@@ -0,0 +1,180 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <math.h>
+#include <ctype.h>
+
+#define VERSION "0.1"
+
+#ifdef _WIN32
+ #define WIN32_LEAN_AND_MEAN
+ #include <windows.h>
+ #define PRIsize_t "Iu"
+ #define htole16(x) (x)
+ #define htole32(x) (x)
+#else
+ #define PRIsize_t "lu"
+#endif
+
+#pragma pack(push, 1)
+typedef struct /*_chunk_riff*/ {
+ char magic_riff[4];
+ uint32_t size;
+ char magic_wave[4];
+} hdr_riff_t;
+
+typedef struct /*_chunk_fmt_base*/ {
+ char magic[4];
+ uint32_t size;
+ uint16_t fmt;
+ uint16_t channels;
+ uint32_t sample_rate;
+ uint32_t byte_rate; //(samplerate * sampleBits * channels) / 8.
+ uint16_t bitdepth; //(sampleBits * channels) / 8
+ uint16_t samplebits;
+ //uint16_t extsize; //only present if fmt != 1
+} hdr_fmt_t;
+
+typedef struct /*_chunk_fact*/ {
+ char magic[4];
+ uint32_t size;
+ uint32_t samples;
+} hdr_fact_t;
+
+typedef struct /*_chunk_data*/ {
+ char magic[4];
+ uint32_t size;
+} hdr_data_t;
+#pragma pack(pop)
+
+static int usage(char* fn){
+ fprintf(stdout, "wavextract " VERSION " - Convert WAVE files to CSV sample data\n");
+ fprintf(stdout, "\tUsage: %s <file.wav>\n\n", fn);
+ //fprintf(stdout, "Supported wave formats: i8, i16 (default), i32, f32, nf32\n");
+ return EXIT_FAILURE;
+}
+
+int main(int argc, char** argv){
+ size_t n;
+ #ifdef _WIN32
+ _fmode = _O_BINARY;
+ #endif
+
+ if(argc < 2){
+ return usage(argv[0]);
+ }
+
+ fprintf(stderr, "Opening wave input file %s\n", argv[1]);
+ int source_fd = open(argv[1], O_RDONLY);
+ if(source_fd < 0){
+ fprintf(stderr, "Failed to open input file %s: %s\n", argv[1], strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ hdr_riff_t riff;
+ if(read(source_fd, &riff, sizeof(riff)) != sizeof(riff)){
+ close(source_fd);
+ fprintf(stderr, "Failed to read input data: %s\n", strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ if(memcmp(riff.magic_riff, "RIFF", 4) || memcmp(riff.magic_wave, "WAVE", 4)){
+ close(source_fd);
+ fprintf(stderr, "Invalid RIFF/WAVE magic\n");
+ return EXIT_FAILURE;
+ }
+
+ fprintf(stderr, "Chunk size: %d\n", riff.size);
+
+ hdr_fmt_t fmt;
+ uint16_t extra_data;
+ if(read(source_fd, &fmt, sizeof(fmt)) != sizeof(fmt)){
+ close(source_fd);
+ fprintf(stderr, "Failed to read input data: %s\n", strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ if(memcmp(fmt.magic, "fmt ", 4)){
+ close(source_fd);
+ fprintf(stderr, "Invalid format magic\n");
+ return EXIT_FAILURE;
+ }
+
+ fprintf(stderr, "Format chunk size: %d\n", fmt.size);
+ fprintf(stderr, "Format: %d\n", fmt.fmt);
+ fprintf(stderr, "Channels: %d\n", fmt.channels);
+ fprintf(stderr, "Samplerate: %d\n", fmt.sample_rate);
+ fprintf(stderr, "Byterate: %d\n", fmt.byte_rate);
+ fprintf(stderr, "Align: %d\n", fmt.bitdepth);
+ fprintf(stderr, "Samplebits: %d\n", fmt.samplebits);
+
+ if(fmt.fmt != 1){
+ read(source_fd, &extra_data, 2);
+ }
+
+ if(fmt.fmt == 1 && fmt.size > 16){
+ fprintf(stderr, "Skipping %d additional header bytes\n", fmt.size - 16);
+ for(n = 16; n < fmt.size; n++){
+ read(source_fd, &extra_data, 1);
+ }
+ }
+
+ /* For now, just skip further analysis and bail on incompatible format */
+ if(fmt.samplebits != 16){
+ close(source_fd);
+ fprintf(stderr, "This format is currently not supported\n");
+ return EXIT_FAILURE;
+ }
+
+ hdr_data_t data_header;
+ if(read(source_fd, &data_header, sizeof(data_header)) != sizeof(data_header)){
+ close(source_fd);
+ fprintf(stderr, "Failed to read input data: %s\n", strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ if(memcmp(data_header.magic, "data", 4)){
+ close(source_fd);
+ fprintf(stderr, "Invalid data magic\n");
+ return EXIT_FAILURE;
+ }
+
+ fprintf(stderr, "%d bytes of sample data in file\n", data_header.size);
+
+ uint16_t sample;
+ uint8_t channel = 0;
+
+ for(n = 0; n < data_header.size / 2; n++){
+ //read sample
+ if(read(source_fd, &sample, 2) != 2){
+ close(source_fd);
+ fprintf(stderr, "Failed to read input data at sample %ld: %s\n", n, strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ if(channel == 0){
+ printf("%ld,%d", n, (int16_t) sample);
+ }
+ else{
+ printf(",%d", (int16_t) sample);
+ }
+
+ if(channel == fmt.channels - 1){
+ printf("\n");
+ }
+
+ channel += 1;
+ channel %= fmt.channels;
+ }
+
+ close(source_fd);
+ fprintf(stderr, "All done\n");
+ return EXIT_SUCCESS;
+}