1 //=========================================================================
2 // FILENAME : tagutils-wav.c
3 // DESCRIPTION : WAV metadata reader
4 //=========================================================================
5 // Copyright (c) 2009 NETGEAR, Inc. All Rights Reserved.
6 // based on software from Ron Pedde's FireFly Media Server project
7 //=========================================================================
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #define GET_WAV_INT32(p) ((((uint8_t)((p)[3])) << 24) | \
25 (((uint8_t)((p)[2])) << 16) | \
26 (((uint8_t)((p)[1])) << 8) | \
27 (((uint8_t)((p)[0]))))
29 #define GET_WAV_INT16(p) ((((uint8_t)((p)[1])) << 8) | \
30 (((uint8_t)((p)[0]))))
33 _get_wavtags(char *filename
, struct song_metadata
*psong
)
37 unsigned char hdr
[12];
38 unsigned char fmt
[16];
39 //uint32_t chunk_data_length;
40 uint32_t format_data_length
= 0;
41 uint32_t compression_code
= 0;
42 uint32_t channel_count
= 0;
43 uint32_t sample_rate
= 0;
44 uint32_t sample_bit_length
= 0;
46 uint32_t data_length
= 0;
49 uint32_t current_offset
;
52 //DEBUG DPRINTF(E_DEBUG,L_SCANNER,"Getting WAV file info\n");
54 if(!(fd
= open(filename
, O_RDONLY
)))
56 DPRINTF(E_WARN
, L_SCANNER
, "Could not create file handle\n");
61 if(!(len
= read(fd
, hdr
, len
)) || (len
!= 12))
63 DPRINTF(E_WARN
, L_SCANNER
, "Could not read wav header from %s\n", filename
);
68 /* I've found some wav files that have INFO tags
70 if(strncmp((char*)hdr
+ 0, "RIFF", 4) ||
71 strncmp((char*)hdr
+ 8, "WAVE", 4))
73 DPRINTF(E_WARN
, L_SCANNER
, "Invalid wav header in %s\n", filename
);
78 //chunk_data_length = GET_WAV_INT32(hdr + 4);
80 /* now, walk through the chunks */
82 while(current_offset
+ 8 < psong
->file_size
)
85 if(!(len
= read(fd
, hdr
, len
)) || (len
!= 8))
88 DPRINTF(E_WARN
, L_SCANNER
, "Error reading block: %s\n", filename
);
93 block_len
= GET_WAV_INT32(hdr
+ 4);
95 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Read block %02x%02x%02x%02x (%c%c%c%c) of "
96 // "size 0x%08x\n",hdr[0],hdr[1],hdr[2],hdr[3],
97 // isprint(hdr[0]) ? hdr[0] : '?',
98 // isprint(hdr[1]) ? hdr[1] : '?',
99 // isprint(hdr[2]) ? hdr[2] : '?',
100 // isprint(hdr[3]) ? hdr[3] : '?',
103 if(block_len
> psong
->file_size
)
106 DPRINTF(E_WARN
, L_SCANNER
, "Bad block len: %s\n", filename
);
110 if(strncmp((char*)&hdr
, "fmt ", 4) == 0)
112 //DEBUG DPRINTF(E_DEBUG,L_SCANNER,"Found 'fmt ' header\n");
114 if(read(fd
, fmt
, len
) != len
)
117 DPRINTF(E_WARN
, L_SCANNER
, "Bad .wav file: can't read fmt: %s\n",
122 format_data_length
= block_len
;
123 compression_code
= GET_WAV_INT16(fmt
);
124 channel_count
= GET_WAV_INT16(fmt
+ 2);
125 sample_rate
= GET_WAV_INT32(fmt
+ 4);
126 sample_bit_length
= GET_WAV_INT16(fmt
+ 14);
127 //DEBUG DPRINTF(E_DEBUG,L_SCANNER,"Compression code: %d\n",compression_code);
128 //DEBUG DPRINTF(E_DEBUG,L_SCANNER,"Channel count: %d\n",channel_count);
129 //DEBUG DPRINTF(E_DEBUG,L_SCANNER,"Sample Rate: %d\n",sample_rate);
130 //DEBUG DPRINTF(E_DEBUG,L_SCANNER,"Sample bit length %d\n",sample_bit_length);
133 else if(strncmp((char*)&hdr
, "data", 4) == 0)
135 //DEBUG DPRINTF(E_DEBUG,L_SCANNER,"Found 'data' header\n");
136 data_length
= block_len
;
139 else if(strncmp((char*)&hdr
, "LIST", 4) == 0)
147 len
= GET_WAV_INT32(hdr
+ 4);
148 if(len
> 65536 || len
< 9)
151 tags
= malloc(len
+1);
155 if(read(fd
, tags
, len
) < len
||
156 strncmp(tags
, "INFO", 4) != 0)
167 taglen
= GET_WAV_INT32(p
+ 4);
169 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "%.*s: %.*s (%d)\n", 4, p, taglen, p + 8, taglen);
172 DPRINTF(E_WARN
, L_SCANNER
, "Ignoring long tag [%.*s] in %s\n",
175 else if(strncmp(p
, "INAM", 4) == 0)
177 else if(strncmp(p
, "IALB", 4) == 0 ||
178 strncmp(p
, "IPRD", 4) == 0)
180 else if(strncmp(p
, "IGRE", 4) == 0 ||
181 strncmp(p
, "IGNR", 4) == 0)
183 else if(strncmp(p
, "ICMT", 4) == 0)
184 m
= &(psong
->comment
);
185 else if(strncmp(p
, "IART", 4) == 0)
186 m
= &(psong
->contributor
[ROLE_TRACKARTIST
]);
187 else if(strncmp(p
, "IAAR", 4) == 0)
188 m
= &(psong
->contributor
[ROLE_ALBUMARTIST
]);
189 else if(strncmp(p
, "ICOM", 4) == 0 ||
190 strncmp(p
, "IMUS", 4) == 0)
191 m
= &(psong
->contributor
[ROLE_COMPOSER
]);
192 else if(strncasecmp(p
, "ITRK", 4) == 0)
193 psong
->track
= atoi(p
+ 8);
194 else if(strncmp(p
, "ICRD", 4) == 0 ||
195 strncmp(p
, "IYER", 4) == 0)
196 psong
->year
= atoi(p
+ 8);
199 *m
= malloc(taglen
+ 1);
200 strncpy(*m
, p
+ 8, taglen
);
206 /* Handle some common WAV file malformations */
207 while (*p
== '\0' && off
< len
) {
215 lseek(fd
, current_offset
+ block_len
, SEEK_SET
);
216 current_offset
+= block_len
;
220 if(((format_data_length
!= 16) && (format_data_length
!= 18)) ||
221 (compression_code
!= 1) ||
224 DPRINTF(E_WARN
, L_SCANNER
, "Invalid wav header in %s\n", filename
);
229 data_length
= psong
->file_size
- 44;
231 bit_rate
= sample_rate
* channel_count
* ((sample_bit_length
+ 7) / 8) * 8;
232 psong
->bitrate
= bit_rate
;
233 psong
->samplerate
= sample_rate
;
234 psong
->channels
= channel_count
;
235 //DEBUG DPRINTF(E_DEBUG,L_SCANNER,"Data length: %d\n", data_length);
236 sec
= data_length
/ (bit_rate
/ 8);
237 ms
= ((data_length
% (bit_rate
/ 8)) * 1000) / (bit_rate
/ 8);
238 psong
->song_length
= (sec
* 1000) + ms
;
239 //DEBUG DPRINTF(E_DEBUG,L_SCANNER,"Song length: %d\n", psong->song_length);
240 //DEBUG DPRINTF(E_DEBUG,L_SCANNER,"Bit rate: %d\n", psong->bitrate);
242 /* Upon further review, WAV files should be little-endian, and DLNA requires the LPCM profile to be big-endian.
243 asprintf(&(psong->mime), "audio/L16;rate=%d;channels=%d", psong->samplerate, psong->channels);
250 _get_wavfileinfo(char *filename
, struct song_metadata
*psong
)
253 /* Upon further review, WAV files should be little-endian, and DLNA requires the LPCM profile to be big-endian.
254 asprintf(&(psong->dlna_pn), "LPCM");