1 /*
2  *  A FLAC decoder plugin for the Audacious Media Player
3  *  Copyright (C) 2005 Ralf Ertzinger
4  *  Copyright (C) 2010-2012 Michał Lipski
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20 
21 #include <string.h>
22 
23 #include <libaudcore/runtime.h>
24 
25 #include "flacng.h"
26 
27 EXPORT FLACng aud_plugin_instance;
28 
29 static FLAC__StreamDecoder *decoder;
30 static callback_info *cinfo;
31 
init()32 bool FLACng::init()
33 {
34     FLAC__StreamDecoderInitStatus ret;
35 
36     /* Callback structure and decoder for main decoding loop */
37 
38     cinfo = new callback_info;
39 
40     if ((decoder = FLAC__stream_decoder_new()) == nullptr)
41     {
42         AUDERR("Could not create the main FLAC decoder instance!\n");
43         return false;
44     }
45 
46     if (FLAC__STREAM_DECODER_INIT_STATUS_OK != (ret = FLAC__stream_decoder_init_stream(
47         decoder,
48         read_callback,
49         seek_callback,
50         tell_callback,
51         length_callback,
52         eof_callback,
53         write_callback,
54         metadata_callback,
55         error_callback,
56         cinfo)))
57     {
58         AUDERR("Could not initialize the main FLAC decoder: %s(%d)\n",
59             FLAC__StreamDecoderInitStatusString[ret], ret);
60         return false;
61     }
62 
63     AUDDBG("Plugin initialized.\n");
64     return true;
65 }
66 
cleanup()67 void FLACng::cleanup()
68 {
69     FLAC__stream_decoder_delete(decoder);
70     delete cinfo;
71 }
72 
is_our_file(const char * filename,VFSFile & file)73 bool FLACng::is_our_file(const char *filename, VFSFile &file)
74 {
75     AUDDBG("Probe for FLAC.\n");
76 
77     char buf[4];
78     if (file.fread (buf, 1, sizeof buf) != sizeof buf)
79         return false;
80 
81     return ! strncmp (buf, "fLaC", sizeof buf);
82 }
83 
squeeze_audio(int32_t * src,void * dst,unsigned count,unsigned res)84 static void squeeze_audio(int32_t* src, void* dst, unsigned count, unsigned res)
85 {
86     int32_t* rp = src;
87     int8_t*  wp = (int8_t*) dst;
88     int16_t* wp2 = (int16_t*) dst;
89     int32_t* wp4 = (int32_t*) dst;
90 
91     switch (res)
92     {
93         case 8:
94             for (unsigned i = 0; i < count; i++, wp++, rp++)
95                 *wp = *rp & 0xff;
96             break;
97 
98         case 16:
99             for (unsigned i = 0; i < count; i++, wp2++, rp++)
100                 *wp2 = *rp & 0xffff;
101             break;
102 
103         case 24:
104         case 32:
105             for (unsigned i = 0; i < count; i++, wp4++, rp++)
106                 *wp4 = *rp;
107             break;
108 
109         default:
110             AUDERR("Can not convert to %u bps\n", res);
111     }
112 }
113 
play(const char * filename,VFSFile & file)114 bool FLACng::play(const char *filename, VFSFile &file)
115 {
116     Index<char> play_buffer;
117     bool error = false;
118 
119     cinfo->fd = &file;
120 
121     if (read_metadata(decoder, cinfo) == false)
122     {
123         AUDERR("Could not prepare file for playing!\n");
124         error = true;
125         goto ERR_NO_CLOSE;
126     }
127 
128     play_buffer.resize(BUFFER_SIZE_BYTE);
129 
130     set_stream_bitrate(cinfo->bitrate);
131     open_audio(SAMPLE_FMT(cinfo->bits_per_sample), cinfo->sample_rate, cinfo->channels);
132 
133     while (FLAC__stream_decoder_get_state(decoder) != FLAC__STREAM_DECODER_END_OF_STREAM)
134     {
135         if (check_stop ())
136             break;
137 
138         int seek_value = check_seek ();
139         if (seek_value >= 0)
140             FLAC__stream_decoder_seek_absolute (decoder, (int64_t)
141              seek_value * cinfo->sample_rate / 1000);
142 
143         /* Try to decode a single frame of audio */
144         if (FLAC__stream_decoder_process_single(decoder) == false)
145         {
146             AUDERR("Error while decoding!\n");
147             error = true;
148             break;
149         }
150 
151         squeeze_audio(cinfo->output_buffer.begin(), play_buffer.begin(),
152          cinfo->buffer_used, cinfo->bits_per_sample);
153         write_audio(play_buffer.begin(), cinfo->buffer_used *
154          SAMPLE_SIZE(cinfo->bits_per_sample));
155 
156         cinfo->reset();
157     }
158 
159 ERR_NO_CLOSE:
160     cinfo->reset();
161 
162     if (FLAC__stream_decoder_flush(decoder) == false)
163         AUDERR("Could not flush decoder state!\n");
164 
165     return ! error;
166 }
167 
168 const char FLACng::about[] =
169  N_("Original code by\n"
170     "Ralf Ertzinger <ralf@skytale.net>\n\n"
171     "http://www.skytale.net/projects/bmp-flac2/");
172 
173 const char *const FLACng::exts[] = { "flac", "fla", nullptr };
174 
175 const char *const FLACng::mimes[] = { "audio/flac", "audio/x-flac", nullptr };
176