Allegro.cc - Online Community

Allegro.cc Forums » Allegro Development » save_wav() is a good idea

This thread is locked; no one can reply to it. rss feed Print
save_wav() is a good idea
thematrixeatsyou
Member #6,183
September 2005
avatar

I haven't got source for Allegro 4.2.0 so I can't check if the load_wav() function has been perfected and not just skipping a bunch of bytes.

Anyways, someone I know (who codes a lot of Assembler but does a lot of C nowadays) has given me the actual WAV header format.
00 DWORD "RIFF"
04 DWORD Filesize-8
08 DWORD "WAVE"
0C DWORD "fmt "
10 DWORD 0x10
14 WORD 1
16 WORD NumChannels
18 DWORD SampleRate
1C DWORD ByteRate=SampleRate*NumChannels*BitsPerSample/8
20 WORD BlockAlign=NumChannels*BitsPerSample/8
22 WORD BitsPerSample
24 DWORD "data"
28 DWORD DataSize=FileSize-0x2C
2C Data goes here
I reckon I could make something quite quickly. Here's a header...

typedef struct WaveFile {
  char riffhead[4]; // Set this to "RIFF"
  unsigned long fsm8; // Set this to sample>len + 36 (24h)
  char wavefmthead[8]; // Set this to "WAVEfmt "
  unsigned long fmtheaderlen; // Set this to 16 (10h)
  unsigned short codecused; // Set this to 1 (PCM unencoded format)
  unsigned short channels; // Set this to sample->stereo + 1
  unsigned long samplerate; // Set this to sample->frequency
  unsigned long byterate; // Set this to samplerate*channels*bits/8
  unsigned short blockalign; // Set this to channels*bits/8
  unsigned short bits; // Set this to sample->bits
  char datahead[4]; // Set this to "data"
  unsigned long datasize; // Set this to sample->len
  void *data; // Set this to sample->data
} WaveFile;

As you see, I'm really anal when it comes to signed and unsigned numbers.

good food is t3h pwn <-- if anyone can find out how old this sig is they win an ascii penguin

Thomas Fjellstrom
Member #476
June 2000
avatar

Why would load_wav need to read data it doesn't use?

Though if you want to handle any riff format, give my libriff a shot. It might not handle aiff/iff files correctly, as for some reason the one file I tried stored the offsets and lengths in the wrong endian layout.

example:

1#include <stdio.h>
2#include <errno.h>
3#include <string.h>
4#include "libriff.h"
5 
6#include "debug.h"
7 
8size_t stdio_read(void *f, size_t size, void *ptr)
9{
10 return fread(ptr, 1, size, (FILE *)f);
11}
12 
13size_t stdio_write(void *f, size_t size, void *ptr)
14{
15 return fwrite(ptr, 1, size, (FILE *)f);
16}
17 
18int32_t stdio_seek(void *f, off_t off, u_int32_t whence)
19{
20 return fseek((FILE *)f, off, whence);
21}
22 
23off_t stdio_tell(void *f)
24{
25 return ftell((FILE *)f);
26}
27 
28int32_t stdio_error(void *f)
29{
30 return ferror((FILE *)f);
31}
32 
33int32_t stdio_eof(void *f)
34{
35 return feof((FILE *)f);
36}
37 
38riff_callbacks_t stdio_vtable = {
39 &stdio_read,
40 &stdio_write,
41 &stdio_seek,
42 &stdio_tell,
43 &stdio_error,
44 &stdio_eof,
45};
46 
47int copy_riff(riff_file_t *src, FILE *dest)
48{
49 riff_file_t *riff = NULL;
50 riff_chunk_t *schunk = NULL, *dchunk = NULL;
51 unsigned char data[1024];
52
53 Assert(src);
54
55 riff = riff_file_copy(src);
56 if(!riff)
57 return -1;
58
59 schunk = riff_file_chunk(src);
60 dchunk = riff_file_chunk(riff);
61
62 riff_file_data_set(riff, dest);
63 if(riff_file_save(riff) != 0) {
64 riff_file_destroy(riff);
65 warn("failed to save dest riff headers");
66 return -1;
67 }
68
69 while(schunk) {
70 u_int32_t i = 0;
71 if(riff_chunk_children(schunk)) {
72 schunk = riff_chunk_children(schunk);
73 dchunk = riff_chunk_children(dchunk);
74 continue;
75 }
76
77 while(i < riff_chunk_size_get(schunk)) {
78 u_int32_t r = riff_chunk_read(src, schunk, 1024, data);
79 printf("read %i bytes\n", r);
80 riff_chunk_write(riff, dchunk, r, data);
81
82 i += r;
83 }
84
85 if(riff_chunk_next(schunk)) {
86 schunk = riff_chunk_next(schunk);
87 dchunk = riff_chunk_next(dchunk);
88 } else if(riff_chunk_parent(schunk) && riff_chunk_next(riff_chunk_parent(schunk))) {
89 schunk = riff_chunk_next(riff_chunk_parent(schunk));
90 dchunk = riff_chunk_next(riff_chunk_parent(dchunk));
91 } else
92 schunk = NULL;
93 }
94
95 riff_file_destroy(riff);
96
97 return 0;
98}
99 
100int main(int argc, char **argv)
101{
102 riff_file_t *riff = NULL;
103
104 FILE *src = NULL;
105 FILE *dest = NULL;
106
107 if(argc < 3)
108 error("Usage: %s <src> <dest>", argv[0]);
109
110 src = fopen(argv[1], "rb");
111 if(!src)
112 error("Failed to open file '%s': %s", argv[1], strerror(errno));
113
114 dest = fopen(argv[2], "wb");
115 if(!dest)
116 error("Failed to open file '%s': %s", argv[2], strerror(errno));
117
118 riff = riff_file_create(&stdio_vtable, src);
119 if(!riff) { // doesn't currently return if creation failed.. may change, so error checking is a good thing...
120 fclose(src);
121 fclose(dest);
122 error("Failed to create riff data");
123 }
124
125 if(!riff_file_load(riff)) {
126 riff_file_destroy(riff);
127 fclose(src);
128 fclose(dest);
129 error("Failed to load riff file");
130 }
131 
132 if(copy_riff(riff, dest) != 0) {
133 warn("failed durring copy...");
134 }
135
136 fclose(dest);
137
138 riff_file_destroy(riff);
139 fclose(src);
140
141 return 0;
142}

This is a somewhat useless example, but hey, it shows how to read and write a riff file, so I thought it was better than just showing the others. it provides a "FILE" like interface to the individual chunks in a given riff file, reading, writing, seeking, and you can't seek, read or write past the end or before the start of a chunk, so its a little safe there.

Though I have to admit, the riff_file_copy function might need to be renamed, it only coppies the metadata, ie: the structs, and chunk structs, and not the actual data stored in the chunks.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

thematrixeatsyou
Member #6,183
September 2005
avatar

Well most of the players load these values to perform checks.
A SAVE_WAV() function MUST save these values correctly anyways.
Let's see what I can whip up... Untested sorry. If it works, then I'm just the man.

1#include <stdio.h>
2 
3#include <allegro.h>
4typedef struct WaveFile {
5 char riffhead[4]; // Set this to "RIFF"
6 unsigned long fsm8; // Set this to sample>len + 36 (24h)
7 char wavefmthead[8]; // Set this to "WAVEfmt "
8 unsigned long fmtheaderlen; // Set this to 16 (10h)
9 unsigned short codecused; // Set this to 1 (PCM unencoded format)
10 unsigned short channels; // Set this to sample->stereo + 1
11 unsigned long samplerate; // Set this to sample->frequency
12 unsigned long byterate; // Set this to samplerate*channels*bits/8
13 unsigned short blockalign; // Set this to channels*bits/8
14 unsigned short bits; // Set this to sample->bits
15 char datahead[4]; // Set this to "data"
16 unsigned long datasize; // Set this to sample->len
17 void *data; // Set this to sample->data
18} WaveFile;
19 
20int save_wav(const char *filename, SAMPLE *spl){
21 PACKFILE *fp;
22 WaveFile wf;
23 
24 if(pack_fopen(filename,"w")){
25 pack_fwrite("RIFF",4,fp);
26 pack_iputl(spl->len*(spl->stereo+1)*(spl->bits/8)+36,fp);
27 pack_fwrite("WAVEfmt ",8,fp);
28 pack_iputl(10,fp);
29 pack_iputw(1,fp);
30 pack_iputw(spl->stereo+1,fp);
31 pack_iputl(spl->freq,fp);
32 pack_iputl(spl->freq*(spl->stereo+1)*(spl->bits/8),fp);
33 pack_iputw((spl->stereo+1)*(spl->bits/8),fp);
34 pack_iputw((spl->stereo+1)*spl->bits,fp);
35 pack_fwrite("data",4,fp);
36 pack_iputl(spl->len*(spl->stereo+1)*(spl->bits/8),fp);
37 pack_fwrite(spl->data,spl->len*(spl->stereo+1)*(spl->bits/8),fp);
38 return 0;
39 }
40 return 1;
41}

good food is t3h pwn <-- if anyone can find out how old this sig is they win an ascii penguin

Ron Novy
Member #6,982
March 2006
avatar

Don't think allegro really needs to save waves but a simple save_wav might be cool for utilities or debugging. If you're looking for a lib, I've written several libraries that can read/write PCM format wave files and the most recent ('My_Audio' library) can handle all bit depths of PCM .wav (8 to 32 bit integer, float type3, double type3, and the non-standard CoolEdit 16.8) and all integer format .aif files. (can also read and write .mid files) It handles all unsupported chunks in a way that allows them to be saved back to the file and supports a lot of chunks common to the pro-audio scene including the 'smpl' chunk (sampler info), 'cue ' and 'plst' and even the 'bext' chunk (EBU extensions). It's not on the net yet but will be soon. You might want to check out libsndfile too. Libsndfile handles all kinds of audio file formats.

----
Oh... Bieber! I thought everyone was chanting Beaver... Now it doesn't make any sense at all. :-/

Elias
Member #358
May 2000

The best way would probably be an addon, which would use the save_sample and register_sample_file_type functions. I also should have a very simple function lying around somewhere which can be registered for saving to wav.

--
"Either help out or stop whining" - Evert

GullRaDriel
Member #3,861
September 2003
avatar

Quote:

I also should have a very simple function lying around somewhere

So do not let it lying around, Let it be a part of allegro ;-)

"Code is like shit - it only smells if it is not yours"
Allegro Wiki, full of examples and articles !!

Go to: