eca5a76e6c8e0c5e66d919d2378a88d0ebbfcfb0
[dos_auplay] / src / auwav.c
1 #include <stdio.h>\r
2 #include <stdlib.h>\r
3 #include "aufile.h"\r
4 #include "inttypes.h"\r
5 \r
6 struct format {\r
7         uint16_t fmt;\r
8         uint16_t nchan;\r
9         uint32_t rate;\r
10         uint16_t avgbaud;\r
11         uint16_t block_align;\r
12         uint16_t sample_bytes;\r
13 };\r
14 \r
15 struct playback_data {\r
16         uint32_t start, size;\r
17         uint32_t bytes_left;\r
18 };\r
19 \r
20 #define FOURCC(a, b, c, d) \\r
21         ((uint32_t)(a) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))\r
22 \r
23 enum {\r
24         ID_RIFF = FOURCC('R', 'I', 'F', 'F'),\r
25         ID_WAVE = FOURCC('W', 'A', 'V', 'E'),\r
26         ID_FMT = FOURCC('f', 'm', 't', ' '),\r
27         ID_DATA = FOURCC('d', 'a', 't', 'a')\r
28 };\r
29 \r
30 static void close_wav(struct au_file *au);\r
31 static void reset_wav(struct au_file *au);\r
32 static int read_wav(struct au_file *au, void *buf, int size);\r
33 static int read_uint32(uint32_t *res, FILE *fp);\r
34 static int read_format(struct format *fmt, int fmtsize, FILE *fp);\r
35 \r
36 \r
37 int au_open_wav(struct au_file *au)\r
38 {\r
39         uint32_t id, len;\r
40         struct format fmt;\r
41         struct playback_data *pb;\r
42 \r
43         if(read_uint32(&id, au->fp) == -1 || id != ID_RIFF)\r
44                 return -1;\r
45         fseek(au->fp, 4, SEEK_CUR);\r
46         if(read_uint32(&id, au->fp) == -1 || id != ID_WAVE)\r
47                 return -1;\r
48         if(read_uint32(&id, au->fp) == -1 || id != ID_FMT)\r
49                 return -1;\r
50         if(read_uint32(&len, au->fp) == -1)\r
51                 return -1;\r
52         if(read_format(&fmt, len, au->fp) == -1)\r
53                 return -1;\r
54         if(read_uint32(&id, au->fp) == -1 || id != ID_DATA)\r
55                 return -1;\r
56         if(read_uint32(&len, au->fp) == -1)\r
57                 return -1;\r
58 \r
59         if(!(pb = malloc(sizeof *pb))) {\r
60                 fprintf(stderr, "failed to allocate wav playback data block\n");\r
61                 return -1;\r
62         }\r
63         pb->start = ftell(au->fp);\r
64         pb->size = pb->bytes_left = len;\r
65 \r
66         au->rate = fmt.rate;\r
67         au->bits = fmt.sample_bytes * 8;\r
68         au->chan = fmt.nchan;\r
69         au->data = pb;\r
70 \r
71         au->close = close_wav;\r
72         au->reset = reset_wav;\r
73         au->read = read_wav;\r
74         return 0;\r
75 }\r
76 \r
77 static void close_wav(struct au_file *au)\r
78 {\r
79         free(au->data);\r
80 }\r
81 \r
82 static void reset_wav(struct au_file *au)\r
83 {\r
84         struct playback_data *pb = au->data;\r
85         pb->bytes_left = pb->size;\r
86         fseek(au->fp, pb->start, SEEK_SET);\r
87 }\r
88 \r
89 static int read_wav(struct au_file *au, void *buf, int size)\r
90 {\r
91         struct playback_data *pb = au->data;\r
92         size_t rd;\r
93 \r
94         if(size > pb->bytes_left) {\r
95                 size = pb->bytes_left;\r
96         }\r
97         if(size <= 0) {\r
98                 return 0;\r
99         }\r
100         if((rd = fread(buf, 1, size, au->fp)) == -1) {\r
101                 pb->bytes_left = 0;\r
102                 return -1;\r
103         }\r
104         pb->bytes_left -= rd;\r
105         return rd;\r
106 }\r
107 \r
108 #ifdef BIGENDIAN\r
109 static void swap_uint32(uint32_t *val)\r
110 {\r
111         uint32_t x = *val;\r
112         *val = (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24);\r
113 }\r
114 \r
115 static void swap_uint16(uint16_t *val)\r
116 {\r
117         uint16_t x = *val;\r
118         *val = (x << 8) | (x >> 8);\r
119 }\r
120 #endif\r
121 \r
122 static int read_uint32(uint32_t *res, FILE *fp)\r
123 {\r
124         if(fread(res, 4, 1, fp) < 1) {\r
125                 return -1;\r
126         }\r
127 #ifdef BIGENDIAN\r
128         swap_uint32(res);\r
129 #endif\r
130         return 0;\r
131 }\r
132 \r
133 static int read_format(struct format *fmt, int fmtsize, FILE *fp)\r
134 {\r
135         if(fread(fmt, 1, fmtsize, fp) < fmtsize) {\r
136                 return -1;\r
137         }\r
138 #ifdef BIGENDIAN\r
139         swap_uint16(&fmt->fmt);\r
140         swap_uint16(&fmt->nchan);\r
141         swap_uint32(&fmt->rate);\r
142         swap_uint16(&fmt->avgbaud);\r
143         swap_uint16(&fmt->block_align);\r
144 #endif\r
145         return 0;\r
146 }\r