#include #include #include #include #include #include #include #include #define VERSION "0.2" #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #define PRIsize_t "llu" #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; typedef union { int32_t i32; int16_t i16; float f32; double f64; uint8_t bytes[8]; } sample_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 \n\n", fn); fprintf(stdout, "Supported wave formats: PCM s16le, s32le, f32, f64\n"); return EXIT_FAILURE; } int wave_verify_riff(int fd, hdr_riff_t* hdr){ if(read(fd, hdr, sizeof(hdr_riff_t)) != sizeof(hdr_riff_t)){ fprintf(stderr, "Failed to read input data: %s\n", strerror(errno)); return 1; } if(memcmp(hdr->magic_riff, "RIFF", 4) || memcmp(hdr->magic_wave, "WAVE", 4)){ fprintf(stderr, "Invalid RIFF/WAVE magic - probably not a WAVE file\n"); return 1; } fprintf(stderr, "RIFF chunk size: %d\n", hdr->size); return 0; } int wave_verify_fmt(int fd, hdr_fmt_t* hdr){ uint16_t extra_data; uint8_t skip_byte; size_t n; if(read(fd, hdr, sizeof(hdr_fmt_t)) != sizeof(hdr_fmt_t)){ fprintf(stderr, "Failed to read input data: %s\n", strerror(errno)); return 1; } if(memcmp(hdr->magic, "fmt ", 4)){ fprintf(stderr, "Invalid format header magic - format probably incompatible\n"); return 1; } fprintf(stderr, "Format chunk size: %d\n", hdr->size); fprintf(stderr, "Format: %d\n", hdr->fmt); fprintf(stderr, "Channels: %d\n", hdr->channels); fprintf(stderr, "Samplerate: %d\n", hdr->sample_rate); fprintf(stderr, "Byterate: %d\n", hdr->byte_rate); fprintf(stderr, "Align: %d\n", hdr->bitdepth); fprintf(stderr, "Samplebits: %d\n", hdr->samplebits); if(hdr->fmt != 1){ if(read(fd, &extra_data, 2) != 2){ fprintf(stderr, "Failed to read input data: %s\n", strerror(errno)); return 1; } } else if(hdr->size > 16 /* length of fmt header as counted from after the size member */){ fprintf(stderr, "Skipping %d additional header bytes:", hdr->size - 16); for(n = 16; n < hdr->size; n++){ if(read(fd, &skip_byte, 1) != 1){ fprintf(stderr, "Failed to read input data: %s\n", strerror(errno)); return 1; } fprintf(stderr, " %02X", skip_byte); } fprintf(stderr, "\n"); } return 0; } int wave_verify_fact(int fd, hdr_fact_t* hdr){ if(read(fd, hdr, sizeof(hdr_fact_t)) != sizeof(hdr_fact_t)){ fprintf(stderr, "Failed to read input data: %s\n", strerror(errno)); return 1; } if(memcmp(hdr->magic, "fact", 4)){ fprintf(stderr, "Invalid data magic\n"); return 1; } fprintf(stderr, "Fact chunk size: %d\n", hdr->size); fprintf(stderr, "%d samples per channel\n", hdr->samples); return 0; } int wave_verify_data(int fd, hdr_data_t* hdr, size_t sample_bits, size_t channels){ if(read(fd, hdr, sizeof(hdr_data_t)) != sizeof(hdr_data_t)){ fprintf(stderr, "Failed to read input data: %s\n", strerror(errno)); return 1; } if(memcmp(hdr->magic, "data", 4)){ fprintf(stderr, "Invalid data magic\n"); return 1; } fprintf(stderr, "%d bytes of sample data in file\n", hdr->size); if(hdr->size % (sample_bits / 8 * channels)){ fprintf(stderr, "Sample data unaligned, unable to decode\n"); return 1; } fprintf(stderr, "Data chunk maps to %" PRIsize_t " samples of data per channel\n", hdr->size / (sample_bits / 8 * channels)); return 0; } int print_sample(sample_t sample, uint16_t format, uint16_t samplebits){ if(format == 1){ if(samplebits == 16){ printf(",%d", sample.i16); return 0; } else if(samplebits == 32){ printf(",%d", sample.i32); return 0; } } else if(format == 3){ if(samplebits == 32){ printf(",%.9g", sample.f32); return 0; } else if(samplebits == 64){ printf(",%.17g", sample.f64); return 0; } } return 1; } int main(int argc, char** argv){ #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; hdr_fmt_t fmt; hdr_fact_t fact; hdr_data_t data; if(wave_verify_riff(source_fd, &riff) || wave_verify_fmt(source_fd, &fmt)){ close(source_fd); return EXIT_FAILURE; } //non-pcm formats have an additional fact header if(fmt.fmt != 1 && wave_verify_fact(source_fd, &fact)){ close(source_fd); return EXIT_FAILURE; } if(wave_verify_data(source_fd, &data, fmt.samplebits, fmt.channels)){ close(source_fd); return EXIT_FAILURE; } //compression currently unsupported if((fmt.fmt != 1 && fmt.fmt != 3) || fmt.samplebits / 8 > sizeof(sample_t)){ close(source_fd); fprintf(stderr, "This format is currently not supported\n"); return EXIT_FAILURE; } sample_t sample; size_t channel = 0; size_t sample_offset = 0; for(sample_offset = 0; sample_offset < data.size / (fmt.samplebits / 8 * fmt.channels); sample_offset++){ //read sample //TODO endianness conversion if(read(source_fd, &sample, fmt.samplebits / 8) != fmt.samplebits / 8){ close(source_fd); fprintf(stderr, "Failed to read input data at sample %" PRIsize_t ": %s\n", sample_offset, strerror(errno)); return EXIT_FAILURE; } if(channel == 0){ printf("%" PRIsize_t "", sample_offset); } print_sample(sample, fmt.fmt, fmt.samplebits); if(channel == fmt.channels - 1){ printf("\n"); } channel += 1; channel %= fmt.channels; } close(source_fd); fprintf(stderr, "Done, exported %" PRIsize_t " frames of data %d channels, processed %" PRIsize_t " samples\n", sample_offset, fmt.channels, fmt.channels * sample_offset); return EXIT_SUCCESS; }