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//========================================================================= 8 9/* 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. 14 * 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. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 */ 24 25#define GET_WAV_INT32(p) ((((uint32_t)((p)[3])) << 24) | \ 26 (((uint32_t)((p)[2])) << 16) | \ 27 (((uint32_t)((p)[1])) << 8) | \ 28 (((uint32_t)((p)[0])))) 29 30#define GET_WAV_INT16(p) ((((uint32_t)((p)[1])) << 8) | \ 31 (((uint32_t)((p)[0])))) 32 33static int 34_get_wavtags(char *filename, struct song_metadata *psong) 35{ 36 int fd; 37 uint32_t len; 38 unsigned char hdr[12]; 39 unsigned char fmt[16]; 40 uint32_t chunk_data_length; 41 uint32_t format_data_length = 0; 42 uint32_t compression_code = 0; 43 uint32_t channel_count = 0; 44 uint32_t sample_rate = 0; 45 uint32_t sample_bit_length = 0; 46 uint32_t bit_rate; 47 uint32_t data_length = 0; 48 uint32_t sec, ms; 49 50 uint32_t current_offset; 51 uint32_t block_len; 52 53 int found_fmt = 0; 54 int found_data = 0; 55 56 //DEBUG DPRINTF(E_DEBUG,L_SCANNER,"Getting WAV file info\n"); 57 58 if(!(fd = open(filename, O_RDONLY))) 59 { 60 DPRINTF(E_WARN, L_SCANNER, "Could not create file handle\n"); 61 return -1; 62 } 63 64 len = 12; 65 if(!(len = read(fd, hdr, len)) || (len != 12)) 66 { 67 DPRINTF(E_WARN, L_SCANNER, "Could not read wav header from %s\n", filename); 68 close(fd); 69 return -1; 70 } 71 72 /* I've found some wav files that have INFO tags 73 * in them... */ 74 if(strncmp((char*)hdr + 0, "RIFF", 4) || 75 strncmp((char*)hdr + 8, "WAVE", 4)) 76 { 77 DPRINTF(E_WARN, L_SCANNER, "Invalid wav header in %s\n", filename); 78 close(fd); 79 return -1; 80 } 81 82 chunk_data_length = GET_WAV_INT32(hdr + 4); 83 84 /* now, walk through the chunks */ 85 current_offset = 12; 86 while(!found_fmt && !found_data) 87 { 88 len = 8; 89 if(!(len = read(fd, hdr, len)) || (len != 8)) 90 { 91 close(fd); 92 DPRINTF(E_WARN, L_SCANNER, "Error reading block: %s\n", filename); 93 return -1; 94 } 95 96 block_len = GET_WAV_INT32(hdr + 4); 97 98 //DEBUG DPRINTF(E_DEBUG,L_SCANNER,"Read block %02x%02x%02x%02x (%c%c%c%c) of " 99 // "size %08x\n",hdr[0],hdr[1],hdr[2],hdr[3], 100 // hdr[0],hdr[1],hdr[2],hdr[3],block_len); 101 102 if(block_len < 0) 103 { 104 close(fd); 105 DPRINTF(E_WARN, L_SCANNER, "Bad block len: %s\n", filename); 106 return -1; 107 } 108 109 if(strncmp((char*)&hdr, "fmt ", 4) == 0) 110 { 111 found_fmt = 1; 112 //DEBUG DPRINTF(E_DEBUG,L_SCANNER,"Found 'fmt ' header\n"); 113 len = 16; 114 if(!read(fd, fmt, len) || (len != 16)) 115 { 116 close(fd); 117 DPRINTF(E_WARN, L_SCANNER, "Bad .wav file: can't read fmt: %s\n", 118 filename); 119 return -1; 120 } 121 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); 131 132 } 133 else if(strncmp((char*)&hdr, "data", 4) == 0) 134 { 135 //DEBUG DPRINTF(E_DEBUG,L_SCANNER,"Found 'data' header\n"); 136 data_length = block_len; 137 found_data = 1; 138 } 139 140 lseek(fd, current_offset + block_len + 8, SEEK_SET); 141 current_offset += block_len; 142 } 143 close(fd); 144 145 if(((format_data_length != 16) && (format_data_length != 18)) || 146 (compression_code != 1) || 147 (channel_count < 1)) 148 { 149 DPRINTF(E_WARN, L_SCANNER, "Invalid wav header in %s\n", filename); 150 return -1; 151 } 152 153 if( !data_length ) 154 data_length = psong->file_size - 44; 155 156 bit_rate = sample_rate * channel_count * ((sample_bit_length + 7) / 8) * 8; 157 psong->bitrate = bit_rate; 158 psong->samplerate = sample_rate; 159 psong->channels = channel_count; 160 //DEBUG DPRINTF(E_DEBUG,L_SCANNER,"Data length: %d\n", data_length); 161 sec = data_length / (bit_rate / 8); 162 ms = ((data_length % (bit_rate / 8)) * 1000) / (bit_rate / 8); 163 psong->song_length = (sec * 1000) + ms; 164 //DEBUG DPRINTF(E_DEBUG,L_SCANNER,"Song length: %d\n", psong->song_length); 165 //DEBUG DPRINTF(E_DEBUG,L_SCANNER,"Bit rate: %d\n", psong->bitrate); 166 167 /* Upon further review, WAV files should be little-endian, and DLNA requires the LPCM profile to be big-endian. 168 asprintf(&(psong->mime), "audio/L16;rate=%d;channels=%d", psong->samplerate, psong->channels); 169 */ 170 171 return 0; 172} 173 174static int 175_get_wavfileinfo(char *filename, struct song_metadata *psong) 176{ 177 psong->lossless = 1; 178 /* Upon further review, WAV files should be little-endian, and DLNA requires the LPCM profile to be big-endian. 179 asprintf(&(psong->dlna_pn), "LPCM"); 180 */ 181 182 return 0; 183} 184