/* play/decoder.c — 格式分发解码实现(接口见 decoder.h,REPORT §19)。 * * mp3 细节:minimp3_ex 的 dec.info(hz/channels)在首次 mp3dec_ex_read 后才保证有效, * 而调用方开 audio_out 前就要 rate/ch —— open 时先预读一批进 stash,read 先吐 stash。 * seek(mp3):关+重开(MP3D_SEEK_TO_BYTE 索引在 open 时重建)再按字节近似 seek,同旧语义。 */ #include "decoder.h" #include "minimp3_ex.h" #include "dr_flac.h" #include #include #include #define DEC_STASH_FRAMES 4096 /* open 预读上限(帧),与播放循环单块读同量级 */ enum { DEC_MP3 = 0, DEC_FLAC = 1 }; struct ncm_decoder { int kind; char path[512]; int rate, ch, bits; int64_t total_frames; /* 0=未知 */ /* mp3 */ mp3dec_ex_t mp3; int16_t *stash; /* 预读样本(交织 s16),stash_n 个样本 */ size_t stash_n, stash_off; /* flac */ drflac *flac; }; /* magic 探测:首 4 字节 "fLaC" → flac;其余(ID3 / 0xFF 帧同步 / 未知)→ mp3 *(未知格式留给 minimp3 在 open 时报错,不在本层加第三种分支)。 */ static int sniff_kind(const char *path) { unsigned char m[4] = {0}; FILE *f = fopen(path, "rb"); if (f) { size_t n = fread(m, 1, 4, f); fclose(f); if (n < 4) return DEC_MP3; } if (m[0] == 'f' && m[1] == 'L' && m[2] == 'a' && m[3] == 'C') return DEC_FLAC; return DEC_MP3; } int ncm_dec_open(ncm_decoder_t **out, const char *path, int *rate, int *ch, int *bits, int64_t *total_frames) { if (!out || !path) return -1; ncm_decoder_t *d = (ncm_decoder_t *)calloc(1, sizeof *d); if (!d) return -1; d->kind = sniff_kind(path); snprintf(d->path, sizeof d->path, "%s", path); if (d->kind == DEC_FLAC) { d->flac = drflac_open_file(path, NULL); if (!d->flac) { free(d); return -1; } d->rate = (int)d->flac->sampleRate; d->ch = (int)d->flac->channels; /* 位宽映射(AUDIOD §4 归一交给 SDK/audiod):≤16 → S16;>16 → S32 满幅 */ d->bits = (d->flac->bitsPerSample <= 16) ? 16 : 32; d->total_frames = (int64_t)d->flac->totalPCMFrameCount; } else { if (mp3dec_ex_open(&d->mp3, path, MP3D_SEEK_TO_BYTE) != 0) { free(d); return -1; } d->bits = 16; d->stash = (int16_t *)malloc(DEC_STASH_FRAMES * 2 * sizeof(int16_t)); if (!d->stash) { mp3dec_ex_close(&d->mp3); free(d); return -1; } /* 预读首块:填 dec.info(hz/channels)+ 预热 stash */ size_t got = mp3dec_ex_read(&d->mp3, d->stash, DEC_STASH_FRAMES * 2); d->stash_n = got; d->rate = d->mp3.info.hz; d->ch = d->mp3.info.channels; if (d->rate <= 0 || d->ch <= 0) { /* 首块即失败/空流 */ mp3dec_ex_close(&d->mp3); free(d->stash); free(d); return -1; } d->total_frames = (d->ch > 0) ? (int64_t)(d->mp3.samples / (uint64_t)d->ch) : 0; } if (d->ch < 1 || d->ch > 2) { ncm_dec_close(d); return -1; } /* v1 只接 单/双声道 */ *out = d; if (rate) *rate = d->rate; if (ch) *ch = d->ch; if (bits) *bits = d->bits; if (total_frames) *total_frames = d->total_frames; return 0; } int64_t ncm_dec_read(ncm_decoder_t *d, void *buf, int64_t cap_frames) { if (!d || !buf || cap_frames <= 0) return 0; if (d->kind == DEC_FLAC) { if (d->bits == 16) return (int64_t)drflac_read_pcm_frames_s16(d->flac, (drflac_uint64)cap_frames, (drflac_int16 *)buf); return (int64_t)drflac_read_pcm_frames_s32(d->flac, (drflac_uint64)cap_frames, (drflac_int32 *)buf); } /* mp3:先吐 stash,再直读 */ int16_t *out = (int16_t *)buf; int64_t cap_samples = cap_frames * d->ch; int64_t done = 0; if (d->stash_off < d->stash_n) { size_t left = d->stash_n - d->stash_off; size_t take = left < (size_t)cap_samples ? left : (size_t)cap_samples; memcpy(out, d->stash + d->stash_off, take * sizeof(int16_t)); d->stash_off += take; done += (int64_t)take; } if (done < cap_samples) { size_t got = mp3dec_ex_read(&d->mp3, out + done, (size_t)(cap_samples - done)); done += (int64_t)got; } return done / d->ch; } int ncm_dec_seek_ms(ncm_decoder_t *d, int64_t ms, int64_t file_bytes, int64_t dur_ms) { if (!d || ms < 0) return -1; if (d->kind == DEC_FLAC) { drflac_uint64 frame = (drflac_uint64)(ms * d->rate / 1000); return drflac_seek_to_pcm_frame(d->flac, frame) ? 0 : -1; } /* mp3:关+重开(重建字节索引)再按字节近似 */ mp3dec_ex_close(&d->mp3); memset(&d->mp3, 0, sizeof d->mp3); if (mp3dec_ex_open(&d->mp3, d->path, MP3D_SEEK_TO_BYTE) != 0) return -1; d->stash_n = d->stash_off = 0; int64_t target = (file_bytes > 0 && dur_ms > 0) ? file_bytes * ms / dur_ms : 0; return mp3dec_ex_seek(&d->mp3, (uint64_t)(target > 0 ? target : 0)); } void ncm_dec_close(ncm_decoder_t *d) { if (!d) return; if (d->kind == DEC_FLAC) { if (d->flac) drflac_close(d->flac); } else { mp3dec_ex_close(&d->mp3); free(d->stash); } free(d); } const char *ncm_dec_format(const ncm_decoder_t *d) { if (!d) return "?"; return d->kind == DEC_FLAC ? "flac" : "mp3"; }