Mercurial > sdl-ios-xcode
comparison src/audio/SDL_wave.c @ 1895:c121d94672cb
SDL 1.2 is moving to a branch, and SDL 1.3 is becoming the head.
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Mon, 10 Jul 2006 21:04:37 +0000 |
parents | 7995cc87b777 |
children | 6abc7e6f9817 |
comparison
equal
deleted
inserted
replaced
1894:c69cee13dd76 | 1895:c121d94672cb |
---|---|
25 | 25 |
26 #include "SDL_audio.h" | 26 #include "SDL_audio.h" |
27 #include "SDL_wave.h" | 27 #include "SDL_wave.h" |
28 | 28 |
29 | 29 |
30 static int ReadChunk(SDL_RWops *src, Chunk *chunk); | 30 static int ReadChunk(SDL_RWops * src, Chunk * chunk); |
31 | 31 |
32 struct MS_ADPCM_decodestate { | 32 struct MS_ADPCM_decodestate |
33 Uint8 hPredictor; | 33 { |
34 Uint16 iDelta; | 34 Uint8 hPredictor; |
35 Sint16 iSamp1; | 35 Uint16 iDelta; |
36 Sint16 iSamp2; | 36 Sint16 iSamp1; |
37 Sint16 iSamp2; | |
37 }; | 38 }; |
38 static struct MS_ADPCM_decoder { | 39 static struct MS_ADPCM_decoder |
39 WaveFMT wavefmt; | 40 { |
40 Uint16 wSamplesPerBlock; | 41 WaveFMT wavefmt; |
41 Uint16 wNumCoef; | 42 Uint16 wSamplesPerBlock; |
42 Sint16 aCoeff[7][2]; | 43 Uint16 wNumCoef; |
43 /* * * */ | 44 Sint16 aCoeff[7][2]; |
44 struct MS_ADPCM_decodestate state[2]; | 45 /* * * */ |
46 struct MS_ADPCM_decodestate state[2]; | |
45 } MS_ADPCM_state; | 47 } MS_ADPCM_state; |
46 | 48 |
47 static int InitMS_ADPCM(WaveFMT *format) | 49 static int |
48 { | 50 InitMS_ADPCM(WaveFMT * format) |
49 Uint8 *rogue_feel; | 51 { |
50 Uint16 extra_info; | 52 Uint8 *rogue_feel; |
51 int i; | 53 Uint16 extra_info; |
52 | 54 int i; |
53 /* Set the rogue pointer to the MS_ADPCM specific data */ | 55 |
54 MS_ADPCM_state.wavefmt.encoding = SDL_SwapLE16(format->encoding); | 56 /* Set the rogue pointer to the MS_ADPCM specific data */ |
55 MS_ADPCM_state.wavefmt.channels = SDL_SwapLE16(format->channels); | 57 MS_ADPCM_state.wavefmt.encoding = SDL_SwapLE16(format->encoding); |
56 MS_ADPCM_state.wavefmt.frequency = SDL_SwapLE32(format->frequency); | 58 MS_ADPCM_state.wavefmt.channels = SDL_SwapLE16(format->channels); |
57 MS_ADPCM_state.wavefmt.byterate = SDL_SwapLE32(format->byterate); | 59 MS_ADPCM_state.wavefmt.frequency = SDL_SwapLE32(format->frequency); |
58 MS_ADPCM_state.wavefmt.blockalign = SDL_SwapLE16(format->blockalign); | 60 MS_ADPCM_state.wavefmt.byterate = SDL_SwapLE32(format->byterate); |
59 MS_ADPCM_state.wavefmt.bitspersample = | 61 MS_ADPCM_state.wavefmt.blockalign = SDL_SwapLE16(format->blockalign); |
60 SDL_SwapLE16(format->bitspersample); | 62 MS_ADPCM_state.wavefmt.bitspersample = |
61 rogue_feel = (Uint8 *)format+sizeof(*format); | 63 SDL_SwapLE16(format->bitspersample); |
62 if ( sizeof(*format) == 16 ) { | 64 rogue_feel = (Uint8 *) format + sizeof(*format); |
63 extra_info = ((rogue_feel[1]<<8)|rogue_feel[0]); | 65 if (sizeof(*format) == 16) { |
64 rogue_feel += sizeof(Uint16); | 66 extra_info = ((rogue_feel[1] << 8) | rogue_feel[0]); |
65 } | 67 rogue_feel += sizeof(Uint16); |
66 MS_ADPCM_state.wSamplesPerBlock = ((rogue_feel[1]<<8)|rogue_feel[0]); | 68 } |
67 rogue_feel += sizeof(Uint16); | 69 MS_ADPCM_state.wSamplesPerBlock = ((rogue_feel[1] << 8) | rogue_feel[0]); |
68 MS_ADPCM_state.wNumCoef = ((rogue_feel[1]<<8)|rogue_feel[0]); | 70 rogue_feel += sizeof(Uint16); |
69 rogue_feel += sizeof(Uint16); | 71 MS_ADPCM_state.wNumCoef = ((rogue_feel[1] << 8) | rogue_feel[0]); |
70 if ( MS_ADPCM_state.wNumCoef != 7 ) { | 72 rogue_feel += sizeof(Uint16); |
71 SDL_SetError("Unknown set of MS_ADPCM coefficients"); | 73 if (MS_ADPCM_state.wNumCoef != 7) { |
72 return(-1); | 74 SDL_SetError("Unknown set of MS_ADPCM coefficients"); |
73 } | 75 return (-1); |
74 for ( i=0; i<MS_ADPCM_state.wNumCoef; ++i ) { | 76 } |
75 MS_ADPCM_state.aCoeff[i][0] = ((rogue_feel[1]<<8)|rogue_feel[0]); | 77 for (i = 0; i < MS_ADPCM_state.wNumCoef; ++i) { |
76 rogue_feel += sizeof(Uint16); | 78 MS_ADPCM_state.aCoeff[i][0] = ((rogue_feel[1] << 8) | rogue_feel[0]); |
77 MS_ADPCM_state.aCoeff[i][1] = ((rogue_feel[1]<<8)|rogue_feel[0]); | 79 rogue_feel += sizeof(Uint16); |
78 rogue_feel += sizeof(Uint16); | 80 MS_ADPCM_state.aCoeff[i][1] = ((rogue_feel[1] << 8) | rogue_feel[0]); |
79 } | 81 rogue_feel += sizeof(Uint16); |
80 return(0); | 82 } |
81 } | 83 return (0); |
82 | 84 } |
83 static Sint32 MS_ADPCM_nibble(struct MS_ADPCM_decodestate *state, | 85 |
84 Uint8 nybble, Sint16 *coeff) | 86 static Sint32 |
85 { | 87 MS_ADPCM_nibble(struct MS_ADPCM_decodestate *state, |
86 const Sint32 max_audioval = ((1<<(16-1))-1); | 88 Uint8 nybble, Sint16 * coeff) |
87 const Sint32 min_audioval = -(1<<(16-1)); | 89 { |
88 const Sint32 adaptive[] = { | 90 const Sint32 max_audioval = ((1 << (16 - 1)) - 1); |
89 230, 230, 230, 230, 307, 409, 512, 614, | 91 const Sint32 min_audioval = -(1 << (16 - 1)); |
90 768, 614, 512, 409, 307, 230, 230, 230 | 92 const Sint32 adaptive[] = { |
91 }; | 93 230, 230, 230, 230, 307, 409, 512, 614, |
92 Sint32 new_sample, delta; | 94 768, 614, 512, 409, 307, 230, 230, 230 |
93 | 95 }; |
94 new_sample = ((state->iSamp1 * coeff[0]) + | 96 Sint32 new_sample, delta; |
95 (state->iSamp2 * coeff[1]))/256; | 97 |
96 if ( nybble & 0x08 ) { | 98 new_sample = ((state->iSamp1 * coeff[0]) + |
97 new_sample += state->iDelta * (nybble-0x10); | 99 (state->iSamp2 * coeff[1])) / 256; |
98 } else { | 100 if (nybble & 0x08) { |
99 new_sample += state->iDelta * nybble; | 101 new_sample += state->iDelta * (nybble - 0x10); |
100 } | 102 } else { |
101 if ( new_sample < min_audioval ) { | 103 new_sample += state->iDelta * nybble; |
102 new_sample = min_audioval; | 104 } |
103 } else | 105 if (new_sample < min_audioval) { |
104 if ( new_sample > max_audioval ) { | 106 new_sample = min_audioval; |
105 new_sample = max_audioval; | 107 } else if (new_sample > max_audioval) { |
106 } | 108 new_sample = max_audioval; |
107 delta = ((Sint32)state->iDelta * adaptive[nybble])/256; | 109 } |
108 if ( delta < 16 ) { | 110 delta = ((Sint32) state->iDelta * adaptive[nybble]) / 256; |
109 delta = 16; | 111 if (delta < 16) { |
110 } | 112 delta = 16; |
111 state->iDelta = (Uint16)delta; | 113 } |
112 state->iSamp2 = state->iSamp1; | 114 state->iDelta = (Uint16) delta; |
113 state->iSamp1 = (Sint16)new_sample; | 115 state->iSamp2 = state->iSamp1; |
114 return(new_sample); | 116 state->iSamp1 = (Sint16) new_sample; |
115 } | 117 return (new_sample); |
116 | 118 } |
117 static int MS_ADPCM_decode(Uint8 **audio_buf, Uint32 *audio_len) | 119 |
118 { | 120 static int |
119 struct MS_ADPCM_decodestate *state[2]; | 121 MS_ADPCM_decode(Uint8 ** audio_buf, Uint32 * audio_len) |
120 Uint8 *freeable, *encoded, *decoded; | 122 { |
121 Sint32 encoded_len, samplesleft; | 123 struct MS_ADPCM_decodestate *state[2]; |
122 Sint8 nybble, stereo; | 124 Uint8 *freeable, *encoded, *decoded; |
123 Sint16 *coeff[2]; | 125 Sint32 encoded_len, samplesleft; |
124 Sint32 new_sample; | 126 Sint8 nybble, stereo; |
125 | 127 Sint16 *coeff[2]; |
126 /* Allocate the proper sized output buffer */ | 128 Sint32 new_sample; |
127 encoded_len = *audio_len; | 129 |
128 encoded = *audio_buf; | 130 /* Allocate the proper sized output buffer */ |
129 freeable = *audio_buf; | 131 encoded_len = *audio_len; |
130 *audio_len = (encoded_len/MS_ADPCM_state.wavefmt.blockalign) * | 132 encoded = *audio_buf; |
131 MS_ADPCM_state.wSamplesPerBlock* | 133 freeable = *audio_buf; |
132 MS_ADPCM_state.wavefmt.channels*sizeof(Sint16); | 134 *audio_len = (encoded_len / MS_ADPCM_state.wavefmt.blockalign) * |
133 *audio_buf = (Uint8 *)SDL_malloc(*audio_len); | 135 MS_ADPCM_state.wSamplesPerBlock * |
134 if ( *audio_buf == NULL ) { | 136 MS_ADPCM_state.wavefmt.channels * sizeof(Sint16); |
135 SDL_Error(SDL_ENOMEM); | 137 *audio_buf = (Uint8 *) SDL_malloc(*audio_len); |
136 return(-1); | 138 if (*audio_buf == NULL) { |
137 } | 139 SDL_Error(SDL_ENOMEM); |
138 decoded = *audio_buf; | 140 return (-1); |
139 | 141 } |
140 /* Get ready... Go! */ | 142 decoded = *audio_buf; |
141 stereo = (MS_ADPCM_state.wavefmt.channels == 2); | 143 |
142 state[0] = &MS_ADPCM_state.state[0]; | 144 /* Get ready... Go! */ |
143 state[1] = &MS_ADPCM_state.state[stereo]; | 145 stereo = (MS_ADPCM_state.wavefmt.channels == 2); |
144 while ( encoded_len >= MS_ADPCM_state.wavefmt.blockalign ) { | 146 state[0] = &MS_ADPCM_state.state[0]; |
145 /* Grab the initial information for this block */ | 147 state[1] = &MS_ADPCM_state.state[stereo]; |
146 state[0]->hPredictor = *encoded++; | 148 while (encoded_len >= MS_ADPCM_state.wavefmt.blockalign) { |
147 if ( stereo ) { | 149 /* Grab the initial information for this block */ |
148 state[1]->hPredictor = *encoded++; | 150 state[0]->hPredictor = *encoded++; |
149 } | 151 if (stereo) { |
150 state[0]->iDelta = ((encoded[1]<<8)|encoded[0]); | 152 state[1]->hPredictor = *encoded++; |
151 encoded += sizeof(Sint16); | 153 } |
152 if ( stereo ) { | 154 state[0]->iDelta = ((encoded[1] << 8) | encoded[0]); |
153 state[1]->iDelta = ((encoded[1]<<8)|encoded[0]); | 155 encoded += sizeof(Sint16); |
154 encoded += sizeof(Sint16); | 156 if (stereo) { |
155 } | 157 state[1]->iDelta = ((encoded[1] << 8) | encoded[0]); |
156 state[0]->iSamp1 = ((encoded[1]<<8)|encoded[0]); | 158 encoded += sizeof(Sint16); |
157 encoded += sizeof(Sint16); | 159 } |
158 if ( stereo ) { | 160 state[0]->iSamp1 = ((encoded[1] << 8) | encoded[0]); |
159 state[1]->iSamp1 = ((encoded[1]<<8)|encoded[0]); | 161 encoded += sizeof(Sint16); |
160 encoded += sizeof(Sint16); | 162 if (stereo) { |
161 } | 163 state[1]->iSamp1 = ((encoded[1] << 8) | encoded[0]); |
162 state[0]->iSamp2 = ((encoded[1]<<8)|encoded[0]); | 164 encoded += sizeof(Sint16); |
163 encoded += sizeof(Sint16); | 165 } |
164 if ( stereo ) { | 166 state[0]->iSamp2 = ((encoded[1] << 8) | encoded[0]); |
165 state[1]->iSamp2 = ((encoded[1]<<8)|encoded[0]); | 167 encoded += sizeof(Sint16); |
166 encoded += sizeof(Sint16); | 168 if (stereo) { |
167 } | 169 state[1]->iSamp2 = ((encoded[1] << 8) | encoded[0]); |
168 coeff[0] = MS_ADPCM_state.aCoeff[state[0]->hPredictor]; | 170 encoded += sizeof(Sint16); |
169 coeff[1] = MS_ADPCM_state.aCoeff[state[1]->hPredictor]; | 171 } |
170 | 172 coeff[0] = MS_ADPCM_state.aCoeff[state[0]->hPredictor]; |
171 /* Store the two initial samples we start with */ | 173 coeff[1] = MS_ADPCM_state.aCoeff[state[1]->hPredictor]; |
172 decoded[0] = state[0]->iSamp2&0xFF; | 174 |
173 decoded[1] = state[0]->iSamp2>>8; | 175 /* Store the two initial samples we start with */ |
174 decoded += 2; | 176 decoded[0] = state[0]->iSamp2 & 0xFF; |
175 if ( stereo ) { | 177 decoded[1] = state[0]->iSamp2 >> 8; |
176 decoded[0] = state[1]->iSamp2&0xFF; | 178 decoded += 2; |
177 decoded[1] = state[1]->iSamp2>>8; | 179 if (stereo) { |
178 decoded += 2; | 180 decoded[0] = state[1]->iSamp2 & 0xFF; |
179 } | 181 decoded[1] = state[1]->iSamp2 >> 8; |
180 decoded[0] = state[0]->iSamp1&0xFF; | 182 decoded += 2; |
181 decoded[1] = state[0]->iSamp1>>8; | 183 } |
182 decoded += 2; | 184 decoded[0] = state[0]->iSamp1 & 0xFF; |
183 if ( stereo ) { | 185 decoded[1] = state[0]->iSamp1 >> 8; |
184 decoded[0] = state[1]->iSamp1&0xFF; | 186 decoded += 2; |
185 decoded[1] = state[1]->iSamp1>>8; | 187 if (stereo) { |
186 decoded += 2; | 188 decoded[0] = state[1]->iSamp1 & 0xFF; |
187 } | 189 decoded[1] = state[1]->iSamp1 >> 8; |
188 | 190 decoded += 2; |
189 /* Decode and store the other samples in this block */ | 191 } |
190 samplesleft = (MS_ADPCM_state.wSamplesPerBlock-2)* | 192 |
191 MS_ADPCM_state.wavefmt.channels; | 193 /* Decode and store the other samples in this block */ |
192 while ( samplesleft > 0 ) { | 194 samplesleft = (MS_ADPCM_state.wSamplesPerBlock - 2) * |
193 nybble = (*encoded)>>4; | 195 MS_ADPCM_state.wavefmt.channels; |
194 new_sample = MS_ADPCM_nibble(state[0],nybble,coeff[0]); | 196 while (samplesleft > 0) { |
195 decoded[0] = new_sample&0xFF; | 197 nybble = (*encoded) >> 4; |
196 new_sample >>= 8; | 198 new_sample = MS_ADPCM_nibble(state[0], nybble, coeff[0]); |
197 decoded[1] = new_sample&0xFF; | 199 decoded[0] = new_sample & 0xFF; |
198 decoded += 2; | 200 new_sample >>= 8; |
199 | 201 decoded[1] = new_sample & 0xFF; |
200 nybble = (*encoded)&0x0F; | 202 decoded += 2; |
201 new_sample = MS_ADPCM_nibble(state[1],nybble,coeff[1]); | 203 |
202 decoded[0] = new_sample&0xFF; | 204 nybble = (*encoded) & 0x0F; |
203 new_sample >>= 8; | 205 new_sample = MS_ADPCM_nibble(state[1], nybble, coeff[1]); |
204 decoded[1] = new_sample&0xFF; | 206 decoded[0] = new_sample & 0xFF; |
205 decoded += 2; | 207 new_sample >>= 8; |
206 | 208 decoded[1] = new_sample & 0xFF; |
207 ++encoded; | 209 decoded += 2; |
208 samplesleft -= 2; | 210 |
209 } | 211 ++encoded; |
210 encoded_len -= MS_ADPCM_state.wavefmt.blockalign; | 212 samplesleft -= 2; |
211 } | 213 } |
212 SDL_free(freeable); | 214 encoded_len -= MS_ADPCM_state.wavefmt.blockalign; |
213 return(0); | 215 } |
214 } | 216 SDL_free(freeable); |
215 | 217 return (0); |
216 struct IMA_ADPCM_decodestate { | 218 } |
217 Sint32 sample; | 219 |
218 Sint8 index; | 220 struct IMA_ADPCM_decodestate |
221 { | |
222 Sint32 sample; | |
223 Sint8 index; | |
219 }; | 224 }; |
220 static struct IMA_ADPCM_decoder { | 225 static struct IMA_ADPCM_decoder |
221 WaveFMT wavefmt; | 226 { |
222 Uint16 wSamplesPerBlock; | 227 WaveFMT wavefmt; |
223 /* * * */ | 228 Uint16 wSamplesPerBlock; |
224 struct IMA_ADPCM_decodestate state[2]; | 229 /* * * */ |
230 struct IMA_ADPCM_decodestate state[2]; | |
225 } IMA_ADPCM_state; | 231 } IMA_ADPCM_state; |
226 | 232 |
227 static int InitIMA_ADPCM(WaveFMT *format) | 233 static int |
228 { | 234 InitIMA_ADPCM(WaveFMT * format) |
229 Uint8 *rogue_feel; | 235 { |
230 Uint16 extra_info; | 236 Uint8 *rogue_feel; |
231 | 237 Uint16 extra_info; |
232 /* Set the rogue pointer to the IMA_ADPCM specific data */ | 238 |
233 IMA_ADPCM_state.wavefmt.encoding = SDL_SwapLE16(format->encoding); | 239 /* Set the rogue pointer to the IMA_ADPCM specific data */ |
234 IMA_ADPCM_state.wavefmt.channels = SDL_SwapLE16(format->channels); | 240 IMA_ADPCM_state.wavefmt.encoding = SDL_SwapLE16(format->encoding); |
235 IMA_ADPCM_state.wavefmt.frequency = SDL_SwapLE32(format->frequency); | 241 IMA_ADPCM_state.wavefmt.channels = SDL_SwapLE16(format->channels); |
236 IMA_ADPCM_state.wavefmt.byterate = SDL_SwapLE32(format->byterate); | 242 IMA_ADPCM_state.wavefmt.frequency = SDL_SwapLE32(format->frequency); |
237 IMA_ADPCM_state.wavefmt.blockalign = SDL_SwapLE16(format->blockalign); | 243 IMA_ADPCM_state.wavefmt.byterate = SDL_SwapLE32(format->byterate); |
238 IMA_ADPCM_state.wavefmt.bitspersample = | 244 IMA_ADPCM_state.wavefmt.blockalign = SDL_SwapLE16(format->blockalign); |
239 SDL_SwapLE16(format->bitspersample); | 245 IMA_ADPCM_state.wavefmt.bitspersample = |
240 rogue_feel = (Uint8 *)format+sizeof(*format); | 246 SDL_SwapLE16(format->bitspersample); |
241 if ( sizeof(*format) == 16 ) { | 247 rogue_feel = (Uint8 *) format + sizeof(*format); |
242 extra_info = ((rogue_feel[1]<<8)|rogue_feel[0]); | 248 if (sizeof(*format) == 16) { |
243 rogue_feel += sizeof(Uint16); | 249 extra_info = ((rogue_feel[1] << 8) | rogue_feel[0]); |
244 } | 250 rogue_feel += sizeof(Uint16); |
245 IMA_ADPCM_state.wSamplesPerBlock = ((rogue_feel[1]<<8)|rogue_feel[0]); | 251 } |
246 return(0); | 252 IMA_ADPCM_state.wSamplesPerBlock = ((rogue_feel[1] << 8) | rogue_feel[0]); |
247 } | 253 return (0); |
248 | 254 } |
249 static Sint32 IMA_ADPCM_nibble(struct IMA_ADPCM_decodestate *state,Uint8 nybble) | 255 |
250 { | 256 static Sint32 |
251 const Sint32 max_audioval = ((1<<(16-1))-1); | 257 IMA_ADPCM_nibble(struct IMA_ADPCM_decodestate *state, Uint8 nybble) |
252 const Sint32 min_audioval = -(1<<(16-1)); | 258 { |
253 const int index_table[16] = { | 259 const Sint32 max_audioval = ((1 << (16 - 1)) - 1); |
254 -1, -1, -1, -1, | 260 const Sint32 min_audioval = -(1 << (16 - 1)); |
255 2, 4, 6, 8, | 261 const int index_table[16] = { |
256 -1, -1, -1, -1, | 262 -1, -1, -1, -1, |
257 2, 4, 6, 8 | 263 2, 4, 6, 8, |
258 }; | 264 -1, -1, -1, -1, |
259 const Sint32 step_table[89] = { | 265 2, 4, 6, 8 |
260 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, | 266 }; |
261 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, | 267 const Sint32 step_table[89] = { |
262 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, | 268 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, |
263 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, | 269 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, |
264 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, | 270 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, |
265 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, | 271 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, |
266 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, | 272 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, |
267 22385, 24623, 27086, 29794, 32767 | 273 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, |
268 }; | 274 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, |
269 Sint32 delta, step; | 275 22385, 24623, 27086, 29794, 32767 |
270 | 276 }; |
271 /* Compute difference and new sample value */ | 277 Sint32 delta, step; |
272 step = step_table[state->index]; | 278 |
273 delta = step >> 3; | 279 /* Compute difference and new sample value */ |
274 if ( nybble & 0x04 ) delta += step; | 280 step = step_table[state->index]; |
275 if ( nybble & 0x02 ) delta += (step >> 1); | 281 delta = step >> 3; |
276 if ( nybble & 0x01 ) delta += (step >> 2); | 282 if (nybble & 0x04) |
277 if ( nybble & 0x08 ) delta = -delta; | 283 delta += step; |
278 state->sample += delta; | 284 if (nybble & 0x02) |
279 | 285 delta += (step >> 1); |
280 /* Update index value */ | 286 if (nybble & 0x01) |
281 state->index += index_table[nybble]; | 287 delta += (step >> 2); |
282 if ( state->index > 88 ) { | 288 if (nybble & 0x08) |
283 state->index = 88; | 289 delta = -delta; |
284 } else | 290 state->sample += delta; |
285 if ( state->index < 0 ) { | 291 |
286 state->index = 0; | 292 /* Update index value */ |
287 } | 293 state->index += index_table[nybble]; |
288 | 294 if (state->index > 88) { |
289 /* Clamp output sample */ | 295 state->index = 88; |
290 if ( state->sample > max_audioval ) { | 296 } else if (state->index < 0) { |
291 state->sample = max_audioval; | 297 state->index = 0; |
292 } else | 298 } |
293 if ( state->sample < min_audioval ) { | 299 |
294 state->sample = min_audioval; | 300 /* Clamp output sample */ |
295 } | 301 if (state->sample > max_audioval) { |
296 return(state->sample); | 302 state->sample = max_audioval; |
303 } else if (state->sample < min_audioval) { | |
304 state->sample = min_audioval; | |
305 } | |
306 return (state->sample); | |
297 } | 307 } |
298 | 308 |
299 /* Fill the decode buffer with a channel block of data (8 samples) */ | 309 /* Fill the decode buffer with a channel block of data (8 samples) */ |
300 static void Fill_IMA_ADPCM_block(Uint8 *decoded, Uint8 *encoded, | 310 static void |
301 int channel, int numchannels, struct IMA_ADPCM_decodestate *state) | 311 Fill_IMA_ADPCM_block(Uint8 * decoded, Uint8 * encoded, |
302 { | 312 int channel, int numchannels, |
303 int i; | 313 struct IMA_ADPCM_decodestate *state) |
304 Sint8 nybble; | 314 { |
305 Sint32 new_sample; | 315 int i; |
306 | 316 Sint8 nybble; |
307 decoded += (channel * 2); | 317 Sint32 new_sample; |
308 for ( i=0; i<4; ++i ) { | 318 |
309 nybble = (*encoded)&0x0F; | 319 decoded += (channel * 2); |
310 new_sample = IMA_ADPCM_nibble(state, nybble); | 320 for (i = 0; i < 4; ++i) { |
311 decoded[0] = new_sample&0xFF; | 321 nybble = (*encoded) & 0x0F; |
312 new_sample >>= 8; | 322 new_sample = IMA_ADPCM_nibble(state, nybble); |
313 decoded[1] = new_sample&0xFF; | 323 decoded[0] = new_sample & 0xFF; |
314 decoded += 2 * numchannels; | 324 new_sample >>= 8; |
315 | 325 decoded[1] = new_sample & 0xFF; |
316 nybble = (*encoded)>>4; | 326 decoded += 2 * numchannels; |
317 new_sample = IMA_ADPCM_nibble(state, nybble); | 327 |
318 decoded[0] = new_sample&0xFF; | 328 nybble = (*encoded) >> 4; |
319 new_sample >>= 8; | 329 new_sample = IMA_ADPCM_nibble(state, nybble); |
320 decoded[1] = new_sample&0xFF; | 330 decoded[0] = new_sample & 0xFF; |
321 decoded += 2 * numchannels; | 331 new_sample >>= 8; |
322 | 332 decoded[1] = new_sample & 0xFF; |
323 ++encoded; | 333 decoded += 2 * numchannels; |
324 } | 334 |
325 } | 335 ++encoded; |
326 | 336 } |
327 static int IMA_ADPCM_decode(Uint8 **audio_buf, Uint32 *audio_len) | 337 } |
328 { | 338 |
329 struct IMA_ADPCM_decodestate *state; | 339 static int |
330 Uint8 *freeable, *encoded, *decoded; | 340 IMA_ADPCM_decode(Uint8 ** audio_buf, Uint32 * audio_len) |
331 Sint32 encoded_len, samplesleft; | 341 { |
332 unsigned int c, channels; | 342 struct IMA_ADPCM_decodestate *state; |
333 | 343 Uint8 *freeable, *encoded, *decoded; |
334 /* Check to make sure we have enough variables in the state array */ | 344 Sint32 encoded_len, samplesleft; |
335 channels = IMA_ADPCM_state.wavefmt.channels; | 345 unsigned int c, channels; |
336 if ( channels > SDL_arraysize(IMA_ADPCM_state.state) ) { | 346 |
337 SDL_SetError("IMA ADPCM decoder can only handle %d channels", | 347 /* Check to make sure we have enough variables in the state array */ |
338 SDL_arraysize(IMA_ADPCM_state.state)); | 348 channels = IMA_ADPCM_state.wavefmt.channels; |
339 return(-1); | 349 if (channels > SDL_arraysize(IMA_ADPCM_state.state)) { |
340 } | 350 SDL_SetError("IMA ADPCM decoder can only handle %d channels", |
341 state = IMA_ADPCM_state.state; | 351 SDL_arraysize(IMA_ADPCM_state.state)); |
342 | 352 return (-1); |
343 /* Allocate the proper sized output buffer */ | 353 } |
344 encoded_len = *audio_len; | 354 state = IMA_ADPCM_state.state; |
345 encoded = *audio_buf; | 355 |
346 freeable = *audio_buf; | 356 /* Allocate the proper sized output buffer */ |
347 *audio_len = (encoded_len/IMA_ADPCM_state.wavefmt.blockalign) * | 357 encoded_len = *audio_len; |
348 IMA_ADPCM_state.wSamplesPerBlock* | 358 encoded = *audio_buf; |
349 IMA_ADPCM_state.wavefmt.channels*sizeof(Sint16); | 359 freeable = *audio_buf; |
350 *audio_buf = (Uint8 *)SDL_malloc(*audio_len); | 360 *audio_len = (encoded_len / IMA_ADPCM_state.wavefmt.blockalign) * |
351 if ( *audio_buf == NULL ) { | 361 IMA_ADPCM_state.wSamplesPerBlock * |
352 SDL_Error(SDL_ENOMEM); | 362 IMA_ADPCM_state.wavefmt.channels * sizeof(Sint16); |
353 return(-1); | 363 *audio_buf = (Uint8 *) SDL_malloc(*audio_len); |
354 } | 364 if (*audio_buf == NULL) { |
355 decoded = *audio_buf; | 365 SDL_Error(SDL_ENOMEM); |
356 | 366 return (-1); |
357 /* Get ready... Go! */ | 367 } |
358 while ( encoded_len >= IMA_ADPCM_state.wavefmt.blockalign ) { | 368 decoded = *audio_buf; |
359 /* Grab the initial information for this block */ | 369 |
360 for ( c=0; c<channels; ++c ) { | 370 /* Get ready... Go! */ |
361 /* Fill the state information for this block */ | 371 while (encoded_len >= IMA_ADPCM_state.wavefmt.blockalign) { |
362 state[c].sample = ((encoded[1]<<8)|encoded[0]); | 372 /* Grab the initial information for this block */ |
363 encoded += 2; | 373 for (c = 0; c < channels; ++c) { |
364 if ( state[c].sample & 0x8000 ) { | 374 /* Fill the state information for this block */ |
365 state[c].sample -= 0x10000; | 375 state[c].sample = ((encoded[1] << 8) | encoded[0]); |
366 } | 376 encoded += 2; |
367 state[c].index = *encoded++; | 377 if (state[c].sample & 0x8000) { |
368 /* Reserved byte in buffer header, should be 0 */ | 378 state[c].sample -= 0x10000; |
369 if ( *encoded++ != 0 ) { | 379 } |
370 /* Uh oh, corrupt data? Buggy code? */; | 380 state[c].index = *encoded++; |
371 } | 381 /* Reserved byte in buffer header, should be 0 */ |
372 | 382 if (*encoded++ != 0) { |
373 /* Store the initial sample we start with */ | 383 /* Uh oh, corrupt data? Buggy code? */ ; |
374 decoded[0] = (Uint8)(state[c].sample&0xFF); | 384 } |
375 decoded[1] = (Uint8)(state[c].sample>>8); | 385 |
376 decoded += 2; | 386 /* Store the initial sample we start with */ |
377 } | 387 decoded[0] = (Uint8) (state[c].sample & 0xFF); |
378 | 388 decoded[1] = (Uint8) (state[c].sample >> 8); |
379 /* Decode and store the other samples in this block */ | 389 decoded += 2; |
380 samplesleft = (IMA_ADPCM_state.wSamplesPerBlock-1)*channels; | 390 } |
381 while ( samplesleft > 0 ) { | 391 |
382 for ( c=0; c<channels; ++c ) { | 392 /* Decode and store the other samples in this block */ |
383 Fill_IMA_ADPCM_block(decoded, encoded, | 393 samplesleft = (IMA_ADPCM_state.wSamplesPerBlock - 1) * channels; |
384 c, channels, &state[c]); | 394 while (samplesleft > 0) { |
385 encoded += 4; | 395 for (c = 0; c < channels; ++c) { |
386 samplesleft -= 8; | 396 Fill_IMA_ADPCM_block(decoded, encoded, |
387 } | 397 c, channels, &state[c]); |
388 decoded += (channels * 8 * 2); | 398 encoded += 4; |
389 } | 399 samplesleft -= 8; |
390 encoded_len -= IMA_ADPCM_state.wavefmt.blockalign; | 400 } |
391 } | 401 decoded += (channels * 8 * 2); |
392 SDL_free(freeable); | 402 } |
393 return(0); | 403 encoded_len -= IMA_ADPCM_state.wavefmt.blockalign; |
394 } | 404 } |
395 | 405 SDL_free(freeable); |
396 SDL_AudioSpec * SDL_LoadWAV_RW (SDL_RWops *src, int freesrc, | 406 return (0); |
397 SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) | 407 } |
398 { | 408 |
399 int was_error; | 409 SDL_AudioSpec * |
400 Chunk chunk; | 410 SDL_LoadWAV_RW(SDL_RWops * src, int freesrc, |
401 int lenread; | 411 SDL_AudioSpec * spec, Uint8 ** audio_buf, Uint32 * audio_len) |
402 int MS_ADPCM_encoded, IMA_ADPCM_encoded; | 412 { |
403 int samplesize; | 413 int was_error; |
404 | 414 Chunk chunk; |
405 /* WAV magic header */ | 415 int lenread; |
406 Uint32 RIFFchunk; | 416 int MS_ADPCM_encoded, IMA_ADPCM_encoded; |
407 Uint32 wavelen = 0; | 417 int samplesize; |
408 Uint32 WAVEmagic; | 418 |
409 Uint32 headerDiff = 0; | 419 /* WAV magic header */ |
410 | 420 Uint32 RIFFchunk; |
411 /* FMT chunk */ | 421 Uint32 wavelen = 0; |
412 WaveFMT *format = NULL; | 422 Uint32 WAVEmagic; |
413 | 423 Uint32 headerDiff = 0; |
414 /* Make sure we are passed a valid data source */ | 424 |
415 was_error = 0; | 425 /* FMT chunk */ |
416 if ( src == NULL ) { | 426 WaveFMT *format = NULL; |
417 was_error = 1; | 427 |
418 goto done; | 428 /* Make sure we are passed a valid data source */ |
419 } | 429 was_error = 0; |
420 | 430 if (src == NULL) { |
421 /* Check the magic header */ | 431 was_error = 1; |
422 RIFFchunk = SDL_ReadLE32(src); | 432 goto done; |
423 wavelen = SDL_ReadLE32(src); | 433 } |
424 if ( wavelen == WAVE ) { /* The RIFFchunk has already been read */ | 434 |
425 WAVEmagic = wavelen; | 435 /* Check the magic header */ |
426 wavelen = RIFFchunk; | 436 RIFFchunk = SDL_ReadLE32(src); |
427 RIFFchunk = RIFF; | 437 wavelen = SDL_ReadLE32(src); |
428 } else { | 438 if (wavelen == WAVE) { /* The RIFFchunk has already been read */ |
429 WAVEmagic = SDL_ReadLE32(src); | 439 WAVEmagic = wavelen; |
430 } | 440 wavelen = RIFFchunk; |
431 if ( (RIFFchunk != RIFF) || (WAVEmagic != WAVE) ) { | 441 RIFFchunk = RIFF; |
432 SDL_SetError("Unrecognized file type (not WAVE)"); | 442 } else { |
433 was_error = 1; | 443 WAVEmagic = SDL_ReadLE32(src); |
434 goto done; | 444 } |
435 } | 445 if ((RIFFchunk != RIFF) || (WAVEmagic != WAVE)) { |
436 headerDiff += sizeof(Uint32); /* for WAVE */ | 446 SDL_SetError("Unrecognized file type (not WAVE)"); |
437 | 447 was_error = 1; |
438 /* Read the audio data format chunk */ | 448 goto done; |
439 chunk.data = NULL; | 449 } |
440 do { | 450 headerDiff += sizeof(Uint32); /* for WAVE */ |
441 if ( chunk.data != NULL ) { | 451 |
442 SDL_free(chunk.data); | 452 /* Read the audio data format chunk */ |
443 } | 453 chunk.data = NULL; |
444 lenread = ReadChunk(src, &chunk); | 454 do { |
445 if ( lenread < 0 ) { | 455 if (chunk.data != NULL) { |
446 was_error = 1; | 456 SDL_free(chunk.data); |
447 goto done; | 457 } |
448 } | 458 lenread = ReadChunk(src, &chunk); |
449 /* 2 Uint32's for chunk header+len, plus the lenread */ | 459 if (lenread < 0) { |
450 headerDiff += lenread + 2 * sizeof(Uint32); | 460 was_error = 1; |
451 } while ( (chunk.magic == FACT) || (chunk.magic == LIST) ); | 461 goto done; |
452 | 462 } |
453 /* Decode the audio data format */ | 463 /* 2 Uint32's for chunk header+len, plus the lenread */ |
454 format = (WaveFMT *)chunk.data; | 464 headerDiff += lenread + 2 * sizeof(Uint32); |
455 if ( chunk.magic != FMT ) { | 465 } |
456 SDL_SetError("Complex WAVE files not supported"); | 466 while ((chunk.magic == FACT) || (chunk.magic == LIST)); |
457 was_error = 1; | 467 |
458 goto done; | 468 /* Decode the audio data format */ |
459 } | 469 format = (WaveFMT *) chunk.data; |
460 MS_ADPCM_encoded = IMA_ADPCM_encoded = 0; | 470 if (chunk.magic != FMT) { |
461 switch (SDL_SwapLE16(format->encoding)) { | 471 SDL_SetError("Complex WAVE files not supported"); |
462 case PCM_CODE: | 472 was_error = 1; |
463 /* We can understand this */ | 473 goto done; |
464 break; | 474 } |
465 case MS_ADPCM_CODE: | 475 MS_ADPCM_encoded = IMA_ADPCM_encoded = 0; |
466 /* Try to understand this */ | 476 switch (SDL_SwapLE16(format->encoding)) { |
467 if ( InitMS_ADPCM(format) < 0 ) { | 477 case PCM_CODE: |
468 was_error = 1; | 478 /* We can understand this */ |
469 goto done; | 479 break; |
470 } | 480 case MS_ADPCM_CODE: |
471 MS_ADPCM_encoded = 1; | 481 /* Try to understand this */ |
472 break; | 482 if (InitMS_ADPCM(format) < 0) { |
473 case IMA_ADPCM_CODE: | 483 was_error = 1; |
474 /* Try to understand this */ | 484 goto done; |
475 if ( InitIMA_ADPCM(format) < 0 ) { | 485 } |
476 was_error = 1; | 486 MS_ADPCM_encoded = 1; |
477 goto done; | 487 break; |
478 } | 488 case IMA_ADPCM_CODE: |
479 IMA_ADPCM_encoded = 1; | 489 /* Try to understand this */ |
480 break; | 490 if (InitIMA_ADPCM(format) < 0) { |
481 case MP3_CODE: | 491 was_error = 1; |
482 SDL_SetError("MPEG Layer 3 data not supported", | 492 goto done; |
483 SDL_SwapLE16(format->encoding)); | 493 } |
484 was_error = 1; | 494 IMA_ADPCM_encoded = 1; |
485 goto done; | 495 break; |
486 default: | 496 case MP3_CODE: |
487 SDL_SetError("Unknown WAVE data format: 0x%.4x", | 497 SDL_SetError("MPEG Layer 3 data not supported", |
488 SDL_SwapLE16(format->encoding)); | 498 SDL_SwapLE16(format->encoding)); |
489 was_error = 1; | 499 was_error = 1; |
490 goto done; | 500 goto done; |
491 } | 501 default: |
492 SDL_memset(spec, 0, (sizeof *spec)); | 502 SDL_SetError("Unknown WAVE data format: 0x%.4x", |
493 spec->freq = SDL_SwapLE32(format->frequency); | 503 SDL_SwapLE16(format->encoding)); |
494 switch (SDL_SwapLE16(format->bitspersample)) { | 504 was_error = 1; |
495 case 4: | 505 goto done; |
496 if ( MS_ADPCM_encoded || IMA_ADPCM_encoded ) { | 506 } |
497 spec->format = AUDIO_S16; | 507 SDL_memset(spec, 0, (sizeof *spec)); |
498 } else { | 508 spec->freq = SDL_SwapLE32(format->frequency); |
499 was_error = 1; | 509 switch (SDL_SwapLE16(format->bitspersample)) { |
500 } | 510 case 4: |
501 break; | 511 if (MS_ADPCM_encoded || IMA_ADPCM_encoded) { |
502 case 8: | 512 spec->format = AUDIO_S16; |
503 spec->format = AUDIO_U8; | 513 } else { |
504 break; | 514 was_error = 1; |
505 case 16: | 515 } |
506 spec->format = AUDIO_S16; | 516 break; |
507 break; | 517 case 8: |
508 default: | 518 spec->format = AUDIO_U8; |
509 was_error = 1; | 519 break; |
510 break; | 520 case 16: |
511 } | 521 spec->format = AUDIO_S16; |
512 if ( was_error ) { | 522 break; |
513 SDL_SetError("Unknown %d-bit PCM data format", | 523 default: |
514 SDL_SwapLE16(format->bitspersample)); | 524 was_error = 1; |
515 goto done; | 525 break; |
516 } | 526 } |
517 spec->channels = (Uint8)SDL_SwapLE16(format->channels); | 527 if (was_error) { |
518 spec->samples = 4096; /* Good default buffer size */ | 528 SDL_SetError("Unknown %d-bit PCM data format", |
519 | 529 SDL_SwapLE16(format->bitspersample)); |
520 /* Read the audio data chunk */ | 530 goto done; |
521 *audio_buf = NULL; | 531 } |
522 do { | 532 spec->channels = (Uint8) SDL_SwapLE16(format->channels); |
523 if ( *audio_buf != NULL ) { | 533 spec->samples = 4096; /* Good default buffer size */ |
524 SDL_free(*audio_buf); | 534 |
525 } | 535 /* Read the audio data chunk */ |
526 lenread = ReadChunk(src, &chunk); | 536 *audio_buf = NULL; |
527 if ( lenread < 0 ) { | 537 do { |
528 was_error = 1; | 538 if (*audio_buf != NULL) { |
529 goto done; | 539 SDL_free(*audio_buf); |
530 } | 540 } |
531 *audio_len = lenread; | 541 lenread = ReadChunk(src, &chunk); |
532 *audio_buf = chunk.data; | 542 if (lenread < 0) { |
533 if(chunk.magic != DATA) headerDiff += lenread + 2 * sizeof(Uint32); | 543 was_error = 1; |
534 } while ( chunk.magic != DATA ); | 544 goto done; |
535 headerDiff += 2 * sizeof(Uint32); /* for the data chunk and len */ | 545 } |
536 | 546 *audio_len = lenread; |
537 if ( MS_ADPCM_encoded ) { | 547 *audio_buf = chunk.data; |
538 if ( MS_ADPCM_decode(audio_buf, audio_len) < 0 ) { | 548 if (chunk.magic != DATA) |
539 was_error = 1; | 549 headerDiff += lenread + 2 * sizeof(Uint32); |
540 goto done; | 550 } |
541 } | 551 while (chunk.magic != DATA); |
542 } | 552 headerDiff += 2 * sizeof(Uint32); /* for the data chunk and len */ |
543 if ( IMA_ADPCM_encoded ) { | 553 |
544 if ( IMA_ADPCM_decode(audio_buf, audio_len) < 0 ) { | 554 if (MS_ADPCM_encoded) { |
545 was_error = 1; | 555 if (MS_ADPCM_decode(audio_buf, audio_len) < 0) { |
546 goto done; | 556 was_error = 1; |
547 } | 557 goto done; |
548 } | 558 } |
549 | 559 } |
550 /* Don't return a buffer that isn't a multiple of samplesize */ | 560 if (IMA_ADPCM_encoded) { |
551 samplesize = ((spec->format & 0xFF)/8)*spec->channels; | 561 if (IMA_ADPCM_decode(audio_buf, audio_len) < 0) { |
552 *audio_len &= ~(samplesize-1); | 562 was_error = 1; |
553 | 563 goto done; |
554 done: | 564 } |
555 if ( format != NULL ) { | 565 } |
556 SDL_free(format); | 566 |
557 } | 567 /* Don't return a buffer that isn't a multiple of samplesize */ |
558 if ( src ) { | 568 samplesize = ((spec->format & 0xFF) / 8) * spec->channels; |
559 if ( freesrc ) { | 569 *audio_len &= ~(samplesize - 1); |
560 SDL_RWclose(src); | 570 |
561 } else { | 571 done: |
562 /* seek to the end of the file (given by the RIFF chunk) */ | 572 if (format != NULL) { |
563 SDL_RWseek(src, wavelen - chunk.length - headerDiff, RW_SEEK_CUR); | 573 SDL_free(format); |
564 } | 574 } |
565 } | 575 if (src) { |
566 if ( was_error ) { | 576 if (freesrc) { |
567 spec = NULL; | 577 SDL_RWclose(src); |
568 } | 578 } else { |
569 return(spec); | 579 /* seek to the end of the file (given by the RIFF chunk) */ |
580 SDL_RWseek(src, wavelen - chunk.length - headerDiff, RW_SEEK_CUR); | |
581 } | |
582 } | |
583 if (was_error) { | |
584 spec = NULL; | |
585 } | |
586 return (spec); | |
570 } | 587 } |
571 | 588 |
572 /* Since the WAV memory is allocated in the shared library, it must also | 589 /* Since the WAV memory is allocated in the shared library, it must also |
573 be freed here. (Necessary under Win32, VC++) | 590 be freed here. (Necessary under Win32, VC++) |
574 */ | 591 */ |
575 void SDL_FreeWAV(Uint8 *audio_buf) | 592 void |
576 { | 593 SDL_FreeWAV(Uint8 * audio_buf) |
577 if ( audio_buf != NULL ) { | 594 { |
578 SDL_free(audio_buf); | 595 if (audio_buf != NULL) { |
579 } | 596 SDL_free(audio_buf); |
580 } | 597 } |
581 | 598 } |
582 static int ReadChunk(SDL_RWops *src, Chunk *chunk) | 599 |
583 { | 600 static int |
584 chunk->magic = SDL_ReadLE32(src); | 601 ReadChunk(SDL_RWops * src, Chunk * chunk) |
585 chunk->length = SDL_ReadLE32(src); | 602 { |
586 chunk->data = (Uint8 *)SDL_malloc(chunk->length); | 603 chunk->magic = SDL_ReadLE32(src); |
587 if ( chunk->data == NULL ) { | 604 chunk->length = SDL_ReadLE32(src); |
588 SDL_Error(SDL_ENOMEM); | 605 chunk->data = (Uint8 *) SDL_malloc(chunk->length); |
589 return(-1); | 606 if (chunk->data == NULL) { |
590 } | 607 SDL_Error(SDL_ENOMEM); |
591 if ( SDL_RWread(src, chunk->data, chunk->length, 1) != 1 ) { | 608 return (-1); |
592 SDL_Error(SDL_EFREAD); | 609 } |
593 SDL_free(chunk->data); | 610 if (SDL_RWread(src, chunk->data, chunk->length, 1) != 1) { |
594 return(-1); | 611 SDL_Error(SDL_EFREAD); |
595 } | 612 SDL_free(chunk->data); |
596 return(chunk->length); | 613 return (-1); |
597 } | 614 } |
615 return (chunk->length); | |
616 } | |
617 | |
618 /* vi: set ts=4 sw=4 expandtab: */ |