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_OGG. */
21 
22 #ifdef ENABLE_OGG
23 
24 #include <glib/gi18n.h>
25 #include <errno.h>
26 #include <vorbis/codec.h>
27 #include <vorbis/vorbisfile.h>
28 
29 #ifdef ENABLE_SPEEX
30 #include <speex/speex_header.h>
31 #include "vcedit.h"
32 #endif
33 
34 #include "ogg_header.h"
35 #include "et_core.h"
36 #include "misc.h"
37 
38 /*
39  * et_ogg_error_quark:
40  *
41  * To get EtOGGError domain.
42  *
43  * Returns: GQuark for EtOGGError domain
44  */
45 GQuark
et_ogg_error_quark(void)46 et_ogg_error_quark (void)
47 {
48     return g_quark_from_static_string ("et-ogg-error-quark");
49 }
50 
51 /*
52  * EtOggHeaderState:
53  * @file: the Ogg file which is currently being parsed
54  * @istream: an input stream for the current Ogg file
55  * @error: either the most recent error, or %NULL
56  *
57  * The current state of the Ogg parser, for passing between the callbacks used
58  * in ov_open_callbacks().
59  */
60 typedef struct
61 {
62     GFile *file;
63     GInputStream *istream;
64     GError *error;
65 } EtOggHeaderState;
66 
67 /*
68  * et_ogg_read_func:
69  * @ptr: the buffer to fill with data
70  * @size: the size of individual reads
71  * @nmemb: the number of members to read
72  * @datasource: the Ogg parser state
73  *
74  * Read a number of bytes from the Ogg file.
75  *
76  * Returns: the number of bytes read from the stream. Returns 0 on end-of-file.
77  * Sets errno and returns 0 on error
78  */
79 static size_t
et_ogg_read_func(void * ptr,size_t size,size_t nmemb,void * datasource)80 et_ogg_read_func (void *ptr, size_t size, size_t nmemb, void *datasource)
81 {
82     EtOggHeaderState *state = (EtOggHeaderState *)datasource;
83     gssize bytes_read;
84 
85     bytes_read = g_input_stream_read (state->istream, ptr, size * nmemb, NULL,
86                                       &state->error);
87 
88     if (bytes_read == -1)
89     {
90         /* FIXME: Convert state->error to errno. */
91         errno = EIO;
92         return 0;
93     }
94     else
95     {
96         return bytes_read;
97     }
98 }
99 
100 /*
101  * et_ogg_seek_func:
102  * @datasource: the Ogg parser state
103  * @offset: the number of bytes to seek
104  * @whence: either %SEEK_SET, %SEEK_CUR or %SEEK_END
105  *
106  * Seek in the currently-open Ogg file.
107  *
108  * Returns: 0 on success, -1 and sets errno on error
109  */
110 static int
et_ogg_seek_func(void * datasource,ogg_int64_t offset,int whence)111 et_ogg_seek_func (void *datasource, ogg_int64_t offset, int whence)
112 {
113     EtOggHeaderState *state = (EtOggHeaderState *)datasource;
114     GSeekType seektype;
115 
116     if (!g_seekable_can_seek (G_SEEKABLE (state->istream)))
117     {
118         return -1;
119     }
120     else
121     {
122         switch (whence)
123         {
124             case SEEK_SET:
125                 seektype = G_SEEK_SET;
126                 break;
127             case SEEK_CUR:
128                 seektype = G_SEEK_CUR;
129                 break;
130             case SEEK_END:
131                 seektype = G_SEEK_END;
132                 break;
133             default:
134                 errno = EINVAL;
135                 return -1;
136         }
137 
138         if (g_seekable_seek (G_SEEKABLE (state->istream), offset, seektype,
139                              NULL, &state->error))
140         {
141             return 0;
142         }
143         else
144         {
145             errno = EBADF;
146             return -1;
147         }
148     }
149 }
150 
151 /*
152  * et_ogg_close_func:
153  * @datasource: the Ogg parser state
154  *
155  * Close the Ogg stream and invalidate the parser state given by @datasource.
156  * Be sure to check the error field before invaidating the state.
157  *
158  * Returns: 0
159  */
160 static int
et_ogg_close_func(void * datasource)161 et_ogg_close_func (void *datasource)
162 {
163     EtOggHeaderState *state = (EtOggHeaderState *)datasource;
164 
165     g_clear_object (&state->istream);
166     g_clear_error (&state->error);
167 
168     return 0;
169 }
170 
171 /*
172  * et_ogg_tell_func:
173  * @datasource: the Ogg parser state
174  *
175  * Tell the current position of the stream from the beginning of the Ogg file.
176  *
177  * Returns: the current position in the Ogg file
178  */
179 static long
et_ogg_tell_func(void * datasource)180 et_ogg_tell_func (void *datasource)
181 {
182     EtOggHeaderState *state = (EtOggHeaderState *)datasource;
183 
184     return g_seekable_tell (G_SEEKABLE (state->istream));
185 }
186 
187 gboolean
et_ogg_header_read_file_info(GFile * file,ET_File_Info * ETFileInfo,GError ** error)188 et_ogg_header_read_file_info (GFile *file,
189                               ET_File_Info *ETFileInfo,
190                               GError **error)
191 {
192     OggVorbis_File vf;
193     vorbis_info *vi;
194     gint encoder_version = 0;
195     gint channels = 0;
196     glong rate = 0;
197     glong bitrate_nominal = 0;
198     gdouble duration = 0;
199     gint res;
200     ov_callbacks callbacks = { et_ogg_read_func, et_ogg_seek_func,
201                                et_ogg_close_func, et_ogg_tell_func };
202     EtOggHeaderState state;
203     GFileInfo *info;
204 
205     g_return_val_if_fail (file != NULL && ETFileInfo != NULL, FALSE);
206     g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
207 
208     info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE,
209                               G_FILE_QUERY_INFO_NONE, NULL, error);
210 
211     if (!info)
212     {
213         return FALSE;
214     }
215 
216     ETFileInfo->size = g_file_info_get_size (info);
217     g_object_unref (info);
218 
219     state.file = file;
220     state.error = NULL;
221     state.istream = G_INPUT_STREAM (g_file_read (state.file, NULL,
222                                                  &state.error));
223 
224     if (!state.istream)
225     {
226         g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
227                      _("Error while opening file: %s"), state.error->message);
228         return FALSE;
229     }
230 
231     if ((res = ov_open_callbacks (&state, &vf, NULL, 0, callbacks)) == 0)
232     {
233         if ( (vi=ov_info(&vf,0)) != NULL )
234         {
235             encoder_version = vi->version;         // Vorbis encoder version used to create this bitstream.
236             channels        = vi->channels;        // Number of channels in bitstream.
237             rate            = vi->rate;            // (Hz) Sampling rate of the bitstream.
238             bitrate_nominal = vi->bitrate_nominal; // (b/s) Specifies the average bitrate for a VBR bitstream.
239         }
240         else
241         {
242             g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "%s",
243                          _("The specified bitstream does not exist or the "
244                          "file has been initialized improperly"));
245             et_ogg_close_func (&state);
246             return FALSE;
247         }
248 
249         duration        = ov_time_total(&vf,-1); // (s) Total time.
250 
251         ov_clear(&vf); // This close also the file
252     }else
253     {
254         /* On error. */
255         if (state.error)
256         {
257             gchar *message;
258 
259             switch (res)
260             {
261                 case OV_EREAD:
262                     message = _("Read from media returned an error");
263                     break;
264                 case OV_ENOTVORBIS:
265                     message = _("Bitstream is not Vorbis data");
266                     break;
267                 case OV_EVERSION:
268                     message = _("Vorbis version mismatch");
269                     break;
270                 case OV_EBADHEADER:
271                     message = _("Invalid Vorbis bitstream header");
272                     break;
273                 case OV_EFAULT:
274                     message = _("Internal logic fault, indicates a bug or heap/stack corruption");
275                     break;
276                 default:
277                     message = _("Error reading tags from file");
278                     break;
279             }
280 
281             g_set_error (error, state.error->domain, state.error->code,
282                          "%s", message);
283             et_ogg_close_func (&state);
284             return FALSE;
285         }
286 
287         et_ogg_close_func (&state);
288     }
289 
290     ETFileInfo->version    = encoder_version;
291     ETFileInfo->bitrate    = bitrate_nominal/1000;
292     ETFileInfo->samplerate = rate;
293     ETFileInfo->mode       = channels;
294     ETFileInfo->duration   = duration;
295 
296     return TRUE;
297 }
298 
299 
300 #ifdef ENABLE_SPEEX
301 
302 gboolean
et_speex_header_read_file_info(GFile * file,ET_File_Info * ETFileInfo,GError ** error)303 et_speex_header_read_file_info (GFile *file,
304                                 ET_File_Info *ETFileInfo,
305                                 GError **error)
306 {
307     EtOggState *state;
308     const SpeexHeader *si;
309     const gchar *encoder_version = NULL;
310     gint channels = 0;
311     glong rate = 0;
312     glong bitrate = 0;
313     gdouble duration = 0;
314     GFileInfo *info;
315     GError *tmp_error = NULL;
316 
317     g_return_val_if_fail (file != NULL && ETFileInfo != NULL, FALSE);
318     g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
319 
320     state = vcedit_new_state();    // Allocate memory for 'state'
321 
322     if (!vcedit_open (state, file, &tmp_error))
323     {
324         g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
325                      _("Failed to open file as Vorbis: %s"),
326                      tmp_error->message);
327         g_error_free (tmp_error);
328         vcedit_clear (state);
329         return FALSE;
330     }
331 
332     info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE,
333                               G_FILE_QUERY_INFO_NONE, NULL, error);
334 
335     if (!info)
336     {
337         return FALSE;
338     }
339 
340     ETFileInfo->size = g_file_info_get_size (info);
341     g_object_unref (info);
342 
343     /* Get Speex information. */
344     if ((si = vcedit_speex_header (state)) != NULL)
345     {
346         encoder_version = si->speex_version;
347         channels        = si->nb_channels;        // Number of channels in bitstream.
348         rate            = si->rate;               // (Hz) Sampling rate of the bitstream.
349         bitrate         = si->bitrate;            // (b/s) Specifies the bitrate
350 
351         duration        = 0;//ov_time_total(&vf,-1); // (s) Total time.
352 
353         //g_print("play time: %ld s\n",(long)ov_time_total(&vf,-1));
354         //g_print("serialnumber: %ld\n",(long)ov_serialnumber(&vf,-1));
355         //g_print("compressed length: %ld bytes\n",(long)(ov_raw_total(&vf,-1)));
356     }
357 
358     ETFileInfo->mpc_version = g_strdup(encoder_version);
359     ETFileInfo->bitrate     = bitrate/1000;
360     ETFileInfo->samplerate  = rate;
361     ETFileInfo->mode        = channels;
362     //if (bitrate > 0)
363     //    ETFileInfo->duration = filesize*8/bitrate/1000; // FIXME : Approximation!! Needs to remove tag size!
364     //else
365         ETFileInfo->duration   = duration;
366 
367     vcedit_clear(state);
368     return TRUE;
369 }
370 #endif
371 
372 EtFileHeaderFields *
et_ogg_header_display_file_info_to_ui(const ET_File * ETFile)373 et_ogg_header_display_file_info_to_ui (const ET_File *ETFile)
374 {
375     EtFileHeaderFields *fields;
376     ET_File_Info *info;
377     gchar *time = NULL;
378     gchar *time1 = NULL;
379     gchar *size = NULL;
380     gchar *size1 = NULL;
381 
382     info = ETFile->ETFileInfo;
383     fields = g_slice_new (EtFileHeaderFields);
384 
385     if (ETFile->ETFileDescription->FileType == OGG_FILE)
386     {
387         fields->description = _("Ogg Vorbis File");
388     }
389     else if (ETFile->ETFileDescription->FileType == SPEEX_FILE)
390     {
391         fields->description = _("Speex File");
392     }
393     else
394     {
395         g_assert_not_reached ();
396     }
397 
398     /* Encoder version */
399     fields->version_label = _("Encoder:");
400 
401     if (!info->mpc_version)
402     {
403         fields->version = g_strdup_printf ("%d", info->version);
404     }
405     else
406     {
407         fields->version = g_strdup (info->mpc_version);
408     }
409 
410     /* Bitrate */
411     fields->bitrate = g_strdup_printf (_("%d kb/s"), info->bitrate);
412 
413     /* Samplerate */
414     fields->samplerate = g_strdup_printf (_("%d Hz"), info->samplerate);
415 
416     /* Mode */
417     fields->mode_label = _("Channels:");
418     fields->mode = g_strdup_printf ("%d", info->mode);
419 
420     /* Size */
421     size = g_format_size (info->size);
422     size1 = g_format_size (ETCore->ETFileDisplayedList_TotalSize);
423     fields->size = g_strdup_printf ("%s (%s)", size, size1);
424     g_free (size);
425     g_free (size1);
426 
427     /* Duration */
428     time = Convert_Duration (info->duration);
429     time1 = Convert_Duration (ETCore->ETFileDisplayedList_TotalDuration);
430     fields->duration = g_strdup_printf ("%s (%s)", time, time1);
431     g_free (time);
432     g_free (time1);
433 
434     return fields;
435 }
436 
437 void
et_ogg_file_header_fields_free(EtFileHeaderFields * fields)438 et_ogg_file_header_fields_free (EtFileHeaderFields *fields)
439 {
440     g_return_if_fail (fields != NULL);
441 
442     g_free (fields->version);
443     g_free (fields->bitrate);
444     g_free (fields->samplerate);
445     g_free (fields->mode);
446     g_free (fields->size);
447     g_free (fields->duration);
448     g_slice_free (EtFileHeaderFields, fields);
449 }
450 
451 #endif /* ENABLE_OGG */
452