diff --git a/Makefile.in b/Makefile.in index 0e5a25633..de7c6ed3c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -71,6 +71,7 @@ OBJS = @OBJS@ \ src/audio/playback/sdi.o \ src/audio/resampler.o \ src/audio/utils.o \ + src/audio/wav_reader.o \ @COREAUDIO_OBJ@ \ @JACK_TRANS_OBJ@ \ @SPEEX_OBJ@ \ diff --git a/src/audio/wav_reader.c b/src/audio/wav_reader.c new file mode 100644 index 000000000..e9a0d2add --- /dev/null +++ b/src/audio/wav_reader.c @@ -0,0 +1,79 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#include "config_unix.h" +#include "config_win32.h" +#endif // HAVE_CONFIG_H + +#include "audio/wav_reader.h" + +#define READ_N(buf, len) if (fread(buf, len, 1, wav_file) != 1) return WAV_HDR_PARSE_READ_ERROR; + +int read_wav_header(FILE *wav_file, struct wav_metadata *metadata) +{ + char buffer[16]; + rewind(wav_file); + + READ_N(buffer, 4); + if(strncmp(buffer, "RIFF", 4) != 0) { + return WAV_HDR_PARSE_WRONG_FORMAT; + } + + uint32_t chunk_size; + READ_N(&chunk_size, 4); + + READ_N(buffer, 4); + if(strncmp(buffer, "WAVE", 4) != 0) { + return WAV_HDR_PARSE_WRONG_FORMAT; + } + + // format chunk + READ_N(buffer, 4); + if(strncmp(buffer, "fmt ", 4) != 0) { + return WAV_HDR_PARSE_WRONG_FORMAT; + } + + uint32_t fmt_chunk_size; + READ_N(&fmt_chunk_size, 4); + if(fmt_chunk_size != 16) { + return WAV_HDR_PARSE_WRONG_FORMAT; + } + + uint16_t format; + READ_N(&format, 2); + if(format != 0x0001) { + return WAV_HDR_PARSE_NOT_PCM; + } + + uint16_t ch_count; + READ_N(&ch_count, 2); + metadata->ch_count = ch_count; + + uint32_t sample_rate; + READ_N(&sample_rate, sizeof(sample_rate)); + metadata->sample_rate = sample_rate; + + uint32_t avg_bytes_per_sec; + READ_N(&avg_bytes_per_sec, sizeof(avg_bytes_per_sec)); + + uint16_t block_align_offset; + READ_N(&block_align_offset, sizeof(block_align_offset)); + + uint16_t bits_per_sample; + READ_N(&bits_per_sample, sizeof(bits_per_sample)); + metadata->bits_per_sample = bits_per_sample; + + // data chunk + READ_N(buffer, 4); + if(strncmp(buffer, "data", 4) != 0) { + return WAV_HDR_PARSE_WRONG_FORMAT; + } + + uint32_t data_chunk_size; + READ_N(&data_chunk_size, 4); + metadata->data_size = data_chunk_size; + + metadata->data_offset = ftell(wav_file); + + return WAV_HDR_PARSE_OK; +} + diff --git a/src/audio/wav_reader.h b/src/audio/wav_reader.h new file mode 100644 index 000000000..55b2c2a1f --- /dev/null +++ b/src/audio/wav_reader.h @@ -0,0 +1,28 @@ +#include // FILE * + +struct wav_metadata { + int ch_count; + int sample_rate; + int bits_per_sample; + + int data_size; + int data_offset; // from the beginning of file +}; + +#define WAV_HDR_PARSE_OK 0 +#define WAV_HDR_PARSE_READ_ERROR 1 +#define WAV_HDR_PARSE_WRONG_FORMAT 2 +#define WAV_HDR_PARSE_NOT_PCM 3 +/** + * This function reads wav header + * + * If read successfully, it leaves read file position at the beginning of data. + * Currently, only interleaved PCM is supported. + * + * @retval WAV_HDR_PARSE_OK if ok + * @retval WAV_HDR_PARSE_READ_ERROR in case of file read error + * @retval WAV_HDR_PARSE_WRONG_FORMAT if unsupported wav format + * @retval WAV_HDR_PARSE_NOT_PCM non-PCM WAV detected + */ +int read_wav_header(FILE *wav_file, struct wav_metadata *metadata); + diff --git a/src/video_capture/import.c b/src/video_capture/import.c index bea5e2b7d..cd20dd14d 100644 --- a/src/video_capture/import.c +++ b/src/video_capture/import.c @@ -58,6 +58,7 @@ #include "tv.h" #include "audio/audio.h" +#include "audio/wav_reader.h" #include "utils/ring_buffer.h" #include "video_export.h" #include "video_capture/import.h" @@ -218,72 +219,33 @@ static bool init_audio(struct vidcap_import_state *s, char *audio_filename) perror("Cannot open audio file"); return false; } - char buffer[16]; // common commands - will run in any way if(!audio_file) { goto error_opening; } - READ_N(buffer, 4); - if(strncmp(buffer, "RIFF", 4) != 0) { - goto error_format; + struct wav_metadata metadata; + + int ret = read_wav_header(audio_file, &metadata); + switch(ret) { + case WAV_HDR_PARSE_READ_ERROR: + fprintf(stderr, "Error reading WAV header!\n"); + goto error_format; + case WAV_HDR_PARSE_WRONG_FORMAT: + fprintf(stderr, "Unsupported WAV format!\n"); + goto error_format; + case WAV_HDR_PARSE_NOT_PCM: + fprintf(stderr, "Only supported audio format is PCM.\n"); + goto error_format; + case WAV_HDR_PARSE_OK: + break; } - uint32_t chunk_size; - READ_N(&chunk_size, 4); - - READ_N(buffer, 4); - if(strncmp(buffer, "WAVE", 4) != 0) { - goto error_format; - } - - // format chunk - READ_N(buffer, 4); - if(strncmp(buffer, "fmt ", 4) != 0) { - goto error_format; - } - - uint32_t fmt_chunk_size; - READ_N(&fmt_chunk_size, 4); - if(fmt_chunk_size != 16) { - goto error_format; - } - - uint16_t format; - READ_N(&format, 2); - if(format != 0x0001) { - fprintf(stderr, "Only supported audio format is PCM.\n"); - goto error_format; - } - - uint16_t ch_count; - READ_N(&ch_count, 2); - s->audio_frame.ch_count = ch_count; - - uint32_t sample_rate; - READ_N(&sample_rate, sizeof(sample_rate)); - s->audio_frame.sample_rate = sample_rate; - - uint32_t avg_bytes_per_sec; - READ_N(&avg_bytes_per_sec, sizeof(avg_bytes_per_sec)); - - uint16_t block_align_offset; - READ_N(&block_align_offset, sizeof(block_align_offset)); - - uint16_t bits_per_sample; - READ_N(&bits_per_sample, sizeof(bits_per_sample)); - s->audio_frame.bps = bits_per_sample / 8; - - // data chunk - READ_N(buffer, 4); - if(strncmp(buffer, "data", 4) != 0) { - goto error_format; - } - - uint32_t data_chunk_size; - READ_N(&data_chunk_size, 4); - s->audio_state.total_samples = data_chunk_size / s->audio_frame.bps / s->audio_frame.ch_count; + s->audio_frame.ch_count = metadata.ch_count; + s->audio_frame.sample_rate = metadata.sample_rate; + s->audio_frame.bps = metadata.bits_per_sample / 8; + s->audio_state.total_samples = metadata.data_size / s->audio_frame.bps / s->audio_frame.ch_count; s->audio_state.samples_read = 0; s->audio_state.data = ring_buffer_init(s->audio_frame.bps * s->audio_frame.sample_rate *