1 /* EasyTAG - Tag editor for audio files
2 * Copyright (C) 2014 David King <amigadave@amigadave.com>
3 * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 #include "config.h" /* For definition of ENABLE_FLAC. */
21
22 #ifdef ENABLE_FLAC
23
24 #include <glib/gi18n.h>
25 #include <errno.h>
26
27 #include "et_core.h"
28 #include "flac_header.h"
29 #include "flac_private.h"
30 #include "misc.h"
31
32 /* Header info of FLAC file */
33 gboolean
et_flac_header_read_file_info(GFile * file,ET_File_Info * ETFileInfo,GError ** error)34 et_flac_header_read_file_info (GFile *file,
35 ET_File_Info *ETFileInfo,
36 GError **error)
37 {
38 GFileInfo *info;
39 FLAC__Metadata_Chain *chain;
40 EtFlacReadState state;
41 GFileInputStream *istream;
42 FLAC__IOCallbacks callbacks = { et_flac_read_func,
43 NULL, /* Do not set a write callback. */
44 et_flac_seek_func, et_flac_tell_func,
45 et_flac_eof_func,
46 et_flac_read_close_func };
47 FLAC__Metadata_Iterator *iter;
48 gsize metadata_len;
49
50 g_return_val_if_fail (file != NULL && ETFileInfo != NULL, FALSE);
51 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
52
53 /* Decoding FLAC file */
54 chain = FLAC__metadata_chain_new ();
55
56 if (chain == NULL)
57 {
58 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOMEM, "%s",
59 g_strerror (ENOMEM));
60 return FALSE;
61 }
62
63 istream = g_file_read (file, NULL, error);
64
65 if (istream == NULL)
66 {
67 FLAC__metadata_chain_delete (chain);
68 return FALSE;
69 }
70
71 state.eof = FALSE;
72 state.error = NULL;
73 state.istream = istream;
74 state.seekable = G_SEEKABLE (istream);
75
76 if (!FLAC__metadata_chain_read_with_callbacks (chain, &state, callbacks))
77 {
78 const FLAC__Metadata_ChainStatus status = FLAC__metadata_chain_status (chain);
79
80 g_debug ("Error reading FLAC metadata chain: %s:",
81 FLAC__Metadata_ChainStatusString[status]);
82 FLAC__metadata_chain_delete (chain);
83 /* TODO: Provide a dedicated error enum corresponding to status. */
84 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "%s",
85 _("Error opening FLAC file"));
86 et_flac_read_close_func (&state);
87 return FALSE;
88 }
89
90 iter = FLAC__metadata_iterator_new ();
91
92 if (iter == NULL)
93 {
94 et_flac_read_close_func (&state);
95 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOMEM, "%s",
96 g_strerror (ENOMEM));
97 return FALSE;
98 }
99
100 FLAC__metadata_iterator_init (iter, chain);
101 metadata_len = 0;
102
103 do
104 {
105 const FLAC__StreamMetadata *block;
106
107 block = FLAC__metadata_iterator_get_block (iter);
108
109 metadata_len += block->length;
110
111 if (FLAC__metadata_iterator_get_block_type (iter)
112 == FLAC__METADATA_TYPE_STREAMINFO)
113 {
114 const FLAC__StreamMetadata_StreamInfo *stream_info = &block->data.stream_info;
115 if (stream_info->sample_rate == 0)
116 {
117 gchar *filename;
118
119 /* This is invalid according to the FLAC specification, but
120 * such files have been observed in the wild. */
121 ETFileInfo->duration = 0;
122
123 filename = g_file_get_path (file);
124 g_debug ("Invalid FLAC sample rate of 0: %s", filename);
125 g_free (filename);
126 }
127 else
128 {
129 ETFileInfo->duration = stream_info->total_samples
130 / stream_info->sample_rate;
131 }
132
133 ETFileInfo->mode = stream_info->channels;
134 ETFileInfo->samplerate = stream_info->sample_rate;
135 ETFileInfo->version = 0; /* Not defined in FLAC file. */
136 }
137 }
138 while (FLAC__metadata_iterator_next (iter));
139
140 FLAC__metadata_iterator_delete (iter);
141 FLAC__metadata_chain_delete (chain);
142 et_flac_read_close_func (&state);
143 /* End of decoding FLAC file */
144
145 info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE,
146 G_FILE_QUERY_INFO_NONE, NULL, NULL);
147
148 if (info)
149 {
150 ETFileInfo->size = g_file_info_get_size (info);
151 g_object_unref (info);
152 }
153 else
154 {
155 ETFileInfo->size = 0;
156 }
157
158 if (ETFileInfo->duration > 0 && ETFileInfo->size > 0)
159 {
160 /* Ignore metadata blocks, and use the remainder to calculate the
161 * average bitrate (including format overhead). */
162 ETFileInfo->bitrate = (ETFileInfo->size - metadata_len) * 8 /
163 ETFileInfo->duration / 1000;
164 }
165
166 return TRUE;
167 }
168
169 EtFileHeaderFields *
et_flac_header_display_file_info_to_ui(const ET_File * ETFile)170 et_flac_header_display_file_info_to_ui (const ET_File *ETFile)
171 {
172 EtFileHeaderFields *fields;
173 ET_File_Info *info;
174 gchar *time = NULL;
175 gchar *time1 = NULL;
176 gchar *size = NULL;
177 gchar *size1 = NULL;
178
179 info = ETFile->ETFileInfo;
180 fields = g_slice_new (EtFileHeaderFields);
181
182 fields->description = _("FLAC File");
183
184 /* Nothing to display */
185 fields->version_label = _("Encoder:");
186 fields->version = g_strdup ("flac");
187
188 /* Bitrate */
189 fields->bitrate = g_strdup_printf (_("%d kb/s"), info->bitrate);
190
191 /* Samplerate */
192 fields->samplerate = g_strdup_printf (_("%d Hz"), info->samplerate);
193
194 /* Mode */
195 fields->mode_label = _("Channels:");
196 fields->mode = g_strdup_printf ("%d", info->mode);
197
198 /* Size */
199 size = g_format_size (info->size);
200 size1 = g_format_size (ETCore->ETFileDisplayedList_TotalSize);
201 fields->size = g_strdup_printf ("%s (%s)", size, size1);
202 g_free (size);
203 g_free (size1);
204
205 /* Duration */
206 time = Convert_Duration (info->duration);
207 time1 = Convert_Duration (ETCore->ETFileDisplayedList_TotalDuration);
208 fields->duration = g_strdup_printf ("%s (%s)", time, time1);
209 g_free (time);
210 g_free (time1);
211
212 return fields;
213 }
214
215 void
et_flac_file_header_fields_free(EtFileHeaderFields * fields)216 et_flac_file_header_fields_free (EtFileHeaderFields *fields)
217 {
218 g_return_if_fail (fields != NULL);
219
220 g_free (fields->version);
221 g_free (fields->bitrate);
222 g_free (fields->samplerate);
223 g_free (fields->mode);
224 g_free (fields->size);
225 g_free (fields->duration);
226 g_slice_free (EtFileHeaderFields, fields);
227 }
228
229 #endif /* ENABLE_FLAC */
230