1 /* EasyTAG - tag editor for audio files
2  * Copyright (C) 2014 Abhinav Jangda (abhijangda@hotmail.com)
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the Free
6  * Software Foundation; either version 2 of the License, or (at your option)
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along with
15  * this program; if not, write to the Free Software Foundation, Inc., 51
16  * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "config.h" /* For definition of ENABLE_OPUS */
20 
21 #ifdef ENABLE_OPUS
22 
23 #include <glib/gi18n.h>
24 
25 #include "opus_header.h"
26 #include "et_core.h"
27 #include "charset.h"
28 #include "misc.h"
29 
30 /*
31  * et_opus_error_quark:
32  *
33  * To get EtOpusError domain.
34  *
35  * Returns: GQuark for EtOpusError domain
36  */
37 GQuark
et_opus_error_quark(void)38 et_opus_error_quark (void)
39 {
40     return g_quark_from_static_string ("et-opus-error-quark");
41 }
42 
43 /*
44  * et_opus_open_file:
45  * @filename: Filepath to open
46  * @error: GError or %NULL
47  *
48  * Opens an Opus file.
49  *
50  * Returns: a OggOpusFile on success or %NULL on error.
51  */
52 OggOpusFile *
et_opus_open_file(GFile * gfile,GError ** error)53 et_opus_open_file (GFile *gfile, GError **error)
54 {
55     OggOpusFile *file;
56     gchar *path;
57     int error_val;
58 
59     g_return_val_if_fail (error == NULL || *error == NULL, NULL);
60     g_return_val_if_fail (gfile != NULL, NULL);
61 
62     path = g_file_get_path (gfile);
63     /* Opusfile does the UTF-8 to UTF-16 translation on Windows
64      * automatically. */
65     file = op_open_file (path, &error_val);
66     g_free (path);
67 
68     if (!file)
69     {
70         /* Got error while opening opus file */
71         switch (error_val)
72         {
73             case OP_EREAD:
74                 g_set_error (error, ET_OPUS_ERROR, ET_OPUS_ERROR_READ,
75                              "Error reading file");
76                 g_assert (error == NULL || *error != NULL);
77                 return NULL;
78 
79             case OP_EFAULT:
80                 g_set_error (error, ET_OPUS_ERROR, ET_OPUS_ERROR_FAULT,
81                              "Memory allocation failure or internal library error");
82                 g_assert (error == NULL || *error != NULL);
83                 return NULL;
84 
85             case OP_EIMPL:
86                 g_set_error (error, ET_OPUS_ERROR, ET_OPUS_ERROR_IMPL,
87                              "Stream used an unimplemented feature");
88                 g_assert (error == NULL || *error != NULL);
89                 return NULL;
90 
91             case OP_EINVAL:
92                 g_set_error (error, ET_OPUS_ERROR, ET_OPUS_ERROR_INVAL,
93                              "seek () succeeded on this source but tell () did not");
94                 g_assert (error == NULL || *error != NULL);
95                 return NULL;
96 
97             case OP_ENOTFORMAT:
98                 g_set_error (error, ET_OPUS_ERROR, ET_OPUS_ERROR_NOTFORMAT,
99                              "No logical stream found in a link");
100                 g_assert (error == NULL || *error != NULL);
101                 return NULL;
102 
103             case OP_EBADHEADER:
104                 g_set_error (error, ET_OPUS_ERROR, ET_OPUS_ERROR_BADHEADER,
105                              "Corrupted header packet");
106                 g_assert (error == NULL || *error != NULL);
107                 return NULL;
108 
109             case OP_EVERSION:
110                 g_set_error (error, ET_OPUS_ERROR, ET_OPUS_ERROR_VERSION,
111                              "ID header contained an unrecognized version number");
112                 g_assert (error == NULL || *error != NULL);
113                 return NULL;
114 
115             case OP_EBADLINK:
116                 g_set_error (error, ET_OPUS_ERROR, ET_OPUS_ERROR_BADLINK,
117                              "Corrupted link found");
118                 g_assert (error == NULL || *error != NULL);
119                 return NULL;
120 
121             case OP_EBADTIMESTAMP:
122                 g_set_error (error, ET_OPUS_ERROR, ET_OPUS_ERROR_BADTIMESTAMP,
123                              "First/last timestamp in a link failed checks");
124                 g_assert (error == NULL || *error != NULL);
125                 return NULL;
126             default:
127                 g_assert_not_reached ();
128                 break;
129         }
130     }
131 
132     return file;
133 }
134 
135 /*
136  * et_opus_read_file_info:
137  * @file: file to read info from
138  * @ETFileInfo: ET_File_Info to put information into
139  * @error: a GError or %NULL
140  *
141  * Read header information of an Opus file.
142  *
143  * Returns: %TRUE if successful otherwise %FALSE
144  */
145 gboolean
et_opus_read_file_info(GFile * gfile,ET_File_Info * ETFileInfo,GError ** error)146 et_opus_read_file_info (GFile *gfile, ET_File_Info *ETFileInfo,
147                         GError **error)
148 {
149     OggOpusFile *file;
150     const OpusHead* head;
151     GFileInfo *info;
152 
153     g_return_val_if_fail (gfile != NULL && ETFileInfo != NULL, FALSE);
154     g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
155 
156     file = et_opus_open_file (gfile, error);
157 
158     if (!file)
159     {
160         g_assert (error == NULL || *error != NULL);
161         return FALSE;
162     }
163 
164     /* FIXME: Improve error-checking. */
165     head = op_head (file, -1);
166     /* TODO: Read the vendor string from the Vorbis comment? */
167     ETFileInfo->version = head->version;
168     ETFileInfo->bitrate = op_bitrate (file, -1) / 1000;
169     ETFileInfo->mode = head->channel_count;
170 
171     /* All Opus audio is encoded at 48 kHz, but the input sample rate can
172      * differ, and then input_sample_rate will be set. */
173     if (head->input_sample_rate != 0)
174     {
175         ETFileInfo->samplerate = head->input_sample_rate;
176     }
177     else
178     {
179         ETFileInfo->samplerate = 48000;
180     }
181 
182     ETFileInfo->duration = op_pcm_total (file, -1) / 48000;
183     op_free (file);
184 
185     info = g_file_query_info (gfile, G_FILE_ATTRIBUTE_STANDARD_SIZE,
186                               G_FILE_QUERY_INFO_NONE, NULL, NULL);
187 
188     if (info)
189     {
190         ETFileInfo->size = g_file_info_get_size (info);
191         g_object_unref (info);
192     }
193     else
194     {
195         ETFileInfo->size = 0;
196     }
197 
198     g_assert (error == NULL || *error == NULL);
199     return TRUE;
200 }
201 
202 /*
203  * et_opus_header_display_file_info_to_ui:
204  * @ETFile: ET_File to display information
205  *
206  * Display header info from ET_File.
207  *
208  * Returns: a new #EtFileHeaderFields, free with
209  * et_opus_file_header_fields_free()
210  */
211 EtFileHeaderFields *
et_opus_header_display_file_info_to_ui(const ET_File * ETFile)212 et_opus_header_display_file_info_to_ui (const ET_File *ETFile)
213 {
214     EtFileHeaderFields *fields;
215     ET_File_Info *info;
216     gchar *time = NULL;
217     gchar *time1 = NULL;
218     gchar *size = NULL;
219     gchar *size1 = NULL;
220 
221     info = ETFile->ETFileInfo;
222     fields = g_slice_new (EtFileHeaderFields);
223 
224     fields->description = _("Opus File");
225 
226     /* Encoder version */
227     fields->version_label = _("Encoder:");
228     fields->version = g_strdup_printf ("%d", info->version);
229 
230     /* Bitrate */
231     fields->bitrate = g_strdup_printf (_("%d kb/s"), info->bitrate);
232 
233     /* Samplerate */
234     fields->samplerate = g_strdup_printf (_("%d Hz"), info->samplerate);
235 
236     /* Mode */
237     fields->mode_label = _("Channels:");
238     fields->mode = g_strdup_printf ("%d", info->mode);
239 
240     /* Size */
241     size = g_format_size (info->size);
242     size1 = g_format_size (ETCore->ETFileDisplayedList_TotalSize);
243     fields->size = g_strdup_printf ("%s (%s)", size, size1);
244     g_free (size);
245     g_free (size1);
246 
247     /* Duration */
248     time = Convert_Duration (info->duration);
249     time1 = Convert_Duration (ETCore->ETFileDisplayedList_TotalDuration);
250     fields->duration = g_strdup_printf ("%s (%s)", time, time1);
251     g_free (time);
252     g_free (time1);
253 
254     return fields;
255 }
256 
257 void
et_opus_file_header_fields_free(EtFileHeaderFields * fields)258 et_opus_file_header_fields_free (EtFileHeaderFields *fields)
259 {
260     g_return_if_fail (fields != NULL);
261 
262     g_free (fields->version);
263     g_free (fields->bitrate);
264     g_free (fields->samplerate);
265     g_free (fields->mode);
266     g_free (fields->size);
267     g_free (fields->duration);
268     g_slice_free (EtFileHeaderFields, fields);
269 }
270 
271 #endif /* ENABLE_OPUS */
272