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