diff --git a/libretro-common/formats/audio_transfer.c b/libretro-common/formats/audio_transfer.c index a6df2d46ff85..f6e061d02848 100644 --- a/libretro-common/formats/audio_transfer.c +++ b/libretro-common/formats/audio_transfer.c @@ -286,9 +286,23 @@ static size_t audio_transfer_ogg_page(const uint8_t *buf, size_t size, * only what is wider than that. * * MP3 (rmp3) - * Does: buffer input; s16 (quantised by the synthesis filter, no float - * round trip) and f32; channels and rate from the first frame; seek - * to any PCM frame; buffer_tell, hence windowing. + * Does: buffer input, decoded through rmp3_stream - the buffer is + * consumed incrementally rather than borrowed whole, so + * buffer_tell reports a real frontier, the bytes behind it + * releasable rather than merely read (it leads the decode position + * by at most the stream's reassembly hold, the safe side for a + * feeder); s16 (quantised by the synthesis filter, no float round + * trip) and f32, freely mixed - the stream converts its filter + * state at the switch as the pull API did; channels and rate from + * the first frame's header, read at open by a parse-only walk that + * decodes nothing; seek to any PCM frame, recorded at seek() and + * performed at the following read as a decode forward from the + * head in that read's own pipeline, so the audio after a seek is + * byte-identical to a linear decode's - MP3 frames depend on the + * bit reservoir of their predecessors, so the top is the only + * sample-accurate way in, which is also what the resident decoder + * did, minus its habit of carrying stale synthesis state through + * the rewind. * Length and gapless trim from the Xing/Info or VBRI header in the * first frame: that frame carries no audio but is decoded like any * other, so the priming dropped is its own length plus the encoder @@ -607,8 +621,17 @@ struct audio_transfer_mp3 { const void *data; size_t size; - rmp3 handle; /* dr_mp3 initialises this in place (by value) */ - int inited; /* handle is embedded, so track init state a flag */ + rmp3_stream_t *stream; /* opened decoder, NULL until start() succeeds */ + size_t off; /* consumption cursor: the compressed frontier */ + unsigned channels; /* from the first frame's header, read at start() */ + unsigned rate; + int eof_sent; /* the stream has been told the tail is all there is */ + /* Recorded rather than done: an MP3 seek is a decode forward from + * the head, and which pipeline it decodes in belongs to the read + * that follows, so the output after a seek is byte-identical to a + * linear decode in that read's own format. Same shape as the Opus + * arm's seek_to. */ + int64_t seek_to; /* Gapless, from the Xing/Info or VBRI header in the first frame. * That frame carries no audio but dr_mp3 decodes it like any * other, so the priming to drop is its own length plus the @@ -1742,6 +1765,89 @@ static int audio_transfer_mp3_gapless(const uint8_t *b, size_t len, #endif #ifdef HAVE_RMP3 +/* Drain 'frames' frames out of the stream in one of the two pipelines, + * feeding it the unconsumed tail of the buffer as it asks. The + * compressed cursor is kept here rather than in the decoder because a + * windowed feeder reads it, and because a seek re-points it. Returns + * the frames written, short of the ask at end of stream. */ +static size_t audio_transfer_mp3_pull(struct audio_transfer_mp3 *m, + int s16, int16_t *o16, float *of, size_t frames) +{ + size_t done = 0; + if (!m->stream || !frames) + return 0; + while (done < frames) + { + size_t rd = 0, wr = 0; + int r; + if (s16) + rmp3_stream_set_out_s16(m->stream, + o16 + done * (size_t)m->channels, frames - done); + else + rmp3_stream_set_out_f32(m->stream, + of + done * (size_t)m->channels, frames - done); + rmp3_stream_set_in(m->stream, + (const uint8_t*)m->data + m->off, m->size - m->off); + r = rmp3_stream_process(m->stream, &rd, &wr); + m->off += rd; + done += wr; + if (r == RMP3_STREAM_ERROR || r == RMP3_STREAM_END) + break; + if (r == RMP3_STREAM_NEED_IN && m->off >= m->size) + { + /* A frame must be presented whole, so the tail of the stream + * sits in the hold waiting for a window that will never fill; + * this is what says the short tail is all there is. Latched + * until a seek resets the stream. */ + if (m->eof_sent && !wr) + break; + rmp3_stream_set_eof(m->stream); + m->eof_sent = 1; + } + } + return done; +} + +/* Restart the stream and decode forward to @frame, discarding, in the + * pipeline of the read that asked - which is what makes the output + * after the seek byte-identical to a linear decode's. MP3 frames + * depend on the bit reservoir of their predecessors, so decoding from + * the top is the only sample-accurate way in. Frame numbers here are + * of the played audio; the priming sits before frame 0 and is part of + * the distance. Returns the frame reached, or < 0 if the stream ends + * first. */ +static int64_t audio_transfer_mp3_seek_to(struct audio_transfer_mp3 *m, + int64_t frame, int s16) +{ + /* Somewhere to throw the frames walked past. On the stack, and one + * union rather than two arrays: these were a pair of function-local + * statics, 24 KiB of BSS of which only ever half was in use, and + * shared by every context. audio_mixer runs up to + * AUDIO_MIXER_MAX_VOICES at once, so two MP3 streams priming in the + * same callback wrote over each other's sink. Small because it is + * only a sink - the loop below iterates until the distance is gone. */ + union { int16_t s16[256]; float f32[256]; } skip; + uint64_t left = (uint64_t)frame + m->start_trim; + unsigned ch = m->channels ? m->channels : 1; + + rmp3_stream_reset(m->stream); + m->off = 0; + m->eof_sent = 0; + while (left) + { + size_t cap = (sizeof(skip.s16) / sizeof(skip.s16[0])) / ch; + size_t want = (left < (uint64_t)cap) ? (size_t)left : cap; + size_t n = audio_transfer_mp3_pull(m, s16, + skip.s16, skip.f32, want); + if (!n) + return -1; /* the stream ends before the target */ + left -= n; + } + m->trim_left = 0; + m->emitted = frame; + return frame; +} + /* Read past the priming and stop at the padding. The trim is dropped * by decoding it and throwing it away - there is nowhere else for it * to go, the frames being coded - and the bound is what the tag says @@ -1749,23 +1855,15 @@ static int audio_transfer_mp3_gapless(const uint8_t *b, size_t len, static size_t audio_transfer_mp3_read(struct audio_transfer_mp3 *m, int s16, int16_t *o16, float *of, size_t frames) { - unsigned ch = m->handle.channels ? m->handle.channels : 1; size_t got; - /* Somewhere to throw the primed frames. On the stack, and one - * union rather than two arrays: these were a pair of function-local - * statics, 24 KiB of BSS of which only ever half was in use, and - * shared by every context. audio_mixer runs up to - * AUDIO_MIXER_MAX_VOICES at once, so two MP3 streams priming in the - * same callback wrote over each other's sink. Small because it is - * only a sink - the loop below iterates until the trim is gone. */ union { int16_t s16[256]; float f32[256]; } skip; + unsigned ch = m->channels ? m->channels : 1; while (m->trim_left) { - size_t cap = (size_t)256 / ch; + size_t cap = (sizeof(skip.s16) / sizeof(skip.s16[0])) / ch; size_t want = (m->trim_left < cap) ? (size_t)m->trim_left : cap; - size_t n = s16 - ? (size_t)rmp3_read_s16(&m->handle, (uint64_t)want, skip.s16) - : (size_t)rmp3_read_f32(&m->handle, (uint64_t)want, skip.f32); + size_t n = audio_transfer_mp3_pull(m, s16, + skip.s16, skip.f32, want); if (!n) { m->trim_left = 0; /* stream ended inside the priming */ @@ -1781,8 +1879,7 @@ static size_t audio_transfer_mp3_read(struct audio_transfer_mp3 *m, if ((int64_t)frames > left) frames = (size_t)left; } - got = s16 ? (size_t)rmp3_read_s16(&m->handle, (uint64_t)frames, o16) - : (size_t)rmp3_read_f32(&m->handle, (uint64_t)frames, of); + got = audio_transfer_mp3_pull(m, s16, o16, of, frames); m->emitted += (int64_t)got; return got; } @@ -2707,10 +2804,50 @@ bool audio_transfer_start(void *data, enum audio_type_enum type) struct audio_transfer_mp3 *m = (struct audio_transfer_mp3*)data; if (!m || !m->data) return false; - m->inited = (rmp3_init_memory(&m->handle, m->data, m->size) != 0); - if (!m->inited) + if (!(m->stream = rmp3_stream_new())) return false; - m->limit = -1; + /* Walk parse-only until the first frame is located, which is + * where the channel count and rate come from - MPEG audio has + * no header ahead of the stream to read them from. Locating + * decodes nothing, so the walk costs a scan; a buffer with no + * frame in it fails here, as the resident open did. Then back + * to the head, so the first read decodes from frame one. */ + m->off = 0; + m->eof_sent = 0; + for (;;) + { + size_t rd = 0, wr = 0; + int r; + rmp3_stream_set_out_s16(m->stream, NULL, 0); + rmp3_stream_set_in(m->stream, + (const uint8_t*)m->data + m->off, m->size - m->off); + r = rmp3_stream_process(m->stream, &rd, &wr); + m->off += rd; + if (rmp3_stream_info(m->stream, &m->channels, &m->rate)) + break; + if (r == RMP3_STREAM_NEED_IN && m->off >= m->size + && !m->eof_sent) + { + /* A file shorter than the reassembly hold never fills + * it; say the short tail is all there is, so its frame + * is still found. */ + rmp3_stream_set_eof(m->stream); + m->eof_sent = 1; + continue; + } + if (r == RMP3_STREAM_ERROR || r == RMP3_STREAM_END + || (r == RMP3_STREAM_NEED_IN && m->off >= m->size)) + { + rmp3_stream_free(m->stream); + m->stream = NULL; + return false; + } + } + rmp3_stream_reset(m->stream); + m->off = 0; + m->eof_sent = 0; + m->seek_to = -1; + m->limit = -1; { uint64_t fr = 0, delay = 0; if (!audio_transfer_mp3_gapless((const uint8_t*)m->data, @@ -3141,7 +3278,7 @@ bool audio_transfer_is_valid(void *data, enum audio_type_enum type) case AUDIO_TYPE_MP3: { struct audio_transfer_mp3 *m = (struct audio_transfer_mp3*)data; - return (m && m->inited); + return (m && m->stream); } #endif #ifdef HAVE_RMODTRACKER @@ -3252,12 +3389,12 @@ bool audio_transfer_info(void *data, enum audio_type_enum type, case AUDIO_TYPE_MP3: { struct audio_transfer_mp3 *m = (struct audio_transfer_mp3*)data; - if (!m || !m->inited) + if (!m || !m->stream) return false; if (channels) - *channels = (unsigned)m->handle.channels; + *channels = m->channels; if (rate) - *rate = (unsigned)m->handle.sampleRate; + *rate = m->rate; /* From the Xing/Info or VBRI count, less the gapless trim * where a LAME tag states one; 0 for a file carrying no * such header, which cannot be measured without a walk. */ @@ -3957,8 +4094,15 @@ int audio_transfer_read_s16(void *data, enum audio_type_enum type, case AUDIO_TYPE_MP3: { struct audio_transfer_mp3 *m = (struct audio_transfer_mp3*)data; - if (!m || !m->inited) + if (!m || !m->stream) return AUDIO_PROCESS_ERROR; + if (m->seek_to >= 0) + { + int64_t at = audio_transfer_mp3_seek_to(m, m->seek_to, 1); + m->seek_to = -1; + if (at < 0) + return AUDIO_PROCESS_ERROR; + } produced = audio_transfer_mp3_read(m, 1, out, NULL, frames); break; } @@ -4167,8 +4311,15 @@ int audio_transfer_read_f32(void *data, enum audio_type_enum type, case AUDIO_TYPE_MP3: { struct audio_transfer_mp3 *m = (struct audio_transfer_mp3*)data; - if (!m || !m->inited) + if (!m || !m->stream) return AUDIO_PROCESS_ERROR; + if (m->seek_to >= 0) + { + int64_t at = audio_transfer_mp3_seek_to(m, m->seek_to, 0); + m->seek_to = -1; + if (at < 0) + return AUDIO_PROCESS_ERROR; + } produced = audio_transfer_mp3_read(m, 0, NULL, out, frames); break; } @@ -4418,8 +4569,13 @@ size_t audio_transfer_buffer_tell(void *data, enum audio_type_enum type) { struct audio_transfer_mp3 *m = (struct audio_transfer_mp3*)data; - if (m->inited) - return (size_t)m->handle.readPos; + /* The stream consumes the buffer as it plays, so the cursor + * kept alongside it is the compressed frontier - a real one, + * the bytes behind it being releasable rather than merely + * read. It leads the decode position by at most the stream's + * reassembly hold, which is the safe side for a feeder. */ + if (m->stream) + return m->off; return 0; } #endif @@ -4672,15 +4828,28 @@ bool audio_transfer_seek(void *data, enum audio_type_enum type, case AUDIO_TYPE_MP3: { struct audio_transfer_mp3 *m = (struct audio_transfer_mp3*)data; - if (!m || !m->inited) + if (!m || !m->stream) return false; - /* Frame numbers here are of the played audio, so the - * priming sits before frame 0. */ - if (!rmp3_seek_to_frame(&m->handle, - (uint64_t)frame + m->start_trim)) + /* Where the length is known, refuse to be sent past it rather + * than walk to the end and report failure from there. */ + if (m->limit >= 0 && (int64_t)frame > m->limit) return false; - m->trim_left = 0; - m->emitted = (int64_t)frame; + if (frame == 0 && !m->start_trim) + { + /* Loop-to-start with nothing to prime past: a rewind, done + * here rather than deferred. */ + rmp3_stream_reset(m->stream); + m->off = 0; + m->eof_sent = 0; + m->seek_to = -1; + m->trim_left = 0; + m->emitted = 0; + return true; + } + /* Recorded, not done: see seek_to. Frame numbers here are of + * the played audio, so the priming sits before frame 0 and the + * walk at the read covers both. */ + m->seek_to = (int64_t)frame; return true; } #endif @@ -4812,8 +4981,8 @@ void audio_transfer_free(void *data, enum audio_type_enum type) case AUDIO_TYPE_MP3: { struct audio_transfer_mp3 *m = (struct audio_transfer_mp3*)data; - if (m->inited) - rmp3_uninit(&m->handle); + if (m->stream) + rmp3_stream_free(m->stream); break; } #endif diff --git a/libretro-common/formats/mp3/rmp3.c b/libretro-common/formats/mp3/rmp3.c index 0c5a97e188fb..6b892001e6f3 100644 --- a/libretro-common/formats/mp3/rmp3.c +++ b/libretro-common/formats/mp3/rmp3.c @@ -2622,42 +2622,42 @@ static uint32_t rmp3_decode_next_frame(rmp3* pMP3) * frame is converted in place (the decoder state has already advanced * past it, so re-decoding is not an option); every later frame is * synthesised natively in the new format. */ -static void rmp3_set_output_mode(rmp3* pMP3, uint32_t f32) +/* The two pipelines keep their persistent decoder state in different + * formats: float for the float pipeline, Q28 fixed point for the s16 + * pipeline (the storage is shared through unions). Convert the QMF + * and IMDCT overlap histories so decoding continues seamlessly in the + * new format. Shared by the pull and streaming APIs, whose format + * switches are the same operation on different bookkeeping. */ +static void rmp3d_convert_state(rmp3dec *dec, uint32_t f32) { - uint32_t total; int i; - if (pMP3->f32_mode == f32) - return; - pMP3->f32_mode = f32; - - /* The two pipelines keep their persistent decoder state in - * different formats: float for the float pipeline, Q28 fixed point - * for the s16 pipeline (the storage is shared through unions). - * Convert the QMF and IMDCT overlap histories so decoding continues - * seamlessly in the new format. */ + float *of = &dec->mdct_overlap.f[0][0]; + int32_t *oq = &dec->mdct_overlap.q[0][0]; + if (f32) { - float *of = &pMP3->decoder.mdct_overlap.f[0][0]; - int32_t *oq = &pMP3->decoder.mdct_overlap.q[0][0]; - if (f32) - { - for (i = 0; i < 15*2*32; i++) - pMP3->decoder.qmf_state.f[i] = - pMP3->decoder.qmf_state.q[i] * (1.0f / (float)(1 << RMP3D_QBITS)); - for (i = 0; i < 2*9*32; i++) - of[i] = oq[i] * (1.0f / (float)(1 << RMP3D_QBITS)); - } - else - { - for (i = 0; i < 15*2*32; i++) - pMP3->decoder.qmf_state.q[i] = - rmp3d_float_to_q(pMP3->decoder.qmf_state.f[i]); - for (i = 0; i < 2*9*32; i++) - oq[i] = rmp3d_float_to_q(of[i]); - } + for (i = 0; i < 15*2*32; i++) + dec->qmf_state.f[i] = + dec->qmf_state.q[i] * (1.0f / (float)(1 << RMP3D_QBITS)); + for (i = 0; i < 2*9*32; i++) + of[i] = oq[i] * (1.0f / (float)(1 << RMP3D_QBITS)); } + else + { + for (i = 0; i < 15*2*32; i++) + dec->qmf_state.q[i] = + rmp3d_float_to_q(dec->qmf_state.f[i]); + for (i = 0; i < 2*9*32; i++) + oq[i] = rmp3d_float_to_q(of[i]); + } +} - total = (pMP3->framesConsumed + pMP3->framesRemaining) - * pMP3->frameChannels; +/* Convert 'total' samples of a buffered frame between the two formats, + * in place. The decoder state has already advanced past the frame, so + * re-decoding is not an option; conversion is what keeps a buffered + * frame playable across a format switch. */ +static void rmp3d_convert_frames(rmp3_frame_buf *fb, uint32_t total, + uint32_t f32) +{ if (total == 0) return; @@ -2667,7 +2667,7 @@ static void rmp3_set_output_mode(rmp3* pMP3, uint32_t f32) * backwards only clobbers s16 slots that are already consumed. */ uint32_t i = total; while (i-- > 0) - pMP3->frames.f32[i] = pMP3->frames.s16[i] * (1.0f / 32768.0f); + fb->f32[i] = fb->s16[i] * (1.0f / 32768.0f); } else { @@ -2676,19 +2676,30 @@ static void rmp3_set_output_mode(rmp3* pMP3, uint32_t f32) uint32_t i; for (i = 0; i < total; i++) { - float s = pMP3->frames.f32[i] * 32768.0f; + float s = fb->f32[i] * 32768.0f; int v; - if (s > 32767.0f) { pMP3->frames.s16[i] = 32767; continue; } - if (s < -32768.0f) { pMP3->frames.s16[i] = -32768; continue; } + if (s > 32767.0f) { fb->s16[i] = 32767; continue; } + if (s < -32768.0f) { fb->s16[i] = -32768; continue; } v = (int)(s + .5f); v -= (v < 0); /* round half away from zero, as rmp3d_scale_pcm */ if (v > 32767) v = 32767; if (v < -32768) v = -32768; - pMP3->frames.s16[i] = (int16_t)v; + fb->s16[i] = (int16_t)v; } } } +static void rmp3_set_output_mode(rmp3* pMP3, uint32_t f32) +{ + if (pMP3->f32_mode == f32) + return; + pMP3->f32_mode = f32; + rmp3d_convert_state(&pMP3->decoder, f32); + rmp3d_convert_frames(&pMP3->frames, + (pMP3->framesConsumed + pMP3->framesRemaining) + * pMP3->frameChannels, f32); +} + uint32_t rmp3_init_memory(rmp3* pMP3, const void* pData, size_t dataSize) { if (!pMP3) @@ -2889,9 +2900,14 @@ struct rmp3_stream size_t in_size; size_t in_pos; + /* One destination or the other, the same way the pull API's two + * reads are one decoder: whichever set_out_* ran last names the + * pipeline, and f32_mode says which. */ int16_t *out; + float *outf; size_t out_frames; size_t out_pos; + uint32_t f32_mode; /* A frame straddling two windows is reassembled here rather than * asking the caller to guarantee alignment it cannot know. */ @@ -2902,8 +2918,9 @@ struct rmp3_stream uint64_t frames_in; /* MPEG frames consumed since reset */ /* One decoded frame, drained across as many calls as the caller's - * output takes to accept it. */ - int16_t pcm[RMP3_MAX_SAMPLES_PER_FRAME]; + * output takes to accept it; held in whichever format the synthesis + * ran in, converted in place if the caller switches. */ + rmp3_frame_buf pcm; unsigned pending; unsigned drained; @@ -2936,6 +2953,22 @@ void rmp3_stream_set_in(rmp3_stream_t *s, const void *in, size_t in_size) s->in_pos = 0; } +/* Selecting a pipeline is the same operation as the pull API's reads + * perform: convert the decoder's persistent filter state, and whatever + * of the last decoded frame has not been drained, so decoding + * continues seamlessly in the new format. A parse-only call selects + * nothing - a walk in the middle of a decode must not disturb the + * pipeline the decode is in. */ +static void rmp3_stream_set_mode(rmp3_stream_t *s, uint32_t f32) +{ + if (s->f32_mode == f32) + return; + s->f32_mode = f32; + rmp3d_convert_state(&s->dec, f32); + rmp3d_convert_frames(&s->pcm, + (uint32_t)s->pending * s->channels, f32); +} + void rmp3_stream_set_out_s16(rmp3_stream_t *s, int16_t *out, size_t out_frames) { if (!s) @@ -2943,8 +2976,23 @@ void rmp3_stream_set_out_s16(rmp3_stream_t *s, int16_t *out, size_t out_frames) /* A null destination, or no room in it, means parse-only: frames are * located and counted, nothing is decoded. */ s->out = out; + s->outf = NULL; + s->out_frames = out ? out_frames : 0; + s->out_pos = 0; + if (out && out_frames) + rmp3_stream_set_mode(s, 0); +} + +void rmp3_stream_set_out_f32(rmp3_stream_t *s, float *out, size_t out_frames) +{ + if (!s) + return; + s->out = NULL; + s->outf = out; s->out_frames = out ? out_frames : 0; s->out_pos = 0; + if (out && out_frames) + rmp3_stream_set_mode(s, 1); } int rmp3_stream_info(const rmp3_stream_t *s, unsigned *channels, unsigned *rate) @@ -3008,7 +3056,8 @@ int rmp3_stream_process(rmp3_stream_t *s, size_t *read, size_t *wrote) const uint8_t *src; size_t avail; int samples; - int parse_only = (!s->out || !s->out_frames); + int parse_only = + ((!s->out && !s->outf) || !s->out_frames); /* Hand over what the last frame produced before decoding another: * one frame is up to 1152 PCM frames and a caller's buffer may be @@ -3021,9 +3070,14 @@ int rmp3_stream_process(rmp3_stream_t *s, size_t *read, size_t *wrote) { if ((size_t)take > room) take = (unsigned)room; - memcpy(s->out + s->out_pos * (size_t)s->channels, - s->pcm + (size_t)s->drained * (size_t)s->channels, - (size_t)take * (size_t)s->channels * sizeof(int16_t)); + if (s->f32_mode) + memcpy(s->outf + s->out_pos * (size_t)s->channels, + s->pcm.f32 + (size_t)s->drained * (size_t)s->channels, + (size_t)take * (size_t)s->channels * sizeof(float)); + else + memcpy(s->out + s->out_pos * (size_t)s->channels, + s->pcm.s16 + (size_t)s->drained * (size_t)s->channels, + (size_t)take * (size_t)s->channels * sizeof(int16_t)); s->out_pos += take; } s->drained += take; @@ -3084,7 +3138,8 @@ int rmp3_stream_process(rmp3_stream_t *s, size_t *read, size_t *wrote) memset(&info, 0, sizeof(info)); samples = rmp3dec_decode_frame(&s->dec, src, (int)avail, - parse_only ? NULL : (void*)s->pcm, &info, 0); + parse_only ? NULL : (void*)&s->pcm, &info, + (int)s->f32_mode); if (!samples && !info.frame_bytes) { @@ -3120,11 +3175,46 @@ int rmp3_stream_process(rmp3_stream_t *s, size_t *read, size_t *wrote) s->rate = (unsigned)info.hz; s->started = 1; } - /* A stream that changes geometry partway is not something the - * fixed output layout can express. */ - else if ((unsigned)info.channels != s->channels - || (unsigned)info.hz != s->rate) - return RMP3_STREAM_ERROR; + /* A malformed stream that changes channel count partway is + * adapted to the stream's own layout - mono duplicated, + * stereo averaged, the same arithmetic the pull API's reads + * apply - so the interleave stays consistent. A rate change + * likewise decodes through at the stream's stated rate, which + * is all the pull API ever did with one. */ + else if (!parse_only + && (unsigned)info.channels != s->channels) + { + if (s->channels == 2) + { + /* Widening in place: pair 2i,2i+1 overlays sample i, + * so walking backwards reads each source before its + * slots are clobbered. */ + int i = samples; + if (s->f32_mode) + while (i-- > 0) + s->pcm.f32[2*i] = s->pcm.f32[2*i + 1] + = s->pcm.f32[i]; + else + while (i-- > 0) + s->pcm.s16[2*i] = s->pcm.s16[2*i + 1] + = s->pcm.s16[i]; + } + else + { + /* Narrowing in place: sample i overlays half of the + * pair 2i,2i+1, which is behind the read position + * walking forwards. */ + int i; + if (s->f32_mode) + for (i = 0; i < samples; i++) + s->pcm.f32[i] = (s->pcm.f32[2*i] + + s->pcm.f32[2*i + 1]) * 0.5f; + else + for (i = 0; i < samples; i++) + s->pcm.s16[i] = (int16_t)(((int32_t)s->pcm.s16[2*i] + + (int32_t)s->pcm.s16[2*i + 1]) >> 1); + } + } if (parse_only) { diff --git a/libretro-common/include/formats/rmp3.h b/libretro-common/include/formats/rmp3.h index 00b8fcc103bd..03ae2473c43e 100644 --- a/libretro-common/include/formats/rmp3.h +++ b/libretro-common/include/formats/rmp3.h @@ -68,6 +68,15 @@ typedef struct } scratch; } rmp3dec; +/* One decoded MPEG frame, in whichever format the synthesis ran in. + * The two pipelines share the storage through this union; a format + * switch converts the contents in place. */ +typedef union +{ + int16_t s16[RMP3_MAX_SAMPLES_PER_FRAME]; + float f32[RMP3_MAX_SAMPLES_PER_FRAME]; +} rmp3_frame_buf; + /* Main API (Pull API) * ===================*/ @@ -80,11 +89,7 @@ typedef struct uint32_t framesConsumed; /* PCM frames of the current block already returned. Internal. */ uint32_t framesRemaining; /* PCM frames of the current block still to return. Internal. */ uint32_t f32_mode; /* 0: synthesis outputs s16; 1: native float. Internal. */ - union - { - int16_t s16[RMP3_MAX_SAMPLES_PER_FRAME]; - float f32[RMP3_MAX_SAMPLES_PER_FRAME]; - } frames; /* Current decoded frame in the latched format. */ + rmp3_frame_buf frames; /* Current decoded frame in the latched format. */ const uint8_t* pData; /* Caller's buffer (borrowed, never freed). */ size_t dataSize; size_t readPos; /* Read cursor into pData. */ @@ -177,21 +182,33 @@ void rmp3_stream_set_in(rmp3_stream_t *s, const void *in, size_t in_size); /** * rmp3_stream_set_out_s16: + * rmp3_stream_set_out_f32: * * Sets the destination and its capacity in frames, interleaved at the * stream's own channel count. A null destination, or a capacity of * zero, is parse-only. + * + * The two entry points select the same two pipelines the pull API + * runs: s16 synthesises in Q28 fixed point straight to s16, f32 in + * native float. They may be mixed freely on one stream - the decoder's + * persistent filter state and any undrained frame are converted in + * place at the switch, exactly as the pull API's reads do - but a + * parse-only call selects nothing, so a walk in the middle of a decode + * does not disturb the pipeline the decode is in. */ void rmp3_stream_set_out_s16(rmp3_stream_t *s, int16_t *out, size_t out_frames); +void rmp3_stream_set_out_f32(rmp3_stream_t *s, float *out, size_t out_frames); int rmp3_stream_process(rmp3_stream_t *s, size_t *read, size_t *wrote); /** * rmp3_stream_info: * - * Returns: nonzero once a frame has been decoded, which is the point + * Returns: nonzero once a frame has been located, which is the point * the channel count and rate are known - MPEG audio has no header - * ahead of the stream to read them from. + * ahead of the stream to read them from, so they come off the first + * frame's own header. A parse-only walk locates frames too, so this + * answers there without anything having been decoded. */ int rmp3_stream_info(const rmp3_stream_t *s, unsigned *channels, unsigned *rate);