1 /*******************************************************************************
2 *                         Goggles Audio Player Library                         *
3 ********************************************************************************
4 *           Copyright (C) 2010-2021 by Sander Jansen. All Rights Reserved      *
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 3 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, see http://www.gnu.org/licenses.           *
18 ********************************************************************************/
19 #include "ap_defs.h"
20 #include "ap_event_private.h"
21 #include "ap_packet.h"
22 #include "ap_input_plugin.h"
23 #include "ap_reader_plugin.h"
24 
25 
26 namespace ap {
27 
28 
29 #define DEFINE_CHUNK(b1,b2,b3,b4) ((b4<<24) | (b3<<16) | (b2<<8) | (b1))
30 
31 
32 class AIFFReader : public ReaderPlugin {
33 protected:
34   FXlong input_start;
35 protected:
36   ReadStatus parse();
37 public:
38   enum Chunk {
39     FORM = DEFINE_CHUNK('F','O','R','M'),
40     AIFF = DEFINE_CHUNK('A','I','F','F'),
41     COMM = DEFINE_CHUNK('C','O','M','M'),
42     SSND = DEFINE_CHUNK('S','S','N','D'),
43     };
44 
45 public:
46   AIFFReader(InputContext*);
47   FXbool init(InputPlugin*) override;
48   ReadStatus process(Packet*) override;
49 
format() const50   FXuchar format() const override { return Format::AIFF; };
51 
52   FXbool can_seek() const override;
53   FXbool seek(FXlong) override;
54   virtual ~AIFFReader();
55   };
56 
57 
AIFFReader(InputContext * ctx)58 AIFFReader::AIFFReader(InputContext * ctx) : ReaderPlugin(ctx), input_start(0) {
59   }
60 
~AIFFReader()61 AIFFReader::~AIFFReader(){
62   }
63 
init(InputPlugin * plugin)64 FXbool AIFFReader::init(InputPlugin*plugin) {
65   ReaderPlugin::init(plugin);
66   input_start=0;
67   return true;
68   }
69 
can_seek() const70 FXbool AIFFReader::can_seek() const {
71   return true;
72   }
73 
74 
seek(FXlong pos)75 FXbool AIFFReader::seek(FXlong pos){
76   FXlong offset=input_start + (FXCLAMP(0,pos,stream_length) * af.framesize());
77   input->position(offset,FXIO::Begin);
78   return true;
79   }
80 
process(Packet * packet)81 ReadStatus AIFFReader::process(Packet*packet) {
82 
83   if (!(flags&FLAG_PARSED)) {
84     if (parse()!=ReadOk)
85       return ReadError;
86     }
87 
88   FXint nbytes = (packet->space() / af.framesize()) * af.framesize();
89   FXint nread = input->read(packet->ptr(),nbytes);
90   if (nread<0) {
91     packet->unref();
92     return ReadError;
93     }
94   else if (nread==0){
95     packet->unref();
96     return ReadDone;
97     }
98 
99   packet->af              = af;
100   packet->wroteBytes(nread);
101   packet->stream_position = static_cast<FXint>( (input->position()-input_start-nread) / af.framesize() );
102   packet->stream_length   = stream_length;
103   if (input->eof())
104     packet->flags=FLAG_EOS;
105   else
106     packet->flags=0;
107 
108   context->post_packet(packet);
109 
110   return ReadOk;
111   }
112 
113 
114 
115 
116 /* Kindly borrowed from FLAC
117  *
118  * flac - Command-line FLAC encoder/decoder
119  * Copyright (C) 2000-2009  Josh Coalson
120  * Copyright (C) 2011-2013  Xiph.Org Foundation
121  * This program is free software; you can redistribute it and/or
122  * modify it under the terms of the GNU General Public License
123  * as published by the Free Software Foundation; either version 2
124  * of the License, or (at your option) any later version.
125 */
parse_extended(FXuint & val,const FXuchar buf[10])126 static FXbool parse_extended(FXuint & val,const FXuchar buf[10])
127 	/* Read an IEEE 754 80-bit (aka SANE) extended floating point value from 'f',
128 	 * convert it into an integral value and store in 'val'.  Return false if only
129 	 * between 1 and 9 bytes remain in 'f', if 0 bytes remain in 'f', or if the
130 	 * value is negative, between zero and one, or too large to be represented by
131 	 * 'val'; return true otherwise.
132 	 */
133 {
134 	unsigned int i;
135 	FXulong p = 0;
136 	FXshort e;
137 	FXshort shift;
138   val = 0;
139 
140 	e = ((FXushort)(buf[0])<<8 | (FXushort)(buf[1]))-0x3FFF;
141 	shift = 63-e;
142 	if((buf[0]>>7)==1U || e<0 || e>63) {
143 		return false;
144 	}
145 
146 	for(i = 0; i < 8; ++i)
147 		p |= (FXulong)(buf[i+2])<<(56U-i*8);
148 
149   val = (FXuint)((p>>shift)+(p>>(shift-1) & 0x1));
150 	return true;
151 }
152 
153 
154 
parse()155 ReadStatus AIFFReader::parse() {
156   FXuint chunkid;
157   FXuint chunksize;
158   FXuint formsize;
159 
160   FXshort channels;
161   FXuint  nsamples;
162   FXshort samplesize;
163   FXuchar extended[10];
164   FXuint  rate;
165 
166   FXbool got_comm = false;
167   FXbool got_ssnd = false;
168 
169 
170   GM_DEBUG_PRINT("[aiff] parsing aiff header\n");
171 
172   // formchunk
173   if (input->read(&chunkid,4)!=4 || chunkid!=FORM){
174     GM_DEBUG_PRINT("[aiff] no FORM tag found\n");
175     return ReadError;
176     }
177 
178   // formsize
179   if (!input->read_uint32_be(formsize)){
180     return ReadError;
181     }
182 
183   // formchunk
184   if (input->read(&chunkid,4)!=4 || chunkid!=AIFF){
185     GM_DEBUG_PRINT("[aiff] no AIFF tag found\n");
186     return ReadError;
187     }
188 
189   while(formsize>0) {
190 
191     // get the chunk id
192     if (input->read(&chunkid,4)!=4){
193       return ReadError;
194       }
195 
196     // chunk size
197     if (!input->read_uint32_be(chunksize)){
198       return ReadError;
199       }
200 
201     // COMM chunk
202     if (chunkid==COMM) {
203       if (got_comm) return ReadError;
204 
205       if (!input->read_int16_be(channels))
206         return ReadError;
207 
208       if (!input->read_uint32_be(nsamples))
209         return ReadError;
210 
211       if (!input->read_int16_be(samplesize))
212         return ReadError;
213 
214       if (input->read(&extended,10)!=10 || !parse_extended(rate,extended))
215         return ReadError;
216 
217       if (nsamples==0) return ReadError;
218 
219       af.set(Format::Signed|Format::Big,samplesize,samplesize>>3,rate,channels);
220       got_comm=true;
221       if (got_ssnd) break;
222       }
223 
224     /// SSND chunk
225     else if (chunkid==SSND) {
226 
227       if (got_ssnd || (input->serial() && got_comm==false)) {
228         GM_DEBUG_PRINT("[aiff] multiple ssnd or ssnd before comm in serial stream\n");
229         return ReadError;
230         }
231 
232       got_ssnd = true;
233 
234       FXuint offset;
235       FXuint blocksize;
236 
237       if (!input->read_uint32_be(offset))
238         return ReadError;
239 
240       if (!input->read_uint32_be(blocksize))
241         return ReadError;
242 
243       if (blocksize!=0) {
244         GM_DEBUG_PRINT("[aiff] blocksize %u not supported\n",blocksize);
245         return ReadError;
246         }
247 
248       input_start = input->position() + offset;
249 
250       // Stop scanning
251       if (got_comm==true) break;
252       }
253     else {
254       input->position(chunksize,FXIO::Current);
255       }
256     formsize-=chunksize;
257     }
258 
259   if (got_ssnd && got_comm) {
260     stream_length = nsamples;
261     flags|=FLAG_PARSED;
262     GM_DEBUG_STREAM_LENGTH("aiff",stream_length,af.rate);
263     context->post_configuration(new ConfigureEvent(af,Codec::PCM));
264     return ReadOk;
265     }
266   return ReadError;
267   }
268 
269 
ap_aiff_reader(InputContext * ctx)270 ReaderPlugin * ap_aiff_reader(InputContext * ctx) {
271   return new AIFFReader(ctx);
272   }
273 }
274