1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 6 нояб. 2018 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <dsp/endian.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <core/debug.h>
26 #include <core/files/lspc/LSPCAudioReader.h>
27 
28 #define BUFFER_SIZE     0x2000
29 #define BUFFER_FRAMES   0x400
30 
31 namespace lsp
32 {
decode_u8(float * vp,const void * src,size_t ns)33     void LSPCAudioReader::decode_u8(float *vp, const void *src, size_t ns)
34     {
35         const uint8_t *p = reinterpret_cast<const uint8_t *>(src);
36         while (ns--)
37             *(vp++) = float(*(p++) - 0x80) / 0x7f;
38     }
39 
decode_s8(float * vp,const void * src,size_t ns)40     void LSPCAudioReader::decode_s8(float *vp, const void *src, size_t ns)
41     {
42         const int8_t *p = reinterpret_cast<const int8_t *>(src);
43         while (ns--)
44             *(vp++) = float(*(p++)) / 0x7f;
45     }
46 
decode_u16(float * vp,const void * src,size_t ns)47     void LSPCAudioReader::decode_u16(float *vp, const void *src, size_t ns)
48     {
49         const uint16_t *p = reinterpret_cast<const uint16_t *>(src);
50         while (ns--)
51             *(vp++) = float(*(p++) - 0x8000) / 0x7fff;
52     }
53 
decode_s16(float * vp,const void * src,size_t ns)54     void LSPCAudioReader::decode_s16(float *vp, const void *src, size_t ns)
55     {
56         const int16_t *p = reinterpret_cast<const int16_t *>(src);
57         while (ns--)
58             *(vp++) = float(*(p++)) / 0x7fff;
59     }
60 
decode_u24le(float * vp,const void * src,size_t ns)61     void LSPCAudioReader::decode_u24le(float *vp, const void *src, size_t ns)
62     {
63         const uint8_t *p = reinterpret_cast<const uint8_t *>(src);
64         while (ns--)
65         {
66             int32_t v =
67                __IF_LEBE(
68                    p[0] | (p[1] << 8) | (p[2] << 16),
69                    p[2] | (p[1] << 8) | (p[0] << 16)
70                );
71 
72             *(vp++) = float(v - 0x800000) / 0x7fffff;
73             p += 3;
74         }
75     }
76 
decode_u24be(float * vp,const void * src,size_t ns)77     void LSPCAudioReader::decode_u24be(float *vp, const void *src, size_t ns)
78     {
79         const uint8_t *p = reinterpret_cast<const uint8_t *>(src);
80         while (ns--)
81         {
82             int32_t v =
83                __IF_LEBE(
84                    p[2] | (p[1] << 8) | (p[0] << 16),
85                    p[0] | (p[1] << 8) | (p[2] << 16)
86                );
87 
88             *(vp++) = float(v - 0x800000) / 0x7fffff;
89             p += 3;
90         }
91     }
92 
decode_s24le(float * vp,const void * src,size_t ns)93     void LSPCAudioReader::decode_s24le(float *vp, const void *src, size_t ns)
94     {
95         const uint8_t *p = reinterpret_cast<const uint8_t *>(src);
96         while (ns--)
97         {
98             int32_t v =
99                __IF_LEBE(
100                    p[0] | (p[1] << 8) | (p[2] << 16),
101                    p[2] | (p[1] << 8) | (p[0] << 16)
102                );
103             v = (v << 8) >> 8; // Sign-extend value
104             *(vp++) = float(v) / 0x7fffff;
105             p += 3;
106         }
107     }
108 
decode_s24be(float * vp,const void * src,size_t ns)109     void LSPCAudioReader::decode_s24be(float *vp, const void *src, size_t ns)
110     {
111         const uint8_t *p = reinterpret_cast<const uint8_t *>(src);
112         while (ns--)
113         {
114             int32_t v =
115                __IF_LEBE(
116                    p[2] | (p[1] << 8) | (p[0] << 16),
117                    p[0] | (p[1] << 8) | (p[2] << 16)
118                );
119             v = (v << 8) >> 8; // Sign-extend value
120             *(vp++) = float(v) / 0x7fffff;
121             p += 3;
122         }
123     }
124 
decode_u32(float * vp,const void * src,size_t ns)125     void LSPCAudioReader::decode_u32(float *vp, const void *src, size_t ns)
126     {
127         const int32_t *p = reinterpret_cast<const int32_t *>(src);
128         while (ns--)
129         {
130             int32_t v = *(p++) - 0x80000000;
131             *(vp++) = double(v) / 0x7fffffff;
132         }
133     }
134 
decode_s32(float * vp,const void * src,size_t ns)135     void LSPCAudioReader::decode_s32(float *vp, const void *src, size_t ns)
136     {
137         const int32_t *p = reinterpret_cast<const int32_t *>(src);
138         while (ns--)
139         {
140             int32_t v = *(p++);
141             *(vp++) = double(v) / 0x7fffffff;
142         }
143     }
144 
decode_f32(float * vp,const void * src,size_t ns)145     void LSPCAudioReader::decode_f32(float *vp, const void *src, size_t ns)
146     {
147         const float *p = reinterpret_cast<const float *>(src);
148         while (ns--)
149             *(vp++) = *(p++);
150     }
151 
decode_f64(float * vp,const void * src,size_t ns)152     void LSPCAudioReader::decode_f64(float *vp, const void *src, size_t ns)
153     {
154         const double *p = reinterpret_cast<const double *>(src);
155         while (ns--)
156             *(vp++) = *(p++);
157     }
158 
LSPCAudioReader()159     LSPCAudioReader::LSPCAudioReader()
160     {
161         sParams.channels        = 0;
162         sParams.sample_format   = 0;
163         sParams.sample_rate     = 0;
164         sParams.codec           = 0;
165         sParams.frames          = 0;
166 
167         pFD                     = NULL;
168         pRD                     = NULL;
169         nFlags                  = 0;
170         nBPS                    = 0;
171         nFrameSize              = 0;
172         nBytesLeft              = 0;
173         sBuf.vData              = NULL;
174         sBuf.nOff               = 0;
175         sBuf.nSize              = 0;
176         pDecode                 = NULL;
177         pFBuffer                = NULL;
178     }
179 
~LSPCAudioReader()180     LSPCAudioReader::~LSPCAudioReader()
181     {
182         close();
183     }
184 
close()185     status_t LSPCAudioReader::close()
186     {
187         // Check open status first
188         if (!(nFlags & F_OPENED))
189             return STATUS_CLOSED;
190 
191         // Close reader (if required)
192         status_t res = STATUS_OK;
193         if (pRD != NULL)
194         {
195             status_t xr     = (nFlags & F_CLOSE_READER) ? pRD->close() : STATUS_OK;
196             if (nFlags & F_DROP_READER)
197                 delete pRD;
198             pRD             = NULL;
199             if (res == STATUS_OK)
200                 res             = xr;
201         }
202 
203         // Close LSPC file (if required)
204         if ((nFlags & F_CLOSE_FILE) && (pFD != NULL))
205         {
206             status_t xr     = pFD->close();
207             pFD             = NULL;
208             if (res == STATUS_OK)
209                 res             = xr;
210         }
211 
212         // Drop buffers
213         if (sBuf.vData != NULL)
214         {
215             delete [] sBuf.vData;
216             sBuf.vData      = NULL;
217         }
218 
219         // Drop buffers
220         if (pFBuffer != NULL)
221         {
222             delete [] pFBuffer;
223             pFBuffer        = NULL;
224         }
225 
226         nFlags          = 0;
227         nBPS            = 0;
228         nFrameSize      = 0;
229         nBytesLeft      = 0;
230         sBuf.nOff       = 0;
231         sBuf.nSize      = 0;
232         pDecode         = NULL;
233         return res;
234     }
235 
read_audio_header(LSPCChunkReader * rd)236     status_t LSPCAudioReader::read_audio_header(LSPCChunkReader *rd)
237     {
238         lspc_chunk_audio_header_t hdr;
239         lspc_audio_parameters_t p;
240 
241         // Read audio header from chunk
242         ssize_t res = rd->read_header(&hdr, sizeof(lspc_chunk_audio_header_t));
243         if (res < 0)
244             return status_t(-res);
245 
246         // Check version and decode header
247         if (hdr.common.version < 1)
248             return STATUS_CORRUPTED_FILE;
249         if (hdr.common.size < sizeof(lspc_chunk_audio_header_t))
250             return STATUS_CORRUPTED_FILE;
251 
252         p.channels          = BE_TO_CPU(hdr.channels);
253         p.sample_format     = BE_TO_CPU(hdr.sample_format);
254         p.sample_rate       = BE_TO_CPU(hdr.sample_rate);
255         p.codec             = BE_TO_CPU(hdr.codec);
256         p.frames            = BE_TO_CPU(hdr.frames);
257 
258         return apply_params(&p);
259     }
260 
apply_params(const lspc_audio_parameters_t * p)261     status_t LSPCAudioReader::apply_params(const lspc_audio_parameters_t *p)
262     {
263         if (p->channels <= 0)
264             return STATUS_BAD_FORMAT;
265         if (p->sample_rate == 0)
266             return STATUS_BAD_FORMAT;
267         if (p->codec != LSPC_CODEC_PCM)
268             return STATUS_UNSUPPORTED_FORMAT;
269 
270         // Check sample format support
271         size_t sb           = 0;
272         decode_func_t df    = NULL;
273         bool le             = false;
274         bool arch_le        = __IF_LEBE(true, false);
275 
276         switch (p->sample_format)
277         {
278             case LSPC_SAMPLE_FMT_U8LE:
279             case LSPC_SAMPLE_FMT_U8BE:
280                 df = decode_u8;
281                 sb = 1;
282                 le = p->sample_format == LSPC_SAMPLE_FMT_U8LE;
283                 break;
284 
285             case LSPC_SAMPLE_FMT_S8LE:
286             case LSPC_SAMPLE_FMT_S8BE:
287                 df = decode_s8;
288                 sb = 1;
289                 le = p->sample_format == LSPC_SAMPLE_FMT_S8LE;
290                 break;
291 
292             case LSPC_SAMPLE_FMT_U16LE:
293             case LSPC_SAMPLE_FMT_U16BE:
294                 df = decode_u16;
295                 sb = 2;
296                 le = p->sample_format == LSPC_SAMPLE_FMT_U16LE;
297                 break;
298 
299             case LSPC_SAMPLE_FMT_S16LE:
300             case LSPC_SAMPLE_FMT_S16BE:
301                 df = decode_s16;
302                 sb = 2;
303                 le = p->sample_format == LSPC_SAMPLE_FMT_S16LE;
304                 break;
305 
306             case LSPC_SAMPLE_FMT_U24LE:
307                 df = decode_u24le;
308                 sb = 3;
309                 le = true;
310                 break;
311             case LSPC_SAMPLE_FMT_U24BE:
312                 df = decode_u24be;
313                 sb = 3;
314                 le = false;
315                 break;
316             case LSPC_SAMPLE_FMT_S24LE:
317                 df = decode_s24le;
318                 sb = 3;
319                 le = true;
320                 break;
321             case LSPC_SAMPLE_FMT_S24BE:
322                 df = decode_s24be;
323                 sb = 3;
324                 le = false;
325                 break;
326 
327             case LSPC_SAMPLE_FMT_U32LE:
328             case LSPC_SAMPLE_FMT_U32BE:
329                 df = decode_u32;
330                 sb = 4;
331                 le = p->sample_format == LSPC_SAMPLE_FMT_U32LE;
332                 break;
333 
334             case LSPC_SAMPLE_FMT_S32LE:
335             case LSPC_SAMPLE_FMT_S32BE:
336                 df = decode_s32;
337                 sb = 4;
338                 le = p->sample_format == LSPC_SAMPLE_FMT_S32LE;
339                 break;
340 
341             case LSPC_SAMPLE_FMT_F32LE:
342             case LSPC_SAMPLE_FMT_F32BE:
343                 df = decode_f32;
344                 sb = 4;
345                 le = p->sample_format == LSPC_SAMPLE_FMT_F32LE;
346                 break;
347 
348             case LSPC_SAMPLE_FMT_F64LE:
349             case LSPC_SAMPLE_FMT_F64BE:
350                 df = decode_f64;
351                 sb = 8;
352                 le = p->sample_format == LSPC_SAMPLE_FMT_F64LE;
353                 break;
354 
355             default:
356                 return STATUS_UNSUPPORTED_FORMAT;
357         }
358 
359         // Estimate number of bytes to read
360         size_t fz               = sb * p->channels;
361         size_t bytes_left       = fz * p->frames;
362 
363         // Allocate buffers
364         sBuf.vData      = new uint8_t[BUFFER_SIZE];
365         if (sBuf.vData == NULL)
366             return STATUS_NO_MEM;
367 
368         pFBuffer        = new float[p->channels * BUFFER_FRAMES];
369         if (pFBuffer == NULL)
370         {
371             delete [] sBuf.vData;
372             sBuf.vData = NULL;
373             return STATUS_NO_MEM;
374         }
375 
376         if (le != arch_le)
377             nFlags     |= F_REV_BYTES; // Set-up byte-reversal flag
378 
379         sParams         = *p;
380         nBPS            = sb;
381         nFrameSize      = fz;
382         nBytesLeft      = bytes_left;
383         sBuf.nOff       = 0;
384         sBuf.nSize      = 0;
385         pDecode         = df;
386 
387         return STATUS_OK;
388     }
389 
open(LSPCFile * lspc,bool auto_close)390     status_t LSPCAudioReader::open(LSPCFile *lspc, bool auto_close)
391     {
392         if (nFlags & F_OPENED)
393             return STATUS_OPENED;
394         nFlags      = 0;
395 
396         LSPCChunkReader *rd = lspc->find_chunk(LSPC_CHUNK_AUDIO, NULL, 0);
397         if (rd == NULL)
398             return STATUS_NOT_FOUND;
399 
400         status_t res = read_audio_header(rd);
401         if (res != STATUS_OK)
402         {
403             rd->close();
404             return res;
405         }
406 
407         pFD         = lspc;
408         pRD         = rd;
409         nFlags     |= F_OPENED | F_CLOSE_READER | F_DROP_READER;
410         if (auto_close)
411             nFlags     |= F_CLOSE_FILE;
412         return STATUS_OK;
413     }
414 
open(LSPCFile * lspc,uint32_t uid,bool auto_close)415     status_t LSPCAudioReader::open(LSPCFile *lspc, uint32_t uid, bool auto_close)
416     {
417         if (nFlags & F_OPENED)
418             return STATUS_OPENED;
419         nFlags      = 0;
420 
421         LSPCChunkReader *rd = lspc->read_chunk(uid);
422         if (rd == NULL)
423             return STATUS_NOT_FOUND;
424         else if (rd->magic() != LSPC_CHUNK_AUDIO)
425         {
426             rd->close();
427             return STATUS_BAD_TYPE;
428         }
429 
430         status_t res = read_audio_header(rd);
431         if (res != STATUS_OK)
432         {
433             rd->close();
434             return res;
435         }
436 
437         pFD         = lspc;
438         pRD         = rd;
439         nFlags     |= F_OPENED | F_CLOSE_READER | F_DROP_READER;
440         if (auto_close)
441             nFlags     |= F_CLOSE_FILE;
442         return STATUS_OK;
443     }
444 
open_raw_magic(LSPCFile * lspc,const lspc_audio_parameters_t * params,uint32_t magic,bool auto_close)445     status_t LSPCAudioReader::open_raw_magic(LSPCFile *lspc, const lspc_audio_parameters_t *params, uint32_t magic, bool auto_close)
446     {
447         if (nFlags & F_OPENED)
448             return STATUS_OPENED;
449         else if (params == NULL)
450             return STATUS_BAD_ARGUMENTS;
451         nFlags      = 0;
452 
453         LSPCChunkReader *rd = lspc->find_chunk(magic, NULL, 0);
454         if (rd == NULL)
455             return STATUS_NOT_FOUND;
456 
457         status_t res = apply_params(params);
458         if (res != STATUS_OK)
459         {
460             rd->close();
461             return res;
462         }
463 
464         pFD         = lspc;
465         pRD         = rd;
466         nFlags     |= F_OPENED | F_CLOSE_READER | F_DROP_READER;
467         if (auto_close)
468             nFlags     |= F_CLOSE_FILE;
469         return STATUS_OK;
470     }
471 
open_raw_uid(LSPCFile * lspc,const lspc_audio_parameters_t * params,uint32_t uid,bool auto_close)472     status_t LSPCAudioReader::open_raw_uid(LSPCFile *lspc, const lspc_audio_parameters_t *params, uint32_t uid, bool auto_close)
473     {
474         if (nFlags & F_OPENED)
475             return STATUS_OPENED;
476         else if (params == NULL)
477             return STATUS_BAD_ARGUMENTS;
478         nFlags      = 0;
479 
480         LSPCChunkReader *rd = lspc->read_chunk(uid);
481         if (rd == NULL)
482             return STATUS_NOT_FOUND;
483 
484         status_t res = apply_params(params);
485         if (res != STATUS_OK)
486         {
487             rd->close();
488             return res;
489         }
490 
491         pFD         = lspc;
492         pRD         = rd;
493         nFlags     |= F_OPENED | F_CLOSE_READER | F_DROP_READER;
494         if (auto_close)
495             nFlags     |= F_CLOSE_FILE;
496         return STATUS_OK;
497     }
498 
open_raw(LSPCChunkReader * rd,const lspc_audio_parameters_t * params,bool auto_close)499     status_t LSPCAudioReader::open_raw(LSPCChunkReader *rd, const lspc_audio_parameters_t *params, bool auto_close)
500     {
501         if (nFlags & F_OPENED)
502             return STATUS_OPENED;
503         else if (params == NULL)
504             return STATUS_BAD_ARGUMENTS;
505         nFlags      = 0;
506 
507         status_t res = apply_params(params);
508         if (res != STATUS_OK)
509             return res;
510 
511         pFD         = NULL;
512         pRD         = rd;
513         nFlags     |= F_OPENED;
514         if (auto_close)
515             nFlags     |= F_CLOSE_READER;
516         return STATUS_OK;
517     }
518 
get_parameters(lspc_audio_parameters_t * dst) const519     status_t LSPCAudioReader::get_parameters(lspc_audio_parameters_t *dst) const
520     {
521         if (!(nFlags & F_OPENED))
522             return STATUS_CLOSED;
523         else if (dst == NULL)
524             return STATUS_BAD_ARGUMENTS;
525         *dst        = sParams;
526 
527         return STATUS_OK;
528     }
529 
fill_buffer()530     status_t LSPCAudioReader::fill_buffer()
531     {
532         // Move buffer data from end to the beginning
533         size_t bsize = sBuf.nSize - sBuf.nOff;
534         if ((sBuf.nSize > 0) && (bsize > 0))
535         {
536             ::memmove(sBuf.vData, &sBuf.vData[sBuf.nOff], bsize);
537             sBuf.nSize      = bsize;
538         }
539         else
540             sBuf.nSize      = 0;
541         sBuf.nOff       = 0;
542 
543         // Try to read additional data
544         bsize       = BUFFER_SIZE - bsize;
545         ssize_t n   = pRD->read(&sBuf.vData[sBuf.nSize], bsize);
546         if (n < 0)
547             return status_t(-n);
548         else if (n == 0) // Number of bytes should be multiple of nFrameSize
549         {
550             size_t delta = sBuf.nSize - sBuf.nOff;
551             if (delta >= nFrameSize)
552                 return STATUS_OK;
553             else if (delta == 0)
554                 return STATUS_EOF;
555             else
556                 return STATUS_CORRUPTED_FILE;
557         }
558 
559         sBuf.nSize     += n;
560         return STATUS_OK;
561     }
562 
read_samples(float ** data,size_t frames)563     ssize_t LSPCAudioReader::read_samples(float **data, size_t frames)
564     {
565         if (!(nFlags & F_OPENED))
566             return STATUS_CLOSED;
567 
568         size_t nc       = sParams.channels;
569         float **vp = reinterpret_cast<float **>(alloca(nc * sizeof(float *)));
570         for (size_t i=0; i<nc; ++i)
571             vp[i]       = data[i];
572 
573         size_t n_read   = 0;
574 
575         while (n_read < frames)
576         {
577             // Estimate number of frames to read
578             size_t to_read = frames - n_read;
579             if (to_read > BUFFER_FRAMES)
580                 to_read = BUFFER_FRAMES;
581 
582             // Read frames to temporary buffer
583             ssize_t n   = read_frames(pFBuffer, to_read);
584             if (n <= 0)
585                 return (n_read > 0) ? n_read : n;
586 
587             n_read     += n;
588 
589             // Unpack frames
590             float *p    = pFBuffer;
591             while (n--)
592             {
593                 for (size_t j=0; j<nc; ++j, ++p)
594                 {
595                     if (!vp[j])
596                         continue;
597                     *(vp[j]++)      = *p;
598                 }
599             }
600         }
601 
602         return n_read;
603     }
604 
read_frames(float * data,size_t frames)605     ssize_t LSPCAudioReader::read_frames(float *data, size_t frames)
606     {
607         if (!(nFlags & F_OPENED))
608             return STATUS_CLOSED;
609 
610         size_t n_read   = 0;
611         while (n_read < frames)
612         {
613             size_t to_read = frames - n_read;
614 
615             // Ensure that we have enough bytes to read at least one frame
616             size_t avail = sBuf.nSize - sBuf.nOff;
617             if (avail < nFrameSize)
618             {
619                 // Try to fill buffer with new data
620                 status_t st = fill_buffer();
621                 if (st != STATUS_OK)
622                     return (n_read > 0) ? n_read : -st;
623                 avail = sBuf.nSize - sBuf.nOff;
624                 if (avail < nFrameSize)
625                     return (n_read > 0) ? n_read : STATUS_CORRUPTED_FILE;
626             }
627 
628             // Perform decode
629             avail   /= nFrameSize;
630             if (avail > to_read)
631                 avail   = to_read;
632             size_t floats = avail * sParams.channels;
633             if (nFlags & F_REV_BYTES)
634             {
635                 switch (nBPS)
636                 {
637                     case 1:
638                     case 3:
639                         break;
640                     case 2:
641                         byte_swap(reinterpret_cast<uint16_t *>(&sBuf.vData[sBuf.nOff]), floats);
642                         break;
643                     case 4:
644                         byte_swap(reinterpret_cast<uint32_t *>(&sBuf.vData[sBuf.nOff]), floats);
645                         break;
646                     case 8:
647                         byte_swap(reinterpret_cast<uint64_t *>(&sBuf.vData[sBuf.nOff]), floats);
648                         break;
649                     default:
650                         return STATUS_BAD_STATE;
651                 }
652             }
653 
654             // Perform decode
655             pDecode(data, &sBuf.vData[sBuf.nOff], floats);
656 
657             // Update pointers
658             n_read         += avail;
659             sBuf.nOff      += avail * nFrameSize;
660             data           += floats;
661         }
662 
663         return n_read;
664     }
665 
skip_frames(size_t frames)666     ssize_t LSPCAudioReader::skip_frames(size_t frames)
667     {
668         if (!(nFlags & F_OPENED))
669             return STATUS_CLOSED;
670 
671         size_t n_skip   = 0;
672         while (n_skip < frames)
673         {
674             size_t to_skip = frames - n_skip;
675 
676             // Ensure that we have enough bytes to read at least one frame
677             size_t avail = sBuf.nSize - sBuf.nOff;
678             if (avail < nFrameSize)
679             {
680                 // Try to fill buffer with new data
681                 status_t st = fill_buffer();
682                 if (st != STATUS_OK)
683                     return (n_skip > 0) ? n_skip : -st;
684                 avail = sBuf.nSize - sBuf.nOff;
685                 if (avail < nFrameSize)
686                     return (n_skip > 0) ? n_skip : STATUS_CORRUPTED_FILE;
687             }
688 
689             // Perform decode
690             avail   /= nFrameSize;
691             if (avail > to_skip)
692                 avail   = to_skip;
693 
694             // Update pointers
695             n_skip         += avail;
696             sBuf.nOff      += avail * nFrameSize;
697         }
698 
699         return n_skip;
700     }
701 
unique_id() const702     uint32_t LSPCAudioReader::unique_id() const
703     {
704         if (!(nFlags & F_OPENED))
705             return 0;
706         else if (pRD == NULL)
707             return 0;
708 
709         return pRD->unique_id();
710     }
711 
magic() const712     uint32_t LSPCAudioReader::magic() const
713     {
714         if (!(nFlags & F_OPENED))
715             return 0;
716         else if (pRD == NULL)
717             return 0;
718 
719         return pRD->magic();
720     }
721 
722 } /* namespace lsp */
723