Skip to content
Permalink
Browse files
Backport of the loops support for FLAC
  • Loading branch information
Wohlstand committed Nov 17, 2019
1 parent 22ed104 commit 6df32954b79a64874d2f728551c8fdf36cb1107f
Showing 1 changed file with 185 additions and 48 deletions.
@@ -48,10 +48,8 @@ typedef struct {
FLAC__StreamDecoderMetadataCallback metadata_callback,
FLAC__StreamDecoderErrorCallback error_callback,
void *client_data);
FLAC__bool (*FLAC__stream_decoder_set_metadata_respond)(FLAC__StreamDecoder *decoder, FLAC__MetadataType type);
FLAC__bool (*FLAC__stream_decoder_finish)(FLAC__StreamDecoder *decoder);
FLAC__bool (*FLAC__stream_decoder_flush)(FLAC__StreamDecoder *decoder);
FLAC__uint64 (*FLAC__stream_decoder_get_total_samples)(const FLAC__StreamDecoder *decoder);
FLAC__bool (*FLAC__stream_decoder_process_single)(
FLAC__StreamDecoder *decoder);
FLAC__bool (*FLAC__stream_decoder_process_until_end_of_metadata)(
@@ -63,6 +61,11 @@ typedef struct {
FLAC__uint64 sample);
FLAC__StreamDecoderState (*FLAC__stream_decoder_get_state)(
const FLAC__StreamDecoder *decoder);
FLAC__uint64 (*FLAC__stream_decoder_get_total_samples)(
const FLAC__StreamDecoder *decoder);
FLAC__bool (*FLAC__stream_decoder_set_metadata_respond)(
FLAC__StreamDecoder *decoder,
FLAC__MetadataType type);
} flac_loader;

static flac_loader flac = {
@@ -111,15 +114,18 @@ static int FLAC_Load(void)
FLAC__StreamDecoderMetadataCallback,
FLAC__StreamDecoderErrorCallback,
void *))
FUNCTION_LOADER(FLAC__stream_decoder_set_metadata_respond, FLAC__bool (*)(FLAC__StreamDecoder *, FLAC__MetadataType));
FUNCTION_LOADER(FLAC__stream_decoder_finish, FLAC__bool (*)(FLAC__StreamDecoder *))
FUNCTION_LOADER(FLAC__stream_decoder_flush, FLAC__bool (*)(FLAC__StreamDecoder *))
FUNCTION_LOADER(FLAC__stream_decoder_get_total_samples, FLAC__uint64 (*)(const FLAC__StreamDecoder *));
FUNCTION_LOADER(FLAC__stream_decoder_process_single, FLAC__bool (*)(FLAC__StreamDecoder *))
FUNCTION_LOADER(FLAC__stream_decoder_process_until_end_of_metadata, FLAC__bool (*)(FLAC__StreamDecoder *))
FUNCTION_LOADER(FLAC__stream_decoder_process_until_end_of_stream, FLAC__bool (*)(FLAC__StreamDecoder *))
FUNCTION_LOADER(FLAC__stream_decoder_seek_absolute, FLAC__bool (*)(FLAC__StreamDecoder *, FLAC__uint64))
FUNCTION_LOADER(FLAC__stream_decoder_get_state, FLAC__StreamDecoderState (*)(const FLAC__StreamDecoder *decoder))
FUNCTION_LOADER(FLAC__stream_decoder_get_total_samples,
FLAC__uint64 (*)(const FLAC__StreamDecoder *));
FUNCTION_LOADER(FLAC__stream_decoder_set_metadata_respond,
FLAC__bool (*)(FLAC__StreamDecoder *,
FLAC__MetadataType));
}
++flac.loaded;

@@ -150,9 +156,13 @@ typedef struct {
SDL_RWops *src;
int freesrc;
SDL_AudioStream *stream;
int loop;
FLAC__uint64 pcm_pos;
SDL_bool loop_flag;
FLAC__uint64 loop_start;
FLAC__uint64 loop_end;
FLAC__uint64 loop_len;
FLAC__uint64 total_samples;
FLAC__uint64 sample_position;
FLAC__uint64 sample_size;
Mix_MusicMetaTags tags;
} FLAC_Music;

@@ -266,7 +276,7 @@ static FLAC__StreamDecoderWriteStatus flac_write_music_cb(
FLAC_Music *music = (FLAC_Music *)client_data;
Sint16 *data;
unsigned int i, j, channels;
int shift_amount = 0;
int shift_amount = 0, amount;
(void)decoder; /* UNUSED */

if (!music->stream) {
@@ -337,53 +347,124 @@ static FLAC__StreamDecoderWriteStatus flac_write_music_cb(
}
}
}
amount = frame->header.blocksize * channels * sizeof(*data);
music->pcm_pos += ((FLAC__uint64)frame->header.blocksize);
if ((music->loop == 1) && (music->pcm_pos >= music->loop_end)) {
amount -= (music->pcm_pos - music->loop_end) * channels * sizeof(*data);
music->loop_flag = SDL_TRUE;
}

/* Increase sample position */
music->sample_position += ((FLAC__uint64)frame->header.blocksize);

SDL_AudioStreamPut(music->stream, data, (int)(frame->header.blocksize * channels * sizeof(*data)));
SDL_AudioStreamPut(music->stream, data, amount);
SDL_stack_free(data);

return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}

/* Parse time string of the form HH:MM:SS.mmm and return equivalent sample
* position */
static FLAC__uint64 parse_time(char *time, unsigned samplerate_hz)
{
char *num_start, *p;
FLAC__uint64 result = 0;
char c;

/* Time is directly expressed as a sample position */
if (SDL_strchr(time, ':') == NULL) {
return (FLAC__uint64)SDL_strtoull(time, NULL, 10);
}

result = 0;
num_start = time;

for (p = time; *p != '\0'; ++p) {
if (*p == '.' || *p == ':') {
c = *p; *p = '\0';
result = result * 60 + SDL_atoi(num_start);
num_start = p + 1;
*p = c;
}

if (*p == '.') {
return result * samplerate_hz
+ (FLAC__uint64) (SDL_atof(p) * samplerate_hz);
}
}

return (result * 60 + SDL_atoi(num_start)) * samplerate_hz;
}

static SDL_bool is_loop_tag(const char *tag)
{
char buf[5];
SDL_strlcpy(buf, tag, 5);
return SDL_strcasecmp(buf, "LOOP") == 0;
}

static void flac_metadata_music_cb(
const FLAC__StreamDecoder *decoder,
const FLAC__StreamMetadata *metadata,
void *client_data)
{
FLAC_Music *music = (FLAC_Music *)client_data;
const FLAC__StreamMetadata_VorbisComment *vc;
int channels;
FLAC__uint32 i;
unsigned rate;
char *param, *argument, *value;
SDL_bool is_loop_length = SDL_FALSE;
MIX_UNUSED(decoder);

if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
music->sample_rate = metadata->data.stream_info.sample_rate;
music->channels = metadata->data.stream_info.channels;
music->bits_per_sample = metadata->data.stream_info.bits_per_sample;
music->total_samples = metadata->data.stream_info.total_samples;
music->sample_size = music->channels * ((music->bits_per_sample) / 8);
/*printf("FLAC: Sample rate = %d, channels = %d, bits_per_sample = %d\n", music->sample_rate, music->channels, music->bits_per_sample);*/

/* SDL's channel mapping and FLAC channel mapping are the same,
except for 3 channels: SDL is FL FR LFE and FLAC is FL FR FC
*/
if (music->channels == 3) {
channels = 2;
} else {
channels = (int)music->channels;
}
/* We check for NULL stream later when we get data */
SDL_assert(!music->stream);
music->stream = SDL_NewAudioStream(AUDIO_S16SYS, (Uint8)channels, (int)music->sample_rate,
music_spec.format, music_spec.channels, music_spec.freq);
} else if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
int i, num = (int)metadata->data.vorbis_comment.num_comments;
for (i = 0; i < num; i++) {
FLAC__uint32 len = metadata->data.vorbis_comment.comments[i].length;
FLAC__byte *ent = metadata->data.vorbis_comment.comments[i].entry;

int paramLen = (int)len + 1;
char *param = (char *)SDL_malloc((size_t)paramLen);
char *argument = param;
char *value = param;

SDL_memset(param, 0, (size_t)paramLen);
SDL_memcpy(param, ent, len);
vc = &metadata->data.vorbis_comment;
rate = music->sample_rate;

for (i = 0; i < vc->num_comments; i++) {
param = SDL_strdup((const char*)vc->comments[i].entry);
argument = param;
value = SDL_strchr(param, '=');

if (value == NULL) {
value = param + paramLen - 1; /* set null */
value = param + SDL_strlen(param);
} else {
*(value++) = '\0';
}

if (SDL_strcasecmp(argument, "TITLE") == 0) {
/* Want to match LOOP-START, LOOP_START, etc. Remove - or _ from
* string if it is present at position 4. */

if (is_loop_tag(argument) && ((argument[4] == '_') || (argument[4] == '-'))) {
SDL_memmove(argument + 4, argument + 5,
SDL_strlen(argument) - 4);
}

if (SDL_strcasecmp(argument, "LOOPSTART") == 0)
music->loop_start = parse_time(value, rate);
else if (SDL_strcasecmp(argument, "LOOPLENGTH") == 0) {
music->loop_len = SDL_strtoull(value, NULL, 10);
is_loop_length = SDL_TRUE;
} else if (SDL_strcasecmp(argument, "LOOPEND") == 0) {
music->loop_end = parse_time(value, rate);
is_loop_length = SDL_FALSE;
} else if (SDL_strcasecmp(argument, "TITLE") == 0) {
meta_tags_set(&music->tags, MIX_META_TITLE, value);
} else if (SDL_strcasecmp(argument, "ARTIST") == 0) {
meta_tags_set(&music->tags, MIX_META_ARTIST, value);
@@ -395,23 +476,17 @@ static void flac_metadata_music_cb(

SDL_free(param);
}

if (is_loop_length) {
music->loop_end = music->loop_start + music->loop_len;
} else {
music->loop_len = music->loop_end - music->loop_start;
}

return;
} else {
return;
}

/* SDL's channel mapping and FLAC channel mapping are the same,
except for 3 channels: SDL is FL FR LFE and FLAC is FL FR FC
*/
if (music->channels == 3) {
channels = 2;
} else {
channels = (int)music->channels;
}
/* We check for NULL stream later when we get data */
SDL_assert(!music->stream);
music->stream = SDL_NewAudioStream(AUDIO_S16SYS, (Uint8)channels, (int)music->sample_rate,
music_spec.format, music_spec.channels, music_spec.freq);
}

static void flac_error_music_cb(
@@ -447,6 +522,7 @@ static void *FLAC_CreateFromRW(SDL_RWops *src, int freesrc)
FLAC_Music *music;
int init_stage = 0;
int was_error = 1;
FLAC__uint64 full_length;

music = (FLAC_Music *)SDL_calloc(1, sizeof(*music));
if (!music) {
@@ -455,13 +531,21 @@ static void *FLAC_CreateFromRW(SDL_RWops *src, int freesrc)
}
music->src = src;
music->volume = MIX_MAX_VOLUME;
music->loop = -1;
music->loop_start = -1;
music->loop_end = 0;
music->loop_len = 0;
music->loop_flag = SDL_FALSE;
music->pcm_pos = 0;

music->flac_decoder = flac.FLAC__stream_decoder_new();
if (music->flac_decoder) {

/* Retreive stream info and vorbis comments. Must be called BEFORE stream initialization */
flac.FLAC__stream_decoder_set_metadata_respond(music->flac_decoder, FLAC__METADATA_TYPE_STREAMINFO);
flac.FLAC__stream_decoder_set_metadata_respond(music->flac_decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);
flac.FLAC__stream_decoder_set_metadata_respond(music->flac_decoder,
FLAC__METADATA_TYPE_STREAMINFO);
flac.FLAC__stream_decoder_set_metadata_respond(music->flac_decoder,
FLAC__METADATA_TYPE_VORBIS_COMMENT);

init_stage++; /* stage 1! */

@@ -499,6 +583,20 @@ static void *FLAC_CreateFromRW(SDL_RWops *src, int freesrc)
return NULL;
}

/* loop_start, loop_end and loop_len get set by metadata callback if tags
* are present in metadata.
*/

full_length = flac.FLAC__stream_decoder_get_total_samples(music->flac_decoder);
if (((music->loop_start >= 0) || (music->loop_end > 0)) &&
((music->loop_start < music->loop_end) || (music->loop_end > 0)) &&
(music->loop_start < full_length) &&
(music->loop_end <= full_length)) {
if (music->loop_start < 0) music->loop_start = 0;
if (music->loop_end == 0) music->loop_end = full_length;
music->loop = 1;
}

music->freesrc = freesrc;
return music;
}
@@ -529,7 +627,7 @@ static int FLAC_Play(void *context, int play_count)
{
FLAC_Music *music = (FLAC_Music *)context;
music->play_count = play_count;
music->sample_position = 0;
music->pcm_pos = 0;
return FLAC_Seek(music, 0.0);
}

@@ -555,6 +653,18 @@ static int FLAC_GetSome(void *context, void *data, int bytes, SDL_bool *done)
return -1;
}

if (music->loop_flag) {
music->pcm_pos = music->loop_start;
if (flac.FLAC__stream_decoder_seek_absolute(music->flac_decoder, music->loop_start) ==
FLAC__STREAM_DECODER_SEEK_ERROR) {
SDL_SetError("FLAC__stream_decoder_seek_absolute() failed");
flac.FLAC__stream_decoder_flush(music->flac_decoder);
return -1;
} else {
music->loop_flag = SDL_FALSE;
}
}

if (flac.FLAC__stream_decoder_get_state(music->flac_decoder) == FLAC__STREAM_DECODER_END_OF_STREAM) {
if (music->play_count == 1) {
music->play_count = 0;
@@ -585,6 +695,8 @@ static int FLAC_Seek(void *context, double position)
FLAC_Music *music = (FLAC_Music *)context;
double seek_sample = music->sample_rate * position;

music->pcm_pos = (FLAC__uint64)seek_sample;

/* Avoid junk chunks after seek */
if (music->stream) {
SDL_AudioStreamClear(music->stream);
@@ -598,16 +710,14 @@ static int FLAC_Seek(void *context, double position)
SDL_SetError("Seeking of FLAC stream failed: libFLAC seek failed.");
return -1;
}
music->sample_position = (FLAC__uint64)seek_sample;

return 0;
}

static double FLAC_get_time(void *context)
{
FLAC_Music *music = (FLAC_Music *)context;
if(music)
return (double)music->sample_position / (double)(music->sample_rate);
return (double)music->pcm_pos / (double)(music->sample_rate);
else
SDL_SetError("Getting position of FLAC stream failed: music was NULL.");
return -1.0;
@@ -627,6 +737,33 @@ static double FLAC_get_time_length(void *context)
return -1.0;
}

static double FLAC_get_loop_start(void *music_p)
{
FLAC_Music *music = (FLAC_Music *)music_p;
if (music->loop) {
return (double)music->loop_start / (double)music->sample_rate;
}
return -1.0;
}

static double FLAC_get_loop_end(void *music_p)
{
FLAC_Music *music = (FLAC_Music *)music_p;
if (music->loop) {
return (double)music->loop_end / (double)music->sample_rate;
}
return -1.0;
}

static double FLAC_get_loop_length(void *music_p)
{
FLAC_Music *music = (FLAC_Music *)music_p;
if (music->loop) {
return (double)music->loop_len / (double)music->sample_rate;
}
return -1.0;
}

/* Close the given FLAC_Music object */
static void FLAC_Delete(void *context)
{
@@ -671,9 +808,9 @@ Mix_MusicInterface Mix_MusicInterface_FLAC =
FLAC_get_time_length,/* FullLength [MIXER-X]*/
NULL, /* Set Tempo multiplier [MIXER-X] */
NULL, /* Get Tempo multiplier [MIXER-X] */
NULL, /* LoopStart [MIXER-X]*/
NULL, /* LoopEnd [MIXER-X]*/
NULL, /* LoopLength [MIXER-X]*/
FLAC_get_loop_start,/* LoopStart [MIXER-X]*/
FLAC_get_loop_end,/* LoopEnd [MIXER-X]*/
FLAC_get_loop_length,/* LoopLength [MIXER-X]*/
FLAC_GetMetaTag,/* GetMetaTag [MIXER-X]*/
NULL, /* Pause */
NULL, /* Resume */

0 comments on commit 6df3295

Please sign in to comment.