/*1sampleadjust: gapless sample offset math23copyright 1995-2023 by the mpg123 project - free software under the terms of the LGPL 2.14see COPYING and AUTHORS files in distribution or http://mpg123.org56This is no stand-alone header, precisely to be able to fool it into using fake handle types for testing the math.7*/89#include "../common/debug.h"1011#ifdef GAPLESS12/* From internal sample number to external. */13static int64_t sample_adjust(mpg123_handle *mh, int64_t x)14{15int64_t s;16if(mh->p.flags & MPG123_GAPLESS)17{18/* It's a bit tricky to do this computation for the padding samples.19They are not there on the outside. */20if(x > mh->end_os)21{22if(x < mh->fullend_os)23s = mh->end_os - mh->begin_os;24else25s = x - (mh->fullend_os - mh->end_os + mh->begin_os);26}27else28s = x - mh->begin_os;29}30else31s = x;3233return s;34}3536/* from external samples to internal */37static int64_t sample_unadjust(mpg123_handle *mh, int64_t x)38{39int64_t s;40if(mh->p.flags & MPG123_GAPLESS)41{42s = x + mh->begin_os;43/* There is a hole; we don't create sample positions in there.44Jump from the end of the gapless track directly to after the padding. */45if(s >= mh->end_os)46s += mh->fullend_os - mh->end_os;47}48else s = x;4950return s;51}5253/*54Take the buffer after a frame decode (strictly: it is the data from frame fr->num!) and cut samples out.55fr->buffer.fill may then be smaller than before...56*/57static void frame_buffercheck(mpg123_handle *fr)58{59/* When we have no accurate position, gapless code does not make sense. */60if(!(fr->state_flags & FRAME_ACCURATE)) return;6162/* Get a grip on dirty streams that start with a gapless header.63Simply accept all data from frames that are too much,64they are supposedly attached to the stream after the fact. */65if(fr->gapless_frames > 0 && fr->num >= fr->gapless_frames) return;6667/* Important: We first cut samples from the end, then cut from beginning (including left-shift of the buffer).68This order works also for the case where firstframe == lastframe. */6970/* The last interesting (planned) frame: Only use some leading samples.71Note a difference from the below: The last frame and offset are unchanges by seeks.72The lastoff keeps being valid. */73if(fr->lastframe > -1 && fr->num >= fr->lastframe)74{75/* There can be more than one frame of padding at the end, so we ignore the whole frame if we are beyond lastframe. */76int64_t byteoff = (fr->num == fr->lastframe) ? INT123_samples_to_bytes(fr, fr->lastoff) : 0;77if((int64_t)fr->buffer.fill > byteoff)78{79fr->buffer.fill = byteoff;80}81if(VERBOSE3)82fprintf(stderr, "\nNote: Cut frame %" PRIi64 " buffer on end of stream to %"83PRIi64 " samples, fill now %zu bytes.\n"84, fr->num, (fr->num == fr->lastframe ? fr->lastoff : 0), fr->buffer.fill);85}8687/* The first interesting frame: Skip some leading samples. */88if(fr->firstoff && fr->num == fr->firstframe)89{90int64_t byteoff = INT123_samples_to_bytes(fr, fr->firstoff);91if((int64_t)fr->buffer.fill > byteoff)92{93fr->buffer.fill -= byteoff;94/* buffer.p != buffer.data only for own buffer */95debug6("cutting %" PRIi64 " samples/%" PRIi6496" bytes on begin, own_buffer=%i at %p=%p, buf[1]=%i"97, fr->firstoff, byteoff, fr->own_buffer98, (void*)fr->buffer.p, (void*)fr->buffer.data, ((short*)fr->buffer.p)[2]);99if(fr->own_buffer) fr->buffer.p = fr->buffer.data + byteoff;100else memmove(fr->buffer.data, fr->buffer.data + byteoff, fr->buffer.fill);101debug3("done cutting, buffer at %p =? %p, buf[1]=%i",102(void*)fr->buffer.p, (void*)fr->buffer.data, ((short*)fr->buffer.p)[2]);103}104else fr->buffer.fill = 0;105106if(VERBOSE3)107fprintf(stderr, "\nNote: Cut frame %" PRIi64108" buffer on beginning of stream by %" PRIi64 " samples, fill now %zu bytes.\n"109, fr->num, fr->firstoff, fr->buffer.fill);110/* We can only reach this frame again by seeking. And on seeking, firstoff will be recomputed.111So it is safe to null it here (and it makes the if() decision abort earlier). */112fr->firstoff = 0;113}114}115116#define SAMPLE_ADJUST(mh,x) sample_adjust(mh,x)117#define SAMPLE_UNADJUST(mh,x) sample_unadjust(mh,x)118#define FRAME_BUFFERCHECK(mh) frame_buffercheck(mh)119120#else /* no gapless code included */121122#define SAMPLE_ADJUST(mh,x) (x)123#define SAMPLE_UNADJUST(mh,x) (x)124#define FRAME_BUFFERCHECK(mh)125126#endif127128129