| /****************************************************************************** |
| * |
| * Copyright 2022 Google LLC |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at: |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| ******************************************************************************/ |
| |
| #include <stdint.h> |
| #include "wave.h" |
| |
| |
| /** |
| * Id formatting |
| */ |
| |
| #define __WAVE_ID(s) \ |
| (uint32_t)( s[0] | (s[1] << 8) | (s[2] << 16) | (s[3] << 24) ) |
| |
| |
| /** |
| * File format statement |
| * | type_id WAVE_FILE_TYPE_ID |
| * | size File size - 8 bytes |
| * | type_id WAVE_FILE_FMT_ID |
| */ |
| |
| #define WAVE_FILE_TYPE_ID __WAVE_ID("RIFF") |
| #define WAVE_FILE_FMT_ID __WAVE_ID("WAVE") |
| |
| struct wave_file { |
| uint32_t type_id; |
| uint32_t size; |
| uint32_t fmt_id; |
| }; |
| |
| |
| /** |
| * Audio format statement |
| * | id WAVE_FORMAT_ID |
| * | size Size of the block - 8 bytes (= 16 bytes) |
| * | format WAVE_FORMAT_PCM |
| * | channels Number of channels |
| * | samplerate Sampling rate |
| * | byterate Bytes per secondes = `samplerate * framesize` |
| * | framesize Bytes per sampling time = `channels * bitdepth / 8` |
| * | bitdepth Number of bits per sample |
| */ |
| |
| #define WAVE_FORMAT_ID __WAVE_ID("fmt ") |
| #define WAVE_FORMAT_PCM 1 |
| |
| struct wave_format { |
| uint32_t id; |
| uint32_t size; |
| uint16_t fmt; |
| uint16_t channels; |
| uint32_t samplerate; |
| uint32_t byterate; |
| uint16_t framesize; |
| uint16_t bitdepth; |
| }; |
| |
| |
| /** |
| * Audio data statement |
| * | id WAV_DATA_ID |
| * | size Size of the data following |
| */ |
| |
| #define WAVE_DATA_ID __WAVE_ID("data") |
| |
| struct wave_data { |
| uint32_t id; |
| uint32_t size; |
| }; |
| |
| |
| /** |
| * Read WAVE file header |
| */ |
| int wave_read_header(FILE *fp, int *bitdepth, int *samplesize, |
| int *samplerate, int *nchannels, int *nframes) |
| { |
| struct wave_file file; |
| struct wave_format format; |
| struct wave_data data; |
| |
| if (fread(&file, sizeof(file), 1, fp) != 1 |
| || file.type_id != WAVE_FILE_TYPE_ID |
| || file.fmt_id != WAVE_FILE_FMT_ID) |
| return -1; |
| |
| if (fread(&format, sizeof(format), 1, fp) != 1 |
| || format.id != WAVE_FORMAT_ID |
| || format.fmt != WAVE_FORMAT_PCM |
| || format.channels <= 0 |
| || format.samplerate <= 0 |
| || format.framesize <= 0 |
| || format.byterate != format.samplerate * format.framesize) |
| return -1; |
| |
| fseek(fp, sizeof(format) - (8 + format.size), SEEK_CUR); |
| |
| if (fread(&data, sizeof(data), 1, fp) != 1 |
| || data.id != WAVE_DATA_ID) |
| return -1; |
| |
| *bitdepth = format.bitdepth; |
| *samplesize = format.framesize / format.channels; |
| *samplerate = format.samplerate; |
| *nchannels = format.channels; |
| *nframes = data.size / format.framesize; |
| |
| return 0; |
| } |
| |
| /** |
| * Read PCM samples from wave file |
| */ |
| int wave_read_pcm(FILE *fp, int samplesize, |
| int nch, int count, void *buffer) |
| { |
| return fread(buffer, nch * samplesize, count, fp); |
| } |
| |
| /** |
| * Write WAVE file header |
| */ |
| void wave_write_header(FILE *fp, int bitdepth, int samplesize, |
| int samplerate, int nchannels, int nframes) |
| { |
| struct { |
| struct wave_file file; |
| struct wave_format format; |
| struct wave_data data; |
| } header; |
| |
| long data_size = nchannels * nframes * samplesize; |
| long file_size = sizeof(header) + data_size; |
| |
| header.file = (struct wave_file){ |
| WAVE_FILE_TYPE_ID, file_size - 8, |
| .fmt_id = WAVE_FILE_FMT_ID |
| }; |
| |
| header.format = (struct wave_format){ |
| WAVE_FORMAT_ID, sizeof(header.format) - 8, |
| .fmt = WAVE_FORMAT_PCM, |
| .channels = nchannels, |
| .samplerate = samplerate, |
| .byterate = samplerate * nchannels * samplesize, |
| .framesize = nchannels * samplesize, |
| .bitdepth = bitdepth, |
| }; |
| |
| header.data = (struct wave_data){ |
| WAVE_DATA_ID, data_size |
| }; |
| |
| fwrite(&header, sizeof(header), 1, fp); |
| } |
| |
| /** |
| * Write PCM samples to wave file |
| */ |
| void wave_write_pcm(FILE *fp, int samplesize, |
| const void *_pcm, int nch, int off, int count) |
| { |
| const int8_t *pcm = _pcm; |
| fwrite(pcm + nch * off * samplesize, nch * samplesize, count, fp); |
| } |