📄 music.c
字号:
/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2004 Sam Lantinga This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Sam Lantinga slouken@libsdl.org*//* $Id: music.c 2408 2006-05-12 09:42:24Z slouken $ */#include <stdlib.h>#include <string.h>#include <ctype.h>#include <assert.h>#include "SDL_endian.h"#include "SDL_audio.h"#include "SDL_timer.h"#include "SDL_mixer.h"#define SDL_SURROUND#ifdef SDL_SURROUND#define MAX_OUTPUT_CHANNELS 6#else#define MAX_OUTPUT_CHANNELS 2#endif/* The music command hack is UNIX specific */#ifndef unix#undef CMD_MUSIC#endif#ifdef CMD_MUSIC#include "music_cmd.h"#endif#ifdef WAV_MUSIC#include "wavestream.h"#endif#if defined(MOD_MUSIC) || defined(LIBMIKMOD_MUSIC)# include "mikmod.h"# if defined(LIBMIKMOD_VERSION) /* libmikmod 3.1.8 */# define UNIMOD MODULE# define MikMod_Init() MikMod_Init(NULL)# define MikMod_LoadSong(a,b) Player_Load(a,b,0)# ifndef LIBMIKMOD_MUSIC# define MikMod_LoadSongRW(a,b) Player_LoadRW(a,b,0)# endif# define MikMod_FreeSong Player_Free extern int MikMod_errno;# else /* old MikMod 3.0.3 */# define MikMod_strerror(x) _mm_errmsg[x])# define MikMod_errno _mm_errno# endif#endif#ifdef MID_MUSIC# ifdef USE_TIMIDITY_MIDI# include "timidity.h"# endif# ifdef USE_NATIVE_MIDI# include "native_midi.h"# endif# if defined(USE_TIMIDITY_MIDI) && defined(USE_NATIVE_MIDI)# define MIDI_ELSE else# else# define MIDI_ELSE# endif#endif#ifdef OGG_MUSIC#include "music_ogg.h"#endif#ifdef MP3_MUSIC#include "dynamic_mp3.h"static SDL_AudioSpec used_mixer;#endifint volatile music_active = 1;static int volatile music_stopped = 0;static int music_loops = 0;static char *music_cmd = NULL;static Mix_Music * volatile music_playing = NULL;static int music_volume = MIX_MAX_VOLUME;static int music_swap8;static int music_swap16;struct _Mix_Music { Mix_MusicType type; union {#ifdef CMD_MUSIC MusicCMD *cmd;#endif#ifdef WAV_MUSIC WAVStream *wave;#endif#if defined(MOD_MUSIC) || defined(LIBMIKMOD_MUSIC) UNIMOD *module;#endif#ifdef MID_MUSIC#ifdef USE_TIMIDITY_MIDI MidiSong *midi;#endif#ifdef USE_NATIVE_MIDI NativeMidiSong *nativemidi;#endif#endif#ifdef OGG_MUSIC OGG_music *ogg;#endif#ifdef MP3_MUSIC SMPEG *mp3;#endif } data; Mix_Fading fading; int fade_step; int fade_steps; int error;};#ifdef MID_MUSIC#ifdef USE_TIMIDITY_MIDIstatic int timidity_ok;static int samplesize;#endif#ifdef USE_NATIVE_MIDIstatic int native_midi_ok;#endif#endif/* Reference for converting mikmod output to 4/6 channels */static int current_output_channels;static Uint16 current_output_format;/* Used to calculate fading steps */static int ms_per_step;/* Local low-level functions prototypes */static void music_internal_initialize_volume(void);static void music_internal_volume(int volume);static int music_internal_play(Mix_Music *music, double position);static int music_internal_position(double position);static int music_internal_playing();static void music_internal_halt(void);/* Support for hooking when the music has finished */static void (*music_finished_hook)(void) = NULL;void Mix_HookMusicFinished(void (*music_finished)(void)){ SDL_LockAudio(); music_finished_hook = music_finished; SDL_UnlockAudio();}/* If music isn't playing, halt it if no looping is required, restart it *//* otherwhise. NOP if the music is playing */static int music_halt_or_loop (void){ /* Restart music if it has to loop */ if (!music_internal_playing()) { /* Restart music if it has to loop at a high level */ if (music_loops && --music_loops) { Mix_Fading current_fade = music_playing->fading; music_internal_play(music_playing, 0.0); music_playing->fading = current_fade; } else { music_internal_halt(); if (music_finished_hook) music_finished_hook(); return 0; } } return 1;}/* Mixing function */void music_mixer(void *udata, Uint8 *stream, int len){ if ( music_playing && music_active ) { /* Handle fading */ if ( music_playing->fading != MIX_NO_FADING ) { if ( music_playing->fade_step++ < music_playing->fade_steps ) { int volume; int fade_step = music_playing->fade_step; int fade_steps = music_playing->fade_steps; if ( music_playing->fading == MIX_FADING_OUT ) { volume = (music_volume * (fade_steps-fade_step)) / fade_steps; } else { /* Fading in */ volume = (music_volume * fade_step) / fade_steps; } music_internal_volume(volume); } else { if ( music_playing->fading == MIX_FADING_OUT ) { music_internal_halt(); if ( music_finished_hook ) { music_finished_hook(); } return; } music_playing->fading = MIX_NO_FADING; } } if (music_halt_or_loop() == 0) return; switch (music_playing->type) {#ifdef CMD_MUSIC case MUS_CMD: /* The playing is done externally */ break;#endif#ifdef WAV_MUSIC case MUS_WAV: WAVStream_PlaySome(stream, len); break;#endif#if defined(MOD_MUSIC) || defined(LIBMIKMOD_MUSIC) case MUS_MOD: if (current_output_channels > 2) { int small_len = 2 * len / current_output_channels; int i; Uint8 *src, *dst; VC_WriteBytes((SBYTE *)stream, small_len); /* and extend to len by copying channels */ src = stream + small_len; dst = stream + len; switch (current_output_format & 0xFF) { case 8: for ( i=small_len/2; i; --i ) { src -= 2; dst -= current_output_channels; dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[0]; dst[3] = src[1]; if (current_output_channels == 6) { dst[4] = src[0]; dst[5] = src[1]; } } break; case 16: for ( i=small_len/4; i; --i ) { src -= 4; dst -= 2 * current_output_channels; dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3]; dst[4] = src[0]; dst[5] = src[1]; dst[6] = src[2]; dst[7] = src[3]; if (current_output_channels == 6) { dst[8] = src[0]; dst[9] = src[1]; dst[10] = src[2]; dst[11] = src[3]; } } break; } } else VC_WriteBytes((SBYTE *)stream, len); if ( music_swap8 ) { Uint8 *dst; int i; dst = stream; for ( i=len; i; --i ) { *dst++ ^= 0x80; } } else if ( music_swap16 ) { Uint8 *dst, tmp; int i; dst = stream; for ( i=(len/2); i; --i ) { tmp = dst[0]; dst[0] = dst[1]; dst[1] = tmp; dst += 2; } } break;#endif#ifdef MID_MUSIC#ifdef USE_TIMIDITY_MIDI case MUS_MID: if ( timidity_ok ) { int samples = len / samplesize; Timidity_PlaySome(stream, samples); } break;#endif#endif#ifdef OGG_MUSIC case MUS_OGG: len = OGG_playAudio(music_playing->data.ogg, stream, len); if (len > 0 && music_halt_or_loop()) OGG_playAudio(music_playing->data.ogg, stream, len); break;#endif#ifdef MP3_MUSIC case MUS_MP3: smpeg.SMPEG_playAudio(music_playing->data.mp3, stream, len); break;#endif default: /* Unknown music type?? */ break; } }}/* Initialize the music players with a certain desired audio format */int open_music(SDL_AudioSpec *mixer){ int music_error;#ifdef LIBMIKMOD_MUSIC CHAR *list;#endif music_error = 0;#ifdef WAV_MUSIC if ( WAVStream_Init(mixer) < 0 ) { ++music_error; }#endif#if defined(MOD_MUSIC) || defined(LIBMIKMOD_MUSIC) /* Set the MikMod music format */ music_swap8 = 0; music_swap16 = 0; switch (mixer->format) { case AUDIO_U8: case AUDIO_S8: { if ( mixer->format == AUDIO_S8 ) { music_swap8 = 1; } md_mode = 0; } break; case AUDIO_S16LSB: case AUDIO_S16MSB: { /* See if we need to correct MikMod mixing */#if SDL_BYTEORDER == SDL_LIL_ENDIAN if ( mixer->format == AUDIO_S16MSB ) {#else if ( mixer->format == AUDIO_S16LSB ) {#endif music_swap16 = 1; } md_mode = DMODE_16BITS; } break; default: { Mix_SetError("Unknown hardware audio format"); ++music_error; } } current_output_channels = mixer->channels; current_output_format = mixer->format; if ( mixer->channels > 1 ) { if ( mixer->channels > MAX_OUTPUT_CHANNELS ) { Mix_SetError("Hardware uses more channels than mixer"); ++music_error; } md_mode |= DMODE_STEREO; } md_mixfreq = mixer->freq; md_device = 0; md_volume = 96; md_musicvolume = 128; md_sndfxvolume = 128; md_pansep = 128; md_reverb = 0; md_mode |= DMODE_HQMIXER|DMODE_SOFT_MUSIC|DMODE_SURROUND;#ifdef LIBMIKMOD_MUSIC list = MikMod_InfoDriver(); if ( list ) free(list); else#endif MikMod_RegisterDriver(&drv_nos);#ifdef LIBMIKMOD_MUSIC list = MikMod_InfoLoader(); if ( list ) free(list); else#endif MikMod_RegisterAllLoaders(); if ( MikMod_Init() ) { Mix_SetError("%s", MikMod_strerror(MikMod_errno)); ++music_error; }#endif#ifdef MID_MUSIC#ifdef USE_TIMIDITY_MIDI samplesize = mixer->size / mixer->samples; if ( Timidity_Init(mixer->freq, mixer->format, mixer->channels, mixer->samples) == 0 ) { timidity_ok = 1; } else { timidity_ok = 0; }#endif#ifdef USE_NATIVE_MIDI#ifdef USE_TIMIDITY_MIDI native_midi_ok = !timidity_ok; if ( native_midi_ok )#endif native_midi_ok = native_midi_detect();#endif#endif#ifdef OGG_MUSIC if ( OGG_init(mixer) < 0 ) { ++music_error; }#endif#ifdef MP3_MUSIC /* Keep a copy of the mixer */ used_mixer = *mixer;#endif music_playing = NULL; music_stopped = 0; if ( music_error ) { return(-1); } Mix_VolumeMusic(SDL_MIX_MAXVOLUME); /* Calculate the number of ms for each callback */ ms_per_step = (int) (((float)mixer->samples * 1000.0) / mixer->freq); return(0);}/* Portable case-insensitive string compare function */int MIX_string_equals(const char *str1, const char *str2){ while ( *str1 && *str2 ) { if ( toupper((unsigned char)*str1) != toupper((unsigned char)*str2) ) break; ++str1; ++str2; } return (!*str1 && !*str2);}/* Load a music file */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -