From 7acd82ca4d0234fead2cf4fd3bc332261cdc7e9c Mon Sep 17 00:00:00 2001 From: N00byKing Date: Sat, 12 Mar 2022 21:10:09 +0100 Subject: [PATCH] Add support for loop points --- desktop_version/src/Music.cpp | 106 ++++++++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 5 deletions(-) diff --git a/desktop_version/src/Music.cpp b/desktop_version/src/Music.cpp index c5dd5a6213..24dd8f960f 100644 --- a/desktop_version/src/Music.cpp +++ b/desktop_version/src/Music.cpp @@ -3,7 +3,6 @@ #include #include - #include //For stb_vorbis @@ -171,6 +170,11 @@ class MusicTrack decoded_buf_playing = (Uint8*)SDL_malloc(size); decoded_buf_reserve = (Uint8*)SDL_malloc(size); + + loopbegin = 0; + looplength = 0; + stb_vorbis_comment vorbis_comment = stb_vorbis_get_comment(vorbis); + parseComments(this, vorbis_comment.comment_list, vorbis_comment.comment_list_length); } void Dispose() @@ -188,6 +192,8 @@ class MusicTrack bool Play(bool loop) { /* Create/Validate static FAudioSourceVoice, begin streaming */ + shouldloop = loop; + sample_pos = 0; stb_vorbis_seek_start(vorbis); if (IsHalted()) { @@ -202,10 +208,15 @@ class MusicTrack FAudioBuffer faudio_buffer; SDL_memset(&faudio_buffer, 0, sizeof(FAudioBuffer)); - faudio_buffer.PlayLength = stb_vorbis_get_samples_float_interleaved(vorbis, channels, (float*)decoded_buf_playing, size/sizeof(float)); + if (looplength == 0) { + faudio_buffer.PlayLength = stb_vorbis_get_samples_float_interleaved(vorbis, channels, (float*)decoded_buf_playing, size/sizeof(float)); + } else { + faudio_buffer.PlayLength = std::min(stb_vorbis_get_samples_float_interleaved(vorbis, channels, (float*)decoded_buf_playing, size/sizeof(float)), (loopbegin+looplength)-sample_pos); + } faudio_buffer.AudioBytes = size; faudio_buffer.pAudioData = decoded_buf_playing; faudio_buffer.pContext = this; + sample_pos += faudio_buffer.PlayLength; if (FAudioSourceVoice_SubmitSourceBuffer(musicVoice, &faudio_buffer, NULL)) { vlog_error("Unable to queue sound buffer"); return false; @@ -249,6 +260,9 @@ class MusicTrack stb_vorbis* vorbis = NULL; int channels; Uint32 size; + int loopbegin; + int looplength; + int sample_pos; //stb_vorbis offset not yet functional on pulldata API. TODO Replace when fixed FAudioVoiceCallback callbacks; FAudioWaveFormatEx format; @@ -264,25 +278,107 @@ class MusicTrack MusicTrack* t = (MusicTrack*)ctx; FAudioBuffer faudio_buffer; SDL_memset(&faudio_buffer, 0, sizeof(FAudioBuffer)); - faudio_buffer.PlayLength = stb_vorbis_get_samples_float_interleaved(t->vorbis, t->channels, (float*)t->decoded_buf_reserve, t->size/sizeof(float)); + if (t->looplength == 0) { + faudio_buffer.PlayLength = stb_vorbis_get_samples_float_interleaved(t->vorbis, t->channels, (float*)t->decoded_buf_reserve, t->size/sizeof(float)); + } else { + faudio_buffer.PlayLength = std::min(stb_vorbis_get_samples_float_interleaved(t->vorbis, t->channels, (float*)t->decoded_buf_reserve, t->size/sizeof(float)), (t->loopbegin+t->looplength)-t->sample_pos); + } faudio_buffer.AudioBytes = t->size; faudio_buffer.pAudioData = t->decoded_buf_reserve; faudio_buffer.pContext = t; if (faudio_buffer.PlayLength == 0) { if (t->shouldloop) { - stb_vorbis_seek_start(t->vorbis); - faudio_buffer.PlayLength = stb_vorbis_get_samples_float_interleaved(t->vorbis, t->channels, (float*)t->decoded_buf_reserve, t->size/sizeof(float)); + stb_vorbis_seek(t->vorbis, t->loopbegin); + t->sample_pos = t->loopbegin; + if (t->looplength != 0) { + faudio_buffer.PlayLength = std::min(stb_vorbis_get_samples_float_interleaved(t->vorbis, t->channels, (float*)t->decoded_buf_reserve, t->size/sizeof(float)), t->looplength); + } else { + faudio_buffer.PlayLength = stb_vorbis_get_samples_float_interleaved(t->vorbis, t->channels, (float*)t->decoded_buf_reserve, t->size/sizeof(float)); + } if (faudio_buffer.PlayLength == 0) return; } else { return; } } + t->sample_pos += faudio_buffer.PlayLength; FAudioSourceVoice_SubmitSourceBuffer(musicVoice, &faudio_buffer, NULL); } static void swapBuffers(FAudioVoiceCallback* callback, void* ctx) { MusicTrack* t = (MusicTrack*)ctx; std::swap(t->decoded_buf_playing, t->decoded_buf_reserve); } + + //Lifted from sdl_mixer + static void parseComments(MusicTrack* t, char** comments, int comment_list_length) { + int loopend = 0; + for (int i = 0; i < comment_list_length; i++) { + char *param = SDL_strdup(comments[i]); + char *argument = param; + char *value = SDL_strchr(param, '='); + if (value == NULL) { + value = param + SDL_strlen(param); + } else { + *(value++) = '\0'; + } + + /* Want to match LOOP-START, LOOP_START, etc. Remove - or _ from + * string if it is present at position 4. */ + //_Mix_IsLoopTag inlined + char buf[5]; + SDL_strlcpy(buf, argument, 5); + if (SDL_strcasecmp(buf, "LOOP") == 0 && ((argument[4] == '_') || (argument[4] == '-'))) { + SDL_memmove(argument + 4, argument + 5, SDL_strlen(argument) - 4); + } + + if (SDL_strcasecmp(argument, "LOOPSTART") == 0) + t->loopbegin = _Mix_ParseTime(value, t->format.nSamplesPerSec); + else if (SDL_strcasecmp(argument, "LOOPLENGTH") == 0) { + t->looplength = SDL_strtoll(value, NULL, 10); + } else if (SDL_strcasecmp(argument, "LOOPEND") == 0) { + loopend = _Mix_ParseTime(value, t->format.nSamplesPerSec); + } + + SDL_free(param); + } + if (loopend != 0) { + t->looplength = loopend - t->loopbegin; + } + } + //_Mix_ParseTime + static int _Mix_ParseTime(char *time, long samplerate_hz) { + char *num_start, *p; + Sint64 result; + char c; + int val; + + /* Time is directly expressed as a sample position */ + if (SDL_strchr(time, ':') == NULL) { + return SDL_strtoll(time, NULL, 10); + } + + result = 0; + num_start = time; + + for (p = time; *p != '\0'; ++p) { + if (*p == '.' || *p == ':') { + c = *p; *p = '\0'; + if ((val = SDL_atoi(num_start)) < 0) + return -1; + result = result * 60 + val; + num_start = p + 1; + *p = c; + } + + if (*p == '.') { + double val_f = SDL_atof(p); + if (val_f < 0) return -1; + return result * samplerate_hz + (Sint64) (val_f * samplerate_hz); + } + } + + if ((val = SDL_atoi(num_start)) < 0) return -1; + return (result * 60 + val) * samplerate_hz; + } }; FAudioSourceVoice* MusicTrack::musicVoice = NULL;