diff options
author | Franklin Wei <git@fwei.tk> | 2017-01-21 15:18:31 -0500 |
---|---|---|
committer | Franklin Wei <git@fwei.tk> | 2017-12-23 21:01:26 -0500 |
commit | a855d6202536ff28e5aae4f22a0f31d8f5b325d0 (patch) | |
tree | 8c75f224dd64ed360505afa8843d016b0d75000b /apps/plugins/sdl/SDL_mixer | |
parent | 01c6dcf6c7b9bb1ad2fa0450f99bacc5f3d3e04b (diff) | |
download | rockbox-a855d6202536ff28e5aae4f22a0f31d8f5b325d0.tar.gz rockbox-a855d6202536ff28e5aae4f22a0f31d8f5b325d0.zip |
Port of Duke Nukem 3D
This ports Fabien Sanglard's Chocolate Duke to run on a version of SDL
for Rockbox.
Change-Id: I8f2c4c78af19de10c1633ed7bb7a997b43256dd9
Diffstat (limited to 'apps/plugins/sdl/SDL_mixer')
81 files changed, 22772 insertions, 0 deletions
diff --git a/apps/plugins/sdl/SDL_mixer/CHANGES b/apps/plugins/sdl/SDL_mixer/CHANGES new file mode 100644 index 0000000000..9eb53f3733 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/CHANGES @@ -0,0 +1,348 @@ +1.2.12: +Sam Lantinga - Sat Jan 14 22:00:29 2012 -0500 + * Fixed seek offset with SMPEG (was relative, should be absolute) +Sam Lantinga - Fri Jan 13 03:04:27 EST 2012 + * Fixed memory crash loading Ogg Vorbis files on Windows +Sam Lantinga - Thu Jan 05 22:51:54 2012 -0500 + * Added an Xcode project for iOS +Nikos Chantziaras - 2012-01-02 17:37:36 PST + * Added Mix_LoadMUSType_RW() so you can tell SDL_mixer what type the music is +Sam Lantinga - Sun Jan 01 16:45:58 2012 -0500 + * Fixed looping native MIDI on Mac OS X and Windows +Sam Lantinga - Sun Jan 01 01:00:51 2012 -0500 + * Added /usr/local/share/timidity to the timidity data path +Sam Lantinga - Sat Dec 31 21:26:46 2011 -0500 + * Fixed timidity loading of some MIDI files +Sam Lantinga - Sat Dec 31 19:11:59 EST 2011 + * Fixed dropping audio in the FLAC audio decoding +Sam Lantinga - Sat Dec 31 18:32:05 EST 2011 + * Fixed memory leak in SDL_LoadMUS() +Sam Lantinga - Sat Dec 31 10:22:05 EST 2011 + * Removed GPL native MIDI code for new licensing +Sam Lantinga - Sat Dec 31 10:22:05 EST 2011 + * SDL_mixer is now under the zlib license +Manuel Montezelo - 2011-12-28 11:42:44 PST + * Fixed drums playing on MIDI channel 16 with timidity +Ryan C. Gordon - Wed Jun 15 03:41:31 2011 -0400 + * The music-finished hook can start a track immediately +James Le Cuirot - Mon Mar 21 16:54:11 PDT 2011 + * Added support for FluidSynth +Egor Suvorov - Tue Jan 18 11:06:47 PST 2011 + * Added support for native MIDI on Haiku +Sam Lantinga - Tue Jan 11 01:29:19 2011 -0800 + * Added Android.mk to build on the Android platform +Jon Atkins - Sat Nov 14 13:00:18 PST 2009 + * Added support for libmodplug (disabled by default) + +1.2.11: +Sam Lantinga - Sat Nov 14 12:38:01 PST 2009 + * Fixed initialization error and crashes if MikMod library isn't available +Sam Lantinga - Sat Nov 14 11:22:14 PST 2009 + * Fixed bug loading multiple music files + +1.2.10: +Sam Lantinga - Sun Nov 8 08:34:48 PST 2009 + * Added Mix_Init()/Mix_Quit() to prevent constantly loading and unloading DLLs +Mike Frysinger - 2009-11-05 09:11:43 PST + * Check for fork/vfork on any platform, don't just assume it on UNIX +Jon Atkins - Thu Nov 5 00:02:50 2009 UTC + * Fixed export of Mix_GetNumChunkDecoders() and Mix_GetNumMusicDecoders() +C.W. Betts - 2009-11-02 00:16:21 PST + * Use newer MIDI API on Mac OS X 10.5+ + +1.2.9: +Ryan Gordon - Sun Oct 18 11:42:31 PDT 2009 + * Updated native MIDI support on Mac OS X for 10.6 +Ryan Gordon - Sun Oct 11 05:29:55 2009 UTC + * Reset channel volumes after a fade out interrupts a fade in. +Ryan Gordon - Sun Oct 11 02:59:12 2009 UTC + * Fixed crash race condition with position audio functions +Ryan Gordon - Sat Oct 10 17:05:45 2009 UTC + * Fixed stereo panning in 8-bit mode +Sam Lantinga - Sat Oct 10 11:07:15 2009 UTC + * Added /usr/share/timidity to the default timidity.cfg locations +Sam Lantinga - Sat Oct 3 13:33:36 PDT 2009 + * MOD support uses libmikmod and is dynamically loaded by default + * A patched version of libmikmod is included in libmikmod-3.1.12.zip + * The libmikmod patches fix security issues CVE-2007-6720 and CVE-2009-0179. +Sam Lantinga - Sat Oct 3 02:49:41 PDT 2009 + * Added TIMIDITY_CFG environment variable to fully locate timidity.cfg +Sam Lantinga - Fri Oct 2 07:15:35 PDT 2009 + * Implemented seamless looping for music playback +Forrest Voight - 2009-06-13 20:31:38 PDT + * ID3 files are now recognized as MP3 format +Steven Noonan - 2008-05-13 13:31:36 PDT + * Fixed native MIDI crash on 64-bit Windows +Ryan Gordon - Fri Jun 5 16:07:08 2009 UTC + * Added decoder enumeration API: + Mix_GetNumChunkDecoders(), Mix_GetChunkDecoder(), + Mix_GetNumMusicDecoders(), Mix_GetMusicDecoder() +Austen Dicken - Tue Feb 26 23:28:27 PST 2008 + * Added support for FLAC audio both as chunks and streaming +Tilman Sauerbeck - Tue Feb 26 03:44:47 PST 2008 + * Added support for streaming WAV files with Mix_LoadMUS_RW() +Ryan Gordon - Mon Feb 4 17:10:08 UTC 2008 + * Fixed crash caused by not resetting position_channels + +1.2.8: +Sam Lantinga - Wed Jul 18 09:45:54 PDT 2007 + * Improved detection of Ogg Vorbis and Tremor libraries +Ryan Gordon - Sun Jul 15 12:03:54 EDT 2007 + * Fixed memory leaks in Effects API. +David Rose - Sat Jul 14 22:16:09 PDT 2007 + * Added support for MP3 playback with libmad (for GPL projects only!) +Sam Lantinga - Sat Jul 14 21:39:30 PDT 2007 + * Fixed the final loop of audio samples of a certain size +Sam Lantinga - Sat Jul 14 21:05:09 PDT 2007 + * Fixed opening Ogg Vorbis files using different C runtimes on Windows +Philippe Simons - Sat Jul 14 20:33:17 PDT 2007 + * Added support for Ogg Vorbis playback with Tremor (an integer decoder) +Sam Lantinga - Sat Jul 14 07:02:09 PDT 2007 + * Fixed memory corruption in timidity resampling code +Ryan Gordon - Tue Jul 3 10:44:29 2007 UTC + * Fixed building SDL_mixer with SDL 1.3 pre-release +Ryan Gordon - Tue Feb 13 08:11:54 2007 UTC + * Fixed compiling both timidity and native midi in the same build +Hans de Goede - Sun Aug 20 23:25:46 2006 UTC + * Added volume control to playmus +Jonathan Atkins - Thu Aug 10 15:06:40 2006 UTC + * Fixed linking with system libmikmod +David Ergo - Fri Jun 23 09:07:19 2006 UTC + * Corrected no-op conditions in SetDistance(), SetPanning() and SetPosition() + * Fixed copy/paste errors in channel amplitudes + +1.2.7: +Sam Lantinga - Fri May 12 00:04:32 PDT 2006 + * Added support for dynamically loading SMPEG library +Sam Lantinga - Thu May 11 22:22:43 PDT 2006 + * Added support for dynamically loading Ogg Vorbis library +Sam Lantinga - Sun Apr 30 09:01:44 PDT 2006 + * Removed automake dependency, to allow Universal binaries on Mac OS X + * Added gcc-fat.sh for generating Universal binaries on Mac OS X +Sam Lantinga - Sun Apr 30 01:48:40 PDT 2006 + * Updated libtool support to version 1.5.22 +Patrice Mandin - Sat Jul 16 16:43:24 UTC 2005 + * Use SDL_RWops also for native midi mac and win32 +Patrice Mandin - Sat Jul 9 14:40:09 UTC 2005 + * Use SDL_RWops also for native midi gpl (todo: mac and win32) +Ryan C. Gordon - Sat Jul 9 01:54:03 EDT 2005 + * Tweaked Mix_Chunk's definition to make predeclaration easier. +Patrice Mandin - Mon Jul 4 19:45:40 UTC 2005 + * Search timidity.cfg also in /etc + * Fix memory leaks in timidity player + * Use also SDL_RWops to read midifiles for timidity +Ryan C. Gordon - Mon Jun 13 18:18:12 EDT 2005 + * Patch from Eric Wing to fix native midi compiling on MacOS/x86. +Sam Lantinga - Wed Dec 22 17:14:32 PST 2004 + * Disabled support for the system version of libmikmod by default +Sam Lantinga - Tue Dec 21 09:51:29 PST 2004 + * Fixed building mikmod support on UNIX + * Always build SDL_RWops music support + * Added SDL_RWops support for reading MP3 files + +1.2.6: +Jonathan Atkins - Wed, 15 Sep 2004 23:26:42 -0500 + * Added support for using the system version of libmikmod +Martin_Storsjö - Sun, 22 Aug 2004 02:21:14 +0300 (EEST) + * Added SDL_RWops support for reading Ogg Vorbis files +Greg Lee - Wed, 14 Jul 2004 05:13:14 -1000 + * Added 4 and 6 channel surround sound output support + * Added support for RMID format MIDI files + * Improved timidity support (reverb, chorus, Roland & Yamaha sysex dumps, etc.) +Sam Lantinga - Wed Nov 19 00:23:44 PST 2003 + * Updated libtool support for new mingw32 DLL build process +Ryan C. Gordon - Sun Nov 9 23:34:47 EST 2003 + * Patch from Steven Fuller to fix positioning effect on bigendian systems. +Laurent Ganter - Mon, 6 Oct 2003 11:51:33 +0200 + * Fixed bug with MIDI volume in native Windows playback +Andre Leiradella - Fri, 30 May 2003 16:12:03 -0300 + * Added SDL_RWops support for reading MOD files +Kyle Davenport - Sat, 19 Apr 2003 17:13:31 -0500 + * Added .la files to the development RPM, fixing RPM build on RedHat 8 + +1.2.5: +Darrell Walisser - Tue Mar 4 09:24:01 PST 2003 + * Worked around MacOS X deadlock between CoreAudio and QuickTime +Darrell Walisser - Fri, 14 Feb 2003 20:56:08 -0500 + * Fixed crash in native midi code with files with more than 32 tracks +Marc Le Douarain - Sat, 15 Feb 2003 14:46:41 +0100 + * Added 8SVX format support to the AIFF loader +Sam Lantinga Wed Feb 12 21:03:57 PST 2003 + * Fixed volume control on WAVE music chunks +Ben Nason - Mon, 10 Feb 2003 11:50:27 -0800 + * Fixed volume control on MOD music chunks +Patrice Mandin - Fri, 31 Jan 2003 15:17:30 +0100 + * Added support for the Atari platform +Ryan C. Gordon - Fri Dec 27 10:14:07 EST 2002 + * Patch from Steven Fuller to fix panning effect with 8-bit sounds. +Ryan C. Gordon - Thu Jan 2 12:31:48 EST 2003 + * Patch from guy on 3DRealms forums to fix native win32 midi volume. +Ryan C. Gordon - Wed Oct 30 07:12:06 EST 2002 + * Small, looping music samples should now be able to fade out correctly. +Sam Lantinga - Sun Oct 20 20:52:24 PDT 2002 + * Added shared library support for MacOS X +Pete Shinners - Wed Oct 16 17:10:08 EDT 2002 + * Correctly report an error when using an unknown filetype +Vaclav Slavik - Sun Sep 8 18:57:38 PDT 2002 + * Added support for loading Ogg Vorbis samples as an audio chunk +Martin Storsjö - Tue Jul 16 10:38:12 PDT 2002 + * Fixed to start playing another sample immediately when one finishes +Martin Storsjö - Tue May 28 13:08:29 PDT 2002 + * Fixed a volume bug when calling Mix_HaltChannel() on unused channel +Xavier Wielemans - Wed Jun 12 14:28:14 EDT 2002 + * Fixed volume reset bug at end of channel fade. +Ryan C. Gordon - Wed Jun 26 16:30:59 EDT 2002 + * Mix_LoadMUS() will now accept an MP3 by file extension, instead of relying + entirely on the magic number. + +1.2.4: +Sam Lantinga - Mon May 20 09:11:22 PDT 2002 + * Updated the CodeWarrior project files +Sam Lantinga - Sun May 19 13:46:29 PDT 2002 + * Added a function to query the music format: Mix_GetMusicType() +Sam Lantinga - Sat May 18 12:45:16 PDT 2002 + * Added a function to load audio data from memory: Mix_QuickLoad_RAW() +Sam Lantinga - Thu May 16 11:26:46 PDT 2002 + * Cleaned up threading issues in the music playback code +Ryan Gordon - Thu May 2 21:08:48 PDT 2002 + * Fixed deadlock introduced in the last release + +1.2.3: +Sam Lantinga - Sat Apr 13 07:49:47 PDT 2002 + * Updated autogen.sh for new versions of automake + * Specify the SDL API calling convention (C by default) +Ryan Gordon - Sat Apr 13 07:33:37 PDT 2002 + * Fixed recursive audio lock in the mixing function +jean-julien Filatriau - Sat Mar 23 18:05:37 PST 2002 + * Fixed setting invalid volume when querying mixer and music volumes +Guillaume Cottenceau - Wed Feb 13 15:43:20 PST 2002 + * Implemented Ogg Vorbis stream rewinding +Peter Kutak - Wed Feb 13 10:26:57 PST 2002 + * Added native midi support on Linux, using GPL code + --enable-music-native-midi-gpl +Pete Shinners - Mon Jan 14 11:31:26 PST 2002 + * Added seek support for MP3 files +Ryan Gordon - Mon Jan 14 11:30:44 PST 2002 + * Sample "finished" callbacks are now always called when a sample is stopped. + +1.2.2: +Guillaume Cottenceau - Wed Dec 19 08:59:05 PST 2001 + * Added an API for seeking in music files (implemented for MOD and Ogg music) + Mix_FadeInMusicPos(), Mix_SetMusicPosition() + * Exposed the mikmod synchro value for music synchronization + Mix_SetSynchroValue(), Mix_GetSynchroValue() + +1.2.1: +Yi-Huang Han - Wed Oct 24 21:55:47 PDT 2001 + * Fixed MOD music volume when looping +David Hedbor - Thu Oct 18 10:01:41 PDT 2001 + * Stop implicit looping, set fade out and other flags on MOD files +Sam Lantinga - Tue Oct 16 11:17:12 PDT 2001 + * The music file type is now determined by extension as well as magic +Ryan C. Gordon - Tue Sep 11 12:05:54 PDT 2001 + * Reworked playwave.c to make it more useful as a mixer testbed + * Added a realtime sound effect API to SDL_mixer.h + * Added the following standard sound effects: + panning, distance attenuation, basic positional audio, stereo reversal + * Added API for mixer versioning: Mix_Linked_Version() and MIX_VERSION() +Sam Lantinga - Tue Sep 11 11:48:53 PDT 2001 + * Updated MikMod code to version 3.1.9a +Torbjörn Andersson - Tue Sep 11 11:22:29 PDT 2001 + * Added support for loading AIFF audio chunks +Max Horn - Tue Sep 4 20:38:11 PDT 2001 + * Added native MIDI music support on MacOS and MacOS X +Florian Schulze - Sun Aug 19 14:55:37 PDT 2001 + * Added native MIDI music support on Windows +Sam Lantinga - Sun Aug 19 02:20:55 PDT 2001 + * Added Project Builder projects for building MacOS X framework +Darrell Walisser - Sun Aug 19 00:47:22 PDT 2001 + * Fixed compilation problems with mikmod under MacOS X +Torbjörn Andersson - Sun, 19 Aug 2001 16:03:30 + * Fixed AIFF music playing support +Sam Lantinga - Sat Aug 18 04:14:13 PDT 2001 + * Fixed building Ogg Vorbis support on Windows +Ryan C. Gordon - Thu, 7 Jun 2001 13:15:51 + * Added Mix_ChannelFinished() and Mix_GetChunk() +Ryan C. Gordon - Tue, 5 Jun 2001 11:01:51 + * Added VOC sound file support +Guillaume Cottenceau - Thu May 10 11:17:55 PDT 2001 + * Fixed crashes when API used with audio not initialized +Paul Jenner - Sat, 14 Apr 2001 09:20:38 -0700 (PDT) + * Added support for building RPM directly from tar archive + +1.2.0: +Sam Lantinga - Wed Apr 4 12:42:20 PDT 2001 + * Synchronized release version with SDL 1.2.0 + +1.1.1: +John Hall - Tue Jan 2 13:46:54 PST 2001 + * Added support to playmus for track switching with Ctrl-C + * Added support to playmus for multiple command line files + +1.1.0: +Sam Lantinga - Wed Nov 29 20:47:13 PST 2000 + * Package specifically for SDL 1.1 (no real reason API-wise, but for clarity) + +1.0.7: +Sam Lantinga - Tue Nov 7 10:22:09 PST 2000 + * Fixed hang in mikmod re-initialization +Stephane Peter - Oct 17 13:07:32 PST 2000 + * Fixed music fading +Ray Kelm - Fri, 04 Aug 2000 20:58:00 -0400 + * Added support for cross-compiling Windows DLL from Linux + +1.0.6: +Sam Lantinga - Sun Jul 2 14:16:44 PDT 2000 + * Added support for the Ogg Vorbis music format: http://www.vorbis.org/ +Darrell Walisser - Wed Jun 28 11:59:40 PDT 2000 + * Added Codewarrior projects for MacOS +Sam Lantinga - Mon Jun 26 12:01:11 PDT 2000 + * Fixed symbol aliasing problem with "channel" +Matt - Wed, 12 Apr 2000 15:36:13 -0700 + * Added SDL_RWops support for mikmod loading (not hooked into music.c yet) + +1.0.5: +Paul Furber - Fri Mar 3 14:58:50 PST 2000 + * Fixed MP3 detection with compilers that use signed char datatypes + +1.0.4: +Sam Lantinga - Thu Feb 10 19:42:03 PST 2000 + * Ported the base mixer and mikmod libraries to MacOS +Markus Oberhumer - Wed Feb 2 13:16:17 PST 2000 + * Fixed problem with short looping sounds +Sam Lantinga - Tue Feb 1 13:25:44 PST 2000 + * Added Visual C++ project file +Markus Oberhumer - Tue Feb 1 13:23:11 PST 2000 + * Cleaned up code for compiling with Visual C++ + * Don't hang in Mix_HaltMusic() if the music is paused +Sam Lantinga - Fri Jan 28 08:54:56 PST 2000 + * Fixed looping WAVE chunks that are not aligned on sample boundaries + +1.0.3: +Sam Lantinga - Mon Jan 17 19:48:09 PST 2000 + * Changed the name of the library from "mixer" to "SDL_mixer" + * Instead of including "mixer.h", include "SDL_mixer.h", + * Instead of linking with libmixer.a, link with libSDL_mixer.a + +1.0.2: +Sam Lantinga - Fri Jan 14 11:06:56 PST 2000 + * Made the CHANGELOG entries Y2K compliant. :) +MFX - Updated the mikmod support to MikMod 3.1.8 +MFX - Added Mix_HookMusicFinished() API function + +1.0.1: +SOL - Added a post-mixing callback +SP - A few music-related bugfixes + +1.0.0: +SOL - Added autoconf support +SP - Added MP3 support using SMPEG +SP - Added fading in/out of music and samples +SP - Added dynamic allocation of channels +SP - Added channel grouping functions +SP - Added expiration delay for samples + +Initial Key: +SOL - Sam Lantinga (hercules@lokigames.com) +SP - Stephane Peter (megastep@lokigames.com) +MFX - Markus Oberhumer (markus.oberhumer@jk.uni-linz.ac.at) diff --git a/apps/plugins/sdl/SDL_mixer/COPYING b/apps/plugins/sdl/SDL_mixer/COPYING new file mode 100644 index 0000000000..dce4d178b1 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/COPYING @@ -0,0 +1,20 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ diff --git a/apps/plugins/sdl/SDL_mixer/Makefile.in b/apps/plugins/sdl/SDL_mixer/Makefile.in new file mode 100644 index 0000000000..3d10565c6d --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/Makefile.in @@ -0,0 +1,133 @@ +# Makefile to build and install the SDL_mixer library + +top_builddir = . +srcdir = @srcdir@ +objects = build +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = $(DESTDIR)@bindir@ +libdir = $(DESTDIR)@libdir@ +includedir = $(DESTDIR)@includedir@ +datarootdir = $(DESTDIR)@datarootdir@ +datadir = @datadir@ +mandir = @mandir@ +auxdir = @ac_aux_dir@ +distpath = $(srcdir)/.. +distdir = SDL_mixer-@VERSION@ +distfile = $(distdir).tar.gz + +@SET_MAKE@ +EXE = @EXE@ +SHELL = @SHELL@ +CC = @CC@ +CXX = g++ +CFLAGS = @BUILD_CFLAGS@ +EXTRA_CFLAGS = @EXTRA_CFLAGS@ +LDFLAGS = @BUILD_LDFLAGS@ +EXTRA_LDFLAGS = @EXTRA_LDFLAGS@ +LIBTOOL = @LIBTOOL@ +INSTALL = @INSTALL@ +AR = @AR@ +RANLIB = @RANLIB@ +WINDRES = @WINDRES@ +SDL_CFLAGS = @SDL_CFLAGS@ +SDL_LIBS = @SDL_LIBS@ + +TARGET = libSDL_mixer.la +OBJECTS = @OBJECTS@ +VERSION_OBJECTS = @VERSION_OBJECTS@ +PLAYWAVE_OBJECTS = @PLAYWAVE_OBJECTS@ +PLAYMUS_OBJECTS = @PLAYMUS_OBJECTS@ + +DIST = Android.mk CHANGES COPYING CWProjects.sea.bin MPWmake.sea.bin Makefile.in SDL_mixer.pc.in README SDL_mixer.h SDL_mixer.qpg.in SDL_mixer.spec SDL_mixer.spec.in VisualC Watcom-OS2.zip Xcode Xcode-iOS acinclude autogen.sh build-scripts configure configure.in dynamic_flac.c dynamic_flac.h dynamic_fluidsynth.c dynamic_fluidsynth.h dynamic_mod.c dynamic_mod.h dynamic_mp3.c dynamic_mp3.h dynamic_ogg.c dynamic_ogg.h effect_position.c effect_stereoreverse.c effects_internal.c effects_internal.h fluidsynth.c fluidsynth.h gcc-fat.sh libmikmod-3.1.12.zip load_aiff.c load_aiff.h load_flac.c load_flac.h load_ogg.c load_ogg.h load_voc.c load_voc.h mixer.c music.c music_cmd.c music_cmd.h music_flac.c music_flac.h music_mad.c music_mad.h music_mod.c music_mod.h music_modplug.c music_modplug.h music_ogg.c music_ogg.h native_midi playmus.c playwave.c timidity wavestream.c wavestream.h version.rc + +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_RELEASE = @LT_RELEASE@ +LT_REVISION = @LT_REVISION@ +LT_LDFLAGS = -no-undefined -rpath $(libdir) -release $(LT_RELEASE) -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) + +all: $(srcdir)/configure Makefile $(objects) $(objects)/$(TARGET) $(objects)/playwave$(EXE) $(objects)/playmus$(EXE) + +$(srcdir)/configure: $(srcdir)/configure.in + @echo "Warning, configure.in is out of date" + #(cd $(srcdir) && sh autogen.sh && sh configure) + @sleep 3 + +Makefile: $(srcdir)/Makefile.in + $(SHELL) config.status $@ + +$(objects): + $(SHELL) $(auxdir)/mkinstalldirs $@ + +.PHONY: all install install-hdrs install-lib install-bin uninstall uninstall-hdrs uninstall-lib uninstall-bin clean distclean dist + +$(objects)/$(TARGET): $(OBJECTS) $(VERSION_OBJECTS) + $(LIBTOOL) --mode=link $(CC) -o $@ $(OBJECTS) $(VERSION_OBJECTS) $(LDFLAGS) $(EXTRA_LDFLAGS) $(LT_LDFLAGS) + +$(objects)/playwave$(EXE): $(objects)/playwave.lo $(objects)/$(TARGET) + $(LIBTOOL) --mode=link $(CC) -o $@ $(objects)/playwave.lo $(SDL_CFLAGS) $(SDL_LIBS) $(objects)/$(TARGET) + +$(objects)/playmus$(EXE): $(objects)/playmus.lo $(objects)/$(TARGET) + $(LIBTOOL) --mode=link $(CC) -o $@ $(objects)/playmus.lo $(SDL_CFLAGS) $(SDL_LIBS) $(objects)/$(TARGET) + +install: all install-hdrs install-lib #install-bin +install-hdrs: + $(SHELL) $(auxdir)/mkinstalldirs $(includedir)/SDL + for src in $(srcdir)/SDL_mixer.h; do \ + file=`echo $$src | sed -e 's|^.*/||'`; \ + $(INSTALL) -m 644 $$src $(includedir)/SDL/$$file; \ + done + $(SHELL) $(auxdir)/mkinstalldirs $(libdir)/pkgconfig + $(INSTALL) -m 644 SDL_mixer.pc $(libdir)/pkgconfig/ +install-lib: $(objects) $(objects)/$(TARGET) + $(SHELL) $(auxdir)/mkinstalldirs $(libdir) + $(LIBTOOL) --mode=install $(INSTALL) $(objects)/$(TARGET) $(libdir)/$(TARGET) +install-bin: + $(SHELL) $(auxdir)/mkinstalldirs $(bindir) + $(LIBTOOL) --mode=install $(INSTALL) -m 755 $(objects)/playwave$(EXE) $(bindir)/playwave$(EXE) + $(LIBTOOL) --mode=install $(INSTALL) -m 755 $(objects)/playmus$(EXE) $(bindir)/playmus$(EXE) + +uninstall: uninstall-hdrs uninstall-lib uninstall-bin +uninstall-hdrs: + for src in $(srcdir)/SDL_mixer.h; do \ + file=`echo $$src | sed -e 's|^.*/||'`; \ + rm -f $(includedir)/SDL/$$file; \ + done + -rmdir $(includedir)/SDL + rm -f $(libdir)/pkgconfig/SDL_mixer.pc + -rmdir $(libdir)/pkgconfig +uninstall-lib: + $(LIBTOOL) --mode=uninstall rm -f $(libdir)/$(TARGET) +uninstall-bin: + rm -f $(bindir)/playwave$(EXE) + rm -f $(bindir)/playmus$(EXE) + +clean: + rm -rf $(objects) + +distclean: clean + rm -f Makefile + rm -f SDL_mixer.qpg + rm -f config.status config.cache config.log libtool + rm -f SDL_mixer.pc + rm -rf $(srcdir)/autom4te* + find $(srcdir) \( \ + -name '*~' -o \ + -name '*.bak' -o \ + -name '*.old' -o \ + -name '*.rej' -o \ + -name '*.orig' -o \ + -name '.#*' \) \ + -exec rm -f {} \; + +dist $(distfile): + $(SHELL) $(auxdir)/mkinstalldirs $(distdir) + tar cf - $(DIST) | (cd $(distdir); tar xf -) + rm -rf `find $(distdir) -name .svn` + rm -f `find $(distdir) -name '.#*'` + tar cvf - $(distdir) | gzip --best >$(distfile) + rm -rf $(distdir) + +rpm: $(distfile) + rpmbuild -ta $? diff --git a/apps/plugins/sdl/SDL_mixer/README b/apps/plugins/sdl/SDL_mixer/README new file mode 100644 index 0000000000..b17cfe6840 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/README @@ -0,0 +1,43 @@ + +SDL_mixer 1.2 + +The latest version of this library is available from: +http://www.libsdl.org/projects/SDL_mixer/ + +Due to popular demand, here is a simple multi-channel audio mixer. +It supports 8 channels of 16 bit stereo audio, plus a single channel +of music, mixed by the popular MikMod MOD, Timidity MIDI and SMPEG MP3 +libraries. + +See the header file SDL_mixer.h and the examples playwave.c and playmus.c +for documentation on this mixer library. + +The mixer can currently load Microsoft WAVE files and Creative Labs VOC +files as audio samples, and can load MIDI files via Timidity and the +following music formats via MikMod: .MOD .S3M .IT .XM. It can load +Ogg Vorbis streams as music if built with Ogg Vorbis or Tremor libraries, +and finally it can load MP3 music using the SMPEG or libmad libraries. + +Tremor decoding is disabled by default; you can enable it by passing + --enable-music-ogg-tremor +to configure, or by defining OGG_MUSIC and OGG_USE_TREMOR. + +libmad decoding is disabled by default; you can enable it by passing + --enable-music-mp3-mad +to configure, or by defining MP3_MAD_MUSIC +vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +WARNING: The license for libmad is GPL, which means that in order to + use it your application must also be GPL! +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The process of mixing MIDI files to wave output is very CPU intensive, +so if playing regular WAVE files sound great, but playing MIDI files +sound choppy, try using 8-bit audio, mono audio, or lower frequencies. + +To play MIDI files, you'll need to get a complete set of GUS patches +from: +http://www.libsdl.org/projects/mixer/timidity/timidity.tar.gz +and unpack them in /usr/local/lib under UNIX, and C:\ under Win32. + +This library is under the zlib license, see the file "COPYING" for details. + diff --git a/apps/plugins/sdl/SDL_mixer/dynamic_flac.c b/apps/plugins/sdl/SDL_mixer/dynamic_flac.c new file mode 100644 index 0000000000..59421fddea --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/dynamic_flac.c @@ -0,0 +1,177 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Implementation of the dynamic loading functionality for libFLAC. + ~ Austen Dicken (admin@cvpcs.org) +*/ + +#ifdef FLAC_MUSIC + +#include "SDL_loadso.h" + +#include "dynamic_flac.h" + +flac_loader flac = { + 0, NULL +}; + +#ifdef FLAC_DYNAMIC +int Mix_InitFLAC() +{ + if ( flac.loaded == 0 ) { + flac.handle = SDL_LoadObject(FLAC_DYNAMIC); + if ( flac.handle == NULL ) { + return -1; + } + flac.FLAC__stream_decoder_new = + (FLAC__StreamDecoder *(*)()) + SDL_LoadFunction(flac.handle, "FLAC__stream_decoder_new"); + if ( flac.FLAC__stream_decoder_new == NULL ) { + SDL_UnloadObject(flac.handle); + return -1; + } + flac.FLAC__stream_decoder_delete = + (void (*)(FLAC__StreamDecoder *)) + SDL_LoadFunction(flac.handle, "FLAC__stream_decoder_delete"); + if ( flac.FLAC__stream_decoder_delete == NULL ) { + SDL_UnloadObject(flac.handle); + return -1; + } + flac.FLAC__stream_decoder_init_stream = + (FLAC__StreamDecoderInitStatus (*)( + FLAC__StreamDecoder *, + FLAC__StreamDecoderReadCallback, + FLAC__StreamDecoderSeekCallback, + FLAC__StreamDecoderTellCallback, + FLAC__StreamDecoderLengthCallback, + FLAC__StreamDecoderEofCallback, + FLAC__StreamDecoderWriteCallback, + FLAC__StreamDecoderMetadataCallback, + FLAC__StreamDecoderErrorCallback, + void *)) + SDL_LoadFunction(flac.handle, "FLAC__stream_decoder_init_stream"); + if ( flac.FLAC__stream_decoder_init_stream == NULL ) { + SDL_UnloadObject(flac.handle); + return -1; + } + flac.FLAC__stream_decoder_finish = + (FLAC__bool (*)(FLAC__StreamDecoder *)) + SDL_LoadFunction(flac.handle, "FLAC__stream_decoder_finish"); + if ( flac.FLAC__stream_decoder_finish == NULL ) { + SDL_UnloadObject(flac.handle); + return -1; + } + flac.FLAC__stream_decoder_flush = + (FLAC__bool (*)(FLAC__StreamDecoder *)) + SDL_LoadFunction(flac.handle, "FLAC__stream_decoder_flush"); + if ( flac.FLAC__stream_decoder_flush == NULL ) { + SDL_UnloadObject(flac.handle); + return -1; + } + flac.FLAC__stream_decoder_process_single = + (FLAC__bool (*)(FLAC__StreamDecoder *)) + SDL_LoadFunction(flac.handle, + "FLAC__stream_decoder_process_single"); + if ( flac.FLAC__stream_decoder_process_single == NULL ) { + SDL_UnloadObject(flac.handle); + return -1; + } + flac.FLAC__stream_decoder_process_until_end_of_metadata = + (FLAC__bool (*)(FLAC__StreamDecoder *)) + SDL_LoadFunction(flac.handle, + "FLAC__stream_decoder_process_until_end_of_metadata"); + if ( flac.FLAC__stream_decoder_process_until_end_of_metadata == NULL ) { + SDL_UnloadObject(flac.handle); + return -1; + } + flac.FLAC__stream_decoder_process_until_end_of_stream = + (FLAC__bool (*)(FLAC__StreamDecoder *)) + SDL_LoadFunction(flac.handle, + "FLAC__stream_decoder_process_until_end_of_stream"); + if ( flac.FLAC__stream_decoder_process_until_end_of_stream == NULL ) { + SDL_UnloadObject(flac.handle); + return -1; + } + flac.FLAC__stream_decoder_seek_absolute = + (FLAC__bool (*)(FLAC__StreamDecoder *, FLAC__uint64)) + SDL_LoadFunction(flac.handle, "FLAC__stream_decoder_seek_absolute"); + if ( flac.FLAC__stream_decoder_seek_absolute == NULL ) { + SDL_UnloadObject(flac.handle); + return -1; + } + flac.FLAC__stream_decoder_get_state = + (FLAC__StreamDecoderState (*)(const FLAC__StreamDecoder *decoder)) + SDL_LoadFunction(flac.handle, "FLAC__stream_decoder_get_state"); + if ( flac.FLAC__stream_decoder_get_state == NULL ) { + SDL_UnloadObject(flac.handle); + return -1; + } + } + ++flac.loaded; + + return 0; +} +void Mix_QuitFLAC() +{ + if ( flac.loaded == 0 ) { + return; + } + if ( flac.loaded == 1 ) { + SDL_UnloadObject(flac.handle); + } + --flac.loaded; +} +#else +int Mix_InitFLAC() +{ + if ( flac.loaded == 0 ) { + flac.FLAC__stream_decoder_new = FLAC__stream_decoder_new; + flac.FLAC__stream_decoder_delete = FLAC__stream_decoder_delete; + flac.FLAC__stream_decoder_init_stream = + FLAC__stream_decoder_init_stream; + flac.FLAC__stream_decoder_finish = FLAC__stream_decoder_finish; + flac.FLAC__stream_decoder_flush = FLAC__stream_decoder_flush; + flac.FLAC__stream_decoder_process_single = + FLAC__stream_decoder_process_single; + flac.FLAC__stream_decoder_process_until_end_of_metadata = + FLAC__stream_decoder_process_until_end_of_metadata; + flac.FLAC__stream_decoder_process_until_end_of_stream = + FLAC__stream_decoder_process_until_end_of_stream; + flac.FLAC__stream_decoder_seek_absolute = + FLAC__stream_decoder_seek_absolute; + flac.FLAC__stream_decoder_get_state = + FLAC__stream_decoder_get_state; + } + ++flac.loaded; + + return 0; +} +void Mix_QuitFLAC() +{ + if ( flac.loaded == 0 ) { + return; + } + if ( flac.loaded == 1 ) { + } + --flac.loaded; +} +#endif /* FLAC_DYNAMIC */ + +#endif /* FLAC_MUSIC */ diff --git a/apps/plugins/sdl/SDL_mixer/dynamic_flac.h b/apps/plugins/sdl/SDL_mixer/dynamic_flac.h new file mode 100644 index 0000000000..e2dd3f7540 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/dynamic_flac.h @@ -0,0 +1,66 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + The following file defines all of the functions/objects used to dynamically + link to the libFLAC library. + ~ Austen Dicken (admin@cvpcs.org) +*/ + +#ifdef FLAC_MUSIC + +#include <FLAC/stream_decoder.h> + +typedef struct { + int loaded; + void *handle; + FLAC__StreamDecoder *(*FLAC__stream_decoder_new)(); + void (*FLAC__stream_decoder_delete)(FLAC__StreamDecoder *decoder); + FLAC__StreamDecoderInitStatus (*FLAC__stream_decoder_init_stream)( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data); + FLAC__bool (*FLAC__stream_decoder_finish)(FLAC__StreamDecoder *decoder); + FLAC__bool (*FLAC__stream_decoder_flush)(FLAC__StreamDecoder *decoder); + FLAC__bool (*FLAC__stream_decoder_process_single)( + FLAC__StreamDecoder *decoder); + FLAC__bool (*FLAC__stream_decoder_process_until_end_of_metadata)( + FLAC__StreamDecoder *decoder); + FLAC__bool (*FLAC__stream_decoder_process_until_end_of_stream)( + FLAC__StreamDecoder *decoder); + FLAC__bool (*FLAC__stream_decoder_seek_absolute)( + FLAC__StreamDecoder *decoder, + FLAC__uint64 sample); + FLAC__StreamDecoderState (*FLAC__stream_decoder_get_state)( + const FLAC__StreamDecoder *decoder); +} flac_loader; + +extern flac_loader flac; + +#endif /* FLAC_MUSIC */ + +extern int Mix_InitFLAC(); +extern void Mix_QuitFLAC(); diff --git a/apps/plugins/sdl/SDL_mixer/dynamic_fluidsynth.c b/apps/plugins/sdl/SDL_mixer/dynamic_fluidsynth.c new file mode 100644 index 0000000000..73c687fb7b --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/dynamic_fluidsynth.c @@ -0,0 +1,87 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + James Le Cuirot + chewi@aura-online.co.uk +*/ + +#ifdef USE_FLUIDSYNTH_MIDI + +#include "SDL_loadso.h" +#include "dynamic_fluidsynth.h" + +fluidsynth_loader fluidsynth = { + 0, NULL +}; + +#ifdef FLUIDSYNTH_DYNAMIC +#define FLUIDSYNTH_LOADER(FUNC, SIG) \ + fluidsynth.FUNC = (SIG) SDL_LoadFunction(fluidsynth.handle, #FUNC); \ + if (fluidsynth.FUNC == NULL) { SDL_UnloadObject(fluidsynth.handle); return -1; } +#else +#define FLUIDSYNTH_LOADER(FUNC, SIG) \ + fluidsynth.FUNC = FUNC; +#endif + +int Mix_InitFluidSynth() +{ + if ( fluidsynth.loaded == 0 ) { +#ifdef FLUIDSYNTH_DYNAMIC + fluidsynth.handle = SDL_LoadObject(FLUIDSYNTH_DYNAMIC); + if ( fluidsynth.handle == NULL ) return -1; +#endif + + FLUIDSYNTH_LOADER(delete_fluid_player, int (*)(fluid_player_t*)); + FLUIDSYNTH_LOADER(delete_fluid_settings, void (*)(fluid_settings_t*)); + FLUIDSYNTH_LOADER(delete_fluid_synth, int (*)(fluid_synth_t*)); + FLUIDSYNTH_LOADER(fluid_player_add, int (*)(fluid_player_t*, const char*)); + FLUIDSYNTH_LOADER(fluid_player_add_mem, int (*)(fluid_player_t*, const void*, size_t)); + FLUIDSYNTH_LOADER(fluid_player_get_status, int (*)(fluid_player_t*)); + FLUIDSYNTH_LOADER(fluid_player_play, int (*)(fluid_player_t*)); + FLUIDSYNTH_LOADER(fluid_player_set_loop, int (*)(fluid_player_t*, int)); + FLUIDSYNTH_LOADER(fluid_player_stop, int (*)(fluid_player_t*)); + FLUIDSYNTH_LOADER(fluid_settings_setnum, int (*)(fluid_settings_t*, const char*, double)); + FLUIDSYNTH_LOADER(fluid_synth_get_settings, fluid_settings_t* (*)(fluid_synth_t*)); + FLUIDSYNTH_LOADER(fluid_synth_set_gain, void (*)(fluid_synth_t*, float)); + FLUIDSYNTH_LOADER(fluid_synth_sfload, int(*)(fluid_synth_t*, const char*, int)); + FLUIDSYNTH_LOADER(fluid_synth_write_s16, int(*)(fluid_synth_t*, int, void*, int, int, void*, int, int)); + FLUIDSYNTH_LOADER(new_fluid_player, fluid_player_t* (*)(fluid_synth_t*)); + FLUIDSYNTH_LOADER(new_fluid_settings, fluid_settings_t* (*)(void)); + FLUIDSYNTH_LOADER(new_fluid_synth, fluid_synth_t* (*)(fluid_settings_t*)); + } + ++fluidsynth.loaded; + + return 0; +} + +void Mix_QuitFluidSynth() +{ + if ( fluidsynth.loaded == 0 ) { + return; + } + if ( fluidsynth.loaded == 1 ) { +#ifdef FLUIDSYNTH_DYNAMIC + SDL_UnloadObject(fluidsynth.handle); +#endif + } + --fluidsynth.loaded; +} + +#endif /* USE_FLUIDSYNTH_MIDI */ diff --git a/apps/plugins/sdl/SDL_mixer/dynamic_fluidsynth.h b/apps/plugins/sdl/SDL_mixer/dynamic_fluidsynth.h new file mode 100644 index 0000000000..5d25232a22 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/dynamic_fluidsynth.h @@ -0,0 +1,57 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + James Le Cuirot + chewi@aura-online.co.uk +*/ + +#ifdef USE_FLUIDSYNTH_MIDI + +#include <fluidsynth.h> + +typedef struct { + int loaded; + void *handle; + + int (*delete_fluid_player)(fluid_player_t*); + void (*delete_fluid_settings)(fluid_settings_t*); + int (*delete_fluid_synth)(fluid_synth_t*); + int (*fluid_player_add)(fluid_player_t*, const char*); + int (*fluid_player_add_mem)(fluid_player_t*, const void*, size_t); + int (*fluid_player_get_status)(fluid_player_t*); + int (*fluid_player_play)(fluid_player_t*); + int (*fluid_player_set_loop)(fluid_player_t*, int); + int (*fluid_player_stop)(fluid_player_t*); + int (*fluid_settings_setnum)(fluid_settings_t*, const char*, double); + fluid_settings_t* (*fluid_synth_get_settings)(fluid_synth_t*); + void (*fluid_synth_set_gain)(fluid_synth_t*, float); + int (*fluid_synth_sfload)(fluid_synth_t*, const char*, int); + int (*fluid_synth_write_s16)(fluid_synth_t*, int, void*, int, int, void*, int, int); + fluid_player_t* (*new_fluid_player)(fluid_synth_t*); + fluid_settings_t* (*new_fluid_settings)(void); + fluid_synth_t* (*new_fluid_synth)(fluid_settings_t*); +} fluidsynth_loader; + +extern fluidsynth_loader fluidsynth; + +#endif /* USE_FLUIDSYNTH_MIDI */ + +extern int Mix_InitFluidSynth(); +extern void Mix_QuitFluidSynth(); diff --git a/apps/plugins/sdl/SDL_mixer/dynamic_mod.c b/apps/plugins/sdl/SDL_mixer/dynamic_mod.c new file mode 100644 index 0000000000..7e3cd0af11 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/dynamic_mod.c @@ -0,0 +1,275 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifdef MOD_MUSIC + +#include "SDL_loadso.h" + +#include "dynamic_mod.h" + +mikmod_loader mikmod = { + 0, NULL +}; + +#ifdef MOD_DYNAMIC +int Mix_InitMOD() +{ + if ( mikmod.loaded == 0 ) { + mikmod.handle = SDL_LoadObject(MOD_DYNAMIC); + if ( mikmod.handle == NULL ) { + return -1; + } + mikmod.MikMod_Exit = + (void (*)(void)) + SDL_LoadFunction(mikmod.handle, "MikMod_Exit"); + if ( mikmod.MikMod_Exit == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.MikMod_InfoDriver = + (CHAR* (*)(void)) + SDL_LoadFunction(mikmod.handle, "MikMod_InfoDriver"); + if ( mikmod.MikMod_InfoDriver == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.MikMod_InfoLoader = + (CHAR* (*)(void)) + SDL_LoadFunction(mikmod.handle, "MikMod_InfoLoader"); + if ( mikmod.MikMod_InfoLoader == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.MikMod_Init = + (BOOL (*)(CHAR*)) + SDL_LoadFunction(mikmod.handle, "MikMod_Init"); + if ( mikmod.MikMod_Init == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.MikMod_RegisterAllLoaders = + (void (*)(void)) + SDL_LoadFunction(mikmod.handle, "MikMod_RegisterAllLoaders"); + if ( mikmod.MikMod_RegisterAllLoaders == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.MikMod_RegisterDriver = + (void (*)(struct MDRIVER*)) + SDL_LoadFunction(mikmod.handle, "MikMod_RegisterDriver"); + if ( mikmod.MikMod_RegisterDriver == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.MikMod_errno = + (int*) + SDL_LoadFunction(mikmod.handle, "MikMod_errno"); + if ( mikmod.MikMod_errno == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.MikMod_strerror = + (char* (*)(int)) + SDL_LoadFunction(mikmod.handle, "MikMod_strerror"); + if ( mikmod.MikMod_strerror == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.Player_Active = + (BOOL (*)(void)) + SDL_LoadFunction(mikmod.handle, "Player_Active"); + if ( mikmod.Player_Active == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.Player_Free = + (void (*)(MODULE*)) + SDL_LoadFunction(mikmod.handle, "Player_Free"); + if ( mikmod.Player_Free == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.Player_LoadGeneric = + (MODULE* (*)(MREADER*,int,BOOL)) + SDL_LoadFunction(mikmod.handle, "Player_LoadGeneric"); + if ( mikmod.Player_LoadGeneric == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.Player_SetPosition = + (void (*)(UWORD)) + SDL_LoadFunction(mikmod.handle, "Player_SetPosition"); + if ( mikmod.Player_SetPosition == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.Player_SetVolume = + (void (*)(SWORD)) + SDL_LoadFunction(mikmod.handle, "Player_SetVolume"); + if ( mikmod.Player_SetVolume == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.Player_Start = + (void (*)(MODULE*)) + SDL_LoadFunction(mikmod.handle, "Player_Start"); + if ( mikmod.Player_Start == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.Player_Stop = + (void (*)(void)) + SDL_LoadFunction(mikmod.handle, "Player_Stop"); + if ( mikmod.Player_Stop == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.VC_WriteBytes = + (ULONG (*)(SBYTE*,ULONG)) + SDL_LoadFunction(mikmod.handle, "VC_WriteBytes"); + if ( mikmod.VC_WriteBytes == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.drv_nos = + (MDRIVER*) + SDL_LoadFunction(mikmod.handle, "drv_nos"); + if ( mikmod.drv_nos == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.md_device = + (UWORD*) + SDL_LoadFunction(mikmod.handle, "md_device"); + if ( mikmod.md_device == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.md_mixfreq = + (UWORD*) + SDL_LoadFunction(mikmod.handle, "md_mixfreq"); + if ( mikmod.md_mixfreq == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.md_mode = + (UWORD*) + SDL_LoadFunction(mikmod.handle, "md_mode"); + if ( mikmod.md_mode == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.md_musicvolume = + (UBYTE*) + SDL_LoadFunction(mikmod.handle, "md_musicvolume"); + if ( mikmod.md_musicvolume == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.md_pansep = + (UBYTE*) + SDL_LoadFunction(mikmod.handle, "md_pansep"); + if ( mikmod.md_pansep == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.md_reverb = + (UBYTE*) + SDL_LoadFunction(mikmod.handle, "md_reverb"); + if ( mikmod.md_reverb == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.md_sndfxvolume = + (UBYTE*) + SDL_LoadFunction(mikmod.handle, "md_sndfxvolume"); + if ( mikmod.md_sndfxvolume == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + mikmod.md_volume = + (UBYTE*) + SDL_LoadFunction(mikmod.handle, "md_volume"); + if ( mikmod.md_volume == NULL ) { + SDL_UnloadObject(mikmod.handle); + return -1; + } + } + ++mikmod.loaded; + + return 0; +} +void Mix_QuitMOD() +{ + if ( mikmod.loaded == 0 ) { + return; + } + if ( mikmod.loaded == 1 ) { + SDL_UnloadObject(mikmod.handle); + } + --mikmod.loaded; +} +#else +int Mix_InitMOD() +{ + if ( mikmod.loaded == 0 ) { + mikmod.MikMod_Exit = MikMod_Exit; + mikmod.MikMod_InfoDriver = MikMod_InfoDriver; + mikmod.MikMod_InfoLoader = MikMod_InfoLoader; + mikmod.MikMod_Init = MikMod_Init; + mikmod.MikMod_RegisterAllLoaders = MikMod_RegisterAllLoaders; + mikmod.MikMod_RegisterDriver = MikMod_RegisterDriver; + mikmod.MikMod_errno = &MikMod_errno; + mikmod.MikMod_strerror = MikMod_strerror; + mikmod.Player_Active = Player_Active; + mikmod.Player_Free = Player_Free; + mikmod.Player_LoadGeneric = Player_LoadGeneric; + mikmod.Player_SetPosition = Player_SetPosition; + mikmod.Player_SetVolume = Player_SetVolume; + mikmod.Player_Start = Player_Start; + mikmod.Player_Stop = Player_Stop; + mikmod.VC_WriteBytes = VC_WriteBytes; + mikmod.drv_nos = &drv_nos; + mikmod.md_device = &md_device; + mikmod.md_mixfreq = &md_mixfreq; + mikmod.md_mode = &md_mode; + mikmod.md_musicvolume = &md_musicvolume; + mikmod.md_pansep = &md_pansep; + mikmod.md_reverb = &md_reverb; + mikmod.md_sndfxvolume = &md_sndfxvolume; + mikmod.md_volume = &md_volume; + } + ++mikmod.loaded; + + return 0; +} +void Mix_QuitMOD() +{ + if ( mikmod.loaded == 0 ) { + return; + } + if ( mikmod.loaded == 1 ) { + } + --mikmod.loaded; +} +#endif /* MOD_DYNAMIC */ + +#endif /* MOD_MUSIC */ diff --git a/apps/plugins/sdl/SDL_mixer/dynamic_mod.h b/apps/plugins/sdl/SDL_mixer/dynamic_mod.h new file mode 100644 index 0000000000..3561b151db --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/dynamic_mod.h @@ -0,0 +1,62 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifdef MOD_MUSIC + +#include "mikmod.h" + +typedef struct { + int loaded; + void *handle; + + void (*MikMod_Exit)(void); + CHAR* (*MikMod_InfoDriver)(void); + CHAR* (*MikMod_InfoLoader)(void); + BOOL (*MikMod_Init)(CHAR*); + void (*MikMod_RegisterAllLoaders)(void); + void (*MikMod_RegisterDriver)(struct MDRIVER*); + int* MikMod_errno; + char* (*MikMod_strerror)(int); + BOOL (*Player_Active)(void); + void (*Player_Free)(MODULE*); + MODULE* (*Player_LoadGeneric)(MREADER*,int,BOOL); + void (*Player_SetPosition)(UWORD); + void (*Player_SetVolume)(SWORD); + void (*Player_Start)(MODULE*); + void (*Player_Stop)(void); + ULONG (*VC_WriteBytes)(SBYTE*,ULONG); + struct MDRIVER* drv_nos; + UWORD* md_device; + UWORD* md_mixfreq; + UWORD* md_mode; + UBYTE* md_musicvolume; + UBYTE* md_pansep; + UBYTE* md_reverb; + UBYTE* md_sndfxvolume; + UBYTE* md_volume; +} mikmod_loader; + +extern mikmod_loader mikmod; + +#endif /* MOD_MUSIC */ + +extern int Mix_InitMOD(); +extern void Mix_QuitMOD(); diff --git a/apps/plugins/sdl/SDL_mixer/dynamic_mp3.c b/apps/plugins/sdl/SDL_mixer/dynamic_mp3.c new file mode 100644 index 0000000000..83a3e309fc --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/dynamic_mp3.c @@ -0,0 +1,171 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifdef MP3_MUSIC + +#include "SDL_loadso.h" + +#include "dynamic_mp3.h" + +smpeg_loader smpeg = { + 0, NULL +}; + +#ifdef MP3_DYNAMIC +int Mix_InitMP3() +{ + if ( smpeg.loaded == 0 ) { + smpeg.handle = SDL_LoadObject(MP3_DYNAMIC); + if ( smpeg.handle == NULL ) { + return -1; + } + smpeg.SMPEG_actualSpec = + (void (*)( SMPEG *, SDL_AudioSpec * )) + SDL_LoadFunction(smpeg.handle, "SMPEG_actualSpec"); + if ( smpeg.SMPEG_actualSpec == NULL ) { + SDL_UnloadObject(smpeg.handle); + return -1; + } + smpeg.SMPEG_delete = + (void (*)( SMPEG* )) + SDL_LoadFunction(smpeg.handle, "SMPEG_delete"); + if ( smpeg.SMPEG_delete == NULL ) { + SDL_UnloadObject(smpeg.handle); + return -1; + } + smpeg.SMPEG_enableaudio = + (void (*)( SMPEG*, int )) + SDL_LoadFunction(smpeg.handle, "SMPEG_enableaudio"); + if ( smpeg.SMPEG_enableaudio == NULL ) { + SDL_UnloadObject(smpeg.handle); + return -1; + } + smpeg.SMPEG_enablevideo = + (void (*)( SMPEG*, int )) + SDL_LoadFunction(smpeg.handle, "SMPEG_enablevideo"); + if ( smpeg.SMPEG_enablevideo == NULL ) { + SDL_UnloadObject(smpeg.handle); + return -1; + } + smpeg.SMPEG_new_rwops = + (SMPEG* (*)(SDL_RWops *, SMPEG_Info*, int)) + SDL_LoadFunction(smpeg.handle, "SMPEG_new_rwops"); + if ( smpeg.SMPEG_new_rwops == NULL ) { + SDL_UnloadObject(smpeg.handle); + return -1; + } + smpeg.SMPEG_play = + (void (*)( SMPEG* )) + SDL_LoadFunction(smpeg.handle, "SMPEG_play"); + if ( smpeg.SMPEG_play == NULL ) { + SDL_UnloadObject(smpeg.handle); + return -1; + } + smpeg.SMPEG_playAudio = + (int (*)( SMPEG *, Uint8 *, int )) + SDL_LoadFunction(smpeg.handle, "SMPEG_playAudio"); + if ( smpeg.SMPEG_playAudio == NULL ) { + SDL_UnloadObject(smpeg.handle); + return -1; + } + smpeg.SMPEG_rewind = + (void (*)( SMPEG* )) + SDL_LoadFunction(smpeg.handle, "SMPEG_rewind"); + if ( smpeg.SMPEG_rewind == NULL ) { + SDL_UnloadObject(smpeg.handle); + return -1; + } + smpeg.SMPEG_setvolume = + (void (*)( SMPEG*, int )) + SDL_LoadFunction(smpeg.handle, "SMPEG_setvolume"); + if ( smpeg.SMPEG_setvolume == NULL ) { + SDL_UnloadObject(smpeg.handle); + return -1; + } + smpeg.SMPEG_skip = + (void (*)( SMPEG*, float )) + SDL_LoadFunction(smpeg.handle, "SMPEG_skip"); + if ( smpeg.SMPEG_skip == NULL ) { + SDL_UnloadObject(smpeg.handle); + return -1; + } + smpeg.SMPEG_status = + (SMPEGstatus (*)( SMPEG* )) + SDL_LoadFunction(smpeg.handle, "SMPEG_status"); + if ( smpeg.SMPEG_status == NULL ) { + SDL_UnloadObject(smpeg.handle); + return -1; + } + smpeg.SMPEG_stop = + (void (*)( SMPEG* )) + SDL_LoadFunction(smpeg.handle, "SMPEG_stop"); + if ( smpeg.SMPEG_stop == NULL ) { + SDL_UnloadObject(smpeg.handle); + return -1; + } + } + ++smpeg.loaded; + + return 0; +} +void Mix_QuitMP3() +{ + if ( smpeg.loaded == 0 ) { + return; + } + if ( smpeg.loaded == 1 ) { + SDL_UnloadObject(smpeg.handle); + } + --smpeg.loaded; +} +#else +int Mix_InitMP3() +{ + if ( smpeg.loaded == 0 ) { + smpeg.SMPEG_actualSpec = SMPEG_actualSpec; + smpeg.SMPEG_delete = SMPEG_delete; + smpeg.SMPEG_enableaudio = SMPEG_enableaudio; + smpeg.SMPEG_enablevideo = SMPEG_enablevideo; + smpeg.SMPEG_new_rwops = SMPEG_new_rwops; + smpeg.SMPEG_play = SMPEG_play; + smpeg.SMPEG_playAudio = SMPEG_playAudio; + smpeg.SMPEG_rewind = SMPEG_rewind; + smpeg.SMPEG_setvolume = SMPEG_setvolume; + smpeg.SMPEG_skip = SMPEG_skip; + smpeg.SMPEG_status = SMPEG_status; + smpeg.SMPEG_stop = SMPEG_stop; + } + ++smpeg.loaded; + + return 0; +} +void Mix_QuitMP3() +{ + if ( smpeg.loaded == 0 ) { + return; + } + if ( smpeg.loaded == 1 ) { + } + --smpeg.loaded; +} +#endif /* MP3_DYNAMIC */ + +#endif /* MP3_MUSIC */ diff --git a/apps/plugins/sdl/SDL_mixer/dynamic_mp3.h b/apps/plugins/sdl/SDL_mixer/dynamic_mp3.h new file mode 100644 index 0000000000..03cbbbf9cf --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/dynamic_mp3.h @@ -0,0 +1,47 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifdef MP3_MUSIC +#include "smpeg.h" + +typedef struct { + int loaded; + void *handle; + void (*SMPEG_actualSpec)( SMPEG *mpeg, SDL_AudioSpec *spec ); + void (*SMPEG_delete)( SMPEG* mpeg ); + void (*SMPEG_enableaudio)( SMPEG* mpeg, int enable ); + void (*SMPEG_enablevideo)( SMPEG* mpeg, int enable ); + SMPEG* (*SMPEG_new_rwops)(SDL_RWops *src, SMPEG_Info* info, int sdl_audio); + void (*SMPEG_play)( SMPEG* mpeg ); + int (*SMPEG_playAudio)( SMPEG *mpeg, Uint8 *stream, int len ); + void (*SMPEG_rewind)( SMPEG* mpeg ); + void (*SMPEG_setvolume)( SMPEG* mpeg, int volume ); + void (*SMPEG_skip)( SMPEG* mpeg, float seconds ); + SMPEGstatus (*SMPEG_status)( SMPEG* mpeg ); + void (*SMPEG_stop)( SMPEG* mpeg ); +} smpeg_loader; + +extern smpeg_loader smpeg; + +#endif /* MUSIC_MP3 */ + +extern int Mix_InitMP3(); +extern void Mix_QuitMP3(); diff --git a/apps/plugins/sdl/SDL_mixer/dynamic_ogg.c b/apps/plugins/sdl/SDL_mixer/dynamic_ogg.c new file mode 100644 index 0000000000..345a8c866f --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/dynamic_ogg.c @@ -0,0 +1,131 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifdef OGG_MUSIC + +#include "SDL_loadso.h" + +#include "dynamic_ogg.h" + +vorbis_loader vorbis = { + 0, NULL +}; + +#ifdef OGG_DYNAMIC +int Mix_InitOgg() +{ + if ( vorbis.loaded == 0 ) { + vorbis.handle = SDL_LoadObject(OGG_DYNAMIC); + if ( vorbis.handle == NULL ) { + return -1; + } + vorbis.ov_clear = + (int (*)(OggVorbis_File *)) + SDL_LoadFunction(vorbis.handle, "ov_clear"); + if ( vorbis.ov_clear == NULL ) { + SDL_UnloadObject(vorbis.handle); + return -1; + } + vorbis.ov_info = + (vorbis_info *(*)(OggVorbis_File *,int)) + SDL_LoadFunction(vorbis.handle, "ov_info"); + if ( vorbis.ov_info == NULL ) { + SDL_UnloadObject(vorbis.handle); + return -1; + } + vorbis.ov_open_callbacks = + (int (*)(void *, OggVorbis_File *, char *, long, ov_callbacks)) + SDL_LoadFunction(vorbis.handle, "ov_open_callbacks"); + if ( vorbis.ov_open_callbacks == NULL ) { + SDL_UnloadObject(vorbis.handle); + return -1; + } + vorbis.ov_pcm_total = + (ogg_int64_t (*)(OggVorbis_File *,int)) + SDL_LoadFunction(vorbis.handle, "ov_pcm_total"); + if ( vorbis.ov_pcm_total == NULL ) { + SDL_UnloadObject(vorbis.handle); + return -1; + } + vorbis.ov_read = +#ifdef OGG_USE_TREMOR + (long (*)(OggVorbis_File *,char *,int,int *)) +#else + (long (*)(OggVorbis_File *,char *,int,int,int,int,int *)) +#endif + SDL_LoadFunction(vorbis.handle, "ov_read"); + if ( vorbis.ov_read == NULL ) { + SDL_UnloadObject(vorbis.handle); + return -1; + } + vorbis.ov_time_seek = +#ifdef OGG_USE_TREMOR + (long (*)(OggVorbis_File *,ogg_int64_t)) +#else + (int (*)(OggVorbis_File *,double)) +#endif + SDL_LoadFunction(vorbis.handle, "ov_time_seek"); + if ( vorbis.ov_time_seek == NULL ) { + SDL_UnloadObject(vorbis.handle); + return -1; + } + } + ++vorbis.loaded; + + return 0; +} +void Mix_QuitOgg() +{ + if ( vorbis.loaded == 0 ) { + return; + } + if ( vorbis.loaded == 1 ) { + SDL_UnloadObject(vorbis.handle); + } + --vorbis.loaded; +} +#else +int Mix_InitOgg() +{ + if ( vorbis.loaded == 0 ) { + vorbis.ov_clear = ov_clear; + vorbis.ov_info = ov_info; + vorbis.ov_open_callbacks = ov_open_callbacks; + vorbis.ov_pcm_total = ov_pcm_total; + vorbis.ov_read = ov_read; + vorbis.ov_time_seek = ov_time_seek; + } + ++vorbis.loaded; + + return 0; +} +void Mix_QuitOgg() +{ + if ( vorbis.loaded == 0 ) { + return; + } + if ( vorbis.loaded == 1 ) { + } + --vorbis.loaded; +} +#endif /* OGG_DYNAMIC */ + +#endif /* OGG_MUSIC */ diff --git a/apps/plugins/sdl/SDL_mixer/dynamic_ogg.h b/apps/plugins/sdl/SDL_mixer/dynamic_ogg.h new file mode 100644 index 0000000000..822458d49e --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/dynamic_ogg.h @@ -0,0 +1,53 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifdef OGG_MUSIC +#ifdef OGG_USE_TREMOR +#include <tremor/ivorbisfile.h> +#else +#include <vorbis/vorbisfile.h> +#endif + +typedef struct { + int loaded; + void *handle; + int (*ov_clear)(OggVorbis_File *vf); + vorbis_info *(*ov_info)(OggVorbis_File *vf,int link); + int (*ov_open_callbacks)(void *datasource, OggVorbis_File *vf, char *initial, long ibytes, ov_callbacks callbacks); + ogg_int64_t (*ov_pcm_total)(OggVorbis_File *vf,int i); +#ifdef OGG_USE_TREMOR + long (*ov_read)(OggVorbis_File *vf,char *buffer,int length, int *bitstream); +#else + long (*ov_read)(OggVorbis_File *vf,char *buffer,int length, int bigendianp,int word,int sgned,int *bitstream); +#endif +#ifdef OGG_USE_TREMOR + int (*ov_time_seek)(OggVorbis_File *vf,ogg_int64_t pos); +#else + int (*ov_time_seek)(OggVorbis_File *vf,double pos); +#endif +} vorbis_loader; + +extern vorbis_loader vorbis; + +#endif /* OGG_MUSIC */ + +extern int Mix_InitOgg(); +extern void Mix_QuitOgg(); diff --git a/apps/plugins/sdl/SDL_mixer/effect_position.c b/apps/plugins/sdl/SDL_mixer/effect_position.c new file mode 100644 index 0000000000..e0b467b12f --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/effect_position.c @@ -0,0 +1,1615 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + This file by Ryan C. Gordon (icculus@icculus.org) + + These are some internally supported special effects that use SDL_mixer's + effect callback API. They are meant for speed over quality. :) +*/ + +/* $Id$ */ + +#include "SDL.h" +#include "SDL_mixer.h" +#include "SDL_endian.h" + +#define __MIX_INTERNAL_EFFECT__ +#include "effects_internal.h" + +/* profile code: + #include <sys/time.h> + #include <unistd.h> + struct timeval tv1; + struct timeval tv2; + + gettimeofday(&tv1, NULL); + + ... do your thing here ... + + gettimeofday(&tv2, NULL); + printf("%ld\n", tv2.tv_usec - tv1.tv_usec); +*/ + + +/* + * Positional effects...panning, distance attenuation, etc. + */ + +typedef struct _Eff_positionargs +{ + volatile float left_f; + volatile float right_f; + volatile Uint8 left_u8; + volatile Uint8 right_u8; + volatile float left_rear_f; + volatile float right_rear_f; + volatile float center_f; + volatile float lfe_f; + volatile Uint8 left_rear_u8; + volatile Uint8 right_rear_u8; + volatile Uint8 center_u8; + volatile Uint8 lfe_u8; + volatile float distance_f; + volatile Uint8 distance_u8; + volatile Sint16 room_angle; + volatile int in_use; + volatile int channels; +} position_args; + +static position_args **pos_args_array = NULL; +static position_args *pos_args_global = NULL; +static int position_channels = 0; + +void _Eff_PositionDeinit(void) +{ + int i; + for (i = 0; i < position_channels; i++) { + SDL_free(pos_args_array[i]); + } + + position_channels = 0; + + SDL_free(pos_args_global); + pos_args_global = NULL; + SDL_free(pos_args_array); + pos_args_array = NULL; +} + + +/* This just frees up the callback-specific data. */ +static void _Eff_PositionDone(int channel, void *udata) +{ + if (channel < 0) { + if (pos_args_global != NULL) { + SDL_free(pos_args_global); + pos_args_global = NULL; + } + } + + else if (pos_args_array[channel] != NULL) { + SDL_free(pos_args_array[channel]); + pos_args_array[channel] = NULL; + } +} + + +static void _Eff_position_u8(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Uint8 *ptr = (Uint8 *) stream; + int i; + + /* + * if there's only a mono channnel (the only way we wouldn't have + * a len divisible by 2 here), then left_f and right_f are always + * 1.0, and are therefore throwaways. + */ + if (len % sizeof (Uint16) != 0) { + *ptr = (Uint8) (((float) *ptr) * args->distance_f); + ptr++; + len--; + } + + if (args->room_angle == 180) + for (i = 0; i < len; i += sizeof (Uint8) * 2) { + /* must adjust the sample so that 0 is the center */ + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f) + 128); + ptr++; + } + else for (i = 0; i < len; i += sizeof (Uint8) * 2) { + /* must adjust the sample so that 0 is the center */ + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f) + 128); + ptr++; + } +} +static void _Eff_position_u8_c4(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Uint8 *ptr = (Uint8 *) stream; + int i; + + /* + * if there's only a mono channnel (the only way we wouldn't have + * a len divisible by 2 here), then left_f and right_f are always + * 1.0, and are therefore throwaways. + */ + if (len % sizeof (Uint16) != 0) { + *ptr = (Uint8) (((float) *ptr) * args->distance_f); + ptr++; + len--; + } + + if (args->room_angle == 0) + for (i = 0; i < len; i += sizeof (Uint8) * 6) { + /* must adjust the sample so that 0 is the center */ + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_rear_f) * args->distance_f) + 128); + ptr++; + } + else if (args->room_angle == 90) + for (i = 0; i < len; i += sizeof (Uint8) * 6) { + /* must adjust the sample so that 0 is the center */ + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_rear_f) * args->distance_f) + 128); + ptr++; + } + else if (args->room_angle == 180) + for (i = 0; i < len; i += sizeof (Uint8) * 6) { + /* must adjust the sample so that 0 is the center */ + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f) + 128); + ptr++; + } + else if (args->room_angle == 270) + for (i = 0; i < len; i += sizeof (Uint8) * 6) { + /* must adjust the sample so that 0 is the center */ + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f) + 128); + ptr++; + } +} + + +static void _Eff_position_u8_c6(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Uint8 *ptr = (Uint8 *) stream; + int i; + + /* + * if there's only a mono channnel (the only way we wouldn't have + * a len divisible by 2 here), then left_f and right_f are always + * 1.0, and are therefore throwaways. + */ + if (len % sizeof (Uint16) != 0) { + *ptr = (Uint8) (((float) *ptr) * args->distance_f); + ptr++; + len--; + } + + if (args->room_angle == 0) + for (i = 0; i < len; i += sizeof (Uint8) * 6) { + /* must adjust the sample so that 0 is the center */ + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->center_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->lfe_f) * args->distance_f) + 128); + ptr++; + } + else if (args->room_angle == 90) + for (i = 0; i < len; i += sizeof (Uint8) * 6) { + /* must adjust the sample so that 0 is the center */ + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_rear_f) * args->distance_f/2) + 128) + + (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f/2) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->lfe_f) * args->distance_f) + 128); + ptr++; + } + else if (args->room_angle == 180) + for (i = 0; i < len; i += sizeof (Uint8) * 6) { + /* must adjust the sample so that 0 is the center */ + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_rear_f) * args->distance_f/2) + 128) + + (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_rear_f) * args->distance_f/2) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->lfe_f) * args->distance_f) + 128); + ptr++; + } + else if (args->room_angle == 270) + for (i = 0; i < len; i += sizeof (Uint8) * 6) { + /* must adjust the sample so that 0 is the center */ + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f/2) + 128) + + (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_rear_f) * args->distance_f/2) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->lfe_f) * args->distance_f) + 128); + ptr++; + } +} + + +/* + * This one runs about 10.1 times faster than the non-table version, with + * no loss in quality. It does, however, require 64k of memory for the + * lookup table. Also, this will only update position information once per + * call; the non-table version always checks the arguments for each sample, + * in case the user has called Mix_SetPanning() or whatnot again while this + * callback is running. + */ +static void _Eff_position_table_u8(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Uint8 *ptr = (Uint8 *) stream; + Uint32 *p; + int i; + Uint8 *l = ((Uint8 *) _Eff_volume_table) + (256 * args->left_u8); + Uint8 *r = ((Uint8 *) _Eff_volume_table) + (256 * args->right_u8); + Uint8 *d = ((Uint8 *) _Eff_volume_table) + (256 * args->distance_u8); + + if (args->room_angle == 180) { + Uint8 *temp = l; + l = r; + r = temp; + } + /* + * if there's only a mono channnel, then l[] and r[] are always + * volume 255, and are therefore throwaways. Still, we have to + * be sure not to overrun the audio buffer... + */ + while (len % sizeof (Uint32) != 0) { + *ptr = d[l[*ptr]]; + ptr++; + if (args->channels > 1) { + *ptr = d[r[*ptr]]; + ptr++; + } + len -= args->channels; + } + + p = (Uint32 *) ptr; + + for (i = 0; i < len; i += sizeof (Uint32)) { +#if (SDL_BYTEORDER == SDL_BIG_ENDIAN) + *p = (d[l[(*p & 0xFF000000) >> 24]] << 24) | + (d[r[(*p & 0x00FF0000) >> 16]] << 16) | + (d[l[(*p & 0x0000FF00) >> 8]] << 8) | + (d[r[(*p & 0x000000FF) ]] ) ; +#else + *p = (d[r[(*p & 0xFF000000) >> 24]] << 24) | + (d[l[(*p & 0x00FF0000) >> 16]] << 16) | + (d[r[(*p & 0x0000FF00) >> 8]] << 8) | + (d[l[(*p & 0x000000FF) ]] ) ; +#endif + ++p; + } +} + + +static void _Eff_position_s8(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Sint8 *ptr = (Sint8 *) stream; + int i; + + /* + * if there's only a mono channnel (the only way we wouldn't have + * a len divisible by 2 here), then left_f and right_f are always + * 1.0, and are therefore throwaways. + */ + if (len % sizeof (Sint16) != 0) { + *ptr = (Sint8) (((float) *ptr) * args->distance_f); + ptr++; + len--; + } + + if (args->room_angle == 180) + for (i = 0; i < len; i += sizeof (Sint8) * 2) { + *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); + ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); + ptr++; + } + else + for (i = 0; i < len; i += sizeof (Sint8) * 2) { + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); + ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); + ptr++; + } +} +static void _Eff_position_s8_c4(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Sint8 *ptr = (Sint8 *) stream; + int i; + + /* + * if there's only a mono channnel (the only way we wouldn't have + * a len divisible by 2 here), then left_f and right_f are always + * 1.0, and are therefore throwaways. + */ + if (len % sizeof (Sint16) != 0) { + *ptr = (Sint8) (((float) *ptr) * args->distance_f); + ptr++; + len--; + } + + for (i = 0; i < len; i += sizeof (Sint8) * 4) { + switch (args->room_angle) { + case 0: + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; + break; + case 90: + *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; + break; + case 180: + *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; + break; + case 270: + *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; + break; + } + } +} +static void _Eff_position_s8_c6(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Sint8 *ptr = (Sint8 *) stream; + int i; + + /* + * if there's only a mono channnel (the only way we wouldn't have + * a len divisible by 2 here), then left_f and right_f are always + * 1.0, and are therefore throwaways. + */ + if (len % sizeof (Sint16) != 0) { + *ptr = (Sint8) (((float) *ptr) * args->distance_f); + ptr++; + len--; + } + + for (i = 0; i < len; i += sizeof (Sint8) * 6) { + switch (args->room_angle) { + case 0: + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->center_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->lfe_f) * args->distance_f); ptr++; + break; + case 90: + *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f / 2) + + (Sint8)((((float) *ptr) * args->right_f) * args->distance_f / 2); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->lfe_f) * args->distance_f); ptr++; + break; + case 180: + *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f / 2) + + (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f / 2); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->lfe_f) * args->distance_f); ptr++; + break; + case 270: + *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f / 2) + + (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f / 2); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->lfe_f) * args->distance_f); ptr++; + break; + } + } +} + + +/* + * This one runs about 10.1 times faster than the non-table version, with + * no loss in quality. It does, however, require 64k of memory for the + * lookup table. Also, this will only update position information once per + * call; the non-table version always checks the arguments for each sample, + * in case the user has called Mix_SetPanning() or whatnot again while this + * callback is running. + */ +static void _Eff_position_table_s8(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Sint8 *ptr = (Sint8 *) stream; + Uint32 *p; + int i; + Sint8 *l = ((Sint8 *) _Eff_volume_table) + (256 * args->left_u8); + Sint8 *r = ((Sint8 *) _Eff_volume_table) + (256 * args->right_u8); + Sint8 *d = ((Sint8 *) _Eff_volume_table) + (256 * args->distance_u8); + + if (args->room_angle == 180) { + Sint8 *temp = l; + l = r; + r = temp; + } + + + while (len % sizeof (Uint32) != 0) { + *ptr = d[l[*ptr]]; + ptr++; + if (args->channels > 1) { + *ptr = d[r[*ptr]]; + ptr++; + } + len -= args->channels; + } + + p = (Uint32 *) ptr; + + for (i = 0; i < len; i += sizeof (Uint32)) { +#if (SDL_BYTEORDER == SDL_BIG_ENDIAN) + *p = (d[l[((Sint16)(Sint8)((*p & 0xFF000000) >> 24))+128]] << 24) | + (d[r[((Sint16)(Sint8)((*p & 0x00FF0000) >> 16))+128]] << 16) | + (d[l[((Sint16)(Sint8)((*p & 0x0000FF00) >> 8))+128]] << 8) | + (d[r[((Sint16)(Sint8)((*p & 0x000000FF) ))+128]] ) ; +#else + *p = (d[r[((Sint16)(Sint8)((*p & 0xFF000000) >> 24))+128]] << 24) | + (d[l[((Sint16)(Sint8)((*p & 0x00FF0000) >> 16))+128]] << 16) | + (d[r[((Sint16)(Sint8)((*p & 0x0000FF00) >> 8))+128]] << 8) | + (d[l[((Sint16)(Sint8)((*p & 0x000000FF) ))+128]] ) ; +#endif + ++p; + } + + +} + + +/* !!! FIXME : Optimize the code for 16-bit samples? */ + +static void _Eff_position_u16lsb(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Uint16 *ptr = (Uint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Uint16) * 2) { + Sint16 sampl = (Sint16) (SDL_SwapLE16(*(ptr+0)) - 32768); + Sint16 sampr = (Sint16) (SDL_SwapLE16(*(ptr+1)) - 32768); + + Uint16 swapl = (Uint16) ((Sint16) (((float) sampl * args->left_f) + * args->distance_f) + 32768); + Uint16 swapr = (Uint16) ((Sint16) (((float) sampr * args->right_f) + * args->distance_f) + 32768); + + if (args->room_angle == 180) { + *(ptr++) = (Uint16) SDL_SwapLE16(swapr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapl); + } + else { + *(ptr++) = (Uint16) SDL_SwapLE16(swapl); + *(ptr++) = (Uint16) SDL_SwapLE16(swapr); + } + } +} +static void _Eff_position_u16lsb_c4(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Uint16 *ptr = (Uint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Uint16) * 4) { + Sint16 sampl = (Sint16) (SDL_SwapLE16(*(ptr+0)) - 32768); + Sint16 sampr = (Sint16) (SDL_SwapLE16(*(ptr+1)) - 32768); + Sint16 samplr = (Sint16) (SDL_SwapLE16(*(ptr+2)) - 32768); + Sint16 samprr = (Sint16) (SDL_SwapLE16(*(ptr+3)) - 32768); + + Uint16 swapl = (Uint16) ((Sint16) (((float) sampl * args->left_f) + * args->distance_f) + 32768); + Uint16 swapr = (Uint16) ((Sint16) (((float) sampr * args->right_f) + * args->distance_f) + 32768); + Uint16 swaplr = (Uint16) ((Sint16) (((float) samplr * args->left_rear_f) + * args->distance_f) + 32768); + Uint16 swaprr = (Uint16) ((Sint16) (((float) samprr * args->right_rear_f) + * args->distance_f) + 32768); + + switch (args->room_angle) { + case 0: + *(ptr++) = (Uint16) SDL_SwapLE16(swapl); + *(ptr++) = (Uint16) SDL_SwapLE16(swapr); + *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); + break; + case 90: + *(ptr++) = (Uint16) SDL_SwapLE16(swapr); + *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapl); + *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); + break; + case 180: + *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapl); + break; + case 270: + *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapl); + *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapr); + break; + } + } +} +static void _Eff_position_u16lsb_c6(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Uint16 *ptr = (Uint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Uint16) * 6) { + Sint16 sampl = (Sint16) (SDL_SwapLE16(*(ptr+0)) - 32768); + Sint16 sampr = (Sint16) (SDL_SwapLE16(*(ptr+1)) - 32768); + Sint16 samplr = (Sint16) (SDL_SwapLE16(*(ptr+2)) - 32768); + Sint16 samprr = (Sint16) (SDL_SwapLE16(*(ptr+3)) - 32768); + Sint16 sampce = (Sint16) (SDL_SwapLE16(*(ptr+4)) - 32768); + Sint16 sampwf = (Sint16) (SDL_SwapLE16(*(ptr+5)) - 32768); + + Uint16 swapl = (Uint16) ((Sint16) (((float) sampl * args->left_f) + * args->distance_f) + 32768); + Uint16 swapr = (Uint16) ((Sint16) (((float) sampr * args->right_f) + * args->distance_f) + 32768); + Uint16 swaplr = (Uint16) ((Sint16) (((float) samplr * args->left_rear_f) + * args->distance_f) + 32768); + Uint16 swaprr = (Uint16) ((Sint16) (((float) samprr * args->right_rear_f) + * args->distance_f) + 32768); + Uint16 swapce = (Uint16) ((Sint16) (((float) sampce * args->center_f) + * args->distance_f) + 32768); + Uint16 swapwf = (Uint16) ((Sint16) (((float) sampwf * args->lfe_f) + * args->distance_f) + 32768); + + switch (args->room_angle) { + case 0: + *(ptr++) = (Uint16) SDL_SwapLE16(swapl); + *(ptr++) = (Uint16) SDL_SwapLE16(swapr); + *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapce); + *(ptr++) = (Uint16) SDL_SwapLE16(swapwf); + break; + case 90: + *(ptr++) = (Uint16) SDL_SwapLE16(swapr); + *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapl); + *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapr)/2 + (Uint16) SDL_SwapLE16(swaprr)/2; + *(ptr++) = (Uint16) SDL_SwapLE16(swapwf); + break; + case 180: + *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapl); + *(ptr++) = (Uint16) SDL_SwapLE16(swaprr)/2 + (Uint16) SDL_SwapLE16(swaplr)/2; + *(ptr++) = (Uint16) SDL_SwapLE16(swapwf); + break; + case 270: + *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapl); + *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapl)/2 + (Uint16) SDL_SwapLE16(swaplr)/2; + *(ptr++) = (Uint16) SDL_SwapLE16(swapwf); + break; + } + } +} + +static void _Eff_position_s16lsb(int chan, void *stream, int len, void *udata) +{ + /* 16 signed bits (lsb) * 2 channels. */ + volatile position_args *args = (volatile position_args *) udata; + Sint16 *ptr = (Sint16 *) stream; + int i; + +#if 0 + if (len % (sizeof(Sint16) * 2)) { + fprintf(stderr,"Not an even number of frames! len=%d\n", len); + return; + } +#endif + + for (i = 0; i < len; i += sizeof (Sint16) * 2) { + Sint16 swapl = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+0))) * + args->left_f) * args->distance_f); + Sint16 swapr = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+1))) * + args->right_f) * args->distance_f); + if (args->room_angle == 180) { + *(ptr++) = (Sint16) SDL_SwapLE16(swapr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapl); + } + else { + *(ptr++) = (Sint16) SDL_SwapLE16(swapl); + *(ptr++) = (Sint16) SDL_SwapLE16(swapr); + } + } +} +static void _Eff_position_s16lsb_c4(int chan, void *stream, int len, void *udata) +{ + /* 16 signed bits (lsb) * 4 channels. */ + volatile position_args *args = (volatile position_args *) udata; + Sint16 *ptr = (Sint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Sint16) * 4) { + Sint16 swapl = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+0))) * + args->left_f) * args->distance_f); + Sint16 swapr = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+1))) * + args->right_f) * args->distance_f); + Sint16 swaplr = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+1))) * + args->left_rear_f) * args->distance_f); + Sint16 swaprr = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+2))) * + args->right_rear_f) * args->distance_f); + switch (args->room_angle) { + case 0: + *(ptr++) = (Sint16) SDL_SwapLE16(swapl); + *(ptr++) = (Sint16) SDL_SwapLE16(swapr); + *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); + break; + case 90: + *(ptr++) = (Sint16) SDL_SwapLE16(swapr); + *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapl); + *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); + break; + case 180: + *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapl); + break; + case 270: + *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapl); + *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapr); + break; + } + } +} + +static void _Eff_position_s16lsb_c6(int chan, void *stream, int len, void *udata) +{ + /* 16 signed bits (lsb) * 6 channels. */ + volatile position_args *args = (volatile position_args *) udata; + Sint16 *ptr = (Sint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Sint16) * 6) { + Sint16 swapl = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+0))) * + args->left_f) * args->distance_f); + Sint16 swapr = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+1))) * + args->right_f) * args->distance_f); + Sint16 swaplr = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+2))) * + args->left_rear_f) * args->distance_f); + Sint16 swaprr = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+3))) * + args->right_rear_f) * args->distance_f); + Sint16 swapce = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+4))) * + args->center_f) * args->distance_f); + Sint16 swapwf = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+5))) * + args->lfe_f) * args->distance_f); + switch (args->room_angle) { + case 0: + *(ptr++) = (Sint16) SDL_SwapLE16(swapl); + *(ptr++) = (Sint16) SDL_SwapLE16(swapr); + *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapce); + *(ptr++) = (Sint16) SDL_SwapLE16(swapwf); + break; + case 90: + *(ptr++) = (Sint16) SDL_SwapLE16(swapr); + *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapl); + *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapr)/2 + (Sint16) SDL_SwapLE16(swaprr)/2; + *(ptr++) = (Sint16) SDL_SwapLE16(swapwf); + break; + case 180: + *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapl); + *(ptr++) = (Sint16) SDL_SwapLE16(swaprr)/2 + (Sint16) SDL_SwapLE16(swaplr)/2; + *(ptr++) = (Sint16) SDL_SwapLE16(swapwf); + break; + case 270: + *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapl); + *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapl)/2 + (Sint16) SDL_SwapLE16(swaplr)/2; + *(ptr++) = (Sint16) SDL_SwapLE16(swapwf); + break; + } + } +} + +static void _Eff_position_u16msb(int chan, void *stream, int len, void *udata) +{ + /* 16 signed bits (lsb) * 2 channels. */ + volatile position_args *args = (volatile position_args *) udata; + Uint16 *ptr = (Uint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Sint16) * 2) { + Sint16 sampl = (Sint16) (SDL_SwapBE16(*(ptr+0)) - 32768); + Sint16 sampr = (Sint16) (SDL_SwapBE16(*(ptr+1)) - 32768); + + Uint16 swapl = (Uint16) ((Sint16) (((float) sampl * args->left_f) + * args->distance_f) + 32768); + Uint16 swapr = (Uint16) ((Sint16) (((float) sampr * args->right_f) + * args->distance_f) + 32768); + + if (args->room_angle == 180) { + *(ptr++) = (Uint16) SDL_SwapBE16(swapr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapl); + } + else { + *(ptr++) = (Uint16) SDL_SwapBE16(swapl); + *(ptr++) = (Uint16) SDL_SwapBE16(swapr); + } + } +} +static void _Eff_position_u16msb_c4(int chan, void *stream, int len, void *udata) +{ + /* 16 signed bits (lsb) * 4 channels. */ + volatile position_args *args = (volatile position_args *) udata; + Uint16 *ptr = (Uint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Sint16) * 4) { + Sint16 sampl = (Sint16) (SDL_SwapBE16(*(ptr+0)) - 32768); + Sint16 sampr = (Sint16) (SDL_SwapBE16(*(ptr+1)) - 32768); + Sint16 samplr = (Sint16) (SDL_SwapBE16(*(ptr+2)) - 32768); + Sint16 samprr = (Sint16) (SDL_SwapBE16(*(ptr+3)) - 32768); + + Uint16 swapl = (Uint16) ((Sint16) (((float) sampl * args->left_f) + * args->distance_f) + 32768); + Uint16 swapr = (Uint16) ((Sint16) (((float) sampr * args->right_f) + * args->distance_f) + 32768); + Uint16 swaplr = (Uint16) ((Sint16) (((float) samplr * args->left_rear_f) + * args->distance_f) + 32768); + Uint16 swaprr = (Uint16) ((Sint16) (((float) samprr * args->right_rear_f) + * args->distance_f) + 32768); + + switch (args->room_angle) { + case 0: + *(ptr++) = (Uint16) SDL_SwapBE16(swapl); + *(ptr++) = (Uint16) SDL_SwapBE16(swapr); + *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); + break; + case 90: + *(ptr++) = (Uint16) SDL_SwapBE16(swapr); + *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapl); + *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); + break; + case 180: + *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapl); + break; + case 270: + *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapl); + *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapr); + break; + } + } +} +static void _Eff_position_u16msb_c6(int chan, void *stream, int len, void *udata) +{ + /* 16 signed bits (lsb) * 6 channels. */ + volatile position_args *args = (volatile position_args *) udata; + Uint16 *ptr = (Uint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Sint16) * 6) { + Sint16 sampl = (Sint16) (SDL_SwapBE16(*(ptr+0)) - 32768); + Sint16 sampr = (Sint16) (SDL_SwapBE16(*(ptr+1)) - 32768); + Sint16 samplr = (Sint16) (SDL_SwapBE16(*(ptr+2)) - 32768); + Sint16 samprr = (Sint16) (SDL_SwapBE16(*(ptr+3)) - 32768); + Sint16 sampce = (Sint16) (SDL_SwapBE16(*(ptr+4)) - 32768); + Sint16 sampwf = (Sint16) (SDL_SwapBE16(*(ptr+5)) - 32768); + + Uint16 swapl = (Uint16) ((Sint16) (((float) sampl * args->left_f) + * args->distance_f) + 32768); + Uint16 swapr = (Uint16) ((Sint16) (((float) sampr * args->right_f) + * args->distance_f) + 32768); + Uint16 swaplr = (Uint16) ((Sint16) (((float) samplr * args->left_rear_f) + * args->distance_f) + 32768); + Uint16 swaprr = (Uint16) ((Sint16) (((float) samprr * args->right_rear_f) + * args->distance_f) + 32768); + Uint16 swapce = (Uint16) ((Sint16) (((float) sampce * args->center_f) + * args->distance_f) + 32768); + Uint16 swapwf = (Uint16) ((Sint16) (((float) sampwf * args->lfe_f) + * args->distance_f) + 32768); + + switch (args->room_angle) { + case 0: + *(ptr++) = (Uint16) SDL_SwapBE16(swapl); + *(ptr++) = (Uint16) SDL_SwapBE16(swapr); + *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapce); + *(ptr++) = (Uint16) SDL_SwapBE16(swapwf); + break; + case 90: + *(ptr++) = (Uint16) SDL_SwapBE16(swapr); + *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapl); + *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapr)/2 + (Uint16) SDL_SwapBE16(swaprr)/2; + *(ptr++) = (Uint16) SDL_SwapBE16(swapwf); + break; + case 180: + *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapl); + *(ptr++) = (Uint16) SDL_SwapBE16(swaprr)/2 + (Uint16) SDL_SwapBE16(swaplr)/2; + *(ptr++) = (Uint16) SDL_SwapBE16(swapwf); + break; + case 270: + *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapl); + *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapl)/2 + (Uint16) SDL_SwapBE16(swaplr)/2; + *(ptr++) = (Uint16) SDL_SwapBE16(swapwf); + break; + } + } +} + +static void _Eff_position_s16msb(int chan, void *stream, int len, void *udata) +{ + /* 16 signed bits (lsb) * 2 channels. */ + volatile position_args *args = (volatile position_args *) udata; + Sint16 *ptr = (Sint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Sint16) * 2) { + Sint16 swapl = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+0))) * + args->left_f) * args->distance_f); + Sint16 swapr = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+1))) * + args->right_f) * args->distance_f); + *(ptr++) = (Sint16) SDL_SwapBE16(swapl); + *(ptr++) = (Sint16) SDL_SwapBE16(swapr); + } +} +static void _Eff_position_s16msb_c4(int chan, void *stream, int len, void *udata) +{ + /* 16 signed bits (lsb) * 4 channels. */ + volatile position_args *args = (volatile position_args *) udata; + Sint16 *ptr = (Sint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Sint16) * 4) { + Sint16 swapl = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+0))) * + args->left_f) * args->distance_f); + Sint16 swapr = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+1))) * + args->right_f) * args->distance_f); + Sint16 swaplr = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+2))) * + args->left_rear_f) * args->distance_f); + Sint16 swaprr = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+3))) * + args->right_rear_f) * args->distance_f); + switch (args->room_angle) { + case 0: + *(ptr++) = (Sint16) SDL_SwapBE16(swapl); + *(ptr++) = (Sint16) SDL_SwapBE16(swapr); + *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); + break; + case 90: + *(ptr++) = (Sint16) SDL_SwapBE16(swapr); + *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapl); + *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); + break; + case 180: + *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapl); + break; + case 270: + *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapl); + *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapr); + break; + } + } +} +static void _Eff_position_s16msb_c6(int chan, void *stream, int len, void *udata) +{ + /* 16 signed bits (lsb) * 6 channels. */ + volatile position_args *args = (volatile position_args *) udata; + Sint16 *ptr = (Sint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Sint16) * 6) { + Sint16 swapl = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+0))) * + args->left_f) * args->distance_f); + Sint16 swapr = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+1))) * + args->right_f) * args->distance_f); + Sint16 swaplr = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+2))) * + args->left_rear_f) * args->distance_f); + Sint16 swaprr = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+3))) * + args->right_rear_f) * args->distance_f); + Sint16 swapce = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+4))) * + args->center_f) * args->distance_f); + Sint16 swapwf = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+5))) * + args->lfe_f) * args->distance_f); + + switch (args->room_angle) { + case 0: + *(ptr++) = (Sint16) SDL_SwapBE16(swapl); + *(ptr++) = (Sint16) SDL_SwapBE16(swapr); + *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapce); + *(ptr++) = (Sint16) SDL_SwapBE16(swapwf); + break; + case 90: + *(ptr++) = (Sint16) SDL_SwapBE16(swapr); + *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapl); + *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapr)/2 + (Sint16) SDL_SwapBE16(swaprr)/2; + *(ptr++) = (Sint16) SDL_SwapBE16(swapwf); + break; + case 180: + *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapl); + *(ptr++) = (Sint16) SDL_SwapBE16(swaprr)/2 + (Sint16) SDL_SwapBE16(swaplr)/2; + *(ptr++) = (Sint16) SDL_SwapBE16(swapwf); + break; + case 270: + *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapl); + *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapl)/2 + (Sint16) SDL_SwapBE16(swaplr)/2; + *(ptr++) = (Sint16) SDL_SwapBE16(swapwf); + break; + } + } +} + +static void init_position_args(position_args *args) +{ + memset(args, '\0', sizeof (position_args)); + args->in_use = 0; + args->room_angle = 0; + args->left_u8 = args->right_u8 = args->distance_u8 = 255; + args->left_f = args->right_f = args->distance_f = 1.0f; + args->left_rear_u8 = args->right_rear_u8 = args->center_u8 = args->lfe_u8 = 255; + args->left_rear_f = args->right_rear_f = args->center_f = args->lfe_f = 1.0f; + Mix_QuerySpec(NULL, NULL, (int *) &args->channels); +} + + +static position_args *get_position_arg(int channel) +{ + void *rc; + int i; + + if (channel < 0) { + if (pos_args_global == NULL) { + pos_args_global = SDL_malloc(sizeof (position_args)); + if (pos_args_global == NULL) { + Mix_SetError("Out of memory"); + return(NULL); + } + init_position_args(pos_args_global); + } + + return(pos_args_global); + } + + if (channel >= position_channels) { + rc = SDL_realloc(pos_args_array, (channel + 1) * sizeof (position_args *)); + if (rc == NULL) { + Mix_SetError("Out of memory"); + return(NULL); + } + pos_args_array = (position_args **) rc; + for (i = position_channels; i <= channel; i++) { + pos_args_array[i] = NULL; + } + position_channels = channel + 1; + } + + if (pos_args_array[channel] == NULL) { + pos_args_array[channel] = (position_args *)SDL_malloc(sizeof(position_args)); + if (pos_args_array[channel] == NULL) { + Mix_SetError("Out of memory"); + return(NULL); + } + init_position_args(pos_args_array[channel]); + } + + return(pos_args_array[channel]); +} + + +static Mix_EffectFunc_t get_position_effect_func(Uint16 format, int channels) +{ + Mix_EffectFunc_t f = NULL; + + switch (format) { + case AUDIO_U8: + switch (channels) { + case 1: + case 2: + f = (_Eff_build_volume_table_u8()) ? _Eff_position_table_u8 : + _Eff_position_u8; + break; + case 4: + f = _Eff_position_u8_c4; + break; + case 6: + f = _Eff_position_u8_c6; + break; + } + break; + + case AUDIO_S8: + switch (channels) { + case 1: + case 2: + f = (_Eff_build_volume_table_s8()) ? _Eff_position_table_s8 : + _Eff_position_s8; + break; + case 4: + f = _Eff_position_s8_c4; + break; + case 6: + f = _Eff_position_s8_c6; + break; + } + break; + + case AUDIO_U16LSB: + switch (channels) { + case 1: + case 2: + f = _Eff_position_u16lsb; + break; + case 4: + f = _Eff_position_u16lsb_c4; + break; + case 6: + f = _Eff_position_u16lsb_c6; + break; + } + break; + + case AUDIO_S16LSB: + switch (channels) { + case 1: + case 2: + f = _Eff_position_s16lsb; + break; + case 4: + f = _Eff_position_s16lsb_c4; + break; + case 6: + f = _Eff_position_s16lsb_c6; + break; + } + break; + + case AUDIO_U16MSB: + switch (channels) { + case 1: + case 2: + f = _Eff_position_u16msb; + break; + case 4: + f = _Eff_position_u16msb_c4; + break; + case 6: + f = _Eff_position_u16msb_c6; + break; + } + break; + + case AUDIO_S16MSB: + switch (channels) { + case 1: + case 2: + f = _Eff_position_s16msb; + break; + case 4: + f = _Eff_position_s16msb_c4; + break; + case 6: + f = _Eff_position_s16msb_c6; + break; + } + break; + + default: + Mix_SetError("Unsupported audio format"); + } + + return(f); +} + +static Uint8 speaker_amplitude[6]; + +static void set_amplitudes(int channels, int angle, int room_angle) +{ + int left = 255, right = 255; + int left_rear = 255, right_rear = 255, center = 255; + + angle = SDL_abs(angle) % 360; /* make angle between 0 and 359. */ + + if (channels == 2) + { + /* + * We only attenuate by position if the angle falls on the far side + * of center; That is, an angle that's due north would not attenuate + * either channel. Due west attenuates the right channel to 0.0, and + * due east attenuates the left channel to 0.0. Slightly east of + * center attenuates the left channel a little, and the right channel + * not at all. I think of this as occlusion by one's own head. :) + * + * ...so, we split our angle circle into four quadrants... + */ + if (angle < 90) { + left = 255 - ((int) (255.0f * (((float) angle) / 89.0f))); + } else if (angle < 180) { + left = (int) (255.0f * (((float) (angle - 90)) / 89.0f)); + } else if (angle < 270) { + right = 255 - ((int) (255.0f * (((float) (angle - 180)) / 89.0f))); + } else { + right = (int) (255.0f * (((float) (angle - 270)) / 89.0f)); + } + } + + if (channels == 4 || channels == 6) + { + /* + * An angle that's due north does not attenuate the center channel. + * An angle in the first quadrant, 0-90, does not attenuate the RF. + * + * ...so, we split our angle circle into 8 ... + * + * CE + * 0 + * LF | RF + * | + * 270<-------|----------->90 + * | + * LR | RR + * 180 + * + */ + if (angle < 45) { + left = ((int) (255.0f * (((float) (180 - angle)) / 179.0f))); + left_rear = 255 - ((int) (255.0f * (((float) (angle + 45)) / 89.0f))); + right_rear = 255 - ((int) (255.0f * (((float) (90 - angle)) / 179.0f))); + } else if (angle < 90) { + center = ((int) (255.0f * (((float) (225 - angle)) / 179.0f))); + left = ((int) (255.0f * (((float) (180 - angle)) / 179.0f))); + left_rear = 255 - ((int) (255.0f * (((float) (135 - angle)) / 89.0f))); + right_rear = ((int) (255.0f * (((float) (90 + angle)) / 179.0f))); + } else if (angle < 135) { + center = ((int) (255.0f * (((float) (225 - angle)) / 179.0f))); + left = 255 - ((int) (255.0f * (((float) (angle - 45)) / 89.0f))); + right = ((int) (255.0f * (((float) (270 - angle)) / 179.0f))); + left_rear = ((int) (255.0f * (((float) (angle)) / 179.0f))); + } else if (angle < 180) { + center = 255 - ((int) (255.0f * (((float) (angle - 90)) / 89.0f))); + left = 255 - ((int) (255.0f * (((float) (225 - angle)) / 89.0f))); + right = ((int) (255.0f * (((float) (270 - angle)) / 179.0f))); + left_rear = ((int) (255.0f * (((float) (angle)) / 179.0f))); + } else if (angle < 225) { + center = 255 - ((int) (255.0f * (((float) (270 - angle)) / 89.0f))); + left = ((int) (255.0f * (((float) (angle - 90)) / 179.0f))); + right = 255 - ((int) (255.0f * (((float) (angle - 135)) / 89.0f))); + right_rear = ((int) (255.0f * (((float) (360 - angle)) / 179.0f))); + } else if (angle < 270) { + center = ((int) (255.0f * (((float) (angle - 135)) / 179.0f))); + left = ((int) (255.0f * (((float) (angle - 90)) / 179.0f))); + right = 255 - ((int) (255.0f * (((float) (315 - angle)) / 89.0f))); + right_rear = ((int) (255.0f * (((float) (360 - angle)) / 179.0f))); + } else if (angle < 315) { + center = ((int) (255.0f * (((float) (angle - 135)) / 179.0f))); + right = ((int) (255.0f * (((float) (angle - 180)) / 179.0f))); + left_rear = ((int) (255.0f * (((float) (450 - angle)) / 179.0f))); + right_rear = 255 - ((int) (255.0f * (((float) (angle - 225)) / 89.0f))); + } else { + right = ((int) (255.0f * (((float) (angle - 180)) / 179.0f))); + left_rear = ((int) (255.0f * (((float) (450 - angle)) / 179.0f))); + right_rear = 255 - ((int) (255.0f * (((float) (405 - angle)) / 89.0f))); + } + } + + if (left < 0) left = 0; if (left > 255) left = 255; + if (right < 0) right = 0; if (right > 255) right = 255; + if (left_rear < 0) left_rear = 0; if (left_rear > 255) left_rear = 255; + if (right_rear < 0) right_rear = 0; if (right_rear > 255) right_rear = 255; + if (center < 0) center = 0; if (center > 255) center = 255; + + if (room_angle == 90) { + speaker_amplitude[0] = (Uint8)left_rear; + speaker_amplitude[1] = (Uint8)left; + speaker_amplitude[2] = (Uint8)right_rear; + speaker_amplitude[3] = (Uint8)right; + } + else if (room_angle == 180) { + if (channels == 2) { + speaker_amplitude[0] = (Uint8)right; + speaker_amplitude[1] = (Uint8)left; + } + else { + speaker_amplitude[0] = (Uint8)right_rear; + speaker_amplitude[1] = (Uint8)left_rear; + speaker_amplitude[2] = (Uint8)right; + speaker_amplitude[3] = (Uint8)left; + } + } + else if (room_angle == 270) { + speaker_amplitude[0] = (Uint8)right; + speaker_amplitude[1] = (Uint8)right_rear; + speaker_amplitude[2] = (Uint8)left; + speaker_amplitude[3] = (Uint8)left_rear; + } + else { + speaker_amplitude[0] = (Uint8)left; + speaker_amplitude[1] = (Uint8)right; + speaker_amplitude[2] = (Uint8)left_rear; + speaker_amplitude[3] = (Uint8)right_rear; + } + speaker_amplitude[4] = (Uint8)center; + speaker_amplitude[5] = 255; +} + +int Mix_SetPosition(int channel, Sint16 angle, Uint8 distance); + +int Mix_SetPanning(int channel, Uint8 left, Uint8 right) +{ + Mix_EffectFunc_t f = NULL; + int channels; + Uint16 format; + position_args *args = NULL; + int retval = 1; + + Mix_QuerySpec(NULL, &format, &channels); + + if (channels != 2 && channels != 4 && channels != 6) /* it's a no-op; we call that successful. */ + return(1); + + if (channels > 2) { + /* left = right = 255 => angle = 0, to unregister effect as when channels = 2 */ + /* left = 255 => angle = -90; left = 0 => angle = +89 */ + int angle = 0; + if ((left != 255) || (right != 255)) { + angle = (int)left; + angle = 127 - angle; + angle = -angle; + angle = angle * 90 / 128; /* Make it larger for more effect? */ + } + return( Mix_SetPosition(channel, angle, 0) ); + } + + f = get_position_effect_func(format, channels); + if (f == NULL) + return(0); + + SDL_LockAudio(); + args = get_position_arg(channel); + if (!args) { + SDL_UnlockAudio(); + return(0); + } + + /* it's a no-op; unregister the effect, if it's registered. */ + if ((args->distance_u8 == 255) && (left == 255) && (right == 255)) { + if (args->in_use) { + retval = _Mix_UnregisterEffect_locked(channel, f); + SDL_UnlockAudio(); + return(retval); + } else { + SDL_UnlockAudio(); + return(1); + } + } + + args->left_u8 = left; + args->left_f = ((float) left) / 255.0f; + args->right_u8 = right; + args->right_f = ((float) right) / 255.0f; + args->room_angle = 0; + + if (!args->in_use) { + args->in_use = 1; + retval=_Mix_RegisterEffect_locked(channel, f, _Eff_PositionDone, (void*)args); + } + + SDL_UnlockAudio(); + return(retval); +} + + +int Mix_SetDistance(int channel, Uint8 distance) +{ + Mix_EffectFunc_t f = NULL; + Uint16 format; + position_args *args = NULL; + int channels; + int retval = 1; + + Mix_QuerySpec(NULL, &format, &channels); + f = get_position_effect_func(format, channels); + if (f == NULL) + return(0); + + SDL_LockAudio(); + args = get_position_arg(channel); + if (!args) { + SDL_UnlockAudio(); + return(0); + } + + distance = 255 - distance; /* flip it to our scale. */ + + /* it's a no-op; unregister the effect, if it's registered. */ + if ((distance == 255) && (args->left_u8 == 255) && (args->right_u8 == 255)) { + if (args->in_use) { + retval = _Mix_UnregisterEffect_locked(channel, f); + SDL_UnlockAudio(); + return(retval); + } else { + SDL_UnlockAudio(); + return(1); + } + } + + args->distance_u8 = distance; + args->distance_f = ((float) distance) / 255.0f; + if (!args->in_use) { + args->in_use = 1; + retval = _Mix_RegisterEffect_locked(channel, f, _Eff_PositionDone, (void *) args); + } + + SDL_UnlockAudio(); + return(retval); +} + + +int Mix_SetPosition(int channel, Sint16 angle, Uint8 distance) +{ + Mix_EffectFunc_t f = NULL; + Uint16 format; + int channels; + position_args *args = NULL; + Sint16 room_angle = 0; + int retval = 1; + + Mix_QuerySpec(NULL, &format, &channels); + f = get_position_effect_func(format, channels); + if (f == NULL) + return(0); + + angle = SDL_abs(angle) % 360; /* make angle between 0 and 359. */ + + SDL_LockAudio(); + args = get_position_arg(channel); + if (!args) { + SDL_UnlockAudio(); + return(0); + } + + /* it's a no-op; unregister the effect, if it's registered. */ + if ((!distance) && (!angle)) { + if (args->in_use) { + retval = _Mix_UnregisterEffect_locked(channel, f); + SDL_UnlockAudio(); + return(retval); + } else { + SDL_UnlockAudio(); + return(1); + } + } + + if (channels == 2) + { + if (angle > 180) + room_angle = 180; /* exchange left and right channels */ + else room_angle = 0; + } + + if (channels == 4 || channels == 6) + { + if (angle > 315) room_angle = 0; + else if (angle > 225) room_angle = 270; + else if (angle > 135) room_angle = 180; + else if (angle > 45) room_angle = 90; + else room_angle = 0; + } + + + distance = 255 - distance; /* flip it to scale Mix_SetDistance() uses. */ + + set_amplitudes(channels, angle, room_angle); + + args->left_u8 = speaker_amplitude[0]; + args->left_f = ((float) speaker_amplitude[0]) / 255.0f; + args->right_u8 = speaker_amplitude[1]; + args->right_f = ((float) speaker_amplitude[1]) / 255.0f; + args->left_rear_u8 = speaker_amplitude[2]; + args->left_rear_f = ((float) speaker_amplitude[2]) / 255.0f; + args->right_rear_u8 = speaker_amplitude[3]; + args->right_rear_f = ((float) speaker_amplitude[3]) / 255.0f; + args->center_u8 = speaker_amplitude[4]; + args->center_f = ((float) speaker_amplitude[4]) / 255.0f; + args->lfe_u8 = speaker_amplitude[5]; + args->lfe_f = ((float) speaker_amplitude[5]) / 255.0f; + args->distance_u8 = distance; + args->distance_f = ((float) distance) / 255.0f; + args->room_angle = room_angle; + if (!args->in_use) { + args->in_use = 1; + retval = _Mix_RegisterEffect_locked(channel, f, _Eff_PositionDone, (void *) args); + } + + SDL_UnlockAudio(); + return(retval); +} + + +/* end of effects_position.c ... */ + diff --git a/apps/plugins/sdl/SDL_mixer/effect_stereoreverse.c b/apps/plugins/sdl/SDL_mixer/effect_stereoreverse.c new file mode 100644 index 0000000000..47a8bf67f1 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/effect_stereoreverse.c @@ -0,0 +1,117 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + This file by Ryan C. Gordon (icculus@icculus.org) + + These are some internally supported special effects that use SDL_mixer's + effect callback API. They are meant for speed over quality. :) +*/ + +/* $Id$ */ + +#include "SDL.h" +#include "SDL_mixer.h" + +#define __MIX_INTERNAL_EFFECT__ +#include "effects_internal.h" + +/* profile code: + #include <sys/time.h> + #include <unistd.h> + struct timeval tv1; + struct timeval tv2; + + gettimeofday(&tv1, NULL); + + ... do your thing here ... + + gettimeofday(&tv2, NULL); + printf("%ld\n", tv2.tv_usec - tv1.tv_usec); +*/ + + + +/* + * Stereo reversal effect...this one's pretty straightforward... + */ + +static void _Eff_reversestereo16(int chan, void *stream, int len, void *udata) +{ + /* 16 bits * 2 channels. */ + Uint32 *ptr = (Uint32 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Uint32), ptr++) { + *ptr = (((*ptr) & 0xFFFF0000) >> 16) | (((*ptr) & 0x0000FFFF) << 16); + } +} + + +static void _Eff_reversestereo8(int chan, void *stream, int len, void *udata) +{ + /* 8 bits * 2 channels. */ + Uint32 *ptr = (Uint32 *) stream; + int i; + + /* get the last two bytes if len is not divisible by four... */ + if (len % sizeof (Uint32) != 0) { + Uint16 *p = (Uint16 *) (((Uint8 *) stream) + (len - 2)); + *p = (Uint16)((((*p) & 0xFF00) >> 8) | (((*ptr) & 0x00FF) << 8)); + len -= 2; + } + + for (i = 0; i < len; i += sizeof (Uint32), ptr++) { + *ptr = (((*ptr) & 0x0000FF00) >> 8) | (((*ptr) & 0x000000FF) << 8) | + (((*ptr) & 0xFF000000) >> 8) | (((*ptr) & 0x00FF0000) << 8); + } +} + + +int Mix_SetReverseStereo(int channel, int flip) +{ + Mix_EffectFunc_t f = NULL; + int channels; + Uint16 format; + + Mix_QuerySpec(NULL, &format, &channels); + + if (channels == 2) { + if ((format & 0xFF) == 16) + f = _Eff_reversestereo16; + else if ((format & 0xFF) == 8) + f = _Eff_reversestereo8; + else { + Mix_SetError("Unsupported audio format"); + return(0); + } + + if (!flip) { + return(Mix_UnregisterEffect(channel, f)); + } else { + return(Mix_RegisterEffect(channel, f, NULL, NULL)); + } + } + + return(1); +} + + +/* end of effect_stereoreverse.c ... */ + diff --git a/apps/plugins/sdl/SDL_mixer/effects_internal.c b/apps/plugins/sdl/SDL_mixer/effects_internal.c new file mode 100644 index 0000000000..353a15b91e --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/effects_internal.c @@ -0,0 +1,121 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + This file by Ryan C. Gordon (icculus@icculus.org) + + These are some helper functions for the internal mixer special effects. +*/ + +/* $Id$ */ + + + /* ------ These are used internally only. Don't touch. ------ */ + + +#include "SDL_mixer.h" + +#define __MIX_INTERNAL_EFFECT__ +#include "effects_internal.h" + +/* Should we favor speed over memory usage and/or quality of output? */ +int _Mix_effects_max_speed = 0; + + +void _Mix_InitEffects(void) +{ + _Mix_effects_max_speed = (SDL_getenv(MIX_EFFECTSMAXSPEED) != NULL); +} + +void _Mix_DeinitEffects(void) +{ + _Eff_PositionDeinit(); +} + + +void *_Eff_volume_table = NULL; + + +/* Build the volume table for Uint8-format samples. + * + * Each column of the table is a possible sample, while each row of the + * table is a volume. Volume is a Uint8, where 0 is silence and 255 is full + * volume. So _Eff_volume_table[128][mysample] would be the value of + * mysample, at half volume. + */ +void *_Eff_build_volume_table_u8(void) +{ + int volume; + int sample; + Uint8 *rc; + + if (!_Mix_effects_max_speed) { + return(NULL); + } + + if (!_Eff_volume_table) { + rc = SDL_malloc(256 * 256); + if (rc) { + _Eff_volume_table = (void *) rc; + for (volume = 0; volume < 256; volume++) { + for (sample = -128; sample < 128; sample ++) { + *rc = (Uint8)(((float) sample) * ((float) volume / 255.0)) + + 128; + rc++; + } + } + } + } + + return(_Eff_volume_table); +} + + +/* Build the volume table for Sint8-format samples. + * + * Each column of the table is a possible sample, while each row of the + * table is a volume. Volume is a Uint8, where 0 is silence and 255 is full + * volume. So _Eff_volume_table[128][mysample+128] would be the value of + * mysample, at half volume. + */ +void *_Eff_build_volume_table_s8(void) +{ + int volume; + int sample; + Sint8 *rc; + + if (!_Eff_volume_table) { + rc = SDL_malloc(256 * 256); + if (rc) { + _Eff_volume_table = (void *) rc; + for (volume = 0; volume < 256; volume++) { + for (sample = -128; sample < 128; sample ++) { + *rc = (Sint8)(((float) sample) * ((float) volume / 255.0)); + rc++; + } + } + } + } + + return(_Eff_volume_table); +} + + +/* end of effects.c ... */ + diff --git a/apps/plugins/sdl/SDL_mixer/effects_internal.h b/apps/plugins/sdl/SDL_mixer/effects_internal.h new file mode 100644 index 0000000000..12b4b3b2da --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/effects_internal.h @@ -0,0 +1,60 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id$ */ + +#ifndef _INCLUDE_EFFECTS_INTERNAL_H_ +#define _INCLUDE_EFFECTS_INTERNAL_H_ + +#ifndef __MIX_INTERNAL_EFFECT__ +#error You should not include this file or use these functions. +#endif + +#include "SDL_mixer.h" + +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +extern int _Mix_effects_max_speed; +extern void *_Eff_volume_table; +void *_Eff_build_volume_table_u8(void); +void *_Eff_build_volume_table_s8(void); + +void _Mix_InitEffects(void); +void _Mix_DeinitEffects(void); +void _Eff_PositionDeinit(void); + +int _Mix_RegisterEffect_locked(int channel, Mix_EffectFunc_t f, + Mix_EffectDone_t d, void *arg); +int _Mix_UnregisterEffect_locked(int channel, Mix_EffectFunc_t f); +int _Mix_UnregisterAllEffects_locked(int channel); + + +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/apps/plugins/sdl/SDL_mixer/fluidsynth.c b/apps/plugins/sdl/SDL_mixer/fluidsynth.c new file mode 100644 index 0000000000..d680576542 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/fluidsynth.c @@ -0,0 +1,219 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + James Le Cuirot + chewi@aura-online.co.uk +*/ + +#ifdef USE_FLUIDSYNTH_MIDI + +#include "SDL_mixer.h" +#include "fluidsynth.h" + +static Uint16 format; +static Uint8 channels; +static int freq; + +int fluidsynth_check_soundfont(const char *path, void *data) +{ + FILE *file = fopen(path, "r"); + + if (file) { + fclose(file); + return 1; + } else { + Mix_SetError("Failed to access the SoundFont %s", path); + return 0; + } +} + +int fluidsynth_load_soundfont(const char *path, void *data) +{ + /* If this fails, it's too late to try Timidity so pray that at least one works. */ + fluidsynth.fluid_synth_sfload((fluid_synth_t*) data, path, 1); + return 1; +} + +int fluidsynth_init(SDL_AudioSpec *mixer) +{ + if (!Mix_EachSoundFont(fluidsynth_check_soundfont, NULL)) + return -1; + + format = mixer->format; + channels = mixer->channels; + freq = mixer->freq; + + return 0; +} + +static FluidSynthMidiSong *fluidsynth_loadsong_common(int (*function)(FluidSynthMidiSong*, void*), void *data) +{ + FluidSynthMidiSong *song; + fluid_settings_t *settings = NULL; + + if (!Mix_Init(MIX_INIT_FLUIDSYNTH)) { + return NULL; + } + + if ((song = SDL_malloc(sizeof(FluidSynthMidiSong)))) { + memset(song, 0, sizeof(FluidSynthMidiSong)); + + if (SDL_BuildAudioCVT(&song->convert, AUDIO_S16, 2, freq, format, channels, freq) >= 0) { + if ((settings = fluidsynth.new_fluid_settings())) { + fluidsynth.fluid_settings_setnum(settings, "synth.sample-rate", (double) freq); + + if ((song->synth = fluidsynth.new_fluid_synth(settings))) { + if (Mix_EachSoundFont(fluidsynth_load_soundfont, (void*) song->synth)) { + if ((song->player = fluidsynth.new_fluid_player(song->synth))) { + if (function(song, data)) return song; + fluidsynth.delete_fluid_player(song->player); + } else { + Mix_SetError("Failed to create FluidSynth player"); + } + } + fluidsynth.delete_fluid_synth(song->synth); + } else { + Mix_SetError("Failed to create FluidSynth synthesizer"); + } + fluidsynth.delete_fluid_settings(settings); + } else { + Mix_SetError("Failed to create FluidSynth settings"); + } + } else { + Mix_SetError("Failed to set up audio conversion"); + } + SDL_free(song); + } else { + Mix_SetError("Insufficient memory for song"); + } + return NULL; +} + +static int fluidsynth_loadsong_RW_internal(FluidSynthMidiSong *song, void *data) +{ + off_t offset; + size_t size; + char *buffer; + SDL_RWops *rw = (SDL_RWops*) data; + + offset = SDL_RWtell(rw); + SDL_RWseek(rw, 0, RW_SEEK_END); + size = SDL_RWtell(rw) - offset; + SDL_RWseek(rw, offset, RW_SEEK_SET); + + if ((buffer = (char*) SDL_malloc(size))) { + if(SDL_RWread(rw, buffer, size, 1) == 1) { + if (fluidsynth.fluid_player_add_mem(song->player, buffer, size) == FLUID_OK) { + return 1; + } else { + Mix_SetError("FluidSynth failed to load in-memory song"); + } + } else { + Mix_SetError("Failed to read in-memory song"); + } + SDL_free(buffer); + } else { + Mix_SetError("Insufficient memory for song"); + } + return 0; +} + +FluidSynthMidiSong *fluidsynth_loadsong_RW(SDL_RWops *rw, int freerw) +{ + FluidSynthMidiSong *song; + + song = fluidsynth_loadsong_common(fluidsynth_loadsong_RW_internal, (void*) rw); + if (freerw) { + SDL_RWclose(rw); + } + return song; +} + +void fluidsynth_freesong(FluidSynthMidiSong *song) +{ + if (!song) return; + fluidsynth.delete_fluid_player(song->player); + fluidsynth.delete_fluid_settings(fluidsynth.fluid_synth_get_settings(song->synth)); + fluidsynth.delete_fluid_synth(song->synth); + SDL_free(song); +} + +void fluidsynth_start(FluidSynthMidiSong *song) +{ + fluidsynth.fluid_player_set_loop(song->player, 1); + fluidsynth.fluid_player_play(song->player); +} + +void fluidsynth_stop(FluidSynthMidiSong *song) +{ + fluidsynth.fluid_player_stop(song->player); +} + +int fluidsynth_active(FluidSynthMidiSong *song) +{ + return fluidsynth.fluid_player_get_status(song->player) == FLUID_PLAYER_PLAYING ? 1 : 0; +} + +void fluidsynth_setvolume(FluidSynthMidiSong *song, int volume) +{ + /* FluidSynth's default is 0.2. Make 0.8 the maximum. */ + fluidsynth.fluid_synth_set_gain(song->synth, (float) (volume * 0.00625)); +} + +int fluidsynth_playsome(FluidSynthMidiSong *song, void *dest, int dest_len) +{ + int result = -1; + int frames = dest_len / channels / ((format & 0xFF) / 8); + int src_len = frames * 4; /* 16-bit stereo */ + void *src = dest; + + if (dest_len < src_len) { + if (!(src = SDL_malloc(src_len))) { + Mix_SetError("Insufficient memory for audio conversion"); + return result; + } + } + + if (fluidsynth.fluid_synth_write_s16(song->synth, frames, src, 0, 2, src, 1, 2) != FLUID_OK) { + Mix_SetError("Error generating FluidSynth audio"); + goto finish; + } + + song->convert.buf = src; + song->convert.len = src_len; + + if (SDL_ConvertAudio(&song->convert) < 0) { + Mix_SetError("Error during audio conversion"); + goto finish; + } + + if (src != dest) + memcpy(dest, src, dest_len); + + result = 0; + +finish: + if (src != dest) + SDL_free(src); + + return result; +} + +#endif /* USE_FLUIDSYNTH_MIDI */ diff --git a/apps/plugins/sdl/SDL_mixer/fluidsynth.h b/apps/plugins/sdl/SDL_mixer/fluidsynth.h new file mode 100644 index 0000000000..47538bbc2b --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/fluidsynth.h @@ -0,0 +1,51 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + James Le Cuirot + chewi@aura-online.co.uk +*/ + +#ifndef _FLUIDSYNTH_H_ +#define _FLUIDSYNTH_H_ + +#ifdef USE_FLUIDSYNTH_MIDI + +#include "dynamic_fluidsynth.h" +#include <SDL_rwops.h> +#include <SDL_audio.h> + +typedef struct { + SDL_AudioCVT convert; + fluid_synth_t *synth; + fluid_player_t* player; +} FluidSynthMidiSong; + +int fluidsynth_init(SDL_AudioSpec *mixer); +FluidSynthMidiSong *fluidsynth_loadsong_RW(SDL_RWops *rw, int freerw); +void fluidsynth_freesong(FluidSynthMidiSong *song); +void fluidsynth_start(FluidSynthMidiSong *song); +void fluidsynth_stop(FluidSynthMidiSong *song); +int fluidsynth_active(FluidSynthMidiSong *song); +void fluidsynth_setvolume(FluidSynthMidiSong *song, int volume); +int fluidsynth_playsome(FluidSynthMidiSong *song, void *stream, int len); + +#endif /* USE_FLUIDSYNTH_MIDI */ + +#endif /* _FLUIDSYNTH_H_ */ diff --git a/apps/plugins/sdl/SDL_mixer/load_aiff.c b/apps/plugins/sdl/SDL_mixer/load_aiff.c new file mode 100644 index 0000000000..ac71e425ff --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/load_aiff.c @@ -0,0 +1,247 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + This is the source needed to decode an AIFF file into a waveform. + It's pretty straightforward once you get going. The only + externally-callable function is Mix_LoadAIFF_RW(), which is meant to + act as identically to SDL_LoadWAV_RW() as possible. + + This file by Torbjörn Andersson (torbjorn.andersson@eurotime.se) + 8SVX file support added by Marc Le Douarain (mavati@club-internet.fr) + in december 2002. +*/ + +/* $Id$ */ + +#include "SDL_endian.h" +#include "SDL_mixer.h" +#include "load_aiff.h" + +/*********************************************/ +/* Define values for AIFF (IFF audio) format */ +/*********************************************/ +#define FORM 0x4d524f46 /* "FORM" */ + +#define AIFF 0x46464941 /* "AIFF" */ +#define SSND 0x444e5353 /* "SSND" */ +#define COMM 0x4d4d4f43 /* "COMM" */ + +#define _8SVX 0x58565338 /* "8SVX" */ +#define VHDR 0x52444856 /* "VHDR" */ +#define BODY 0x59444F42 /* "BODY" */ + +/* This function was taken from libsndfile. I don't pretend to fully + * understand it. + */ + +static Uint32 SANE_to_Uint32 (Uint8 *sanebuf) +{ + /* Is the frequency outside of what we can represent with Uint32? */ + if ( (sanebuf[0] & 0x80) || (sanebuf[0] <= 0x3F) || (sanebuf[0] > 0x40) + || (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C) ) + return 0; + + return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7) + | (sanebuf[5] >> 1)) >> (29 - sanebuf[1]); +} + +/* This function is based on SDL_LoadWAV_RW(). */ + +SDL_AudioSpec *Mix_LoadAIFF_RW (SDL_RWops *src, int freesrc, + SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) +{ + int was_error; + int found_SSND; + int found_COMM; + int found_VHDR; + int found_BODY; + long start = 0; + + Uint32 chunk_type; + Uint32 chunk_length; + long next_chunk; + + /* AIFF magic header */ + Uint32 FORMchunk; + Uint32 AIFFmagic; + + /* SSND chunk */ + Uint32 offset; + Uint32 blocksize; + + /* COMM format chunk */ + Uint16 channels = 0; + Uint32 numsamples = 0; + Uint16 samplesize = 0; + Uint8 sane_freq[10]; + Uint32 frequency = 0; + + /* Make sure we are passed a valid data source */ + was_error = 0; + if ( src == NULL ) { + was_error = 1; + goto done; + } + + FORMchunk = SDL_ReadLE32(src); + chunk_length = SDL_ReadBE32(src); + if ( chunk_length == AIFF ) { /* The FORMchunk has already been read */ + AIFFmagic = chunk_length; + chunk_length = FORMchunk; + FORMchunk = FORM; + } else { + AIFFmagic = SDL_ReadLE32(src); + } + if ( (FORMchunk != FORM) || ( (AIFFmagic != AIFF) && (AIFFmagic != _8SVX) ) ) { + SDL_SetError("Unrecognized file type (not AIFF nor 8SVX)"); + was_error = 1; + goto done; + } + + /* TODO: Better santity-checking. */ + + found_SSND = 0; + found_COMM = 0; + found_VHDR = 0; + found_BODY = 0; + + do { + chunk_type = SDL_ReadLE32(src); + chunk_length = SDL_ReadBE32(src); + next_chunk = SDL_RWtell(src) + chunk_length; + /* Paranoia to avoid infinite loops */ + if (chunk_length == 0) + break; + + switch (chunk_type) { + case SSND: + found_SSND = 1; + offset = SDL_ReadBE32(src); + blocksize = SDL_ReadBE32(src); + start = SDL_RWtell(src) + offset; + break; + + case COMM: + found_COMM = 1; + channels = SDL_ReadBE16(src); + numsamples = SDL_ReadBE32(src); + samplesize = SDL_ReadBE16(src); + SDL_RWread(src, sane_freq, sizeof(sane_freq), 1); + frequency = SANE_to_Uint32(sane_freq); + if (frequency == 0) { + SDL_SetError("Bad AIFF sample frequency"); + was_error = 1; + goto done; + } + break; + + case VHDR: + found_VHDR = 1; + SDL_ReadBE32(src); + SDL_ReadBE32(src); + SDL_ReadBE32(src); + frequency = SDL_ReadBE16(src); + channels = 1; + samplesize = 8; + break; + + case BODY: + found_BODY = 1; + numsamples = chunk_length; + start = SDL_RWtell(src); + break; + + default: + break; + } + /* a 0 pad byte can be stored for any odd-length chunk */ + if (chunk_length&1) + next_chunk++; + } while ( ( ( (AIFFmagic == AIFF) && ( !found_SSND || !found_COMM ) ) + || ( (AIFFmagic == _8SVX ) && ( !found_VHDR || !found_BODY ) ) ) + && SDL_RWseek(src, next_chunk, RW_SEEK_SET) != 1 ); + + if ( (AIFFmagic == AIFF) && !found_SSND ) { + SDL_SetError("Bad AIFF (no SSND chunk)"); + was_error = 1; + goto done; + } + + if ( (AIFFmagic == AIFF) && !found_COMM ) { + SDL_SetError("Bad AIFF (no COMM chunk)"); + was_error = 1; + goto done; + } + + if ( (AIFFmagic == _8SVX) && !found_VHDR ) { + SDL_SetError("Bad 8SVX (no VHDR chunk)"); + was_error = 1; + goto done; + } + + if ( (AIFFmagic == _8SVX) && !found_BODY ) { + SDL_SetError("Bad 8SVX (no BODY chunk)"); + was_error = 1; + goto done; + } + + /* Decode the audio data format */ + memset(spec, 0, sizeof(*spec)); + spec->freq = frequency; + switch (samplesize) { + case 8: + spec->format = AUDIO_S8; + break; + case 16: + spec->format = AUDIO_S16MSB; + break; + default: + SDL_SetError("Unsupported AIFF samplesize"); + was_error = 1; + goto done; + } + spec->channels = (Uint8) channels; + spec->samples = 4096; /* Good default buffer size */ + + *audio_len = channels * numsamples * (samplesize / 8); + *audio_buf = (Uint8 *)SDL_malloc(*audio_len); + if ( *audio_buf == NULL ) { + SDL_SetError("Out of memory"); + return(NULL); + } + SDL_RWseek(src, start, RW_SEEK_SET); + if ( SDL_RWread(src, *audio_buf, *audio_len, 1) != 1 ) { + SDL_SetError("Unable to read audio data"); + return(NULL); + } + + /* Don't return a buffer that isn't a multiple of samplesize */ + *audio_len &= ~((samplesize / 8) - 1); + +done: + if ( freesrc && src ) { + SDL_RWclose(src); + } + if ( was_error ) { + spec = NULL; + } + return(spec); +} + diff --git a/apps/plugins/sdl/SDL_mixer/load_aiff.h b/apps/plugins/sdl/SDL_mixer/load_aiff.h new file mode 100644 index 0000000000..ed55d36440 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/load_aiff.h @@ -0,0 +1,31 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2009 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 + + This is the source needed to decode an AIFF file into a waveform. + It's pretty straightforward once you get going. The only + externally-callable function is Mix_LoadAIFF_RW(), which is meant to + act as identically to SDL_LoadWAV_RW() as possible. + + This file by Torbjörn Andersson (torbjorn.andersson@eurotime.se) +*/ + +/* $Id$ */ + +/* Don't call this directly; use Mix_LoadWAV_RW() for now. */ +SDL_AudioSpec *Mix_LoadAIFF_RW (SDL_RWops *src, int freesrc, + SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len); diff --git a/apps/plugins/sdl/SDL_mixer/load_flac.c b/apps/plugins/sdl/SDL_mixer/load_flac.c new file mode 100644 index 0000000000..151de63f6f --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/load_flac.c @@ -0,0 +1,338 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + This is the source needed to decode a FLAC into a waveform. + ~ Austen Dicken (admin@cvpcs.org). +*/ + +#ifdef FLAC_MUSIC + +#include "SDL_mutex.h" +#include "SDL_endian.h" +#include "SDL_timer.h" + +#include "SDL_mixer.h" +#include "dynamic_flac.h" +#include "load_flac.h" + +#include <FLAC/stream_decoder.h> + +typedef struct { + SDL_RWops* sdl_src; + SDL_AudioSpec* sdl_spec; + Uint8** sdl_audio_buf; + Uint32* sdl_audio_len; + int sdl_audio_read; + FLAC__uint64 flac_total_samples; + unsigned flac_bps; +} FLAC_SDL_Data; + +static FLAC__StreamDecoderReadStatus flac_read_load_cb( + const FLAC__StreamDecoder *decoder, + FLAC__byte buffer[], + size_t *bytes, + void *client_data) +{ + // make sure there is something to be reading + if (*bytes > 0) { + FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data; + + *bytes = SDL_RWread (data->sdl_src, buffer, sizeof (FLAC__byte), + *bytes); + + if(*bytes < 0) { // error in read + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } + else if(*bytes == 0) { // no data was read (EOF) + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + } + else { // data was read, continue + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } + } + else { + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } +} + +static FLAC__StreamDecoderSeekStatus flac_seek_load_cb( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 absolute_byte_offset, + void *client_data) +{ + FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data; + + if (SDL_RWseek (data->sdl_src, absolute_byte_offset, RW_SEEK_SET) < 0) { + return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + } + else { + return FLAC__STREAM_DECODER_SEEK_STATUS_OK; + } +} + +static FLAC__StreamDecoderTellStatus flac_tell_load_cb( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 *absolute_byte_offset, + void *client_data) +{ + FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data; + + int pos = SDL_RWtell (data->sdl_src); + + if (pos < 0) { + return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + } + else { + *absolute_byte_offset = (FLAC__uint64)pos; + return FLAC__STREAM_DECODER_TELL_STATUS_OK; + } +} + +static FLAC__StreamDecoderLengthStatus flac_length_load_cb( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 *stream_length, + void *client_data) +{ + FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data; + + int pos = SDL_RWtell (data->sdl_src); + int length = SDL_RWseek (data->sdl_src, 0, RW_SEEK_END); + + if (SDL_RWseek (data->sdl_src, pos, RW_SEEK_SET) != pos || length < 0) { + /* there was an error attempting to return the stream to the original + * position, or the length was invalid. */ + return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + } + else { + *stream_length = (FLAC__uint64)length; + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + } +} + +static FLAC__bool flac_eof_load_cb(const FLAC__StreamDecoder *decoder, + void *client_data) +{ + FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data; + + int pos = SDL_RWtell (data->sdl_src); + int end = SDL_RWseek (data->sdl_src, 0, RW_SEEK_END); + + // was the original position equal to the end (a.k.a. the seek didn't move)? + if (pos == end) { + // must be EOF + return true; + } + else { + // not EOF, return to the original position + SDL_RWseek (data->sdl_src, pos, RW_SEEK_SET); + + return false; + } +} + +static FLAC__StreamDecoderWriteStatus flac_write_load_cb( + const FLAC__StreamDecoder *decoder, + const FLAC__Frame *frame, + const FLAC__int32 *const buffer[], + void *client_data) +{ + FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data; + size_t i; + Uint8 *buf; + + if (data->flac_total_samples == 0) { + SDL_SetError ("Given FLAC file does not specify its sample count."); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + + if (data->sdl_spec->channels != 2 || data->flac_bps != 16) { + SDL_SetError ("Current FLAC support is only for 16 bit Stereo files."); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + + // check if it is the first audio frame so we can initialize the output + // buffer + if (frame->header.number.sample_number == 0) { + *(data->sdl_audio_len) = data->sdl_spec->size; + data->sdl_audio_read = 0; + *(data->sdl_audio_buf) = SDL_malloc (*(data->sdl_audio_len)); + + if (*(data->sdl_audio_buf) == NULL) { + SDL_SetError + ("Unable to allocate memory to store the FLAC stream."); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + } + + buf = *(data->sdl_audio_buf); + + for (i = 0; i < frame->header.blocksize; i++) { + FLAC__int16 i16; + FLAC__uint16 ui16; + + i16 = (FLAC__int16)buffer[0][i]; + ui16 = (FLAC__uint16)i16; + + *(buf + (data->sdl_audio_read++)) = (char)(ui16); + *(buf + (data->sdl_audio_read++)) = (char)(ui16 >> 8); + + i16 = (FLAC__int16)buffer[1][i]; + ui16 = (FLAC__uint16)i16; + + *(buf + (data->sdl_audio_read++)) = (char)(ui16); + *(buf + (data->sdl_audio_read++)) = (char)(ui16 >> 8); + } + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +static void flac_metadata_load_cb( + const FLAC__StreamDecoder *decoder, + const FLAC__StreamMetadata *metadata, + void *client_data) +{ + FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data; + FLAC__uint64 total_samples; + unsigned bps; + + if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { + // save the metadata right now for use later on + *(data->sdl_audio_buf) = NULL; + *(data->sdl_audio_len) = 0; + memset (data->sdl_spec, '\0', sizeof (SDL_AudioSpec)); + + data->sdl_spec->format = AUDIO_S16; + data->sdl_spec->freq = (int)(metadata->data.stream_info.sample_rate); + data->sdl_spec->channels = (Uint8)(metadata->data.stream_info.channels); + data->sdl_spec->samples = 8192; /* buffer size */ + + total_samples = metadata->data.stream_info.total_samples; + bps = metadata->data.stream_info.bits_per_sample; + + data->sdl_spec->size = total_samples * data->sdl_spec->channels * + (bps / 8); + data->flac_total_samples = total_samples; + data->flac_bps = bps; + } +} + +static void flac_error_load_cb( + const FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderErrorStatus status, + void *client_data) +{ + // print an SDL error based on the error status + switch (status) { + case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC: + SDL_SetError ("Error processing the FLAC file [LOST_SYNC]."); + break; + case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER: + SDL_SetError ("Error processing the FLAC file [BAD_HEADER]."); + break; + case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH: + SDL_SetError ("Error processing the FLAC file [CRC_MISMATCH]."); + break; + case FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM: + SDL_SetError ("Error processing the FLAC file [UNPARSEABLE]."); + break; + default: + SDL_SetError ("Error processing the FLAC file [UNKNOWN]."); + break; + } +} + +/* don't call this directly; use Mix_LoadWAV_RW() for now. */ +SDL_AudioSpec *Mix_LoadFLAC_RW (SDL_RWops *src, int freesrc, + SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) +{ + FLAC__StreamDecoder *decoder = 0; + FLAC__StreamDecoderInitStatus init_status; + int was_error = 1; + int was_init = 0; + Uint32 samplesize; + + // create the client data passing information + FLAC_SDL_Data* client_data; + client_data = (FLAC_SDL_Data *)SDL_malloc (sizeof (FLAC_SDL_Data)); + + if ((!src) || (!audio_buf) || (!audio_len)) /* sanity checks. */ + goto done; + + if (!Mix_Init(MIX_INIT_FLAC)) + goto done; + + if ((decoder = flac.FLAC__stream_decoder_new ()) == NULL) { + SDL_SetError ("Unable to allocate FLAC decoder."); + goto done; + } + + init_status = flac.FLAC__stream_decoder_init_stream (decoder, + flac_read_load_cb, flac_seek_load_cb, + flac_tell_load_cb, flac_length_load_cb, + flac_eof_load_cb, flac_write_load_cb, + flac_metadata_load_cb, flac_error_load_cb, + client_data); + + if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) { + SDL_SetError ("Unable to initialize FLAC stream decoder."); + goto done; + } + + was_init = 1; + + client_data->sdl_src = src; + client_data->sdl_spec = spec; + client_data->sdl_audio_buf = audio_buf; + client_data->sdl_audio_len = audio_len; + + if (!flac.FLAC__stream_decoder_process_until_end_of_stream (decoder)) { + SDL_SetError ("Unable to process FLAC file."); + goto done; + } + + was_error = 0; + + /* Don't return a buffer that isn't a multiple of samplesize */ + samplesize = ((spec->format & 0xFF) / 8) * spec->channels; + *audio_len &= ~(samplesize - 1); + +done: + if (was_init && decoder) { + flac.FLAC__stream_decoder_finish (decoder); + } + + if (decoder) { + flac.FLAC__stream_decoder_delete (decoder); + } + + if (src) { + if (freesrc) + SDL_RWclose (src); + else + SDL_RWseek (src, 0, RW_SEEK_SET); + } + + if (was_error) + spec = NULL; + + return spec; +} + +#endif // FLAC_MUSIC diff --git a/apps/plugins/sdl/SDL_mixer/load_flac.h b/apps/plugins/sdl/SDL_mixer/load_flac.h new file mode 100644 index 0000000000..63fcd4bcd0 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/load_flac.h @@ -0,0 +1,31 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + This is the source needed to decode a FLAC into a waveform. + ~ Austen Dicken (admin@cvpcs.org). +*/ + +/* $Id: $ */ + +#ifdef FLAC_MUSIC +/* Don't call this directly; use Mix_LoadWAV_RW() for now. */ +SDL_AudioSpec *Mix_LoadFLAC_RW (SDL_RWops *src, int freesrc, + SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len); +#endif diff --git a/apps/plugins/sdl/SDL_mixer/load_ogg.c b/apps/plugins/sdl/SDL_mixer/load_ogg.c new file mode 100644 index 0000000000..829bbdbe8b --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/load_ogg.c @@ -0,0 +1,159 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + This is the source needed to decode an Ogg Vorbis into a waveform. + This file by Vaclav Slavik (vaclav.slavik@matfyz.cz). +*/ + +/* $Id$ */ + +#ifdef OGG_MUSIC + +#include "SDL_mutex.h" +#include "SDL_endian.h" +#include "SDL_timer.h" + +#include "SDL_mixer.h" +#include "dynamic_ogg.h" +#include "load_ogg.h" + +static size_t sdl_read_func(void *ptr, size_t size, size_t nmemb, void *datasource) +{ + return SDL_RWread((SDL_RWops*)datasource, ptr, size, nmemb); +} + +static int sdl_seek_func(void *datasource, ogg_int64_t offset, int whence) +{ + return SDL_RWseek((SDL_RWops*)datasource, (int)offset, whence); +} + +static int sdl_close_func_freesrc(void *datasource) +{ + return SDL_RWclose((SDL_RWops*)datasource); +} + +static int sdl_close_func_nofreesrc(void *datasource) +{ + return SDL_RWseek((SDL_RWops*)datasource, 0, RW_SEEK_SET); +} + +static long sdl_tell_func(void *datasource) +{ + return SDL_RWtell((SDL_RWops*)datasource); +} + + +/* don't call this directly; use Mix_LoadWAV_RW() for now. */ +SDL_AudioSpec *Mix_LoadOGG_RW (SDL_RWops *src, int freesrc, + SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) +{ + OggVorbis_File vf; + ov_callbacks callbacks; + vorbis_info *info; + Uint8 *buf; + int bitstream = -1; + long samplesize; + long samples; + int read, to_read; + int must_close = 1; + int was_error = 1; + + if ( (!src) || (!audio_buf) || (!audio_len) ) /* sanity checks. */ + goto done; + + if ( !Mix_Init(MIX_INIT_OGG) ) + goto done; + + callbacks.read_func = sdl_read_func; + callbacks.seek_func = sdl_seek_func; + callbacks.tell_func = sdl_tell_func; + callbacks.close_func = freesrc ? + sdl_close_func_freesrc : sdl_close_func_nofreesrc; + + if (vorbis.ov_open_callbacks(src, &vf, NULL, 0, callbacks) != 0) + { + SDL_SetError("OGG bitstream is not valid Vorbis stream!"); + goto done; + } + + must_close = 0; + + info = vorbis.ov_info(&vf, -1); + + *audio_buf = NULL; + *audio_len = 0; + memset(spec, '\0', sizeof (SDL_AudioSpec)); + + spec->format = AUDIO_S16; + spec->channels = info->channels; + spec->freq = info->rate; + spec->samples = 4096; /* buffer size */ + + samples = (long)vorbis.ov_pcm_total(&vf, -1); + + *audio_len = spec->size = samples * spec->channels * 2; + *audio_buf = SDL_malloc(*audio_len); + if (*audio_buf == NULL) + goto done; + + buf = *audio_buf; + to_read = *audio_len; +#ifdef OGG_USE_TREMOR + for (read = vorbis.ov_read(&vf, (char *)buf, to_read, &bitstream); + read > 0; + read = vorbis.ov_read(&vf, (char *)buf, to_read, &bitstream)) +#else + for (read = vorbis.ov_read(&vf, (char *)buf, to_read, 0/*LE*/, 2/*16bit*/, 1/*signed*/, &bitstream); + read > 0; + read = vorbis.ov_read(&vf, (char *)buf, to_read, 0, 2, 1, &bitstream)) +#endif + { + if (read == OV_HOLE || read == OV_EBADLINK) + break; /* error */ + + to_read -= read; + buf += read; + } + + vorbis.ov_clear(&vf); + was_error = 0; + + /* Don't return a buffer that isn't a multiple of samplesize */ + samplesize = ((spec->format & 0xFF)/8)*spec->channels; + *audio_len &= ~(samplesize-1); + +done: + if (src && must_close) + { + if (freesrc) + SDL_RWclose(src); + else + SDL_RWseek(src, 0, RW_SEEK_SET); + } + + if ( was_error ) + spec = NULL; + + return(spec); +} /* Mix_LoadOGG_RW */ + +/* end of load_ogg.c ... */ + +#endif diff --git a/apps/plugins/sdl/SDL_mixer/load_ogg.h b/apps/plugins/sdl/SDL_mixer/load_ogg.h new file mode 100644 index 0000000000..e63b04f7b3 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/load_ogg.h @@ -0,0 +1,31 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + This is the source needed to decode an Ogg Vorbis into a waveform. + This file by Vaclav Slavik (vaclav.slavik@matfyz.cz). +*/ + +/* $Id$ */ + +#ifdef OGG_MUSIC +/* Don't call this directly; use Mix_LoadWAV_RW() for now. */ +SDL_AudioSpec *Mix_LoadOGG_RW (SDL_RWops *src, int freesrc, + SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len); +#endif diff --git a/apps/plugins/sdl/SDL_mixer/load_voc.c b/apps/plugins/sdl/SDL_mixer/load_voc.c new file mode 100644 index 0000000000..2e7798e222 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/load_voc.c @@ -0,0 +1,458 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + This is the source needed to decode a Creative Labs VOC file into a + waveform. It's pretty straightforward once you get going. The only + externally-callable function is Mix_LoadVOC_RW(), which is meant to + act as identically to SDL_LoadWAV_RW() as possible. + + This file by Ryan C. Gordon (icculus@icculus.org). + + Heavily borrowed from sox v12.17.1's voc.c. + (http://www.freshmeat.net/projects/sox/) +*/ + +/* $Id$ */ + +#include "SDL_mutex.h" +#include "SDL_endian.h" +#include "SDL_timer.h" + +#include "SDL_mixer.h" +#include "load_voc.h" + +/* Private data for VOC file */ +typedef struct vocstuff { + Uint32 rest; /* bytes remaining in current block */ + Uint32 rate; /* rate code (byte) of this chunk */ + int silent; /* sound or silence? */ + Uint32 srate; /* rate code (byte) of silence */ + Uint32 blockseek; /* start of current output block */ + Uint32 samples; /* number of samples output */ + Uint32 size; /* word length of data */ + Uint8 channels; /* number of sound channels */ + int has_extended; /* Has an extended block been read? */ +} vs_t; + +/* Size field */ +/* SJB: note that the 1st 3 are sometimes used as sizeof(type) */ +#define ST_SIZE_BYTE 1 +#define ST_SIZE_8BIT 1 +#define ST_SIZE_WORD 2 +#define ST_SIZE_16BIT 2 +#define ST_SIZE_DWORD 4 +#define ST_SIZE_32BIT 4 +#define ST_SIZE_FLOAT 5 +#define ST_SIZE_DOUBLE 6 +#define ST_SIZE_IEEE 7 /* IEEE 80-bit floats. */ + +/* Style field */ +#define ST_ENCODING_UNSIGNED 1 /* unsigned linear: Sound Blaster */ +#define ST_ENCODING_SIGN2 2 /* signed linear 2's comp: Mac */ +#define ST_ENCODING_ULAW 3 /* U-law signed logs: US telephony, SPARC */ +#define ST_ENCODING_ALAW 4 /* A-law signed logs: non-US telephony */ +#define ST_ENCODING_ADPCM 5 /* Compressed PCM */ +#define ST_ENCODING_IMA_ADPCM 6 /* Compressed PCM */ +#define ST_ENCODING_GSM 7 /* GSM 6.10 33-byte frame lossy compression */ + +#define VOC_TERM 0 +#define VOC_DATA 1 +#define VOC_CONT 2 +#define VOC_SILENCE 3 +#define VOC_MARKER 4 +#define VOC_TEXT 5 +#define VOC_LOOP 6 +#define VOC_LOOPEND 7 +#define VOC_EXTENDED 8 +#define VOC_DATA_16 9 + + +static int voc_check_header(SDL_RWops *src) +{ + /* VOC magic header */ + Uint8 signature[20]; /* "Creative Voice File\032" */ + Uint16 datablockofs; + + SDL_RWseek(src, 0, RW_SEEK_SET); + + if (SDL_RWread(src, signature, sizeof (signature), 1) != 1) + return(0); + + if (memcmp(signature, "Creative Voice File\032", sizeof (signature)) != 0) { + SDL_SetError("Unrecognized file type (not VOC)"); + return(0); + } + + /* get the offset where the first datablock is located */ + if (SDL_RWread(src, &datablockofs, sizeof (Uint16), 1) != 1) + return(0); + + datablockofs = SDL_SwapLE16(datablockofs); + + if (SDL_RWseek(src, datablockofs, RW_SEEK_SET) != datablockofs) + return(0); + + return(1); /* success! */ +} /* voc_check_header */ + + +/* Read next block header, save info, leave position at start of data */ +static int voc_get_block(SDL_RWops *src, vs_t *v, SDL_AudioSpec *spec) +{ + Uint8 bits24[3]; + Uint8 uc, block; + Uint32 sblen; + Uint16 new_rate_short; + Uint32 new_rate_long; + Uint8 trash[6]; + Uint16 period; + unsigned int i; + + v->silent = 0; + while (v->rest == 0) + { + if (SDL_RWread(src, &block, sizeof (block), 1) != 1) + return 1; /* assume that's the end of the file. */ + + if (block == VOC_TERM) + return 1; + + if (SDL_RWread(src, bits24, sizeof (bits24), 1) != 1) + return 1; /* assume that's the end of the file. */ + + /* Size is an 24-bit value. Ugh. */ + sblen = ( (bits24[0]) | (bits24[1] << 8) | (bits24[2] << 16) ); + + switch(block) + { + case VOC_DATA: + if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1) + return 0; + + /* When DATA block preceeded by an EXTENDED */ + /* block, the DATA blocks rate value is invalid */ + if (!v->has_extended) + { + if (uc == 0) + { + SDL_SetError("VOC Sample rate is zero?"); + return 0; + } + + if ((v->rate != -1) && (uc != v->rate)) + { + SDL_SetError("VOC sample rate codes differ"); + return 0; + } + + v->rate = uc; + spec->freq = (Uint16)(1000000.0/(256 - v->rate)); + v->channels = 1; + } + + if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1) + return 0; + + if (uc != 0) + { + SDL_SetError("VOC decoder only interprets 8-bit data"); + return 0; + } + + v->has_extended = 0; + v->rest = sblen - 2; + v->size = ST_SIZE_BYTE; + return 1; + + case VOC_DATA_16: + if (SDL_RWread(src, &new_rate_long, sizeof (new_rate_long), 1) != 1) + return 0; + new_rate_long = SDL_SwapLE32(new_rate_long); + if (new_rate_long == 0) + { + SDL_SetError("VOC Sample rate is zero?"); + return 0; + } + if ((v->rate != -1) && (new_rate_long != v->rate)) + { + SDL_SetError("VOC sample rate codes differ"); + return 0; + } + v->rate = new_rate_long; + spec->freq = new_rate_long; + + if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1) + return 0; + + switch (uc) + { + case 8: v->size = ST_SIZE_BYTE; break; + case 16: v->size = ST_SIZE_WORD; break; + default: + SDL_SetError("VOC with unknown data size"); + return 0; + } + + if (SDL_RWread(src, &v->channels, sizeof (Uint8), 1) != 1) + return 0; + + if (SDL_RWread(src, trash, sizeof (Uint8), 6) != 6) + return 0; + + v->rest = sblen - 12; + return 1; + + case VOC_CONT: + v->rest = sblen; + return 1; + + case VOC_SILENCE: + if (SDL_RWread(src, &period, sizeof (period), 1) != 1) + return 0; + period = SDL_SwapLE16(period); + + if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1) + return 0; + if (uc == 0) + { + SDL_SetError("VOC silence sample rate is zero"); + return 0; + } + + /* + * Some silence-packed files have gratuitously + * different sample rate codes in silence. + * Adjust period. + */ + if ((v->rate != -1) && (uc != v->rate)) + period = (Uint16)((period * (256 - uc))/(256 - v->rate)); + else + v->rate = uc; + v->rest = period; + v->silent = 1; + return 1; + + case VOC_LOOP: + case VOC_LOOPEND: + for(i = 0; i < sblen; i++) /* skip repeat loops. */ + { + if (SDL_RWread(src, trash, sizeof (Uint8), 1) != 1) + return 0; + } + break; + + case VOC_EXTENDED: + /* An Extended block is followed by a data block */ + /* Set this byte so we know to use the rate */ + /* value from the extended block and not the */ + /* data block. */ + v->has_extended = 1; + if (SDL_RWread(src, &new_rate_short, sizeof (new_rate_short), 1) != 1) + return 0; + new_rate_short = SDL_SwapLE16(new_rate_short); + if (new_rate_short == 0) + { + SDL_SetError("VOC sample rate is zero"); + return 0; + } + if ((v->rate != -1) && (new_rate_short != v->rate)) + { + SDL_SetError("VOC sample rate codes differ"); + return 0; + } + v->rate = new_rate_short; + + if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1) + return 0; + + if (uc != 0) + { + SDL_SetError("VOC decoder only interprets 8-bit data"); + return 0; + } + + if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1) + return 0; + + if (uc) + spec->channels = 2; /* Stereo */ + /* Needed number of channels before finishing + compute for rate */ + spec->freq = (256000000L/(65536L - v->rate))/spec->channels; + /* An extended block must be followed by a data */ + /* block to be valid so loop back to top so it */ + /* can be grabed. */ + continue; + + case VOC_MARKER: + if (SDL_RWread(src, trash, sizeof (Uint8), 2) != 2) + return 0; + + /* Falling! Falling! */ + + default: /* text block or other krapola. */ + for(i = 0; i < sblen; i++) + { + if (SDL_RWread(src, &trash, sizeof (Uint8), 1) != 1) + return 0; + } + + if (block == VOC_TEXT) + continue; /* get next block */ + } + } + + return 1; +} + + +static int voc_read(SDL_RWops *src, vs_t *v, Uint8 *buf, SDL_AudioSpec *spec) +{ + int done = 0; + Uint8 silence = 0x80; + + if (v->rest == 0) + { + if (!voc_get_block(src, v, spec)) + return 0; + } + + if (v->rest == 0) + return 0; + + if (v->silent) + { + if (v->size == ST_SIZE_WORD) + silence = 0x00; + + /* Fill in silence */ + memset(buf, silence, v->rest); + done = v->rest; + v->rest = 0; + } + + else + { + done = SDL_RWread(src, buf, 1, v->rest); + v->rest -= done; + if (v->size == ST_SIZE_WORD) + { + #if (SDL_BYTEORDER == SDL_BIG_ENDIAN) + Uint16 *samples = (Uint16 *)buf; + for (; v->rest > 0; v->rest -= 2) + { + *samples = SDL_SwapLE16(*samples); + samples++; + } + #endif + done >>= 1; + } + } + + return done; +} /* voc_read */ + + +/* don't call this directly; use Mix_LoadWAV_RW() for now. */ +SDL_AudioSpec *Mix_LoadVOC_RW (SDL_RWops *src, int freesrc, + SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) +{ + vs_t v; + int was_error = 1; + int samplesize; + Uint8 *fillptr; + void *ptr; + + if ( (!src) || (!audio_buf) || (!audio_len) ) /* sanity checks. */ + goto done; + + if ( !voc_check_header(src) ) + goto done; + + v.rate = -1; + v.rest = 0; + v.has_extended = 0; + *audio_buf = NULL; + *audio_len = 0; + memset(spec, '\0', sizeof (SDL_AudioSpec)); + + if (!voc_get_block(src, &v, spec)) + goto done; + + if (v.rate == -1) + { + SDL_SetError("VOC data had no sound!"); + goto done; + } + + spec->format = ((v.size == ST_SIZE_WORD) ? AUDIO_S16 : AUDIO_U8); + if (spec->channels == 0) + spec->channels = v.channels; + + *audio_len = v.rest; + *audio_buf = SDL_malloc(v.rest); + if (*audio_buf == NULL) + goto done; + + fillptr = *audio_buf; + + while (voc_read(src, &v, fillptr, spec) > 0) + { + if (!voc_get_block(src, &v, spec)) + goto done; + + *audio_len += v.rest; + ptr = SDL_realloc(*audio_buf, *audio_len); + if (ptr == NULL) + { + SDL_free(*audio_buf); + *audio_buf = NULL; + *audio_len = 0; + goto done; + } + + *audio_buf = ptr; + fillptr = ((Uint8 *) ptr) + (*audio_len - v.rest); + } + + spec->samples = (Uint16)(*audio_len / v.size); + + was_error = 0; /* success, baby! */ + + /* Don't return a buffer that isn't a multiple of samplesize */ + samplesize = ((spec->format & 0xFF)/8)*spec->channels; + *audio_len &= ~(samplesize-1); + +done: + if (src) + { + if (freesrc) + SDL_RWclose(src); + else + SDL_RWseek(src, 0, RW_SEEK_SET); + } + + if ( was_error ) + spec = NULL; + + return(spec); +} /* Mix_LoadVOC_RW */ + +/* end of load_voc.c ... */ diff --git a/apps/plugins/sdl/SDL_mixer/load_voc.h b/apps/plugins/sdl/SDL_mixer/load_voc.h new file mode 100644 index 0000000000..20ae23ca40 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/load_voc.h @@ -0,0 +1,36 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + This is the source needed to decode a Creative Labs VOC file into a + waveform. It's pretty straightforward once you get going. The only + externally-callable function is Mix_LoadVOC_RW(), which is meant to + act as identically to SDL_LoadWAV_RW() as possible. + + This file by Ryan C. Gordon (icculus@icculus.org). + + Heavily borrowed from sox v12.17.1's voc.c. + (http://www.freshmeat.net/projects/sox/) +*/ + +/* $Id$ */ + +/* Don't call this directly; use Mix_LoadWAV_RW() for now. */ +SDL_AudioSpec *Mix_LoadVOC_RW (SDL_RWops *src, int freesrc, + SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len); diff --git a/apps/plugins/sdl/SDL_mixer/mixer.c b/apps/plugins/sdl/SDL_mixer/mixer.c new file mode 100644 index 0000000000..a24a0e7c1d --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/mixer.c @@ -0,0 +1,1484 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id$ */ + +#include "SDL_mutex.h" +#include "SDL_endian.h" +#include "SDL_timer.h" + +#include "SDL_mixer.h" +#include "load_aiff.h" +#include "load_voc.h" +#include "load_ogg.h" +#include "load_flac.h" +#include "dynamic_flac.h" +#include "dynamic_mod.h" +#include "dynamic_mp3.h" +#include "dynamic_ogg.h" + +#define __MIX_INTERNAL_EFFECT__ +#include "effects_internal.h" + +/* Magic numbers for various audio file formats */ +#define RIFF 0x46464952 /* "RIFF" */ +#define WAVE 0x45564157 /* "WAVE" */ +#define FORM 0x4d524f46 /* "FORM" */ +#define OGGS 0x5367674f /* "OggS" */ +#define CREA 0x61657243 /* "Crea" */ +#define FLAC 0x43614C66 /* "fLaC" */ + +static int audio_opened = 0; +static SDL_AudioSpec mixer; + +typedef struct _Mix_effectinfo +{ + Mix_EffectFunc_t callback; + Mix_EffectDone_t done_callback; + void *udata; + struct _Mix_effectinfo *next; +} effect_info; + +static struct _Mix_Channel { + Mix_Chunk *chunk; + int playing; + int paused; + Uint8 *samples; + int volume; + int looping; + int tag; + Uint32 expire; + Uint32 start_time; + Mix_Fading fading; + int fade_volume; + int fade_volume_reset; + Uint32 fade_length; + Uint32 ticks_fade; + effect_info *effects; +} *mix_channel = NULL; + +static effect_info *posteffects = NULL; + +static int num_channels; +static int reserved_channels = 0; + + +/* Support for hooking into the mixer callback system */ +static void (*mix_postmix)(void *udata, Uint8 *stream, int len) = NULL; +static void *mix_postmix_data = NULL; + +/* rcg07062001 callback to alert when channels are done playing. */ +static void (*channel_done_callback)(int channel) = NULL; + +/* Music function declarations */ +extern int open_music(SDL_AudioSpec *mixer); +extern void close_music(void); + +/* Support for user defined music functions, plus the default one */ +extern int volatile music_active; +extern void music_mixer(void *udata, Uint8 *stream, int len); +static void (*mix_music)(void *udata, Uint8 *stream, int len) = music_mixer; +static void *music_data = NULL; + +/* rcg06042009 report available decoders at runtime. */ +static const char **chunk_decoders = NULL; +static int num_decoders = 0; + +/* Semicolon-separated SoundFont paths */ +#ifdef MID_MUSIC +extern char* soundfont_paths; +#endif + +int Mix_GetNumChunkDecoders(void) +{ + return(num_decoders); +} + +const char *Mix_GetChunkDecoder(int index) +{ + if ((index < 0) || (index >= num_decoders)) { + return NULL; + } + return(chunk_decoders[index]); +} + +static void add_chunk_decoder(const char *decoder) +{ + void *ptr = SDL_realloc(chunk_decoders, (num_decoders + 1) * sizeof (const char **)); + if (ptr == NULL) { + return; /* oh well, go on without it. */ + } + chunk_decoders = (const char **) ptr; + chunk_decoders[num_decoders++] = decoder; +} + +/* rcg06192001 get linked library's version. */ +const SDL_version *Mix_Linked_Version(void) +{ + static SDL_version linked_version; + SDL_MIXER_VERSION(&linked_version); + return(&linked_version); +} + +static int initialized = 0; + +int Mix_Init(int flags) +{ + int result = 0; + + if (flags & MIX_INIT_FLUIDSYNTH) { +#ifdef USE_FLUIDSYNTH_MIDI + if ((initialized & MIX_INIT_FLUIDSYNTH) || Mix_InitFluidSynth() == 0) { + result |= MIX_INIT_FLUIDSYNTH; + } +#else + Mix_SetError("Mixer not built with FluidSynth support"); +#endif + } + if (flags & MIX_INIT_FLAC) { +#ifdef FLAC_MUSIC + if ((initialized & MIX_INIT_FLAC) || Mix_InitFLAC() == 0) { + result |= MIX_INIT_FLAC; + } +#else + Mix_SetError("Mixer not built with FLAC support"); +#endif + } + if (flags & MIX_INIT_MOD) { +#ifdef MOD_MUSIC + if ((initialized & MIX_INIT_MOD) || Mix_InitMOD() == 0) { + result |= MIX_INIT_MOD; + } +#else + Mix_SetError("Mixer not built with MOD support"); +#endif + } + if (flags & MIX_INIT_MP3) { +#ifdef MP3_MUSIC + if ((initialized & MIX_INIT_MP3) || Mix_InitMP3() == 0) { + result |= MIX_INIT_MP3; + } +#else + Mix_SetError("Mixer not built with MP3 support"); +#endif + } + if (flags & MIX_INIT_OGG) { +#ifdef OGG_MUSIC + if ((initialized & MIX_INIT_OGG) || Mix_InitOgg() == 0) { + result |= MIX_INIT_OGG; + } +#else + Mix_SetError("Mixer not built with Ogg Vorbis support"); +#endif + } + initialized |= result; + + return (result); +} + +void Mix_Quit() +{ +#ifdef USE_FLUIDSYNTH_MIDI + if (initialized & MIX_INIT_FLUIDSYNTH) { + Mix_QuitFluidSynth(); + } +#endif +#ifdef FLAC_MUSIC + if (initialized & MIX_INIT_FLAC) { + Mix_QuitFLAC(); + } +#endif +#ifdef MOD_MUSIC + if (initialized & MIX_INIT_MOD) { + Mix_QuitMOD(); + } +#endif +#ifdef MP3_MUSIC + if (initialized & MIX_INIT_MP3) { + Mix_QuitMP3(); + } +#endif +#ifdef OGG_MUSIC + if (initialized & MIX_INIT_OGG) { + Mix_QuitOgg(); + } +#endif +#ifdef MID_MUSIC + if (soundfont_paths) { + SDL_free(soundfont_paths); + } +#endif + initialized = 0; +} + +static int _Mix_remove_all_effects(int channel, effect_info **e); + +/* + * rcg06122001 Cleanup effect callbacks. + * MAKE SURE SDL_LockAudio() is called before this (or you're in the + * audio callback). + */ +static void _Mix_channel_done_playing(int channel) +{ + if (channel_done_callback) { + channel_done_callback(channel); + } + + /* + * Call internal function directly, to avoid locking audio from + * inside audio callback. + */ + _Mix_remove_all_effects(channel, &mix_channel[channel].effects); +} + + +static void *Mix_DoEffects(int chan, void *snd, int len) +{ + int posteffect = (chan == MIX_CHANNEL_POST); + effect_info *e = ((posteffect) ? posteffects : mix_channel[chan].effects); + void *buf = snd; + + if (e != NULL) { /* are there any registered effects? */ + /* if this is the postmix, we can just overwrite the original. */ + if (!posteffect) { + buf = SDL_malloc(len); + if (buf == NULL) { + return(snd); + } + memcpy(buf, snd, len); + } + + for (; e != NULL; e = e->next) { + if (e->callback != NULL) { + e->callback(chan, buf, len, e->udata); + } + } + } + + /* be sure to SDL_free() the return value if != snd ... */ + return(buf); +} + + +/* Mixing function */ +static void mix_channels(void *udata, Uint8 *stream, int len) +{ + Uint8 *mix_input; + int i, mixable, volume = SDL_MIX_MAXVOLUME; + Uint32 sdl_ticks; + +#if SDL_VERSION_ATLEAST(1, 3, 0) + /* Need to initialize the stream in SDL 1.3+ */ + memset(stream, mixer.silence, len); +#endif + + /* Mix the music (must be done before the channels are added) */ + if ( music_active || (mix_music != music_mixer) ) { + mix_music(music_data, stream, len); + } + + /* Mix any playing channels... */ + sdl_ticks = SDL_GetTicks(); + for ( i=0; i<num_channels; ++i ) { + if( ! mix_channel[i].paused ) { + if ( mix_channel[i].expire > 0 && mix_channel[i].expire < sdl_ticks ) { + /* Expiration delay for that channel is reached */ + mix_channel[i].playing = 0; + mix_channel[i].looping = 0; + mix_channel[i].fading = MIX_NO_FADING; + mix_channel[i].expire = 0; + _Mix_channel_done_playing(i); + } else if ( mix_channel[i].fading != MIX_NO_FADING ) { + Uint32 ticks = sdl_ticks - mix_channel[i].ticks_fade; + if( ticks > mix_channel[i].fade_length ) { + Mix_Volume(i, mix_channel[i].fade_volume_reset); /* Restore the volume */ + if( mix_channel[i].fading == MIX_FADING_OUT ) { + mix_channel[i].playing = 0; + mix_channel[i].looping = 0; + mix_channel[i].expire = 0; + _Mix_channel_done_playing(i); + } + mix_channel[i].fading = MIX_NO_FADING; + } else { + if( mix_channel[i].fading == MIX_FADING_OUT ) { + Mix_Volume(i, (mix_channel[i].fade_volume * (mix_channel[i].fade_length-ticks)) + / mix_channel[i].fade_length ); + } else { + Mix_Volume(i, (mix_channel[i].fade_volume * ticks) / mix_channel[i].fade_length ); + } + } + } + if ( mix_channel[i].playing > 0 ) { + int index = 0; + int remaining = len; + while (mix_channel[i].playing > 0 && index < len) { + remaining = len - index; + volume = (mix_channel[i].volume*mix_channel[i].chunk->volume) / MIX_MAX_VOLUME; + mixable = mix_channel[i].playing; + if ( mixable > remaining ) { + mixable = remaining; + } + + mix_input = Mix_DoEffects(i, mix_channel[i].samples, mixable); + SDL_MixAudio(stream+index,mix_input,mixable,volume); + if (mix_input != mix_channel[i].samples) + SDL_free(mix_input); + + mix_channel[i].samples += mixable; + mix_channel[i].playing -= mixable; + index += mixable; + + /* rcg06072001 Alert app if channel is done playing. */ + if (!mix_channel[i].playing && !mix_channel[i].looping) { + _Mix_channel_done_playing(i); + } + } + + /* If looping the sample and we are at its end, make sure + we will still return a full buffer */ + while ( mix_channel[i].looping && index < len ) { + int alen = mix_channel[i].chunk->alen; + remaining = len - index; + if (remaining > alen) { + remaining = alen; + } + + mix_input = Mix_DoEffects(i, mix_channel[i].chunk->abuf, remaining); + SDL_MixAudio(stream+index, mix_input, remaining, volume); + if (mix_input != mix_channel[i].chunk->abuf) + SDL_free(mix_input); + + --mix_channel[i].looping; + mix_channel[i].samples = mix_channel[i].chunk->abuf + remaining; + mix_channel[i].playing = mix_channel[i].chunk->alen - remaining; + index += remaining; + } + if ( ! mix_channel[i].playing && mix_channel[i].looping ) { + --mix_channel[i].looping; + mix_channel[i].samples = mix_channel[i].chunk->abuf; + mix_channel[i].playing = mix_channel[i].chunk->alen; + } + } + } + } + + /* rcg06122001 run posteffects... */ + Mix_DoEffects(MIX_CHANNEL_POST, stream, len); + + if ( mix_postmix ) { + mix_postmix(mix_postmix_data, stream, len); + } +} + +#if 0 +static void PrintFormat(char *title, SDL_AudioSpec *fmt) +{ + printf("%s: %d bit %s audio (%s) at %u Hz\n", title, (fmt->format&0xFF), + (fmt->format&0x8000) ? "signed" : "unsigned", + (fmt->channels > 2) ? "surround" : + (fmt->channels > 1) ? "stereo" : "mono", fmt->freq); +} +#endif + + +/* Open the mixer with a certain desired audio format */ +int Mix_OpenAudio(int frequency, Uint16 format, int nchannels, int chunksize) +{ + int i; + SDL_AudioSpec desired; + + /* If the mixer is already opened, increment open count */ + if ( audio_opened ) { + if ( format == mixer.format && nchannels == mixer.channels ) { + ++audio_opened; + return(0); + } + while ( audio_opened ) { + Mix_CloseAudio(); + } + } + + /* Set the desired format and frequency */ + desired.freq = frequency; + desired.format = format; + desired.channels = nchannels; + desired.samples = chunksize; + desired.callback = mix_channels; + desired.userdata = NULL; + + /* Accept nearly any audio format */ + if ( SDL_OpenAudio(&desired, &mixer) < 0 ) { + return(-1); + } +#if 0 + PrintFormat("Audio device", &mixer); +#endif + + /* Initialize the music players */ + if ( open_music(&mixer) < 0 ) { + SDL_CloseAudio(); + return(-1); + } + + num_channels = MIX_CHANNELS; + mix_channel = (struct _Mix_Channel *) SDL_malloc(num_channels * sizeof(struct _Mix_Channel)); + + /* Clear out the audio channels */ + for ( i=0; i<num_channels; ++i ) { + mix_channel[i].chunk = NULL; + mix_channel[i].playing = 0; + mix_channel[i].looping = 0; + mix_channel[i].volume = SDL_MIX_MAXVOLUME; + mix_channel[i].fade_volume = SDL_MIX_MAXVOLUME; + mix_channel[i].fade_volume_reset = SDL_MIX_MAXVOLUME; + mix_channel[i].fading = MIX_NO_FADING; + mix_channel[i].tag = -1; + mix_channel[i].expire = 0; + mix_channel[i].effects = NULL; + mix_channel[i].paused = 0; + } + Mix_VolumeMusic(SDL_MIX_MAXVOLUME); + + _Mix_InitEffects(); + + /* This list is (currently) decided at build time. */ + add_chunk_decoder("WAVE"); + add_chunk_decoder("AIFF"); + add_chunk_decoder("VOC"); +#ifdef OGG_MUSIC + add_chunk_decoder("OGG"); +#endif +#ifdef FLAC_MUSIC + add_chunk_decoder("FLAC"); +#endif + + audio_opened = 1; + SDL_PauseAudio(0); + return(0); +} + +/* Dynamically change the number of channels managed by the mixer. + If decreasing the number of channels, the upper channels are + stopped. + */ +int Mix_AllocateChannels(int numchans) +{ + if ( numchans<0 || numchans==num_channels ) + return(num_channels); + + if ( numchans < num_channels ) { + /* Stop the affected channels */ + int i; + for(i=numchans; i < num_channels; i++) { + Mix_UnregisterAllEffects(i); + Mix_HaltChannel(i); + } + } + SDL_LockAudio(); + mix_channel = (struct _Mix_Channel *) SDL_realloc(mix_channel, numchans * sizeof(struct _Mix_Channel)); + if ( numchans > num_channels ) { + /* Initialize the new channels */ + int i; + for(i=num_channels; i < numchans; i++) { + mix_channel[i].chunk = NULL; + mix_channel[i].playing = 0; + mix_channel[i].looping = 0; + mix_channel[i].volume = SDL_MIX_MAXVOLUME; + mix_channel[i].fade_volume = SDL_MIX_MAXVOLUME; + mix_channel[i].fade_volume_reset = SDL_MIX_MAXVOLUME; + mix_channel[i].fading = MIX_NO_FADING; + mix_channel[i].tag = -1; + mix_channel[i].expire = 0; + mix_channel[i].effects = NULL; + mix_channel[i].paused = 0; + } + } + num_channels = numchans; + SDL_UnlockAudio(); + return(num_channels); +} + +/* Return the actual mixer parameters */ +int Mix_QuerySpec(int *frequency, Uint16 *format, int *channels) +{ + if ( audio_opened ) { + if ( frequency ) { + *frequency = mixer.freq; + } + if ( format ) { + *format = mixer.format; + } + if ( channels ) { + *channels = mixer.channels; + } + } + return(audio_opened); +} + + +/* + * !!! FIXME: Ideally, we want a Mix_LoadSample_RW(), which will handle the + * generic setup, then call the correct file format loader. + */ + +/* Load a wave file */ +Mix_Chunk *Mix_LoadWAV_RW(SDL_RWops *src, int freesrc) +{ + Uint32 magic; + Mix_Chunk *chunk; + SDL_AudioSpec wavespec, *loaded; + SDL_AudioCVT wavecvt; + int samplesize; + + /* rcg06012001 Make sure src is valid */ + if ( ! src ) { + SDL_SetError("Mix_LoadWAV_RW with NULL src"); + return(NULL); + } + + /* Make sure audio has been opened */ + if ( ! audio_opened ) { + SDL_SetError("Audio device hasn't been opened"); + if ( freesrc && src ) { + SDL_RWclose(src); + } + return(NULL); + } + + /* Allocate the chunk memory */ + chunk = (Mix_Chunk *)SDL_malloc(sizeof(Mix_Chunk)); + if ( chunk == NULL ) { + SDL_SetError("Out of memory"); + if ( freesrc ) { + SDL_RWclose(src); + } + return(NULL); + } + + /* Find out what kind of audio file this is */ + magic = SDL_ReadLE32(src); + /* Seek backwards for compatibility with older loaders */ + SDL_RWseek(src, -(int)sizeof(Uint32), RW_SEEK_CUR); + + switch (magic) { + case WAVE: + case RIFF: + loaded = SDL_LoadWAV_RW(src, freesrc, &wavespec, + (Uint8 **)&chunk->abuf, &chunk->alen); + break; + case FORM: + loaded = Mix_LoadAIFF_RW(src, freesrc, &wavespec, + (Uint8 **)&chunk->abuf, &chunk->alen); + break; +#ifdef OGG_MUSIC + case OGGS: + loaded = Mix_LoadOGG_RW(src, freesrc, &wavespec, + (Uint8 **)&chunk->abuf, &chunk->alen); + break; +#endif +#ifdef FLAC_MUSIC + case FLAC: + loaded = Mix_LoadFLAC_RW(src, freesrc, &wavespec, + (Uint8 **)&chunk->abuf, &chunk->alen); + break; +#endif + case CREA: + loaded = Mix_LoadVOC_RW(src, freesrc, &wavespec, + (Uint8 **)&chunk->abuf, &chunk->alen); + break; + default: + SDL_SetError("Unrecognized sound file type"); + return(0); + } + if ( !loaded ) { + SDL_free(chunk); + if ( freesrc ) { + SDL_RWclose(src); + } + return(NULL); + } + +#if 0 + PrintFormat("Audio device", &mixer); + PrintFormat("-- Wave file", &wavespec); +#endif + + /* Build the audio converter and create conversion buffers */ + if ( wavespec.format != mixer.format || + wavespec.channels != mixer.channels || + wavespec.freq != mixer.freq ) { + if ( SDL_BuildAudioCVT(&wavecvt, + wavespec.format, wavespec.channels, wavespec.freq, + mixer.format, mixer.channels, mixer.freq) < 0 ) { + SDL_free(chunk->abuf); + SDL_free(chunk); + return(NULL); + } + samplesize = ((wavespec.format & 0xFF)/8)*wavespec.channels; + wavecvt.len = chunk->alen & ~(samplesize-1); + wavecvt.buf = (Uint8 *)SDL_calloc(1, wavecvt.len*wavecvt.len_mult); + if ( wavecvt.buf == NULL ) { + SDL_SetError("Out of memory"); + SDL_free(chunk->abuf); + SDL_free(chunk); + return(NULL); + } + memcpy(wavecvt.buf, chunk->abuf, chunk->alen); + SDL_free(chunk->abuf); + + /* Run the audio converter */ + if ( SDL_ConvertAudio(&wavecvt) < 0 ) { + SDL_free(wavecvt.buf); + SDL_free(chunk); + return(NULL); + } + + chunk->abuf = wavecvt.buf; + chunk->alen = wavecvt.len_cvt; + } + + chunk->allocated = 1; + chunk->volume = MIX_MAX_VOLUME; + + return(chunk); +} + +/* Load a wave file of the mixer format from a memory buffer */ +Mix_Chunk *Mix_QuickLoad_WAV(Uint8 *mem) +{ + Mix_Chunk *chunk; + Uint8 magic[4]; + + /* Make sure audio has been opened */ + if ( ! audio_opened ) { + SDL_SetError("Audio device hasn't been opened"); + return(NULL); + } + + /* Allocate the chunk memory */ + chunk = (Mix_Chunk *)SDL_calloc(1,sizeof(Mix_Chunk)); + if ( chunk == NULL ) { + SDL_SetError("Out of memory"); + return(NULL); + } + + /* Essentially just skip to the audio data (no error checking - fast) */ + chunk->allocated = 0; + mem += 12; /* WAV header */ + do { + memcpy(magic, mem, 4); + mem += 4; + chunk->alen = ((mem[3]<<24)|(mem[2]<<16)|(mem[1]<<8)|(mem[0])); + mem += 4; + chunk->abuf = mem; + mem += chunk->alen; + } while ( memcmp(magic, "data", 4) != 0 ); + chunk->volume = MIX_MAX_VOLUME; + + return(chunk); +} + +/* Load raw audio data of the mixer format from a memory buffer */ +Mix_Chunk *Mix_QuickLoad_RAW(Uint8 *mem, Uint32 len) +{ + Mix_Chunk *chunk; + + /* Make sure audio has been opened */ + if ( ! audio_opened ) { + SDL_SetError("Audio device hasn't been opened"); + return(NULL); + } + + /* Allocate the chunk memory */ + chunk = (Mix_Chunk *)SDL_malloc(sizeof(Mix_Chunk)); + if ( chunk == NULL ) { + SDL_SetError("Out of memory"); + return(NULL); + } + + /* Essentially just point at the audio data (no error checking - fast) */ + chunk->allocated = 0; + chunk->alen = len; + chunk->abuf = mem; + chunk->volume = MIX_MAX_VOLUME; + + return(chunk); +} + +/* Free an audio chunk previously loaded */ +void Mix_FreeChunk(Mix_Chunk *chunk) +{ + int i; + + /* Caution -- if the chunk is playing, the mixer will crash */ + if ( chunk ) { + /* Guarantee that this chunk isn't playing */ + SDL_LockAudio(); + if ( mix_channel ) { + for ( i=0; i<num_channels; ++i ) { + if ( chunk == mix_channel[i].chunk ) { + mix_channel[i].playing = 0; + mix_channel[i].looping = 0; + } + } + } + SDL_UnlockAudio(); + /* Actually free the chunk */ + if ( chunk->allocated ) { + SDL_free(chunk->abuf); + } + SDL_free(chunk); + } +} + +/* Set a function that is called after all mixing is performed. + This can be used to provide real-time visual display of the audio stream + or add a custom mixer filter for the stream data. +*/ +void Mix_SetPostMix(void (*mix_func) + (void *udata, Uint8 *stream, int len), void *arg) +{ + SDL_LockAudio(); + mix_postmix_data = arg; + mix_postmix = mix_func; + SDL_UnlockAudio(); +} + +/* Add your own music player or mixer function. + If 'mix_func' is NULL, the default music player is re-enabled. + */ +void Mix_HookMusic(void (*mix_func)(void *udata, Uint8 *stream, int len), + void *arg) +{ + SDL_LockAudio(); + if ( mix_func != NULL ) { + music_data = arg; + mix_music = mix_func; + } else { + music_data = NULL; + mix_music = music_mixer; + } + SDL_UnlockAudio(); +} + +void *Mix_GetMusicHookData(void) +{ + return(music_data); +} + +void Mix_ChannelFinished(void (*channel_finished)(int channel)) +{ + SDL_LockAudio(); + channel_done_callback = channel_finished; + SDL_UnlockAudio(); +} + + +/* Reserve the first channels (0 -> n-1) for the application, i.e. don't allocate + them dynamically to the next sample if requested with a -1 value below. + Returns the number of reserved channels. + */ +int Mix_ReserveChannels(int num) +{ + if (num > num_channels) + num = num_channels; + reserved_channels = num; + return num; +} + +static int checkchunkintegral(Mix_Chunk *chunk) +{ + int frame_width = 1; + + if ((mixer.format & 0xFF) == 16) frame_width = 2; + frame_width *= mixer.channels; + while (chunk->alen % frame_width) chunk->alen--; + return chunk->alen; +} + +/* Play an audio chunk on a specific channel. + If the specified channel is -1, play on the first free channel. + 'ticks' is the number of milliseconds at most to play the sample, or -1 + if there is no limit. + Returns which channel was used to play the sound. +*/ +int Mix_PlayChannelTimed(int which, Mix_Chunk *chunk, int loops, int ticks) +{ + int i; + + /* Don't play null pointers :-) */ + if ( chunk == NULL ) { + Mix_SetError("Tried to play a NULL chunk"); + return(-1); + } + if ( !checkchunkintegral(chunk)) { + Mix_SetError("Tried to play a chunk with a bad frame"); + return(-1); + } + + /* Lock the mixer while modifying the playing channels */ + SDL_LockAudio(); + { + /* If which is -1, play on the first free channel */ + if ( which == -1 ) { + for ( i=reserved_channels; i<num_channels; ++i ) { + if ( mix_channel[i].playing <= 0 ) + break; + } + if ( i == num_channels ) { + Mix_SetError("No free channels available"); + which = -1; + } else { + which = i; + } + } + + /* Queue up the audio data for this channel */ + if ( which >= 0 && which < num_channels ) { + Uint32 sdl_ticks = SDL_GetTicks(); + if (Mix_Playing(which)) + _Mix_channel_done_playing(which); + mix_channel[which].samples = chunk->abuf; + mix_channel[which].playing = chunk->alen; + mix_channel[which].looping = loops; + mix_channel[which].chunk = chunk; + mix_channel[which].paused = 0; + mix_channel[which].fading = MIX_NO_FADING; + mix_channel[which].start_time = sdl_ticks; + mix_channel[which].expire = (ticks>0) ? (sdl_ticks + ticks) : 0; + } + } + SDL_UnlockAudio(); + + /* Return the channel on which the sound is being played */ + return(which); +} + +/* Change the expiration delay for a channel */ +int Mix_ExpireChannel(int which, int ticks) +{ + int status = 0; + + if ( which == -1 ) { + int i; + for ( i=0; i < num_channels; ++ i ) { + status += Mix_ExpireChannel(i, ticks); + } + } else if ( which < num_channels ) { + SDL_LockAudio(); + mix_channel[which].expire = (ticks>0) ? (SDL_GetTicks() + ticks) : 0; + SDL_UnlockAudio(); + ++ status; + } + return(status); +} + +/* Fade in a sound on a channel, over ms milliseconds */ +int Mix_FadeInChannelTimed(int which, Mix_Chunk *chunk, int loops, int ms, int ticks) +{ + int i; + + /* Don't play null pointers :-) */ + if ( chunk == NULL ) { + return(-1); + } + if ( !checkchunkintegral(chunk)) { + Mix_SetError("Tried to play a chunk with a bad frame"); + return(-1); + } + + /* Lock the mixer while modifying the playing channels */ + SDL_LockAudio(); + { + /* If which is -1, play on the first free channel */ + if ( which == -1 ) { + for ( i=reserved_channels; i<num_channels; ++i ) { + if ( mix_channel[i].playing <= 0 ) + break; + } + if ( i == num_channels ) { + which = -1; + } else { + which = i; + } + } + + /* Queue up the audio data for this channel */ + if ( which >= 0 && which < num_channels ) { + Uint32 sdl_ticks = SDL_GetTicks(); + if (Mix_Playing(which)) + _Mix_channel_done_playing(which); + mix_channel[which].samples = chunk->abuf; + mix_channel[which].playing = chunk->alen; + mix_channel[which].looping = loops; + mix_channel[which].chunk = chunk; + mix_channel[which].paused = 0; + mix_channel[which].fading = MIX_FADING_IN; + mix_channel[which].fade_volume = mix_channel[which].volume; + mix_channel[which].fade_volume_reset = mix_channel[which].volume; + mix_channel[which].volume = 0; + mix_channel[which].fade_length = (Uint32)ms; + mix_channel[which].start_time = mix_channel[which].ticks_fade = sdl_ticks; + mix_channel[which].expire = (ticks > 0) ? (sdl_ticks+ticks) : 0; + } + } + SDL_UnlockAudio(); + + /* Return the channel on which the sound is being played */ + return(which); +} + +/* Set volume of a particular channel */ +int Mix_Volume(int which, int volume) +{ + int i; + int prev_volume = 0; + + if ( which == -1 ) { + for ( i=0; i<num_channels; ++i ) { + prev_volume += Mix_Volume(i, volume); + } + prev_volume /= num_channels; + } else if ( which < num_channels ) { + prev_volume = mix_channel[which].volume; + if ( volume >= 0 ) { + if ( volume > SDL_MIX_MAXVOLUME ) { + volume = SDL_MIX_MAXVOLUME; + } + mix_channel[which].volume = volume; + } + } + return(prev_volume); +} +/* Set volume of a particular chunk */ +int Mix_VolumeChunk(Mix_Chunk *chunk, int volume) +{ + int prev_volume; + + prev_volume = chunk->volume; + if ( volume >= 0 ) { + if ( volume > MIX_MAX_VOLUME ) { + volume = MIX_MAX_VOLUME; + } + chunk->volume = volume; + } + return(prev_volume); +} + +/* Halt playing of a particular channel */ +int Mix_HaltChannel(int which) +{ + int i; + + if ( which == -1 ) { + for ( i=0; i<num_channels; ++i ) { + Mix_HaltChannel(i); + } + } else if ( which < num_channels ) { + SDL_LockAudio(); + if (mix_channel[which].playing) { + _Mix_channel_done_playing(which); + mix_channel[which].playing = 0; + mix_channel[which].looping = 0; + } + mix_channel[which].expire = 0; + if(mix_channel[which].fading != MIX_NO_FADING) /* Restore volume */ + mix_channel[which].volume = mix_channel[which].fade_volume_reset; + mix_channel[which].fading = MIX_NO_FADING; + SDL_UnlockAudio(); + } + return(0); +} + +/* Halt playing of a particular group of channels */ +int Mix_HaltGroup(int tag) +{ + int i; + + for ( i=0; i<num_channels; ++i ) { + if( mix_channel[i].tag == tag ) { + Mix_HaltChannel(i); + } + } + return(0); +} + +/* Fade out a channel and then stop it automatically */ +int Mix_FadeOutChannel(int which, int ms) +{ + int status; + + status = 0; + if ( audio_opened ) { + if ( which == -1 ) { + int i; + + for ( i=0; i<num_channels; ++i ) { + status += Mix_FadeOutChannel(i, ms); + } + } else if ( which < num_channels ) { + SDL_LockAudio(); + if ( mix_channel[which].playing && + (mix_channel[which].volume > 0) && + (mix_channel[which].fading != MIX_FADING_OUT) ) { + mix_channel[which].fade_volume = mix_channel[which].volume; + mix_channel[which].fading = MIX_FADING_OUT; + mix_channel[which].fade_length = ms; + mix_channel[which].ticks_fade = SDL_GetTicks(); + + /* only change fade_volume_reset if we're not fading. */ + if (mix_channel[which].fading == MIX_NO_FADING) { + mix_channel[which].fade_volume_reset = mix_channel[which].volume; + } + ++status; + } + SDL_UnlockAudio(); + } + } + return(status); +} + +/* Halt playing of a particular group of channels */ +int Mix_FadeOutGroup(int tag, int ms) +{ + int i; + int status = 0; + for ( i=0; i<num_channels; ++i ) { + if( mix_channel[i].tag == tag ) { + status += Mix_FadeOutChannel(i,ms); + } + } + return(status); +} + +Mix_Fading Mix_FadingChannel(int which) +{ + if ( which < 0 || which >= num_channels ) { + return MIX_NO_FADING; + } + return mix_channel[which].fading; +} + +/* Check the status of a specific channel. + If the specified mix_channel is -1, check all mix channels. +*/ +int Mix_Playing(int which) +{ + int status; + + status = 0; + if ( which == -1 ) { + int i; + + for ( i=0; i<num_channels; ++i ) { + if ((mix_channel[i].playing > 0) || + (mix_channel[i].looping > 0)) + { + ++status; + } + } + } else if ( which < num_channels ) { + if ( (mix_channel[which].playing > 0) || + (mix_channel[which].looping > 0) ) + { + ++status; + } + } + return(status); +} + +/* rcg06072001 Get the chunk associated with a channel. */ +Mix_Chunk *Mix_GetChunk(int channel) +{ + Mix_Chunk *retval = NULL; + + if ((channel >= 0) && (channel < num_channels)) { + retval = mix_channel[channel].chunk; + } + + return(retval); +} + +/* Close the mixer, halting all playing audio */ +void Mix_CloseAudio(void) +{ + int i; + + if ( audio_opened ) { + if ( audio_opened == 1 ) { + for (i = 0; i < num_channels; i++) { + Mix_UnregisterAllEffects(i); + } + Mix_UnregisterAllEffects(MIX_CHANNEL_POST); + close_music(); + Mix_HaltChannel(-1); + _Mix_DeinitEffects(); + SDL_CloseAudio(); + SDL_free(mix_channel); + mix_channel = NULL; + + /* rcg06042009 report available decoders at runtime. */ + SDL_free(chunk_decoders); + chunk_decoders = NULL; + num_decoders = 0; + } + --audio_opened; + } +} + +/* Pause a particular channel (or all) */ +void Mix_Pause(int which) +{ + Uint32 sdl_ticks = SDL_GetTicks(); + if ( which == -1 ) { + int i; + + for ( i=0; i<num_channels; ++i ) { + if ( mix_channel[i].playing > 0 ) { + mix_channel[i].paused = sdl_ticks; + } + } + } else if ( which < num_channels ) { + if ( mix_channel[which].playing > 0 ) { + mix_channel[which].paused = sdl_ticks; + } + } +} + +/* Resume a paused channel */ +void Mix_Resume(int which) +{ + Uint32 sdl_ticks = SDL_GetTicks(); + + SDL_LockAudio(); + if ( which == -1 ) { + int i; + + for ( i=0; i<num_channels; ++i ) { + if ( mix_channel[i].playing > 0 ) { + if(mix_channel[i].expire > 0) + mix_channel[i].expire += sdl_ticks - mix_channel[i].paused; + mix_channel[i].paused = 0; + } + } + } else if ( which < num_channels ) { + if ( mix_channel[which].playing > 0 ) { + if(mix_channel[which].expire > 0) + mix_channel[which].expire += sdl_ticks - mix_channel[which].paused; + mix_channel[which].paused = 0; + } + } + SDL_UnlockAudio(); +} + +int Mix_Paused(int which) +{ + if ( which < 0 ) { + int status = 0; + int i; + for( i=0; i < num_channels; ++i ) { + if ( mix_channel[i].paused ) { + ++ status; + } + } + return(status); + } else if ( which < num_channels ) { + return(mix_channel[which].paused != 0); + } else { + return(0); + } +} + +/* Change the group of a channel */ +int Mix_GroupChannel(int which, int tag) +{ + if ( which < 0 || which > num_channels ) + return(0); + + SDL_LockAudio(); + mix_channel[which].tag = tag; + SDL_UnlockAudio(); + return(1); +} + +/* Assign several consecutive channels to a group */ +int Mix_GroupChannels(int from, int to, int tag) +{ + int status = 0; + for( ; from <= to; ++ from ) { + status += Mix_GroupChannel(from, tag); + } + return(status); +} + +/* Finds the first available channel in a group of channels */ +int Mix_GroupAvailable(int tag) +{ + int i; + for( i=0; i < num_channels; i ++ ) { + if ( ((tag == -1) || (tag == mix_channel[i].tag)) && + (mix_channel[i].playing <= 0) ) + return i; + } + return(-1); +} + +int Mix_GroupCount(int tag) +{ + int count = 0; + int i; + for( i=0; i < num_channels; i ++ ) { + if ( mix_channel[i].tag==tag || tag==-1 ) + ++ count; + } + return(count); +} + +/* Finds the "oldest" sample playing in a group of channels */ +int Mix_GroupOldest(int tag) +{ + int chan = -1; + Uint32 mintime = SDL_GetTicks(); + int i; + for( i=0; i < num_channels; i ++ ) { + if ( (mix_channel[i].tag==tag || tag==-1) && mix_channel[i].playing > 0 + && mix_channel[i].start_time <= mintime ) { + mintime = mix_channel[i].start_time; + chan = i; + } + } + return(chan); +} + +/* Finds the "most recent" (i.e. last) sample playing in a group of channels */ +int Mix_GroupNewer(int tag) +{ + int chan = -1; + Uint32 maxtime = 0; + int i; + for( i=0; i < num_channels; i ++ ) { + if ( (mix_channel[i].tag==tag || tag==-1) && mix_channel[i].playing > 0 + && mix_channel[i].start_time >= maxtime ) { + maxtime = mix_channel[i].start_time; + chan = i; + } + } + return(chan); +} + + + +/* + * rcg06122001 The special effects exportable API. + * Please see effect_*.c for internally-implemented effects, such + * as Mix_SetPanning(). + */ + +/* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */ +static int _Mix_register_effect(effect_info **e, Mix_EffectFunc_t f, + Mix_EffectDone_t d, void *arg) +{ + effect_info *new_e; + + if (!e) { + Mix_SetError("Internal error"); + return(0); + } + + if (f == NULL) { + Mix_SetError("NULL effect callback"); + return(0); + } + + new_e = SDL_malloc(sizeof (effect_info)); + if (new_e == NULL) { + Mix_SetError("Out of memory"); + return(0); + } + + new_e->callback = f; + new_e->done_callback = d; + new_e->udata = arg; + new_e->next = NULL; + + /* add new effect to end of linked list... */ + if (*e == NULL) { + *e = new_e; + } else { + effect_info *cur = *e; + while (1) { + if (cur->next == NULL) { + cur->next = new_e; + break; + } + cur = cur->next; + } + } + + return(1); +} + + +/* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */ +static int _Mix_remove_effect(int channel, effect_info **e, Mix_EffectFunc_t f) +{ + effect_info *cur; + effect_info *prev = NULL; + effect_info *next = NULL; + + if (!e) { + Mix_SetError("Internal error"); + return(0); + } + + for (cur = *e; cur != NULL; cur = cur->next) { + if (cur->callback == f) { + next = cur->next; + if (cur->done_callback != NULL) { + cur->done_callback(channel, cur->udata); + } + SDL_free(cur); + + if (prev == NULL) { /* removing first item of list? */ + *e = next; + } else { + prev->next = next; + } + return(1); + } + prev = cur; + } + + Mix_SetError("No such effect registered"); + return(0); +} + + +/* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */ +static int _Mix_remove_all_effects(int channel, effect_info **e) +{ + effect_info *cur; + effect_info *next; + + if (!e) { + Mix_SetError("Internal error"); + return(0); + } + + for (cur = *e; cur != NULL; cur = next) { + next = cur->next; + if (cur->done_callback != NULL) { + cur->done_callback(channel, cur->udata); + } + SDL_free(cur); + } + *e = NULL; + + return(1); +} + + +/* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */ +int _Mix_RegisterEffect_locked(int channel, Mix_EffectFunc_t f, + Mix_EffectDone_t d, void *arg) +{ + effect_info **e = NULL; + + if (channel == MIX_CHANNEL_POST) { + e = &posteffects; + } else { + if ((channel < 0) || (channel >= num_channels)) { + Mix_SetError("Invalid channel number"); + return(0); + } + e = &mix_channel[channel].effects; + } + + return _Mix_register_effect(e, f, d, arg); +} + +int Mix_RegisterEffect(int channel, Mix_EffectFunc_t f, + Mix_EffectDone_t d, void *arg) +{ + int retval; + SDL_LockAudio(); + retval = _Mix_RegisterEffect_locked(channel, f, d, arg); + SDL_UnlockAudio(); + return retval; +} + + +/* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */ +int _Mix_UnregisterEffect_locked(int channel, Mix_EffectFunc_t f) +{ + effect_info **e = NULL; + + if (channel == MIX_CHANNEL_POST) { + e = &posteffects; + } else { + if ((channel < 0) || (channel >= num_channels)) { + Mix_SetError("Invalid channel number"); + return(0); + } + e = &mix_channel[channel].effects; + } + + return _Mix_remove_effect(channel, e, f); +} + +int Mix_UnregisterEffect(int channel, Mix_EffectFunc_t f) +{ + int retval; + SDL_LockAudio(); + retval = _Mix_UnregisterEffect_locked(channel, f); + SDL_UnlockAudio(); + return(retval); +} + +/* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */ +int _Mix_UnregisterAllEffects_locked(int channel) +{ + effect_info **e = NULL; + + if (channel == MIX_CHANNEL_POST) { + e = &posteffects; + } else { + if ((channel < 0) || (channel >= num_channels)) { + Mix_SetError("Invalid channel number"); + return(0); + } + e = &mix_channel[channel].effects; + } + + return _Mix_remove_all_effects(channel, e); +} + +int Mix_UnregisterAllEffects(int channel) +{ + int retval; + SDL_LockAudio(); + retval = _Mix_UnregisterAllEffects_locked(channel); + SDL_UnlockAudio(); + return(retval); +} + +/* end of mixer.c ... */ + diff --git a/apps/plugins/sdl/SDL_mixer/music.c b/apps/plugins/sdl/SDL_mixer/music.c new file mode 100644 index 0000000000..ab41327394 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/music.c @@ -0,0 +1,1599 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id$ */ + +#include <ctype.h> +#include "SDL_endian.h" +#include "SDL_audio.h" +#include "SDL_timer.h" + +#include "SDL_mixer.h" + +#ifdef CMD_MUSIC +#include "music_cmd.h" +#endif +#ifdef WAV_MUSIC +#include "wavestream.h" +#endif +#ifdef MODPLUG_MUSIC +#include "music_modplug.h" +#endif +#ifdef MOD_MUSIC +#include "music_mod.h" +#endif +#ifdef MID_MUSIC +# ifdef USE_TIMIDITY_MIDI +# include "timidity/timidity.h" +# endif +# ifdef USE_FLUIDSYNTH_MIDI +# include "fluidsynth.h" +# endif +# ifdef USE_NATIVE_MIDI +# include "native_midi.h" +# endif +#endif +#ifdef OGG_MUSIC +#include "music_ogg.h" +#endif +#ifdef MP3_MUSIC +#include "dynamic_mp3.h" +#endif +#ifdef MP3_MAD_MUSIC +#include "music_mad.h" +#endif +#ifdef FLAC_MUSIC +#include "music_flac.h" +#endif + +#if defined(MP3_MUSIC) || defined(MP3_MAD_MUSIC) +static SDL_AudioSpec used_mixer; +#endif + + +int 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; + +struct _Mix_Music { + Mix_MusicType type; + union { +#ifdef CMD_MUSIC + MusicCMD *cmd; +#endif +#ifdef WAV_MUSIC + WAVStream *wave; +#endif +#ifdef MODPLUG_MUSIC + modplug_data *modplug; +#endif +#ifdef MOD_MUSIC + struct MODULE *module; +#endif +#ifdef MID_MUSIC +#ifdef USE_TIMIDITY_MIDI + MidiSong *midi; +#endif +#ifdef USE_FLUIDSYNTH_MIDI + FluidSynthMidiSong *fluidsynthmidi; +#endif +#ifdef USE_NATIVE_MIDI + NativeMidiSong *nativemidi; +#endif +#endif +#ifdef OGG_MUSIC + OGG_music *ogg; +#endif +#ifdef MP3_MUSIC + SMPEG *mp3; +#endif +#ifdef MP3_MAD_MUSIC + mad_data *mp3_mad; +#endif +#ifdef FLAC_MUSIC + FLAC_music *flac; +#endif + } data; + Mix_Fading fading; + int fade_step; + int fade_steps; + int error; +}; +#ifdef MID_MUSIC +#ifdef USE_TIMIDITY_MIDI +static int timidity_ok; +static int samplesize; +#endif +#ifdef USE_FLUIDSYNTH_MIDI +static int fluidsynth_ok; +#endif +#ifdef USE_NATIVE_MIDI +static int native_midi_ok; +#endif +#endif + +/* Used to calculate fading steps */ +static int ms_per_step; + +/* rcg06042009 report available decoders at runtime. */ +static const char **music_decoders = NULL; +static int num_decoders = 0; + +/* Semicolon-separated SoundFont paths */ +#ifdef MID_MUSIC +char* soundfont_paths = NULL; +#endif + +int Mix_GetNumMusicDecoders(void) +{ + return(num_decoders); +} + +const char *Mix_GetMusicDecoder(int index) +{ + if ((index < 0) || (index >= num_decoders)) { + return NULL; + } + return(music_decoders[index]); +} + +static void add_music_decoder(const char *decoder) +{ + void *ptr = SDL_realloc(music_decoders, (num_decoders + 1) * sizeof (const char **)); + if (ptr == NULL) { + return; /* oh well, go on without it. */ + } + music_decoders = (const char **) ptr; + music_decoders[num_decoders++] = decoder; +} + +/* 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()) + { +#ifdef USE_NATIVE_MIDI + /* Native MIDI handles looping internally */ + if (music_playing->type == MUS_MID && native_midi_ok) { + music_loops = 0; + } +#endif + + /* Restart music if it has to loop at a high level */ + if (music_loops) + { + Mix_Fading current_fade; + --music_loops; + 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) +{ + //printf("music_mixer() called!\n"); + int left = 0; + + 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; + } + } + + music_halt_or_loop(); + if (!music_internal_playing()) + return; + + switch (music_playing->type) { +#ifdef CMD_MUSIC + case MUS_CMD: + /* The playing is done externally */ + break; +#endif +#ifdef WAV_MUSIC + case MUS_WAV: + left = WAVStream_PlaySome(stream, len); + break; +#endif +#ifdef MODPLUG_MUSIC + case MUS_MODPLUG: + left = modplug_playAudio(music_playing->data.modplug, stream, len); + break; +#endif +#ifdef MOD_MUSIC + case MUS_MOD: + left = MOD_playAudio(music_playing->data.module, stream, len); + break; +#endif +#ifdef MID_MUSIC + case MUS_MID: +#ifdef USE_NATIVE_MIDI + if ( native_midi_ok ) { + /* Native midi is handled asynchronously */ + goto skip; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_ok ) { + fluidsynth_playsome(music_playing->data.fluidsynthmidi, stream, len); + goto skip; + } +#endif +#ifdef USE_TIMIDITY_MIDI + if ( timidity_ok ) { + int samples = len / samplesize; + Timidity_PlaySome(stream, samples); + goto skip; + } +#endif + break; +#endif +#ifdef OGG_MUSIC + case MUS_OGG: + + left = OGG_playAudio(music_playing->data.ogg, stream, len); + break; +#endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + left = FLAC_playAudio(music_playing->data.flac, stream, len); + break; +#endif +#ifdef MP3_MUSIC + case MUS_MP3: + left = (len - smpeg.SMPEG_playAudio(music_playing->data.mp3, stream, len)); + break; +#endif +#ifdef MP3_MAD_MUSIC + case MUS_MP3_MAD: + left = mad_getSamples(music_playing->data.mp3_mad, stream, len); + break; +#endif + default: + /* Unknown music type?? */ + break; + } + } + + +skip: + /* Handle seamless music looping */ + if (left > 0 && left < len) { + music_halt_or_loop(); + if (music_internal_playing()) + music_mixer(udata, stream+(len-left), left); + } + //printf("sample 0: %d %d", stream[0], stream[1]); +} + +/* Initialize the music players with a certain desired audio format */ +int open_music(SDL_AudioSpec *mixer) +{ +#ifdef WAV_MUSIC + if ( WAVStream_Init(mixer) == 0 ) { + add_music_decoder("WAVE"); + } +#endif +#ifdef MODPLUG_MUSIC + if ( modplug_init(mixer) == 0 ) { + add_music_decoder("MODPLUG"); + } +#endif +#ifdef MOD_MUSIC + if ( MOD_init(mixer) == 0 ) { + add_music_decoder("MIKMOD"); + } +#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; + add_music_decoder("TIMIDITY"); + } else { + timidity_ok = 0; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_init(mixer) == 0 ) { + fluidsynth_ok = 1; + add_music_decoder("FLUIDSYNTH"); + } else { + fluidsynth_ok = 0; + } +#endif +#ifdef USE_NATIVE_MIDI +#ifdef USE_FLUIDSYNTH_MIDI + native_midi_ok = !fluidsynth_ok; + if ( native_midi_ok ) +#endif +#ifdef USE_TIMIDITY_MIDI + native_midi_ok = !timidity_ok; + if ( !native_midi_ok ) { + native_midi_ok = (getenv("SDL_NATIVE_MUSIC") != NULL); + } + if ( native_midi_ok ) +#endif + native_midi_ok = native_midi_detect(); + if ( native_midi_ok ) + add_music_decoder("NATIVEMIDI"); +#endif +#endif +#ifdef OGG_MUSIC + if ( OGG_init(mixer) == 0 ) { + add_music_decoder("OGG"); + } +#endif +#ifdef FLAC_MUSIC + if ( FLAC_init(mixer) == 0 ) { + add_music_decoder("FLAC"); + } +#endif +#if defined(MP3_MUSIC) || defined(MP3_MAD_MUSIC) + /* Keep a copy of the mixer */ + used_mixer = *mixer; + add_music_decoder("MP3"); +#endif + + music_playing = NULL; + music_stopped = 0; + 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); +} + +static int detect_mp3(Uint8 *magic) +{ + if ( strncmp((char *)magic, "ID3", 3) == 0 ) { + return 1; + } + + /* Detection code lifted from SMPEG */ + if(((magic[0] & 0xff) != 0xff) || // No sync bits + ((magic[1] & 0xf0) != 0xf0) || // + ((magic[2] & 0xf0) == 0x00) || // Bitrate is 0 + ((magic[2] & 0xf0) == 0xf0) || // Bitrate is 15 + ((magic[2] & 0x0c) == 0x0c) || // Frequency is 3 + ((magic[1] & 0x06) == 0x00)) { // Layer is 4 + return(0); + } + return 1; +} + +/* MUS_MOD can't be auto-detected. If no other format was detected, MOD is + * assumed and MUS_MOD will be returned, meaning that the format might not + * actually be MOD-based. + * + * Returns MUS_NONE in case of errors. */ +static Mix_MusicType detect_music_type(SDL_RWops *rw) +{ + Uint8 magic[5]; + Uint8 moremagic[9]; + + int start = SDL_RWtell(rw); + if (SDL_RWread(rw, magic, 1, 4) != 4 || SDL_RWread(rw, moremagic, 1, 8) != 8 ) { + Mix_SetError("Couldn't read from RWops"); + return MUS_NONE; + } + SDL_RWseek(rw, start, RW_SEEK_SET); + magic[4]='\0'; + moremagic[8] = '\0'; + + /* WAVE files have the magic four bytes "RIFF" + AIFF files have the magic 12 bytes "FORM" XXXX "AIFF" */ + if (((strcmp((char *)magic, "RIFF") == 0) && (strcmp((char *)(moremagic+4), "WAVE") == 0)) || + (strcmp((char *)magic, "FORM") == 0)) { + return MUS_WAV; + } + + /* Ogg Vorbis files have the magic four bytes "OggS" */ + if (strcmp((char *)magic, "OggS") == 0) { + return MUS_OGG; + } + + /* FLAC files have the magic four bytes "fLaC" */ + if (strcmp((char *)magic, "fLaC") == 0) { + return MUS_FLAC; + } + + /* MIDI files have the magic four bytes "MThd" */ + if (strcmp((char *)magic, "MThd") == 0) { + return MUS_MID; + } + + if (detect_mp3(magic)) { + return MUS_MP3; + } + + /* Assume MOD format. + * + * Apparently there is no way to check if the file is really a MOD, + * or there are too many formats supported by MikMod/ModPlug, or + * MikMod/ModPlug does this check by itself. */ + return MUS_MOD; +} + +/* Load a music file */ +Mix_Music *Mix_LoadMUS(const char *file) +{ + SDL_RWops *rw; + Mix_Music *music; + Mix_MusicType type; + char *ext = strrchr(file, '.'); + +#ifdef CMD_MUSIC + if ( music_cmd ) { + /* Allocate memory for the music structure */ + music = (Mix_Music *)SDL_malloc(sizeof(Mix_Music)); + if ( music == NULL ) { + Mix_SetError("Out of memory"); + return(NULL); + } + music->error = 0; + music->type = MUS_CMD; + music->data.cmd = MusicCMD_LoadSong(music_cmd, file); + if ( music->data.cmd == NULL ) { + SDL_free(music); + music == NULL; + } + return music; + } +#endif + + rw = SDL_RWFromFile(file, "rb"); + if ( rw == NULL ) { + Mix_SetError("Couldn't open '%s'", file); + return NULL; + } + + /* Use the extension as a first guess on the file type */ + type = MUS_NONE; + ext = strrchr(file, '.'); + /* No need to guard these with #ifdef *_MUSIC stuff, + * since we simply call Mix_LoadMUSType_RW() later */ + if ( ext ) { + ++ext; /* skip the dot in the extension */ + if ( MIX_string_equals(ext, "WAV") ) { + type = MUS_WAV; + } else if ( MIX_string_equals(ext, "MID") || + MIX_string_equals(ext, "MIDI") || + MIX_string_equals(ext, "KAR") ) { + type = MUS_MID; + } else if ( MIX_string_equals(ext, "OGG") ) { + type = MUS_OGG; + } else if ( MIX_string_equals(ext, "FLAC") ) { + type = MUS_FLAC; + } else if ( MIX_string_equals(ext, "MPG") || + MIX_string_equals(ext, "MPEG") || + MIX_string_equals(ext, "MP3") || + MIX_string_equals(ext, "MAD") ) { + type = MUS_MP3; + } + } + if ( type == MUS_NONE ) { + type = detect_music_type(rw); + } + + /* We need to know if a specific error occurs; if not, we'll set a + * generic one, so we clear the current one. */ + Mix_SetError(""); + music = Mix_LoadMUSType_RW(rw, type, SDL_TRUE); + if ( music == NULL && Mix_GetError()[0] == '\0' ) { + SDL_FreeRW(rw); + Mix_SetError("Couldn't open '%s'", file); + } + return music; +} + +Mix_Music *Mix_LoadMUS_RW(SDL_RWops *rw) +{ + return Mix_LoadMUSType_RW(rw, MUS_NONE, SDL_FALSE); +} + +Mix_Music *Mix_LoadMUSType_RW(SDL_RWops *rw, Mix_MusicType type, int freesrc) +{ + Mix_Music *music; + + if (!rw) { + Mix_SetError("RWops pointer is NULL"); + return NULL; + } + + /* If the caller wants auto-detection, figure out what kind of file + * this is. */ + if (type == MUS_NONE) { + if ((type = detect_music_type(rw)) == MUS_NONE) { + /* Don't call Mix_SetError() here since detect_music_type() + * does that. */ + return NULL; + } + } + + /* Allocate memory for the music structure */ + music = (Mix_Music *)SDL_malloc(sizeof(Mix_Music)); + if (music == NULL ) { + Mix_SetError("Out of memory"); + return NULL; + } + music->error = 0; + + switch (type) { +#ifdef WAV_MUSIC + case MUS_WAV: + /* The WAVE loader needs the first 4 bytes of the header */ + { + Uint8 magic[5]; + int start = SDL_RWtell(rw); + if (SDL_RWread(rw, magic, 1, 4) != 4) { + Mix_SetError("Couldn't read from RWops"); + return MUS_NONE; + } + SDL_RWseek(rw, start, RW_SEEK_SET); + magic[4] = '\0'; + music->type = MUS_WAV; + music->data.wave = WAVStream_LoadSong_RW(rw, (char *)magic, freesrc); + } + if (music->data.wave == NULL) { + music->error = 1; + } + break; +#endif +#ifdef OGG_MUSIC + case MUS_OGG: + music->type = MUS_OGG; + music->data.ogg = OGG_new_RW(rw, freesrc); + if ( music->data.ogg == NULL ) { + music->error = 1; + } + break; +#endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + music->type = MUS_FLAC; + music->data.flac = FLAC_new_RW(rw, freesrc); + if ( music->data.flac == NULL ) { + music->error = 1; + } + break; +#endif +#ifdef MP3_MUSIC + case MUS_MP3: + if ( Mix_Init(MIX_INIT_MP3) ) { + SMPEG_Info info; + music->type = MUS_MP3; + music->data.mp3 = smpeg.SMPEG_new_rwops(rw, &info, 0); + if ( !info.has_audio ) { + Mix_SetError("MPEG file does not have any audio stream."); + music->error = 1; + } else { + smpeg.SMPEG_actualSpec(music->data.mp3, &used_mixer); + } + } else { + music->error = 1; + } + break; +#elif defined(MP3_MAD_MUSIC) + case MUS_MP3: + music->type = MUS_MP3_MAD; + music->data.mp3_mad = mad_openFileRW(rw, &used_mixer, freesrc); + if (music->data.mp3_mad == 0) { + Mix_SetError("Could not initialize MPEG stream."); + music->error = 1; + } + break; +#endif +#ifdef MID_MUSIC + case MUS_MID: + music->type = MUS_MID; +#ifdef USE_NATIVE_MIDI + if ( native_midi_ok ) { + music->data.nativemidi = native_midi_loadsong_RW(rw, freesrc); + if ( music->data.nativemidi == NULL ) { + Mix_SetError("%s", native_midi_error()); + music->error = 1; + } + break; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_ok ) { + music->data.fluidsynthmidi = fluidsynth_loadsong_RW(rw, freesrc); + if ( music->data.fluidsynthmidi == NULL ) { + music->error = 1; + } + break; + } +#endif +#ifdef USE_TIMIDITY_MIDI + if ( timidity_ok ) { + music->data.midi = Timidity_LoadSong_RW(rw, freesrc); + if ( music->data.midi == NULL ) { + Mix_SetError("%s", Timidity_Error()); + music->error = 1; + } + //else + //printf("Timidity successfully loaded song!\n"); + } else { + Mix_SetError("%s", Timidity_Error()); + music->error = 1; + } +#endif + break; +#endif +#if defined(MODPLUG_MUSIC) || defined(MOD_MUSIC) + case MUS_MOD: + music->error = 1; +#ifdef MODPLUG_MUSIC + if ( music->error ) { + music->type = MUS_MODPLUG; + music->data.modplug = modplug_new_RW(rw, freesrc); + if ( music->data.modplug ) { + music->error = 0; + } + } +#endif +#ifdef MOD_MUSIC + if ( music->error ) { + music->type = MUS_MOD; + music->data.module = MOD_new_RW(rw, freesrc); + if ( music->data.module ) { + music->error = 0; + } + } +#endif + break; +#endif + + default: + Mix_SetError("Unrecognized music format"); + music->error=1; + } /* switch (want) */ + + + if (music->error) { + SDL_free(music); + music=NULL; + } + return(music); +} + +/* Free a music chunk previously loaded */ +void Mix_FreeMusic(Mix_Music *music) +{ + if ( music ) { + /* Stop the music if it's currently playing */ + SDL_LockAudio(); + if ( music == music_playing ) { + /* Wait for any fade out to finish */ + while ( music->fading == MIX_FADING_OUT ) { + SDL_UnlockAudio(); + SDL_Delay(100); + SDL_LockAudio(); + } + if ( music == music_playing ) { + music_internal_halt(); + } + } + SDL_UnlockAudio(); + switch (music->type) { +#ifdef CMD_MUSIC + case MUS_CMD: + MusicCMD_FreeSong(music->data.cmd); + break; +#endif +#ifdef WAV_MUSIC + case MUS_WAV: + WAVStream_FreeSong(music->data.wave); + break; +#endif +#ifdef MODPLUG_MUSIC + case MUS_MODPLUG: + modplug_delete(music->data.modplug); + break; +#endif +#ifdef MOD_MUSIC + case MUS_MOD: + MOD_delete(music->data.module); + break; +#endif +#ifdef MID_MUSIC + case MUS_MID: +#ifdef USE_NATIVE_MIDI + if ( native_midi_ok ) { + native_midi_freesong(music->data.nativemidi); + goto skip; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_ok ) { + fluidsynth_freesong(music->data.fluidsynthmidi); + goto skip; + } +#endif +#ifdef USE_TIMIDITY_MIDI + if ( timidity_ok ) { + Timidity_FreeSong(music->data.midi); + goto skip; + } +#endif + break; +#endif +#ifdef OGG_MUSIC + case MUS_OGG: + OGG_delete(music->data.ogg); + break; +#endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + FLAC_delete(music->data.flac); + break; +#endif +#ifdef MP3_MUSIC + case MUS_MP3: + smpeg.SMPEG_delete(music->data.mp3); + break; +#endif +#ifdef MP3_MAD_MUSIC + case MUS_MP3_MAD: + mad_closeFile(music->data.mp3_mad); + break; +#endif + default: + /* Unknown music type?? */ + break; + } + + skip: + SDL_free(music); + } +} + +/* Find out the music format of a mixer music, or the currently playing + music, if 'music' is NULL. +*/ +Mix_MusicType Mix_GetMusicType(const Mix_Music *music) +{ + Mix_MusicType type = MUS_NONE; + + if ( music ) { + type = music->type; + } else { + SDL_LockAudio(); + if ( music_playing ) { + type = music_playing->type; + } + SDL_UnlockAudio(); + } + return(type); +} + +/* Play a music chunk. Returns 0, or -1 if there was an error. + */ +static int music_internal_play(Mix_Music *music, double position) +{ + int retval = 0; + +#if defined(__MACOSX__) && defined(USE_NATIVE_MIDI) + /* This fixes a bug with native MIDI on Mac OS X, where you + can't really stop and restart MIDI from the audio callback. + */ + if ( music == music_playing && music->type == MUS_MID && native_midi_ok ) { + /* Just a seek suffices to restart playing */ + music_internal_position(position); + return 0; + } +#endif + + /* Note the music we're playing */ + if ( music_playing ) { + music_internal_halt(); + } + music_playing = music; + + /* Set the initial volume */ + if ( music->type != MUS_MOD ) { + music_internal_initialize_volume(); + } + + /* Set up for playback */ + switch (music->type) { +#ifdef CMD_MUSIC + case MUS_CMD: + MusicCMD_Start(music->data.cmd); + break; +#endif +#ifdef WAV_MUSIC + case MUS_WAV: + WAVStream_Start(music->data.wave); + break; +#endif +#ifdef MODPLUG_MUSIC + case MUS_MODPLUG: + /* can't set volume until file is loaded, so finally set it now */ + music_internal_initialize_volume(); + modplug_play(music->data.modplug); + break; +#endif +#ifdef MOD_MUSIC + case MUS_MOD: + MOD_play(music->data.module); + /* Player_SetVolume() does nothing before Player_Start() */ + music_internal_initialize_volume(); + break; +#endif +#ifdef MID_MUSIC + case MUS_MID: +#ifdef USE_NATIVE_MIDI + if ( native_midi_ok ) { + native_midi_start(music->data.nativemidi, music_loops); + goto skip; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if (fluidsynth_ok ) { + fluidsynth_start(music->data.fluidsynthmidi); + goto skip; + } +#endif +#ifdef USE_TIMIDITY_MIDI + if ( timidity_ok ) { + Timidity_Start(music->data.midi); + goto skip; + } +#endif + break; +#endif +#ifdef OGG_MUSIC + case MUS_OGG: + OGG_play(music->data.ogg); + break; +#endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + FLAC_play(music->data.flac); + break; +#endif +#ifdef MP3_MUSIC + case MUS_MP3: + smpeg.SMPEG_enableaudio(music->data.mp3,1); + smpeg.SMPEG_enablevideo(music->data.mp3,0); + smpeg.SMPEG_play(music_playing->data.mp3); + break; +#endif +#ifdef MP3_MAD_MUSIC + case MUS_MP3_MAD: + mad_start(music->data.mp3_mad); + break; +#endif + default: + Mix_SetError("Can't play unknown music type"); + retval = -1; + break; + } + +skip: + /* Set the playback position, note any errors if an offset is used */ + if ( retval == 0 ) { + if ( position > 0.0 ) { + if ( music_internal_position(position) < 0 ) { + Mix_SetError("Position not implemented for music type"); + retval = -1; + } + } else { + music_internal_position(0.0); + } + } + + /* If the setup failed, we're not playing any music anymore */ + if ( retval < 0 ) { + music_playing = NULL; + } + return(retval); +} +int Mix_FadeInMusicPos(Mix_Music *music, int loops, int ms, double position) +{ + int retval; + + if ( ms_per_step == 0 ) { + SDL_SetError("Audio device hasn't been opened"); + return(-1); + } + + /* Don't play null pointers :-) */ + if ( music == NULL ) { + Mix_SetError("music parameter was NULL"); + return(-1); + } + + /* Setup the data */ + if ( ms ) { + music->fading = MIX_FADING_IN; + } else { + music->fading = MIX_NO_FADING; + } + music->fade_step = 0; + music->fade_steps = ms/ms_per_step; + + /* Play the puppy */ + SDL_LockAudio(); + /* If the current music is fading out, wait for the fade to complete */ + while ( music_playing && (music_playing->fading == MIX_FADING_OUT) ) { + SDL_UnlockAudio(); + SDL_Delay(100); + SDL_LockAudio(); + } + music_active = 1; + if (loops == 1) { + /* Loop is the number of times to play the audio */ + loops = 0; + } + music_loops = loops; + retval = music_internal_play(music, position); + SDL_UnlockAudio(); + + return(retval); +} +int Mix_FadeInMusic(Mix_Music *music, int loops, int ms) +{ + return Mix_FadeInMusicPos(music, loops, ms, 0.0); +} +int Mix_PlayMusic(Mix_Music *music, int loops) +{ + return Mix_FadeInMusicPos(music, loops, 0, 0.0); +} + +/* Set the playing music position */ +int music_internal_position(double position) +{ + int retval = 0; + + switch (music_playing->type) { +#ifdef MODPLUG_MUSIC + case MUS_MODPLUG: + modplug_jump_to_time(music_playing->data.modplug, position); + break; +#endif +#ifdef MOD_MUSIC + case MUS_MOD: + MOD_jump_to_time(music_playing->data.module, position); + break; +#endif +#ifdef OGG_MUSIC + case MUS_OGG: + OGG_jump_to_time(music_playing->data.ogg, position); + break; +#endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + FLAC_jump_to_time(music_playing->data.flac, position); + break; +#endif +#ifdef MP3_MUSIC + case MUS_MP3: + smpeg.SMPEG_rewind(music_playing->data.mp3); + smpeg.SMPEG_play(music_playing->data.mp3); + if ( position > 0.0 ) { + smpeg.SMPEG_skip(music_playing->data.mp3, (float)position); + } + break; +#endif +#ifdef MP3_MAD_MUSIC + case MUS_MP3_MAD: + mad_seek(music_playing->data.mp3_mad, position); + break; +#endif + default: + /* TODO: Implement this for other music backends */ + retval = -1; + break; + } + return(retval); +} +int Mix_SetMusicPosition(double position) +{ + int retval; + + SDL_LockAudio(); + if ( music_playing ) { + retval = music_internal_position(position); + if ( retval < 0 ) { + Mix_SetError("Position not implemented for music type"); + } + } else { + Mix_SetError("Music isn't playing"); + retval = -1; + } + SDL_UnlockAudio(); + + return(retval); +} + +/* Set the music's initial volume */ +static void music_internal_initialize_volume(void) +{ + if ( music_playing->fading == MIX_FADING_IN ) { + music_internal_volume(0); + } else { + music_internal_volume(music_volume); + } +} + +/* Set the music volume */ +static void music_internal_volume(int volume) +{ + switch (music_playing->type) { +#ifdef CMD_MUSIC + case MUS_CMD: + MusicCMD_SetVolume(volume); + break; +#endif +#ifdef WAV_MUSIC + case MUS_WAV: + WAVStream_SetVolume(volume); + break; +#endif +#ifdef MODPLUG_MUSIC + case MUS_MODPLUG: + modplug_setvolume(music_playing->data.modplug, volume); + break; +#endif +#ifdef MOD_MUSIC + case MUS_MOD: + MOD_setvolume(music_playing->data.module, volume); + break; +#endif +#ifdef MID_MUSIC + case MUS_MID: +#ifdef USE_NATIVE_MIDI + if ( native_midi_ok ) { + native_midi_setvolume(volume); + return; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_ok ) { + fluidsynth_setvolume(music_playing->data.fluidsynthmidi, volume); + return; + } +#endif +#ifdef USE_TIMIDITY_MIDI + if ( timidity_ok ) { + Timidity_SetVolume(volume); + return; + } +#endif + break; +#endif +#ifdef OGG_MUSIC + case MUS_OGG: + OGG_setvolume(music_playing->data.ogg, volume); + break; +#endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + FLAC_setvolume(music_playing->data.flac, volume); + break; +#endif +#ifdef MP3_MUSIC + case MUS_MP3: + smpeg.SMPEG_setvolume(music_playing->data.mp3,(int)(((float)volume/(float)MIX_MAX_VOLUME)*100.0)); + break; +#endif +#ifdef MP3_MAD_MUSIC + case MUS_MP3_MAD: + mad_setVolume(music_playing->data.mp3_mad, volume); + break; +#endif + default: + /* Unknown music type?? */ + break; + } +} +int Mix_VolumeMusic(int volume) +{ + int prev_volume; + + prev_volume = music_volume; + if ( volume < 0 ) { + return prev_volume; + } + if ( volume > SDL_MIX_MAXVOLUME ) { + volume = SDL_MIX_MAXVOLUME; + } + music_volume = volume; + SDL_LockAudio(); + if ( music_playing ) { + music_internal_volume(music_volume); + } + SDL_UnlockAudio(); + return(prev_volume); +} + +/* Halt playing of music */ +static void music_internal_halt(void) +{ + switch (music_playing->type) { +#ifdef CMD_MUSIC + case MUS_CMD: + MusicCMD_Stop(music_playing->data.cmd); + break; +#endif +#ifdef WAV_MUSIC + case MUS_WAV: + WAVStream_Stop(); + break; +#endif +#ifdef MODPLUG_MUSIC + case MUS_MODPLUG: + modplug_stop(music_playing->data.modplug); + break; +#endif +#ifdef MOD_MUSIC + case MUS_MOD: + MOD_stop(music_playing->data.module); + break; +#endif +#ifdef MID_MUSIC + case MUS_MID: +#ifdef USE_NATIVE_MIDI + if ( native_midi_ok ) { + native_midi_stop(); + goto skip; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_ok ) { + fluidsynth_stop(music_playing->data.fluidsynthmidi); + goto skip; + } +#endif +#ifdef USE_TIMIDITY_MIDI + if ( timidity_ok ) { + Timidity_Stop(); + goto skip; + } +#endif + break; +#endif +#ifdef OGG_MUSIC + case MUS_OGG: + OGG_stop(music_playing->data.ogg); + break; +#endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + FLAC_stop(music_playing->data.flac); + break; +#endif +#ifdef MP3_MUSIC + case MUS_MP3: + smpeg.SMPEG_stop(music_playing->data.mp3); + break; +#endif +#ifdef MP3_MAD_MUSIC + case MUS_MP3_MAD: + mad_stop(music_playing->data.mp3_mad); + break; +#endif + default: + /* Unknown music type?? */ + return; + } + +skip: + music_playing->fading = MIX_NO_FADING; + music_playing = NULL; +} +int Mix_HaltMusic(void) +{ + SDL_LockAudio(); + if ( music_playing ) { + music_internal_halt(); + } + SDL_UnlockAudio(); + + return(0); +} + +/* Progressively stop the music */ +int Mix_FadeOutMusic(int ms) +{ + int retval = 0; + + if ( ms_per_step == 0 ) { + SDL_SetError("Audio device hasn't been opened"); + return 0; + } + + if (ms <= 0) { /* just halt immediately. */ + Mix_HaltMusic(); + return 1; + } + + SDL_LockAudio(); + if ( music_playing) { + int fade_steps = (ms + ms_per_step - 1)/ms_per_step; + if ( music_playing->fading == MIX_NO_FADING ) { + music_playing->fade_step = 0; + } else { + int step; + int old_fade_steps = music_playing->fade_steps; + if ( music_playing->fading == MIX_FADING_OUT ) { + step = music_playing->fade_step; + } else { + step = old_fade_steps + - music_playing->fade_step + 1; + } + music_playing->fade_step = (step * fade_steps) + / old_fade_steps; + } + music_playing->fading = MIX_FADING_OUT; + music_playing->fade_steps = fade_steps; + retval = 1; + } + SDL_UnlockAudio(); + + return(retval); +} + +Mix_Fading Mix_FadingMusic(void) +{ + Mix_Fading fading = MIX_NO_FADING; + + SDL_LockAudio(); + if ( music_playing ) { + fading = music_playing->fading; + } + SDL_UnlockAudio(); + + return(fading); +} + +/* Pause/Resume the music stream */ +void Mix_PauseMusic(void) +{ + music_active = 0; +} + +void Mix_ResumeMusic(void) +{ + music_active = 1; +} + +void Mix_RewindMusic(void) +{ + Mix_SetMusicPosition(0.0); +} + +int Mix_PausedMusic(void) +{ + return (music_active == 0); +} + +/* Check the status of the music */ +static int music_internal_playing() +{ + int playing = 1; + + if (music_playing == NULL) { + return 0; + } + + switch (music_playing->type) { +#ifdef CMD_MUSIC + case MUS_CMD: + if (!MusicCMD_Active(music_playing->data.cmd)) { + playing = 0; + } + break; +#endif +#ifdef WAV_MUSIC + case MUS_WAV: + if ( ! WAVStream_Active() ) { + playing = 0; + } + break; +#endif +#ifdef MODPLUG_MUSIC + case MUS_MODPLUG: + if ( ! modplug_playing(music_playing->data.modplug) ) { + playing = 0; + } + break; +#endif +#ifdef MOD_MUSIC + case MUS_MOD: + if ( ! MOD_playing(music_playing->data.module) ) { + playing = 0; + } + break; +#endif +#ifdef MID_MUSIC + case MUS_MID: +#ifdef USE_NATIVE_MIDI + if ( native_midi_ok ) { + if ( ! native_midi_active() ) + playing = 0; + goto skip; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_ok ) { + if ( ! fluidsynth_active(music_playing->data.fluidsynthmidi) ) + playing = 0; + goto skip; + } +#endif +#ifdef USE_TIMIDITY_MIDI + if ( timidity_ok ) { + if ( ! Timidity_Active() ) + playing = 0; + goto skip; + } +#endif + break; +#endif +#ifdef OGG_MUSIC + case MUS_OGG: + if ( ! OGG_playing(music_playing->data.ogg) ) { + playing = 0; + } + break; +#endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + if ( ! FLAC_playing(music_playing->data.flac) ) { + playing = 0; + } + break; +#endif +#ifdef MP3_MUSIC + case MUS_MP3: + if ( smpeg.SMPEG_status(music_playing->data.mp3) != SMPEG_PLAYING ) + playing = 0; + break; +#endif +#ifdef MP3_MAD_MUSIC + case MUS_MP3_MAD: + if (!mad_isPlaying(music_playing->data.mp3_mad)) { + playing = 0; + } + break; +#endif + default: + playing = 0; + break; + } + +skip: + return(playing); +} +int Mix_PlayingMusic(void) +{ + int playing = 0; + + SDL_LockAudio(); + if ( music_playing ) { + playing = music_loops || music_internal_playing(); + } + SDL_UnlockAudio(); + + return(playing); +} + +/* Set the external music playback command */ +int Mix_SetMusicCMD(const char *command) +{ + Mix_HaltMusic(); + if ( music_cmd ) { + SDL_free(music_cmd); + music_cmd = NULL; + } + if ( command ) { + music_cmd = (char *)SDL_malloc(strlen(command)+1); + if ( music_cmd == NULL ) { + return(-1); + } + strcpy(music_cmd, command); + } + return(0); +} + +int Mix_SetSynchroValue(int i) +{ + /* Not supported by any players at this time */ + return(-1); +} + +int Mix_GetSynchroValue(void) +{ + /* Not supported by any players at this time */ + return(-1); +} + + +/* Uninitialize the music players */ +void close_music(void) +{ + Mix_HaltMusic(); +#ifdef CMD_MUSIC + Mix_SetMusicCMD(NULL); +#endif +#ifdef MODPLUG_MUSIC + modplug_exit(); +#endif +#ifdef MOD_MUSIC + MOD_exit(); +#endif +#ifdef MID_MUSIC +# ifdef USE_TIMIDITY_MIDI + Timidity_Close(); +# endif +#endif + + /* rcg06042009 report available decoders at runtime. */ + SDL_free(music_decoders); + music_decoders = NULL; + num_decoders = 0; + + ms_per_step = 0; +} + +int Mix_SetSoundFonts(const char *paths) +{ +#ifdef MID_MUSIC + if (soundfont_paths) { + SDL_free(soundfont_paths); + soundfont_paths = NULL; + } + + if (paths) { + if (!(soundfont_paths = SDL_strdup(paths))) { + Mix_SetError("Insufficient memory to set SoundFonts"); + return 0; + } + } +#endif + return 1; +} + +#ifdef MID_MUSIC +const char* Mix_GetSoundFonts(void) +{ + const char* force = getenv("SDL_FORCE_SOUNDFONTS"); + + if (!soundfont_paths || (force && force[0] == '1')) { + return getenv("SDL_SOUNDFONTS"); + } else { + return soundfont_paths; + } +} + +int Mix_EachSoundFont(int (*function)(const char*, void*), void *data) +{ + char *context, *path, *paths; + const char* cpaths = Mix_GetSoundFonts(); + + if (!cpaths) { + Mix_SetError("No SoundFonts have been requested"); + return 0; + } + + if (!(paths = SDL_strdup(cpaths))) { + Mix_SetError("Insufficient memory to iterate over SoundFonts"); + return 0; + } + +#if defined(__MINGW32__) || defined(__MINGW64__) + for (path = strtok(paths, ";"); path; path = strtok(NULL, ";")) { +#elif defined(_WIN32) + for (path = strtok_s(paths, ";", &context); path; path = strtok_s(NULL, ";", &context)) { +#else + for (path = strtok_r(paths, ":;", &context); path; path = strtok_r(NULL, ":;", &context)) { +#endif + if (!function(path, data)) { + SDL_free(paths); + return 0; + } + } + + SDL_free(paths); + return 1; +} +#endif diff --git a/apps/plugins/sdl/SDL_mixer/music_cmd.c b/apps/plugins/sdl/SDL_mixer/music_cmd.c new file mode 100644 index 0000000000..968794b3fb --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/music_cmd.c @@ -0,0 +1,241 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" + +/* This file supports an external command for playing music */ + +#ifdef CMD_MUSIC + +#include <sys/types.h> +#include <sys/wait.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <ctype.h> + +#include "SDL_mixer.h" +#include "music_cmd.h" + +/* Unimplemented */ +void MusicCMD_SetVolume(int volume) +{ + Mix_SetError("No way to modify external player volume"); +} + +/* Load a music stream from the given file */ +MusicCMD *MusicCMD_LoadSong(const char *cmd, const char *file) +{ + MusicCMD *music; + + /* Allocate and fill the music structure */ + music = (MusicCMD *)SDL_malloc(sizeof *music); + if ( music == NULL ) { + Mix_SetError("Out of memory"); + return(NULL); + } + strncpy(music->file, file, (sizeof music->file)-1); + music->file[(sizeof music->file)-1] = '\0'; + strncpy(music->cmd, cmd, (sizeof music->cmd)-1); + music->cmd[(sizeof music->cmd)-1] = '\0'; + music->pid = 0; + + /* We're done */ + return(music); +} + +/* Parse a command line buffer into arguments */ +static int ParseCommandLine(char *cmdline, char **argv) +{ + char *bufp; + int argc; + + argc = 0; + for ( bufp = cmdline; *bufp; ) { + /* Skip leading whitespace */ + while ( isspace(*bufp) ) { + ++bufp; + } + /* Skip over argument */ + if ( *bufp == '"' ) { + ++bufp; + if ( *bufp ) { + if ( argv ) { + argv[argc] = bufp; + } + ++argc; + } + /* Skip over word */ + while ( *bufp && (*bufp != '"') ) { + ++bufp; + } + } else { + if ( *bufp ) { + if ( argv ) { + argv[argc] = bufp; + } + ++argc; + } + /* Skip over word */ + while ( *bufp && ! isspace(*bufp) ) { + ++bufp; + } + } + if ( *bufp ) { + if ( argv ) { + *bufp = '\0'; + } + ++bufp; + } + } + if ( argv ) { + argv[argc] = NULL; + } + return(argc); +} + +static char **parse_args(char *command, char *last_arg) +{ + int argc; + char **argv; + + /* Parse the command line */ + argc = ParseCommandLine(command, NULL); + if ( last_arg ) { + ++argc; + } + argv = (char **)SDL_malloc((argc+1)*(sizeof *argv)); + if ( argv == NULL ) { + return(NULL); + } + argc = ParseCommandLine(command, argv); + + /* Add last command line argument */ + if ( last_arg ) { + argv[argc++] = last_arg; + } + argv[argc] = NULL; + + /* We're ready! */ + return(argv); +} + +/* Start playback of a given music stream */ +void MusicCMD_Start(MusicCMD *music) +{ +#ifdef HAVE_FORK + music->pid = fork(); +#else + music->pid = vfork(); +#endif + switch(music->pid) { + /* Failed fork() system call */ + case -1: + Mix_SetError("fork() failed"); + return; + + /* Child process - executes here */ + case 0: { + char command[PATH_MAX]; + char **argv; + + /* Unblock signals in case we're called from a thread */ + { + sigset_t mask; + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, NULL); + } + + /* Execute the command */ + strcpy(command, music->cmd); + argv = parse_args(command, music->file); + if ( argv != NULL ) { + execvp(argv[0], argv); + } + + /* exec() failed */ + perror(argv[0]); + _exit(-1); + } + break; + + /* Parent process - executes here */ + default: + break; + } + return; +} + +/* Stop playback of a stream previously started with MusicCMD_Start() */ +void MusicCMD_Stop(MusicCMD *music) +{ + int status; + + if ( music->pid > 0 ) { + while ( kill(music->pid, 0) == 0 ) { + kill(music->pid, SIGTERM); + sleep(1); + waitpid(music->pid, &status, WNOHANG); + } + music->pid = 0; + } +} + +/* Pause playback of a given music stream */ +void MusicCMD_Pause(MusicCMD *music) +{ + if ( music->pid > 0 ) { + kill(music->pid, SIGSTOP); + } +} + +/* Resume playback of a given music stream */ +void MusicCMD_Resume(MusicCMD *music) +{ + if ( music->pid > 0 ) { + kill(music->pid, SIGCONT); + } +} + +/* Close the given music stream */ +void MusicCMD_FreeSong(MusicCMD *music) +{ + SDL_free(music); +} + +/* Return non-zero if a stream is currently playing */ +int MusicCMD_Active(MusicCMD *music) +{ + int status; + int active; + + active = 0; + if ( music->pid > 0 ) { + waitpid(music->pid, &status, WNOHANG); + if ( kill(music->pid, 0) == 0 ) { + active = 1; + } + } + return(active); +} + +#endif /* CMD_MUSIC */ diff --git a/apps/plugins/sdl/SDL_mixer/music_cmd.h b/apps/plugins/sdl/SDL_mixer/music_cmd.h new file mode 100644 index 0000000000..10168deaa0 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/music_cmd.h @@ -0,0 +1,62 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* This file supports an external command for playing music */ + +#ifdef CMD_MUSIC + +#include <sys/types.h> +#include <limits.h> +#include <stdio.h> +#if defined(__linux__) && defined(__arm__) +# include <linux/limits.h> +#endif +typedef struct { + char file[PATH_MAX]; + char cmd[PATH_MAX]; + pid_t pid; +} MusicCMD; + +/* Unimplemented */ +extern void MusicCMD_SetVolume(int volume); + +/* Load a music stream from the given file */ +extern MusicCMD *MusicCMD_LoadSong(const char *cmd, const char *file); + +/* Start playback of a given music stream */ +extern void MusicCMD_Start(MusicCMD *music); + +/* Stop playback of a stream previously started with MusicCMD_Start() */ +extern void MusicCMD_Stop(MusicCMD *music); + +/* Pause playback of a given music stream */ +extern void MusicCMD_Pause(MusicCMD *music); + +/* Resume playback of a given music stream */ +extern void MusicCMD_Resume(MusicCMD *music); + +/* Close the given music stream */ +extern void MusicCMD_FreeSong(MusicCMD *music); + +/* Return non-zero if a stream is currently playing */ +extern int MusicCMD_Active(MusicCMD *music); + +#endif /* CMD_MUSIC */ diff --git a/apps/plugins/sdl/SDL_mixer/music_flac.c b/apps/plugins/sdl/SDL_mixer/music_flac.c new file mode 100644 index 0000000000..e2ffc573eb --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/music_flac.c @@ -0,0 +1,593 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + This file is used to support SDL_LoadMUS playback of FLAC files. + ~ Austen Dicken (admin@cvpcs.org) +*/ + +#ifdef FLAC_MUSIC + +#include "SDL_mixer.h" +#include "dynamic_flac.h" +#include "music_flac.h" + +/* This is the format of the audio mixer data */ +static SDL_AudioSpec mixer; + +/* Initialize the FLAC player, with the given mixer settings + This function returns 0, or -1 if there was an error. + */ +int FLAC_init(SDL_AudioSpec *mixerfmt) +{ + mixer = *mixerfmt; + return(0); +} + +/* Set the volume for an FLAC stream */ +void FLAC_setvolume(FLAC_music *music, int volume) +{ + music->volume = volume; +} + +static FLAC__StreamDecoderReadStatus flac_read_music_cb( + const FLAC__StreamDecoder *decoder, + FLAC__byte buffer[], + size_t *bytes, + void *client_data) +{ + FLAC_music *data = (FLAC_music*)client_data; + + // make sure there is something to be reading + if (*bytes > 0) { + *bytes = SDL_RWread (data->rwops, buffer, sizeof (FLAC__byte), *bytes); + + if (*bytes < 0) { // error in read + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } + else if (*bytes == 0 ) { // no data was read (EOF) + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + } + else { // data was read, continue + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } + } + else { + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } +} + +static FLAC__StreamDecoderSeekStatus flac_seek_music_cb( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 absolute_byte_offset, + void *client_data) +{ + FLAC_music *data = (FLAC_music*)client_data; + + if (SDL_RWseek (data->rwops, absolute_byte_offset, RW_SEEK_SET) < 0) { + return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + } + else { + return FLAC__STREAM_DECODER_SEEK_STATUS_OK; + } +} + +static FLAC__StreamDecoderTellStatus flac_tell_music_cb( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 *absolute_byte_offset, + void *client_data ) +{ + FLAC_music *data = (FLAC_music*)client_data; + + int pos = SDL_RWtell (data->rwops); + + if (pos < 0) { + return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + } + else { + *absolute_byte_offset = (FLAC__uint64)pos; + return FLAC__STREAM_DECODER_TELL_STATUS_OK; + } +} + +static FLAC__StreamDecoderLengthStatus flac_length_music_cb ( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 *stream_length, + void *client_data) +{ + FLAC_music *data = (FLAC_music*)client_data; + + int pos = SDL_RWtell (data->rwops); + int length = SDL_RWseek (data->rwops, 0, RW_SEEK_END); + + if (SDL_RWseek (data->rwops, pos, RW_SEEK_SET) != pos || length < 0) { + /* there was an error attempting to return the stream to the original + * position, or the length was invalid. */ + return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + } + else { + *stream_length = (FLAC__uint64)length; + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + } +} + +static FLAC__bool flac_eof_music_cb( + const FLAC__StreamDecoder *decoder, + void *client_data ) +{ + FLAC_music *data = (FLAC_music*)client_data; + + int pos = SDL_RWtell (data->rwops); + int end = SDL_RWseek (data->rwops, 0, RW_SEEK_END); + + // was the original position equal to the end (a.k.a. the seek didn't move)? + if (pos == end) { + // must be EOF + return true; + } + else { + // not EOF, return to the original position + SDL_RWseek (data->rwops, pos, RW_SEEK_SET); + + return false; + } +} + +static FLAC__StreamDecoderWriteStatus flac_write_music_cb( + const FLAC__StreamDecoder *decoder, + const FLAC__Frame *frame, + const FLAC__int32 *const buffer[], + void *client_data) +{ + FLAC_music *data = (FLAC_music *)client_data; + size_t i; + + if (data->flac_data.total_samples == 0) { + SDL_SetError ("Given FLAC file does not specify its sample count."); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + + if (data->flac_data.channels != 2 || + data->flac_data.bits_per_sample != 16) { + SDL_SetError("Current FLAC support is only for 16 bit Stereo files."); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + + for (i = 0; i < frame->header.blocksize; i++) { + FLAC__int16 i16; + FLAC__uint16 ui16; + + // make sure we still have at least two bytes that can be read (one for + // each channel) + if (data->flac_data.max_to_read >= 4) { + // does the data block exist? + if (!data->flac_data.data) { + data->flac_data.data_len = data->flac_data.max_to_read; + data->flac_data.data_read = 0; + + // create it + data->flac_data.data = + (char *)SDL_malloc (data->flac_data.data_len); + + if (!data->flac_data.data) { + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + } + + i16 = (FLAC__int16)buffer[0][i]; + ui16 = (FLAC__uint16)i16; + + *((data->flac_data.data) + (data->flac_data.data_read++)) = + (char)(ui16); + *((data->flac_data.data) + (data->flac_data.data_read++)) = + (char)(ui16 >> 8); + + i16 = (FLAC__int16)buffer[1][i]; + ui16 = (FLAC__uint16)i16; + + *((data->flac_data.data) + (data->flac_data.data_read++)) = + (char)(ui16); + *((data->flac_data.data) + (data->flac_data.data_read++)) = + (char)(ui16 >> 8); + + data->flac_data.max_to_read -= 4; + + if (data->flac_data.max_to_read < 4) { + // we need to set this so that the read halts from the + // FLAC_getsome function. + data->flac_data.max_to_read = 0; + } + } + else { + // we need to write to the overflow + if (!data->flac_data.overflow) { + data->flac_data.overflow_len = + 4 * (frame->header.blocksize - i); + data->flac_data.overflow_read = 0; + + // make it big enough for the rest of the block + data->flac_data.overflow = + (char *)SDL_malloc (data->flac_data.overflow_len); + + if (!data->flac_data.overflow) { + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + } + + i16 = (FLAC__int16)buffer[0][i]; + ui16 = (FLAC__uint16)i16; + + *((data->flac_data.overflow) + (data->flac_data.overflow_read++)) = + (char)(ui16); + *((data->flac_data.overflow) + (data->flac_data.overflow_read++)) = + (char)(ui16 >> 8); + + i16 = (FLAC__int16)buffer[1][i]; + ui16 = (FLAC__uint16)i16; + + *((data->flac_data.overflow) + (data->flac_data.overflow_read++)) = + (char)(ui16); + *((data->flac_data.overflow) + (data->flac_data.overflow_read++)) = + (char)(ui16 >> 8); + } + } + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +static void flac_metadata_music_cb( + const FLAC__StreamDecoder *decoder, + const FLAC__StreamMetadata *metadata, + void *client_data) +{ + FLAC_music *data = (FLAC_music *)client_data; + + if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { + data->flac_data.sample_rate = metadata->data.stream_info.sample_rate; + data->flac_data.channels = metadata->data.stream_info.channels; + data->flac_data.total_samples = + metadata->data.stream_info.total_samples; + data->flac_data.bits_per_sample = + metadata->data.stream_info.bits_per_sample; + data->flac_data.sample_size = data->flac_data.channels * + ((data->flac_data.bits_per_sample) / 8); + } +} + +static void flac_error_music_cb( + const FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderErrorStatus status, + void *client_data) +{ + // print an SDL error based on the error status + switch (status) { + case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC: + SDL_SetError ("Error processing the FLAC file [LOST_SYNC]."); + break; + case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER: + SDL_SetError ("Error processing the FLAC file [BAD_HEADER]."); + break; + case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH: + SDL_SetError ("Error processing the FLAC file [CRC_MISMATCH]."); + break; + case FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM: + SDL_SetError ("Error processing the FLAC file [UNPARSEABLE]."); + break; + default: + SDL_SetError ("Error processing the FLAC file [UNKNOWN]."); + break; + } +} + +/* Load an FLAC stream from an SDL_RWops object */ +FLAC_music *FLAC_new_RW(SDL_RWops *rw, int freerw) +{ + FLAC_music *music; + int init_stage = 0; + int was_error = 1; + + if (!Mix_Init(MIX_INIT_FLAC)) { + if (freerw) { + SDL_RWclose(rw); + } + return NULL; + } + + music = (FLAC_music *)SDL_malloc ( sizeof (*music)); + if (music) { + /* Initialize the music structure */ + memset (music, 0, (sizeof (*music))); + FLAC_stop (music); + FLAC_setvolume (music, MIX_MAX_VOLUME); + music->section = -1; + music->rwops = rw; + music->freerw = freerw; + music->flac_data.max_to_read = 0; + music->flac_data.overflow = NULL; + music->flac_data.overflow_len = 0; + music->flac_data.overflow_read = 0; + music->flac_data.data = NULL; + music->flac_data.data_len = 0; + music->flac_data.data_read = 0; + + init_stage++; // stage 1! + + music->flac_decoder = flac.FLAC__stream_decoder_new (); + + if (music->flac_decoder != NULL) { + init_stage++; // stage 2! + + if (flac.FLAC__stream_decoder_init_stream( + music->flac_decoder, + flac_read_music_cb, flac_seek_music_cb, + flac_tell_music_cb, flac_length_music_cb, + flac_eof_music_cb, flac_write_music_cb, + flac_metadata_music_cb, flac_error_music_cb, + music) == FLAC__STREAM_DECODER_INIT_STATUS_OK ) { + init_stage++; // stage 3! + + if (flac.FLAC__stream_decoder_process_until_end_of_metadata + (music->flac_decoder)) { + was_error = 0; + } else { + SDL_SetError("FLAC__stream_decoder_process_until_end_of_metadata() failed"); + } + } else { + SDL_SetError("FLAC__stream_decoder_init_stream() failed"); + } + } else { + SDL_SetError("FLAC__stream_decoder_new() failed"); + } + + if (was_error) { + switch (init_stage) { + case 3: + flac.FLAC__stream_decoder_finish( music->flac_decoder ); + case 2: + flac.FLAC__stream_decoder_delete( music->flac_decoder ); + case 1: + case 0: + SDL_free(music); + if (freerw) { + SDL_RWclose(rw); + } + break; + } + return NULL; + } + } else { + SDL_OutOfMemory(); + if (freerw) { + SDL_RWclose(rw); + } + return NULL; + } + + return music; +} + +/* Start playback of a given FLAC stream */ +void FLAC_play(FLAC_music *music) +{ + music->playing = 1; +} + +/* Return non-zero if a stream is currently playing */ +int FLAC_playing(FLAC_music *music) +{ + return(music->playing); +} + +/* Read some FLAC stream data and convert it for output */ +static void FLAC_getsome(FLAC_music *music) +{ + SDL_AudioCVT *cvt; + + /* GET AUDIO WAVE DATA */ + // set the max number of characters to read + music->flac_data.max_to_read = 8192; + music->flac_data.data_len = music->flac_data.max_to_read; + music->flac_data.data_read = 0; + if (!music->flac_data.data) { + music->flac_data.data = (char *)SDL_malloc (music->flac_data.data_len); + } + + // we have data to read + while(music->flac_data.max_to_read > 0) { + // first check if there is data in the overflow from before + if (music->flac_data.overflow) { + size_t overflow_len = music->flac_data.overflow_read; + + if (overflow_len > music->flac_data.max_to_read) { + size_t overflow_extra_len = overflow_len - + music->flac_data.max_to_read; + + memcpy (music->flac_data.data+music->flac_data.data_read, + music->flac_data.overflow, music->flac_data.max_to_read); + music->flac_data.data_read += music->flac_data.max_to_read; + memcpy (music->flac_data.overflow, + music->flac_data.overflow + music->flac_data.max_to_read, + overflow_extra_len); + music->flac_data.overflow_len = overflow_extra_len; + music->flac_data.overflow_read = overflow_extra_len; + music->flac_data.max_to_read = 0; + } + else { + memcpy (music->flac_data.data+music->flac_data.data_read, + music->flac_data.overflow, overflow_len); + music->flac_data.data_read += overflow_len; + free (music->flac_data.overflow); + music->flac_data.overflow = NULL; + music->flac_data.overflow_len = 0; + music->flac_data.overflow_read = 0; + music->flac_data.max_to_read -= overflow_len; + } + } + else { + if (!flac.FLAC__stream_decoder_process_single ( + music->flac_decoder)) { + music->flac_data.max_to_read = 0; + } + + if (flac.FLAC__stream_decoder_get_state (music->flac_decoder) + == FLAC__STREAM_DECODER_END_OF_STREAM) { + music->flac_data.max_to_read = 0; + } + } + } + + if (music->flac_data.data_read <= 0) { + if (music->flac_data.data_read == 0) { + music->playing = 0; + } + return; + } + cvt = &music->cvt; + if (music->section < 0) { + + SDL_BuildAudioCVT (cvt, AUDIO_S16, (Uint8)music->flac_data.channels, + (int)music->flac_data.sample_rate, mixer.format, + mixer.channels, mixer.freq); + if (cvt->buf) { + free (cvt->buf); + } + cvt->buf = (Uint8 *)SDL_malloc (music->flac_data.data_len * cvt->len_mult); + music->section = 0; + } + if (cvt->buf) { + memcpy (cvt->buf, music->flac_data.data, music->flac_data.data_read); + if (cvt->needed) { + cvt->len = music->flac_data.data_read; + SDL_ConvertAudio (cvt); + } + else { + cvt->len_cvt = music->flac_data.data_read; + } + music->len_available = music->cvt.len_cvt; + music->snd_available = music->cvt.buf; + } + else { + SDL_SetError ("Out of memory"); + music->playing = 0; + } +} + +/* Play some of a stream previously started with FLAC_play() */ +int FLAC_playAudio(FLAC_music *music, Uint8 *snd, int len) +{ + int mixable; + + while ((len > 0) && music->playing) { + if (!music->len_available) { + FLAC_getsome (music); + } + mixable = len; + if (mixable > music->len_available) { + mixable = music->len_available; + } + if (music->volume == MIX_MAX_VOLUME) { + memcpy (snd, music->snd_available, mixable); + } + else { + SDL_MixAudio (snd, music->snd_available, mixable, music->volume); + } + music->len_available -= mixable; + music->snd_available += mixable; + len -= mixable; + snd += mixable; + } + + return len; +} + +/* Stop playback of a stream previously started with FLAC_play() */ +void FLAC_stop(FLAC_music *music) +{ + music->playing = 0; +} + +/* Close the given FLAC_music object */ +void FLAC_delete(FLAC_music *music) +{ + if (music) { + if (music->flac_decoder) { + flac.FLAC__stream_decoder_finish (music->flac_decoder); + flac.FLAC__stream_decoder_delete (music->flac_decoder); + } + + if (music->flac_data.data) { + free (music->flac_data.data); + } + + if (music->flac_data.overflow) { + free (music->flac_data.overflow); + } + + if (music->cvt.buf) { + free (music->cvt.buf); + } + + if (music->freerw) { + SDL_RWclose(music->rwops); + } + free (music); + } +} + +/* Jump (seek) to a given position (time is in seconds) */ +void FLAC_jump_to_time(FLAC_music *music, double time) +{ + if (music) { + if (music->flac_decoder) { + double seek_sample = music->flac_data.sample_rate * time; + + // clear data if it has data + if (music->flac_data.data) { + free (music->flac_data.data); + music->flac_data.data = NULL; + } + + // clear overflow if it has data + if (music->flac_data.overflow) { + free (music->flac_data.overflow); + music->flac_data.overflow = NULL; + } + + if (!flac.FLAC__stream_decoder_seek_absolute (music->flac_decoder, + (FLAC__uint64)seek_sample)) { + if (flac.FLAC__stream_decoder_get_state (music->flac_decoder) + == FLAC__STREAM_DECODER_SEEK_ERROR) { + flac.FLAC__stream_decoder_flush (music->flac_decoder); + } + + SDL_SetError + ("Seeking of FLAC stream failed: libFLAC seek failed."); + } + } + else { + SDL_SetError + ("Seeking of FLAC stream failed: FLAC decoder was NULL."); + } + } + else { + SDL_SetError ("Seeking of FLAC stream failed: music was NULL."); + } +} + +#endif /* FLAC_MUSIC */ diff --git a/apps/plugins/sdl/SDL_mixer/music_flac.h b/apps/plugins/sdl/SDL_mixer/music_flac.h new file mode 100644 index 0000000000..c87dc9ea9f --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/music_flac.h @@ -0,0 +1,90 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Header to handle loading FLAC music files in SDL. + ~ Austen Dicken (admin@cvpcs.org) +*/ + +/* $Id: $ */ + +#ifdef FLAC_MUSIC + +#include <FLAC/stream_decoder.h> + +typedef struct { + FLAC__uint64 sample_size; + unsigned sample_rate; + unsigned channels; + unsigned bits_per_sample; + FLAC__uint64 total_samples; + + // the following are used to handle the callback nature of the writer + int max_to_read; + char *data; // pointer to beginning of data array + int data_len; // size of data array + int data_read; // amount of data array used + char *overflow; // pointer to beginning of overflow array + int overflow_len; // size of overflow array + int overflow_read; // amount of overflow array used +} FLAC_Data; + +typedef struct { + int playing; + int volume; + int section; + FLAC__StreamDecoder *flac_decoder; + FLAC_Data flac_data; + SDL_RWops *rwops; + int freerw; + SDL_AudioCVT cvt; + int len_available; + Uint8 *snd_available; +} FLAC_music; + +/* Initialize the FLAC player, with the given mixer settings + This function returns 0, or -1 if there was an error. + */ +extern int FLAC_init(SDL_AudioSpec *mixer); + +/* Set the volume for a FLAC stream */ +extern void FLAC_setvolume(FLAC_music *music, int volume); + +/* Load an FLAC stream from an SDL_RWops object */ +extern FLAC_music *FLAC_new_RW(SDL_RWops *rw, int freerw); + +/* Start playback of a given FLAC stream */ +extern void FLAC_play(FLAC_music *music); + +/* Return non-zero if a stream is currently playing */ +extern int FLAC_playing(FLAC_music *music); + +/* Play some of a stream previously started with FLAC_play() */ +extern int FLAC_playAudio(FLAC_music *music, Uint8 *stream, int len); + +/* Stop playback of a stream previously started with FLAC_play() */ +extern void FLAC_stop(FLAC_music *music); + +/* Close the given FLAC stream */ +extern void FLAC_delete(FLAC_music *music); + +/* Jump (seek) to a given position (time is in seconds) */ +extern void FLAC_jump_to_time(FLAC_music *music, double time); + +#endif /* FLAC_MUSIC */ diff --git a/apps/plugins/sdl/SDL_mixer/music_mad.c b/apps/plugins/sdl/SDL_mixer/music_mad.c new file mode 100644 index 0000000000..e9bedf59d4 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/music_mad.c @@ -0,0 +1,325 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifdef MP3_MAD_MUSIC + +#include "music_mad.h" + +mad_data * +mad_openFileRW(SDL_RWops *rw, SDL_AudioSpec *mixer, int freerw) +{ + mad_data *mp3_mad; + + mp3_mad = (mad_data *)SDL_malloc(sizeof(mad_data)); + if (mp3_mad) { + mp3_mad->rw = rw; + mp3_mad->freerw = freerw; + mad_stream_init(&mp3_mad->stream); + mad_frame_init(&mp3_mad->frame); + mad_synth_init(&mp3_mad->synth); + mp3_mad->frames_read = 0; + mad_timer_reset(&mp3_mad->next_frame_start); + mp3_mad->volume = MIX_MAX_VOLUME; + mp3_mad->status = 0; + mp3_mad->output_begin = 0; + mp3_mad->output_end = 0; + mp3_mad->mixer = *mixer; + } + return mp3_mad; +} + +void +mad_closeFile(mad_data *mp3_mad) +{ + mad_stream_finish(&mp3_mad->stream); + mad_frame_finish(&mp3_mad->frame); + mad_synth_finish(&mp3_mad->synth); + + if (mp3_mad->freerw) { + SDL_RWclose(mp3_mad->rw); + } + SDL_free(mp3_mad); +} + +/* Starts the playback. */ +void +mad_start(mad_data *mp3_mad) { + mp3_mad->status |= MS_playing; +} + +/* Stops the playback. */ +void +mad_stop(mad_data *mp3_mad) { + mp3_mad->status &= ~MS_playing; +} + +/* Returns true if the playing is engaged, false otherwise. */ +int +mad_isPlaying(mad_data *mp3_mad) { + return ((mp3_mad->status & MS_playing) != 0); +} + +/* Reads the next frame from the file. Returns true on success or + false on failure. */ +static int +read_next_frame(mad_data *mp3_mad) { + if (mp3_mad->stream.buffer == NULL || + mp3_mad->stream.error == MAD_ERROR_BUFLEN) { + size_t read_size; + size_t remaining; + unsigned char *read_start; + + /* There might be some bytes in the buffer left over from last + time. If so, move them down and read more bytes following + them. */ + if (mp3_mad->stream.next_frame != NULL) { + remaining = mp3_mad->stream.bufend - mp3_mad->stream.next_frame; + memmove(mp3_mad->input_buffer, mp3_mad->stream.next_frame, remaining); + read_start = mp3_mad->input_buffer + remaining; + read_size = MAD_INPUT_BUFFER_SIZE - remaining; + + } else { + read_size = MAD_INPUT_BUFFER_SIZE; + read_start = mp3_mad->input_buffer; + remaining = 0; + } + + /* Now read additional bytes from the input file. */ + read_size = SDL_RWread(mp3_mad->rw, read_start, 1, read_size); + + if (read_size <= 0) { + if ((mp3_mad->status & (MS_input_eof | MS_input_error)) == 0) { + if (read_size == 0) { + mp3_mad->status |= MS_input_eof; + } else { + mp3_mad->status |= MS_input_error; + } + + /* At the end of the file, we must stuff MAD_BUFFER_GUARD + number of 0 bytes. */ + memset(read_start + read_size, 0, MAD_BUFFER_GUARD); + read_size += MAD_BUFFER_GUARD; + } + } + + /* Now feed those bytes into the libmad stream. */ + mad_stream_buffer(&mp3_mad->stream, mp3_mad->input_buffer, + read_size + remaining); + mp3_mad->stream.error = MAD_ERROR_NONE; + } + + /* Now ask libmad to extract a frame from the data we just put in + its buffer. */ + if (mad_frame_decode(&mp3_mad->frame, &mp3_mad->stream)) { + if (MAD_RECOVERABLE(mp3_mad->stream.error)) { + return 0; + + } else if (mp3_mad->stream.error == MAD_ERROR_BUFLEN) { + return 0; + + } else { + mp3_mad->status |= MS_decode_error; + return 0; + } + } + + mp3_mad->frames_read++; + mad_timer_add(&mp3_mad->next_frame_start, mp3_mad->frame.header.duration); + + return 1; +} + +/* Scale a MAD sample to 16 bits for output. */ +static signed int +scale(mad_fixed_t sample) { + /* round */ + sample += (1L << (MAD_F_FRACBITS - 16)); + + /* clip */ + if (sample >= MAD_F_ONE) + sample = MAD_F_ONE - 1; + else if (sample < -MAD_F_ONE) + sample = -MAD_F_ONE; + + /* quantize */ + return sample >> (MAD_F_FRACBITS + 1 - 16); +} + +/* Once the frame has been read, copies its samples into the output + buffer. */ +static void +decode_frame(mad_data *mp3_mad) { + struct mad_pcm *pcm; + unsigned int nchannels, nsamples; + mad_fixed_t const *left_ch, *right_ch; + unsigned char *out; + int ret; + + mad_synth_frame(&mp3_mad->synth, &mp3_mad->frame); + pcm = &mp3_mad->synth.pcm; + out = mp3_mad->output_buffer + mp3_mad->output_end; + + if ((mp3_mad->status & MS_cvt_decoded) == 0) { + mp3_mad->status |= MS_cvt_decoded; + + /* The first frame determines some key properties of the stream. + In particular, it tells us enough to set up the convert + structure now. */ + SDL_BuildAudioCVT(&mp3_mad->cvt, AUDIO_S16, pcm->channels, mp3_mad->frame.header.samplerate, mp3_mad->mixer.format, mp3_mad->mixer.channels, mp3_mad->mixer.freq); + } + + /* pcm->samplerate contains the sampling frequency */ + + nchannels = pcm->channels; + nsamples = pcm->length; + left_ch = pcm->samples[0]; + right_ch = pcm->samples[1]; + + while (nsamples--) { + signed int sample; + + /* output sample(s) in 16-bit signed little-endian PCM */ + + sample = scale(*left_ch++); + *out++ = ((sample >> 0) & 0xff); + *out++ = ((sample >> 8) & 0xff); + + if (nchannels == 2) { + sample = scale(*right_ch++); + *out++ = ((sample >> 0) & 0xff); + *out++ = ((sample >> 8) & 0xff); + } + } + + mp3_mad->output_end = out - mp3_mad->output_buffer; + /*assert(mp3_mad->output_end <= MAD_OUTPUT_BUFFER_SIZE);*/ +} + +int +mad_getSamples(mad_data *mp3_mad, Uint8 *stream, int len) { + int bytes_remaining; + int num_bytes; + Uint8 *out; + + if ((mp3_mad->status & MS_playing) == 0) { + /* We're not supposed to be playing, so send silence instead. */ + memset(stream, 0, len); + return; + } + + out = stream; + bytes_remaining = len; + while (bytes_remaining > 0) { + if (mp3_mad->output_end == mp3_mad->output_begin) { + /* We need to get a new frame. */ + mp3_mad->output_begin = 0; + mp3_mad->output_end = 0; + if (!read_next_frame(mp3_mad)) { + if ((mp3_mad->status & MS_error_flags) != 0) { + /* Couldn't read a frame; either an error condition or + end-of-file. Stop. */ + memset(out, 0, bytes_remaining); + mp3_mad->status &= ~MS_playing; + return bytes_remaining; + } + } else { + decode_frame(mp3_mad); + + /* Now convert the frame data to the appropriate format for + output. */ + mp3_mad->cvt.buf = mp3_mad->output_buffer; + mp3_mad->cvt.len = mp3_mad->output_end; + + mp3_mad->output_end = (int)(mp3_mad->output_end * mp3_mad->cvt.len_ratio); + /*assert(mp3_mad->output_end <= MAD_OUTPUT_BUFFER_SIZE);*/ + SDL_ConvertAudio(&mp3_mad->cvt); + } + } + + num_bytes = mp3_mad->output_end - mp3_mad->output_begin; + if (bytes_remaining < num_bytes) { + num_bytes = bytes_remaining; + } + + if (mp3_mad->volume == MIX_MAX_VOLUME) { + memcpy(out, mp3_mad->output_buffer + mp3_mad->output_begin, num_bytes); + } else { + SDL_MixAudio(out, mp3_mad->output_buffer + mp3_mad->output_begin, + num_bytes, mp3_mad->volume); + } + out += num_bytes; + mp3_mad->output_begin += num_bytes; + bytes_remaining -= num_bytes; + } + return 0; +} + +void +mad_seek(mad_data *mp3_mad, double position) { + mad_timer_t target; + int int_part; + + int_part = (int)position; + mad_timer_set(&target, int_part, + (int)((position - int_part) * 1000000), 1000000); + + if (mad_timer_compare(mp3_mad->next_frame_start, target) > 0) { + /* In order to seek backwards in a VBR file, we have to rewind and + start again from the beginning. This isn't necessary if the + file happens to be CBR, of course; in that case we could seek + directly to the frame we want. But I leave that little + optimization for the future developer who discovers she really + needs it. */ + mp3_mad->frames_read = 0; + mad_timer_reset(&mp3_mad->next_frame_start); + mp3_mad->status &= ~MS_error_flags; + mp3_mad->output_begin = 0; + mp3_mad->output_end = 0; + + SDL_RWseek(mp3_mad->rw, 0, RW_SEEK_SET); + } + + /* Now we have to skip frames until we come to the right one. + Again, only truly necessary if the file is VBR. */ + while (mad_timer_compare(mp3_mad->next_frame_start, target) < 0) { + if (!read_next_frame(mp3_mad)) { + if ((mp3_mad->status & MS_error_flags) != 0) { + /* Couldn't read a frame; either an error condition or + end-of-file. Stop. */ + mp3_mad->status &= ~MS_playing; + return; + } + } + } + + /* Here we are, at the beginning of the frame that contains the + target time. Ehh, I say that's close enough. If we wanted to, + we could get more precise by decoding the frame now and counting + the appropriate number of samples out of it. */ +} + +void +mad_setVolume(mad_data *mp3_mad, int volume) { + mp3_mad->volume = volume; +} + + +#endif /* MP3_MAD_MUSIC */ diff --git a/apps/plugins/sdl/SDL_mixer/music_mad.h b/apps/plugins/sdl/SDL_mixer/music_mad.h new file mode 100644 index 0000000000..af93c8df5e --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/music_mad.h @@ -0,0 +1,72 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifdef MP3_MAD_MUSIC + +#include "mad.h" +#include "SDL_rwops.h" +#include "SDL_audio.h" +#include "SDL_mixer.h" + +#define MAD_INPUT_BUFFER_SIZE (5*8192) +#define MAD_OUTPUT_BUFFER_SIZE 8192 + +enum { + MS_input_eof = 0x0001, + MS_input_error = 0x0001, + MS_decode_eof = 0x0002, + MS_decode_error = 0x0004, + MS_error_flags = 0x000f, + + MS_playing = 0x0100, + MS_cvt_decoded = 0x0200, +}; + +typedef struct { + SDL_RWops *rw; + int freerw; + struct mad_stream stream; + struct mad_frame frame; + struct mad_synth synth; + int frames_read; + mad_timer_t next_frame_start; + int volume; + int status; + int output_begin, output_end; + SDL_AudioSpec mixer; + SDL_AudioCVT cvt; + + unsigned char input_buffer[MAD_INPUT_BUFFER_SIZE + MAD_BUFFER_GUARD]; + unsigned char output_buffer[MAD_OUTPUT_BUFFER_SIZE]; +} mad_data; + +mad_data *mad_openFileRW(SDL_RWops *rw, SDL_AudioSpec *mixer, int freerw); +void mad_closeFile(mad_data *mp3_mad); + +void mad_start(mad_data *mp3_mad); +void mad_stop(mad_data *mp3_mad); +int mad_isPlaying(mad_data *mp3_mad); + +int mad_getSamples(mad_data *mp3_mad, Uint8 *stream, int len); +void mad_seek(mad_data *mp3_mad, double position); +void mad_setVolume(mad_data *mp3_mad, int volume); + +#endif diff --git a/apps/plugins/sdl/SDL_mixer/music_mod.c b/apps/plugins/sdl/SDL_mixer/music_mod.c new file mode 100644 index 0000000000..be17902266 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/music_mod.c @@ -0,0 +1,346 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id: music_mod.c 4211 2008-12-08 00:27:32Z slouken $ */ + +#ifdef MOD_MUSIC + +/* This file supports MOD tracker music streams */ + +#include "SDL_mixer.h" +#include "dynamic_mod.h" +#include "music_mod.h" + +#include "mikmod.h" + +#define SDL_SURROUND +#ifdef SDL_SURROUND +#define MAX_OUTPUT_CHANNELS 6 +#else +#define MAX_OUTPUT_CHANNELS 2 +#endif + +/* Reference for converting mikmod output to 4/6 channels */ +static int current_output_channels; +static Uint16 current_output_format; + +static int music_swap8; +static int music_swap16; + +/* Initialize the MOD player, with the given mixer settings + This function returns 0, or -1 if there was an error. + */ +int MOD_init(SDL_AudioSpec *mixerfmt) +{ + CHAR *list; + + if ( !Mix_Init(MIX_INIT_MOD) ) { + return -1; + } + + /* Set the MikMod music format */ + music_swap8 = 0; + music_swap16 = 0; + switch (mixerfmt->format) { + + case AUDIO_U8: + case AUDIO_S8: { + if ( mixerfmt->format == AUDIO_S8 ) { + music_swap8 = 1; + } + *mikmod.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 ( mixerfmt->format == AUDIO_S16MSB ) { +#else + if ( mixerfmt->format == AUDIO_S16LSB ) { +#endif + music_swap16 = 1; + } + *mikmod.md_mode = DMODE_16BITS; + } + break; + + default: { + Mix_SetError("Unknown hardware audio format"); + return -1; + } + } + current_output_channels = mixerfmt->channels; + current_output_format = mixerfmt->format; + if ( mixerfmt->channels > 1 ) { + if ( mixerfmt->channels > MAX_OUTPUT_CHANNELS ) { + Mix_SetError("Hardware uses more channels than mixerfmt"); + return -1; + } + *mikmod.md_mode |= DMODE_STEREO; + } + *mikmod.md_mixfreq = mixerfmt->freq; + *mikmod.md_device = 0; + *mikmod.md_volume = 96; + *mikmod.md_musicvolume = 128; + *mikmod.md_sndfxvolume = 128; + *mikmod.md_pansep = 128; + *mikmod.md_reverb = 0; + *mikmod.md_mode |= DMODE_HQMIXER|DMODE_SOFT_MUSIC|DMODE_SURROUND; + + list = mikmod.MikMod_InfoDriver(); + if ( list ) + free(list); + else + mikmod.MikMod_RegisterDriver(mikmod.drv_nos); + + list = mikmod.MikMod_InfoLoader(); + if ( list ) + free(list); + else + mikmod.MikMod_RegisterAllLoaders(); + + if ( mikmod.MikMod_Init(NULL) ) { + Mix_SetError("%s", mikmod.MikMod_strerror(*mikmod.MikMod_errno)); + return -1; + } + + return 0; +} + +/* Uninitialize the music players */ +void MOD_exit(void) +{ + if (mikmod.MikMod_Exit) { + mikmod.MikMod_Exit(); + } +} + +/* Set the volume for a MOD stream */ +void MOD_setvolume(MODULE *music, int volume) +{ + mikmod.Player_SetVolume((SWORD)volume); +} + +typedef struct +{ + MREADER mr; + long offset; + long eof; + SDL_RWops *rw; +} LMM_MREADER; + +BOOL LMM_Seek(struct MREADER *mr,long to,int dir) +{ + LMM_MREADER* lmmmr = (LMM_MREADER*)mr; + if ( dir == SEEK_SET ) { + to += lmmmr->offset; + } + return (SDL_RWseek(lmmmr->rw, to, dir) < lmmmr->offset); +} +long LMM_Tell(struct MREADER *mr) +{ + LMM_MREADER* lmmmr = (LMM_MREADER*)mr; + return SDL_RWtell(lmmmr->rw) - lmmmr->offset; +} +BOOL LMM_Read(struct MREADER *mr,void *buf,size_t sz) +{ + LMM_MREADER* lmmmr = (LMM_MREADER*)mr; + return SDL_RWread(lmmmr->rw, buf, sz, 1); +} +int LMM_Get(struct MREADER *mr) +{ + unsigned char c; + LMM_MREADER* lmmmr = (LMM_MREADER*)mr; + if ( SDL_RWread(lmmmr->rw, &c, 1, 1) ) { + return c; + } + return EOF; +} +BOOL LMM_Eof(struct MREADER *mr) +{ + long offset; + LMM_MREADER* lmmmr = (LMM_MREADER*)mr; + offset = LMM_Tell(mr); + return offset >= lmmmr->eof; +} +MODULE *MikMod_LoadSongRW(SDL_RWops *rw, int maxchan) +{ + LMM_MREADER lmmmr = { + { LMM_Seek, LMM_Tell, LMM_Read, LMM_Get, LMM_Eof }, + 0, + 0, + 0 + }; + lmmmr.offset = SDL_RWtell(rw); + SDL_RWseek(rw, 0, RW_SEEK_END); + lmmmr.eof = SDL_RWtell(rw); + SDL_RWseek(rw, lmmmr.offset, RW_SEEK_SET); + lmmmr.rw = rw; + return mikmod.Player_LoadGeneric((MREADER*)&lmmmr, maxchan, 0); +} + +/* Load a MOD stream from an SDL_RWops object */ +MODULE *MOD_new_RW(SDL_RWops *rw, int freerw) +{ + MODULE *module; + + /* Make sure the mikmod library is loaded */ + if ( !Mix_Init(MIX_INIT_MOD) ) { + if ( freerw ) { + SDL_RWclose(rw); + } + return NULL; + } + + module = MikMod_LoadSongRW(rw,64); + if (!module) { + Mix_SetError("%s", mikmod.MikMod_strerror(*mikmod.MikMod_errno)); + if ( freerw ) { + SDL_RWclose(rw); + } + return NULL; + } + + /* Stop implicit looping, fade out and other flags. */ + module->extspd = 1; + module->panflag = 1; + module->wrap = 0; + module->loop = 0; +#if 0 /* Don't set fade out by default - unfortunately there's no real way +to query the status of the song or set trigger actions. Hum. */ + module->fadeout = 1; +#endif + + if ( freerw ) { + SDL_RWclose(rw); + } + return module; +} + +/* Start playback of a given MOD stream */ +void MOD_play(MODULE *music) +{ + mikmod.Player_Start(music); +} + +/* Return non-zero if a stream is currently playing */ +int MOD_playing(MODULE *music) +{ + return mikmod.Player_Active(); +} + +/* Play some of a stream previously started with MOD_play() */ +int MOD_playAudio(MODULE *music, Uint8 *stream, int len) +{ + if (current_output_channels > 2) { + int small_len = 2 * len / current_output_channels; + int i; + Uint8 *src, *dst; + + mikmod.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 { + mikmod.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; + } + } + return 0; +} + +/* Stop playback of a stream previously started with MOD_play() */ +void MOD_stop(MODULE *music) +{ + mikmod.Player_Stop(); +} + +/* Close the given MOD stream */ +void MOD_delete(MODULE *music) +{ + mikmod.Player_Free(music); +} + +/* Jump (seek) to a given position (time is in seconds) */ +void MOD_jump_to_time(MODULE *music, double time) +{ + mikmod.Player_SetPosition((UWORD)time); +} + +#endif /* MOD_MUSIC */ diff --git a/apps/plugins/sdl/SDL_mixer/music_mod.h b/apps/plugins/sdl/SDL_mixer/music_mod.h new file mode 100644 index 0000000000..4328e2b2c7 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/music_mod.h @@ -0,0 +1,62 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id: music_mod.h 4211 2008-12-08 00:27:32Z slouken $ */ + +#ifdef MOD_MUSIC + +/* This file supports MOD tracker music streams */ + +struct MODULE; + +/* Initialize the Ogg Vorbis player, with the given mixer settings + This function returns 0, or -1 if there was an error. + */ +extern int MOD_init(SDL_AudioSpec *mixer); + +/* Uninitialize the music players */ +extern void MOD_exit(void); + +/* Set the volume for a MOD stream */ +extern void MOD_setvolume(struct MODULE *music, int volume); + +/* Load a MOD stream from an SDL_RWops object */ +extern struct MODULE *MOD_new_RW(SDL_RWops *rw, int freerw); + +/* Start playback of a given MOD stream */ +extern void MOD_play(struct MODULE *music); + +/* Return non-zero if a stream is currently playing */ +extern int MOD_playing(struct MODULE *music); + +/* Play some of a stream previously started with MOD_play() */ +extern int MOD_playAudio(struct MODULE *music, Uint8 *stream, int len); + +/* Stop playback of a stream previously started with MOD_play() */ +extern void MOD_stop(struct MODULE *music); + +/* Close the given MOD stream */ +extern void MOD_delete(struct MODULE *music); + +/* Jump (seek) to a given position (time is in seconds) */ +extern void MOD_jump_to_time(struct MODULE *music, double time); + +#endif /* MOD_MUSIC */ diff --git a/apps/plugins/sdl/SDL_mixer/music_modplug.c b/apps/plugins/sdl/SDL_mixer/music_modplug.c new file mode 100644 index 0000000000..31a7ba200f --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/music_modplug.c @@ -0,0 +1,239 @@ +#ifdef MODPLUG_MUSIC + +#include "music_modplug.h" + +static int current_output_channels=0; +static int music_swap8=0; +static int music_swap16=0; +static ModPlug_Settings settings; + +int modplug_init(SDL_AudioSpec *spec) +{ + ModPlug_GetSettings(&settings); + settings.mFlags=MODPLUG_ENABLE_OVERSAMPLING; + current_output_channels=spec->channels; + settings.mChannels=spec->channels>1?2:1; + settings.mBits=spec->format&0xFF; + + music_swap8 = 0; + music_swap16 = 0; + + switch(spec->format) + { + case AUDIO_U8: + case AUDIO_S8: { + if ( spec->format == AUDIO_S8 ) { + music_swap8 = 1; + } + settings.mBits=8; + } + break; + + case AUDIO_S16LSB: + case AUDIO_S16MSB: { + /* See if we need to correct MikMod mixing */ +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + if ( spec->format == AUDIO_S16MSB ) { +#else + if ( spec->format == AUDIO_S16LSB ) { +#endif + music_swap16 = 1; + } + settings.mBits=16; + } + break; + + default: { + Mix_SetError("Unknown hardware audio format"); + return -1; + } + + } + + settings.mFrequency=spec->freq; /*TODO: limit to 11025, 22050, or 44100 ? */ + settings.mResamplingMode=MODPLUG_RESAMPLE_FIR; + settings.mReverbDepth=0; + settings.mReverbDelay=100; + settings.mBassAmount=0; + settings.mBassRange=50; + settings.mSurroundDepth=0; + settings.mSurroundDelay=10; + settings.mLoopCount=0; + ModPlug_SetSettings(&settings); + return 0; +} + +/* Uninitialize the music players */ +void modplug_exit() +{ +} + +/* Set the volume for a modplug stream */ +void modplug_setvolume(modplug_data *music, int volume) +{ + ModPlug_SetMasterVolume(music->file, volume*4); +} + +/* Load a modplug stream from an SDL_RWops object */ +modplug_data *modplug_new_RW(SDL_RWops *rw, int freerw) +{ + modplug_data *music=NULL; + long offset,sz; + char *buf=NULL; + + offset = SDL_RWtell(rw); + SDL_RWseek(rw, 0, RW_SEEK_END); + sz = SDL_RWtell(rw)-offset; + SDL_RWseek(rw, offset, RW_SEEK_SET); + buf=(char*)SDL_malloc(sz); + if(buf) + { + if(SDL_RWread(rw, buf, sz, 1)==1) + { + music=(modplug_data*)SDL_malloc(sizeof(modplug_data)); + if (music) + { + music->playing=0; + music->file=ModPlug_Load(buf,sz); + if(!music->file) + { + SDL_free(music); + music=NULL; + } + } + else + { + SDL_OutOfMemory(); + } + } + SDL_free(buf); + } + else + { + SDL_OutOfMemory(); + } + if (freerw) { + SDL_RWclose(rw); + } + return music; +} + +/* Start playback of a given modplug stream */ +void modplug_play(modplug_data *music) +{ + ModPlug_Seek(music->file,0); + music->playing=1; +} + +/* Return non-zero if a stream is currently playing */ +int modplug_playing(modplug_data *music) +{ + return music && music->playing; +} + +/* Play some of a stream previously started with modplug_play() */ +int modplug_playAudio(modplug_data *music, Uint8 *stream, int len) +{ + if (current_output_channels > 2) { + int small_len = 2 * len / current_output_channels; + int i; + Uint8 *src, *dst; + + i=ModPlug_Read(music->file, stream, small_len); + if(i<small_len) + { + memset(stream+i,0,small_len-i); + music->playing=0; + } + /* and extend to len by copying channels */ + src = stream + small_len; + dst = stream + len; + + switch (settings.mBits) { + 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 { + int i=ModPlug_Read(music->file, stream, len); + if(i<len) + { + memset(stream+i,0,len-i); + music->playing=0; + } + } + 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; + } + } + return 0; +} + +/* Stop playback of a stream previously started with modplug_play() */ +void modplug_stop(modplug_data *music) +{ + music->playing=0; +} + +/* Close the given modplug stream */ +void modplug_delete(modplug_data *music) +{ + ModPlug_Unload(music->file); + SDL_free(music); +} + +/* Jump (seek) to a given position (time is in seconds) */ +void modplug_jump_to_time(modplug_data *music, double time) +{ + ModPlug_Seek(music->file,(int)(time*1000)); +} + +#endif diff --git a/apps/plugins/sdl/SDL_mixer/music_modplug.h b/apps/plugins/sdl/SDL_mixer/music_modplug.h new file mode 100644 index 0000000000..92cbafd094 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/music_modplug.h @@ -0,0 +1,42 @@ +#ifdef MODPLUG_MUSIC + +#include "modplug.h" +#include "SDL_rwops.h" +#include "SDL_audio.h" +#include "SDL_mixer.h" + +typedef struct { + ModPlugFile *file; + int playing; +} modplug_data; + +int modplug_init(SDL_AudioSpec *mixer); + +/* Uninitialize the music players */ +void modplug_exit(void); + +/* Set the volume for a modplug stream */ +void modplug_setvolume(modplug_data *music, int volume); + +/* Load a modplug stream from an SDL_RWops object */ +modplug_data *modplug_new_RW(SDL_RWops *rw, int freerw); + +/* Start playback of a given modplug stream */ +void modplug_play(modplug_data *music); + +/* Return non-zero if a stream is currently playing */ +int modplug_playing(modplug_data *music); + +/* Play some of a stream previously started with modplug_play() */ +int modplug_playAudio(modplug_data *music, Uint8 *stream, int len); + +/* Stop playback of a stream previously started with modplug_play() */ +void modplug_stop(modplug_data *music); + +/* Close the given modplug stream */ +void modplug_delete(modplug_data *music); + +/* Jump (seek) to a given position (time is in seconds) */ +void modplug_jump_to_time(modplug_data *music, double time); + +#endif diff --git a/apps/plugins/sdl/SDL_mixer/music_ogg.c b/apps/plugins/sdl/SDL_mixer/music_ogg.c new file mode 100644 index 0000000000..de5f9c5421 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/music_ogg.c @@ -0,0 +1,230 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id$ */ + +#ifdef OGG_MUSIC + +/* This file supports Ogg Vorbis music streams */ + +#include "SDL_mixer.h" +#include "dynamic_ogg.h" +#include "music_ogg.h" + +/* This is the format of the audio mixer data */ +static SDL_AudioSpec mixer; + +/* Initialize the Ogg Vorbis player, with the given mixer settings + This function returns 0, or -1 if there was an error. + */ +int OGG_init(SDL_AudioSpec *mixerfmt) +{ + mixer = *mixerfmt; + return(0); +} + +/* Set the volume for an OGG stream */ +void OGG_setvolume(OGG_music *music, int volume) +{ + music->volume = volume; +} + +static size_t sdl_read_func(void *ptr, size_t size, size_t nmemb, void *datasource) +{ + return SDL_RWread((SDL_RWops*)datasource, ptr, size, nmemb); +} + +static int sdl_seek_func(void *datasource, ogg_int64_t offset, int whence) +{ + return SDL_RWseek((SDL_RWops*)datasource, (int)offset, whence); +} + +static long sdl_tell_func(void *datasource) +{ + return SDL_RWtell((SDL_RWops*)datasource); +} + +/* Load an OGG stream from an SDL_RWops object */ +OGG_music *OGG_new_RW(SDL_RWops *rw, int freerw) +{ + OGG_music *music; + ov_callbacks callbacks; + + if ( !Mix_Init(MIX_INIT_OGG) ) { + if ( freerw ) { + SDL_RWclose(rw); + } + return(NULL); + } + + SDL_memset(&callbacks, 0, sizeof(callbacks)); + callbacks.read_func = sdl_read_func; + callbacks.seek_func = sdl_seek_func; + callbacks.tell_func = sdl_tell_func; + + music = (OGG_music *)SDL_malloc(sizeof *music); + if ( music ) { + /* Initialize the music structure */ + memset(music, 0, (sizeof *music)); + music->rw = rw; + music->freerw = freerw; + OGG_stop(music); + OGG_setvolume(music, MIX_MAX_VOLUME); + music->section = -1; + + if ( vorbis.ov_open_callbacks(rw, &music->vf, NULL, 0, callbacks) < 0 ) { + SDL_free(music); + if ( freerw ) { + SDL_RWclose(rw); + } + SDL_SetError("Not an Ogg Vorbis audio stream"); + return(NULL); + } + } else { + if ( freerw ) { + SDL_RWclose(rw); + } + SDL_OutOfMemory(); + return(NULL); + } + return(music); +} + +/* Start playback of a given OGG stream */ +void OGG_play(OGG_music *music) +{ + music->playing = 1; +} + +/* Return non-zero if a stream is currently playing */ +int OGG_playing(OGG_music *music) +{ + return(music->playing); +} + +/* Read some Ogg stream data and convert it for output */ +static void OGG_getsome(OGG_music *music) +{ + int section; + int len; + char data[4096]; + SDL_AudioCVT *cvt; + +#ifdef OGG_USE_TREMOR + len = vorbis.ov_read(&music->vf, data, sizeof(data), §ion); +#else + len = vorbis.ov_read(&music->vf, data, sizeof(data), 0, 2, 1, §ion); +#endif + if ( len <= 0 ) { + if ( len == 0 ) { + music->playing = 0; + } + return; + } + cvt = &music->cvt; + if ( section != music->section ) { + vorbis_info *vi; + + vi = vorbis.ov_info(&music->vf, -1); + SDL_BuildAudioCVT(cvt, AUDIO_S16, vi->channels, vi->rate, + mixer.format,mixer.channels,mixer.freq); + if ( cvt->buf ) { + SDL_free(cvt->buf); + } + cvt->buf = (Uint8 *)SDL_malloc(sizeof(data)*cvt->len_mult); + music->section = section; + } + if ( cvt->buf ) { + memcpy(cvt->buf, data, len); + if ( cvt->needed ) { + cvt->len = len; + SDL_ConvertAudio(cvt); + } else { + cvt->len_cvt = len; + } + music->len_available = music->cvt.len_cvt; + music->snd_available = music->cvt.buf; + } else { + SDL_SetError("Out of memory"); + music->playing = 0; + } +} + +/* Play some of a stream previously started with OGG_play() */ +int OGG_playAudio(OGG_music *music, Uint8 *snd, int len) +{ + int mixable; + + while ( (len > 0) && music->playing ) { + if ( ! music->len_available ) { + OGG_getsome(music); + } + mixable = len; + if ( mixable > music->len_available ) { + mixable = music->len_available; + } + if ( music->volume == MIX_MAX_VOLUME ) { + memcpy(snd, music->snd_available, mixable); + } else { + SDL_MixAudio(snd, music->snd_available, mixable, + music->volume); + } + music->len_available -= mixable; + music->snd_available += mixable; + len -= mixable; + snd += mixable; + } + + return len; +} + +/* Stop playback of a stream previously started with OGG_play() */ +void OGG_stop(OGG_music *music) +{ + music->playing = 0; +} + +/* Close the given OGG stream */ +void OGG_delete(OGG_music *music) +{ + if ( music ) { + if ( music->cvt.buf ) { + SDL_free(music->cvt.buf); + } + if ( music->freerw ) { + SDL_RWclose(music->rw); + } + vorbis.ov_clear(&music->vf); + SDL_free(music); + } +} + +/* Jump (seek) to a given position (time is in seconds) */ +void OGG_jump_to_time(OGG_music *music, double time) +{ +#ifdef OGG_USE_TREMOR + vorbis.ov_time_seek( &music->vf, (ogg_int64_t)time ); +#else + vorbis.ov_time_seek( &music->vf, time ); +#endif +} + +#endif /* OGG_MUSIC */ diff --git a/apps/plugins/sdl/SDL_mixer/music_ogg.h b/apps/plugins/sdl/SDL_mixer/music_ogg.h new file mode 100644 index 0000000000..4d93a2bc62 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/music_ogg.h @@ -0,0 +1,75 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id$ */ + +#ifdef OGG_MUSIC + +/* This file supports Ogg Vorbis music streams */ + +#ifdef OGG_USE_TREMOR +#include <tremor/ivorbisfile.h> +#else +#include <vorbis/vorbisfile.h> +#endif + +typedef struct { + SDL_RWops *rw; + int freerw; + int playing; + int volume; + OggVorbis_File vf; + int section; + SDL_AudioCVT cvt; + int len_available; + Uint8 *snd_available; +} OGG_music; + +/* Initialize the Ogg Vorbis player, with the given mixer settings + This function returns 0, or -1 if there was an error. + */ +extern int OGG_init(SDL_AudioSpec *mixer); + +/* Set the volume for an OGG stream */ +extern void OGG_setvolume(OGG_music *music, int volume); + +/* Load an OGG stream from an SDL_RWops object */ +extern OGG_music *OGG_new_RW(SDL_RWops *rw, int freerw); + +/* Start playback of a given OGG stream */ +extern void OGG_play(OGG_music *music); + +/* Return non-zero if a stream is currently playing */ +extern int OGG_playing(OGG_music *music); + +/* Play some of a stream previously started with OGG_play() */ +extern int OGG_playAudio(OGG_music *music, Uint8 *stream, int len); + +/* Stop playback of a stream previously started with OGG_play() */ +extern void OGG_stop(OGG_music *music); + +/* Close the given OGG stream */ +extern void OGG_delete(OGG_music *music); + +/* Jump (seek) to a given position (time is in seconds) */ +extern void OGG_jump_to_time(OGG_music *music, double time); + +#endif /* OGG_MUSIC */ diff --git a/apps/plugins/sdl/SDL_mixer/native_midi/native_midi.h b/apps/plugins/sdl/SDL_mixer/native_midi/native_midi.h new file mode 100644 index 0000000000..17e4769d86 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/native_midi/native_midi.h @@ -0,0 +1,38 @@ +/* + native_midi: Hardware Midi support for the SDL_mixer library + Copyright (C) 2000 Florian 'Proff' Schulze <florian.proff.schulze@gmx.net> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef _NATIVE_MIDI_H_ +#define _NATIVE_MIDI_H_ + +#include <SDL_rwops.h> + +typedef struct _NativeMidiSong NativeMidiSong; + +int native_midi_detect(); +NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw, int freerw); +void native_midi_freesong(NativeMidiSong *song); +void native_midi_start(NativeMidiSong *song, int loops); +void native_midi_stop(); +int native_midi_active(); +void native_midi_setvolume(int volume); +const char *native_midi_error(void); + +#endif /* _NATIVE_MIDI_H_ */ diff --git a/apps/plugins/sdl/SDL_mixer/native_midi/native_midi_common.c b/apps/plugins/sdl/SDL_mixer/native_midi/native_midi_common.c new file mode 100644 index 0000000000..12294750f8 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/native_midi/native_midi_common.c @@ -0,0 +1,409 @@ +/* + native_midi: Hardware Midi support for the SDL_mixer library + Copyright (C) 2000,2001 Florian 'Proff' Schulze <florian.proff.schulze@gmx.net> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + + +#include "native_midi_common.h" + +#include "../SDL_mixer.h" + +#include <stdlib.h> +#include <string.h> +#include <limits.h> + + +/* The maximum number of midi tracks that we can handle +#define MIDI_TRACKS 32 */ + + +/* A single midi track as read from the midi file */ +typedef struct +{ + Uint8 *data; /* MIDI message stream */ + int len; /* length of the track data */ +} MIDITrack; + +/* A midi file, stripped down to the absolute minimum - divison & track data */ +typedef struct +{ + int division; /* number of pulses per quarter note (ppqn) */ + int nTracks; /* number of tracks */ + MIDITrack *track; /* tracks */ +} MIDIFile; + + +/* Some macros that help us stay endianess-independant */ +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define BE_SHORT(x) (x) +#define BE_LONG(x) (x) +#else +#define BE_SHORT(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF)) +#define BE_LONG(x) ((((x)&0x0000FF)<<24) | \ + (((x)&0x00FF00)<<8) | \ + (((x)&0xFF0000)>>8) | \ + (((x)>>24)&0xFF)) +#endif + + + +/* Get Variable Length Quantity */ +static int GetVLQ(MIDITrack *track, int *currentPos) +{ + int l = 0; + Uint8 c; + while(1) + { + c = track->data[*currentPos]; + (*currentPos)++; + l += (c & 0x7f); + if (!(c & 0x80)) + return l; + l <<= 7; + } +} + +/* Create a single MIDIEvent */ +static MIDIEvent *CreateEvent(Uint32 time, Uint8 event, Uint8 a, Uint8 b) +{ + MIDIEvent *newEvent; + + newEvent = calloc(1, sizeof(MIDIEvent)); + + if (newEvent) + { + newEvent->time = time; + newEvent->status = event; + newEvent->data[0] = a; + newEvent->data[1] = b; + } + else + Mix_SetError("Out of memory"); + + return newEvent; +} + +/* Convert a single midi track to a list of MIDIEvents */ +static MIDIEvent *MIDITracktoStream(MIDITrack *track) +{ + Uint32 atime = 0; + Uint32 len = 0; + Uint8 event,type,a,b; + Uint8 laststatus = 0; + Uint8 lastchan = 0; + int currentPos = 0; + int end = 0; + MIDIEvent *head = CreateEvent(0,0,0,0); /* dummy event to make handling the list easier */ + MIDIEvent *currentEvent = head; + + while (!end) + { + if (currentPos >= track->len) + break; /* End of data stream reached */ + + atime += GetVLQ(track, ¤tPos); + event = track->data[currentPos++]; + + /* Handle SysEx seperatly */ + if (((event>>4) & 0x0F) == MIDI_STATUS_SYSEX) + { + if (event == 0xFF) + { + type = track->data[currentPos]; + currentPos++; + switch(type) + { + case 0x2f: /* End of data marker */ + end = 1; + case 0x51: /* Tempo change */ + /* + a=track->data[currentPos]; + b=track->data[currentPos+1]; + c=track->data[currentPos+2]; + AddEvent(song, atime, MEVT_TEMPO, c, b, a); + */ + break; + } + } + else + type = 0; + + len = GetVLQ(track, ¤tPos); + + /* Create an event and attach the extra data, if any */ + currentEvent->next = CreateEvent(atime, event, type, 0); + currentEvent = currentEvent->next; + if (NULL == currentEvent) + { + FreeMIDIEventList(head); + return NULL; + } + if (len) + { + currentEvent->extraLen = len; + currentEvent->extraData = malloc(len); + memcpy(currentEvent->extraData, &(track->data[currentPos]), len); + currentPos += len; + } + } + else + { + a = event; + if (a & 0x80) /* It's a status byte */ + { + /* Extract channel and status information */ + lastchan = a & 0x0F; + laststatus = (a>>4) & 0x0F; + + /* Read the next byte which should always be a data byte */ + a = track->data[currentPos++] & 0x7F; + } + switch(laststatus) + { + case MIDI_STATUS_NOTE_OFF: + case MIDI_STATUS_NOTE_ON: /* Note on */ + case MIDI_STATUS_AFTERTOUCH: /* Key Pressure */ + case MIDI_STATUS_CONTROLLER: /* Control change */ + case MIDI_STATUS_PITCH_WHEEL: /* Pitch wheel */ + b = track->data[currentPos++] & 0x7F; + currentEvent->next = CreateEvent(atime, (Uint8)((laststatus<<4)+lastchan), a, b); + currentEvent = currentEvent->next; + if (NULL == currentEvent) + { + FreeMIDIEventList(head); + return NULL; + } + break; + + case MIDI_STATUS_PROG_CHANGE: /* Program change */ + case MIDI_STATUS_PRESSURE: /* Channel pressure */ + a &= 0x7f; + currentEvent->next = CreateEvent(atime, (Uint8)((laststatus<<4)+lastchan), a, 0); + currentEvent = currentEvent->next; + if (NULL == currentEvent) + { + FreeMIDIEventList(head); + return NULL; + } + break; + + default: /* Sysex already handled above */ + break; + } + } + } + + currentEvent = head->next; + free(head); /* release the dummy head event */ + return currentEvent; +} + +/* + * Convert a midi song, consisting of up to 32 tracks, to a list of MIDIEvents. + * To do so, first convert the tracks seperatly, then interweave the resulting + * MIDIEvent-Lists to one big list. + */ +static MIDIEvent *MIDItoStream(MIDIFile *mididata) +{ + MIDIEvent **track; + MIDIEvent *head = CreateEvent(0,0,0,0); /* dummy event to make handling the list easier */ + MIDIEvent *currentEvent = head; + int trackID; + + if (NULL == head) + return NULL; + + track = (MIDIEvent**) calloc(1, sizeof(MIDIEvent*) * mididata->nTracks); + if (NULL == head) + return NULL; + + /* First, convert all tracks to MIDIEvent lists */ + for (trackID = 0; trackID < mididata->nTracks; trackID++) + track[trackID] = MIDITracktoStream(&mididata->track[trackID]); + + /* Now, merge the lists. */ + /* TODO */ + while(1) + { + Uint32 lowestTime = INT_MAX; + int currentTrackID = -1; + + /* Find the next event */ + for (trackID = 0; trackID < mididata->nTracks; trackID++) + { + if (track[trackID] && (track[trackID]->time < lowestTime)) + { + currentTrackID = trackID; + lowestTime = track[currentTrackID]->time; + } + } + + /* Check if we processes all events */ + if (currentTrackID == -1) + break; + + currentEvent->next = track[currentTrackID]; + track[currentTrackID] = track[currentTrackID]->next; + + currentEvent = currentEvent->next; + + + lowestTime = 0; + } + + /* Make sure the list is properly terminated */ + currentEvent->next = 0; + + currentEvent = head->next; + free(track); + free(head); /* release the dummy head event */ + return currentEvent; +} + +static int ReadMIDIFile(MIDIFile *mididata, SDL_RWops *rw) +{ + int i = 0; + Uint32 ID; + Uint32 size; + Uint16 format; + Uint16 tracks; + Uint16 division; + + if (!mididata) + return 0; + if (!rw) + return 0; + + /* Make sure this is really a MIDI file */ + SDL_RWread(rw, &ID, 1, 4); + if (BE_LONG(ID) != 'MThd') + return 0; + + /* Header size must be 6 */ + SDL_RWread(rw, &size, 1, 4); + size = BE_LONG(size); + if (size != 6) + return 0; + + /* We only support format 0 and 1, but not 2 */ + SDL_RWread(rw, &format, 1, 2); + format = BE_SHORT(format); + if (format != 0 && format != 1) + return 0; + + SDL_RWread(rw, &tracks, 1, 2); + tracks = BE_SHORT(tracks); + mididata->nTracks = tracks; + + /* Allocate tracks */ + mididata->track = (MIDITrack*) calloc(1, sizeof(MIDITrack) * mididata->nTracks); + if (NULL == mididata->track) + { + Mix_SetError("Out of memory"); + goto bail; + } + + /* Retrieve the PPQN value, needed for playback */ + SDL_RWread(rw, &division, 1, 2); + mididata->division = BE_SHORT(division); + + + for (i=0; i<tracks; i++) + { + SDL_RWread(rw, &ID, 1, 4); /* We might want to verify this is MTrk... */ + SDL_RWread(rw, &size, 1, 4); + size = BE_LONG(size); + mididata->track[i].len = size; + mididata->track[i].data = malloc(size); + if (NULL == mididata->track[i].data) + { + Mix_SetError("Out of memory"); + goto bail; + } + SDL_RWread(rw, mididata->track[i].data, 1, size); + } + return 1; + +bail: + for(;i >= 0; i--) + { + if (mididata->track[i].data) + free(mididata->track[i].data); + } + + return 0; +} + +MIDIEvent *CreateMIDIEventList(SDL_RWops *rw, Uint16 *division) +{ + MIDIFile *mididata = NULL; + MIDIEvent *eventList; + int trackID; + + mididata = calloc(1, sizeof(MIDIFile)); + if (!mididata) + return NULL; + + /* Open the file */ + if ( rw != NULL ) + { + /* Read in the data */ + if ( ! ReadMIDIFile(mididata, rw)) + { + free(mididata); + return NULL; + } + } + else + { + free(mididata); + return NULL; + } + + if (division) + *division = mididata->division; + + eventList = MIDItoStream(mididata); + + for(trackID = 0; trackID < mididata->nTracks; trackID++) + { + if (mididata->track[trackID].data) + free(mididata->track[trackID].data); + } + free(mididata->track); + free(mididata); + + return eventList; +} + +void FreeMIDIEventList(MIDIEvent *head) +{ + MIDIEvent *cur, *next; + + cur = head; + + while (cur) + { + next = cur->next; + if (cur->extraData) + free (cur->extraData); + free (cur); + cur = next; + } +} diff --git a/apps/plugins/sdl/SDL_mixer/native_midi/native_midi_common.h b/apps/plugins/sdl/SDL_mixer/native_midi/native_midi_common.h new file mode 100644 index 0000000000..e0400272d8 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/native_midi/native_midi_common.h @@ -0,0 +1,63 @@ +/* + native_midi: Hardware Midi support for the SDL_mixer library + Copyright (C) 2000,2001 Florian 'Proff' Schulze <florian.proff.schulze@gmx.net> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef _NATIVE_MIDI_COMMON_H_ +#define _NATIVE_MIDI_COMMON_H_ + +#include "SDL.h" + +/* Midi Status Bytes */ +#define MIDI_STATUS_NOTE_OFF 0x8 +#define MIDI_STATUS_NOTE_ON 0x9 +#define MIDI_STATUS_AFTERTOUCH 0xA +#define MIDI_STATUS_CONTROLLER 0xB +#define MIDI_STATUS_PROG_CHANGE 0xC +#define MIDI_STATUS_PRESSURE 0xD +#define MIDI_STATUS_PITCH_WHEEL 0xE +#define MIDI_STATUS_SYSEX 0xF + +/* We store the midi events in a linked list; this way it is + easy to shuffle the tracks together later on; and we are + flexible in the size of each elemnt. + */ +typedef struct MIDIEvent +{ + Uint32 time; /* Time at which this midi events occurs */ + Uint8 status; /* Status byte */ + Uint8 data[2]; /* 1 or 2 bytes additional data for most events */ + + Uint32 extraLen; /* For some SysEx events, we need additional storage */ + Uint8 *extraData; + + struct MIDIEvent *next; +} MIDIEvent; + + +/* Load a midifile to memory, converting it to a list of MIDIEvents. + This function returns a linked lists of MIDIEvents, 0 if an error occured. + */ +MIDIEvent *CreateMIDIEventList(SDL_RWops *rw, Uint16 *division); + +/* Release a MIDIEvent list after usage. */ +void FreeMIDIEventList(MIDIEvent *head); + + +#endif /* _NATIVE_MIDI_COMMON_H_ */ diff --git a/apps/plugins/sdl/SDL_mixer/native_midi/native_midi_haiku.cpp b/apps/plugins/sdl/SDL_mixer/native_midi/native_midi_haiku.cpp new file mode 100644 index 0000000000..8de350e80a --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/native_midi/native_midi_haiku.cpp @@ -0,0 +1,281 @@ +/* + native_midi_haiku: Native Midi support on Haiku for the SDL_mixer library + Copyright (C) 2010 Egor Suvorov <egor_suvorov@mail.ru> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" + +#ifdef __HAIKU__ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <MidiStore.h> +#include <MidiDefs.h> +#include <MidiSynthFile.h> +#include <algorithm> +#include <assert.h> +extern "C" { +#include "native_midi.h" +#include "native_midi_common.h" +} + +bool compareMIDIEvent(const MIDIEvent &a, const MIDIEvent &b) +{ + return a.time < b.time; +} + +class MidiEventsStore : public BMidi +{ + public: + MidiEventsStore() + { + fPlaying = false; + fLoops = 0; + } + virtual status_t Import(SDL_RWops *rw) + { + fEvs = CreateMIDIEventList(rw, &fDivision); + if (!fEvs) { + return B_BAD_MIDI_DATA; + } + fTotal = 0; + for (MIDIEvent *x = fEvs; x; x = x->next) fTotal++; + fPos = fTotal; + + sort_events(); + return B_OK; + } + virtual void Run() + { + fPlaying = true; + fPos = 0; + MIDIEvent *ev = fEvs; + + uint32 startTime = B_NOW; + while (KeepRunning()) + { + if (!ev) { + if (fLoops && fEvs) { + --fLoops; + fPos = 0; + ev = fEvs; + } else + break; + } + SprayEvent(ev, ev->time + startTime); + ev = ev->next; + fPos++; + } + fPos = fTotal; + fPlaying = false; + } + virtual ~MidiEventsStore() + { + if (!fEvs) return; + FreeMIDIEventList(fEvs); + fEvs = 0; + } + + bool IsPlaying() + { + return fPlaying; + } + + void SetLoops(int loops) + { + fLoops = loops; + } + + protected: + MIDIEvent *fEvs; + Uint16 fDivision; + + int fPos, fTotal; + int fLoops; + bool fPlaying; + + void SprayEvent(MIDIEvent *ev, uint32 time) + { + switch (ev->status & 0xF0) + { + case B_NOTE_OFF: + SprayNoteOff((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time); + break; + case B_NOTE_ON: + SprayNoteOn((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time); + break; + case B_KEY_PRESSURE: + SprayKeyPressure((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time); + break; + case B_CONTROL_CHANGE: + SprayControlChange((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time); + break; + case B_PROGRAM_CHANGE: + SprayProgramChange((ev->status & 0x0F) + 1, ev->data[0], time); + break; + case B_CHANNEL_PRESSURE: + SprayChannelPressure((ev->status & 0x0F) + 1, ev->data[0], time); + break; + case B_PITCH_BEND: + SprayPitchBend((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time); + break; + case 0xF: + switch (ev->status) + { + case B_SYS_EX_START: + SpraySystemExclusive(ev->extraData, ev->extraLen, time); + break; + case B_MIDI_TIME_CODE: + case B_SONG_POSITION: + case B_SONG_SELECT: + case B_CABLE_MESSAGE: + case B_TUNE_REQUEST: + case B_SYS_EX_END: + SpraySystemCommon(ev->status, ev->data[0], ev->data[1], time); + break; + case B_TIMING_CLOCK: + case B_START: + case B_STOP: + case B_CONTINUE: + case B_ACTIVE_SENSING: + SpraySystemRealTime(ev->status, time); + break; + case B_SYSTEM_RESET: + if (ev->data[0] == 0x51 && ev->data[1] == 0x03) + { + assert(ev->extraLen == 3); + int val = (ev->extraData[0] << 16) | (ev->extraData[1] << 8) | ev->extraData[2]; + int tempo = 60000000 / val; + SprayTempoChange(tempo, time); + } + else + { + SpraySystemRealTime(ev->status, time); + } + } + break; + } + } + + void sort_events() + { + MIDIEvent *items = new MIDIEvent[fTotal]; + MIDIEvent *x = fEvs; + for (int i = 0; i < fTotal; i++) + { + memcpy(items + i, x, sizeof(MIDIEvent)); + x = x->next; + } + std::sort(items, items + fTotal, compareMIDIEvent); + + x = fEvs; + for (int i = 0; i < fTotal; i++) + { + MIDIEvent *ne = x->next; + memcpy(x, items + i, sizeof(MIDIEvent)); + x->next = ne; + x = ne; + } + + for (x = fEvs; x && x->next; x = x->next) + assert(x->time <= x->next->time); + + delete[] items; + } +}; + +BMidiSynth synth; +struct _NativeMidiSong { + MidiEventsStore *store; +} *currentSong = NULL; + +char lasterr[1024]; + +int native_midi_detect() +{ + status_t res = synth.EnableInput(true, false); + return res == B_OK; +} + +void native_midi_setvolume(int volume) +{ + if (volume < 0) volume = 0; + if (volume > 128) volume = 128; + synth.SetVolume(volume / 128.0); +} + +NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw, int freerw) +{ + NativeMidiSong *song = new NativeMidiSong; + song->store = new MidiEventsStore; + status_t res = song->store->Import(rw); + + if (freerw) { + SDL_RWclose(rw); + } + if (res != B_OK) + { + snprintf(lasterr, sizeof lasterr, "Cannot Import() midi file: status_t=%d", res); + delete song->store; + delete song; + return NULL; + } + return song; +} + +void native_midi_freesong(NativeMidiSong *song) +{ + if (song == NULL) return; + song->store->Stop(); + song->store->Disconnect(&synth); + if (currentSong == song) + { + currentSong = NULL; + } + delete song->store; + delete song; song = 0; +} +void native_midi_start(NativeMidiSong *song, int loops) +{ + native_midi_stop(); + song->store->Connect(&synth); + song->store->SetLoops(loops); + song->store->Start(); + currentSong = song; +} +void native_midi_stop() +{ + if (currentSong == NULL) return; + currentSong->store->Stop(); + currentSong->store->Disconnect(&synth); + while (currentSong->store->IsPlaying()) + usleep(1000); + currentSong = NULL; +} +int native_midi_active() +{ + if (currentSong == NULL) return 0; + return currentSong->store->IsPlaying(); +} + +const char* native_midi_error(void) +{ + return lasterr; +} + +#endif /* __HAIKU__ */ diff --git a/apps/plugins/sdl/SDL_mixer/native_midi/native_midi_mac.c b/apps/plugins/sdl/SDL_mixer/native_midi/native_midi_mac.c new file mode 100644 index 0000000000..01e7877472 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/native_midi/native_midi_mac.c @@ -0,0 +1,644 @@ +/* + native_midi_mac: Native Midi support on MacOS for the SDL_mixer library + Copyright (C) 2001 Max Horn <max@quendi.de> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" +#include "SDL_endian.h" + +#if __MACOS__ /*|| __MACOSX__ */ + +#include "native_midi.h" +#include "native_midi_common.h" + +#if __MACOSX__ +#include <QuickTime/QuickTimeMusic.h> +#else +#include <QuickTimeMusic.h> +#endif + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + + +/* Native Midi song */ +struct _NativeMidiSong +{ + Uint32 *tuneSequence; + Uint32 *tuneHeader; +}; + +enum +{ + /* number of (32-bit) long words in a note request event */ + kNoteRequestEventLength = ((sizeof(NoteRequest)/sizeof(long)) + 2), + + /* number of (32-bit) long words in a marker event */ + kMarkerEventLength = 1, + + /* number of (32-bit) long words in a general event, minus its data */ + kGeneralEventLength = 2 +}; + +#define ERROR_BUF_SIZE 256 +#define BUFFER_INCREMENT 5000 + +#define REST_IF_NECESSARY() do {\ + int timeDiff = eventPos->time - lastEventTime; \ + if(timeDiff) \ + { \ + timeDiff = (int)(timeDiff*tick); \ + qtma_StuffRestEvent(*tunePos, timeDiff); \ + tunePos++; \ + lastEventTime = eventPos->time; \ + } \ + } while(0) + + +static Uint32 *BuildTuneSequence(MIDIEvent *evntlist, int ppqn, int part_poly_max[32], int part_to_inst[32], int *numParts); +static Uint32 *BuildTuneHeader(int part_poly_max[32], int part_to_inst[32], int numParts); + +/* The global TunePlayer instance */ +static TunePlayer gTunePlayer = NULL; +static int gInstaceCount = 0; +static Uint32 *gCurrentTuneSequence = NULL; +static char gErrorBuffer[ERROR_BUF_SIZE] = ""; + + +/* Check whether QuickTime is available */ +int native_midi_detect() +{ + /* TODO */ + return 1; +} + +NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw, int freerw) +{ + NativeMidiSong *song = NULL; + MIDIEvent *evntlist = NULL; + int part_to_inst[32]; + int part_poly_max[32]; + int numParts = 0; + Uint16 ppqn; + + /* Init the arrays */ + memset(part_poly_max,0,sizeof(part_poly_max)); + memset(part_to_inst,-1,sizeof(part_to_inst)); + + /* Attempt to load the midi file */ + evntlist = CreateMIDIEventList(rw, &ppqn); + if (!evntlist) + goto bail; + + /* Allocate memory for the song struct */ + song = malloc(sizeof(NativeMidiSong)); + if (!song) + goto bail; + + /* Build a tune sequence from the event list */ + song->tuneSequence = BuildTuneSequence(evntlist, ppqn, part_poly_max, part_to_inst, &numParts); + if(!song->tuneSequence) + goto bail; + + /* Now build a tune header from the data we collect above, create + all parts as needed and assign them the correct instrument. + */ + song->tuneHeader = BuildTuneHeader(part_poly_max, part_to_inst, numParts); + if(!song->tuneHeader) + goto bail; + + /* Increment the instance count */ + gInstaceCount++; + if (gTunePlayer == NULL) + gTunePlayer = OpenDefaultComponent(kTunePlayerComponentType, 0); + + /* Finally, free the event list */ + FreeMIDIEventList(evntlist); + + if (freerw) { + SDL_RWclose(rw); + } + return song; + +bail: + if (evntlist) + FreeMIDIEventList(evntlist); + + if (song) + { + if(song->tuneSequence) + free(song->tuneSequence); + + if(song->tuneHeader) + DisposePtr((Ptr)song->tuneHeader); + + free(song); + } + + if (freerw) { + SDL_RWclose(rw); + } + return NULL; +} + +void native_midi_freesong(NativeMidiSong *song) +{ + if(!song || !song->tuneSequence) + return; + + /* If this is the currently playing song, stop it now */ + if (song->tuneSequence == gCurrentTuneSequence) + native_midi_stop(); + + /* Finally, free the data storage */ + free(song->tuneSequence); + DisposePtr((Ptr)song->tuneHeader); + free(song); + + /* Increment the instance count */ + gInstaceCount--; + if ((gTunePlayer != NULL) && (gInstaceCount == 0)) + { + CloseComponent(gTunePlayer); + gTunePlayer = NULL; + } +} + +void native_midi_start(NativeMidiSong *song, int loops) +{ + UInt32 queueFlags = 0; + ComponentResult tpError; + + assert (gTunePlayer != NULL); + + /* FIXME: is this code even used anymore? */ + assert (loops == 0); + + SDL_PauseAudio(1); + SDL_UnlockAudio(); + + /* First, stop the currently playing music */ + native_midi_stop(); + + /* Set up the queue flags */ + queueFlags = kTuneStartNow; + + /* Set the time scale (units per second), we want milliseconds */ + tpError = TuneSetTimeScale(gTunePlayer, 1000); + if (tpError != noErr) + { + strncpy (gErrorBuffer, "MIDI error during TuneSetTimeScale", ERROR_BUF_SIZE); + goto done; + } + + /* Set the header, to tell what instruments are used */ + tpError = TuneSetHeader(gTunePlayer, (UInt32 *)song->tuneHeader); + if (tpError != noErr) + { + strncpy (gErrorBuffer, "MIDI error during TuneSetHeader", ERROR_BUF_SIZE); + goto done; + } + + /* Have it allocate whatever resources are needed */ + tpError = TunePreroll(gTunePlayer); + if (tpError != noErr) + { + strncpy (gErrorBuffer, "MIDI error during TunePreroll", ERROR_BUF_SIZE); + goto done; + } + + /* We want to play at normal volume */ + tpError = TuneSetVolume(gTunePlayer, 0x00010000); + if (tpError != noErr) + { + strncpy (gErrorBuffer, "MIDI error during TuneSetVolume", ERROR_BUF_SIZE); + goto done; + } + + /* Finally, start playing the full song */ + gCurrentTuneSequence = song->tuneSequence; + tpError = TuneQueue(gTunePlayer, (UInt32 *)song->tuneSequence, 0x00010000, 0, 0xFFFFFFFF, queueFlags, NULL, 0); + if (tpError != noErr) + { + strncpy (gErrorBuffer, "MIDI error during TuneQueue", ERROR_BUF_SIZE); + goto done; + } + +done: + SDL_LockAudio(); + SDL_PauseAudio(0); +} + +void native_midi_stop() +{ + if (gTunePlayer == NULL) + return; + + /* Stop music */ + TuneStop(gTunePlayer, 0); + + /* Deallocate all instruments */ + TuneUnroll(gTunePlayer); +} + +int native_midi_active() +{ + if (gTunePlayer != NULL) + { + TuneStatus ts; + + TuneGetStatus(gTunePlayer,&ts); + return ts.queueTime != 0; + } + else + return 0; +} + +void native_midi_setvolume(int volume) +{ + if (gTunePlayer == NULL) + return; + + /* QTMA olume may range from 0.0 to 1.0 (in 16.16 fixed point encoding) */ + TuneSetVolume(gTunePlayer, (0x00010000 * volume)/SDL_MIX_MAXVOLUME); +} + +const char *native_midi_error(void) +{ + return gErrorBuffer; +} + +Uint32 *BuildTuneSequence(MIDIEvent *evntlist, int ppqn, int part_poly_max[32], int part_to_inst[32], int *numParts) +{ + int part_poly[32]; + int channel_to_part[16]; + + int channel_pan[16]; + int channel_vol[16]; + int channel_pitch_bend[16]; + + int lastEventTime = 0; + int tempo = 500000; + double Ippqn = 1.0 / (1000*ppqn); + double tick = tempo * Ippqn; + MIDIEvent *eventPos = evntlist; + MIDIEvent *noteOffPos; + Uint32 *tunePos, *endPos; + Uint32 *tuneSequence; + size_t tuneSize; + + /* allocate space for the tune header */ + tuneSize = 5000; + tuneSequence = (Uint32 *)malloc(tuneSize * sizeof(Uint32)); + if (tuneSequence == NULL) + return NULL; + + /* Set starting position in our tune memory */ + tunePos = tuneSequence; + endPos = tuneSequence + tuneSize; + + /* Initialise the arrays */ + memset(part_poly,0,sizeof(part_poly)); + + memset(channel_to_part,-1,sizeof(channel_to_part)); + memset(channel_pan,-1,sizeof(channel_pan)); + memset(channel_vol,-1,sizeof(channel_vol)); + memset(channel_pitch_bend,-1,sizeof(channel_pitch_bend)); + + *numParts = 0; + + /* + * Now the major work - iterate over all GM events, + * and turn them into QuickTime Music format. + * At the same time, calculate the max. polyphony for each part, + * and also the part->instrument mapping. + */ + while(eventPos) + { + int status = (eventPos->status&0xF0)>>4; + int channel = eventPos->status&0x0F; + int part = channel_to_part[channel]; + int velocity, pitch; + int value, controller; + int bend; + int newInst; + + /* Check if we are running low on space... */ + if((tunePos+16) > endPos) + { + /* Resize our data storage. */ + Uint32 *oldTuneSequence = tuneSequence; + + tuneSize += BUFFER_INCREMENT; + tuneSequence = (Uint32 *)realloc(tuneSequence, tuneSize * sizeof(Uint32)); + if(oldTuneSequence != tuneSequence) + tunePos += tuneSequence - oldTuneSequence; + endPos = tuneSequence + tuneSize; + } + + switch (status) + { + case MIDI_STATUS_NOTE_OFF: + assert(part>=0 && part<=31); + + /* Keep track of the polyphony of the current part */ + part_poly[part]--; + break; + case MIDI_STATUS_NOTE_ON: + if (part < 0) + { + /* If no part is specified yet, we default to the first instrument, which + is piano (or the first drum kit if we are on the drum channel) + */ + int newInst; + + if (channel == 9) + newInst = kFirstDrumkit + 1; /* the first drum kit is the "no drum" kit! */ + else + newInst = kFirstGMInstrument; + part = channel_to_part[channel] = *numParts; + part_to_inst[(*numParts)++] = newInst; + } + /* TODO - add support for more than 32 parts using eXtended QTMA events */ + assert(part<=31); + + /* Decode pitch & velocity */ + pitch = eventPos->data[0]; + velocity = eventPos->data[1]; + + if (velocity == 0) + { + /* was a NOTE OFF in disguise, so we decrement the polyphony */ + part_poly[part]--; + } + else + { + /* Keep track of the polyphony of the current part */ + int foo = ++part_poly[part]; + if (part_poly_max[part] < foo) + part_poly_max[part] = foo; + + /* Now scan forward to find the matching NOTE OFF event */ + for(noteOffPos = eventPos; noteOffPos; noteOffPos = noteOffPos->next) + { + if ((noteOffPos->status&0xF0)>>4 == MIDI_STATUS_NOTE_OFF + && channel == (eventPos->status&0x0F) + && pitch == noteOffPos->data[0]) + break; + /* NOTE ON with velocity == 0 is the same as a NOTE OFF */ + if ((noteOffPos->status&0xF0)>>4 == MIDI_STATUS_NOTE_ON + && channel == (eventPos->status&0x0F) + && pitch == noteOffPos->data[0] + && 0 == noteOffPos->data[1]) + break; + } + + /* Did we find a note off? Should always be the case, but who knows... */ + if (noteOffPos) + { + /* We found a NOTE OFF, now calculate the note duration */ + int duration = (int)((noteOffPos->time - eventPos->time)*tick); + + REST_IF_NECESSARY(); + /* Now we need to check if we get along with a normal Note Event, or if we need an extended one... */ + if (duration < 2048 && pitch>=32 && pitch<=95 && velocity>=0 && velocity<=127) + { + qtma_StuffNoteEvent(*tunePos, part, pitch, velocity, duration); + tunePos++; + } + else + { + qtma_StuffXNoteEvent(*tunePos, *(tunePos+1), part, pitch, velocity, duration); + tunePos+=2; + } + } + } + break; + case MIDI_STATUS_AFTERTOUCH: + /* NYI - use kControllerAfterTouch. But how are the parameters to be mapped? */ + break; + case MIDI_STATUS_CONTROLLER: + controller = eventPos->data[0]; + value = eventPos->data[1]; + + switch(controller) + { + case 0: /* bank change - igore for now */ + break; + case kControllerVolume: + if(channel_vol[channel] != value<<8) + { + channel_vol[channel] = value<<8; + if(part>=0 && part<=31) + { + REST_IF_NECESSARY(); + qtma_StuffControlEvent(*tunePos, part, kControllerVolume, channel_vol[channel]); + tunePos++; + } + } + break; + case kControllerPan: + if(channel_pan[channel] != (value << 1) + 256) + { + channel_pan[channel] = (value << 1) + 256; + if(part>=0 && part<=31) + { + REST_IF_NECESSARY(); + qtma_StuffControlEvent(*tunePos, part, kControllerPan, channel_pan[channel]); + tunePos++; + } + } + break; + default: + /* No other controllers implemented yet */; + break; + } + + break; + case MIDI_STATUS_PROG_CHANGE: + /* Instrument changed */ + newInst = eventPos->data[0]; + + /* Channel 9 (the 10th channel) is different, it indicates a drum kit */ + if (channel == 9) + newInst += kFirstDrumkit; + else + newInst += kFirstGMInstrument; + /* Only if the instrument for this channel *really* changed, add a new part. */ + if(newInst != part_to_inst[part]) + { + /* TODO maybe make use of kGeneralEventPartChange here, + to help QT reuse note channels? + */ + part = channel_to_part[channel] = *numParts; + part_to_inst[(*numParts)++] = newInst; + + if(channel_vol[channel] >= 0) + { + REST_IF_NECESSARY(); + qtma_StuffControlEvent(*tunePos, part, kControllerVolume, channel_vol[channel]); + tunePos++; + } + if(channel_pan[channel] >= 0) + { + REST_IF_NECESSARY(); + qtma_StuffControlEvent(*tunePos, part, kControllerPan, channel_pan[channel]); + tunePos++; + } + if(channel_pitch_bend[channel] >= 0) + { + REST_IF_NECESSARY(); + qtma_StuffControlEvent(*tunePos, part, kControllerPitchBend, channel_pitch_bend[channel]); + tunePos++; + } + } + break; + case MIDI_STATUS_PRESSURE: + /* NYI */ + break; + case MIDI_STATUS_PITCH_WHEEL: + /* In the midi spec, 0x2000 = center, 0x0000 = - 2 semitones, 0x3FFF = +2 semitones + but for QTMA, we specify it as a 8.8 fixed point of semitones + TODO: detect "pitch bend range changes" & honor them! + */ + bend = (eventPos->data[0] & 0x7f) | ((eventPos->data[1] & 0x7f) << 7); + + /* "Center" the bend */ + bend -= 0x2000; + + /* Move it to our format: */ + bend <<= 4; + + /* If it turns out the pitch bend didn't change, stop here */ + if(channel_pitch_bend[channel] == bend) + break; + + channel_pitch_bend[channel] = bend; + if(part>=0 && part<=31) + { + /* Stuff a control event */ + REST_IF_NECESSARY(); + qtma_StuffControlEvent(*tunePos, part, kControllerPitchBend, bend); + tunePos++; + } + break; + case MIDI_STATUS_SYSEX: + if (eventPos->status == 0xFF && eventPos->data[0] == 0x51) /* Tempo change */ + { + tempo = (eventPos->extraData[0] << 16) + + (eventPos->extraData[1] << 8) + + eventPos->extraData[2]; + + tick = tempo * Ippqn; + } + break; + } + + /* on to the next event */ + eventPos = eventPos->next; + } + + /* Finally, place an end marker */ + *tunePos = kEndMarkerValue; + + return tuneSequence; +} + +Uint32 *BuildTuneHeader(int part_poly_max[32], int part_to_inst[32], int numParts) +{ + Uint32 *myHeader; + Uint32 *myPos1, *myPos2; /* pointers to the head and tail long words of a music event */ + NoteRequest *myNoteRequest; + NoteAllocator myNoteAllocator; /* for the NAStuffToneDescription call */ + ComponentResult myErr = noErr; + int part; + + myHeader = NULL; + myNoteAllocator = NULL; + + /* + * Open up the Note Allocator + */ + myNoteAllocator = OpenDefaultComponent(kNoteAllocatorComponentType,0); + if (myNoteAllocator == NULL) + goto bail; + + /* + * Allocate space for the tune header + */ + myHeader = (Uint32 *) + NewPtrClear((numParts * kNoteRequestEventLength + kMarkerEventLength) * sizeof(Uint32)); + if (myHeader == NULL) + goto bail; + + myPos1 = myHeader; + + /* + * Loop over all parts + */ + for(part = 0; part < numParts; ++part) + { + /* + * Stuff request for the instrument with the given polyphony + */ + myPos2 = myPos1 + (kNoteRequestEventLength - 1); /* last longword of general event */ + qtma_StuffGeneralEvent(*myPos1, *myPos2, part, kGeneralEventNoteRequest, kNoteRequestEventLength); + myNoteRequest = (NoteRequest *)(myPos1 + 1); + myNoteRequest->info.flags = 0; + /* I'm told by the Apple people that the Quicktime types were poorly designed and it was + * too late to change them. On little endian, the BigEndian(Short|Fixed) types are structs + * while on big endian they are primitive types. Furthermore, Quicktime failed to + * provide setter and getter functions. To get this to work, we need to case the + * code for the two possible situations. + * My assumption is that the right-side value was always expected to be BigEndian + * as it was written way before the Universal Binary transition. So in the little endian + * case, OSSwap is used. + */ +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + myNoteRequest->info.polyphony.bigEndianValue = OSSwapHostToBigInt16(part_poly_max[part]); + myNoteRequest->info.typicalPolyphony.bigEndianValue = OSSwapHostToBigInt32(0x00010000); +#else + myNoteRequest->info.polyphony = part_poly_max[part]; + myNoteRequest->info.typicalPolyphony = 0x00010000; +#endif + myErr = NAStuffToneDescription(myNoteAllocator,part_to_inst[part],&myNoteRequest->tone); + if (myErr != noErr) + goto bail; + + /* move pointer to beginning of next event */ + myPos1 += kNoteRequestEventLength; + } + + *myPos1 = kEndMarkerValue; /* end of sequence marker */ + + +bail: + if(myNoteAllocator) + CloseComponent(myNoteAllocator); + + /* if we encountered an error, dispose of the storage we allocated and return NULL */ + if (myErr != noErr) { + DisposePtr((Ptr)myHeader); + myHeader = NULL; + } + + return myHeader; +} + +#endif /* MacOS native MIDI support */ diff --git a/apps/plugins/sdl/SDL_mixer/native_midi/native_midi_macosx.c b/apps/plugins/sdl/SDL_mixer/native_midi/native_midi_macosx.c new file mode 100644 index 0000000000..8fefbc9616 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/native_midi/native_midi_macosx.c @@ -0,0 +1,322 @@ +/* + native_midi_macosx: Native Midi support on Mac OS X for the SDL_mixer library + Copyright (C) 2009 Ryan C. Gordon <icculus@icculus.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* This is Mac OS X only, using Core MIDI. + Mac OS 9 support via QuickTime is in native_midi_mac.c */ + +#include "SDL_config.h" + +#if __MACOSX__ + +#include <Carbon/Carbon.h> +#include <AudioToolbox/AudioToolbox.h> +#include <AvailabilityMacros.h> + +#include "../SDL_mixer.h" +#include "SDL_endian.h" +#include "native_midi.h" + +/* Native Midi song */ +struct _NativeMidiSong +{ + MusicPlayer player; + MusicSequence sequence; + MusicTimeStamp endTime; + AudioUnit audiounit; + int loops; +}; + +static NativeMidiSong *currentsong = NULL; +static int latched_volume = MIX_MAX_VOLUME; + +static OSStatus +GetSequenceLength(MusicSequence sequence, MusicTimeStamp *_sequenceLength) +{ + // http://lists.apple.com/archives/Coreaudio-api/2003/Jul/msg00370.html + // figure out sequence length + UInt32 ntracks, i; + MusicTimeStamp sequenceLength = 0; + OSStatus err; + + err = MusicSequenceGetTrackCount(sequence, &ntracks); + if (err != noErr) + return err; + + for (i = 0; i < ntracks; ++i) + { + MusicTrack track; + MusicTimeStamp tracklen = 0; + UInt32 tracklenlen = sizeof (tracklen); + + err = MusicSequenceGetIndTrack(sequence, i, &track); + if (err != noErr) + return err; + + err = MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength, + &tracklen, &tracklenlen); + if (err != noErr) + return err; + + if (sequenceLength < tracklen) + sequenceLength = tracklen; + } + + *_sequenceLength = sequenceLength; + + return noErr; +} + + +/* we're looking for the sequence output audiounit. */ +static OSStatus +GetSequenceAudioUnit(MusicSequence sequence, AudioUnit *aunit) +{ + AUGraph graph; + UInt32 nodecount, i; + OSStatus err; + + err = MusicSequenceGetAUGraph(sequence, &graph); + if (err != noErr) + return err; + + err = AUGraphGetNodeCount(graph, &nodecount); + if (err != noErr) + return err; + + for (i = 0; i < nodecount; i++) { + AUNode node; + + if (AUGraphGetIndNode(graph, i, &node) != noErr) + continue; /* better luck next time. */ + +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 /* this is deprecated, but works back to 10.0 */ + { + struct ComponentDescription desc; + UInt32 classdatasize = 0; + void *classdata = NULL; + err = AUGraphGetNodeInfo(graph, node, &desc, &classdatasize, + &classdata, aunit); + if (err != noErr) + continue; + else if (desc.componentType != kAudioUnitType_Output) + continue; + else if (desc.componentSubType != kAudioUnitSubType_DefaultOutput) + continue; + } + #else /* not deprecated, but requires 10.5 or later */ + { + AudioComponentDescription desc; + if (AUGraphNodeInfo(graph, node, &desc, aunit) != noErr) + continue; + else if (desc.componentType != kAudioUnitType_Output) + continue; + else if (desc.componentSubType != kAudioUnitSubType_DefaultOutput) + continue; + } + #endif + + return noErr; /* found it! */ + } + + return kAUGraphErr_NodeNotFound; +} + + +int native_midi_detect() +{ + return 1; /* always available. */ +} + +NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw, int freerw) +{ + NativeMidiSong *retval = NULL; + void *buf = NULL; + int len = 0; + CFDataRef data = NULL; + + if (SDL_RWseek(rw, 0, RW_SEEK_END) < 0) + goto fail; + len = SDL_RWtell(rw); + if (len < 0) + goto fail; + if (SDL_RWseek(rw, 0, RW_SEEK_SET) < 0) + goto fail; + + buf = malloc(len); + if (buf == NULL) + goto fail; + + if (SDL_RWread(rw, buf, len, 1) != 1) + goto fail; + + retval = malloc(sizeof(NativeMidiSong)); + if (retval == NULL) + goto fail; + + memset(retval, '\0', sizeof (*retval)); + + if (NewMusicPlayer(&retval->player) != noErr) + goto fail; + if (NewMusicSequence(&retval->sequence) != noErr) + goto fail; + + data = CFDataCreate(NULL, (const UInt8 *) buf, len); + if (data == NULL) + goto fail; + + free(buf); + buf = NULL; + + #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 /* this is deprecated, but works back to 10.3 */ + if (MusicSequenceLoadSMFDataWithFlags(retval->sequence, data, 0) != noErr) + goto fail; + #else /* not deprecated, but requires 10.5 or later */ + if (MusicSequenceFileLoadData(retval->sequence, data, 0, 0) != noErr) + goto fail; + #endif + + CFRelease(data); + data = NULL; + + if (GetSequenceLength(retval->sequence, &retval->endTime) != noErr) + goto fail; + + if (MusicPlayerSetSequence(retval->player, retval->sequence) != noErr) + goto fail; + + if (freerw) + SDL_RWclose(rw); + + return retval; + +fail: + if (retval) { + if (retval->sequence) + DisposeMusicSequence(retval->sequence); + if (retval->player) + DisposeMusicPlayer(retval->player); + free(retval); + } + + if (data) + CFRelease(data); + + if (buf) + free(buf); + + if (freerw) + SDL_RWclose(rw); + + return NULL; +} + +void native_midi_freesong(NativeMidiSong *song) +{ + if (song != NULL) { + if (currentsong == song) + currentsong = NULL; + MusicPlayerStop(song->player); + DisposeMusicSequence(song->sequence); + DisposeMusicPlayer(song->player); + free(song); + } +} + +void native_midi_start(NativeMidiSong *song, int loops) +{ + int vol; + + if (song == NULL) + return; + + SDL_PauseAudio(1); + SDL_UnlockAudio(); + + if (currentsong) + MusicPlayerStop(currentsong->player); + + currentsong = song; + currentsong->loops = loops; + + MusicPlayerPreroll(song->player); + MusicPlayerSetTime(song->player, 0); + MusicPlayerStart(song->player); + + GetSequenceAudioUnit(song->sequence, &song->audiounit); + + vol = latched_volume; + latched_volume++; /* just make this not match. */ + native_midi_setvolume(vol); + + SDL_LockAudio(); + SDL_PauseAudio(0); +} + +void native_midi_stop() +{ + if (currentsong) { + SDL_PauseAudio(1); + SDL_UnlockAudio(); + MusicPlayerStop(currentsong->player); + currentsong = NULL; + SDL_LockAudio(); + SDL_PauseAudio(0); + } +} + +int native_midi_active() +{ + MusicTimeStamp currentTime = 0; + if (currentsong == NULL) + return 0; + + MusicPlayerGetTime(currentsong->player, ¤tTime); + if ((currentTime < currentsong->endTime) || + (currentTime >= kMusicTimeStamp_EndOfTrack)) { + return 1; + } else if (currentsong->loops) { + --currentsong->loops; + MusicPlayerSetTime(currentsong->player, 0); + return 1; + } + return 0; +} + +void native_midi_setvolume(int volume) +{ + if (latched_volume == volume) + return; + + latched_volume = volume; + if ((currentsong) && (currentsong->audiounit)) { + const float floatvol = ((float) volume) / ((float) MIX_MAX_VOLUME); + AudioUnitSetParameter(currentsong->audiounit, kHALOutputParam_Volume, + kAudioUnitScope_Global, 0, floatvol, 0); + } +} + +const char *native_midi_error(void) +{ + return ""; /* !!! FIXME */ +} + +#endif /* Mac OS X native MIDI support */ + diff --git a/apps/plugins/sdl/SDL_mixer/native_midi/native_midi_win32.c b/apps/plugins/sdl/SDL_mixer/native_midi/native_midi_win32.c new file mode 100644 index 0000000000..187d989ff3 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/native_midi/native_midi_win32.c @@ -0,0 +1,312 @@ +/* + native_midi: Hardware Midi support for the SDL_mixer library + Copyright (C) 2000,2001 Florian 'Proff' Schulze <florian.proff.schulze@gmx.net> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" + +/* everything below is currently one very big bad hack ;) Proff */ + +#if __WIN32__ +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <windowsx.h> +#include <mmsystem.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include "native_midi.h" +#include "native_midi_common.h" + +struct _NativeMidiSong { + int MusicLoaded; + int MusicPlaying; + int Loops; + int CurrentHdr; + MIDIHDR MidiStreamHdr[2]; + MIDIEVENT *NewEvents; + Uint16 ppqn; + int Size; + int NewPos; +}; + +static UINT MidiDevice=MIDI_MAPPER; +static HMIDISTRM hMidiStream; +static NativeMidiSong *currentsong; + +static int BlockOut(NativeMidiSong *song) +{ + MMRESULT err; + int BlockSize; + MIDIHDR *hdr; + + if ((song->MusicLoaded) && (song->NewEvents)) + { + // proff 12/8/98: Added for safety + song->CurrentHdr = !song->CurrentHdr; + hdr = &song->MidiStreamHdr[song->CurrentHdr]; + midiOutUnprepareHeader((HMIDIOUT)hMidiStream,hdr,sizeof(MIDIHDR)); + if (song->NewPos>=song->Size) + return 0; + BlockSize=(song->Size-song->NewPos); + if (BlockSize<=0) + return 0; + if (BlockSize>36000) + BlockSize=36000; + hdr->lpData=(void *)((unsigned char *)song->NewEvents+song->NewPos); + song->NewPos+=BlockSize; + hdr->dwBufferLength=BlockSize; + hdr->dwBytesRecorded=BlockSize; + hdr->dwFlags=0; + hdr->dwOffset=0; + err=midiOutPrepareHeader((HMIDIOUT)hMidiStream,hdr,sizeof(MIDIHDR)); + if (err!=MMSYSERR_NOERROR) + return 0; + err=midiStreamOut(hMidiStream,hdr,sizeof(MIDIHDR)); + return 0; + } + return 1; +} + +static void MIDItoStream(NativeMidiSong *song, MIDIEvent *evntlist) +{ + int eventcount; + MIDIEvent *event; + MIDIEVENT *newevent; + + eventcount=0; + event=evntlist; + while (event) + { + eventcount++; + event=event->next; + } + song->NewEvents=malloc(eventcount*3*sizeof(DWORD)); + if (!song->NewEvents) + return; + memset(song->NewEvents,0,(eventcount*3*sizeof(DWORD))); + + eventcount=0; + event=evntlist; + newevent=song->NewEvents; + while (event) + { + int status = (event->status&0xF0)>>4; + switch (status) + { + case MIDI_STATUS_NOTE_OFF: + case MIDI_STATUS_NOTE_ON: + case MIDI_STATUS_AFTERTOUCH: + case MIDI_STATUS_CONTROLLER: + case MIDI_STATUS_PROG_CHANGE: + case MIDI_STATUS_PRESSURE: + case MIDI_STATUS_PITCH_WHEEL: + newevent->dwDeltaTime=event->time; + newevent->dwEvent=(event->status|0x80)|(event->data[0]<<8)|(event->data[1]<<16)|(MEVT_SHORTMSG<<24); + newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD))); + eventcount++; + break; + + case MIDI_STATUS_SYSEX: + if (event->status == 0xFF && event->data[0] == 0x51) /* Tempo change */ + { + int tempo = (event->extraData[0] << 16) | + (event->extraData[1] << 8) | + event->extraData[2]; + newevent->dwDeltaTime=event->time; + newevent->dwEvent=(MEVT_TEMPO<<24) | tempo; + newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD))); + eventcount++; + } + break; + } + + event=event->next; + } + + song->Size=eventcount*3*sizeof(DWORD); + + { + int time; + int temptime; + + song->NewPos=0; + time=0; + newevent=song->NewEvents; + while (song->NewPos<song->Size) + { + temptime=newevent->dwDeltaTime; + newevent->dwDeltaTime-=time; + time=temptime; + if ((song->NewPos+12)>=song->Size) + newevent->dwEvent |= MEVT_F_CALLBACK; + newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD))); + song->NewPos+=12; + } + } + song->NewPos=0; + song->MusicLoaded=1; +} + +void CALLBACK MidiProc( HMIDIIN hMidi, UINT uMsg, DWORD_PTR dwInstance, + DWORD_PTR dwParam1, DWORD_PTR dwParam2 ) +{ + switch( uMsg ) + { + case MOM_DONE: + if ((currentsong->MusicLoaded) && (dwParam1 == (DWORD_PTR)¤tsong->MidiStreamHdr[currentsong->CurrentHdr])) + BlockOut(currentsong); + break; + case MOM_POSITIONCB: + if ((currentsong->MusicLoaded) && (dwParam1 == (DWORD_PTR)¤tsong->MidiStreamHdr[currentsong->CurrentHdr])) { + if (currentsong->Loops) { + --currentsong->Loops; + currentsong->NewPos=0; + BlockOut(currentsong); + } else { + currentsong->MusicPlaying=0; + } + } + break; + default: + break; + } +} + +int native_midi_detect() +{ + MMRESULT merr; + HMIDISTRM MidiStream; + + merr=midiStreamOpen(&MidiStream,&MidiDevice,(DWORD)1,(DWORD_PTR)MidiProc,(DWORD_PTR)0,CALLBACK_FUNCTION); + if (merr!=MMSYSERR_NOERROR) + return 0; + midiStreamClose(MidiStream); + return 1; +} + +NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw, int freerw) +{ + NativeMidiSong *newsong; + MIDIEvent *evntlist = NULL; + + newsong=malloc(sizeof(NativeMidiSong)); + if (!newsong) { + if (freerw) { + SDL_RWclose(rw); + } + return NULL; + } + memset(newsong,0,sizeof(NativeMidiSong)); + + /* Attempt to load the midi file */ + evntlist = CreateMIDIEventList(rw, &newsong->ppqn); + if (!evntlist) + { + free(newsong); + if (freerw) { + SDL_RWclose(rw); + } + return NULL; + } + + MIDItoStream(newsong, evntlist); + + FreeMIDIEventList(evntlist); + + if (freerw) { + SDL_RWclose(rw); + } + return newsong; +} + +void native_midi_freesong(NativeMidiSong *song) +{ + if (hMidiStream) + { + midiStreamStop(hMidiStream); + midiStreamClose(hMidiStream); + } + if (song) + { + if (song->NewEvents) + free(song->NewEvents); + free(song); + } +} + +void native_midi_start(NativeMidiSong *song, int loops) +{ + MMRESULT merr; + MIDIPROPTIMEDIV mptd; + + native_midi_stop(); + if (!hMidiStream) + { + merr=midiStreamOpen(&hMidiStream,&MidiDevice,(DWORD)1,(DWORD_PTR)MidiProc,(DWORD_PTR)0,CALLBACK_FUNCTION); + if (merr!=MMSYSERR_NOERROR) + { + hMidiStream = NULL; // should I do midiStreamClose(hMidiStream) before? + return; + } + //midiStreamStop(hMidiStream); + currentsong=song; + currentsong->NewPos=0; + currentsong->MusicPlaying=1; + currentsong->Loops=loops; + mptd.cbStruct=sizeof(MIDIPROPTIMEDIV); + mptd.dwTimeDiv=currentsong->ppqn; + merr=midiStreamProperty(hMidiStream,(LPBYTE)&mptd,MIDIPROP_SET | MIDIPROP_TIMEDIV); + BlockOut(song); + merr=midiStreamRestart(hMidiStream); + } +} + +void native_midi_stop() +{ + if (!hMidiStream) + return; + midiStreamStop(hMidiStream); + midiStreamClose(hMidiStream); + currentsong=NULL; + hMidiStream = NULL; +} + +int native_midi_active() +{ + return currentsong->MusicPlaying; +} + +void native_midi_setvolume(int volume) +{ + int calcVolume; + if (volume > 128) + volume = 128; + if (volume < 0) + volume = 0; + calcVolume = (65535 * volume / 128); + + midiOutSetVolume((HMIDIOUT)hMidiStream, MAKELONG(calcVolume , calcVolume)); +} + +const char *native_midi_error(void) +{ + return ""; +} + +#endif /* Windows native MIDI support */ diff --git a/apps/plugins/sdl/SDL_mixer/playmus.c b/apps/plugins/sdl/SDL_mixer/playmus.c new file mode 100644 index 0000000000..4f8bb612a1 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/playmus.c @@ -0,0 +1,234 @@ +/* + PLAYMUS: A test application for the SDL mixer library. + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id$ */ + +#ifdef unix +#include <unistd.h> +#endif + +#include "SDL.h" +#include "SDL_mixer.h" + +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif + + +static int audio_open = 0; +static Mix_Music *music = NULL; +static int next_track = 0; + +void CleanUp(int exitcode) +{ + if( Mix_PlayingMusic() ) { + Mix_FadeOutMusic(1500); + SDL_Delay(1500); + } + if ( music ) { + Mix_FreeMusic(music); + music = NULL; + } + if ( audio_open ) { + Mix_CloseAudio(); + audio_open = 0; + } + SDL_Quit(); + exit(exitcode); +} + +void Usage(char *argv0) +{ + fprintf(stderr, "Usage: %s [-i] [-l] [-8] [-r rate] [-c channels] [-b buffers] [-v N] [-rwops] <musicfile>\n", argv0); +} + +void Menu(void) +{ + char buf[10]; + + printf("Available commands: (p)ause (r)esume (h)alt volume(v#) > "); + if (scanf("%s",buf) == 1) { + switch(buf[0]){ + case 'p': case 'P': + Mix_PauseMusic(); + break; + case 'r': case 'R': + Mix_ResumeMusic(); + break; + case 'h': case 'H': + Mix_HaltMusic(); + break; + case 'v': case 'V': + Mix_VolumeMusic(atoi(buf+1)); + break; + } + } + printf("Music playing: %s Paused: %s\n", Mix_PlayingMusic() ? "yes" : "no", + Mix_PausedMusic() ? "yes" : "no"); +} + +#ifdef HAVE_SIGNAL_H + +void IntHandler(int sig) +{ + switch (sig) { + case SIGINT: + next_track++; + break; + } +} + +#endif + +int main(int argc, char *argv[]) +{ + SDL_RWops *rwfp = NULL; + int audio_rate; + Uint16 audio_format; + int audio_channels; + int audio_buffers; + int audio_volume = MIX_MAX_VOLUME; + int looping = 0; + int interactive = 0; + int rwops = 0; + int i; + + /* Initialize variables */ + audio_rate = 22050; + audio_format = AUDIO_S16; + audio_channels = 2; + audio_buffers = 4096; + + /* Check command line usage */ + for ( i=1; argv[i] && (*argv[i] == '-'); ++i ) { + if ( (strcmp(argv[i], "-r") == 0) && argv[i+1] ) { + ++i; + audio_rate = atoi(argv[i]); + } else + if ( strcmp(argv[i], "-m") == 0 ) { + audio_channels = 1; + } else + if ( (strcmp(argv[i], "-c") == 0) && argv[i+1] ) { + ++i; + audio_channels = atoi(argv[i]); + } else + if ( (strcmp(argv[i], "-b") == 0) && argv[i+1] ) { + ++i; + audio_buffers = atoi(argv[i]); + } else + if ( (strcmp(argv[i], "-v") == 0) && argv[i+1] ) { + ++i; + audio_volume = atoi(argv[i]); + } else + if ( strcmp(argv[i], "-l") == 0 ) { + looping = -1; + } else + if ( strcmp(argv[i], "-i") == 0 ) { + interactive = 1; + } else + if ( strcmp(argv[i], "-8") == 0 ) { + audio_format = AUDIO_U8; + } else + if ( strcmp(argv[i], "-rwops") == 0 ) { + rwops = 1; + } else { + Usage(argv[0]); + return(1); + } + } + if ( ! argv[i] ) { + Usage(argv[0]); + return(1); + } + + /* Initialize the SDL library */ + if ( SDL_Init(SDL_INIT_AUDIO) < 0 ) { + fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError()); + return(255); + } + +#ifdef HAVE_SIGNAL_H + signal(SIGINT, IntHandler); + signal(SIGTERM, CleanUp); +#endif + + /* Open the audio device */ + if (Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers) < 0) { + fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError()); + return(2); + } else { + Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels); + printf("Opened audio at %d Hz %d bit %s (%s), %d bytes audio buffer\n", audio_rate, + (audio_format&0xFF), + (audio_channels > 2) ? "surround" : (audio_channels > 1) ? "stereo" : "mono", + (audio_format&0x1000) ? "BE" : "LE", + audio_buffers ); + } + audio_open = 1; + + /* Set the music volume */ + Mix_VolumeMusic(audio_volume); + + /* Set the external music player, if any */ + Mix_SetMusicCMD(SDL_getenv("MUSIC_CMD")); + + while (argv[i]) { + next_track = 0; + + /* Load the requested music file */ + if ( rwops ) { + rwfp = SDL_RWFromFile(argv[i], "rb"); + music = Mix_LoadMUS_RW(rwfp); + } else { + music = Mix_LoadMUS(argv[i]); + } + if ( music == NULL ) { + fprintf(stderr, "Couldn't load %s: %s\n", + argv[i], SDL_GetError()); + CleanUp(2); + } + + /* Play and then exit */ + printf("Playing %s\n", argv[i]); + Mix_FadeInMusic(music,looping,2000); + while ( !next_track && (Mix_PlayingMusic() || Mix_PausedMusic()) ) { + if(interactive) + Menu(); + else + SDL_Delay(100); + } + Mix_FreeMusic(music); + if ( rwops ) { + SDL_RWclose(rwfp); + } + music = NULL; + + /* If the user presses Ctrl-C more than once, exit. */ + SDL_Delay(500); + if ( next_track > 1 ) break; + + i++; + } + CleanUp(0); + + /* Not reached, but fixes compiler warnings */ + return 0; +} diff --git a/apps/plugins/sdl/SDL_mixer/playwave.c b/apps/plugins/sdl/SDL_mixer/playwave.c new file mode 100644 index 0000000000..e53f1a93d5 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/playwave.c @@ -0,0 +1,497 @@ +/* + PLAYWAVE: A test application for the SDL mixer library. + Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id$ */ + +#ifdef unix +#include <unistd.h> +#endif + +#include "SDL.h" +#include "SDL_mixer.h" + +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif + + +/* + * rcg06132001 various mixer tests. Define the ones you want. + */ +/*#define TEST_MIX_DECODERS*/ +/*#define TEST_MIX_VERSIONS*/ +/*#define TEST_MIX_CHANNELFINISHED*/ +/*#define TEST_MIX_PANNING*/ +/*#define TEST_MIX_DISTANCE*/ +/*#define TEST_MIX_POSITION*/ + + +#if (defined TEST_MIX_POSITION) + +#if (defined TEST_MIX_PANNING) +#error TEST_MIX_POSITION interferes with TEST_MIX_PANNING. +#endif + +#if (defined TEST_MIX_DISTANCE) +#error TEST_MIX_POSITION interferes with TEST_MIX_DISTANCE. +#endif + +#endif + + +/* rcg06192001 for debugging purposes. */ +static void output_test_warnings(void) +{ +#if (defined TEST_MIX_CHANNELFINISHED) + fprintf(stderr, "Warning: TEST_MIX_CHANNELFINISHED is enabled in this binary...\n"); +#endif +#if (defined TEST_MIX_PANNING) + fprintf(stderr, "Warning: TEST_MIX_PANNING is enabled in this binary...\n"); +#endif +#if (defined TEST_MIX_VERSIONS) + fprintf(stderr, "Warning: TEST_MIX_VERSIONS is enabled in this binary...\n"); +#endif +#if (defined TEST_MIX_DISTANCE) + fprintf(stderr, "Warning: TEST_MIX_DISTANCE is enabled in this binary...\n"); +#endif +#if (defined TEST_MIX_POSITION) + fprintf(stderr, "Warning: TEST_MIX_POSITION is enabled in this binary...\n"); +#endif +} + + +static int audio_open = 0; +static Mix_Chunk *wave = NULL; + +/* rcg06042009 Report available decoders. */ +#if (defined TEST_MIX_DECODERS) +static void report_decoders(void) +{ + int i, total; + + printf("Supported decoders...\n"); + total = Mix_GetNumChunkDecoders(); + for (i = 0; i < total; i++) { + fprintf(stderr, " - chunk decoder: %s\n", Mix_GetChunkDecoder(i)); + } + + total = Mix_GetNumMusicDecoders(); + for (i = 0; i < total; i++) { + fprintf(stderr, " - music decoder: %s\n", Mix_GetMusicDecoder(i)); + } +} +#endif + +/* rcg06192001 Check new Mixer version API. */ +#if (defined TEST_MIX_VERSIONS) +static void output_versions(const char *libname, const SDL_version *compiled, + const SDL_version *linked) +{ + fprintf(stderr, + "This program was compiled against %s %d.%d.%d,\n" + " and is dynamically linked to %d.%d.%d.\n", libname, + compiled->major, compiled->minor, compiled->patch, + linked->major, linked->minor, linked->patch); +} + +static void test_versions(void) +{ + SDL_version compiled; + const SDL_version *linked; + + SDL_VERSION(&compiled); + linked = SDL_Linked_Version(); + output_versions("SDL", &compiled, linked); + + SDL_MIXER_VERSION(&compiled); + linked = Mix_Linked_Version(); + output_versions("SDL_mixer", &compiled, linked); +} +#endif + + +#ifdef TEST_MIX_CHANNELFINISHED /* rcg06072001 */ +static volatile int channel_is_done = 0; +static void channel_complete_callback(int chan) +{ + Mix_Chunk *done_chunk = Mix_GetChunk(chan); + fprintf(stderr, "We were just alerted that Mixer channel #%d is done.\n", chan); + fprintf(stderr, "Channel's chunk pointer is (%p).\n", done_chunk); + fprintf(stderr, " Which %s correct.\n", (wave == done_chunk) ? "is" : "is NOT"); + channel_is_done = 1; +} +#endif + + +/* rcg06192001 abstract this out for testing purposes. */ +static int still_playing(void) +{ +#ifdef TEST_MIX_CHANNELFINISHED + return(!channel_is_done); +#else + return(Mix_Playing(0)); +#endif +} + + +#if (defined TEST_MIX_PANNING) +static void do_panning_update(void) +{ + static Uint8 leftvol = 128; + static Uint8 rightvol = 128; + static Uint8 leftincr = -1; + static Uint8 rightincr = 1; + static int panningok = 1; + static Uint32 next_panning_update = 0; + + if ((panningok) && (SDL_GetTicks() >= next_panning_update)) { + panningok = Mix_SetPanning(0, leftvol, rightvol); + if (!panningok) { + fprintf(stderr, "Mix_SetPanning(0, %d, %d) failed!\n", + (int) leftvol, (int) rightvol); + fprintf(stderr, "Reason: [%s].\n", Mix_GetError()); + } + + if ((leftvol == 255) || (leftvol == 0)) { + if (leftvol == 255) + printf("All the way in the left speaker.\n"); + leftincr *= -1; + } + + if ((rightvol == 255) || (rightvol == 0)) { + if (rightvol == 255) + printf("All the way in the right speaker.\n"); + rightincr *= -1; + } + + leftvol += leftincr; + rightvol += rightincr; + next_panning_update = SDL_GetTicks() + 10; + } +} +#endif + + +#if (defined TEST_MIX_DISTANCE) +static void do_distance_update(void) +{ + static Uint8 distance = 1; + static Uint8 distincr = 1; + static int distanceok = 1; + static Uint32 next_distance_update = 0; + + if ((distanceok) && (SDL_GetTicks() >= next_distance_update)) { + distanceok = Mix_SetDistance(0, distance); + if (!distanceok) { + fprintf(stderr, "Mix_SetDistance(0, %d) failed!\n", (int) distance); + fprintf(stderr, "Reason: [%s].\n", Mix_GetError()); + } + + if (distance == 0) { + printf("Distance at nearest point.\n"); + distincr *= -1; + } + else if (distance == 255) { + printf("Distance at furthest point.\n"); + distincr *= -1; + } + + distance += distincr; + next_distance_update = SDL_GetTicks() + 15; + } +} +#endif + + +#if (defined TEST_MIX_POSITION) +static void do_position_update(void) +{ + static Sint16 distance = 1; + static Sint8 distincr = 1; + static Uint16 angle = 0; + static Sint8 angleincr = 1; + static int positionok = 1; + static Uint32 next_position_update = 0; + + if ((positionok) && (SDL_GetTicks() >= next_position_update)) { + positionok = Mix_SetPosition(0, angle, distance); + if (!positionok) { + fprintf(stderr, "Mix_SetPosition(0, %d, %d) failed!\n", + (int) angle, (int) distance); + fprintf(stderr, "Reason: [%s].\n", Mix_GetError()); + } + + if (angle == 0) { + printf("Due north; now rotating clockwise...\n"); + angleincr = 1; + } + + else if (angle == 360) { + printf("Due north; now rotating counter-clockwise...\n"); + angleincr = -1; + } + + distance += distincr; + + if (distance < 0) { + distance = 0; + distincr = 3; + printf("Distance is very, very near. Stepping away by threes...\n"); + } else if (distance > 255) { + distance = 255; + distincr = -3; + printf("Distance is very, very far. Stepping towards by threes...\n"); + } + + angle += angleincr; + next_position_update = SDL_GetTicks() + 30; + } +} +#endif + + +static void CleanUp(int exitcode) +{ + if ( wave ) { + Mix_FreeChunk(wave); + wave = NULL; + } + if ( audio_open ) { + Mix_CloseAudio(); + audio_open = 0; + } + SDL_Quit(); + + exit(exitcode); +} + + +static void Usage(char *argv0) +{ + fprintf(stderr, "Usage: %s [-8] [-r rate] [-c channels] [-f] [-F] [-l] [-m] <wavefile>\n", argv0); +} + + +/* + * rcg06182001 This is sick, but cool. + * + * Actually, it's meant to be an example of how to manipulate a voice + * without having to use the mixer effects API. This is more processing + * up front, but no extra during the mixing process. Also, in a case like + * this, when you need to touch the whole sample at once, it's the only + * option you've got. And, with the effects API, you are altering a copy of + * the original sample for each playback, and thus, your changes aren't + * permanent; here, you've got a reversed sample, and that's that until + * you either reverse it again, or reload it. + */ +static void flip_sample(Mix_Chunk *wave) +{ + Uint16 format; + int channels, i, incr; + Uint8 *start = wave->abuf; + Uint8 *end = wave->abuf + wave->alen; + + Mix_QuerySpec(NULL, &format, &channels); + incr = (format & 0xFF) * channels; + + end -= incr; + + switch (incr) { + case 8: + for (i = wave->alen / 2; i >= 0; i -= 1) { + Uint8 tmp = *start; + *start = *end; + *end = tmp; + start++; + end--; + } + break; + + case 16: + for (i = wave->alen / 2; i >= 0; i -= 2) { + Uint16 tmp = *start; + *((Uint16 *) start) = *((Uint16 *) end); + *((Uint16 *) end) = tmp; + start += 2; + end -= 2; + } + break; + + case 32: + for (i = wave->alen / 2; i >= 0; i -= 4) { + Uint32 tmp = *start; + *((Uint32 *) start) = *((Uint32 *) end); + *((Uint32 *) end) = tmp; + start += 4; + end -= 4; + } + break; + + default: + fprintf(stderr, "Unhandled format in sample flipping.\n"); + return; + } +} + + +int main(int argc, char *argv[]) +{ + int audio_rate; + Uint16 audio_format; + int audio_channels; + int loops = 0; + int i; + int reverse_stereo = 0; + int reverse_sample = 0; + +#ifdef HAVE_SETBUF + setbuf(stdout, NULL); /* rcg06132001 for debugging purposes. */ + setbuf(stderr, NULL); /* rcg06192001 for debugging purposes, too. */ +#endif + output_test_warnings(); + + /* Initialize variables */ + audio_rate = MIX_DEFAULT_FREQUENCY; + audio_format = MIX_DEFAULT_FORMAT; + audio_channels = 2; + + /* Check command line usage */ + for ( i=1; argv[i] && (*argv[i] == '-'); ++i ) { + if ( (strcmp(argv[i], "-r") == 0) && argv[i+1] ) { + ++i; + audio_rate = atoi(argv[i]); + } else + if ( strcmp(argv[i], "-m") == 0 ) { + audio_channels = 1; + } else + if ( (strcmp(argv[i], "-c") == 0) && argv[i+1] ) { + ++i; + audio_channels = atoi(argv[i]); + } else + if ( strcmp(argv[i], "-l") == 0 ) { + loops = -1; + } else + if ( strcmp(argv[i], "-8") == 0 ) { + audio_format = AUDIO_U8; + } else + if ( strcmp(argv[i], "-f") == 0 ) { /* rcg06122001 flip stereo */ + reverse_stereo = 1; + } else + if ( strcmp(argv[i], "-F") == 0 ) { /* rcg06172001 flip sample */ + reverse_sample = 1; + } else { + Usage(argv[0]); + return(1); + } + } + if ( ! argv[i] ) { + Usage(argv[0]); + return(1); + } + + /* Initialize the SDL library */ + if ( SDL_Init(SDL_INIT_AUDIO) < 0 ) { + fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError()); + return(255); + } +#ifdef HAVE_SIGNAL_H + signal(SIGINT, CleanUp); + signal(SIGTERM, CleanUp); +#endif + + /* Open the audio device */ + if (Mix_OpenAudio(audio_rate, audio_format, audio_channels, 4096) < 0) { + fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError()); + CleanUp(2); + } else { + Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels); + printf("Opened audio at %d Hz %d bit %s", audio_rate, + (audio_format&0xFF), + (audio_channels > 2) ? "surround" : + (audio_channels > 1) ? "stereo" : "mono"); + if ( loops ) { + printf(" (looping)\n"); + } else { + putchar('\n'); + } + } + audio_open = 1; + +#if (defined TEST_MIX_VERSIONS) + test_versions(); +#endif + +#if (defined TEST_MIX_DECODERS) + report_decoders(); +#endif + + /* Load the requested wave file */ + wave = Mix_LoadWAV(argv[i]); + if ( wave == NULL ) { + fprintf(stderr, "Couldn't load %s: %s\n", + argv[i], SDL_GetError()); + CleanUp(2); + } + + if (reverse_sample) { + flip_sample(wave); + } + +#ifdef TEST_MIX_CHANNELFINISHED /* rcg06072001 */ + Mix_ChannelFinished(channel_complete_callback); +#endif + + if ( (!Mix_SetReverseStereo(MIX_CHANNEL_POST, reverse_stereo)) && + (reverse_stereo) ) + { + printf("Failed to set up reverse stereo effect!\n"); + printf("Reason: [%s].\n", Mix_GetError()); + } + + /* Play and then exit */ + Mix_PlayChannel(0, wave, loops); + + while (still_playing()) { + +#if (defined TEST_MIX_PANNING) /* rcg06132001 */ + do_panning_update(); +#endif + +#if (defined TEST_MIX_DISTANCE) /* rcg06192001 */ + do_distance_update(); +#endif + +#if (defined TEST_MIX_POSITION) /* rcg06202001 */ + do_position_update(); +#endif + + SDL_Delay(1); + + } /* while still_playing() loop... */ + + CleanUp(0); + + /* Not reached, but fixes compiler warnings */ + return 0; +} + +/* end of playwave.c ... */ + diff --git a/apps/plugins/sdl/SDL_mixer/timidity/COPYING b/apps/plugins/sdl/SDL_mixer/timidity/COPYING new file mode 100644 index 0000000000..cdbb291069 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/timidity/COPYING @@ -0,0 +1,127 @@ + The "Artistic License" + + Preamble + +The intent of this document is to state the conditions under which a +Package may be copied, such that the Copyright Holder maintains some +semblance of artistic control over the development of the package, +while giving the users of the package the right to use and distribute +the Package in a more-or-less customary fashion, plus the right to make +reasonable modifications. + +Definitions: + + "Package" refers to the collection of files distributed by the + Copyright Holder, and derivatives of that collection of files + created through textual modification. + + "Standard Version" refers to such a Package if it has not been + modified, or has been modified in accordance with the wishes + of the Copyright Holder as specified below. + + "Copyright Holder" is whoever is named in the copyright or + copyrights for the package. + + "You" is you, if you're thinking about copying or distributing + this Package. + + "Reasonable copying fee" is whatever you can justify on the + basis of media cost, duplication charges, time of people involved, + and so on. (You will not be required to justify it to the + Copyright Holder, but only to the computing community at large + as a market that must bear the fee.) + + "Freely Available" means that no fee is charged for the item + itself, though there may be fees involved in handling the item. + It also means that recipients of the item may redistribute it + under the same conditions they received it. + +1. You may make and give away verbatim copies of the source form of the +Standard Version of this Package without restriction, provided that you +duplicate all of the original copyright notices and associated disclaimers. + +2. You may apply bug fixes, portability fixes and other modifications +derived from the Public Domain or from the Copyright Holder. A Package +modified in such a way shall still be considered the Standard Version. + +3. You may otherwise modify your copy of this Package in any way, provided +that you insert a prominent notice in each changed file stating how and +when you changed that file, and provided that you do at least ONE of the +following: + + a) place your modifications in the Public Domain or otherwise make them + Freely Available, such as by posting said modifications to Usenet or + an equivalent medium, or placing the modifications on a major archive + site such as uunet.uu.net, or by allowing the Copyright Holder to include + your modifications in the Standard Version of the Package. + + b) use the modified Package only within your corporation or organization. + + c) rename any non-standard executables so the names do not conflict + with standard executables, which must also be provided, and provide + a separate manual page for each non-standard executable that clearly + documents how it differs from the Standard Version. + + d) make other distribution arrangements with the Copyright Holder. + +4. You may distribute the programs of this Package in object code or +executable form, provided that you do at least ONE of the following: + + a) distribute a Standard Version of the executables and library files, + together with instructions (in the manual page or equivalent) on where + to get the Standard Version. + + b) accompany the distribution with the machine-readable source of + the Package with your modifications. + + c) give non-standard executables non-standard names, and clearly + document the differences in manual pages (or equivalent), together + with instructions on where to get the Standard Version. + + d) make other distribution arrangements with the Copyright Holder. + +5. You may charge a reasonable copying fee for any distribution of this +Package. You may charge any fee you choose for support of this +Package. You may not charge a fee for this Package itself. However, +you may distribute this Package in aggregate with other (possibly +commercial) programs as part of a larger (possibly commercial) software +distribution provided that you do not advertise this Package as a +product of your own. You may embed this Package's interpreter within +an executable of yours (by linking); this shall be construed as a mere +form of aggregation, provided that the complete Standard Version of the +interpreter is so embedded. + +6. The scripts and library files supplied as input to or produced as +output from the programs of this Package do not automatically fall +under the copyright of this Package, but belong to whoever generated +them, and may be sold commercially, and may be aggregated with this +Package. If such scripts or library files are aggregated with this +Package via the so-called "undump" or "unexec" methods of producing a +binary executable image, then distribution of such an image shall +neither be construed as a distribution of this Package nor shall it +fall under the restrictions of Paragraphs 3 and 4, provided that you do +not represent such an executable image as a Standard Version of this +Package. + +7. C subroutines (or comparably compiled subroutines in other +languages) supplied by you and linked into this Package in order to +emulate subroutines and variables of the language defined by this +Package shall not be considered part of this Package, but are the +equivalent of input as in Paragraph 6, provided these subroutines do +not change the language in any way that would cause it to fail the +regression tests for the language. + +8. Aggregation of this Package with a commercial distribution is always +permitted provided that the use of this Package is embedded; that is, +when no overt attempt is made to make this Package's interfaces visible +to the end user of the commercial distribution. Such use shall not be +construed as a distribution of this Package. + +9. The name of the Copyright Holder may not be used to endorse or promote +products derived from this software without specific prior written permission. + +10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + The End diff --git a/apps/plugins/sdl/SDL_mixer/timidity/FAQ b/apps/plugins/sdl/SDL_mixer/timidity/FAQ new file mode 100644 index 0000000000..f1f8e237b4 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/timidity/FAQ @@ -0,0 +1,112 @@ +---------------------------*-indented-text-*------------------------------ + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi> + +-------------------------------------------------------------------------- + + Frequently Asked Questions with answers: + +-------------------------------------------------------------------------- +Q: What is it? + +A: Where? Well Chris, TiMidity is a software-only synthesizer, MIDI + renderer, MIDI to WAVE converter, realtime MIDI player for UNIX machines, + even (I've heard) a Netscape helper application. It takes a MIDI file + and writes a WAVE or raw PCM data or plays it on your digital audio + device. It sounds much more realistic than FM synthesis, but you need a + ~100Mhz processor to listen to 32kHz stereo music in the background while + you work. 11kHz mono can be played on a low-end 486, and, to some, it + still sounds better than FM. + +-------------------------------------------------------------------------- +Q: I don't have a GUS, can I use TiMidity? + +A: Yes. That's the point. You don't need a Gravis Ultrasound to use + TiMidity, you just need GUS-compatible patches, which are freely + available on the Internet. See below for pointers. + +-------------------------------------------------------------------------- +Q: I have a GUS, can I use TiMidity? + +A: The DOS port doesn't have GUS support, and TiMidity won't be taking + advantage of the board's internal synthesizer under other operating + systems either. So it kind of defeats the purpose. But you can use it. + +-------------------------------------------------------------------------- +Q: I tried playing a MIDI file I got off the Net but all I got was a + dozen warnings saying "No instrument mapped to tone bank 0, program + xx - this instrument will not be heard". What's wrong? + +A: The General MIDI standard specifies 128 melodic instruments and + some sixty percussion sounds. If you wish to play arbitrary General + MIDI files, you'll need to get more patch files. + + There's a program called Midia for SGI's, which also plays MIDI + files and has a lot more bells and whistles than TiMidity. It uses + GUS-compatible patches, too -- so you can get the 8 MB set at + ftp://archive.cs.umbc.edu/pub/midia for pretty good GM compatibility. + + There are also many excellent patches on the Ultrasound FTP sites. + I can recommend Dustin McCartney's collections gsdrum*.zip and + wow*.zip in the "[.../]sound/patches/files" directory. The huge + ProPats series (pp3-*.zip) contains good patches as well. General + MIDI files can also be found on these sites. + + This site list is from the GUS FAQ: + +> FTP Sites Archive Directories +> --------- ------------------- +> Main N.American Site: archive.orst.edu pub/packages/gravis +> wuarchive.wustl.edu systems/ibmpc/ultrasound +> Main Asian Site: nctuccca.edu.tw PC/ultrasound +> Main European Site: src.doc.ic.ac.uk packages/ultrasound +> Main Australian Site: ftp.mpx.com.au /ultrasound/general +> /ultrasound/submit +> South African Site: ftp.sun.ac.za /pub/packages/ultrasound +> Submissions: archive.epas.utoronto.ca pub/pc/ultrasound/submit +> Newly Validated Files: archive.epas.utoronto.ca pub/pc/ultrasound +> +> Mirrors: garbo.uwasa.fi mirror/ultrasound +> ftp.st.nepean.uws.edu.au pc/ultrasound +> ftp.luth.se pub/msdos/ultrasound + +-------------------------------------------------------------------------- +Q: Some files have awful clicks and pops. + +A: Find out which patch is responsible for the clicking (try "timidity + -P<patch> <midi/test-decay|midi/test-panning>". Add "strip=tail" in + the config file after its name. If this doesn't fix it, mail me the + patch. + +-------------------------------------------------------------------------- +Q: I'm playing Fantasie Impromptu in the background. When I run Netscape, + the sound gets choppy and it takes ten minutes to load. What can I do? + +A: Here are some things to try: + + - Use a lower sampling rate. + + - Use mono output. This can improve performance by 10-30%. + (Using 8-bit instead of 16-bit output makes no difference.) + + - Use a smaller number of simultaneous voices. + + - Make sure you compiled with FAST_DECAY and PRECALC_LOOPS enabled + in config.h + + - If you don't have hardware to compute sines, recompile with + LOOKUP_SINE enabled in config.h + + - Recompile with LOOKUP_HACK enabled in config.h. + + - Recompile with LINEAR_INTERPOLATION disabled in config.h. + + - Recompile with DANGEROUS_RENICE enabled in config.h, and make + TiMidity setuid root. This will help only if you frequently play + music while other processes are running. + + - Recompile with an Intel-optimized gcc for a 5-15% + performance increase. + +-------------------------------------------------------------------------- diff --git a/apps/plugins/sdl/SDL_mixer/timidity/README b/apps/plugins/sdl/SDL_mixer/timidity/README new file mode 100644 index 0000000000..e0882f3d03 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/timidity/README @@ -0,0 +1,57 @@ +[This version of timidity has been stripped for simplicity in porting to SDL] +---------------------------------*-text-*--------------------------------- + + From http://www.cgs.fi/~tt/discontinued.html : + + If you'd like to continue hacking on TiMidity, feel free. I'm + hereby extending the TiMidity license agreement: you can now + select the most convenient license for your needs from (1) the + GNU GPL, (2) the GNU LGPL, or (3) the Perl Artistic License. + +-------------------------------------------------------------------------- + + This is the README file for TiMidity v0.2i + + TiMidity is a MIDI to WAVE converter that uses Gravis +Ultrasound(*)-compatible patch files to generate digital audio data +from General MIDI files. The audio data can be played through any +sound device or stored on disk. On a fast machine, music can be +played in real time. TiMidity runs under Linux, FreeBSD, HP-UX, SunOS, and +Win32, and porting to other systems with gcc should be easy. + + TiMidity Features: + + * 32 or more dynamically allocated fully independent voices + * Compatibility with GUS patch files + * Output to 16- or 8-bit PCM or uLaw audio device, file, or + stdout at any sampling rate + * Optional interactive mode with real-time status display + under ncurses and SLang terminal control libraries. Also + a user friendly motif interface since version 0.2h + * Support for transparent loading of compressed MIDI files and + patch files + + * Support for the following MIDI events: + - Program change + - Key pressure + - Channel main volume + - Tempo + - Panning + - Damper pedal (Sustain) + - Pitch wheel + - Pitch wheel sensitivity + - Change drum set + +* TiMidity requires sampled instruments (patches) to play MIDI files. You + should get the file "timidity-lib-0.1.tar.gz" and unpack it in the same + directory where you unpacked the source code archive. You'll want more + patches later -- read the file "FAQ" for pointers. + +* Timidity is no longer supported, but can be found by searching the web. + + + Tuukka Toivonen <toivonen@clinet.fi> + +[(*) Any Registered Trademarks used anywhere in the documentation or +source code for TiMidity are acknowledged as belonging to their +respective owners.] diff --git a/apps/plugins/sdl/SDL_mixer/timidity/common.c b/apps/plugins/sdl/SDL_mixer/timidity/common.c new file mode 100644 index 0000000000..bc284e6ccf --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/timidity/common.c @@ -0,0 +1,238 @@ +/* + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi> + + This program is free software; you can redistribute it and/or modify + it under the terms of the Perl Artistic License, available in COPYING. + */ + +#include "config.h" +#include "common.h" +#include "output.h" +#include "ctrlmode.h" + +/* I guess "rb" should be right for any libc */ +#define OPEN_MODE "rb" + +char current_filename[PATH_MAX]; + +static PathList *pathlist=NULL; + +/* Try to open a file for reading. If the filename ends in one of the + defined compressor extensions, pipe the file through the decompressor */ +static FILE *try_to_open(const char *name, int decompress, int noise_mode) +{ + FILE *fp; + + fp=fopen(name, OPEN_MODE); /* First just check that the file exists */ + + if (!fp) + return 0; + +#ifdef DECOMPRESSOR_LIST + if (decompress) + { + int l,el; + static char *decompressor_list[] = DECOMPRESSOR_LIST, **dec; + const char *cp; + char tmp[PATH_MAX], tmp2[PATH_MAX], *cp2; + /* Check if it's a compressed file */ + l=strlen(name); + for (dec=decompressor_list; *dec; dec+=2) + { + el=strlen(*dec); + if ((el>=l) || (strcmp(name+l-el, *dec))) + continue; + + /* Yes. Close the file, open a pipe instead. */ + fclose(fp); + + /* Quote some special characters in the file name */ + cp=name; + cp2=tmp2; + while (*cp) + { + switch(*cp) + { + case '\'': + case '\\': + case ' ': + case '`': + case '!': + case '"': + case '&': + case ';': + *cp2++='\\'; + } + *cp2++=*cp++; + } + *cp2=0; + + sprintf(tmp, *(dec+1), tmp2); + fp=popen(tmp, "r"); + break; + } + } +#endif + + return fp; +} + +/* This is meant to find and open files for reading, possibly piping + them through a decompressor. */ +FILE *open_file(const char *name, int decompress, int noise_mode) +{ + FILE *fp; + PathList *plp; + int l; + + if (!name || !(*name)) + { + ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Attempted to open nameless file."); + return 0; + } + + if (pathlist==NULL) { + /* Generate path list */ +#ifdef DEFAULT_PATH + add_to_pathlist(DEFAULT_PATH); +#endif +#ifdef DEFAULT_PATH1 + add_to_pathlist(DEFAULT_PATH1); +#endif +#ifdef DEFAULT_PATH2 + add_to_pathlist(DEFAULT_PATH2); +#endif +#ifdef DEFAULT_PATH3 + add_to_pathlist(DEFAULT_PATH3); +#endif + } + + /* First try the given name */ + + strncpy(current_filename, name, PATH_MAX - 1); + current_filename[PATH_MAX - 1]='\0'; + + ctl->cmsg(CMSG_INFO, VERB_DEBUG, "Trying to open %s", current_filename); + if ((fp=try_to_open(current_filename, decompress, noise_mode))) + return fp; + +#ifdef ENOENT + if (noise_mode && (errno != ENOENT)) + { + ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s", + current_filename, strerror(errno)); + return 0; + } +#endif + + plp=pathlist; + if (name[0] != PATH_SEP) + while (plp) /* Try along the path then */ + { + *current_filename=0; + l=strlen(plp->path); + if(l) + { + strcpy(current_filename, plp->path); + if(current_filename[l-1]!=PATH_SEP) + strcat(current_filename, PATH_STRING); + } + strcat(current_filename, name); + ctl->cmsg(CMSG_INFO, VERB_DEBUG, "Trying to open %s", current_filename); + if ((fp=try_to_open(current_filename, decompress, noise_mode))) + return fp; +#ifdef ENOENT + if (noise_mode && (errno != ENOENT)) + { + ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s", + current_filename, strerror(errno)); + return 0; + } +#endif + plp=plp->next; + } + + /* Nothing could be opened. */ + + *current_filename=0; + + if (noise_mode>=2) + ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s", name, strerror(errno)); + + return 0; +} + +/* This closes files opened with open_file */ +void close_file(FILE *fp) +{ +#ifdef DECOMPRESSOR_LIST + if (pclose(fp)) /* Any better ideas? */ +#endif + fclose(fp); + + strncpy(current_filename, "MIDI file", PATH_MAX - 1); +} + +/* This is meant for skipping a few bytes in a file or fifo. */ +void skip(FILE *fp, size_t len) +{ + size_t c; + char tmp[PATH_MAX]; + while (len>0) + { + c=len; + if (c>PATH_MAX) c=PATH_MAX; + len-=c; + if (c!=fread(tmp, 1, c, fp)) + ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: skip: %s", + current_filename, strerror(errno)); + } +} + +/* This'll allocate memory or die. */ +void *safe_malloc(size_t count) +{ + void *p; + if (count > (1<<21)) + { + ctl->cmsg(CMSG_FATAL, VERB_NORMAL, + "Strange, I feel like allocating %d bytes. This must be a bug.", + count); + } + else if ((p=malloc(count))) + return p; + else + ctl->cmsg(CMSG_FATAL, VERB_NORMAL, "Sorry. Couldn't malloc %d bytes.", count); + + ctl->close(); + exit(10); + return(NULL); +} + +/* This adds a directory to the path list */ +void add_to_pathlist(const char *s) +{ + PathList *plp=safe_malloc(sizeof(PathList)); + strcpy((plp->path=safe_malloc(strlen(s)+1)),s); + plp->next=pathlist; + pathlist=plp; +} + +/* Free memory associated to path list */ +void free_pathlist(void) +{ + PathList *plp, *next_plp; + + plp = pathlist; + while (plp) { + if (plp->path) { + free(plp->path); + plp->path=NULL; + } + next_plp = plp->next; + free(plp); + plp = next_plp; + } + pathlist = NULL; +} diff --git a/apps/plugins/sdl/SDL_mixer/timidity/common.h b/apps/plugins/sdl/SDL_mixer/timidity/common.h new file mode 100644 index 0000000000..8d9c0ec8bc --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/timidity/common.h @@ -0,0 +1,39 @@ +/* + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi> + + This program is free software; you can redistribute it and/or modify + it under the terms of the Perl Artistic License, available in COPYING. + */ + +#include <limits.h> + +#ifndef PATH_MAX /* GNU Hurd doesn't limit path size, thus no PATH_MAX... */ +#define PATH_MAX 1024 /* ...so we'll just impose an arbitrary limit. */ +#endif + +extern char *program_name, current_filename[]; + +extern FILE *msgfp; + +extern int num_ochannels; + +#define MULTICHANNEL_OUT +#define MAX_OUT_CHANNELS 6 + +typedef struct { + char *path; + void *next; +} PathList; + +/* Noise modes for open_file */ +#define OF_SILENT 0 +#define OF_NORMAL 1 +#define OF_VERBOSE 2 + +extern FILE *open_file(const char *name, int decompress, int noise_mode); +extern void add_to_pathlist(const char *s); +extern void free_pathlist(void); +extern void close_file(FILE *fp); +extern void skip(FILE *fp, size_t len); +extern void *safe_malloc(size_t count); diff --git a/apps/plugins/sdl/SDL_mixer/timidity/config.h b/apps/plugins/sdl/SDL_mixer/timidity/config.h new file mode 100644 index 0000000000..46f1aa95fe --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/timidity/config.h @@ -0,0 +1,229 @@ +/* + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi> + + This program is free software; you can redistribute it and/or modify + it under the terms of the Perl Artistic License, available in COPYING. + */ + +/* This is for use with the SDL library */ +#ifndef __TIMIDITY_CONFIG_H__ +#define __TIMIDITY_CONFIG_H__ +#define SDL +#include "SDL_config.h" +#include "SDL_endian.h" + +#define TIMIDITY_ERROR_SIZE 1024 + +/* When a patch file can't be opened, one of these extensions is + appended to the filename and the open is tried again. + */ +#define PATCH_EXT_LIST { ".pat", 0 } + +/* Acoustic Grand Piano seems to be the usual default instrument. */ +#define DEFAULT_PROGRAM 0 + +/* 9 here is MIDI channel 10, which is the standard percussion channel. + Some files (notably C:\WINDOWS\CANYON.MID) think that 16 is one too. + On the other hand, some files know that 16 is not a drum channel and + try to play music on it. This is now a runtime option, so this isn't + a critical choice anymore. */ +#define DEFAULT_DRUMCHANNELS (1<<9) + +/* A somewhat arbitrary frequency range. The low end of this will + sound terrible as no lowpass filtering is performed on most + instruments before resampling. */ +#define MIN_OUTPUT_RATE 4000 +#define MAX_OUTPUT_RATE 65000 + +/* In percent. */ +/* #define DEFAULT_AMPLIFICATION 70 */ +/* #define DEFAULT_AMPLIFICATION 50 */ +#define DEFAULT_AMPLIFICATION 30 + +/* Default sampling rate, default polyphony, and maximum polyphony. + All but the last can be overridden from the command line. */ +#define DEFAULT_RATE 32000 +/* #define DEFAULT_VOICES 32 */ +/* #define MAX_VOICES 48 */ +#define DEFAULT_VOICES 256 +#define MAX_VOICES 256 +#define MAXCHAN 16 +/* #define MAXCHAN 64 */ +#define MAXNOTE 128 + +/* 1000 here will give a control ratio of 22:1 with 22 kHz output. + Higher CONTROLS_PER_SECOND values allow more accurate rendering + of envelopes and tremolo. The cost is CPU time. */ +#define CONTROLS_PER_SECOND 1000 + +/* Strongly recommended. This option increases CPU usage by half, but + without it sound quality is very poor. */ +#define LINEAR_INTERPOLATION + +/* This is an experimental kludge that needs to be done right, but if + you've got an 8-bit sound card, or cheap multimedia speakers hooked + to your 16-bit output device, you should definitely give it a try. + + Defining LOOKUP_HACK causes table lookups to be used in mixing + instead of multiplication. We convert the sample data to 8 bits at + load time and volumes to logarithmic 7-bit values before looking up + the product, which degrades sound quality noticeably. + + Defining LOOKUP_HACK should save ~20% of CPU on an Intel machine. + LOOKUP_INTERPOLATION might give another ~5% */ +/* #define LOOKUP_HACK + #define LOOKUP_INTERPOLATION */ + +/* Make envelopes twice as fast. Saves ~20% CPU time (notes decay + faster) and sounds more like a GUS. There is now a command line + option to toggle this as well. */ +/* #define FAST_DECAY */ + +/* How many bits to use for the fractional part of sample positions. + This affects tonal accuracy. The entire position counter must fit + in 32 bits, so with FRACTION_BITS equal to 12, the maximum size of + a sample is 1048576 samples (2 megabytes in memory). The GUS gets + by with just 9 bits and a little help from its friends... + "The GUS does not SUCK!!!" -- a happy user :) */ +#define FRACTION_BITS 12 + +#define MAX_SAMPLE_SIZE (1 << (32-FRACTION_BITS)) + +typedef double FLOAT_T; + +/* For some reason the sample volume is always set to maximum in all + patch files. Define this for a crude adjustment that may help + equalize instrument volumes. */ +#define ADJUST_SAMPLE_VOLUMES + +/* The number of samples to use for ramping out a dying note. Affects + click removal. */ +#define MAX_DIE_TIME 20 + +/* On some machines (especially PCs without math coprocessors), + looking up sine values in a table will be significantly faster than + computing them on the fly. Uncomment this to use lookups. */ +/* #define LOOKUP_SINE */ + +/* Shawn McHorse's resampling optimizations. These may not in fact be + faster on your particular machine and compiler. You'll have to run + a benchmark to find out. */ +#define PRECALC_LOOPS + +/* If calling ldexp() is faster than a floating point multiplication + on your machine/compiler/libm, uncomment this. It doesn't make much + difference either way, but hey -- it was on the TODO list, so it + got done. */ +/* #define USE_LDEXP */ + +/**************************************************************************/ +/* Anything below this shouldn't need to be changed unless you're porting + to a new machine with other than 32-bit, big-endian words. */ +/**************************************************************************/ + +/* change FRACTION_BITS above, not these */ +#define INTEGER_BITS (32 - FRACTION_BITS) +#define INTEGER_MASK (0xFFFFFFFF << FRACTION_BITS) +#define FRACTION_MASK (~ INTEGER_MASK) + +/* This is enforced by some computations that must fit in an int */ +#define MAX_CONTROL_RATIO 255 + +typedef unsigned int uint32; +typedef int int32; +typedef unsigned short uint16; +typedef short int16; +typedef unsigned char uint8; +typedef char int8; + +/* Instrument files are little-endian, MIDI files big-endian, so we + need to do some conversions. */ + +#define XCHG_SHORT(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF)) +# define XCHG_LONG(x) ((((x)&0xFF)<<24) | \ + (((x)&0xFF00)<<8) | \ + (((x)&0xFF0000)>>8) | \ + (((x)>>24)&0xFF)) + +#if SDL_BYTEORDER == SDL_LIL_ENDIAN +#define LE_SHORT(x) x +#define LE_LONG(x) x +#define BE_SHORT(x) XCHG_SHORT(x) +#define BE_LONG(x) XCHG_LONG(x) +#else +#define BE_SHORT(x) x +#define BE_LONG(x) x +#define LE_SHORT(x) XCHG_SHORT(x) +#define LE_LONG(x) XCHG_LONG(x) +#endif + +#define MAX_AMPLIFICATION 800 + +/* You could specify a complete path, e.g. "/etc/timidity.cfg", and + then specify the library directory in the configuration file. */ +#define CONFIG_FILE "/.rockbox/timidity/timidity.cfg" +#define CONFIG_FILE_ETC "/.rockbox/timidity/timidity.cfg" + +#if defined(__WIN32__) || defined(__OS2__) +#define DEFAULT_PATH "C:\\TIMIDITY" +#else +#define DEFAULT_PATH "/.rockbox/patchset" +#define DEFAULT_PATH1 "/.rockbox/duke3d" +#define DEFAULT_PATH2 "/.rockbox/timidity" +#define DEFAULT_PATH3 "/.rockbox/midi" +#endif + +/* These affect general volume */ +#define GUARD_BITS 3 +#define AMP_BITS (15-GUARD_BITS) + +#ifdef LOOKUP_HACK + typedef int8 sample_t; + typedef uint8 final_volume_t; +# define FINAL_VOLUME(v) (~_l2u[v]) +# define MIXUP_SHIFT 5 +# define MAX_AMP_VALUE 4095 +#else + typedef int16 sample_t; + typedef int32 final_volume_t; +# define FINAL_VOLUME(v) (v) +# define MAX_AMP_VALUE ((1<<(AMP_BITS+1))-1) +#endif + +typedef int16 resample_t; + +#ifdef USE_LDEXP +# define FSCALE(a,b) ldexp((a),(b)) +# define FSCALENEG(a,b) ldexp((a),-(b)) +#else +# define FSCALE(a,b) (float)((a) * (double)(1<<(b))) +# define FSCALENEG(a,b) (float)((a) * (1.0L / (double)(1<<(b)))) +#endif + +/* Vibrato and tremolo Choices of the Day */ +#define SWEEP_TUNING 38 +#define VIBRATO_AMPLITUDE_TUNING 1.0L +#define VIBRATO_RATE_TUNING 38 +#define TREMOLO_AMPLITUDE_TUNING 1.0L +#define TREMOLO_RATE_TUNING 38 + +#define SWEEP_SHIFT 16 +#define RATE_SHIFT 5 + +#define VIBRATO_SAMPLE_INCREMENTS 32 + +#ifndef PI + #define PI 3.14159265358979323846 +#endif + +/* The path separator (D.M.) */ +#if defined(__WIN32__) || defined(__OS2__) +# define PATH_SEP '\\' +# define PATH_STRING "\\" +#else +# define PATH_SEP '/' +# define PATH_STRING "/" +#endif + +#endif diff --git a/apps/plugins/sdl/SDL_mixer/timidity/ctrlmode.c b/apps/plugins/sdl/SDL_mixer/timidity/ctrlmode.c new file mode 100644 index 0000000000..facaa0b4f9 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/timidity/ctrlmode.c @@ -0,0 +1,26 @@ +/* + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi> + + This program is free software; you can redistribute it and/or modify + it under the terms of the Perl Artistic License, available in COPYING. + */ + +#include "config.h" +#include "ctrlmode.h" + +#ifdef SDL + extern ControlMode sdl_control_mode; +# ifndef DEFAULT_CONTROL_MODE +# define DEFAULT_CONTROL_MODE &sdl_control_mode +# endif +#endif + +ControlMode *ctl_list[]={ +#ifdef SDL + &sdl_control_mode, +#endif + 0 +}; + +ControlMode *ctl=DEFAULT_CONTROL_MODE; diff --git a/apps/plugins/sdl/SDL_mixer/timidity/ctrlmode.h b/apps/plugins/sdl/SDL_mixer/timidity/ctrlmode.h new file mode 100644 index 0000000000..5a116bc63c --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/timidity/ctrlmode.h @@ -0,0 +1,74 @@ +/* + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi> + + This program is free software; you can redistribute it and/or modify + it under the terms of the Perl Artistic License, available in COPYING. + */ + +/* Return values for ControlMode.read */ + +#define RC_ERROR -1 +#define RC_NONE 0 +#define RC_QUIT 1 +#define RC_NEXT 2 +#define RC_PREVIOUS 3 /* Restart this song at beginning, or the previous + song if we're less than a second into this one. */ +#define RC_FORWARD 4 +#define RC_BACK 5 +#define RC_JUMP 6 +#define RC_TOGGLE_PAUSE 7 /* Pause/continue */ +#define RC_RESTART 8 /* Restart song at beginning */ + +#define RC_PAUSE 9 /* Really pause playing */ +#define RC_CONTINUE 10 /* Continue if paused */ +#define RC_REALLY_PREVIOUS 11 /* Really go to the previous song */ +#define RC_CHANGE_VOLUME 12 +#define RC_LOAD_FILE 13 /* Load a new midifile */ +#define RC_TUNE_END 14 /* The tune is over, play it again sam? */ + +#define CMSG_INFO 0 +#define CMSG_WARNING 1 +#define CMSG_ERROR 2 +#define CMSG_FATAL 3 +#define CMSG_TRACE 4 +#define CMSG_TIME 5 +#define CMSG_TOTAL 6 +#define CMSG_FILE 7 +#define CMSG_TEXT 8 + +#define VERB_NORMAL 0 +#define VERB_VERBOSE 1 +#define VERB_NOISY 2 +#define VERB_DEBUG 3 +#define VERB_DEBUG_SILLY 4 + +typedef struct { + char *id_name, id_character; + int verbosity, trace_playing, opened; + + int (*open)(int using_stdin, int using_stdout); + void (*pass_playing_list)(int number_of_files, char *list_of_files[]); + void (*close)(void); + int (*read)(int32 *valp); + int (*cmsg)(int type, int verbosity_level, char *fmt, ...); + + void (*refresh)(void); + void (*reset)(void); + void (*file_name)(char *name); + void (*total_time)(int tt); + void (*current_time)(int ct); + + void (*note)(int v); + void (*master_volume)(int mv); + void (*program)(int channel, int val); /* val<0 means drum set -val */ + void (*volume)(int channel, int val); + void (*expression)(int channel, int val); + void (*panning)(int channel, int val); + void (*sustain)(int channel, int val); + void (*pitch_bend)(int channel, int val); + +} ControlMode; + +extern ControlMode *ctl_list[], *ctl; +extern char timidity_error[]; diff --git a/apps/plugins/sdl/SDL_mixer/timidity/filter.c b/apps/plugins/sdl/SDL_mixer/timidity/filter.c new file mode 100644 index 0000000000..e93cc6b23c --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/timidity/filter.c @@ -0,0 +1,187 @@ +/* + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi> + + This program is free software; you can redistribute it and/or modify + it under the terms of the Perl Artistic License, available in COPYING. + + filter.c: written by Vincent Pagel ( pagel@loria.fr ) + + implements fir antialiasing filter : should help when setting sample + rates as low as 8Khz. + + April 95 + - first draft + + 22/5/95 + - modify "filter" so that it simulate leading and trailing 0 in the buffer + */ + +#include "config.h" +#include "common.h" +#include "ctrlmode.h" +#include "instrum.h" +#include "filter.h" + +/* bessel function */ +static float ino(float x) +{ + float y, de, e, sde; + int i; + + y = x / 2; + e = 1.0; + de = 1.0; + i = 1; + do { + de = de * y / (float) i; + sde = de * de; + e += sde; + } while (!( (e * 1.0e-08 - sde > 0) || (i++ > 25) )); + return(e); +} + +/* Kaiser Window (symetric) */ +static void kaiser(float *w,int n,float beta) +{ + float xind, xi; + int i; + + xind = (float)((2*n - 1) * (2*n - 1)); + for (i =0; i<n ; i++) + { + xi = (float)(i + 0.5); + w[i] = ino((float)(beta * sqrt((double)(1. - 4 * xi * xi / xind)))) + / ino((float)beta); + } +} + +/* + * fir coef in g, cuttoff frequency in fc + */ +static void designfir(float *g , float fc) +{ + int i; + float xi, omega, att, beta ; + float w[ORDER2]; + + for (i =0; i < ORDER2 ;i++) + { + xi = (float) (i + 0.5); + omega = (float)(PI * xi); + g[i] = (float)(sin( (double) omega * fc) / omega); + } + + att = 40.; /* attenuation in db */ + beta = (float) (exp(log((double)0.58417 * (att - 20.96)) * 0.4) + 0.07886 + * (att - 20.96)); + kaiser( w, ORDER2, beta); + + /* Matrix product */ + for (i =0; i < ORDER2 ; i++) + g[i] = g[i] * w[i]; +} + +/* + * FIR filtering -> apply the filter given by coef[] to the data buffer + * Note that we simulate leading and trailing 0 at the border of the + * data buffer + */ +static void filter(sample_t *result,sample_t *data, int32 length,float coef[]) +{ + int32 sample,i,sample_window; + int16 peak = 0; + float sum; + + /* Simulate leading 0 at the begining of the buffer */ + for (sample = 0; sample < ORDER2 ; sample++ ) + { + sum = 0.0; + sample_window= sample - ORDER2; + + for (i = 0; i < ORDER ;i++) + sum += (float)(coef[i] * + ((sample_window<0)? 0.0 : data[sample_window++])) ; + + /* Saturation ??? */ + if (sum> 32767.) { sum=32767.; peak++; } + if (sum< -32768.) { sum=-32768; peak++; } + result[sample] = (sample_t) sum; + } + + /* The core of the buffer */ + for (sample = ORDER2; sample < length - ORDER + ORDER2 ; sample++ ) + { + sum = 0.0; + sample_window= sample - ORDER2; + + for (i = 0; i < ORDER ;i++) + sum += data[sample_window++] * coef[i]; + + /* Saturation ??? */ + if (sum> 32767.) { sum=32767.; peak++; } + if (sum< -32768.) { sum=-32768; peak++; } + result[sample] = (sample_t) sum; + } + + /* Simulate 0 at the end of the buffer */ + for (sample = length - ORDER + ORDER2; sample < length ; sample++ ) + { + sum = 0.0; + sample_window= sample - ORDER2; + + for (i = 0; i < ORDER ;i++) + sum += (float)(coef[i] * + ((sample_window>=length)? 0.0 : data[sample_window++])) ; + + /* Saturation ??? */ + if (sum> 32767.) { sum=32767.; peak++; } + if (sum< -32768.) { sum=-32768; peak++; } + result[sample] = (sample_t) sum; + } + + if (peak) + ctl->cmsg(CMSG_ERROR, VERB_NORMAL, + "Saturation %2.3f %%.", 100.0*peak/ (float) length); +} + +/***********************************************************************/ +/* Prevent aliasing by filtering any freq above the output_rate */ +/* */ +/* I don't worry about looping point -> they will remain soft if they */ +/* were already */ +/***********************************************************************/ +void antialiasing(Sample *sp, int32 output_rate ) +{ + sample_t *temp; + int i; + float fir_symetric[ORDER]; + float fir_coef[ORDER2]; + float freq_cut; /* cutoff frequency [0..1.0] FREQ_CUT/SAMP_FREQ*/ + + + ctl->cmsg(CMSG_INFO, VERB_NOISY, "Antialiasing: Fsample=%iKHz", + sp->sample_rate); + + /* No oversampling */ + if (output_rate>=sp->sample_rate) + return; + + freq_cut= (float) output_rate / (float) sp->sample_rate; + ctl->cmsg(CMSG_INFO, VERB_NOISY, "Antialiasing: cutoff=%f%%", + freq_cut*100.); + + designfir(fir_coef,freq_cut); + + /* Make the filter symetric */ + for (i = 0 ; i<ORDER2 ;i++) + fir_symetric[ORDER-1 - i] = fir_symetric[i] = fir_coef[ORDER2-1 - i]; + + /* We apply the filter we have designed on a copy of the patch */ + temp = safe_malloc(sp->data_length); + memcpy(temp,sp->data,sp->data_length); + + filter(sp->data,temp,sp->data_length/sizeof(sample_t),fir_symetric); + + free(temp); +} diff --git a/apps/plugins/sdl/SDL_mixer/timidity/filter.h b/apps/plugins/sdl/SDL_mixer/timidity/filter.h new file mode 100644 index 0000000000..79133377dd --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/timidity/filter.h @@ -0,0 +1,23 @@ +/* + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi> + + This program is free software; you can redistribute it and/or modify + it under the terms of the Perl Artistic License, available in COPYING. + + filter.h : written by Vincent Pagel ( pagel@loria.fr ) + + implements fir antialiasing filter : should help when setting sample + rates as low as 8Khz. + + */ + +/* Order of the FIR filter = 20 should be enough ! */ +#define ORDER 20 +#define ORDER2 ORDER/2 + +#ifndef PI +#define PI 3.14159265 +#endif + +extern void antialiasing(Sample *sp, int32 output_rate); diff --git a/apps/plugins/sdl/SDL_mixer/timidity/instrum.c b/apps/plugins/sdl/SDL_mixer/timidity/instrum.c new file mode 100644 index 0000000000..c480ec18a1 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/timidity/instrum.c @@ -0,0 +1,1018 @@ +/* + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi> + + This program is free software; you can redistribute it and/or modify + it under the terms of the Perl Artistic License, available in COPYING. + */ + +#include "config.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" +#include "output.h" +#include "ctrlmode.h" +#include "resample.h" +#include "tables.h" +#include "filter.h" + +/* Some functions get aggravated if not even the standard banks are + available. */ +static ToneBank standard_tonebank, standard_drumset; +ToneBank + *tonebank[MAXBANK]={&standard_tonebank}, + *drumset[MAXBANK]={&standard_drumset}; + +/* This is a special instrument, used for all melodic programs */ +InstrumentLayer *default_instrument=0; + +/* This is only used for tracks that don't specify a program */ +int default_program=DEFAULT_PROGRAM; + +int antialiasing_allowed=0; +#ifdef FAST_DECAY +int fast_decay=1; +#else +int fast_decay=0; +#endif + + +int current_tune_number = 0; +int last_tune_purged = 0; +int current_patch_memory = 0; +int max_patch_memory = 60000000; + +static void purge_as_required(void); + +static void free_instrument(Instrument *ip) +{ + Sample *sp; + int i; + if (!ip) return; + + if (!ip->contents) + for (i=0; i<ip->samples; i++) + { + sp=&(ip->sample[i]); + if (sp->data) free(sp->data); + } + free(ip->sample); + + if (!ip->contents) + for (i=0; i<ip->right_samples; i++) + { + sp=&(ip->right_sample[i]); + if (sp->data) free(sp->data); + } + if (ip->right_sample) + free(ip->right_sample); + free(ip); +} + + +static void free_layer(InstrumentLayer *lp) +{ + InstrumentLayer *next; + + current_patch_memory -= lp->size; + + for (; lp; lp = next) + { + next = lp->next; + free_instrument(lp->instrument); + free(lp); + } +} + +static void free_bank(int dr, int b) +{ + int i; + ToneBank *bank=((dr) ? drumset[b] : tonebank[b]); + for (i=0; i<MAXPROG; i++) + { + if (bank->tone[i].layer) + { + /* Not that this could ever happen, of course */ + if (bank->tone[i].layer != MAGIC_LOAD_INSTRUMENT) + { + free_layer(bank->tone[i].layer); + bank->tone[i].layer=NULL; + bank->tone[i].last_used=-1; + } + } + if (bank->tone[i].name) + { + free(bank->tone[i].name); + bank->tone[i].name = NULL; + } + } +} + + +static void free_old_bank(int dr, int b, int how_old) +{ + int i; + ToneBank *bank=((dr) ? drumset[b] : tonebank[b]); + for (i=0; i<MAXPROG; i++) + if (bank->tone[i].layer && bank->tone[i].last_used < how_old) + { + if (bank->tone[i].layer != MAGIC_LOAD_INSTRUMENT) + { + ctl->cmsg(CMSG_INFO, VERB_DEBUG, + "Unloading %s %s[%d,%d] - last used %d.", + (dr)? "drum" : "inst", bank->tone[i].name, + i, b, bank->tone[i].last_used); + free_layer(bank->tone[i].layer); + bank->tone[i].layer=NULL; + bank->tone[i].last_used=-1; + } + } +} + + +int32 convert_envelope_rate_attack(uint8 rate, uint8 fastness) +{ + int32 r; + + r=3-((rate>>6) & 0x3); + r*=3; + r = (int32)(rate & 0x3f) << r; /* 6.9 fixed point */ + + /* 15.15 fixed point. */ + return (((r * 44100) / play_mode->rate) * control_ratio) + << 10; +} + +int32 convert_envelope_rate(uint8 rate) +{ + int32 r; + + r=3-((rate>>6) & 0x3); + r*=3; + r = (int32)(rate & 0x3f) << r; /* 6.9 fixed point */ + + /* 15.15 fixed point. */ + return (((r * 44100) / play_mode->rate) * control_ratio) + << ((fast_decay) ? 10 : 9); +} + +int32 convert_envelope_offset(uint8 offset) +{ + /* This is not too good... Can anyone tell me what these values mean? + Are they GUS-style "exponential" volumes? And what does that mean? */ + + /* 15.15 fixed point */ + return offset << (7+15); +} + +int32 convert_tremolo_sweep(uint8 sweep) +{ + if (!sweep) + return 0; + + return + ((control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / + (play_mode->rate * sweep); +} + +int32 convert_vibrato_sweep(uint8 sweep, int32 vib_control_ratio) +{ + if (!sweep) + return 0; + + return + (int32) (FSCALE((double) (vib_control_ratio) * SWEEP_TUNING, SWEEP_SHIFT) + / (double)(play_mode->rate * sweep)); + + /* this was overflowing with seashore.pat + + ((vib_control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / + (play_mode->rate * sweep); */ +} + +int32 convert_tremolo_rate(uint8 rate) +{ + return + ((SINE_CYCLE_LENGTH * control_ratio * rate) << RATE_SHIFT) / + (TREMOLO_RATE_TUNING * play_mode->rate); +} + +int32 convert_vibrato_rate(uint8 rate) +{ + /* Return a suitable vibrato_control_ratio value */ + return + (VIBRATO_RATE_TUNING * play_mode->rate) / + (rate * 2 * VIBRATO_SAMPLE_INCREMENTS); +} + +static void reverse_data(int16 *sp, int32 ls, int32 le) +{ + int16 s, *ep=sp+le; + sp+=ls; + le-=ls; + le/=2; + while (le--) + { + s=*sp; + *sp++=*ep; + *ep--=s; + } +} + +/* + If panning or note_to_use != -1, it will be used for all samples, + instead of the sample-specific values in the instrument file. + + For note_to_use, any value <0 or >127 will be forced to 0. + + For other parameters, 1 means yes, 0 means no, other values are + undefined. + + TODO: do reverse loops right */ +static InstrumentLayer *load_instrument(const char *name, int font_type, int percussion, + int panning, int amp, int cfg_tuning, int note_to_use, + int strip_loop, int strip_envelope, + int strip_tail, int bank, int gm_num, int sf_ix) +{ + InstrumentLayer *lp, *lastlp, *headlp = 0; + Instrument *ip; + FILE *fp; + uint8 tmp[1024]; + int i,j,noluck=0; +#ifdef PATCH_EXT_LIST + static char *patch_ext[] = PATCH_EXT_LIST; +#endif + int sf2flag = 0; + int right_samples = 0; + int stereo_channels = 1, stereo_layer; + int vlayer_list[19][4], vlayer, vlayer_count = 0; + + if (!name) return 0; + + /* Open patch file */ + if ((fp=open_file(name, 1, OF_NORMAL)) == NULL) + { + noluck=1; +#ifdef PATCH_EXT_LIST + /* Try with various extensions */ + for (i=0; patch_ext[i]; i++) + { + if (strlen(name)+strlen(patch_ext[i])<PATH_MAX) + { + char path[PATH_MAX]; + strcpy(path, name); + strcat(path, patch_ext[i]); + if ((fp=open_file(path, 1, OF_NORMAL)) != NULL) + { + noluck=0; + break; + } + } + } +#endif + } + + if (noluck) + { + ctl->cmsg(CMSG_ERROR, VERB_NORMAL, + "Instrument `%s' can't be found.", name); + fclose(fp); + return 0; + } + + /*ctl->cmsg(CMSG_INFO, VERB_NOISY, "Loading instrument %s", current_filename);*/ + + /* Read some headers and do cursory sanity checks. There are loads + of magic offsets. This could be rewritten... */ + + if ((239 != fread(tmp, 1, 239, fp)) || + (memcmp(tmp, "GF1PATCH110\0ID#000002", 22) && + memcmp(tmp, "GF1PATCH100\0ID#000002", 22))) /* don't know what the + differences are */ + { + ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: not an instrument", name); + fclose(fp); + return 0; + } + +/* patch layout: + * bytes: info: starts at offset: + * 22 id (see above) 0 + * 60 copyright 22 + * 1 instruments 82 + * 1 voices 83 + * 1 channels 84 + * 2 number of waveforms 85 + * 2 master volume 87 + * 4 datasize 89 + * 36 reserved, but now: 93 + * 7 "SF2EXT\0" id 93 + * 1 right samples 100 + * 28 reserved 101 + * 2 instrument number 129 + * 16 instrument name 131 + * 4 instrument size 147 + * 1 number of layers 151 + * 40 reserved 152 + * 1 layer duplicate 192 + * 1 layer number 193 + * 4 layer size 194 + * 1 number of samples 198 + * 40 reserved 199 + * 239 + * THEN, for each sample, see below + */ + + if (!memcmp(tmp + 93, "SF2EXT", 6)) + { + sf2flag = 1; + vlayer_count = tmp[152]; + } + + if (tmp[82] != 1 && tmp[82] != 0) /* instruments. To some patch makers, + 0 means 1 */ + { + ctl->cmsg(CMSG_ERROR, VERB_NORMAL, + "Can't handle patches with %d instruments", tmp[82]); + fclose(fp); + return 0; + } + + if (tmp[151] != 1 && tmp[151] != 0) /* layers. What's a layer? */ + { + ctl->cmsg(CMSG_ERROR, VERB_NORMAL, + "Can't handle instruments with %d layers", tmp[151]); + fclose(fp); + return 0; + } + + + if (sf2flag && vlayer_count > 0) { + for (i = 0; i < 9; i++) + for (j = 0; j < 4; j++) + vlayer_list[i][j] = tmp[153+i*4+j]; + for (i = 9; i < 19; i++) + for (j = 0; j < 4; j++) + vlayer_list[i][j] = tmp[199+(i-9)*4+j]; + } + else { + for (i = 0; i < 19; i++) + for (j = 0; j < 4; j++) + vlayer_list[i][j] = 0; + vlayer_list[0][0] = 0; + vlayer_list[0][1] = 127; + vlayer_list[0][2] = tmp[198]; + vlayer_list[0][3] = 0; + vlayer_count = 1; + } + + lastlp = 0; + + for (vlayer = 0; vlayer < vlayer_count; vlayer++) { + + lp=(InstrumentLayer *)safe_malloc(sizeof(InstrumentLayer)); + lp->size = sizeof(InstrumentLayer); + lp->lo = vlayer_list[vlayer][0]; + lp->hi = vlayer_list[vlayer][1]; + ip=(Instrument *)safe_malloc(sizeof(Instrument)); + lp->size += sizeof(Instrument); + lp->instrument = ip; + lp->next = 0; + + if (lastlp) lastlp->next = lp; + else headlp = lp; + + lastlp = lp; + + if (sf2flag) ip->type = INST_SF2; + else ip->type = INST_GUS; + ip->samples = vlayer_list[vlayer][2]; + ip->sample = (Sample *)safe_malloc(sizeof(Sample) * ip->samples); + lp->size += sizeof(Sample) * ip->samples; + ip->left_samples = ip->samples; + ip->left_sample = ip->sample; + right_samples = vlayer_list[vlayer][3]; + ip->right_samples = right_samples; + if (right_samples) + { + ip->right_sample = (Sample *)safe_malloc(sizeof(Sample) * right_samples); + lp->size += sizeof(Sample) * right_samples; + stereo_channels = 2; + } + else ip->right_sample = 0; + ip->contents = 0; + + ctl->cmsg(CMSG_INFO, VERB_NOISY, "%s%s[%d,%d] %s(%d-%d layer %d of %d)", + (percussion)? " ":"", name, + (percussion)? note_to_use : gm_num, bank, + (right_samples)? "(2) " : "", + lp->lo, lp->hi, vlayer+1, vlayer_count); + + for (stereo_layer = 0; stereo_layer < stereo_channels; stereo_layer++) + { + int sample_count = 0; + + if (stereo_layer == 0) sample_count = ip->left_samples; + else if (stereo_layer == 1) sample_count = ip->right_samples; + + for (i=0; i < sample_count; i++) + { + uint8 fractions; + int32 tmplong; + uint16 tmpshort; + uint16 sample_volume = 0; + uint8 tmpchar; + Sample *sp = 0; + uint8 sf2delay = 0; + +#define READ_CHAR(thing) \ + if (1 != fread(&tmpchar, 1, 1, fp)) { \ + printf("error readc\n"); goto fail; } \ + thing = tmpchar; +#define READ_SHORT(thing) \ + if (1 != fread(&tmpshort, 2, 1, fp)) { \ + printf("error reads\n"); goto fail; } \ + thing = LE_SHORT(tmpshort); +#define READ_LONG(thing) \ + if (1 != fread(&tmplong, 4, 1, fp)) { \ + printf("error readl\n"); goto fail; } \ + thing = LE_LONG(tmplong); + +/* + * 7 sample name + * 1 fractions + * 4 length + * 4 loop start + * 4 loop end + * 2 sample rate + * 4 low frequency + * 4 high frequency + * 2 finetune + * 1 panning + * 6 envelope rates | + * 6 envelope offsets | 18 bytes + * 3 tremolo sweep, rate, depth | + * 3 vibrato sweep, rate, depth | + * 1 sample mode + * 2 scale frequency + * 2 scale factor + * 2 sample volume (??) + * 34 reserved + * Now: 1 delay + * 33 reserved + */ + skip(fp, 7); /* Skip the wave name */ + + if (1 != fread(&fractions, 1, 1, fp)) + { + printf("error 1\n"); + fail: + ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Error reading sample %d", i); + if (stereo_layer == 1) + { + for (j=0; j<i; j++) + free(ip->right_sample[j].data); + free(ip->right_sample); + i = ip->left_samples; + } + for (j=0; j<i; j++) + free(ip->left_sample[j].data); + free(ip->left_sample); + free(ip); + free(lp); + fclose(fp); + return 0; + } + + if (stereo_layer == 0) sp=&(ip->left_sample[i]); + else if (stereo_layer == 1) sp=&(ip->right_sample[i]); + + READ_LONG(sp->data_length); + READ_LONG(sp->loop_start); + READ_LONG(sp->loop_end); + READ_SHORT(sp->sample_rate); + READ_LONG(sp->low_freq); + READ_LONG(sp->high_freq); + READ_LONG(sp->root_freq); + skip(fp, 2); /* Why have a "root frequency" and then "tuning"?? */ + + READ_CHAR(tmp[0]); + + if (panning==-1) + sp->panning = (tmp[0] * 8 + 4) & 0x7f; + else + sp->panning=(uint8)(panning & 0x7F); + + sp->resonance=0; + sp->cutoff_freq=0; + sp->reverberation=0; + sp->chorusdepth=0; + sp->exclusiveClass=0; + sp->keyToModEnvHold=0; + sp->keyToModEnvDecay=0; + sp->keyToVolEnvHold=0; + sp->keyToVolEnvDecay=0; + + if (cfg_tuning) + { + double tune_factor = (double)(cfg_tuning)/1200.0; + tune_factor = pow(2.0, tune_factor); + sp->root_freq = (uint32)( tune_factor * (double)sp->root_freq ); + } + + /* envelope, tremolo, and vibrato */ + if (18 != fread(tmp, 1, 18, fp)) { printf("error 2\n"); goto fail; } + + if (!tmp[13] || !tmp[14]) + { + sp->tremolo_sweep_increment= + sp->tremolo_phase_increment=sp->tremolo_depth=0; + ctl->cmsg(CMSG_INFO, VERB_DEBUG, " * no tremolo"); + } + else + { + sp->tremolo_sweep_increment=convert_tremolo_sweep(tmp[12]); + sp->tremolo_phase_increment=convert_tremolo_rate(tmp[13]); + sp->tremolo_depth=tmp[14]; + ctl->cmsg(CMSG_INFO, VERB_DEBUG, + " * tremolo: sweep %d, phase %d, depth %d", + sp->tremolo_sweep_increment, sp->tremolo_phase_increment, + sp->tremolo_depth); + } + + if (!tmp[16] || !tmp[17]) + { + sp->vibrato_sweep_increment= + sp->vibrato_control_ratio=sp->vibrato_depth=0; + ctl->cmsg(CMSG_INFO, VERB_DEBUG, " * no vibrato"); + } + else + { + sp->vibrato_control_ratio=convert_vibrato_rate(tmp[16]); + sp->vibrato_sweep_increment= + convert_vibrato_sweep(tmp[15], sp->vibrato_control_ratio); + sp->vibrato_depth=tmp[17]; + ctl->cmsg(CMSG_INFO, VERB_DEBUG, + " * vibrato: sweep %d, ctl %d, depth %d", + sp->vibrato_sweep_increment, sp->vibrato_control_ratio, + sp->vibrato_depth); + + } + + READ_CHAR(sp->modes); + READ_SHORT(sp->freq_center); + READ_SHORT(sp->freq_scale); + + if (sf2flag) + { + READ_SHORT(sample_volume); + READ_CHAR(sf2delay); + READ_CHAR(sp->exclusiveClass); + skip(fp, 32); + } + else + { + skip(fp, 36); + } + + /* Mark this as a fixed-pitch instrument if such a deed is desired. */ + if (note_to_use!=-1) + sp->note_to_use=(uint8)(note_to_use); + else + sp->note_to_use=0; + + /* seashore.pat in the Midia patch set has no Sustain. I don't + understand why, and fixing it by adding the Sustain flag to + all looped patches probably breaks something else. We do it + anyway. */ + + if (sp->modes & MODES_LOOPING) + sp->modes |= MODES_SUSTAIN; + + /* Strip any loops and envelopes we're permitted to */ + if ((strip_loop==1) && + (sp->modes & (MODES_SUSTAIN | MODES_LOOPING | + MODES_PINGPONG | MODES_REVERSE))) + { + ctl->cmsg(CMSG_INFO, VERB_DEBUG, " - Removing loop and/or sustain"); + sp->modes &=~(MODES_SUSTAIN | MODES_LOOPING | + MODES_PINGPONG | MODES_REVERSE); + } + + if (strip_envelope==1) + { + if (sp->modes & MODES_ENVELOPE) + ctl->cmsg(CMSG_INFO, VERB_DEBUG, " - Removing envelope"); + sp->modes &= ~MODES_ENVELOPE; + } + else if (strip_envelope != 0) + { + /* Have to make a guess. */ + if (!(sp->modes & (MODES_LOOPING | MODES_PINGPONG | MODES_REVERSE))) + { + /* No loop? Then what's there to sustain? No envelope needed + either... */ + sp->modes &= ~(MODES_SUSTAIN|MODES_ENVELOPE); + ctl->cmsg(CMSG_INFO, VERB_DEBUG, + " - No loop, removing sustain and envelope"); + } + else if (!memcmp(tmp, "??????", 6) || tmp[11] >= 100) + { + /* Envelope rates all maxed out? Envelope end at a high "offset"? + That's a weird envelope. Take it out. */ + sp->modes &= ~MODES_ENVELOPE; + ctl->cmsg(CMSG_INFO, VERB_DEBUG, + " - Weirdness, removing envelope"); + } + else if (!(sp->modes & MODES_SUSTAIN)) + { + /* No sustain? Then no envelope. I don't know if this is + justified, but patches without sustain usually don't need the + envelope either... at least the Gravis ones. They're mostly + drums. I think. */ + sp->modes &= ~MODES_ENVELOPE; + ctl->cmsg(CMSG_INFO, VERB_DEBUG, + " - No sustain, removing envelope"); + } + } + + sp->attenuation = 0; + + for (j=ATTACK; j<DELAY; j++) + { + sp->envelope_rate[j]= + (j<3)? convert_envelope_rate_attack(tmp[j], 11) : convert_envelope_rate(tmp[j]); + sp->envelope_offset[j]= + convert_envelope_offset(tmp[6+j]); + } + if (sf2flag) + { + if (sf2delay > 5) sf2delay = 5; + sp->envelope_rate[DELAY] = (int32)( (sf2delay*play_mode->rate) / 1000 ); + } + else + { + sp->envelope_rate[DELAY]=0; + } + sp->envelope_offset[DELAY]=0; + + for (j=ATTACK; j<DELAY; j++) + { + sp->modulation_rate[j]=sp->envelope_rate[j]; + sp->modulation_offset[j]=sp->envelope_offset[j]; + } + sp->modulation_rate[DELAY] = sp->modulation_offset[DELAY] = 0; + sp->modEnvToFilterFc=0; + sp->modEnvToPitch=0; + sp->lfo_sweep_increment = 0; + sp->lfo_phase_increment = 0; + sp->modLfoToFilterFc = 0; + sp->vibrato_delay = 0; + + /* Then read the sample data */ + if (sp->data_length/2 > MAX_SAMPLE_SIZE) + { + printf("error 3\n"); + goto fail; + } + sp->data = safe_malloc(sp->data_length + 1); + lp->size += sp->data_length + 1; + + if (1 != fread(sp->data, sp->data_length, 1, fp)) + { + printf("error 4\n"); + goto fail; + } + + if (!(sp->modes & MODES_16BIT)) /* convert to 16-bit data */ + { + int32 i=sp->data_length; + uint8 *cp=(uint8 *)(sp->data); + uint16 *tmp,*newdta; + tmp=newdta=safe_malloc(sp->data_length*2 + 2); + while (i--) + *tmp++ = (uint16)(*cp++) << 8; + cp=(uint8 *)(sp->data); + sp->data = (sample_t *)newdta; + free(cp); + sp->data_length *= 2; + sp->loop_start *= 2; + sp->loop_end *= 2; + } +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + else + /* convert to machine byte order */ + { + int32 i=sp->data_length/2; + int16 *tmp=(int16 *)sp->data,s; + while (i--) + { + s=LE_SHORT(*tmp); + *tmp++=s; + } + } +#endif + + if (sp->modes & MODES_UNSIGNED) /* convert to signed data */ + { + int32 i=sp->data_length/2; + int16 *tmp=(int16 *)sp->data; + while (i--) + *tmp++ ^= 0x8000; + } + + /* Reverse reverse loops and pass them off as normal loops */ + if (sp->modes & MODES_REVERSE) + { + int32 t; + /* The GUS apparently plays reverse loops by reversing the + whole sample. We do the same because the GUS does not SUCK. */ + + ctl->cmsg(CMSG_WARNING, VERB_NORMAL, "Reverse loop in %s", name); + reverse_data((int16 *)sp->data, 0, sp->data_length/2); + + t=sp->loop_start; + sp->loop_start=sp->data_length - sp->loop_end; + sp->loop_end=sp->data_length - t; + + sp->modes &= ~MODES_REVERSE; + sp->modes |= MODES_LOOPING; /* just in case */ + } + + /* If necessary do some anti-aliasing filtering */ + + if (antialiasing_allowed) + antialiasing(sp,play_mode->rate); + +#ifdef ADJUST_SAMPLE_VOLUMES + if (amp!=-1) + sp->volume=(FLOAT_T)((amp) / 100.0); + else if (sf2flag) + sp->volume=(FLOAT_T)((sample_volume) / 255.0); + else + { + /* Try to determine a volume scaling factor for the sample. + This is a very crude adjustment, but things sound more + balanced with it. Still, this should be a runtime option. */ + uint32 i, numsamps=sp->data_length/2; + uint32 higher=0, highcount=0; + int16 maxamp=0,a; + int16 *tmp=(int16 *)sp->data; + i = numsamps; + while (i--) + { + a=*tmp++; + if (a<0) a=-a; + if (a>maxamp) + maxamp=a; + } + tmp=(int16 *)sp->data; + i = numsamps; + while (i--) + { + a=*tmp++; + if (a<0) a=-a; + if (a > 3*maxamp/4) + { + higher += a; + highcount++; + } + } + if (highcount) higher /= highcount; + else higher = 10000; + sp->volume = (32768.0 * 0.875) / (double)higher ; + ctl->cmsg(CMSG_INFO, VERB_DEBUG, " * volume comp: %f", sp->volume); + } +#else + if (amp!=-1) + sp->volume=(double)(amp) / 100.0; + else + sp->volume=1.0; +#endif + + sp->data_length /= 2; /* These are in bytes. Convert into samples. */ + + sp->loop_start /= 2; + sp->loop_end /= 2; + sp->data[sp->data_length] = sp->data[sp->data_length-1]; + + /* Then fractional samples */ + sp->data_length <<= FRACTION_BITS; + sp->loop_start <<= FRACTION_BITS; + sp->loop_end <<= FRACTION_BITS; + + /* trim off zero data at end */ + { + int ls = sp->loop_start>>FRACTION_BITS; + int le = sp->loop_end>>FRACTION_BITS; + int se = sp->data_length>>FRACTION_BITS; + while (se > 1 && !sp->data[se-1]) se--; + if (le > se) le = se; + if (ls >= le) sp->modes &= ~MODES_LOOPING; + sp->loop_end = le<<FRACTION_BITS; + sp->data_length = se<<FRACTION_BITS; + } + + /* Adjust for fractional loop points. This is a guess. Does anyone + know what "fractions" really stands for? */ + sp->loop_start |= + (fractions & 0x0F) << (FRACTION_BITS-4); + sp->loop_end |= + ((fractions>>4) & 0x0F) << (FRACTION_BITS-4); + + /* If this instrument will always be played on the same note, + and it's not looped, we can resample it now. */ + if (sp->note_to_use && !(sp->modes & MODES_LOOPING)) + pre_resample(sp); + +#ifdef LOOKUP_HACK + /* Squash the 16-bit data into 8 bits. */ + { + uint8 *gulp,*ulp; + int16 *swp; + int l=sp->data_length >> FRACTION_BITS; + gulp=ulp=safe_malloc(l+1); + swp=(int16 *)sp->data; + while(l--) + *ulp++ = (*swp++ >> 8) & 0xFF; + free(sp->data); + sp->data=(sample_t *)gulp; + } +#endif + + if (strip_tail==1) + { + /* Let's not really, just say we did. */ + ctl->cmsg(CMSG_INFO, VERB_DEBUG, " - Stripping tail"); + sp->data_length = sp->loop_end; + } + } /* end of sample loop */ + } /* end of stereo layer loop */ + } /* end of vlayer loop */ + + + close_file(fp); + return headlp; +} + +static int fill_bank(int dr, int b) +{ + int i, errors=0; + ToneBank *bank=((dr) ? drumset[b] : tonebank[b]); + if (!bank) + { + ctl->cmsg(CMSG_ERROR, VERB_NORMAL, + "Huh. Tried to load instruments in non-existent %s %d", + (dr) ? "drumset" : "tone bank", b); + return 0; + } + for (i=0; i<MAXPROG; i++) + { + if (bank->tone[i].layer==MAGIC_LOAD_INSTRUMENT) + { + if (!(bank->tone[i].name)) + { + ctl->cmsg(CMSG_WARNING, (b!=0) ? VERB_VERBOSE : VERB_NORMAL, + "No instrument mapped to %s %d, program %d%s", + (dr)? "drum set" : "tone bank", b, i, + (b!=0) ? "" : " - this instrument will not be heard"); + if (b!=0) + { + /* Mark the corresponding instrument in the default + bank / drumset for loading (if it isn't already) */ + if (!dr) + { + if (!(standard_tonebank.tone[i].layer)) + standard_tonebank.tone[i].layer= + MAGIC_LOAD_INSTRUMENT; + } + else + { + if (!(standard_drumset.tone[i].layer)) + standard_drumset.tone[i].layer= + MAGIC_LOAD_INSTRUMENT; + } + } + bank->tone[i].layer=0; + errors++; + } + else if (!(bank->tone[i].layer= + load_instrument(bank->tone[i].name, + bank->tone[i].font_type, + (dr) ? 1 : 0, + bank->tone[i].pan, + bank->tone[i].amp, + bank->tone[i].tuning, + (bank->tone[i].note!=-1) ? + bank->tone[i].note : + ((dr) ? i : -1), + (bank->tone[i].strip_loop!=-1) ? + bank->tone[i].strip_loop : + ((dr) ? 1 : -1), + (bank->tone[i].strip_envelope != -1) ? + bank->tone[i].strip_envelope : + ((dr) ? 1 : -1), + bank->tone[i].strip_tail, + b, + ((dr) ? i + 128 : i), + bank->tone[i].sf_ix + ))) + { + ctl->cmsg(CMSG_ERROR, VERB_NORMAL, + "Couldn't load instrument %s (%s %d, program %d)", + bank->tone[i].name, + (dr)? "drum set" : "tone bank", b, i); + errors++; + } + else + { /* it's loaded now */ + bank->tone[i].last_used = current_tune_number; + current_patch_memory += bank->tone[i].layer->size; + purge_as_required(); + if (current_patch_memory > max_patch_memory) { + ctl->cmsg(CMSG_ERROR, VERB_NORMAL, + "Not enough memory to load instrument %s (%s %d, program %d)", + bank->tone[i].name, + (dr)? "drum set" : "tone bank", b, i); + errors++; + free_layer(bank->tone[i].layer); + bank->tone[i].layer=0; + bank->tone[i].last_used=-1; + } +#if 0 + if (check_for_rc()) { + free_layer(bank->tone[i].layer); + bank->tone[i].layer=0; + bank->tone[i].last_used=-1; + return 0; + } +#endif + } + } + } + return errors; +} + +static void free_old_instruments(int how_old) +{ + int i=MAXBANK; + while(i--) + { + if (tonebank[i]) + free_old_bank(0, i, how_old); + if (drumset[i]) + free_old_bank(1, i, how_old); + } +} + +static void purge_as_required(void) +{ + if (!max_patch_memory) return; + + while (last_tune_purged < current_tune_number + && current_patch_memory > max_patch_memory) + { + last_tune_purged++; + free_old_instruments(last_tune_purged); + } +} + + +int load_missing_instruments(void) +{ + int i=MAXBANK,errors=0; + while (i--) + { + if (tonebank[i]) + errors+=fill_bank(0,i); + if (drumset[i]) + errors+=fill_bank(1,i); + } + current_tune_number++; + return errors; +} + +void free_instruments(void) +{ + int i=128; + while(i--) + { + if (tonebank[i]) + free_bank(0,i); + if (drumset[i]) + free_bank(1,i); + } +} + +int set_default_instrument(const char *name) +{ + InstrumentLayer *lp; +/* if (!(lp=load_instrument(name, 0, -1, -1, -1, 0, 0, 0))) */ + if (!(lp=load_instrument(name, FONT_NORMAL, 0, -1, -1, 0, -1, -1, -1, -1, 0, -1, -1))) + return -1; + if (default_instrument) + free_layer(default_instrument); + default_instrument=lp; + default_program=SPECIAL_PROGRAM; + return 0; +} diff --git a/apps/plugins/sdl/SDL_mixer/timidity/instrum.h b/apps/plugins/sdl/SDL_mixer/timidity/instrum.h new file mode 100644 index 0000000000..7ba0a99bc1 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/timidity/instrum.h @@ -0,0 +1,168 @@ +/* + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi> + + This program is free software; you can redistribute it and/or modify + it under the terms of the Perl Artistic License, available in COPYING. + */ + + +typedef struct { + int32 + loop_start, loop_end, data_length, + sample_rate, low_freq, high_freq, root_freq; + uint8 + root_tune, fine_tune; + int32 + envelope_rate[7], envelope_offset[7], + modulation_rate[7], modulation_offset[7]; + FLOAT_T + volume, resonance, + modEnvToFilterFc, modEnvToPitch, modLfoToFilterFc; + sample_t *data; + int32 + tremolo_sweep_increment, tremolo_phase_increment, + lfo_sweep_increment, lfo_phase_increment, + vibrato_sweep_increment, vibrato_control_ratio, + cutoff_freq; + uint8 + reverberation, chorusdepth, + tremolo_depth, vibrato_depth, + modes; + uint8 + attenuation, freq_center; + int8 + panning, note_to_use, exclusiveClass; + int16 + scale_tuning, keyToModEnvHold, keyToModEnvDecay, + keyToVolEnvHold, keyToVolEnvDecay; + int32 + freq_scale, vibrato_delay; +} Sample; + +/* Bits in modes: */ +#define MODES_16BIT (1<<0) +#define MODES_UNSIGNED (1<<1) +#define MODES_LOOPING (1<<2) +#define MODES_PINGPONG (1<<3) +#define MODES_REVERSE (1<<4) +#define MODES_SUSTAIN (1<<5) +#define MODES_ENVELOPE (1<<6) +#define MODES_FAST_RELEASE (1<<7) + +#if 0 +typedef struct { + int samples; + Sample *sample; +} Instrument; +#endif + +#define INST_GUS 0 +#define INST_SF2 1 + +typedef struct { + int type; + int samples; + Sample *sample; + int left_samples; + Sample *left_sample; + int right_samples; + Sample *right_sample; + unsigned char *contents; +} Instrument; + + +typedef struct _InstrumentLayer { + uint8 lo, hi; + int size; + Instrument *instrument; + struct _InstrumentLayer *next; +} InstrumentLayer; + +struct cfg_type { + int font_code; + int num; + const char *name; +}; + +#define FONT_NORMAL 0 +#define FONT_FFF 1 +#define FONT_SBK 2 +#define FONT_TONESET 3 +#define FONT_DRUMSET 4 +#define FONT_PRESET 5 + + +typedef struct { + char *name; + InstrumentLayer *layer; + int font_type, sf_ix, last_used, tuning; + int note, amp, pan, strip_loop, strip_envelope, strip_tail; +} ToneBankElement; + +#if 0 +typedef struct { + char *name; + Instrument *instrument; + int note, amp, pan, strip_loop, strip_envelope, strip_tail; +} ToneBankElement; +#endif +/* A hack to delay instrument loading until after reading the + entire MIDI file. */ +#define MAGIC_LOAD_INSTRUMENT ((InstrumentLayer *)(-1)) + +#define MAXPROG 128 +#define MAXBANK 130 +#define SFXBANK (MAXBANK-1) +#define SFXDRUM1 (MAXBANK-2) +#define SFXDRUM2 (MAXBANK-1) +#define XGDRUM 1 + +#if 0 +typedef struct { + ToneBankElement tone[128]; +} ToneBank; +#endif + +typedef struct { + char *name; + ToneBankElement tone[MAXPROG]; +} ToneBank; + + +extern char *sf_file; + +extern ToneBank *tonebank[], *drumset[]; + +#if 0 +extern Instrument *default_instrument; +#endif +extern InstrumentLayer *default_instrument; +extern int default_program; +extern int antialiasing_allowed; +extern int fast_decay; +extern int free_instruments_afterwards; + +#define SPECIAL_PROGRAM -1 + +extern int load_missing_instruments(void); +extern void free_instruments(void); +extern void end_soundfont(void); +extern int set_default_instrument(const char *name); + + +extern int32 convert_tremolo_sweep(uint8 sweep); +extern int32 convert_vibrato_sweep(uint8 sweep, int32 vib_control_ratio); +extern int32 convert_tremolo_rate(uint8 rate); +extern int32 convert_vibrato_rate(uint8 rate); + +extern int init_soundfont(char *fname, int oldbank, int newbank, int level); +extern InstrumentLayer *load_sbk_patch(const char *name, int gm_num, int bank, int percussion, + int panning, int amp, int note_to_use, int sf_ix); +extern int current_tune_number; +extern int max_patch_memory; +extern int current_patch_memory; +#define XMAPMAX 800 +extern int xmap[XMAPMAX][5]; +extern void pcmap(int *b, int *v, int *p, int *drums); + diff --git a/apps/plugins/sdl/SDL_mixer/timidity/mix.c b/apps/plugins/sdl/SDL_mixer/timidity/mix.c new file mode 100644 index 0000000000..79d4bfd757 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/timidity/mix.c @@ -0,0 +1,847 @@ +/* + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi> + + This program is free software; you can redistribute it and/or modify + it under the terms of the Perl Artistic License, available in COPYING. + */ + +#include "config.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" +#include "output.h" +#include "ctrlmode.h" +#include "tables.h" +#include "resample.h" +#include "mix.h" + +/* Returns 1 if envelope runs out */ +int recompute_envelope(int v) +{ + int stage; + + stage = voice[v].envelope_stage; + + if (stage>5) + { + /* Envelope ran out. */ + int tmp=(voice[v].status == VOICE_DIE); /* Already displayed as dead */ + voice[v].status = VOICE_FREE; + if(!tmp) + ctl->note(v); + return 1; + } + + if (voice[v].sample->modes & MODES_ENVELOPE) + { + if (voice[v].status==VOICE_ON || voice[v].status==VOICE_SUSTAINED) + { + if (stage>2) + { + /* Freeze envelope until note turns off. Trumpets want this. */ + voice[v].envelope_increment=0; + return 0; + } + } + } + voice[v].envelope_stage=stage+1; + + if (voice[v].envelope_volume==voice[v].sample->envelope_offset[stage]) + return recompute_envelope(v); + voice[v].envelope_target=voice[v].sample->envelope_offset[stage]; + voice[v].envelope_increment = voice[v].sample->envelope_rate[stage]; + if (voice[v].envelope_target<voice[v].envelope_volume) + voice[v].envelope_increment = -voice[v].envelope_increment; + return 0; +} + +void apply_envelope_to_amp(int v) +{ + FLOAT_T lamp=voice[v].left_amp, ramp, lramp, rramp, ceamp, lfeamp; + int32 la,ra, lra, rra, cea, lfea; + if (voice[v].panned == PANNED_MYSTERY) + { + lramp=voice[v].lr_amp; + ramp=voice[v].right_amp; + ceamp=voice[v].ce_amp; + rramp=voice[v].rr_amp; + lfeamp=voice[v].lfe_amp; + + if (voice[v].tremolo_phase_increment) + { + FLOAT_T tv = voice[v].tremolo_volume; + lramp *= tv; + lamp *= tv; + ceamp *= tv; + ramp *= tv; + rramp *= tv; + lfeamp *= tv; + } + if (voice[v].sample->modes & MODES_ENVELOPE) + { + FLOAT_T ev = (FLOAT_T)vol_table[voice[v].envelope_volume>>23]; + lramp *= ev; + lamp *= ev; + ceamp *= ev; + ramp *= ev; + rramp *= ev; + lfeamp *= ev; + } + + la = (int32)FSCALE(lamp,AMP_BITS); + ra = (int32)FSCALE(ramp,AMP_BITS); + lra = (int32)FSCALE(lramp,AMP_BITS); + rra = (int32)FSCALE(rramp,AMP_BITS); + cea = (int32)FSCALE(ceamp,AMP_BITS); + lfea = (int32)FSCALE(lfeamp,AMP_BITS); + + if (la>MAX_AMP_VALUE) la=MAX_AMP_VALUE; + if (ra>MAX_AMP_VALUE) ra=MAX_AMP_VALUE; + if (lra>MAX_AMP_VALUE) lra=MAX_AMP_VALUE; + if (rra>MAX_AMP_VALUE) rra=MAX_AMP_VALUE; + if (cea>MAX_AMP_VALUE) cea=MAX_AMP_VALUE; + if (lfea>MAX_AMP_VALUE) lfea=MAX_AMP_VALUE; + + voice[v].lr_mix=FINAL_VOLUME(lra); + voice[v].left_mix=FINAL_VOLUME(la); + voice[v].ce_mix=FINAL_VOLUME(cea); + voice[v].right_mix=FINAL_VOLUME(ra); + voice[v].rr_mix=FINAL_VOLUME(rra); + voice[v].lfe_mix=FINAL_VOLUME(lfea); + } + else + { + if (voice[v].tremolo_phase_increment) + lamp *= voice[v].tremolo_volume; + if (voice[v].sample->modes & MODES_ENVELOPE) + lamp *= (FLOAT_T)vol_table[voice[v].envelope_volume>>23]; + + la = (int32)FSCALE(lamp,AMP_BITS); + + if (la>MAX_AMP_VALUE) + la=MAX_AMP_VALUE; + + voice[v].left_mix=FINAL_VOLUME(la); + } +} + +static int update_envelope(int v) +{ + voice[v].envelope_volume += voice[v].envelope_increment; + /* Why is there no ^^ operator?? */ + if (((voice[v].envelope_increment < 0) && + (voice[v].envelope_volume <= voice[v].envelope_target)) || + ((voice[v].envelope_increment > 0) && + (voice[v].envelope_volume >= voice[v].envelope_target))) + { + voice[v].envelope_volume = voice[v].envelope_target; + if (recompute_envelope(v)) + return 1; + } + return 0; +} + +static void update_tremolo(int v) +{ + int32 depth=voice[v].sample->tremolo_depth<<7; + + if (voice[v].tremolo_sweep) + { + /* Update sweep position */ + + voice[v].tremolo_sweep_position += voice[v].tremolo_sweep; + if (voice[v].tremolo_sweep_position>=(1<<SWEEP_SHIFT)) + voice[v].tremolo_sweep=0; /* Swept to max amplitude */ + else + { + /* Need to adjust depth */ + depth *= voice[v].tremolo_sweep_position; + depth >>= SWEEP_SHIFT; + } + } + + voice[v].tremolo_phase += voice[v].tremolo_phase_increment; + + /* if (voice[v].tremolo_phase >= (SINE_CYCLE_LENGTH<<RATE_SHIFT)) + voice[v].tremolo_phase -= SINE_CYCLE_LENGTH<<RATE_SHIFT; */ + + voice[v].tremolo_volume = (FLOAT_T) + (1.0 - FSCALENEG((sine(voice[v].tremolo_phase >> RATE_SHIFT) + 1.0) + * depth * TREMOLO_AMPLITUDE_TUNING, + 17)); + + /* I'm not sure about the +1.0 there -- it makes tremoloed voices' + volumes on average the lower the higher the tremolo amplitude. */ +} + +/* Returns 1 if the note died */ +static int update_signal(int v) +{ + if (voice[v].envelope_increment && update_envelope(v)) + return 1; + + if (voice[v].tremolo_phase_increment) + update_tremolo(v); + + apply_envelope_to_amp(v); + return 0; +} + +#ifdef LOOKUP_HACK +# define MIXATION(a) *lp++ += mixup[(a<<8) | (uint8)s]; +#else +# define MIXATION(a) *lp++ += (a)*s; +#endif + +#define MIXSKIP lp++ +#define MIXMAX(a,b) *lp++ += ((a>b)?a:b) * s +#define MIXCENT(a,b) *lp++ += (a/2+b/2) * s +#define MIXHALF(a) *lp++ += (a>>1)*s; + +static void mix_mystery_signal(resample_t *sp, int32 *lp, int v, int count) +{ + Voice *vp = voice + v; + final_volume_t + left_rear=vp->lr_mix, + left=vp->left_mix, + center=vp->ce_mix, + right=vp->right_mix, + right_rear=vp->rr_mix, + lfe=vp->lfe_mix; + int cc; + resample_t s; + + if (!(cc = vp->control_counter)) + { + cc = control_ratio; + if (update_signal(v)) + return; /* Envelope ran out */ + + left_rear = vp->lr_mix; + left = vp->left_mix; + center = vp->ce_mix; + right = vp->right_mix; + right_rear = vp->rr_mix; + lfe = vp->lfe_mix; + } + + while (count) + if (cc < count) + { + count -= cc; + while (cc--) + { + s = *sp++; + MIXATION(left); + MIXATION(right); + if (num_ochannels >= 4) { + MIXATION(left_rear); + MIXATION(right_rear); + } + if (num_ochannels == 6) { + MIXATION(center); + MIXATION(lfe); + } + } + cc = control_ratio; + if (update_signal(v)) + return; /* Envelope ran out */ + left_rear = vp->lr_mix; + left = vp->left_mix; + center = vp->ce_mix; + right = vp->right_mix; + right_rear = vp->rr_mix; + lfe = vp->lfe_mix; + } + else + { + vp->control_counter = cc - count; + while (count--) + { + s = *sp++; + MIXATION(left); + MIXATION(right); + if (num_ochannels >= 4) { + MIXATION(left_rear); + MIXATION(right_rear); + } + if (num_ochannels == 6) { + MIXATION(center); + MIXATION(lfe); + } + } + return; + } +} + +static void mix_center_signal(resample_t *sp, int32 *lp, int v, int count) +{ + Voice *vp = voice + v; + final_volume_t + left=vp->left_mix; + int cc; + resample_t s; + + if (!(cc = vp->control_counter)) + { + cc = control_ratio; + if (update_signal(v)) + return; /* Envelope ran out */ + left = vp->left_mix; + } + + while (count) + if (cc < count) + { + count -= cc; + while (cc--) + { + s = *sp++; + if (num_ochannels == 2) { + MIXATION(left); + MIXATION(left); + } + else if (num_ochannels == 4) { + MIXATION(left); + MIXSKIP; + MIXATION(left); + MIXSKIP; + } + else if (num_ochannels == 6) { + MIXSKIP; + MIXSKIP; + MIXSKIP; + MIXSKIP; + MIXATION(left); + MIXATION(left); + } + } + cc = control_ratio; + if (update_signal(v)) + return; /* Envelope ran out */ + left = vp->left_mix; + } + else + { + vp->control_counter = cc - count; + while (count--) + { + s = *sp++; + if (num_ochannels == 2) { + MIXATION(left); + MIXATION(left); + } + else if (num_ochannels == 4) { + MIXATION(left); + MIXSKIP; + MIXATION(left); + MIXSKIP; + } + else if (num_ochannels == 6) { + MIXSKIP; + MIXSKIP; + MIXSKIP; + MIXSKIP; + MIXATION(left); + MIXATION(left); + } + } + return; + } +} + +static void mix_single_left_signal(resample_t *sp, int32 *lp, int v, int count) +{ + Voice *vp = voice + v; + final_volume_t + left=vp->left_mix; + int cc; + resample_t s; + + if (!(cc = vp->control_counter)) + { + cc = control_ratio; + if (update_signal(v)) + return; /* Envelope ran out */ + left = vp->left_mix; + } + + while (count) + if (cc < count) + { + count -= cc; + while (cc--) + { + s = *sp++; + if (num_ochannels == 2) { + MIXATION(left); + MIXSKIP; + } + if (num_ochannels >= 4) { + MIXHALF(left); + MIXSKIP; + MIXATION(left); + MIXSKIP; + } + if (num_ochannels == 6) { + MIXSKIP; + MIXATION(left); + } + } + cc = control_ratio; + if (update_signal(v)) + return; /* Envelope ran out */ + left = vp->left_mix; + } + else + { + vp->control_counter = cc - count; + while (count--) + { + s = *sp++; + if (num_ochannels == 2) { + MIXATION(left); + MIXSKIP; + } + if (num_ochannels >= 4) { + MIXHALF(left); + MIXSKIP; + MIXATION(left); + MIXSKIP; + } + if (num_ochannels == 6) { + MIXSKIP; + MIXATION(left); + } + } + return; + } +} + +static void mix_single_right_signal(resample_t *sp, int32 *lp, int v, int count) +{ + Voice *vp = voice + v; + final_volume_t + left=vp->left_mix; + int cc; + resample_t s; + + if (!(cc = vp->control_counter)) + { + cc = control_ratio; + if (update_signal(v)) + return; /* Envelope ran out */ + left = vp->left_mix; + } + + while (count) + if (cc < count) + { + count -= cc; + while (cc--) + { + s = *sp++; + if (num_ochannels == 2) { + MIXSKIP; + MIXATION(left); + } + if (num_ochannels >= 4) { + MIXSKIP; + MIXHALF(left); + MIXSKIP; + MIXATION(left); + } if (num_ochannels == 6) { + MIXSKIP; + MIXATION(left); + } + } + cc = control_ratio; + if (update_signal(v)) + return; /* Envelope ran out */ + left = vp->left_mix; + } + else + { + vp->control_counter = cc - count; + while (count--) + { + s = *sp++; + if (num_ochannels == 2) { + MIXSKIP; + MIXATION(left); + } + if (num_ochannels >= 4) { + MIXSKIP; + MIXHALF(left); + MIXSKIP; + MIXATION(left); + } if (num_ochannels == 6) { + MIXSKIP; + MIXATION(left); + } + } + return; + } +} + +static void mix_mono_signal(resample_t *sp, int32 *lp, int v, int count) +{ + Voice *vp = voice + v; + final_volume_t + left=vp->left_mix; + int cc; + resample_t s; + + if (!(cc = vp->control_counter)) + { + cc = control_ratio; + if (update_signal(v)) + return; /* Envelope ran out */ + left = vp->left_mix; + } + + while (count) + if (cc < count) + { + count -= cc; + while (cc--) + { + s = *sp++; + MIXATION(left); + } + cc = control_ratio; + if (update_signal(v)) + return; /* Envelope ran out */ + left = vp->left_mix; + } + else + { + vp->control_counter = cc - count; + while (count--) + { + s = *sp++; + MIXATION(left); + } + return; + } +} + +static void mix_mystery(resample_t *sp, int32 *lp, int v, int count) +{ + final_volume_t + left_rear=voice[v].lr_mix, + left=voice[v].left_mix, + center=voice[v].ce_mix, + right=voice[v].right_mix, + right_rear=voice[v].rr_mix, + lfe=voice[v].lfe_mix; + resample_t s; + + while (count--) + { + s = *sp++; + MIXATION(left); + MIXATION(right); + if (num_ochannels >= 4) { + MIXATION(left_rear); + MIXATION(right_rear); + } + if (num_ochannels == 6) { + MIXATION(center); + MIXATION(lfe); + } + } +} + +static void mix_center(resample_t *sp, int32 *lp, int v, int count) +{ + final_volume_t + left=voice[v].left_mix; + resample_t s; + + while (count--) + { + s = *sp++; + if (num_ochannels == 2) { + MIXATION(left); + MIXATION(left); + } + else if (num_ochannels == 4) { + MIXATION(left); + MIXATION(left); + MIXSKIP; + MIXSKIP; + } + else if (num_ochannels == 6) { + MIXSKIP; + MIXSKIP; + MIXSKIP; + MIXSKIP; + MIXATION(left); + MIXATION(left); + } + } +} + +static void mix_single_left(resample_t *sp, int32 *lp, int v, int count) +{ + final_volume_t + left=voice[v].left_mix; + resample_t s; + + while (count--) + { + s = *sp++; + if (num_ochannels == 2) { + MIXATION(left); + MIXSKIP; + } + if (num_ochannels >= 4) { + MIXHALF(left); + MIXSKIP; + MIXATION(left); + MIXSKIP; + } + if (num_ochannels == 6) { + MIXSKIP; + MIXATION(left); + } + } +} +static void mix_single_right(resample_t *sp, int32 *lp, int v, int count) +{ + final_volume_t + left=voice[v].left_mix; + resample_t s; + + while (count--) + { + s = *sp++; + if (num_ochannels == 2) { + MIXSKIP; + MIXATION(left); + } + if (num_ochannels >= 4) { + MIXSKIP; + MIXHALF(left); + MIXSKIP; + MIXATION(left); + } + if (num_ochannels == 6) { + MIXSKIP; + MIXATION(left); + } + } +} + +static void mix_mono(resample_t *sp, int32 *lp, int v, int count) +{ + final_volume_t + left=voice[v].left_mix; + resample_t s; + + while (count--) + { + s = *sp++; + MIXATION(left); + } +} + +/* Ramp a note out in c samples */ +static void ramp_out(resample_t *sp, int32 *lp, int v, int32 c) +{ + + /* should be final_volume_t, but uint8 gives trouble. */ + int32 left_rear, left, center, right, right_rear, lfe, li, ri; + + resample_t s = 0; /* silly warning about uninitialized s */ + + /* Fix by James Caldwell */ + if ( c == 0 ) c = 1; + + left = voice[v].left_mix; + li = -(left/c); + if (!li) li = -1; + + /* printf("Ramping out: left=%d, c=%d, li=%d\n", left, c, li); */ + + if (!(play_mode->encoding & PE_MONO)) + { + if (voice[v].panned==PANNED_MYSTERY) + { + left_rear = voice[v].lr_mix; + center=voice[v].ce_mix; + right=voice[v].right_mix; + right_rear = voice[v].rr_mix; + lfe = voice[v].lfe_mix; + + ri=-(right/c); + while (c--) + { + left_rear += li; if (left_rear<0) left_rear=0; + left += li; if (left<0) left=0; + center += li; if (center<0) center=0; + right += ri; if (right<0) right=0; + right_rear += ri; if (right_rear<0) right_rear=0; + lfe += li; if (lfe<0) lfe=0; + s=*sp++; + MIXATION(left); + MIXATION(right); + if (num_ochannels >= 4) { + MIXATION(left_rear); + MIXATION(right_rear); + } + if (num_ochannels == 6) { + MIXATION(center); + MIXATION(lfe); + } + } + } + else if (voice[v].panned==PANNED_CENTER) + { + while (c--) + { + left += li; + if (left<0) + return; + s=*sp++; + if (num_ochannels == 2) { + MIXATION(left); + MIXATION(left); + } + else if (num_ochannels == 4) { + MIXATION(left); + MIXATION(left); + MIXSKIP; + MIXSKIP; + } + else if (num_ochannels == 6) { + MIXSKIP; + MIXSKIP; + MIXSKIP; + MIXSKIP; + MIXATION(left); + MIXATION(left); + } + } + } + else if (voice[v].panned==PANNED_LEFT) + { + while (c--) + { + left += li; + if (left<0) + return; + s=*sp++; + MIXATION(left); + MIXSKIP; + if (num_ochannels >= 4) { + MIXATION(left); + MIXSKIP; + } if (num_ochannels == 6) { + MIXATION(left); + MIXATION(left); + } + } + } + else if (voice[v].panned==PANNED_RIGHT) + { + while (c--) + { + left += li; + if (left<0) + return; + s=*sp++; + MIXSKIP; + MIXATION(left); + if (num_ochannels >= 4) { + MIXSKIP; + MIXATION(left); + } if (num_ochannels == 6) { + MIXATION(left); + MIXATION(left); + } + } + } + } + else + { + /* Mono output. */ + while (c--) + { + left += li; + if (left<0) + return; + s=*sp++; + MIXATION(left); + } + } +} + + +/**************** interface function ******************/ + +void mix_voice(int32 *buf, int v, int32 c) +{ + Voice *vp=voice+v; + int32 count=c; + resample_t *sp; + if (c<0) return; + if (vp->status==VOICE_DIE) + { + if (count>=MAX_DIE_TIME) + count=MAX_DIE_TIME; + sp=resample_voice(v, &count); + ramp_out(sp, buf, v, count); + vp->status=VOICE_FREE; + } + else + { + sp=resample_voice(v, &count); + if (count<0) return; + if (play_mode->encoding & PE_MONO) + { + /* Mono output. */ + if (vp->envelope_increment || vp->tremolo_phase_increment) + mix_mono_signal(sp, buf, v, count); + else + mix_mono(sp, buf, v, count); + } + else + { + if (vp->panned == PANNED_MYSTERY) + { + if (vp->envelope_increment || vp->tremolo_phase_increment) + mix_mystery_signal(sp, buf, v, count); + else + mix_mystery(sp, buf, v, count); + } + else if (vp->panned == PANNED_CENTER) + { + if (vp->envelope_increment || vp->tremolo_phase_increment) + mix_center_signal(sp, buf, v, count); + else + mix_center(sp, buf, v, count); + } + else + { + /* It's either full left or full right. In either case, + every other sample is 0. Just get the offset right: */ + + if (vp->envelope_increment || vp->tremolo_phase_increment) + { + if (vp->panned == PANNED_RIGHT) + mix_single_right_signal(sp, buf, v, count); + else mix_single_left_signal(sp, buf, v, count); + } + else + { + if (vp->panned == PANNED_RIGHT) + mix_single_right(sp, buf, v, count); + else mix_single_left(sp, buf, v, count); + } + } + } + } +} diff --git a/apps/plugins/sdl/SDL_mixer/timidity/mix.h b/apps/plugins/sdl/SDL_mixer/timidity/mix.h new file mode 100644 index 0000000000..2f582b8a6a --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/timidity/mix.h @@ -0,0 +1,11 @@ +/* + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi> + + This program is free software; you can redistribute it and/or modify + it under the terms of the Perl Artistic License, available in COPYING. + */ + +extern void mix_voice(int32 *buf, int v, int32 c); +extern int recompute_envelope(int v); +extern void apply_envelope_to_amp(int v); diff --git a/apps/plugins/sdl/SDL_mixer/timidity/output.c b/apps/plugins/sdl/SDL_mixer/timidity/output.c new file mode 100644 index 0000000000..5dab01f2eb --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/timidity/output.c @@ -0,0 +1,122 @@ +/* + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi> + + This program is free software; you can redistribute it and/or modify + it under the terms of the Perl Artistic License, available in COPYING. + */ + +#include "config.h" +#include "output.h" +#include "tables.h" + + +#ifdef SDL +extern PlayMode sdl_play_mode; +#define DEFAULT_PLAY_MODE &sdl_play_mode +#endif + +PlayMode *play_mode_list[] = { +#ifdef DEFAULT_PLAY_MODE + DEFAULT_PLAY_MODE, +#endif + 0 +}; + +#ifdef DEFAULT_PLAY_MODE + PlayMode *play_mode=DEFAULT_PLAY_MODE; +#endif + +/*****************************************************************/ +/* Some functions to convert signed 32-bit data to other formats */ + +void s32tos8(void *dp, int32 *lp, int32 c) +{ + int8 *cp=(int8 *)(dp); + int32 l; + while (c--) + { + l=(*lp++)>>(32-8-GUARD_BITS); + if (l>127) l=127; + else if (l<-128) l=-128; + *cp++ = (int8) (l); + } +} + +void s32tou8(void *dp, int32 *lp, int32 c) +{ + uint8 *cp=(uint8 *)(dp); + int32 l; + while (c--) + { + l=(*lp++)>>(32-8-GUARD_BITS); + if (l>127) l=127; + else if (l<-128) l=-128; + *cp++ = 0x80 ^ ((uint8) l); + } +} + +void s32tos16(void *dp, int32 *lp, int32 c) +{ + int16 *sp=(int16 *)(dp); + int32 l; + while (c--) + { + l=(*lp++)>>(32-16-GUARD_BITS); + if (l > 32767) l=32767; + else if (l<-32768) l=-32768; + *sp++ = (int16)(l); + } +} + +void s32tou16(void *dp, int32 *lp, int32 c) +{ + uint16 *sp=(uint16 *)(dp); + int32 l; + while (c--) + { + l=(*lp++)>>(32-16-GUARD_BITS); + if (l > 32767) l=32767; + else if (l<-32768) l=-32768; + *sp++ = 0x8000 ^ (uint16)(l); + } +} + +void s32tos16x(void *dp, int32 *lp, int32 c) +{ + int16 *sp=(int16 *)(dp); + int32 l; + while (c--) + { + l=(*lp++)>>(32-16-GUARD_BITS); + if (l > 32767) l=32767; + else if (l<-32768) l=-32768; + *sp++ = XCHG_SHORT((int16)(l)); + } +} + +void s32tou16x(void *dp, int32 *lp, int32 c) +{ + uint16 *sp=(uint16 *)(dp); + int32 l; + while (c--) + { + l=(*lp++)>>(32-16-GUARD_BITS); + if (l > 32767) l=32767; + else if (l<-32768) l=-32768; + *sp++ = XCHG_SHORT(0x8000 ^ (uint16)(l)); + } +} + +void s32toulaw(void *dp, int32 *lp, int32 c) +{ + uint8 *up=(uint8 *)(dp); + int32 l; + while (c--) + { + l=(*lp++)>>(32-13-GUARD_BITS); + if (l > 4095) l=4095; + else if (l<-4096) l=-4096; + *up++ = _l2u[l]; + } +} diff --git a/apps/plugins/sdl/SDL_mixer/timidity/output.h b/apps/plugins/sdl/SDL_mixer/timidity/output.h new file mode 100644 index 0000000000..598792dd20 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/timidity/output.h @@ -0,0 +1,60 @@ +/* + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi> + + This program is free software; you can redistribute it and/or modify + it under the terms of the Perl Artistic License, available in COPYING. + */ + +/* Data format encoding bits */ + +#define PE_MONO 0x01 /* versus stereo */ +#define PE_SIGNED 0x02 /* versus unsigned */ +#define PE_16BIT 0x04 /* versus 8-bit */ +#define PE_ULAW 0x08 /* versus linear */ +#define PE_BYTESWAP 0x10 /* versus the other way */ + +typedef struct { + int32 rate, encoding; + char *id_name; +} PlayMode; + +extern PlayMode *play_mode_list[], *play_mode; +extern int init_buffers(int kbytes); + +/* Conversion functions -- These overwrite the int32 data in *lp with + data in another format */ + +/* The size of the output buffers */ +extern int AUDIO_BUFFER_SIZE; + +/* Actual copy function */ +extern void (*s32tobuf)(void *dp, int32 *lp, int32 c); + +/* 8-bit signed and unsigned*/ +extern void s32tos8(void *dp, int32 *lp, int32 c); +extern void s32tou8(void *dp, int32 *lp, int32 c); + +/* 16-bit */ +extern void s32tos16(void *dp, int32 *lp, int32 c); +extern void s32tou16(void *dp, int32 *lp, int32 c); + +/* byte-exchanged 16-bit */ +extern void s32tos16x(void *dp, int32 *lp, int32 c); +extern void s32tou16x(void *dp, int32 *lp, int32 c); + +/* uLaw (8 bits) */ +extern void s32toulaw(void *dp, int32 *lp, int32 c); + +/* little-endian and big-endian specific */ +#if SDL_BYTEORDER == SDL_LIL_ENDIAN +#define s32tou16l s32tou16 +#define s32tou16b s32tou16x +#define s32tos16l s32tos16 +#define s32tos16b s32tos16x +#else +#define s32tou16l s32tou16x +#define s32tou16b s32tou16 +#define s32tos16l s32tos16x +#define s32tos16b s32tos16 +#endif diff --git a/apps/plugins/sdl/SDL_mixer/timidity/playmidi.c b/apps/plugins/sdl/SDL_mixer/timidity/playmidi.c new file mode 100644 index 0000000000..84b18cf5cb --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/timidity/playmidi.c @@ -0,0 +1,1746 @@ +/* + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi> + + This program is free software; you can redistribute it and/or modify + it under the terms of the Perl Artistic License, available in COPYING. + */ + +#include <SDL_rwops.h> + +#include "config.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" +#include "readmidi.h" +#include "output.h" +#include "mix.h" +#include "ctrlmode.h" +#include "timidity.h" + +#include "tables.h" + + +static int opt_expression_curve = 2; +static int opt_volume_curve = 2; +static int opt_stereo_surround = 0; + + +Channel channel[MAXCHAN]; +Voice voice[MAX_VOICES]; +signed char drumvolume[MAXCHAN][MAXNOTE]; +signed char drumpanpot[MAXCHAN][MAXNOTE]; +signed char drumreverberation[MAXCHAN][MAXNOTE]; +signed char drumchorusdepth[MAXCHAN][MAXNOTE]; + +int + voices=DEFAULT_VOICES; + +int32 + control_ratio=0, + amplification=DEFAULT_AMPLIFICATION; + +FLOAT_T + master_volume; + +int32 drumchannels=DEFAULT_DRUMCHANNELS; +int adjust_panning_immediately=0; + +struct _MidiSong { + int32 samples; + MidiEvent *events; +}; +static int midi_playing = 0; +static int32 lost_notes, cut_notes; +static int32 *buffer_pointer; +static int32 buffered_count; +extern int32 *common_buffer; +extern resample_t *resample_buffer; /* to free it on Timidity_Close */ + +static MidiEvent *event_list, *current_event; +static int32 sample_count, current_sample; + +int GM_System_On=0; +int XG_System_On=0; +int GS_System_On=0; +int XG_System_reverb_type; +int XG_System_chorus_type; +int XG_System_variation_type; + + +static void adjust_amplification(void) +{ + master_volume = (FLOAT_T)(amplification) / (FLOAT_T)100.0; + master_volume /= 2; +} + + +static void adjust_master_volume(int32 vol) +{ + master_volume = (double)(vol*amplification) / 1638400.0L; + master_volume /= 2; +} + + +static void reset_voices(void) +{ + int i; + for (i=0; i<MAX_VOICES; i++) + voice[i].status=VOICE_FREE; +} + +/* Process the Reset All Controllers event */ +static void reset_controllers(int c) +{ + channel[c].volume=90; /* Some standard says, although the SCC docs say 0. */ + channel[c].expression=127; /* SCC-1 does this. */ + channel[c].sustain=0; + channel[c].pitchbend=0x2000; + channel[c].pitchfactor=0; /* to be computed */ + + channel[c].reverberation = 0; + channel[c].chorusdepth = 0; +} + +static void redraw_controllers(int c) +{ + ctl->volume(c, channel[c].volume); + ctl->expression(c, channel[c].expression); + ctl->sustain(c, channel[c].sustain); + ctl->pitch_bend(c, channel[c].pitchbend); +} + +static void reset_midi(void) +{ + int i; + for (i=0; i<MAXCHAN; i++) + { + reset_controllers(i); + /* The rest of these are unaffected by the Reset All Controllers event */ + channel[i].program=default_program; + channel[i].panning=NO_PANNING; + channel[i].pitchsens=2; + channel[i].bank=0; /* tone bank or drum set */ + channel[i].harmoniccontent=64, + channel[i].releasetime=64, + channel[i].attacktime=64, + channel[i].brightness=64, + channel[i].sfx=0; + } + reset_voices(); +} + +static void select_sample(int v, Instrument *ip) +{ + int32 f, cdiff, diff, midfreq; + int s,i; + Sample *sp, *closest; + + s=ip->samples; + sp=ip->sample; + + if (s==1) + { + voice[v].sample=sp; + return; + } + + f=voice[v].orig_frequency; + /* + No suitable sample found! We'll select the sample whose root + frequency is closest to the one we want. (Actually we should + probably convert the low, high, and root frequencies to MIDI note + values and compare those.) */ + + cdiff=0x7FFFFFFF; + closest=sp=ip->sample; + midfreq = (sp->low_freq + sp->high_freq) / 2; + for(i=0; i<s; i++) + { + diff=sp->root_freq - f; + /* But the root freq. can perfectly well lie outside the keyrange + * frequencies, so let's try: + */ + /* diff=midfreq - f; */ + if (diff<0) diff=-diff; + if (diff<cdiff) + { + cdiff=diff; + closest=sp; + } + sp++; + } + voice[v].sample=closest; + return; +} + + + +static void select_stereo_samples(int v, InstrumentLayer *lp) +{ + Instrument *ip; + InstrumentLayer *nlp, *bestvel; + int diffvel, midvel, mindiff; + +/* select closest velocity */ + bestvel = lp; + mindiff = 500; + for (nlp = lp; nlp; nlp = nlp->next) { + midvel = (nlp->hi + nlp->lo)/2; + if (!midvel) diffvel = 127; + else if (voice[v].velocity < nlp->lo || voice[v].velocity > nlp->hi) + diffvel = 200; + else diffvel = voice[v].velocity - midvel; + if (diffvel < 0) diffvel = -diffvel; + if (diffvel < mindiff) { + mindiff = diffvel; + bestvel = nlp; + } + } + ip = bestvel->instrument; + + if (ip->right_sample) { + ip->sample = ip->right_sample; + ip->samples = ip->right_samples; + select_sample(v, ip); + voice[v].right_sample = voice[v].sample; + } + else voice[v].right_sample = 0; + ip->sample = ip->left_sample; + ip->samples = ip->left_samples; + select_sample(v, ip); +} + + +static void recompute_freq(int v) +{ + int + sign=(voice[v].sample_increment < 0), /* for bidirectional loops */ + pb=channel[voice[v].channel].pitchbend; + double a; + + if (!voice[v].sample->sample_rate) + return; + + if (voice[v].vibrato_control_ratio) + { + /* This instrument has vibrato. Invalidate any precomputed + sample_increments. */ + + int i=VIBRATO_SAMPLE_INCREMENTS; + while (i--) + voice[v].vibrato_sample_increment[i]=0; + } + + if (pb==0x2000 || pb<0 || pb>0x3FFF) + voice[v].frequency=voice[v].orig_frequency; + else + { + pb-=0x2000; + if (!(channel[voice[v].channel].pitchfactor)) + { + /* Damn. Somebody bent the pitch. */ + int32 i=pb*channel[voice[v].channel].pitchsens; + if (pb<0) + i=-i; + channel[voice[v].channel].pitchfactor= + (FLOAT_T)(bend_fine[(i>>5) & 0xFF] * bend_coarse[i>>13]); + } + if (pb>0) + voice[v].frequency= + (int32)(channel[voice[v].channel].pitchfactor * + (double)(voice[v].orig_frequency)); + else + voice[v].frequency= + (int32)((double)(voice[v].orig_frequency) / + channel[voice[v].channel].pitchfactor); + } + + a = FSCALE(((double)(voice[v].sample->sample_rate) * + (double)(voice[v].frequency)) / + ((double)(voice[v].sample->root_freq) * + (double)(play_mode->rate)), + FRACTION_BITS); + + if (sign) + a = -a; /* need to preserve the loop direction */ + + voice[v].sample_increment = (int32)(a); +} + +static int expr_curve[128] = { + 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 11, + 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 14, 14, 14, 14, 15, 15, + 15, 16, 16, 17, 17, 17, 18, 18, 19, 19, 19, 20, 20, 21, 21, 22, + 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, + 32, 32, 33, 34, 35, 35, 36, 37, 38, 39, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 53, 54, 55, 56, 57, 59, 60, 61, 63, + 64, 65, 67, 68, 70, 71, 73, 75, 76, 78, 80, 82, 83, 85, 87, 89, + 91, 93, 95, 97, 99, 102, 104, 106, 109, 111, 113, 116, 118, 121, + 124, 127 +}; + +static int panf(int pan, int speaker, int separation) +{ + int val; + val = abs(pan - speaker); + val = (val * 127) / separation; + val = 127 - val; + if (val < 0) val = 0; + if (val > 127) val = 127; + return expr_curve[val]; +} + + +static int vcurve[128] = { +0,0,18,29,36,42,47,51,55,58, +60,63,65,67,69,71,73,74,76,77, +79,80,81,82,83,84,85,86,87,88, +89,90,91,92,92,93,94,95,95,96, +97,97,98,99,99,100,100,101,101,102, +103,103,104,104,105,105,106,106,106,107, +107,108,108,109,109,109,110,110,111,111, +111,112,112,112,113,113,114,114,114,115, +115,115,116,116,116,116,117,117,117,118, +118,118,119,119,119,119,120,120,120,120, +121,121,121,122,122,122,122,123,123,123, +123,123,124,124,124,124,125,125,125,125, +126,126,126,126,126,127,127,127 +}; + +static void recompute_amp(int v) +{ + int32 tempamp; + int chan = voice[v].channel; + int panning = voice[v].panning; + int vol = channel[chan].volume; + int expr = channel[chan].expression; + int vel = vcurve[voice[v].velocity]; + FLOAT_T curved_expression, curved_volume; + + if (channel[chan].kit) + { + int note = voice[v].sample->note_to_use; + if (note>0 && drumvolume[chan][note]>=0) vol = drumvolume[chan][note]; + if (note>0 && drumpanpot[chan][note]>=0) panning = drumvolume[chan][note]; + } + + if (opt_expression_curve == 2) curved_expression = 127.0 * vol_table[expr]; + else if (opt_expression_curve == 1) curved_expression = 127.0 * expr_table[expr]; + else curved_expression = (FLOAT_T)expr; + + if (opt_volume_curve == 2) curved_volume = 127.0 * vol_table[vol]; + else if (opt_volume_curve == 1) curved_volume = 127.0 * expr_table[vol]; + else curved_volume = (FLOAT_T)vol; + + tempamp= (int32)((FLOAT_T)vel * curved_volume * curved_expression); /* 21 bits */ + + /* TODO: use fscale */ + + if (num_ochannels > 1) + { + if (panning > 60 && panning < 68) + { + voice[v].panned=PANNED_CENTER; + + if (num_ochannels == 6) voice[v].left_amp = + FSCALENEG((double) (tempamp) * voice[v].sample->volume * + master_volume, 20); + else voice[v].left_amp= + FSCALENEG((double)(tempamp) * voice[v].sample->volume * + master_volume, 21); + } + else if (panning<5) + { + voice[v].panned = PANNED_LEFT; + + voice[v].left_amp= + FSCALENEG((double)(tempamp) * voice[v].sample->volume * master_volume, + 20); + } + else if (panning>123) + { + voice[v].panned = PANNED_RIGHT; + + voice[v].left_amp= /* left_amp will be used */ + FSCALENEG((double)(tempamp) * voice[v].sample->volume * master_volume, + 20); + } + else + { + FLOAT_T refv = (double)(tempamp) * voice[v].sample->volume * master_volume; + int wide_panning = 64; + + if (num_ochannels == 4) wide_panning = 95; + + voice[v].panned = PANNED_MYSTERY; + voice[v].lfe_amp = FSCALENEG(refv * 64, 27); + + switch (num_ochannels) + { + case 2: + voice[v].lr_amp = 0; + voice[v].left_amp = FSCALENEG(refv * (128-panning), 27); + voice[v].ce_amp = 0; + voice[v].right_amp = FSCALENEG(refv * panning, 27); + voice[v].rr_amp = 0; + break; + case 4: + voice[v].lr_amp = FSCALENEG(refv * panf(panning, 0, wide_panning), 27); + voice[v].left_amp = FSCALENEG(refv * panf(panning, 32, wide_panning), 27); + voice[v].ce_amp = 0; + voice[v].right_amp = FSCALENEG(refv * panf(panning, 95, wide_panning), 27); + voice[v].rr_amp = FSCALENEG(refv * panf(panning, 128, wide_panning), 27); + break; + case 6: + voice[v].lr_amp = FSCALENEG(refv * panf(panning, 0, wide_panning), 27); + voice[v].left_amp = FSCALENEG(refv * panf(panning, 32, wide_panning), 27); + voice[v].ce_amp = FSCALENEG(refv * panf(panning, 64, wide_panning), 27); + voice[v].right_amp = FSCALENEG(refv * panf(panning, 95, wide_panning), 27); + voice[v].rr_amp = FSCALENEG(refv * panf(panning, 128, wide_panning), 27); + break; + } + + } + } + else + { + voice[v].panned=PANNED_CENTER; + + voice[v].left_amp= + FSCALENEG((double)(tempamp) * voice[v].sample->volume * master_volume, + 21); + } +} + + +#define NOT_CLONE 0 +#define STEREO_CLONE 1 +#define REVERB_CLONE 2 +#define CHORUS_CLONE 3 + + +/* just a variant of note_on() */ +static int vc_alloc(int j) +{ + int i=voices; + + while (i--) + { + if (i == j) continue; + if (voice[i].status & VOICE_FREE) { + return i; + } + } + return -1; +} + +static void kill_note(int i); + +static void kill_others(int i) +{ + int j=voices; + + if (!voice[i].sample->exclusiveClass) return; + + while (j--) + { + if (voice[j].status & (VOICE_FREE|VOICE_OFF|VOICE_DIE)) continue; + if (i == j) continue; + if (voice[i].channel != voice[j].channel) continue; + if (voice[j].sample->note_to_use) + { + if (voice[j].sample->exclusiveClass != voice[i].sample->exclusiveClass) continue; + kill_note(j); + } + } +} + + +static void clone_voice(Instrument *ip, int v, MidiEvent *e, int clone_type, int variationbank) +{ + int w, played_note, chorus=0, reverb=0, milli; + int chan = voice[v].channel; + + if (clone_type == STEREO_CLONE) { + if (!voice[v].right_sample && variationbank != 3) return; + if (variationbank == 6) return; + } + + if (channel[chan].kit) { + reverb = drumreverberation[chan][voice[v].note]; + chorus = drumchorusdepth[chan][voice[v].note]; + } + else { + reverb = channel[chan].reverberation; + chorus = channel[chan].chorusdepth; + } + + if (clone_type == REVERB_CLONE) chorus = 0; + else if (clone_type == CHORUS_CLONE) reverb = 0; + else if (clone_type == STEREO_CLONE) reverb = chorus = 0; + + if (reverb > 127) reverb = 127; + if (chorus > 127) chorus = 127; + + if (clone_type == CHORUS_CLONE) { + if (variationbank == 32) chorus = 30; + else if (variationbank == 33) chorus = 60; + else if (variationbank == 34) chorus = 90; + } + + chorus /= 2; /* This is an ad hoc adjustment. */ + + if (!reverb && !chorus && clone_type != STEREO_CLONE) return; + + if ( (w = vc_alloc(v)) < 0 ) return; + + voice[w] = voice[v]; + if (clone_type==STEREO_CLONE) voice[v].clone_voice = w; + voice[w].clone_voice = v; + voice[w].clone_type = clone_type; + + voice[w].sample = voice[v].right_sample; + voice[w].velocity= e->b; + + milli = play_mode->rate/1000; + + if (clone_type == STEREO_CLONE) { + int left, right, leftpan, rightpan; + int panrequest = voice[v].panning; + if (variationbank == 3) { + voice[v].panning = 0; + voice[w].panning = 127; + } + else { + if (voice[v].sample->panning > voice[w].sample->panning) { + left = w; + right = v; + } + else { + left = v; + right = w; + } +#define INSTRUMENT_SEPARATION 12 + leftpan = panrequest - INSTRUMENT_SEPARATION / 2; + rightpan = leftpan + INSTRUMENT_SEPARATION; + if (leftpan < 0) { + leftpan = 0; + rightpan = leftpan + INSTRUMENT_SEPARATION; + } + if (rightpan > 127) { + rightpan = 127; + leftpan = rightpan - INSTRUMENT_SEPARATION; + } + voice[left].panning = leftpan; + voice[right].panning = rightpan; + voice[right].echo_delay = 20 * milli; + } + } + + voice[w].volume = voice[w].sample->volume; + + if (reverb) { + if (opt_stereo_surround) { + if (voice[w].panning > 64) voice[w].panning = 127; + else voice[w].panning = 0; + } + else { + if (voice[v].panning < 64) voice[w].panning = 64 + reverb/2; + else voice[w].panning = 64 - reverb/2; + } + +/* try 98->99 for melodic instruments ? (bit much for percussion) */ + voice[w].volume *= vol_table[(127-reverb)/8 + 98]; + + voice[w].echo_delay += reverb * milli; + voice[w].envelope_rate[DECAY] *= 2; + voice[w].envelope_rate[RELEASE] /= 2; + + if (XG_System_reverb_type >= 0) { + int subtype = XG_System_reverb_type & 0x07; + int rtype = XG_System_reverb_type >>3; + switch (rtype) { + case 0: /* no effect */ + break; + case 1: /* hall */ + if (subtype) voice[w].echo_delay += 100 * milli; + break; + case 2: /* room */ + voice[w].echo_delay /= 2; + break; + case 3: /* stage */ + voice[w].velocity = voice[v].velocity; + break; + case 4: /* plate */ + voice[w].panning = voice[v].panning; + break; + case 16: /* white room */ + voice[w].echo_delay = 0; + break; + case 17: /* tunnel */ + voice[w].echo_delay *= 2; + voice[w].velocity /= 2; + break; + case 18: /* canyon */ + voice[w].echo_delay *= 2; + break; + case 19: /* basement */ + voice[w].velocity /= 2; + break; + default: break; + } + } + } + played_note = voice[w].sample->note_to_use; + if (!played_note) { + played_note = e->a & 0x7f; + if (variationbank == 35) played_note += 12; + else if (variationbank == 36) played_note -= 12; + else if (variationbank == 37) played_note += 7; + else if (variationbank == 36) played_note -= 7; + } +#if 0 + played_note = ( (played_note - voice[w].sample->freq_center) * voice[w].sample->freq_scale ) / 1024 + + voice[w].sample->freq_center; +#endif + voice[w].note = played_note; + voice[w].orig_frequency = freq_table[played_note]; + + if (chorus) { + if (opt_stereo_surround) { + if (voice[v].panning < 64) voice[w].panning = voice[v].panning + 32; + else voice[w].panning = voice[v].panning - 32; + } + + if (!voice[w].vibrato_control_ratio) { + voice[w].vibrato_control_ratio = 100; + voice[w].vibrato_depth = 6; + voice[w].vibrato_sweep = 74; + } + voice[w].volume *= 0.40; + voice[v].volume = voice[w].volume; + recompute_amp(v); + apply_envelope_to_amp(v); + voice[w].vibrato_sweep = chorus/2; + voice[w].vibrato_depth /= 2; + if (!voice[w].vibrato_depth) voice[w].vibrato_depth = 2; + voice[w].vibrato_control_ratio /= 2; + voice[w].echo_delay += 30 * milli; + + if (XG_System_chorus_type >= 0) { + int subtype = XG_System_chorus_type & 0x07; + int chtype = 0x0f & (XG_System_chorus_type >> 3); + switch (chtype) { + case 0: /* no effect */ + break; + case 1: /* chorus */ + chorus /= 3; + if(channel[ voice[w].channel ].pitchbend + chorus < 0x2000) + voice[w].orig_frequency = + (uint32)( (FLOAT_T)voice[w].orig_frequency * bend_fine[chorus] ); + else voice[w].orig_frequency = + (uint32)( (FLOAT_T)voice[w].orig_frequency / bend_fine[chorus] ); + if (subtype) voice[w].vibrato_depth *= 2; + break; + case 2: /* celeste */ + voice[w].orig_frequency += (voice[w].orig_frequency/128) * chorus; + break; + case 3: /* flanger */ + voice[w].vibrato_control_ratio = 10; + voice[w].vibrato_depth = 100; + voice[w].vibrato_sweep = 8; + voice[w].echo_delay += 200 * milli; + break; + case 4: /* symphonic : cf Children of the Night /128 bad, /1024 ok */ + voice[w].orig_frequency += (voice[w].orig_frequency/512) * chorus; + voice[v].orig_frequency -= (voice[v].orig_frequency/512) * chorus; + recompute_freq(v); + break; + case 8: /* phaser */ + break; + default: + break; + } + } + else { + chorus /= 3; + if(channel[ voice[w].channel ].pitchbend + chorus < 0x2000) + voice[w].orig_frequency = + (uint32)( (FLOAT_T)voice[w].orig_frequency * bend_fine[chorus] ); + else voice[w].orig_frequency = + (uint32)( (FLOAT_T)voice[w].orig_frequency / bend_fine[chorus] ); + } + } +#if 0 + voice[w].loop_start = voice[w].sample->loop_start; + voice[w].loop_end = voice[w].sample->loop_end; +#endif + voice[w].echo_delay_count = voice[w].echo_delay; + if (reverb) voice[w].echo_delay *= 2; + + recompute_freq(w); + recompute_amp(w); + if (voice[w].sample->modes & MODES_ENVELOPE) + { + /* Ramp up from 0 */ + voice[w].envelope_stage=ATTACK; + voice[w].modulation_stage=ATTACK; + voice[w].envelope_volume=0; + voice[w].modulation_volume=0; + voice[w].control_counter=0; + voice[w].modulation_counter=0; + recompute_envelope(w); + /*recompute_modulation(w);*/ + } + else + { + voice[w].envelope_increment=0; + voice[w].modulation_increment=0; + } + apply_envelope_to_amp(w); +} + + +static void xremap(int *banknumpt, int *this_notept, int this_kit) { + int i, newmap; + int banknum = *banknumpt; + int this_note = *this_notept; + int newbank, newnote; + + if (!this_kit) { + if (banknum == SFXBANK && tonebank[SFXBANK]) return; + if (banknum == SFXBANK && tonebank[120]) *banknumpt = 120; + return; + } + + if (this_kit != 127 && this_kit != 126) return; + + for (i = 0; i < XMAPMAX; i++) { + newmap = xmap[i][0]; + if (!newmap) return; + if (this_kit == 127 && newmap != XGDRUM) continue; + if (this_kit == 126 && newmap != SFXDRUM1) continue; + if (xmap[i][1] != banknum) continue; + if (xmap[i][3] != this_note) continue; + newbank = xmap[i][2]; + newnote = xmap[i][4]; + if (newbank == banknum && newnote == this_note) return; + if (!drumset[newbank]) return; + if (!drumset[newbank]->tone[newnote].layer) return; + if (drumset[newbank]->tone[newnote].layer == MAGIC_LOAD_INSTRUMENT) return; + *banknumpt = newbank; + *this_notept = newnote; + return; + } +} + + +static void start_note(MidiEvent *e, int i) +{ + InstrumentLayer *lp; + Instrument *ip; + int j, banknum, ch=e->channel; + int played_note, drumpan=NO_PANNING; + int32 rt; + int attacktime, releasetime, decaytime, variationbank; + int brightness = channel[ch].brightness; + int harmoniccontent = channel[ch].harmoniccontent; + int this_note = e->a; + int this_velocity = e->b; + int drumsflag = channel[ch].kit; + int this_prog = channel[ch].program; + + if (channel[ch].sfx) banknum=channel[ch].sfx; + else banknum=channel[ch].bank; + + voice[i].velocity=this_velocity; + + if (XG_System_On) xremap(&banknum, &this_note, drumsflag); + /* if (current_config_pc42b) pcmap(&banknum, &this_note, &this_prog, &drumsflag); */ + + if (drumsflag) + { + if (!(lp=drumset[banknum]->tone[this_note].layer)) + { + if (!(lp=drumset[0]->tone[this_note].layer)) + return; /* No instrument? Then we can't play. */ + } + ip = lp->instrument; + if (ip->type == INST_GUS && ip->samples != 1) + { + ctl->cmsg(CMSG_WARNING, VERB_VERBOSE, + "Strange: percussion instrument with %d samples!", ip->samples); + } + + if (ip->sample->note_to_use) /* Do we have a fixed pitch? */ + { + voice[i].orig_frequency=freq_table[(int)(ip->sample->note_to_use)]; + drumpan=drumpanpot[ch][(int)ip->sample->note_to_use]; + } + else + voice[i].orig_frequency=freq_table[this_note & 0x7F]; + + } + else + { + if (channel[ch].program==SPECIAL_PROGRAM) + lp=default_instrument; + else if (!(lp=tonebank[channel[ch].bank]-> + tone[channel[ch].program].layer)) + { + if (!(lp=tonebank[0]->tone[this_prog].layer)) + return; /* No instrument? Then we can't play. */ + } + ip = lp->instrument; + if (ip->sample->note_to_use) /* Fixed-pitch instrument? */ + voice[i].orig_frequency=freq_table[(int)(ip->sample->note_to_use)]; + else + voice[i].orig_frequency=freq_table[this_note & 0x7F]; + } + + select_stereo_samples(i, lp); + + voice[i].starttime = e->time; + played_note = voice[i].sample->note_to_use; + + if (!played_note || !drumsflag) played_note = this_note & 0x7f; +#if 0 + played_note = ( (played_note - voice[i].sample->freq_center) * voice[i].sample->freq_scale ) / 1024 + + voice[i].sample->freq_center; +#endif + voice[i].status=VOICE_ON; + voice[i].channel=ch; + voice[i].note=played_note; + voice[i].velocity=this_velocity; + voice[i].sample_offset=0; + voice[i].sample_increment=0; /* make sure it isn't negative */ + + voice[i].tremolo_phase=0; + voice[i].tremolo_phase_increment=voice[i].sample->tremolo_phase_increment; + voice[i].tremolo_sweep=voice[i].sample->tremolo_sweep_increment; + voice[i].tremolo_sweep_position=0; + + voice[i].vibrato_sweep=voice[i].sample->vibrato_sweep_increment; + voice[i].vibrato_sweep_position=0; + voice[i].vibrato_depth=voice[i].sample->vibrato_depth; + voice[i].vibrato_control_ratio=voice[i].sample->vibrato_control_ratio; + voice[i].vibrato_control_counter=voice[i].vibrato_phase=0; + voice[i].vibrato_delay = voice[i].sample->vibrato_delay; + + kill_others(i); + + for (j=0; j<VIBRATO_SAMPLE_INCREMENTS; j++) + voice[i].vibrato_sample_increment[j]=0; + + + attacktime = channel[ch].attacktime; + releasetime = channel[ch].releasetime; + decaytime = 64; + variationbank = channel[ch].variationbank; + + switch (variationbank) { + case 8: + attacktime = 64+32; + break; + case 12: + decaytime = 64-32; + break; + case 16: + brightness = 64+16; + break; + case 17: + brightness = 64+32; + break; + case 18: + brightness = 64-16; + break; + case 19: + brightness = 64-32; + break; + case 20: + harmoniccontent = 64+16; + break; +#if 0 + case 24: + voice[i].modEnvToFilterFc=2.0; + voice[i].sample->cutoff_freq = 800; + break; + case 25: + voice[i].modEnvToFilterFc=-2.0; + voice[i].sample->cutoff_freq = 800; + break; + case 27: + voice[i].modLfoToFilterFc=2.0; + voice[i].lfo_phase_increment=109; + voice[i].lfo_sweep=122; + voice[i].sample->cutoff_freq = 800; + break; + case 28: + voice[i].modLfoToFilterFc=-2.0; + voice[i].lfo_phase_increment=109; + voice[i].lfo_sweep=122; + voice[i].sample->cutoff_freq = 800; + break; +#endif + default: + break; + } + + + for (j=ATTACK; j<MAXPOINT; j++) + { + voice[i].envelope_rate[j]=voice[i].sample->envelope_rate[j]; + voice[i].envelope_offset[j]=voice[i].sample->envelope_offset[j]; + } + + voice[i].echo_delay=voice[i].envelope_rate[DELAY]; + voice[i].echo_delay_count = voice[i].echo_delay; + + if (attacktime!=64) + { + rt = voice[i].envelope_rate[ATTACK]; + rt = rt + ( (64-attacktime)*rt ) / 100; + if (rt > 1000) voice[i].envelope_rate[ATTACK] = rt; + } + if (releasetime!=64) + { + rt = voice[i].envelope_rate[RELEASE]; + rt = rt + ( (64-releasetime)*rt ) / 100; + if (rt > 1000) voice[i].envelope_rate[RELEASE] = rt; + } + if (decaytime!=64) + { + rt = voice[i].envelope_rate[DECAY]; + rt = rt + ( (64-decaytime)*rt ) / 100; + if (rt > 1000) voice[i].envelope_rate[DECAY] = rt; + } + + if (channel[ch].panning != NO_PANNING) + voice[i].panning=channel[ch].panning; + else + voice[i].panning=voice[i].sample->panning; + if (drumpan != NO_PANNING) + voice[i].panning=drumpan; + + if (variationbank == 1) { + int pan = voice[i].panning; + int disturb = 0; + /* If they're close up (no reverb) and you are behind the pianist, + * high notes come from the right, so we'll spread piano etc. notes + * out horizontally according to their pitches. + */ + if (this_prog < 21) { + int n = voice[i].velocity - 32; + if (n < 0) n = 0; + if (n > 64) n = 64; + pan = pan/2 + n; + } + /* For other types of instruments, the music sounds more alive if + * notes come from slightly different directions. However, instruments + * do drift around in a sometimes disconcerting way, so the following + * might not be such a good idea. + */ + else disturb = (voice[i].velocity/32 % 8) + + (voice[i].note % 8); /* /16? */ + + if (pan < 64) pan += disturb; + else pan -= disturb; + if (pan < 0) pan = 0; + else if (pan > 127) pan = 127; + voice[i].panning = pan; + } + + recompute_freq(i); + recompute_amp(i); + if (voice[i].sample->modes & MODES_ENVELOPE) + { + /* Ramp up from 0 */ + voice[i].envelope_stage=ATTACK; + voice[i].envelope_volume=0; + voice[i].control_counter=0; + recompute_envelope(i); + } + else + { + voice[i].envelope_increment=0; + } + apply_envelope_to_amp(i); + + voice[i].clone_voice = -1; + voice[i].clone_type = NOT_CLONE; + + clone_voice(ip, i, e, STEREO_CLONE, variationbank); + clone_voice(ip, i, e, CHORUS_CLONE, variationbank); + clone_voice(ip, i, e, REVERB_CLONE, variationbank); + + ctl->note(i); +} + +static void kill_note(int i) +{ + voice[i].status=VOICE_DIE; + if (voice[i].clone_voice >= 0) + voice[ voice[i].clone_voice ].status=VOICE_DIE; + ctl->note(i); +} + + +/* Only one instance of a note can be playing on a single channel. */ +static void note_on(MidiEvent *e) +{ + int i=voices, lowest=-1; + int32 lv=0x7FFFFFFF, v; + + while (i--) + { + if (voice[i].status == VOICE_FREE) + lowest=i; /* Can't get a lower volume than silence */ + else if (voice[i].channel==e->channel && + (voice[i].note==e->a || channel[voice[i].channel].mono)) + kill_note(i); + } + + if (lowest != -1) + { + /* Found a free voice. */ + start_note(e,lowest); + return; + } + +#if 0 + /* Look for the decaying note with the lowest volume */ + i=voices; + while (i--) + { + if (voice[i].status & ~(VOICE_ON | VOICE_DIE | VOICE_FREE)) + { + v=voice[i].left_mix; + if ((voice[i].panned==PANNED_MYSTERY) && (voice[i].right_mix>v)) + v=voice[i].right_mix; + if (v<lv) + { + lv=v; + lowest=i; + } + } + } +#endif + + /* Look for the decaying note with the lowest volume */ + if (lowest==-1) + { + i=voices; + while (i--) + { + if ( (voice[i].status & ~(VOICE_ON | VOICE_DIE | VOICE_FREE)) && + (!voice[i].clone_type)) + { + v=voice[i].left_mix; + if ((voice[i].panned==PANNED_MYSTERY) && (voice[i].right_mix>v)) + v=voice[i].right_mix; + if (v<lv) + { + lv=v; + lowest=i; + } + } + } + } + + if (lowest != -1) + { + int cl = voice[lowest].clone_voice; + + /* This can still cause a click, but if we had a free voice to + spare for ramping down this note, we wouldn't need to kill it + in the first place... Still, this needs to be fixed. Perhaps + we could use a reserve of voices to play dying notes only. */ + + if (cl >= 0) { + if (voice[cl].clone_type==STEREO_CLONE || + (!voice[cl].clone_type && voice[lowest].clone_type==STEREO_CLONE)) + voice[cl].status=VOICE_FREE; + else if (voice[cl].clone_voice==lowest) voice[cl].clone_voice=-1; + } + + cut_notes++; + voice[lowest].status=VOICE_FREE; + ctl->note(lowest); + start_note(e,lowest); + } + else + lost_notes++; +} + +static void finish_note(int i) +{ + if (voice[i].sample->modes & MODES_ENVELOPE) + { + /* We need to get the envelope out of Sustain stage */ + voice[i].envelope_stage=3; + voice[i].status=VOICE_OFF; + recompute_envelope(i); + apply_envelope_to_amp(i); + ctl->note(i); + } + else + { + /* Set status to OFF so resample_voice() will let this voice out + of its loop, if any. In any case, this voice dies when it + hits the end of its data (ofs>=data_length). */ + voice[i].status=VOICE_OFF; + } + + { int v; + if ( (v=voice[i].clone_voice) >= 0) + { + voice[i].clone_voice = -1; + finish_note(v); + } + } +} + +static void note_off(MidiEvent *e) +{ + int i=voices, v; + while (i--) + if (voice[i].status==VOICE_ON && + voice[i].channel==e->channel && + voice[i].note==e->a) + { + if (channel[e->channel].sustain) + { + voice[i].status=VOICE_SUSTAINED; + + if ( (v=voice[i].clone_voice) >= 0) + { + if (voice[v].status == VOICE_ON) + voice[v].status=VOICE_SUSTAINED; + } + + ctl->note(i); + } + else + finish_note(i); + return; + } +} + +/* Process the All Notes Off event */ +static void all_notes_off(int c) +{ + int i=voices; + ctl->cmsg(CMSG_INFO, VERB_DEBUG, "All notes off on channel %d", c); + while (i--) + if (voice[i].status==VOICE_ON && + voice[i].channel==c) + { + if (channel[c].sustain) + { + voice[i].status=VOICE_SUSTAINED; + ctl->note(i); + } + else + finish_note(i); + } +} + +/* Process the All Sounds Off event */ +static void all_sounds_off(int c) +{ + int i=voices; + while (i--) + if (voice[i].channel==c && + voice[i].status != VOICE_FREE && + voice[i].status != VOICE_DIE) + { + kill_note(i); + } +} + +static void adjust_pressure(MidiEvent *e) +{ + int i=voices; + while (i--) + if (voice[i].status==VOICE_ON && + voice[i].channel==e->channel && + voice[i].note==e->a) + { + voice[i].velocity=e->b; + recompute_amp(i); + apply_envelope_to_amp(i); + return; + } +} + +static void adjust_panning(int c) +{ + int i=voices; + while (i--) + if ((voice[i].channel==c) && + (voice[i].status==VOICE_ON || voice[i].status==VOICE_SUSTAINED)) + { + if (voice[i].clone_type != NOT_CLONE) continue; + voice[i].panning=channel[c].panning; + recompute_amp(i); + apply_envelope_to_amp(i); + } +} + +static void drop_sustain(int c) +{ + int i=voices; + while (i--) + if (voice[i].status==VOICE_SUSTAINED && voice[i].channel==c) + finish_note(i); +} + +static void adjust_pitchbend(int c) +{ + int i=voices; + while (i--) + if (voice[i].status!=VOICE_FREE && voice[i].channel==c) + { + recompute_freq(i); + } +} + +static void adjust_volume(int c) +{ + int i=voices; + while (i--) + if (voice[i].channel==c && + (voice[i].status==VOICE_ON || voice[i].status==VOICE_SUSTAINED)) + { + recompute_amp(i); + apply_envelope_to_amp(i); + } +} + +static void seek_forward(int32 until_time) +{ + reset_voices(); + while (current_event->time < until_time) + { + switch(current_event->type) + { + /* All notes stay off. Just handle the parameter changes. */ + + case ME_PITCH_SENS: + channel[current_event->channel].pitchsens= + current_event->a; + channel[current_event->channel].pitchfactor=0; + break; + + case ME_PITCHWHEEL: + channel[current_event->channel].pitchbend= + current_event->a + current_event->b * 128; + channel[current_event->channel].pitchfactor=0; + break; + + case ME_MAINVOLUME: + channel[current_event->channel].volume=current_event->a; + break; + + case ME_MASTERVOLUME: + adjust_master_volume(current_event->a + (current_event->b <<7)); + break; + + case ME_PAN: + channel[current_event->channel].panning=current_event->a; + break; + + case ME_EXPRESSION: + channel[current_event->channel].expression=current_event->a; + break; + + case ME_PROGRAM: + /* if (ISDRUMCHANNEL(current_event->channel)) */ + if (channel[current_event->channel].kit) + /* Change drum set */ + channel[current_event->channel].bank=current_event->a; + else + channel[current_event->channel].program=current_event->a; + break; + + case ME_SUSTAIN: + channel[current_event->channel].sustain=current_event->a; + break; + + + case ME_REVERBERATION: + channel[current_event->channel].reverberation=current_event->a; + break; + + case ME_CHORUSDEPTH: + channel[current_event->channel].chorusdepth=current_event->a; + break; + + case ME_HARMONICCONTENT: + channel[current_event->channel].harmoniccontent=current_event->a; + break; + + case ME_RELEASETIME: + channel[current_event->channel].releasetime=current_event->a; + break; + + case ME_ATTACKTIME: + channel[current_event->channel].attacktime=current_event->a; + break; + + case ME_BRIGHTNESS: + channel[current_event->channel].brightness=current_event->a; + break; + + case ME_TONE_KIT: + if (current_event->a==SFX_BANKTYPE) + { + channel[current_event->channel].sfx=SFXBANK; + channel[current_event->channel].kit=0; + } + else + { + channel[current_event->channel].sfx=0; + channel[current_event->channel].kit=current_event->a; + } + break; + + + case ME_RESET_CONTROLLERS: + reset_controllers(current_event->channel); + break; + + case ME_TONE_BANK: + channel[current_event->channel].bank=current_event->a; + break; + + case ME_EOT: + current_sample=current_event->time; + return; + } + current_event++; + } + /*current_sample=current_event->time;*/ + if (current_event != event_list) + current_event--; + current_sample=until_time; +} + +static void skip_to(int32 until_time) +{ + if (current_sample > until_time) + current_sample=0; + + reset_midi(); + buffered_count=0; + buffer_pointer=common_buffer; + current_event=event_list; + + if (until_time) + seek_forward(until_time); + ctl->reset(); +} + +static int apply_controls(void) +{ + int rc, i, did_skip=0; + int32 val; + /* ASCII renditions of CD player pictograms indicate approximate effect */ + do + switch(rc=ctl->read(&val)) + { + case RC_QUIT: /* [] */ + case RC_LOAD_FILE: + case RC_NEXT: /* >>| */ + case RC_REALLY_PREVIOUS: /* |<< */ + return rc; + + case RC_CHANGE_VOLUME: + if (val>0 || amplification > -val) + amplification += val; + else + amplification=0; + if (amplification > MAX_AMPLIFICATION) + amplification=MAX_AMPLIFICATION; + adjust_amplification(); + for (i=0; i<voices; i++) + if (voice[i].status != VOICE_FREE) + { + recompute_amp(i); + apply_envelope_to_amp(i); + } + ctl->master_volume(amplification); + break; + + case RC_PREVIOUS: /* |<< */ + if (current_sample < 2*play_mode->rate) + return RC_REALLY_PREVIOUS; + return RC_RESTART; + + case RC_RESTART: /* |<< */ + skip_to(0); + did_skip=1; + break; + + case RC_JUMP: + if (val >= sample_count) + return RC_NEXT; + skip_to(val); + return rc; + + case RC_FORWARD: /* >> */ + if (val+current_sample >= sample_count) + return RC_NEXT; + skip_to(val+current_sample); + did_skip=1; + break; + + case RC_BACK: /* << */ + if (current_sample > val) + skip_to(current_sample-val); + else + skip_to(0); /* We can't seek to end of previous song. */ + did_skip=1; + break; + } + while (rc!= RC_NONE); + + /* Advertise the skip so that we stop computing the audio buffer */ + if (did_skip) + return RC_JUMP; + else + return rc; +} + +static void do_compute_data(uint32 count) +{ + int i; + if (!count) return; /* (gl) */ + memset(buffer_pointer, 0, count * num_ochannels * 4); + for (i=0; i<voices; i++) + { + if(voice[i].status != VOICE_FREE) + { + //printf("mixing voice %d\n", i); + if (!voice[i].sample_offset && voice[i].echo_delay_count) + { + if ((uint32)voice[i].echo_delay_count >= count) voice[i].echo_delay_count -= count; + else + { + mix_voice(buffer_pointer+voice[i].echo_delay_count, i, count-voice[i].echo_delay_count); + voice[i].echo_delay_count = 0; + } + } + else mix_voice(buffer_pointer, i, count); + } + } + current_sample += count; +} + + +/* count=0 means flush remaining buffered data to output device, then + flush the device itself */ +static int compute_data(void *stream, int32 count) +{ + int rc, channels; + + if ( play_mode->encoding & PE_MONO ) + channels = 1; + else + channels = num_ochannels; + + if (!count) + { + if (buffered_count) + s32tobuf(stream, common_buffer, channels*buffered_count); + buffer_pointer=common_buffer; + buffered_count=0; + return RC_NONE; + } + + while ((count+buffered_count) >= AUDIO_BUFFER_SIZE) + { + do_compute_data(AUDIO_BUFFER_SIZE-buffered_count); + count -= AUDIO_BUFFER_SIZE-buffered_count; + s32tobuf(stream, common_buffer, channels*AUDIO_BUFFER_SIZE); + buffer_pointer=common_buffer; + buffered_count=0; + + ctl->current_time(current_sample); + if ((rc=apply_controls())!=RC_NONE) + return rc; + } + if (count>0) + { + do_compute_data(count); + buffered_count += count; + buffer_pointer += count * channels; + } + return RC_NONE; +} + +int Timidity_PlaySome(void *stream, int samples) +{ + //printf("Timidity_PlaySome()\n"); + int rc = RC_NONE; + int32 end_sample; + + if ( ! midi_playing ) { + return RC_NONE; + } + end_sample = current_sample+samples; + while ( current_sample < end_sample ) { + /* Handle all events that should happen at this time */ + while (current_event->time <= current_sample) { + switch(current_event->type) { + + /* Effects affecting a single note */ + + case ME_NOTEON: + current_event->a += channel[current_event->channel].transpose; + if (!(current_event->b)) /* Velocity 0? */ + note_off(current_event); + else + note_on(current_event); + break; + + case ME_NOTEOFF: + current_event->a += channel[current_event->channel].transpose; + note_off(current_event); + break; + + case ME_KEYPRESSURE: + adjust_pressure(current_event); + break; + + /* Effects affecting a single channel */ + + case ME_PITCH_SENS: + channel[current_event->channel].pitchsens=current_event->a; + channel[current_event->channel].pitchfactor=0; + break; + + case ME_PITCHWHEEL: + channel[current_event->channel].pitchbend= + current_event->a + current_event->b * 128; + channel[current_event->channel].pitchfactor=0; + /* Adjust pitch for notes already playing */ + adjust_pitchbend(current_event->channel); + ctl->pitch_bend(current_event->channel, + channel[current_event->channel].pitchbend); + break; + + case ME_MAINVOLUME: + channel[current_event->channel].volume=current_event->a; + adjust_volume(current_event->channel); + ctl->volume(current_event->channel, current_event->a); + break; + + case ME_MASTERVOLUME: + adjust_master_volume(current_event->a + (current_event->b <<7)); + break; + + case ME_REVERBERATION: + channel[current_event->channel].reverberation=current_event->a; + break; + + case ME_CHORUSDEPTH: + channel[current_event->channel].chorusdepth=current_event->a; + break; + + case ME_PAN: + channel[current_event->channel].panning=current_event->a; + if (adjust_panning_immediately) + adjust_panning(current_event->channel); + ctl->panning(current_event->channel, current_event->a); + break; + + case ME_EXPRESSION: + channel[current_event->channel].expression=current_event->a; + adjust_volume(current_event->channel); + ctl->expression(current_event->channel, current_event->a); + break; + + case ME_PROGRAM: + /* if (ISDRUMCHANNEL(current_event->channel)) { */ + if (channel[current_event->channel].kit) { + /* Change drum set */ + channel[current_event->channel].bank=current_event->a; + } + else + { + channel[current_event->channel].program=current_event->a; + } + ctl->program(current_event->channel, current_event->a); + break; + + case ME_SUSTAIN: + channel[current_event->channel].sustain=current_event->a; + if (!current_event->a) + drop_sustain(current_event->channel); + ctl->sustain(current_event->channel, current_event->a); + break; + + case ME_RESET_CONTROLLERS: + reset_controllers(current_event->channel); + redraw_controllers(current_event->channel); + break; + + case ME_ALL_NOTES_OFF: + all_notes_off(current_event->channel); + break; + + case ME_ALL_SOUNDS_OFF: + all_sounds_off(current_event->channel); + break; + + case ME_HARMONICCONTENT: + channel[current_event->channel].harmoniccontent=current_event->a; + break; + + case ME_RELEASETIME: + channel[current_event->channel].releasetime=current_event->a; + break; + + case ME_ATTACKTIME: + channel[current_event->channel].attacktime=current_event->a; + break; + + case ME_BRIGHTNESS: + channel[current_event->channel].brightness=current_event->a; + break; + + case ME_TONE_BANK: + channel[current_event->channel].bank=current_event->a; + break; + + + case ME_TONE_KIT: + if (current_event->a==SFX_BANKTYPE) + { + channel[current_event->channel].sfx=SFXBANK; + channel[current_event->channel].kit=0; + } + else + { + channel[current_event->channel].sfx=0; + channel[current_event->channel].kit=current_event->a; + } + break; + + case ME_EOT: + /* Give the last notes a couple of seconds to decay */ + ctl->cmsg(CMSG_INFO, VERB_VERBOSE, + "Playing time: ~%d seconds", current_sample/play_mode->rate+2); + ctl->cmsg(CMSG_INFO, VERB_VERBOSE, + "Notes cut: %d", cut_notes); + ctl->cmsg(CMSG_INFO, VERB_VERBOSE, + "Notes lost totally: %d", lost_notes); + midi_playing = 0; + return RC_TUNE_END; + } + current_event++; + } + if (current_event->time > end_sample) + rc=compute_data(stream, end_sample-current_sample); + else + rc=compute_data(stream, current_event->time-current_sample); + ctl->refresh(); + if ( (rc!=RC_NONE) && (rc!=RC_JUMP)) + break; + } + return rc; +} + + +void Timidity_SetVolume(int volume) +{ + int i; + if (volume > MAX_AMPLIFICATION) + amplification=MAX_AMPLIFICATION; + else + if (volume < 0) + amplification=0; + else + amplification=volume; + adjust_amplification(); + for (i=0; i<voices; i++) + if (voice[i].status != VOICE_FREE) + { + recompute_amp(i); + apply_envelope_to_amp(i); + } + ctl->master_volume(amplification); +} + +MidiSong *Timidity_LoadSong_RW(SDL_RWops *rw, int freerw) +{ + MidiSong *song; + int32 events; + + /* Allocate memory for the song */ + song = (MidiSong *)safe_malloc(sizeof(*song)); + memset(song, 0, sizeof(*song)); + + strcpy(midi_name, "SDLrwops source"); + + song->events = read_midi_file(rw, &events, &song->samples); + if (freerw) { + SDL_RWclose(rw); + } + + /* Make sure everything is okay */ + if (!song->events) { + free(song); + song = NULL; + } + return(song); +} + +void Timidity_Start(MidiSong *song) +{ + load_missing_instruments(); + adjust_amplification(); + sample_count = song->samples; + event_list = song->events; + lost_notes=cut_notes=0; + //printf("Timidity: playing song with %d samples\n", sample_count); + + skip_to(0); + midi_playing = 1; +} + +int Timidity_Active(void) +{ + return(midi_playing); +} + +void Timidity_Stop(void) +{ + midi_playing = 0; +} + +void Timidity_FreeSong(MidiSong *song) +{ + if (free_instruments_afterwards) + free_instruments(); + + free(song->events); + free(song); +} + +void Timidity_Close(void) +{ + if (resample_buffer) { + free(resample_buffer); + resample_buffer=NULL; + } + if (common_buffer) { + free(common_buffer); + common_buffer=NULL; + } + free_instruments(); + free_pathlist(); +} + diff --git a/apps/plugins/sdl/SDL_mixer/timidity/playmidi.h b/apps/plugins/sdl/SDL_mixer/timidity/playmidi.h new file mode 100644 index 0000000000..2a32d7ebe2 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/timidity/playmidi.h @@ -0,0 +1,160 @@ +/* + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi> + + This program is free software; you can redistribute it and/or modify + it under the terms of the Perl Artistic License, available in COPYING. + */ + +typedef struct { + int32 time; + uint8 channel, type, a, b; +} MidiEvent; + +/* Midi events */ +#define ME_NONE 0 +#define ME_NOTEON 1 +#define ME_NOTEOFF 2 +#define ME_KEYPRESSURE 3 +#define ME_MAINVOLUME 4 +#define ME_PAN 5 +#define ME_SUSTAIN 6 +#define ME_EXPRESSION 7 +#define ME_PITCHWHEEL 8 +#define ME_PROGRAM 9 +#define ME_TEMPO 10 +#define ME_PITCH_SENS 11 + +#define ME_ALL_SOUNDS_OFF 12 +#define ME_RESET_CONTROLLERS 13 +#define ME_ALL_NOTES_OFF 14 +#define ME_TONE_BANK 15 + +#define ME_LYRIC 16 +#define ME_TONE_KIT 17 +#define ME_MASTERVOLUME 18 +#define ME_CHANNEL_PRESSURE 19 + +#define ME_HARMONICCONTENT 71 +#define ME_RELEASETIME 72 +#define ME_ATTACKTIME 73 +#define ME_BRIGHTNESS 74 + +#define ME_REVERBERATION 91 +#define ME_CHORUSDEPTH 93 + +#define ME_EOT 99 + + +#define SFX_BANKTYPE 64 + +typedef struct { + int + bank, program, volume, sustain, panning, pitchbend, expression, + mono, /* one note only on this channel -- not implemented yet */ + /* new stuff */ + variationbank, reverberation, chorusdepth, harmoniccontent, + releasetime, attacktime, brightness, kit, sfx, + /* end new */ + pitchsens; + FLOAT_T + pitchfactor; /* precomputed pitch bend factor to save some fdiv's */ + char transpose; + char *name; +} Channel; + +/* Causes the instrument's default panning to be used. */ +#define NO_PANNING -1 +/* envelope points */ +#define MAXPOINT 7 + +typedef struct { + uint8 + status, channel, note, velocity, clone_type; + Sample *sample; + Sample *left_sample; + Sample *right_sample; + int32 clone_voice; + int32 + orig_frequency, frequency, + sample_offset, loop_start, loop_end; + int32 + envelope_volume, modulation_volume; + int32 + envelope_target, modulation_target; + int32 + tremolo_sweep, tremolo_sweep_position, tremolo_phase, + lfo_sweep, lfo_sweep_position, lfo_phase, + vibrato_sweep, vibrato_sweep_position, vibrato_depth, vibrato_delay, + starttime, echo_delay_count; + int32 + echo_delay, + sample_increment, + envelope_increment, + modulation_increment, + tremolo_phase_increment, + lfo_phase_increment; + + final_volume_t left_mix, right_mix, lr_mix, rr_mix, ce_mix, lfe_mix; + + FLOAT_T + left_amp, right_amp, lr_amp, rr_amp, ce_amp, lfe_amp, + volume, tremolo_volume, lfo_volume; + int32 + vibrato_sample_increment[VIBRATO_SAMPLE_INCREMENTS]; + int32 + envelope_rate[MAXPOINT], envelope_offset[MAXPOINT]; + int32 + vibrato_phase, vibrato_control_ratio, vibrato_control_counter, + envelope_stage, modulation_stage, control_counter, + modulation_delay, modulation_counter, panning, panned; +} Voice; + +/* Voice status options: */ +#define VOICE_FREE 0 +#define VOICE_ON 1 +#define VOICE_SUSTAINED 2 +#define VOICE_OFF 3 +#define VOICE_DIE 4 + +/* Voice panned options: */ +#define PANNED_MYSTERY 0 +#define PANNED_LEFT 1 +#define PANNED_RIGHT 2 +#define PANNED_CENTER 3 +/* Anything but PANNED_MYSTERY only uses the left volume */ + +/* Envelope stages: */ +#define ATTACK 0 +#define HOLD 1 +#define DECAY 2 +#define RELEASE 3 +#define RELEASEB 4 +#define RELEASEC 5 +#define DELAY 6 + +extern Channel channel[16]; +extern Voice voice[MAX_VOICES]; +extern signed char drumvolume[MAXCHAN][MAXNOTE]; +extern signed char drumpanpot[MAXCHAN][MAXNOTE]; +extern signed char drumreverberation[MAXCHAN][MAXNOTE]; +extern signed char drumchorusdepth[MAXCHAN][MAXNOTE]; + +extern int32 control_ratio, amp_with_poly, amplification; +extern int32 drumchannels; +extern int adjust_panning_immediately; +extern int voices; + +#define ISDRUMCHANNEL(c) ((drumchannels & (1<<(c)))) + +extern int GM_System_On; +extern int XG_System_On; +extern int GS_System_On; + +extern int XG_System_reverb_type; +extern int XG_System_chorus_type; +extern int XG_System_variation_type; + +extern int play_midi(MidiEvent *el, int32 events, int32 samples); +extern int play_midi_file(const char *fn); +extern void dumb_pass_playing_list(int number_of_files, char *list_of_files[]); diff --git a/apps/plugins/sdl/SDL_mixer/timidity/readmidi.c b/apps/plugins/sdl/SDL_mixer/timidity/readmidi.c new file mode 100644 index 0000000000..afd3f9f571 --- /dev/null +++ b/apps/plugins/sdl/SDL_mixer/timidity/readmidi.c @@ -0,0 +1,1065 @@ +/* + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi> + + This program is free software; you can redistribute it and/or modify + it under the terms of the Perl Artistic License, available in COPYING. + */ + +#include <SDL_rwops.h> + +#include "config.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" +#include "readmidi.h" +#include "output.h" +#include "ctrlmode.h" + +int32 quietchannels=0; + +static int midi_port_number; +char midi_name[FILENAME_MAX+1]; + +static int track_info, curr_track, curr_title_track; +static char title[128]; + +#if MAXCHAN <= 16 +#define MERGE_CHANNEL_PORT(ch) ((int)(ch)) +#else +#define MERGE_CHANNEL_PORT(ch) ((int)(ch) | (midi_port_number << 4)) +#endif + +/* to avoid some unnecessary parameter passing */ +static MidiEventList *evlist; +static int32 event_count; +static SDL_RWops *rw; +static int32 at; + +/* These would both fit into 32 bits, but they are often added in + large multiples, so it's simpler to have two roomy ints */ +static int32 sample_increment, sample_correction; /*samples per MIDI delta-t*/ + +/* Computes how many (fractional) samples one MIDI delta-time unit contains */ +static void compute_sample_increment(int32 tempo, int32 divisions) +{ + double a; + a = (double) (tempo) * (double) (play_mode->rate) * (65536.0/1000000.0) / + (double)(divisions); + + sample_correction = (int32)(a) & 0xFFFF; + sample_increment = (int32)(a) >> 16; + + ctl->cmsg(CMSG_INFO, VERB_DEBUG, "Samples per delta-t: %d (correction %d)", + sample_increment, sample_correction); +} + +/* Read variable-length number (7 bits per byte, MSB first) */ +static int32 getvl(void) +{ + int32 l=0; + uint8 c; + for (;;) + { + SDL_RWread(rw,&c,1,1); + l += (c & 0x7f); + if (!(c & 0x80)) return l; + l<<=7; + } +} + + +static int sysex(uint32 len, uint8 *syschan, uint8 *sysa, uint8 *sysb, SDL_RWops *rw) +{ + unsigned char *s=(unsigned char *)safe_malloc(len); + int id, model, ch, port, adhi, adlo, cd, dta, dtb, dtc; + if (len != (uint32)SDL_RWread(rw, s, 1, len)) + { + free(s); + return 0; + } + if (len<5) { free(s); return 0; } + if (curr_track == curr_title_track && track_info > 1) title[0] = '\0'; + id=s[0]; port=s[1]; model=s[2]; adhi=s[3]; adlo=s[4]; + if (id==0x7e && port==0x7f && model==0x09 && adhi==0x01) + { + ctl->cmsg(CMSG_TEXT, VERB_VERBOSE, "GM System On", len); + GM_System_On=1; + free(s); + return 0; + } + ch = adlo & 0x0f; + *syschan=(uint8)ch; + if (id==0x7f && len==7 && port==0x7f && model==0x04 && adhi==0x01) + { + ctl->cmsg(CMSG_TEXT, VERB_DEBUG, "Master Volume %d", s[4]+(s[5]<<7)); + *sysa = s[4]; + *sysb = s[5]; + free(s); + return ME_MASTERVOLUME; + /** return s[4]+(s[5]<<7); **/ + } + if (len<8) { free(s); return 0; } + port &=0x0f; + ch = (adlo & 0x0f) | ((port & 0x03) << 4); + *syschan=(uint8)ch; + cd=s[5]; dta=s[6]; + if (len >= 8) dtb=s[7]; + else dtb=-1; + if (len >= 9) dtc=s[8]; + else dtc=-1; + free(s); + if (id==0x43 && model==0x4c) + { + if (!adhi && !adlo && cd==0x7e && !dta) + { + ctl->cmsg(CMSG_TEXT, VERB_VERBOSE, "XG System On", len); + XG_System_On=1; + #ifdef tplus + vol_table = xg_vol_table; + #endif + } + else if (adhi == 2 && adlo == 1) + { + if (dtb==8) dtb=3; + switch (cd) + { + case 0x00: + XG_System_reverb_type=(dta<<3)+dtb; + break; + case 0x20: + XG_System_chorus_type=((dta-64)<<3)+dtb; + break; + case 0x40: + XG_System_variation_type=dta; + break; + case 0x5a: + /* dta==0 Insertion; dta==1 System */ + break; + default: break; + } + } + else if (adhi == 8 && cd <= 40) + { + *sysa = dta & 0x7f; + switch (cd) + { + case 0x01: /* bank select MSB */ + return ME_TONE_KIT; + break; + case 0x02: /* bank select LSB */ + return ME_TONE_BANK; + break; + case 0x03: /* program number */ + /** MIDIEVENT(d->at, ME_PROGRAM, lastchan, a, 0); **/ + return ME_PROGRAM; + break; + case 0x08: /* */ + /* d->channel[adlo&0x0f].transpose = (char)(dta-64); */ + channel[ch].transpose = (char)(dta-64); + ctl->cmsg(CMSG_TEXT, VERB_DEBUG, "transpose channel %d by %d", + (adlo&0x0f)+1, dta-64); + break; + case 0x0b: /* volume */ + return ME_MAINVOLUME; + break; + case 0x0e: /* pan */ + return ME_PAN; + break; + case 0x12: /* chorus send */ + return ME_CHORUSDEPTH; + break; + case 0x13: /* reverb send */ + return ME_REVERBERATION; + break; + case 0x14: /* variation send */ + break; + case 0x18: /* filter cutoff */ + return ME_BRIGHTNESS; + break; + case 0x19: /* filter resonance */ + return ME_HARMONICCONTENT; + break; + default: break; + } + } + return 0; + } + else if (id==0x41 && model==0x42 && adhi==0x12 && adlo==0x40) + { + if (dtc<0) return 0; + if (!cd && dta==0x7f && !dtb && dtc==0x41) + { + ctl->cmsg(CMSG_TEXT, VERB_VERBOSE, "GS System On", len); + GS_System_On=1; + #ifdef tplus + vol_table = gs_vol_table; + #endif + } + else if (dta==0x15 && (cd&0xf0)==0x10) + { + int chan=cd&0x0f; + if (!chan) chan=9; + else if (chan<10) chan--; + chan = MERGE_CHANNEL_PORT(chan); + channel[chan].kit=dtb; + } + else if (cd==0x01) switch(dta) + { + case 0x30: + switch(dtb) + { + case 0: XG_System_reverb_type=16+0; break; + case 1: XG_System_reverb_type=16+1; break; + case 2: XG_System_reverb_type=16+2; break; + case 3: XG_System_reverb_type= 8+0; break; + case 4: XG_System_reverb_type= 8+1; break; + case 5: XG_System_reverb_type=32+0; break; + case 6: XG_System_reverb_type=8*17; break; + case 7: XG_System_reverb_type=8*18; break; + } + break; + case 0x38: + switch(dtb) + { + case 0: XG_System_chorus_type= 8+0; break; + case 1: XG_System_chorus_type= 8+1; break; + case 2: XG_System_chorus_type= 8+2; break; + case 3: XG_System_chorus_type= 8+4; break; + case 4: XG_System_chorus_type= -1; break; + case 5: XG_System_chorus_type= 8*3; break; + case 6: XG_System_chorus_type= -1; break; + case 7: XG_System_chorus_type= -1; break; + } + break; + } + return 0; + } + return 0; +} + +/* Print a string from the file, followed by a newline. Any non-ASCII + or unprintable characters will be converted to periods. */ +static int dumpstring(int32 len, const char *label) +{ + signed char *s=safe_malloc(len+1); + if (len != (int32)SDL_RWread(rw, s, 1, len)) + { + free(s); + return -1; + } + s[len]='\0'; + while (len--) + { + if (s[len]<32) + s[len]='.'; + } + ctl->cmsg(CMSG_TEXT, VERB_VERBOSE, "%s%s", label, s); + free(s); + return 0; +} + +#define MIDIEVENT(at,t,ch,pa,pb) \ + new=safe_malloc(sizeof(MidiEventList)); \ + new->event.time=at; new->event.type=t; new->event.channel=ch; \ + new->event.a=pa; new->event.b=pb; new->next=0;\ + return new; + +#define MAGIC_EOT ((MidiEventList *)(-1)) + +/* Read a MIDI event, returning a freshly allocated element that can + be linked to the event list */ +static MidiEventList *read_midi_event(void) +{ + static uint8 laststatus, lastchan; + static uint8 nrpn=0, rpn_msb[16], rpn_lsb[16]; /* one per channel */ + uint8 me, type, a,b,c; + int32 len; + MidiEventList *new; + + for (;;) + { + at+=getvl(); + if (SDL_RWread(rw,&me,1,1)!=1) + { + ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: read_midi_event: %s", + current_filename, strerror(errno)); + return 0; + } + + if(me==0xF0 || me == 0xF7) /* SysEx event */ + { + int32 sret; + uint8 sysa=0, sysb=0, syschan=0; + + len=getvl(); + sret=sysex(len, &syschan, &sysa, &sysb, rw); + if (sret) + { + MIDIEVENT(at, sret, syschan, sysa, sysb); + } + } + else if(me==0xFF) /* Meta event */ + { + SDL_RWread(rw,&type,1,1); + len=getvl(); + if (type>0 && type<16) + { + static char *label[]={ + "Text event: ", "Text: ", "Copyright: ", "Track name: ", + "Instrument: ", "Lyric: ", "Marker: ", "Cue point: "}; + dumpstring(len, label[(type>7) ? 0 : type]); + } + else + switch(type) + { + + case 0x21: /* MIDI port number */ + if(len == 1) + { + SDL_RWread(rw,&midi_port_number,1,1); + if(midi_port_number == EOF) + { + ctl->cmsg(CMSG_ERROR, VERB_NORMAL, + "Warning: \"%s\": Short midi file.", + midi_name); + return 0; + } + midi_port_number &= 0x0f; + if (midi_port_number) + ctl->cmsg(CMSG_INFO, VERB_VERBOSE, + "(MIDI port number %d)", midi_port_number); + midi_port_number &= 0x03; + } + else SDL_RWseek(rw, len, RW_SEEK_CUR); + break; + + case 0x2F: /* End of Track */ + return MAGIC_EOT; + + case 0x51: /* Tempo */ + SDL_RWread(rw,&a,1,1); SDL_RWread(rw,&b,1,1); SDL_RWread(rw,&c,1,1); + MIDIEVENT(at, ME_TEMPO, c, a, b); + + default: + ctl->cmsg(CMSG_INFO, VERB_DEBUG, + "(Meta event type 0x%02x, length %ld)", type, len); + SDL_RWseek(rw, len, RW_SEEK_CUR); + break; + } + } + else + { + a=me; + if (a & 0x80) /* status byte */ + { + lastchan=a & 0x0F; + laststatus=(a>>4) & 0x07; + SDL_RWread(rw,&a, 1,1); + a &= 0x7F; + } + switch(laststatus) + { + case 0: /* Note off */ + SDL_RWread(rw,&b, 1,1); + b &= 0x7F; + MIDIEVENT(at, ME_NOTEOFF, lastchan, a,b); + + case 1: /* Note on */ + SDL_RWread(rw,&b, 1,1); + b &= 0x7F; + if (curr_track == curr_title_track && track_info > 1) title[0] = '\0'; + MIDIEVENT(at, ME_NOTEON, lastchan, a,b); + + + case 2: /* Key Pressure */ + SDL_RWread(rw,&b, 1,1); + b &= 0x7F; + MIDIEVENT(at, ME_KEYPRESSURE, lastchan, a, b); + + case 3: /* Control change */ + SDL_RWread(rw,&b, 1,1); + b &= 0x7F; + { + int control=255; + switch(a) + { + case 7: control=ME_MAINVOLUME; break; + case 10: control=ME_PAN; break; + case 11: control=ME_EXPRESSION; break; + case 64: control=ME_SUSTAIN; break; + + case 71: control=ME_HARMONICCONTENT; break; + case 72: control=ME_RELEASETIME; break; + case 73: control=ME_ATTACKTIME; break; + case 74: control=ME_BRIGHTNESS; break; + case 91: control=ME_REVERBERATION; break; + case 93: control=ME_CHORUSDEPTH; break; + + case 120: control=ME_ALL_SOUNDS_OFF; break; + case 121: control=ME_RESET_CONTROLLERS; break; + case 123: control=ME_ALL_NOTES_OFF; break; |