Originally Posted by
yj1214
I think my sound.wav is 177,084 bytes long...do I need to change some kind of settings in order to play the sound? thanks.
Add
Code:
fprintf(stderr, "sizeof (struct wavfile_header) = %d\n", (int)sizeof (struct wavfile_header));
fflush(stderr);
to your wavfile_open() function, and compile and run the program.
If it reports anything other than
sizeof (struct wavfile_header) = 44
the problem is that the code uses a structure to describe a file header, but does not take into account structure packing. You could try modifying the structure definition into:
Code:
#include <stdint.h>
struct wavfile_header {
char riff_tag[4];
uint32_t riff_length;
char wave_tag[4];
char fmt_tag[4];
uint32_t fmt_length;
uint16_t audio_format;
uint16_t num_channels;
uint32_t sample_rate;
uint32_t byte_rate;
uint16_t block_align;
uint16_t bits_per_sample;
char data_tag[4];
uint32_t data_length;
} __attribute__((packed));
but it is kinda hacky, relying on GCC's structure attributes (here, pack as tightly as possible, no padding). Other compilers support the same in other ways, e.g. #pragma pack, and so on, so it's not portable, and falls outside standard C.
In standard C, you cannot just assume you can use a structure to describe a specific data structure, because C allows the compiler to align and add padding within the structure. Telling the compiler to not do that is nonstandard, and varies a bit between compilers, so it's definitely hacky. Just because many people do this this way, does not make it right.
The correct approach, overall, would be to define the header as a simple 44-byte buffer, and use accessor functions to set the 16- and 32-bit low-endian unsigned integers, and 4-character identifiers, in it, preferably in a portable fashion.
Just to give you a hint, I'd start with wavfile.h that looks more like:
Code:
#ifndef WAVFILE_H
#define WAVFILE_H
#include <stdio.h>
typedef struct {
FILE *out;
uint32_t length;
unsigned char header[44];
} wavfile;
int wavfile_create(wavfile *const w, const char *const filename,
const unsigned int sample_rate);
int wavfile_write(wavfile *const w, const void *const data, const size_t samples);
int wavfile_close(wavfile *const w);
#endif /* WAVFILE_H */
The idea is that the wavfile structure maintains all needed information on the file, including the stream handle.
In wavfile_c, we'd have
Code:
static void set_u16(wavfile *const w, const int offset, const unsigned int value)
{
if (w && offset >= 0 && offset <= 42) {
w->header[offset + 0] = value & 255U;
w->header[offset + 1] = (value >> 8) & 255U;
}
}
static void set_u32(wavfile *const w, const int offset, const unsigned int value)
{
if (w && offset >= 0 && offset <= 40) {
w->header[offset + 0] = value & 255U;
w->header[offset + 1] = (value >> 8) & 255U;
w->header[offset + 2] = (value >> 16) & 255U;
w->header[offset + 3] = (value >> 24) & 255U;
}
}
static void set_id4(wavfile *const w, const int offset, const char value[4])
{
if (w && offset >= 0 && offset <= 40) {
w->header[offset + 0] = (unsigned char)(value[0]);
w->header[offset + 1] = (unsigned char)(value[1]);
w->header[offset + 2] = (unsigned char)(value[2]);
w->header[offset + 3] = (unsigned char)(value[3]);
}
}
#define SET_RIFF_TAG(w) set_id4(w, 0, "RIFF")
#define SET_RIFF_LEN(w, len) set_u32(w, 4, len)
#define SET_WAVE_TAG(w) set_id4(w, 8, "WAVE")
#define SET_FMT_TAG(w) set_id4(w, 12, "fmt ")
#define SET_FMT_LEN(w, len) set_u32(w, 16, len)
#define SET_AUDIO_FORMAT(w, fmt) set_u16(w, 20, fmt)
#define SET_NUM_CHANNELS(w, num) set_u16(w, 22, num)
#define SET_SAMPLE_RATE(w, rate) set_u32(w, 24, rate)
#define SET_BYTE_RATE(w, rate) set_u32(w, 28, rate)
#define SET_BLOCK_ALIGN(w, al) set_u16(w, 32, al)
#define SET_BITS_PER_SAMPLE(w, bits) set_u16(w, 34, bits)
#define SET_DATA_TAG(w) set_id4(w, 36, "data")
#define SET_DATA_LENGTH(w, len) set_u32(w, 40, len)