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