diff --git a/examples/MusPlay-Qt/MainWindow/musplayer_qt.cpp b/examples/MusPlay-Qt/MainWindow/musplayer_qt.cpp index 6b2c526..add3394 100644 --- a/examples/MusPlay-Qt/MainWindow/musplayer_qt.cpp +++ b/examples/MusPlay-Qt/MainWindow/musplayer_qt.cpp @@ -413,8 +413,10 @@ void MusPlayer_Qt::on_play_clicked() QString musicPath = m_currentMusic; #ifdef SDL_MIXER_GE21 QString midiRawArgs = m_setupMidi->getRawMidiArgs(); - if(ui->gme_setup->isEnabled() || PGE_MusicPlayer::type == MUS_PXTONE) + if(ui->gme_setup->isEnabled()) musicPath += "|" + ui->trackID->text() + ";" + midiRawArgs; + else if(PGE_MusicPlayer::type == MUS_PXTONE || PGE_MusicPlayer::type == MUS_OGG) + musicPath += "|" + midiRawArgs; else if((PGE_MusicPlayer::type == MUS_MID || PGE_MusicPlayer::type == MUS_ADLMIDI)) { if(midiRawArgs.isEmpty()) diff --git a/examples/MusPlay-Qt/MainWindow/setup_midi.cpp b/examples/MusPlay-Qt/MainWindow/setup_midi.cpp index bcd5265..b85d988 100644 --- a/examples/MusPlay-Qt/MainWindow/setup_midi.cpp +++ b/examples/MusPlay-Qt/MainWindow/setup_midi.cpp @@ -741,7 +741,8 @@ void SetupMidi::on_midiRawArgs_editingFinished() (PGE_MusicPlayer::type == MUS_MID || PGE_MusicPlayer::type == MUS_ADLMIDI || PGE_MusicPlayer::type == MUS_GME || - PGE_MusicPlayer::type == MUS_PXTONE)) + PGE_MusicPlayer::type == MUS_PXTONE || + PGE_MusicPlayer::type == MUS_OGG)) { emit songRestartNeeded(); } diff --git a/src/codecs/music_ogg_stb.c b/src/codecs/music_ogg_stb.c index 11cf9c3..e521761 100644 --- a/src/codecs/music_ogg_stb.c +++ b/src/codecs/music_ogg_stb.c @@ -35,7 +35,7 @@ #define STB_VORBIS_NO_STDIO 1 #define STB_VORBIS_NO_CRT 1 #define STB_VORBIS_NO_PUSHDATA_API 1 -#define STB_VORBIS_MAX_CHANNELS 8 /* For 7.1 surround sound */ +#define STB_VORBIS_MAX_CHANNELS 32 /* For 7.1 surround sound */ #define STB_FORCEINLINE SDL_FORCE_INLINE #if SDL_BYTEORDER == SDL_BIG_ENDIAN #define STB_VORBIS_BIG_ENDIAN 1 @@ -106,6 +106,26 @@ #include "stb_vorbis/stb_vorbis.h" +/* Global flags which are applying on initializing of Vorbis player with a file */ +typedef struct { + int multitrack; + int channels_per_track; + int total_tracks; + double speed; +} OGGVorbis_Setup; + +static OGGVorbis_Setup oggvorbis_setup = { + 0, 0, 0, 1.0 +}; + +static void OGGVorbis_SetDefault(OGGVorbis_Setup *setup) +{ + setup->multitrack = 0; + setup->channels_per_track = 0; + setup->total_tracks = 0; + setup->speed = 1.0; +} + typedef struct { SDL_RWops *src; int freesrc; @@ -124,6 +144,14 @@ typedef struct { Sint64 full_length; int computed_src_rate; double speed; + + SDL_bool multitrack; + float *multitrack_buffer[STB_VORBIS_MAX_CHANNELS]; + int multitrack_mute[STB_VORBIS_MAX_CHANNELS]; + int multitrack_buffer_samples; + int multitrack_channels; + int multitrack_tracks; + Mix_MusicMetaTags tags; } OGG_music; @@ -181,7 +209,7 @@ static int OGG_UpdateSpeed(OGG_music *music) music->computed_src_rate = 1000; } - music->stream = SDL_NewAudioStream(AUDIO_F32SYS, (Uint8)music->vi.channels, music->computed_src_rate, + music->stream = SDL_NewAudioStream(AUDIO_F32SYS, (Uint8)(music->multitrack ? music->multitrack_channels : music->vi.channels), music->computed_src_rate, music_spec.format, music_spec.channels, music_spec.freq); if (!music->stream) { return -2; @@ -193,6 +221,8 @@ static int OGG_UpdateSpeed(OGG_music *music) static int OGG_UpdateSection(OGG_music *music) { stb_vorbis_info vi; + Uint8 in_channels; + int i; vi = stb_vorbis_get_info(music->vf); @@ -211,12 +241,25 @@ static int OGG_UpdateSection(OGG_music *music) music->buffer = NULL; } + for (i = 0; i < STB_VORBIS_MAX_CHANNELS; ++i) { + if (music->multitrack_buffer[i]) { + SDL_free(music->multitrack_buffer[i]); + music->multitrack_buffer[i] = NULL; + } + } + if (music->stream) { SDL_FreeAudioStream(music->stream); music->stream = NULL; } - music->stream = SDL_NewAudioStream(AUDIO_F32SYS, (Uint8)vi.channels, music->computed_src_rate, + if (music->multitrack) { + in_channels = (Uint8)music->multitrack_channels; + } else { + in_channels = (Uint8)vi.channels; + } + + music->stream = SDL_NewAudioStream(AUDIO_F32SYS, in_channels, music->computed_src_rate, music_spec.format, music_spec.channels, music_spec.freq); if (!music->stream) { return -1; @@ -231,17 +274,99 @@ static int OGG_UpdateSection(OGG_music *music) if (!music->buffer) { return -1; } + + if (music->multitrack) { + if (music->multitrack_channels * music->multitrack_tracks > music->vi.channels) { + Mix_SetError("Invalid multitrack setup: product of channels and tracks must not be bigger than actual channels number at this file."); + return -1; + } + + music->multitrack_buffer_samples = music_spec.samples; + for (i = 0; i < music->multitrack_channels * music->multitrack_tracks; ++i) { + music->multitrack_buffer[i] = (float *)SDL_malloc((size_t)music->multitrack_buffer_samples * sizeof(float)); + if (!music->multitrack_buffer[i]) { + return -1; + } + } + } + return 0; } +static void process_args(const char *args, OGGVorbis_Setup *setup) +{ +#define ARG_BUFFER_SIZE 1024 + char arg[ARG_BUFFER_SIZE]; + char type = '-'; + size_t maxlen = 0; + size_t i, j = 0; + int value_opened = 0; + if (args == NULL) { + return; + } + maxlen = SDL_strlen(args); + if (maxlen == 0) { + return; + } + + maxlen += 1; + OGGVorbis_SetDefault(setup); + + for (i = 0; i < maxlen; i++) { + char c = args[i]; + if (value_opened == 1) { + if ((c == ';') || (c == '\0')) { + int value = 0; + arg[j] = '\0'; + value = SDL_atoi(arg); + switch(type) + { + case 'm': + setup->multitrack = value; + break; + case 'c': + setup->channels_per_track = value; + break; + case 'r': + setup->total_tracks = value; + break; + case 's': + if (arg[0] == '=') { + setup->speed = SDL_strtod(arg + 1, NULL); + if (setup->speed < 0.0) { + setup->speed = 1.0; + } + } + break; + case '\0': + break; + default: + break; + } + value_opened = 0; + } + arg[j++] = c; + } else { + if (c == '\0') { + return; + } + type = c; + value_opened = 1; + j = 0; + } + } +#undef ARG_BUFFER_SIZE +} + /* Load an OGG stream from an SDL_RWops object */ -static void *OGG_CreateFromRW(SDL_RWops *src, int freesrc) +static void *OGG_CreateFromRWex(SDL_RWops *src, int freesrc, const char *args) { OGG_music *music; stb_vorbis_comment vc; long rate; SDL_bool is_loop_length = SDL_FALSE; int i, error; + OGGVorbis_Setup setup = oggvorbis_setup; music = (OGG_music *)SDL_calloc(1, sizeof *music); if (!music) { @@ -250,10 +375,13 @@ static void *OGG_CreateFromRW(SDL_RWops *src, int freesrc) } music->src = src; music->volume = MIX_MAX_VOLUME; - music->speed = 1.0; music->computed_src_rate = -1; music->section = -1; + process_args(args, &setup); + + music->speed = setup.speed; + music->vf = stb_vorbis_open_rwops(src, 0, &error, NULL); if (music->vf == NULL) { @@ -262,6 +390,12 @@ static void *OGG_CreateFromRW(SDL_RWops *src, int freesrc) return NULL; } + if (setup.multitrack > 0) { + music->multitrack = SDL_TRUE; + music->multitrack_channels = setup.channels_per_track; + music->multitrack_tracks = setup.total_tracks; + } + if (OGG_UpdateSection(music) < 0) { OGG_Delete(music); return NULL; @@ -339,6 +473,11 @@ static void *OGG_CreateFromRW(SDL_RWops *src, int freesrc) return music; } +static void *OGG_CreateFromRW(struct SDL_RWops *src, int freesrc) +{ + return OGG_CreateFromRWex(src, freesrc, NULL); +} + static const char* OGG_GetMetaTag(void *context, Mix_MusicMetaTag tag_type) { OGG_music *music = (OGG_music *)context; @@ -378,9 +517,11 @@ static int OGG_GetSome(void *context, void *data, int bytes, SDL_bool *done) { OGG_music *music = (OGG_music *)context; SDL_bool looped = SDL_FALSE; - int filled, amount, result; + int filled, amount, channels, result, i, j, k; int section; Sint64 pcmPos; + float *cur; + float *cur_src[STB_VORBIS_MAX_CHANNELS]; filled = SDL_AudioStreamGet(music->stream, data, bytes); if (filled != 0) { @@ -394,12 +535,40 @@ static int OGG_GetSome(void *context, void *data, int bytes, SDL_bool *done) } section = music->section; - amount = stb_vorbis_get_samples_float_interleaved(music->vf, - music->vi.channels, - (float *)music->buffer, - music_spec.samples * music->vi.channels); - amount *= music->vi.channels * sizeof(float); + if (music->multitrack) { + channels = music->multitrack_channels; + amount = stb_vorbis_get_samples_float(music->vf, + music->multitrack_channels * music->multitrack_tracks, + music->multitrack_buffer, + music_spec.samples); + + cur = (float *)music->buffer; + SDL_memcpy(cur_src, music->multitrack_buffer, sizeof(float *) * STB_VORBIS_MAX_CHANNELS); + SDL_memset(music->buffer, 0, music->buffer_size); + + for (i = 0; i < music_spec.samples; ++i) { + for (j = 0; j < music->multitrack_tracks; ++j) { + if (music->multitrack_mute[j]) { + continue; + } + + for (k = 0; k < music->multitrack_channels; ++k) { + cur[k] += *(cur_src[(j * music->multitrack_channels) + k]++); + } + } + + cur += music->multitrack_channels; + } + } else { + channels = music->vi.channels; + amount = stb_vorbis_get_samples_float_interleaved(music->vf, + music->vi.channels, + (float *)music->buffer, + music_spec.samples * music->vi.channels); + } + + amount *= channels * sizeof(float); if (section != music->section) { music->section = section; @@ -419,7 +588,7 @@ static int OGG_GetSome(void *context, void *data, int bytes, SDL_bool *done) pcmPos = stb_vorbis_get_playback_sample_offset(music->vf); if (music->loop && (music->play_count != 1) && (pcmPos >= music->loop_end)) { - amount -= (int)((pcmPos - music->loop_end) * music->vi.channels) * (int)sizeof(float); + amount -= (int)((pcmPos - music->loop_end) * channels) * (int)sizeof(float); result = stb_vorbis_seek(music->vf, music->loop_start); if (!result) { set_ov_error("stb_vorbis_seek", stb_vorbis_get_error(music->vf)); @@ -532,10 +701,29 @@ static double OGG_LoopLength(void *music_p) return -1.0; } +static int OGG_GetTracksCount(void *music_p) +{ + OGG_music *music = (OGG_music *)music_p; + if (music->multitrack) { + return music->multitrack_tracks; + } + return -1; +} + +static int OGG_SetTrackMute(void *music_p, int track, int mute) +{ + OGG_music *music = (OGG_music *)music_p; + if (music->multitrack && track >= 0 && track < music->multitrack_tracks) { + music->multitrack_mute[track] = mute; + return 0; + } + return -1; +} /* Close the given OGG stream */ static void OGG_Delete(void *context) { + int i; OGG_music *music = (OGG_music *)context; meta_tags_clear(&music->tags); stb_vorbis_close(music->vf); @@ -545,6 +733,12 @@ static void OGG_Delete(void *context) if (music->buffer) { SDL_free(music->buffer); } + for (i = 0; i < STB_VORBIS_MAX_CHANNELS; ++i) { + if (music->multitrack_buffer[i]) { + SDL_free(music->multitrack_buffer[i]); + music->multitrack_buffer[i] = NULL; + } + } if (music->freesrc) { SDL_RWclose(music->src); } @@ -562,7 +756,7 @@ Mix_MusicInterface Mix_MusicInterface_OGG = NULL, /* Load */ NULL, /* Open */ OGG_CreateFromRW, - NULL, /* CreateFromRWex [MIXER-X]*/ + OGG_CreateFromRWex, /* [MIXER-X] */ NULL, /* CreateFromFile */ NULL, /* CreateFromFileEx [MIXER-X]*/ OGG_SetVolume, @@ -580,8 +774,8 @@ Mix_MusicInterface Mix_MusicInterface_OGG = OGG_GetSpeed, NULL, /* SetPitch [MIXER-X] */ NULL, /* GetPitch [MIXER-X] */ - NULL, /* GetTracksCount [MIXER-X] */ - NULL, /* SetTrackMute [MIXER-X] */ + OGG_GetTracksCount, /* [MIXER-X] */ + OGG_SetTrackMute, /* [MIXER-X] */ OGG_LoopStart, OGG_LoopEnd, OGG_LoopLength,