audio: Kill dr_flac and update libFLAC and use it directly

This commit is contained in:
Joel16 2021-12-24 01:45:37 -05:00
parent 10ef0d85aa
commit 438da0f6ae
3 changed files with 198 additions and 12367 deletions

View File

@ -1,23 +1,59 @@
#include <FLAC/metadata.h>
#include <cassert>
#include <cstring>
#include <FLAC/stream_decoder.h>
#include "audio.h"
#define DR_FLAC_IMPLEMENTATION
#include "dr_flac.h"
#include "log.h"
#include "textures.h"
#define FLACDECODE_IMAGE_BYTES_MAX (100 * 1024 * 1024)
namespace FLAC {
static drflac *flac;
static drflac_uint64 frames_read = 0;
typedef struct {
const FLAC__Frame *frame = nullptr;
const FLAC__int32 * const *buffer = nullptr;
FLAC__uint8 channels = 0;
FLAC__uint32 sample_rate = 0;
FLAC__uint32 bps = 0;
FLAC__uint64 position = 0;
FLAC__uint64 total_samples = 0;
} FLACInfo;
int Init(const std::string &path) {
flac = drflac_open_file(path.c_str(), nullptr);
if (flac == nullptr)
return -1;
static FLAC__StreamDecoder *flac = nullptr;
static FLACInfo cb_info { 0 };
FLAC__StreamMetadata *tags;
if (FLAC__metadata_get_tags(path.c_str(), &tags)) {
for (FLAC__uint32 i = 0; i < tags->data.vorbis_comment.num_comments; i++) {
char *tag = reinterpret_cast<char *>(tags->data.vorbis_comment.comments[i].entry);
static FLAC__StreamDecoderWriteStatus write_cb(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) {
FLACInfo *info = reinterpret_cast<FLACInfo *>(client_data);
if (info->total_samples == 0) {
std::printf("No samples to decode!\n");
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}
if ((info->channels != 2) || (info->bps != 16)) {
std::printf("Not stereo 16-bit!\n");
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}
info->frame = frame;
info->buffer = buffer;
info->position = 0;
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
static void metadata_cb(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *stream, void *client_data) {
FLACInfo *info = reinterpret_cast<FLACInfo *>(client_data);
switch (stream->type) {
case FLAC__METADATA_TYPE_STREAMINFO:
info->total_samples = stream->data.stream_info.total_samples;
info->sample_rate = stream->data.stream_info.sample_rate;
info->channels = stream->data.stream_info.channels;
info->bps = stream->data.stream_info.bits_per_sample;
break;
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
for (FLAC__uint32 i = 0; i < stream->data.vorbis_comment.num_comments; i++) {
char *tag = reinterpret_cast<char *>(stream->data.vorbis_comment.comments[i].entry);
if (!strncasecmp("TITLE=", tag, 6)) {
metadata.has_meta = true;
@ -49,87 +85,148 @@ namespace FLAC {
metadata.genre = tag + 6;
}
}
}
break;
if (tags)
FLAC__metadata_object_delete(tags);
FLAC__StreamMetadata *picture;
if (FLAC__metadata_get_picture(path.c_str(), &picture, FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER, "image/jpg", nullptr, 512, 512, -1, -1)) {
case FLAC__METADATA_TYPE_PICTURE:
if (stream->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER) {
if ((!strcasecmp(stream->data.picture.mime_type, "image/jpeg")) || (!strcasecmp(stream->data.picture.mime_type, "image/jpg"))) {
metadata.has_meta = true;
metadata.cover_image = Textures::LoadImageBufferJPEG(picture->data.picture.data, picture->length);
FLAC__metadata_object_delete(picture);
if (!metadata.cover_image)
metadata.cover_image = Textures::LoadImageBufferJPEG(stream->data.picture.data, stream->data.picture.data_length);
}
else if (FLAC__metadata_get_picture(path.c_str(), &picture, FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER, "image/jpeg", nullptr, 512, 512, -1, -1)) {
else if (!strcasecmp(stream->data.picture.mime_type, "image/png")) {
metadata.has_meta = true;
metadata.cover_image = Textures::LoadImageBufferJPEG(picture->data.picture.data, picture->length);
FLAC__metadata_object_delete(picture);
if (!metadata.cover_image)
metadata.cover_image = Textures::LoadImageBufferPNG(stream->data.picture.data, stream->data.picture.data_length);
}
}
break;
default:
break;
}
else if (FLAC__metadata_get_picture(path.c_str(), &picture, FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER, "image/png", nullptr, 512, 512, -1, -1)) {
metadata.has_meta = true;
metadata.cover_image = Textures::LoadImageBufferPNG(picture->data.picture.data, picture->length);
FLAC__metadata_object_delete(picture);
}
return 0;
static void error_cb(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) {
std::string error;
switch(status) {
case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
error = "Lost sync";
break;
case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
error = "Bad header";
break;
case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
error = "Frame CRC mismatch";
break;
case FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM:
error = "Unparseable stream";
break;
default:
error = "???";
}
u32 GetSampleRate(void) {
return flac->sampleRate;
std::printf("error: %s\n", error.c_str());
}
u8 GetChannels(void) {
return flac->channels;
}
void Decode(void *buf, unsigned int length, void *userdata) {
frames_read += drflac_read_pcm_frames_s16(flac, static_cast<drflac_uint64>(length), static_cast<drflac_int16 *>(buf));
if (frames_read >= flac->totalPCMFrameCount)
playing = false;
}
u64 GetPosition(void) {
return frames_read;
}
u64 GetLength(void) {
return flac->totalPCMFrameCount;
}
u64 Seek(u64 index) {
drflac_uint64 seek_frame = (flac->totalPCMFrameCount * (index / 225.0));
if (drflac_seek_to_pcm_frame(flac, seek_frame) == DRFLAC_TRUE) {
frames_read = seek_frame;
return frames_read;
}
int Init(const std::string &path) {
if ((flac = FLAC__stream_decoder_new()) == nullptr)
return -1;
if (FLAC__stream_decoder_set_metadata_respond(flac, FLAC__METADATA_TYPE_STREAMINFO) == false) {
Log::Error("FLAC__METADATA_TYPE_STREAMINFO response failed\n");
return -1;
}
if (FLAC__stream_decoder_set_metadata_respond(flac, FLAC__METADATA_TYPE_SEEKTABLE) == false)
Log::Error("FLAC__METADATA_TYPE_SEEKTABLE response failed\n");
if (FLAC__stream_decoder_set_metadata_respond(flac, FLAC__METADATA_TYPE_VORBIS_COMMENT) == false)
Log::Error("FLAC__METADATA_TYPE_VORBIS_COMMENT response failed\n");
if (FLAC__stream_decoder_set_metadata_respond(flac, FLAC__METADATA_TYPE_PICTURE) == false)
Log::Error("FLAC__METADATA_TYPE_PICTURE response failed\n");
FLAC__StreamDecoderInitStatus ret;
if ((ret = FLAC__stream_decoder_init_file(flac, path.c_str(), write_cb, metadata_cb, error_cb, &cb_info)) != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
Log::Error("FLAC__stream_decoder_init_file failed: %s\n", FLAC__StreamDecoderInitStatusString[ret]);
return ret;
}
return FLAC__stream_decoder_process_until_end_of_metadata(flac);
}
u32 GetSampleRate(void) {
return cb_info.sample_rate;
}
u8 GetChannels(void) {
return cb_info.channels;
}
void Decode(void *buf, unsigned int length, void *userdata) {
unsigned int decoded = 0;
FLAC__bool ret = false;
if (length <= 0)
return;
FLAC__StreamDecoderState state = FLAC__stream_decoder_get_state(flac);
if (state == FLAC__STREAM_DECODER_END_OF_STREAM)
playing = false;
else if (state == FLAC__STREAM_DECODER_ABORTED)
return;
while(decoded < length) {
if (cb_info.frame == nullptr || cb_info.position == cb_info.frame->header.blocksize) {
ret = FLAC__stream_decoder_process_single(flac);
if (ret == false) {
playing = false;
return;
}
state = FLAC__stream_decoder_get_state(flac);
if (state == FLAC__STREAM_DECODER_END_OF_STREAM || state == FLAC__STREAM_DECODER_ABORTED) {
playing = false;
return;
}
}
for(; decoded < length && cb_info.position < cb_info.frame->header.blocksize; cb_info.position++, decoded++) {
// Copy to buffer here; convert from BE to LE
short *buffer = static_cast<short *>(buf);
buffer[decoded * 2] = cb_info.buffer[0][cb_info.position];
buffer[decoded * 2 + 1] = cb_info.buffer[1][cb_info.position];
}
}
}
u64 GetPosition(void) {
FLAC__uint64 position = 0;
FLAC__stream_decoder_get_decode_position(flac, &position);
return position;
}
u64 GetLength(void) {
return cb_info.total_samples;
}
u64 Seek(u64 index) {
return 0;
}
void Exit(void) {
frames_read = 0;
if (metadata.has_meta) {
metadata.has_meta = false;
if (metadata.cover_image)
g2dTexFree(&metadata.cover_image);
}
drflac_close(flac);
}
// Functions needed for libFLAC
int chmod(const char *pathname, mode_t mode) {
return 0;
}
int chown(const char *path, int owner, int group) {
return 0;
}
int utime(const char *filename, const void *buf) {
return 0;
cb_info = { 0 };
FLAC__stream_decoder_finish(flac);
FLAC__stream_decoder_delete(flac);
}
}

File diff suppressed because it is too large Load Diff

Binary file not shown.