Project

General

Profile

vorbis_v3.6jwt.cc

Jim Turner, March 06, 2015 19:39

 
1
/*
2
 * Copyright (C) Tony Arcieri <bascule@inferno.tusculum.edu>
3
 * Copyright (C) 2001-2002 Håvard Kvålen <havardk@xmms.org>
4
 * Copyright (C) 2007 William Pitcock <nenolod@sacredspiral.co.uk>
5
 * Copyright (C) 2008 Cristi Măgherușan <majeru@gentoo.ro>
6
 * Copyright (C) 2008 Eugene Zagidullin <e.asphyx@gmail.com>
7
 * Copyright (C) 2009-2011 Audacious Developers
8
 *
9
 * ReplayGain processing Copyright (C) 2002 Gian-Carlo Pascutto <gcp@sjeng.org>
10
 *
11
 * This program is free software; you can redistribute it and/or
12
 * modify it under the terms of the GNU General Public License
13
 * as published by the Free Software Foundation; either version 2
14
 * of the License, or (at your option) any later version.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU General Public License
22
 * along with this program; if not, write to the Free Software
23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24
 * 02110-1301, USA.
25
 *
26
 */
27

    
28
#include <glib.h>
29
#include <stdlib.h>
30
#include <string.h>
31
#include <math.h>
32

    
33
#include <ogg/ogg.h>
34
#include <vorbis/codec.h>
35
#include <vorbis/vorbisfile.h>
36

    
37
#define WANT_AUD_BSWAP
38
#define WANT_VFS_STDIO_COMPAT
39
#include <libaudcore/audstrings.h>
40
#include <libaudcore/plugin.h>
41
#include <libaudcore/runtime.h>
42

    
43
#include "vorbis.h"
44

    
45
EXPORT VorbisPlugin aud_plugin_instance;
46

    
47
static size_t ovcb_read (void * buffer, size_t size, size_t count, void * file)
48
{
49
    return ((VFSFile *) file)->fread (buffer, size, count);
50
}
51

    
52
static int ovcb_seek (void * file, ogg_int64_t offset, int whence)
53
{
54
    return ((VFSFile *) file)->fseek (offset, to_vfs_seek_type (whence));
55
}
56

    
57
static int ovcb_close (void * file)
58
{
59
    return 0;
60
}
61

    
62
static long ovcb_tell (void * file)
63
{
64
    return ((VFSFile *) file)->ftell ();
65
}
66

    
67
ov_callbacks vorbis_callbacks = {
68
    ovcb_read,
69
    ovcb_seek,
70
    ovcb_close,
71
    ovcb_tell
72
};
73

    
74
ov_callbacks vorbis_callbacks_stream = {
75
    ovcb_read,
76
    nullptr,
77
    ovcb_close,
78
    nullptr
79
};
80

    
81
ov_callbacks vorbis_callbacks_stdin = {   /* JWT:ADDED TO HANDLE INPUT FROM stdin */
82
    ovcb_read,
83
    NULL,
84
    NULL,
85
    NULL
86
};
87

    
88
bool VorbisPlugin::is_our_file (const char * filename, VFSFile & file)
89
{
90
    ogg_sync_state oy = {0};
91
    ogg_stream_state os = {0};
92
    ogg_page og = {0};
93
    ogg_packet op = {0};
94

    
95
    if (strstr(filename, "://-"))  return true; /*  JWT */
96
                
97
    bool result = false;
98

    
99
    ogg_sync_init (& oy);
100

    
101
    while (1)
102
    {
103
        int64_t bytes = ogg_sync_pageseek (& oy, & og);
104

    
105
        if (bytes < 0) /* skipped some bytes */
106
            continue;
107
        if (bytes > 0) /* got a page */
108
            break;
109

    
110
        void * buffer = ogg_sync_buffer (& oy, 2048);
111
        bytes = file.fread (buffer, 1, 2048);
112

    
113
        if (bytes <= 0)
114
            goto end;
115

    
116
        ogg_sync_wrote (& oy, bytes);
117
    }
118

    
119
    if (! ogg_page_bos (& og))
120
        goto end;
121

    
122
    ogg_stream_init (& os, ogg_page_serialno (& og));
123
    ogg_stream_pagein (& os, & og);
124

    
125
    if (ogg_stream_packetout (& os, & op) > 0 && vorbis_synthesis_idheader (& op))
126
        result = true;
127

    
128
end:
129
    ogg_sync_clear (& oy);
130
    ogg_stream_clear (& os);
131

    
132
    return result;
133
}
134

    
135
static void
136
set_tuple_str(Tuple &tuple, Tuple::Field field,
137
    vorbis_comment *comment, const char *key)
138
{
139
    tuple.set_str (field, vorbis_comment_query (comment, key, 0));
140
}
141

    
142
static Tuple
143
get_tuple_for_vorbisfile(OggVorbis_File * vorbisfile, const char *filename, bool stream)
144
{
145
    Tuple tuple;
146
    int length = -1;
147
    vorbis_comment *comment = nullptr;
148

    
149
    tuple.set_filename(filename);
150

    
151
    if (! stream)
152
        length = ov_time_total (vorbisfile, -1) * 1000;
153

    
154
    /* associate with tuple */
155
    tuple.set_int (Tuple::Length, length);
156

    
157
    if ((comment = ov_comment(vorbisfile, -1)) != nullptr) {
158
        char *tmps;
159
        set_tuple_str(tuple, Tuple::Title, comment, "title");
160
        set_tuple_str(tuple, Tuple::Artist, comment, "artist");
161
        set_tuple_str(tuple, Tuple::Album, comment, "album");
162
        set_tuple_str(tuple, Tuple::Genre, comment, "genre");
163
        set_tuple_str(tuple, Tuple::Comment, comment, "comment");
164

    
165
        if ((tmps = vorbis_comment_query(comment, "tracknumber", 0)) != nullptr)
166
            tuple.set_int (Tuple::Track, atoi(tmps));
167

    
168
        if ((tmps = vorbis_comment_query (comment, "date", 0)) != nullptr)
169
            tuple.set_int (Tuple::Year, atoi (tmps));
170
    }
171

    
172
    vorbis_info * info = ov_info (vorbisfile, -1);
173
    tuple.set_format ("Ogg Vorbis", info->channels, info->rate, info->bitrate_nominal / 1000);
174

    
175
    tuple.set_str (Tuple::MIMEType, "application/ogg");
176

    
177
    return tuple;
178
}
179

    
180
static float atof_no_locale (const char * string)
181
{
182
    float result = 0;
183
    bool negative = false;
184

    
185
    if (* string == '+')
186
        string ++;
187
    else if (* string == '-')
188
    {
189
        negative = true;
190
        string ++;
191
    }
192

    
193
    while (* string >= '0' && * string <= '9')
194
        result = 10 * result + (* string ++ - '0');
195

    
196
    if (* string == '.')
197
    {
198
        float place = 0.1;
199

    
200
        string ++;
201

    
202
        while (* string >= '0' && * string <= '9')
203
        {
204
            result += (* string ++ - '0') * place;
205
            place *= 0.1;
206
        }
207
    }
208

    
209
    return negative ? -result : result;
210
}
211

    
212
/* try to detect when metadata has changed */
213
static bool vorbis_fetch_tuple (OggVorbis_File * vf, const char * filename, bool stream, Tuple & tuple)
214
{
215
    String old_title = tuple.get_str (Tuple::Title);
216
    vorbis_comment * comment = ov_comment (vf, -1);
217
    const char * new_title = comment ? vorbis_comment_query (comment, "title", 0) : nullptr;
218

    
219
    if (! new_title || (old_title && ! strcmp (old_title, new_title)))
220
        return false;
221

    
222
    tuple = get_tuple_for_vorbisfile (vf, filename, stream);
223
    return true;
224
}
225

    
226
static bool vorbis_fetch_replaygain (OggVorbis_File * vf, ReplayGainInfo * rg_info)
227
{
228
    vorbis_comment *comment;
229
    char *rg_gain, *rg_peak;
230

    
231
    if (vf == nullptr || rg_info == nullptr || (comment = ov_comment(vf, -1)) == nullptr)
232
    {
233
        AUDDBG ("No replay gain info.\n");
234
        return false;
235
    }
236

    
237
    rg_gain = vorbis_comment_query(comment, "replaygain_album_gain", 0);
238
    if (!rg_gain) rg_gain = vorbis_comment_query(comment, "rg_audiophile", 0);    /* Old */
239
    rg_info->album_gain = (rg_gain != nullptr) ? atof_no_locale (rg_gain) : 0.0;
240
    AUDDBG ("Album gain: %s (%f)\n", rg_gain, rg_info->album_gain);
241

    
242
    rg_gain = vorbis_comment_query(comment, "replaygain_track_gain", 0);
243
    if (!rg_gain) rg_gain = vorbis_comment_query(comment, "rg_radio", 0);    /* Old */
244
    rg_info->track_gain = (rg_gain != nullptr) ? atof_no_locale (rg_gain) : 0.0;
245
    AUDDBG ("Track gain: %s (%f)\n", rg_gain, rg_info->track_gain);
246

    
247
    rg_peak = vorbis_comment_query(comment, "replaygain_album_peak", 0);
248
    rg_info->album_peak = rg_peak != nullptr ? atof_no_locale (rg_peak) : 0.0;
249
    AUDDBG ("Album peak: %s (%f)\n", rg_peak, rg_info->album_peak);
250

    
251
    rg_peak = vorbis_comment_query(comment, "replaygain_track_peak", 0);
252
    if (!rg_peak) rg_peak = vorbis_comment_query(comment, "rg_peak", 0);  /* Old */
253
    rg_info->track_peak = rg_peak != nullptr ? atof_no_locale (rg_peak) : 0.0;
254
    AUDDBG ("Track peak: %s (%f)\n", rg_peak, rg_info->track_peak);
255

    
256
    return true;
257
}
258

    
259
static long
260
vorbis_interleave_buffer(float **pcm, int samples, int ch, float *pcmout)
261
{
262
    int i, j;
263
    for (i = 0; i < samples; i++)
264
        for (j = 0; j < ch; j++)
265
            *pcmout++ = pcm[j][i];
266

    
267
    return ch * samples * sizeof(float);
268
}
269

    
270

    
271
#define PCM_FRAMES 1024
272
#define PCM_BUFSIZE (PCM_FRAMES * 2)
273

    
274
bool VorbisPlugin::play (const char * filename, VFSFile & file)
275
{
276
    vorbis_info *vi;
277
    OggVorbis_File vf;
278
    int last_section = -1;
279
    Tuple tuple;
280
    ReplayGainInfo rg_info;
281
    float pcmout[PCM_BUFSIZE*sizeof(float)], **pcm;
282
    int bytes, channels, samplerate, br;
283

    
284
    memset(&vf, 0, sizeof(vf));
285

    
286
    bool stream = (file.fsize () < 0);
287
    bool error = false;
288

    
289
    if (strstr(filename, "://-"))   /* JWT:ADDED TO HANDLE INPUT FROM stdin */
290
    {
291
        if (ov_open_callbacks (& file, & vf, nullptr, 0, vorbis_callbacks_stdin) < 0)
292
        {
293
            error = true;
294
            goto play_cleanup;
295
        }
296
    }
297
    else
298
    {
299
        if (ov_open_callbacks (& file, & vf, nullptr, 0, stream ?
300
         vorbis_callbacks_stream : vorbis_callbacks) < 0)
301
        {
302
            error = true;
303
            goto play_cleanup;
304
        }
305
    }
306
        
307

    
308
    vi = ov_info(&vf, -1);
309

    
310
    br = vi->bitrate_nominal;
311
    channels = vi->channels;
312
    samplerate = vi->rate;
313

    
314
    set_stream_bitrate (br);
315

    
316
    if (vorbis_fetch_tuple (& vf, filename, stream, tuple))
317
        set_playback_tuple (tuple.ref ());
318

    
319
    if (vorbis_fetch_replaygain (& vf, & rg_info))
320
        set_replay_gain (rg_info);
321

    
322
    open_audio (FMT_FLOAT, samplerate, channels);
323

    
324
    /*
325
     * Note that chaining changes things here; A vorbis file may
326
     * be a mix of different channels, bitrates and sample rates.
327
     * You can fetch the information for any section of the file
328
     * using the ov_ interface.
329
     */
330

    
331
    while (! check_stop ())
332
    {
333
        int seek_value = check_seek ();
334

    
335
        if (seek_value >= 0 && ov_time_seek (& vf, (double) seek_value / 1000) < 0)
336
        {
337
            AUDERR ("seek failed\n");
338
            error = true;
339
            break;
340
        }
341

    
342
        int current_section = last_section;
343
        bytes = ov_read_float(&vf, &pcm, PCM_FRAMES, &current_section);
344
        if (bytes == OV_HOLE)
345
            continue;
346

    
347
        if (bytes <= 0)
348
            break;
349

    
350
        bytes = vorbis_interleave_buffer (pcm, bytes, channels, pcmout);
351

    
352
        if (vorbis_fetch_tuple (& vf, filename, stream, tuple))
353
            set_playback_tuple (tuple.ref ());
354

    
355
        if (current_section != last_section)
356
        {
357
            /*
358
             * The info struct is different in each section.  vf
359
             * holds them all for the given bitstream.  This
360
             * requests the current one
361
             */
362
            vi = ov_info(&vf, -1);
363

    
364
            if (vi->rate != samplerate || vi->channels != channels)
365
            {
366
                samplerate = vi->rate;
367
                channels = vi->channels;
368

    
369
                if (vorbis_fetch_replaygain (& vf, & rg_info))
370
                    set_replay_gain (rg_info);
371

    
372
                open_audio (FMT_FLOAT, vi->rate, vi->channels);
373
            }
374
        }
375

    
376
        write_audio (pcmout, bytes);
377

    
378
        if (current_section != last_section)
379
        {
380
            set_stream_bitrate (br);
381
            last_section = current_section;
382
        }
383
    } /* main loop */
384

    
385
play_cleanup:
386

    
387
    ov_clear(&vf);
388
    return ! error;
389
}
390

    
391
Tuple VorbisPlugin::read_tuple (const char * filename, VFSFile & file)
392
{
393
    OggVorbis_File vfile;          /* avoid thread interaction */
394

    
395
    bool stream = (file.fsize () < 0);
396
    Tuple tuple;
397

    
398
    /*
399
     * The open function performs full stream detection and
400
     * machine initialization.  If it returns zero, the stream
401
     * *is* Vorbis and we're fully ready to decode.
402
     */
403
    if (strstr(filename, "://-"))   /* JWT:ADDED TO HANDLE INPUT FROM stdin */
404
    {
405
        tuple.set_filename (filename);
406
        return tuple;   /* JWT:ADDED TO HANDLE INPUT FROM stdin */
407
    }
408

    
409
    if (ov_open_callbacks (& file, & vfile, nullptr, 0, stream ?
410
            vorbis_callbacks_stream : vorbis_callbacks) < 0)
411
    {
412
        return Tuple ();
413
    }
414

    
415
    tuple = get_tuple_for_vorbisfile(&vfile, filename, stream);
416
    ov_clear(&vfile);
417
    return tuple;
418
}
419

    
420
Index<char> VorbisPlugin::read_image (const char * filename, VFSFile & file)
421
{
422
    Index<char> data;
423

    
424
    OggVorbis_File vfile;
425

    
426
    bool stream = (file.fsize () < 0);
427

    
428
    if (strstr(filename, "://-"))   /* JWT:ADDED TO HANDLE INPUT FROM stdin */
429
    {
430
        if (ov_open_callbacks (& file, & vfile, nullptr, 0, vorbis_callbacks_stdin) < 0)
431
            return data;
432
    }
433
    else
434
    {
435
        if (ov_open_callbacks (& file, & vfile, nullptr, 0, stream ?
436
         vorbis_callbacks_stream : vorbis_callbacks) < 0)
437
            return data;
438
    }
439

    
440
    vorbis_comment * comment = ov_comment (& vfile, -1);
441
    if (! comment)
442
        goto ERR;
443

    
444
    const char * s;
445

    
446
    if ((s = vorbis_comment_query (comment, "METADATA_BLOCK_PICTURE", 0)))
447
    {
448
        unsigned mime_length, desc_length, length;
449

    
450
        size_t length2;
451
        unsigned char * data2 = g_base64_decode (s, & length2);
452
        if (! data2 || length2 < 8)
453
            goto PARSE_ERR;
454

    
455
        mime_length = FROM_BE32 (* (uint32_t *) (data2 + 4));
456
        if (length2 < 8 + mime_length + 4)
457
            goto PARSE_ERR;
458

    
459
        desc_length = FROM_BE32 (* (uint32_t *) (data2 + 8 + mime_length));
460
        if (length2 < 8 + mime_length + 4 + desc_length + 20)
461
            goto PARSE_ERR;
462

    
463
        length = FROM_BE32 (* (uint32_t *) (data2 + 8 + mime_length + 4 + desc_length + 16));
464
        if (length2 < 8 + mime_length + 4 + desc_length + 20 + length)
465
            goto PARSE_ERR;
466

    
467
        data.insert ((char *) data2 + 8 + mime_length + 4 + desc_length + 20, 0, length);
468

    
469
        g_free (data2);
470
        ov_clear (& vfile);
471
        return data;
472

    
473
    PARSE_ERR:
474
        AUDERR ("Error parsing METADATA_BLOCK_PICTURE in %s.\n", filename);
475
        g_free (data2);
476
    }
477

    
478
    if ((s = vorbis_comment_query (comment, "COVERART", 0)))
479
    {
480
        size_t length2;
481
        void * data2 = g_base64_decode (s, & length2);
482

    
483
        if (! data2 || ! length2)
484
        {
485
            AUDERR ("Error parsing COVERART in %s.\n", filename);
486
            g_free (data2);
487
            goto ERR;
488
        }
489

    
490
        data.insert ((const char *) data2, 0, length2);
491

    
492
        g_free (data2);
493
        ov_clear (& vfile);
494
        return data;
495
    }
496

    
497
ERR:
498
    ov_clear (& vfile);
499
    return data;
500
}
501

    
502
const char VorbisPlugin::about[] =
503
 N_("Audacious Ogg Vorbis Decoder\n\n"
504
    "Based on the Xiph.org Foundation's Ogg Vorbis Plugin:\n"
505
    "http://www.xiph.org/\n\n"
506
    "Original code by:\n"
507
    "Tony Arcieri <bascule@inferno.tusculum.edu>\n\n"
508
    "Contributions from:\n"
509
    "Chris Montgomery <monty@xiph.org>\n"
510
    "Peter Alm <peter@xmms.org>\n"
511
    "Michael Smith <msmith@labyrinth.edu.au>\n"
512
    "Jack Moffitt <jack@icecast.org>\n"
513
    "Jorn Baayen <jorn@nl.linux.org>\n"
514
    "Håvard Kvålen <havardk@xmms.org>\n"
515
    "Gian-Carlo Pascutto <gcp@sjeng.org>\n"
516
    "Eugene Zagidullin <e.asphyx@gmail.com>");
517

    
518
const char * const VorbisPlugin::exts[] = {"ogg", "ogm", "oga", nullptr};
519
const char * const VorbisPlugin::mimes[] = {"application/ogg", nullptr};