mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-12-18 09:25:53 +01:00
132 lines
3.5 KiB
C
132 lines
3.5 KiB
C
#include "wav_utils.h"
|
|
#include "../src/util/reader_put.h"
|
|
|
|
static int make_riff_chunk(uint8_t* buf, wav_header_t* wav, uint32_t header_size, uint32_t data_size) {
|
|
|
|
put_data (buf+0x00, "RIFF", 0x04);
|
|
put_u32le(buf+0x04, header_size - 0x08 + data_size);
|
|
|
|
put_data (buf+0x08, "WAVE", 0x04);
|
|
|
|
return 0x08 + 0x04;
|
|
}
|
|
|
|
static int make_fmt_chunk(uint8_t* buf, wav_header_t* wav) {
|
|
|
|
int codec = wav->is_float ? 0x0003 : 0x0001; /* PCM */
|
|
int bytes_per_second = wav->sample_rate * wav->channels * wav->sample_size;
|
|
int16_t block_align = wav->channels * wav->sample_size;
|
|
int bits_per_sample = wav->sample_size * 8;
|
|
|
|
put_data (buf + 0x00, "fmt ", 0x04);
|
|
put_u32le(buf + 0x04, 0x10);
|
|
|
|
put_s16le(buf + 0x08, codec);
|
|
put_s16le(buf + 0x0a, wav->channels);
|
|
put_s32le(buf + 0x0c, wav->sample_rate);
|
|
put_s32le(buf + 0x10, bytes_per_second);
|
|
put_s16le(buf + 0x14, block_align);
|
|
put_s16le(buf + 0x16, bits_per_sample);
|
|
|
|
return 0x08 + 0x10;
|
|
}
|
|
|
|
/* see riff.c */
|
|
static int make_smpl_chunk(uint8_t* buf, wav_header_t* wav) {
|
|
|
|
put_data (buf + 0x00, "smpl", 0x04);
|
|
put_s32le(buf + 0x04, 0x3c);
|
|
|
|
for (int i = 0; i < 7; i++) {
|
|
put_s32le(buf + 0x08 + i * 0x04, 0);
|
|
}
|
|
|
|
put_s32le(buf + 0x24, 1);
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
put_s32le(buf + 0x28 + i * 0x04, 0);
|
|
}
|
|
|
|
put_s32le(buf + 0x34, wav->loop_start);
|
|
put_s32le(buf + 0x38, wav->loop_end);
|
|
put_s32le(buf + 0x3C, 0);
|
|
put_s32le(buf + 0x40, 0);
|
|
|
|
return 0x08 + 0x3c;
|
|
}
|
|
|
|
static int make_data_chunk(uint8_t* buf, wav_header_t* wav, uint32_t data_size) {
|
|
|
|
put_data (buf + 0x00, "data", 0x04);
|
|
put_u32le(buf + 0x04, data_size);
|
|
|
|
return 0x08;
|
|
}
|
|
|
|
/* make a RIFF header for .wav */
|
|
size_t wav_make_header(uint8_t* buf, size_t buf_size, wav_header_t* wav) {
|
|
size_t header_size;
|
|
|
|
/* RIFF + fmt + smpl + data */
|
|
header_size = 0x08 + 0x04;
|
|
header_size += 0x08 + 0x10;
|
|
if (wav->write_smpl_chunk && wav->loop_end)
|
|
header_size += 0x08 + 0x3c;
|
|
header_size += 0x08;
|
|
|
|
if (header_size > buf_size)
|
|
return 0;
|
|
|
|
if (!wav->sample_size)
|
|
wav->sample_size = sizeof(short);
|
|
if (wav->sample_size <= 0 || wav->sample_size >= 4)
|
|
return 0;
|
|
|
|
size_t data_size = wav->sample_count * wav->channels * wav->sample_size;
|
|
int size;
|
|
|
|
size = make_riff_chunk(buf, wav, header_size, data_size);
|
|
buf += size;
|
|
|
|
size = make_fmt_chunk(buf, wav);
|
|
buf += size;
|
|
|
|
if (wav->write_smpl_chunk && wav->loop_end) {
|
|
size = make_smpl_chunk(buf, wav);
|
|
buf += size;
|
|
}
|
|
|
|
size = make_data_chunk(buf, wav, data_size);
|
|
buf += size;
|
|
|
|
/* could try to add channel_layout but would need WAVEFORMATEXTENSIBLE */
|
|
|
|
return header_size;
|
|
}
|
|
|
|
static inline void swap_value(uint8_t* buf, int sample_size) {
|
|
for (int i = 0; i < sample_size / 2; i++) {
|
|
char temp = buf[i];
|
|
buf[i] = buf[sample_size - i - 1];
|
|
buf[sample_size - i - 1] = temp;
|
|
}
|
|
}
|
|
|
|
/* when endianness is LE buffer is correct already and this function can be skipped */
|
|
void wav_swap_samples_le(void* samples, int samples_len, int sample_size) {
|
|
/* Windows can't be BE... I think */
|
|
#if !defined(_WIN32)
|
|
#if !defined(__BYTE_ORDER__) || __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
|
|
if (!sample_size)
|
|
sample_size = sizeof(short);
|
|
|
|
/* 16b sample in memory is AABB where AA=MSB, BB=LSB, swap to BBAA */
|
|
|
|
uint8_t* buf = samples;
|
|
for (int i = 0; i < samples_len; i += sample_size) {
|
|
swap_value(buf + i, sample_size);
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|