Merge pull request #5244 from rtissera/modsupport

Add MOD/S3M/XM support to audio mixer (libretro-common) through micromod/ibxm
This commit is contained in:
Twinaphex 2017-08-04 20:36:55 +02:00 committed by GitHub
commit d11b9e80b4
20 changed files with 2335 additions and 1 deletions

View File

@ -1324,6 +1324,11 @@ ifeq ($(HAVE_RBMP), 1)
OBJ += $(LIBRETRO_COMM_DIR)/formats/bmp/rbmp.o
endif
ifeq ($(HAVE_IBXM), 1)
DEFINES += -DHAVE_IBXM
OBJ += $(DEPS_DIR)/ibxm/ibxm.o
endif
OBJ += $(LIBRETRO_COMM_DIR)/formats/bmp/rbmp_encode.o \
$(LIBRETRO_COMM_DIR)/formats/json/jsonsax.o \
$(LIBRETRO_COMM_DIR)/formats/json/jsonsax_full.o \

View File

@ -133,6 +133,7 @@ endif
HAVE_RJPEG := 1
HAVE_RBMP := 1
HAVE_RTGA := 1
HAVE_IBXM := 1
HAVE_OVERLAY := 1
HAVE_ZLIB := 1
WANT_ZLIB := 1

View File

@ -914,6 +914,11 @@ bool audio_driver_mixer_extension_supported(const char *ext)
#ifdef HAVE_STB_VORBIS
string_list_append(str_list, "ogg", attr);
#endif
#ifdef HAVE_IBXM
string_list_append(str_list, "mod", attr);
string_list_append(str_list, "s3m", attr);
string_list_append(str_list, "xm", attr);
#endif
string_list_append(str_list, "wav", attr);
@ -1008,6 +1013,9 @@ bool audio_driver_mixer_add_stream(audio_mixer_stream_params_t *params)
case AUDIO_MIXER_TYPE_OGG:
handle = audio_mixer_load_ogg(buf, (int32_t)params->bufsize);
break;
case AUDIO_MIXER_TYPE_MOD:
handle = audio_mixer_load_mod(buf, (int32_t)params->bufsize);
break;
case AUDIO_MIXER_TYPE_NONE:
free(buf);
return false;

21
deps/ibxm/README vendored Normal file
View File

@ -0,0 +1,21 @@
Micromod (c)2017 mumart@gmail.com
A good-quality player library for the ProTracker MOD music format
for Javascript (HTML5 Web Audio), Java, ANSI C (SDL) and Pascal (SDL).
Also hosted here is IBXM, a player library for the ProTracker MOD,
Scream Tracker 3 S3M, and FastTracker 2 XM music formats for Javascript
(HTML5 Web Audio), Java and ANSI C.
The Java version of Micromod contains a powerful command-line tool for
the creation of MOD files from textual MT files and WAV samples.
There is some basic documentation built-in to the tool and some example
MT files are contained in the songs directory. Some knowledge of the
ProTracker MOD format, audio synthesis and "tracking" is assumed.
If you have any questions or feedback please feel free to
contact me at the above email address!
Cheers,
Martin

1922
deps/ibxm/ibxm.c vendored Normal file

File diff suppressed because it is too large Load Diff

71
deps/ibxm/ibxm.h vendored Normal file
View File

@ -0,0 +1,71 @@
/* ibxm/ac mod/xm/s3m replay (c)mumart@gmail.com */
#ifndef __IBXM_H__
#define __IBXM_H__
const char *IBXM_VERSION;
struct data {
char *buffer;
int length;
};
struct sample {
char name[ 32 ];
int loop_start, loop_length;
short volume, panning, rel_note, fine_tune, *data;
};
struct envelope {
char enabled, sustain, looped, num_points;
short sustain_tick, loop_start_tick, loop_end_tick;
short points_tick[ 16 ], points_ampl[ 16 ];
};
struct instrument {
int num_samples, vol_fadeout;
char name[ 32 ], key_to_sample[ 97 ];
char vib_type, vib_sweep, vib_depth, vib_rate;
struct envelope vol_env, pan_env;
struct sample *samples;
};
struct pattern {
int num_channels, num_rows;
char *data;
};
struct module {
char name[ 32 ];
int num_channels, num_instruments;
int num_patterns, sequence_len, restart_pos;
int default_gvol, default_speed, default_tempo, c2_rate, gain;
int linear_periods, fast_vol_slides;
unsigned char *default_panning, *sequence;
struct pattern *patterns;
struct instrument *instruments;
};
/* Allocate and initialize a module from the specified data, returns NULL on error.
Message should point to a 64-character buffer to receive error messages. */
struct module* module_load( struct data *data, char *message );
/* Deallocate the specified module. */
void dispose_module( struct module *module );
/* Allocate and initialize a replay with the specified module and sampling rate. */
struct replay* new_replay( struct module *module, int sample_rate, int interpolation );
/* Deallocate the specified replay. */
void dispose_replay( struct replay *replay );
/* Returns the song duration in samples at the current sampling rate. */
int replay_calculate_duration( struct replay *replay );
/* Seek to approximately the specified sample position.
The actual sample position reached is returned. */
int replay_seek( struct replay *replay, int sample_pos );
/* Set the pattern in the sequence to play. The tempo is reset to the default. */
void replay_set_sequence_pos( struct replay *replay, int pos );
/* Generates audio and returns the number of stereo samples written into mix_buf. */
int replay_get_audio( struct replay *replay, int *mix_buf );
/* Returns the length of the output buffer required by replay_get_audio(). */
int calculate_mix_buf_len( int sample_rate );
#endif

37
deps/ibxm/licence.txt vendored Normal file
View File

@ -0,0 +1,37 @@
---
Copyright (c) 2015, Martin Cameron
All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the organization nor the names of
its contributors may be used to endorse or promote
products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
---

View File

@ -82,6 +82,9 @@ enum file_path_enum
FILE_PATH_7Z_EXTENSION,
FILE_PATH_OGG_EXTENSION,
FILE_PATH_WAV_EXTENSION,
FILE_PATH_MOD_EXTENSION,
FILE_PATH_S3M_EXTENSION,
FILE_PATH_XM_EXTENSION,
FILE_PATH_CONFIG_EXTENSION,
FILE_PATH_CORE_INFO_EXTENSION
};

View File

@ -107,6 +107,15 @@ const char *file_path_str(enum file_path_enum enum_idx)
case FILE_PATH_WAV_EXTENSION:
str = ".wav";
break;
case FILE_PATH_MOD_EXTENSION:
str = ".mod";
break;
case FILE_PATH_S3M_EXTENSION:
str = ".s3m";
break;
case FILE_PATH_XM_EXTENSION:
str = ".xm";
break;
case FILE_PATH_JPEG_EXTENSION:
str = ".jpeg";
break;

View File

@ -14,6 +14,8 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#define HAVE_IBXM 1
#if defined(HAVE_CG) || defined(HAVE_HLSL) || defined(HAVE_GLSL)
#define HAVE_SHADERS
#endif
@ -350,6 +352,8 @@ VIDEO DRIVER
#include "../gfx/drivers/gdi_gfx.c"
#endif
#include "../deps/ibxm/ibxm.c"
/*============================================================
FONTS
============================================================ */

View File

@ -46,6 +46,10 @@
#include "../../deps/stb/stb_vorbis.h"
#endif
#ifdef HAVE_IBXM
#include "../../deps/ibxm/ibxm.h"
#endif
#define AUDIO_MIXER_MAX_VOICES 8
#define AUDIO_MIXER_TEMP_OGG_BUFFER 8192
@ -70,6 +74,15 @@ struct audio_mixer_sound
const void* data;
} ogg;
#endif
#ifdef HAVE_IBXM
struct
{
/* mod/s3m/xm */
unsigned size;
const void* data;
} mod;
#endif
} types;
};
@ -103,6 +116,18 @@ struct audio_mixer_voice
const retro_resampler_t *resampler;
} ogg;
#endif
#ifdef HAVE_IBXM
struct
{
/* mod/s3m/xm */
unsigned position;
unsigned samples;
unsigned buf_samples;
int* buffer;
struct replay* stream;
} mod;
#endif
} types;
};
@ -319,6 +344,24 @@ audio_mixer_sound_t* audio_mixer_load_ogg(void *buffer, int32_t size)
#endif
}
audio_mixer_sound_t* audio_mixer_load_mod(void *buffer, int32_t size)
{
#ifdef HAVE_IBXM
audio_mixer_sound_t* sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound));
if (!sound)
return NULL;
sound->type = AUDIO_MIXER_TYPE_MOD;
sound->types.mod.size = size;
sound->types.mod.data = buffer;
return sound;
#else
return NULL;
#endif
}
void audio_mixer_destroy(audio_mixer_sound_t* sound)
{
void *handle = NULL;
@ -337,6 +380,13 @@ void audio_mixer_destroy(audio_mixer_sound_t* sound)
handle = (void*)sound->types.ogg.data;
if (handle)
free(handle);
#endif
break;
case AUDIO_MIXER_TYPE_MOD:
#ifdef HAVE_IBXM
handle = (void*)sound->types.mod.data;
if (handle)
free(handle);
#endif
break;
case AUDIO_MIXER_TYPE_NONE:
@ -413,6 +463,65 @@ error:
}
#endif
#ifdef HAVE_IBXM
static bool audio_mixer_play_mod(
audio_mixer_sound_t* sound,
audio_mixer_voice_t* voice,
bool repeat, float volume,
audio_mixer_stop_cb_t stop_cb)
{
int buf_samples = 0;
int samples = 0;
void *mod_buffer = NULL;
struct module* module = NULL;
struct replay* replay = NULL;
struct data data;
char message[64];
data.buffer = (char*)sound->types.ogg.data;
data.length = sound->types.ogg.size;
module = module_load(&data, message);
if (module==NULL) {
printf("audio_mixer_play_mod module_load() failed with error: %s\n", message);
goto error;
}
replay = new_replay( module, s_rate, 1);
if (replay==NULL) {
printf("audio_mixer_play_mod new_replay() failed\n");
goto error;
}
buf_samples = calculate_mix_buf_len(s_rate);
mod_buffer = memalign_alloc(16, ((buf_samples + 15) & ~15) * sizeof(int));
if (!mod_buffer) {
printf("audio_mixer_play_mod cannot allocate mod_buffer !\n");
goto error;
}
samples = replay_calculate_duration(replay);
if (!samples) {
printf("audio_mixer_play_mod cannot retrieve duration !\n");
goto error;
}
voice->types.mod.buffer = mod_buffer;
voice->types.mod.buf_samples = buf_samples;
voice->types.mod.stream = replay;
voice->types.mod.position = 0;
voice->types.mod.samples = 0;//samples;
return true;
error:
if (mod_buffer)
memalign_free(mod_buffer);
dispose_module(module);
return false;
}
#endif
audio_mixer_voice_t* audio_mixer_play(audio_mixer_sound_t* sound, bool repeat,
float volume, audio_mixer_stop_cb_t stop_cb)
{
@ -440,6 +549,11 @@ audio_mixer_voice_t* audio_mixer_play(audio_mixer_sound_t* sound, bool repeat,
case AUDIO_MIXER_TYPE_OGG:
#ifdef HAVE_STB_VORBIS
res = audio_mixer_play_ogg(sound, voice, repeat, volume, stop_cb);
#endif
break;
case AUDIO_MIXER_TYPE_MOD:
#ifdef HAVE_IBXM
res = audio_mixer_play_mod(sound, voice, repeat, volume, stop_cb);
#endif
break;
case AUDIO_MIXER_TYPE_NONE:
@ -608,6 +722,79 @@ again:
}
#endif
#ifdef HAVE_IBXM
static void audio_mixer_mix_mod(float* buffer, size_t num_frames,
audio_mixer_voice_t* voice,
float volume)
{
int i;
unsigned temp_samples = 0;
unsigned buf_free = num_frames * 2;
int* pcm = NULL;
if (voice->types.mod.position == voice->types.mod.samples)
{
again:
temp_samples = replay_get_audio( voice->types.mod.stream, voice->types.mod.buffer );
temp_samples *= 2; // stereo
if (temp_samples == 0)
{
if (voice->repeat)
{
if (voice->stop_cb)
voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_REPEATED);
replay_seek( voice->types.mod.stream, 0);
goto again;
}
else
{
if (voice->stop_cb)
voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED);
voice->type = AUDIO_MIXER_TYPE_NONE;
return;
}
}
voice->types.mod.position = 0;
voice->types.mod.samples = temp_samples;
}
pcm = voice->types.mod.buffer + voice->types.mod.position;
float samplef = 0.0f;
int samplei = 0;
if (voice->types.mod.samples < buf_free)
{
for (i = voice->types.mod.samples; i != 0; i--)
{
samplei = *pcm++ * volume;
samplef = (float)((int)samplei + 32768) / 65535.0f;
samplef = samplef * 2.0f - 1.0f;
*buffer++ = samplef;
}
buf_free -= voice->types.mod.samples;
goto again;
}
else
{
int i;
for (i = buf_free; i != 0; --i )
{
samplei = *pcm++ * volume;
samplef = (float)((int)samplei + 32768) / 65535.0f;
samplef = samplef * 2.0f - 1.0f;
*buffer++ = samplef;
}
voice->types.mod.position += buf_free;
voice->types.mod.samples -= buf_free;
}
}
#endif
void audio_mixer_mix(float* buffer, size_t num_frames, float volume_override, bool override)
{
unsigned i;
@ -631,6 +818,10 @@ void audio_mixer_mix(float* buffer, size_t num_frames, float volume_override, bo
case AUDIO_MIXER_TYPE_OGG:
#ifdef HAVE_STB_VORBIS
audio_mixer_mix_ogg(buffer, num_frames, voice, volume);
#endif
case AUDIO_MIXER_TYPE_MOD:
#ifdef HAVE_IBXM
audio_mixer_mix_mod(buffer, num_frames, voice, volume);
#endif
break;
case AUDIO_MIXER_TYPE_NONE:

View File

@ -40,7 +40,8 @@ enum audio_mixer_type
{
AUDIO_MIXER_TYPE_NONE = 0,
AUDIO_MIXER_TYPE_WAV,
AUDIO_MIXER_TYPE_OGG
AUDIO_MIXER_TYPE_OGG,
AUDIO_MIXER_TYPE_MOD
};
typedef struct audio_mixer_sound audio_mixer_sound_t;
@ -59,6 +60,7 @@ void audio_mixer_done(void);
audio_mixer_sound_t* audio_mixer_load_wav(void *buffer, int32_t size);
audio_mixer_sound_t* audio_mixer_load_ogg(void *buffer, int32_t size);
audio_mixer_sound_t* audio_mixer_load_mod(void *buffer, int32_t size);
void audio_mixer_destroy(audio_mixer_sound_t* sound);

View File

@ -6436,6 +6436,11 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data)
#endif
#ifdef HAVE_RTGA
string_list_append(str_list, "tga", attr);
#endif
#ifdef HAVE_IBXM
string_list_append(str_list, "mod", attr);
string_list_append(str_list, "s3m", attr);
string_list_append(str_list, "xm", attr);
#endif
string_list_join_concat(info->exts, sizeof(info->exts), str_list, "|");
string_list_free(str_list);

View File

@ -169,6 +169,9 @@ uint32_t msg_hash_calculate(const char *s)
#define MENU_VALUE_FILE_MP3 0x0b889135U
#define MENU_VALUE_FILE_FLAC 0x7c96d67bU
#define MENU_VALUE_FILE_OGG 0x0b8898c2U
#define MENU_VALUE_FILE_MOD 0x0b889145U
#define MENU_VALUE_FILE_S3M 0x0b88a318U
#define MENU_VALUE_FILE_XM 0x00597a2aU
#define MENU_VALUE_FILE_FLV 0x0b88732dU
#define MENU_VALUE_FILE_WAV 0x0b88ba13U
#define MENU_VALUE_FILE_MOV 0x0b889157U
@ -347,6 +350,14 @@ enum msg_file_type msg_hash_to_file_type(uint32_t hash)
case MENU_VALUE_FILE_WMA:
return FILE_TYPE_WMA;
#endif
#ifdef HAVE_IBXM
case MENU_VALUE_FILE_MOD:
return FILE_TYPE_MOD;
case MENU_VALUE_FILE_S3M:
return FILE_TYPE_S3M;
case MENU_VALUE_FILE_XM:
return FILE_TYPE_XM;
#endif
#ifdef HAVE_IMAGEVIEWER
case MENU_VALUE_FILE_JPG:
case MENU_VALUE_FILE_JPG_CAPS:

View File

@ -127,6 +127,10 @@ enum msg_file_type
FILE_TYPE_TGA,
FILE_TYPE_BMP,
FILE_TYPE_MOD,
FILE_TYPE_S3M,
FILE_TYPE_XM,
FILE_TYPE_CUE,
FILE_TYPE_ISO,
FILE_TYPE_LUTRO,

View File

@ -744,6 +744,13 @@ enum rarch_content_type path_is_media_type(const char *path)
case FILE_TYPE_BMP:
return RARCH_CONTENT_IMAGE;
#endif
#ifdef HAVE_IBXM
case FILE_TYPE_MOD:
case FILE_TYPE_S3M:
case FILE_TYPE_XM:
return RARCH_CONTENT_MUSIC;
#endif
case FILE_TYPE_NONE:
default:
break;

View File

@ -71,6 +71,7 @@ HAVE_FREETYPE=auto # FreeType support
HAVE_STB_FONT=yes # stb_truetype font support
HAVE_STB_IMAGE=yes # stb image loading support
HAVE_STB_VORBIS=yes # stb vorbis support
HAVE_IBXM=yes # IBXM support
HAVE_XVIDEO=auto # XVideo support
HAVE_PYTHON=no # Python 3 support for shaders
C89_PYTHON=no

View File

@ -99,6 +99,28 @@ static void task_audio_mixer_handle_upload_ogg(void *task_data,
free(user_data);
}
static void task_audio_mixer_handle_upload_mod(void *task_data,
void *user_data, const char *err)
{
audio_mixer_stream_params_t params;
nbio_buf_t *img = (nbio_buf_t*)task_data;
if (!img)
return;
params.volume = 1.0f;
params.type = AUDIO_MIXER_TYPE_MOD;
params.state = AUDIO_STREAM_STATE_PLAYING;
params.buf = img->buf;
params.bufsize = img->bufsize;
params.cb = NULL;
audio_driver_mixer_add_stream(&params);
free(img);
free(user_data);
}
static void task_audio_mixer_handle_upload_wav(void *task_data,
void *user_data, const char *err)
{
@ -191,6 +213,14 @@ bool task_push_audio_mixer_load(const char *fullpath, retro_task_callback_t cb,
nbio->type = NBIO_TYPE_OGG;
t->callback = task_audio_mixer_handle_upload_ogg;
}
else if ( strstr(fullpath, file_path_str(FILE_PATH_MOD_EXTENSION)) ||
strstr(fullpath, file_path_str(FILE_PATH_S3M_EXTENSION)) ||
strstr(fullpath, file_path_str(FILE_PATH_XM_EXTENSION)))
{
image->type = AUDIO_MIXER_TYPE_MOD;
nbio->type = NBIO_TYPE_MOD;
t->callback = task_audio_mixer_handle_upload_mod;
}
nbio->data = (struct audio_mixer_handle*)image;
nbio->is_finished = false;

View File

@ -104,6 +104,7 @@ void task_file_load_handler(retro_task_t *task)
task_set_finished(task, true);
break;
case NBIO_TYPE_OGG:
case NBIO_TYPE_MOD:
case NBIO_TYPE_WAV:
if (!task_audio_mixer_load_handler(task))
task_set_finished(task, true);

View File

@ -74,6 +74,7 @@ enum nbio_type
NBIO_TYPE_TGA,
NBIO_TYPE_BMP,
NBIO_TYPE_OGG,
NBIO_TYPE_MOD,
NBIO_TYPE_WAV
};