summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Alexey Yakovenko <wakeroid@gmail.com>2009-12-27 21:39:22 +0100
committerGravatar Alexey Yakovenko <wakeroid@gmail.com>2009-12-27 21:39:22 +0100
commit670ad41967e397f7d591b96abf90f092180b7783 (patch)
treec8d55c243cb618752764f88c822624cbf7b0b891
parent84d1d8c7d473e67b056ed7fc1fcd99321036fedf (diff)
parentdc9958e41747704a8095f5cdaae5c99fd0b0f2d1 (diff)
Merge branch 'guiplug'
Conflicts: main.c plugins/vorbis/vorbis.c
-rw-r--r--Makefile.am21
-rw-r--r--about.txt8
-rw-r--r--cdumb.c12
-rw-r--r--cgme.c15
-rw-r--r--configure.ac47
-rw-r--r--csid.cpp11
-rw-r--r--deadbeef.h164
-rw-r--r--decoder_template.c205
-rw-r--r--dumb/dumb-kode54/src/it/reads3m.c7
-rw-r--r--gtksession.c44
-rw-r--r--help.txt2
-rw-r--r--junklib.c4
-rw-r--r--main.c641
-rw-r--r--playback.h33
-rw-r--r--playlist.c552
-rw-r--r--playlist.h72
-rw-r--r--plugins.c278
-rw-r--r--plugins.h30
-rw-r--r--plugins/adplug/Makefile.am71
-rw-r--r--plugins/adplug/adplug-db.cpp251
-rw-r--r--plugins/adplug/adplug/COPYING509
-rw-r--r--plugins/adplug/adplug/a2m.cpp483
-rw-r--r--plugins/adplug/adplug/a2m.h83
-rw-r--r--plugins/adplug/adplug/adl.cpp2431
-rw-r--r--plugins/adplug/adplug/adl.h65
-rw-r--r--plugins/adplug/adplug/adlibemu.c600
-rw-r--r--plugins/adplug/adplug/adlibemu.h26
-rw-r--r--plugins/adplug/adplug/adplug.cpp182
-rw-r--r--plugins/adplug/adplug/adplug.h55
-rw-r--r--plugins/adplug/adplug/adtrack.cpp176
-rw-r--r--plugins/adplug/adplug/adtrack.h53
-rw-r--r--plugins/adplug/adplug/amd.cpp193
-rw-r--r--plugins/adplug/adplug/amd.h49
-rw-r--r--plugins/adplug/adplug/analopl.cpp67
-rw-r--r--plugins/adplug/adplug/analopl.h44
-rw-r--r--plugins/adplug/adplug/bam.cpp203
-rw-r--r--plugins/adplug/adplug/bam.h56
-rw-r--r--plugins/adplug/adplug/bmf.cpp597
-rw-r--r--plugins/adplug/adplug/bmf.h91
-rw-r--r--plugins/adplug/adplug/cff.cpp508
-rw-r--r--plugins/adplug/adplug/cff.h103
-rw-r--r--plugins/adplug/adplug/d00.cpp545
-rw-r--r--plugins/adplug/adplug/d00.h109
-rw-r--r--plugins/adplug/adplug/database.cpp426
-rw-r--r--plugins/adplug/adplug/database.h169
-rw-r--r--plugins/adplug/adplug/debug.c57
-rw-r--r--plugins/adplug/adplug/debug.h41
-rw-r--r--plugins/adplug/adplug/dfm.cpp115
-rw-r--r--plugins/adplug/adplug/dfm.h52
-rw-r--r--plugins/adplug/adplug/diskopl.cpp90
-rw-r--r--plugins/adplug/adplug/diskopl.h52
-rw-r--r--plugins/adplug/adplug/dmo.cpp403
-rw-r--r--plugins/adplug/adplug/dmo.h51
-rw-r--r--plugins/adplug/adplug/dro.cpp130
-rw-r--r--plugins/adplug/adplug/dro.h52
-rw-r--r--plugins/adplug/adplug/dtm.cpp317
-rw-r--r--plugins/adplug/adplug/dtm.h69
-rw-r--r--plugins/adplug/adplug/emuopl.cpp147
-rw-r--r--plugins/adplug/adplug/emuopl.h49
-rw-r--r--plugins/adplug/adplug/flash.cpp230
-rw-r--r--plugins/adplug/adplug/flash.h57
-rw-r--r--plugins/adplug/adplug/fmc.cpp224
-rw-r--r--plugins/adplug/adplug/fmc.h91
-rw-r--r--plugins/adplug/adplug/fmopl.c1390
-rw-r--r--plugins/adplug/adplug/fmopl.h174
-rw-r--r--plugins/adplug/adplug/fprovide.cpp76
-rw-r--r--plugins/adplug/adplug/fprovide.h50
-rw-r--r--plugins/adplug/adplug/hsc.cpp317
-rw-r--r--plugins/adplug/adplug/hsc.h75
-rw-r--r--plugins/adplug/adplug/hsp.cpp68
-rw-r--r--plugins/adplug/adplug/hsp.h39
-rw-r--r--plugins/adplug/adplug/hybrid.cpp248
-rw-r--r--plugins/adplug/adplug/hybrid.h80
-rw-r--r--plugins/adplug/adplug/hyp.cpp125
-rw-r--r--plugins/adplug/adplug/hyp.h53
-rw-r--r--plugins/adplug/adplug/imf.cpp196
-rw-r--r--plugins/adplug/adplug/imf.h68
-rw-r--r--plugins/adplug/adplug/kemuopl.h61
-rw-r--r--plugins/adplug/adplug/ksm.cpp336
-rw-r--r--plugins/adplug/adplug/ksm.h62
-rw-r--r--plugins/adplug/adplug/lds.cpp676
-rw-r--r--plugins/adplug/adplug/lds.h91
-rw-r--r--plugins/adplug/adplug/mad.cpp127
-rw-r--r--plugins/adplug/adplug/mad.h48
-rw-r--r--plugins/adplug/adplug/mid.cpp1090
-rw-r--r--plugins/adplug/adplug/mid.h112
-rw-r--r--plugins/adplug/adplug/mididata.h174
-rw-r--r--plugins/adplug/adplug/mkj.cpp164
-rw-r--r--plugins/adplug/adplug/mkj.h50
-rw-r--r--plugins/adplug/adplug/msc.cpp313
-rw-r--r--plugins/adplug/adplug/msc.h87
-rw-r--r--plugins/adplug/adplug/mtk.cpp141
-rw-r--r--plugins/adplug/adplug/mtk.h50
-rw-r--r--plugins/adplug/adplug/opl.h69
-rw-r--r--plugins/adplug/adplug/player.cpp70
-rw-r--r--plugins/adplug/adplug/player.h83
-rw-r--r--plugins/adplug/adplug/players.cpp105
-rw-r--r--plugins/adplug/adplug/players.h60
-rw-r--r--plugins/adplug/adplug/protrack.cpp820
-rw-r--r--plugins/adplug/adplug/protrack.h119
-rw-r--r--plugins/adplug/adplug/psi.cpp177
-rw-r--r--plugins/adplug/adplug/psi.h64
-rw-r--r--plugins/adplug/adplug/rad.cpp125
-rw-r--r--plugins/adplug/adplug/rad.h44
-rw-r--r--plugins/adplug/adplug/rat.cpp293
-rw-r--r--plugins/adplug/adplug/rat.h121
-rw-r--r--plugins/adplug/adplug/raw.cpp104
-rw-r--r--plugins/adplug/adplug/raw.h52
-rw-r--r--plugins/adplug/adplug/realopl.cpp208
-rw-r--r--plugins/adplug/adplug/realopl.h72
-rw-r--r--plugins/adplug/adplug/rix.cpp495
-rw-r--r--plugins/adplug/adplug/rix.h112
-rw-r--r--plugins/adplug/adplug/rol.cpp723
-rw-r--r--plugins/adplug/adplug/rol.h309
-rw-r--r--plugins/adplug/adplug/s3m.cpp536
-rw-r--r--plugins/adplug/adplug/s3m.h107
-rw-r--r--plugins/adplug/adplug/sa2.cpp262
-rw-r--r--plugins/adplug/adplug/sa2.h55
-rw-r--r--plugins/adplug/adplug/silentopl.h29
-rw-r--r--plugins/adplug/adplug/sng.cpp85
-rw-r--r--plugins/adplug/adplug/sng.h64
-rw-r--r--plugins/adplug/adplug/temuopl.cpp75
-rw-r--r--plugins/adplug/adplug/temuopl.h47
-rw-r--r--plugins/adplug/adplug/u6m.cpp934
-rw-r--r--plugins/adplug/adplug/u6m.h168
-rw-r--r--plugins/adplug/adplug/xad.cpp140
-rw-r--r--plugins/adplug/adplug/xad.h97
-rw-r--r--plugins/adplug/adplug/xsm.cpp117
-rw-r--r--plugins/adplug/adplug/xsm.h46
-rw-r--r--plugins/adplug/libbinio/COPYING510
-rw-r--r--plugins/adplug/libbinio/binfile.cpp247
-rw-r--r--plugins/adplug/libbinio/binfile.h110
-rw-r--r--plugins/adplug/libbinio/binio.cpp640
-rw-r--r--plugins/adplug/libbinio/binio.h175
-rw-r--r--plugins/adplug/libbinio/binstr.cpp114
-rw-r--r--plugins/adplug/libbinio/binstr.h66
-rw-r--r--plugins/adplug/libbinio/binwrap.cpp132
-rw-r--r--plugins/adplug/libbinio/binwrap.h92
-rw-r--r--plugins/adplug/plugin.c68
-rw-r--r--plugins/alsa/Makefile.am9
-rw-r--r--plugins/alsa/alsa.c (renamed from palsa.c)199
-rw-r--r--plugins/cdda/cdda.c58
-rw-r--r--plugins/ffap/ffap.c74
-rw-r--r--plugins/ffmpeg/Makefile.am9
-rw-r--r--plugins/ffmpeg/ffmpeg.c593
-rw-r--r--plugins/flac/flac.c23
-rw-r--r--plugins/gtkui/Makefile.am17
-rw-r--r--plugins/gtkui/callbacks.c (renamed from callbacks.c)990
-rw-r--r--plugins/gtkui/callbacks.h (renamed from callbacks.h)67
-rw-r--r--plugins/gtkui/deadbeef.glade (renamed from deadbeef.glade)499
-rw-r--r--plugins/gtkui/deadbeef.gladep (renamed from deadbeef.gladep)2
-rw-r--r--plugins/gtkui/drawing.h (renamed from drawing.h)0
-rw-r--r--plugins/gtkui/fileman.c67
-rw-r--r--plugins/gtkui/gdkdrawing.c (renamed from gdkdrawing.c)0
-rw-r--r--plugins/gtkui/gtkplaylist.c (renamed from gtkplaylist.c)1201
-rw-r--r--plugins/gtkui/gtkplaylist.h (renamed from gtkplaylist.h)56
-rw-r--r--plugins/gtkui/gtkui.c518
-rw-r--r--plugins/gtkui/gtkui.h20
-rw-r--r--plugins/gtkui/interface.c (renamed from interface.c)446
-rw-r--r--plugins/gtkui/interface.h (renamed from interface.h)1
-rw-r--r--plugins/gtkui/parser.c107
-rw-r--r--plugins/gtkui/parser.h (renamed from palsa.h)47
-rw-r--r--plugins/gtkui/progress.c (renamed from progress.c)0
-rw-r--r--plugins/gtkui/progress.h (renamed from progress.h)0
-rw-r--r--plugins/gtkui/search.c (renamed from search.c)62
-rw-r--r--plugins/gtkui/search.h (renamed from search.h)0
-rw-r--r--plugins/gtkui/support.c (renamed from support.c)0
-rw-r--r--plugins/gtkui/support.h (renamed from support.h)0
-rw-r--r--plugins/hotkeys/hotkeys.c164
-rw-r--r--plugins/lastfm/lastfm.c93
-rw-r--r--plugins/mpgmad/mpgmad.c2
-rw-r--r--plugins/nullout/Makefile.am5
-rw-r--r--plugins/nullout/nullout.c242
-rw-r--r--plugins/sndfile/sndfile.c87
-rw-r--r--plugins/vfs_curl/vfs_curl.c37
-rw-r--r--plugins/vorbis/vorbis.c7
-rw-r--r--plugins/vtx/Makefile.am6
-rw-r--r--plugins/vtx/ay8912.c498
-rw-r--r--plugins/vtx/ayemu.h84
-rw-r--r--plugins/vtx/ayemu_8912.h156
-rw-r--r--plugins/vtx/ayemu_vtxfile.h73
-rw-r--r--plugins/vtx/lh5dec.c304
-rw-r--r--plugins/vtx/vtx.c308
-rw-r--r--plugins/vtx/vtxfile.c283
-rw-r--r--plugins/wavpack/wavpack.c4
-rw-r--r--session.h7
-rw-r--r--streamer.c141
-rw-r--r--streamer.h18
-rw-r--r--threading.h2
-rw-r--r--threading_pthread.c2
-rw-r--r--timeline.c4
-rw-r--r--vfs.c4
-rw-r--r--web/index.html2
193 files changed, 33609 insertions, 2189 deletions
diff --git a/Makefile.am b/Makefile.am
index 0bf99b0e..9079ff2d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,6 +1,3 @@
-## Process this file with automake to produce Makefile.in
-#ACLOCAL_AMFLAGS = -I m4
-
SUBDIRS = gme/Game_Music_Emu-0.5.2\
gme/Game_Music_Emu-0.5.2/gme\
sid/sidplay-libs-2.1.0\
@@ -9,6 +6,10 @@ SUBDIRS = gme/Game_Music_Emu-0.5.2\
icons\
plugins/hotkeys\
plugins/ffap\
+ plugins/nullout\
+ plugins/vtx\
+ plugins/adplug\
+ ${ALSA_DIR}\
${LFM_DIR}\
${MPGMAD_DIR}\
${VORBIS_DIR}\
@@ -16,7 +17,9 @@ SUBDIRS = gme/Game_Music_Emu-0.5.2\
${WAVPACK_DIR}\
${SNDFILE_DIR}\
${VFS_CURL_DIR}\
- ${CDDA_DIR}
+ ${CDDA_DIR}\
+ ${GTKUI_DIR}\
+ ${FFMPEG_DIR}
dumbpath=@top_srcdir@/dumb
sidpath=@top_srcdir@/sid/sidplay-libs-2.1.0
@@ -27,21 +30,17 @@ bin_PROGRAMS = deadbeef
deadbeef_SOURCES =\
main.c common.h deadbeef.h\
plugins.c plugins.h moduleconf.h\
- callbacks.c interface.c support.c callbacks.h interface.h support.h\
- playlist.c playlist.h gtkplaylist.c gtkplaylist.h\
+ playlist.c playlist.h \
streamer.c streamer.h\
- progress.c progress.h\
codec.c codec.h\
messagepump.c messagepump.h\
conf.c conf.h\
- search.c search.h\
cgme.c cdumb.c csid.cpp\
- palsa.c palsa.h playback.h\
+ playback.h\
threading_pthread.c threading.h\
md5/md5.c md5/md5.h md5/md5_loc.h\
volume.c volume.h\
- drawing.h gdkdrawing.c\
- session.h session.c gtksession.c\
+ session.h session.c \
junklib.h junklib.c utf8.c utf8.h\
optmath.h\
vfs.c vfs.h vfs_stdio.c\
diff --git a/about.txt b/about.txt
index 976413c1..21d1e5c7 100644
--- a/about.txt
+++ b/about.txt
@@ -59,3 +59,11 @@ Copyright © 1998 - 2006 Conifer Software
libsndfile - PCM read/write/conversion library
Copyright © 1999-2009 Erik de Castro Lopo <erikd@mega-nerd.com>
+
+
+libayemu - AY/YM sound chip emulator and music file loader
+Copyright © 2003-2004 Alexander Sashnov <sashnov@ngs.ru>
+
+
+adplug - Replayer for many OPL2/OPL3 audio file formats.
+Copyright © 1999 - 2006 Simon Peter <dn.tlp@gmx.net>, et al.
diff --git a/cdumb.c b/cdumb.c
index 0aaaec71..4d9659b2 100644
--- a/cdumb.c
+++ b/cdumb.c
@@ -100,7 +100,7 @@ cdumb_init (DB_playItem_t *it) {
plugin.info.bps = 16;
plugin.info.channels = 2;
- plugin.info.samplerate = deadbeef->playback_get_samplerate ();
+ plugin.info.samplerate = deadbeef->get_output ()->samplerate ();
plugin.info.readpos = 0;
if (cdumb_startrenderer () < 0) {
@@ -775,7 +775,15 @@ cdumb_insert (DB_playItem_t *after, const char *fname) {
it->fname = strdup (fname);
DUMB_IT_SIGDATA * itsd = duh_get_it_sigdata(duh);
if (itsd->name[0]) {
- deadbeef->pl_add_meta (it, "title", convstr ((char*)&itsd->name, sizeof(itsd->name)));
+ int tl = sizeof(itsd->name);
+ int i;
+ for (i = 0; i < tl && itsd->name[i] && itsd->name[i] == ' '; i++);
+ if (i == tl || !itsd->name[i]) {
+ deadbeef->pl_add_meta (it, "title", NULL);
+ }
+ else {
+ deadbeef->pl_add_meta (it, "title", convstr ((char*)&itsd->name, sizeof(itsd->name)));
+ }
}
else {
deadbeef->pl_add_meta (it, "title", NULL);
diff --git a/cgme.c b/cgme.c
index 21aeb292..d291bafe 100644
--- a/cgme.c
+++ b/cgme.c
@@ -32,7 +32,8 @@ static float duration; // of current song
static int
cgme_init (DB_playItem_t *it) {
- if (gme_open_file (it->fname, &emu, deadbeef->playback_get_samplerate ())) {
+ int samplerate = deadbeef->get_output ()->samplerate ();
+ if (gme_open_file (it->fname, &emu, samplerate)) {
return -1;
}
gme_mute_voices (emu, cgme_voicemask);
@@ -41,7 +42,7 @@ cgme_init (DB_playItem_t *it) {
gme_track_info (emu, &inf, it->tracknum);
plugin.info.bps = 16;
plugin.info.channels = 2;
- plugin.info.samplerate = deadbeef->playback_get_samplerate ();
+ plugin.info.samplerate = samplerate;
duration = deadbeef->pl_get_item_duration (it);
reallength = inf.length;
nzerosamples = 0;
@@ -127,7 +128,15 @@ cgme_insert (DB_playItem_t *after, const char *fname) {
// add metadata
deadbeef->pl_add_meta (it, "system", inf.system);
deadbeef->pl_add_meta (it, "album", inf.game);
- deadbeef->pl_add_meta (it, "title", inf.song);
+ int tl = sizeof (inf.song);
+ int i;
+ for (i = 0; i < tl && inf.song[i] && inf.song[i] == ' '; i++);
+ if (i == tl || !inf.song[i]) {
+ deadbeef->pl_add_meta (it, "title", NULL);
+ }
+ else {
+ deadbeef->pl_add_meta (it, "title", inf.song);
+ }
deadbeef->pl_add_meta (it, "artist", inf.author);
deadbeef->pl_add_meta (it, "copyright", inf.copyright);
deadbeef->pl_add_meta (it, "comment", inf.comment);
diff --git a/configure.ac b/configure.ac
index 24fe84d6..807f6c8b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,7 +3,7 @@ AC_INIT
AC_CONFIG_HEADER(config.h)
PACKAGE="deadbeef"
-VERSION="0.3.1"
+VERSION="guiplug-snap"
AM_INIT_AUTOMAKE($PACKAGE,$VERSION)
@@ -12,7 +12,6 @@ AC_PROG_CXX
AC_STDC_HEADERS
AC_PROG_INSTALL
AC_PROG_LIBTOOL
-LT_INIT
AC_CONFIG_MACRO_DIR([m4])
AC_C_BIGENDIAN
@@ -39,7 +38,12 @@ test "x$prefix" = xNONE && prefix=$ac_default_prefix
CFLAGS="$CFLAGS -D_GNU_SOURCE -DLIBDIR=\\\"$libdir\\\" -DPREFIX=\\\"$prefix\\\""
CPPFLAGS="$CFLAGS"
-PKG_CHECK_MODULES(DEPS, gtk+-2.0 >= 2.12 gthread-2.0 glib-2.0 samplerate alsa)
+PKG_CHECK_MODULES(DEPS, samplerate)
+PKG_CHECK_MODULES(GTKUI_DEPS, gtk+-2.0 >= 2.12 gthread-2.0 glib-2.0, HAVE_GTK=1, NO_GTK=1)
+PKG_CHECK_MODULES(ALSA_DEPS, alsa, HAVE_ALSA=1, NO_ALSA=1)
+PKG_CHECK_MODULES(FFMPEG_DEPS, libavcodec libavutil libavformat >= 52.0.0, HAVE_FFMPEG=1, NO_FFMPEG=1)
+AC_CHECK_LIB([pthread], [main])
+AC_CHECK_LIB([dl], [main])
AX_CHECK_COMPILER_FLAGS(-msse2, HAVE_SSE2=1, [])
if test ${HAVE_SSE2}; then
@@ -143,6 +147,31 @@ if test ${HAVE_CDIO} && test ${HAVE_CDDB}; then
AC_SUBST(CDDA_DIR)
fi
+dnl gtkui plugin
+AM_CONDITIONAL(HAVE_GTK, test $HAVE_GTK)
+if test ${HAVE_GTK}; then
+ GTKUI_DIR="plugins/gtkui"
+ AC_SUBST(GTKUI_DEPS_CFLAGS)
+ AC_SUBST(GTKUI_DEPS_LIBS)
+ AC_SUBST(GTKUI_DIR)
+fi
+
+AM_CONDITIONAL(HAVE_ALSA, test $HAVE_ALSA)
+if test ${HAVE_ALSA}; then
+ ALSA_DIR="plugins/alsa"
+ AC_SUBST(ALSA_DEPS_CFLAGS)
+ AC_SUBST(ALSA_DEPS_LIBS)
+ AC_SUBST(ALSA_DIR)
+fi
+
+AM_CONDITIONAL(HAVE_FFMPEG, test $HAVE_FFMPEG)
+if test ${HAVE_FFMPEG}; then
+ FFMPEG_DIR="plugins/ffmpeg"
+ AC_SUBST(FFMPEG_DEPS_CFLAGS)
+ AC_SUBST(FFMPEG_DEPS_LIBS)
+ AC_SUBST(FFMPEG_DIR)
+fi
+
dnl print summary
echo
echo "plugin summary:"
@@ -159,6 +188,8 @@ AC_DEFUN([PRINT_PLUGIN_INFO],
)
PRINT_PLUGIN_INFO([stdio],[Standard IO plugin],[true])
+PRINT_PLUGIN_INFO([nullout],[NULL output],[true])
+PRINT_PLUGIN_INFO([alsa],[ALSA output],[test $HAVE_ALSA])
PRINT_PLUGIN_INFO([sid],[SID player based on libsidplay2],[true])
PRINT_PLUGIN_INFO([gme],[chiptune music player based on GME],[true])
PRINT_PLUGIN_INFO([dumb],[module player based on DUMB library],[true])
@@ -169,9 +200,13 @@ PRINT_PLUGIN_INFO([vorbis],[ogg vorbis player],[test $HAVE_VORBISFILE && test $H
PRINT_PLUGIN_INFO([flac],[flac player],[test $HAVE_FLAC])
PRINT_PLUGIN_INFO([wavpack],[wavpack player],[test $HAVE_WAVPACK])
PRINT_PLUGIN_INFO([sndfile],[PCM (wav,aiff,etc) player based on libsndfile],[test $HAVE_SNDFILE])
+PRINT_PLUGIN_INFO([vtx],[vtx file player (ay8910/12 emulation)],[true])
+PRINT_PLUGIN_INFO([adplug],[adplug player (OPL2/OPL3 emulation)],[true])
PRINT_PLUGIN_INFO([vfs_curl],[http/ftp streaming support],[test $HAVE_CURL])
dnl PRINT_PLUGIN_INFO([faad2],[aac/mp4 player],[test $HAVE_FAAD && test $HAVE_MP4FF])
PRINT_PLUGIN_INFO([cdda],[cd audio player],[test $HAVE_CDIO && test $HAVE_CDDB])
+PRINT_PLUGIN_INFO([gtkui],[GTK user interface],[test $HAVE_GTK])
+PRINT_PLUGIN_INFO([ffmpeg],[ffmpeg codecs],[test $HAVE_FFMPEG])
echo
AC_OUTPUT([
@@ -182,6 +217,7 @@ gme/Game_Music_Emu-0.5.2/Makefile
gme/Game_Music_Emu-0.5.2/gme/Makefile
sid/sidplay-libs-2.1.0/Makefile
dumb/Makefile
+plugins/alsa/Makefile
plugins/hotkeys/Makefile
plugins/lastfm/Makefile
plugins/ffap/Makefile
@@ -192,6 +228,11 @@ plugins/wavpack/Makefile
plugins/sndfile/Makefile
plugins/vfs_curl/Makefile
plugins/cdda/Makefile
+plugins/gtkui/Makefile
+plugins/nullout/Makefile
+plugins/vtx/Makefile
+plugins/adplug/Makefile
+plugins/ffmpeg/Makefile
deadbeef.desktop
])
diff --git a/csid.cpp b/csid.cpp
index 76e6b43e..9051a1df 100644
--- a/csid.cpp
+++ b/csid.cpp
@@ -52,6 +52,11 @@ extern "C" {
static const char *exts[] = { "sid",NULL };
const char *filetypes[] = { "SID", NULL };
+static const char settings_dlg[] =
+ "property \"Enable HVSC\" checkbox hvsc_enable 0;\n"
+ "property \"HVSC path\" file hvsc_path \"\";\n"
+;
+
// define plugin interface
static DB_decoder_t plugin = {
{ // plugin
@@ -62,6 +67,7 @@ static DB_decoder_t plugin = {
/* .plugin.version_major = */0,
/* .plugin.version_minor = */1,
/* .inactive = */0,
+ /* .plugin.nostop = */0,
/* .plugin.name = */"SID decoder",
/* .plugin.descr = */"SID player based on libsidplay2",
/* .plugin.author = */"Alexey Yakovenko",
@@ -70,6 +76,7 @@ static DB_decoder_t plugin = {
/* .plugin.start = */NULL,
/* .plugin.stop = */csid_stop,
/* .plugin.exec_cmdline = */NULL,
+ /* .plugin.configdialog = */settings_dlg,
},
{ // info
/* .info.bps = */0,
@@ -336,7 +343,7 @@ csid_init (DB_playItem_t *it) {
resid->create (sidplay->info ().maxsids);
// resid->create (1);
resid->filter (true);
- resid->sampling (deadbeef->playback_get_samplerate ());
+ resid->sampling (deadbeef->get_output ()->samplerate ());
duration = deadbeef->pl_get_item_duration (it);
tune = new SidTune (it->fname);
@@ -344,7 +351,7 @@ csid_init (DB_playItem_t *it) {
plugin.info.channels = tune->isStereo () ? 2 : 1;
sid2_config_t conf;
conf = sidplay->config ();
- conf.frequency = deadbeef->playback_get_samplerate ();
+ conf.frequency = deadbeef->get_output ()->samplerate ();
conf.precision = 16;
conf.playback = plugin.info.channels == 2 ? sid2_stereo : sid2_mono;
conf.sidEmulation = resid;
diff --git a/deadbeef.h b/deadbeef.h
index 8dfa1296..96f54c76 100644
--- a/deadbeef.h
+++ b/deadbeef.h
@@ -68,6 +68,11 @@ extern "C" {
////////////////////////////
// playlist structures
+// iterators
+// that's a good candidate for redesign
+#define PL_MAIN 0
+#define PL_SEARCH 1
+
// playlist item
// these are "public" fields, available to plugins
typedef struct {
@@ -95,15 +100,48 @@ enum {
DB_PLUGIN_VFS = 5,
};
+// output plugin states
+enum output_state_t {
+ OUTPUT_STATE_STOPPED = 0,
+ OUTPUT_STATE_PLAYING = 1,
+ OUTPUT_STATE_PAUSED = 2,
+};
+
+// playback order
+enum playback_order_t {
+ PLAYBACK_ORDER_LINEAR = 0,
+ PLAYBACK_ORDER_SHUFFLE = 1,
+ PLAYBACK_ORDER_RANDOM = 2,
+};
+
+// playback modes
+enum playback_mode_t {
+ PLAYBACK_MODE_LOOP_ALL = 0, // loop playlist
+ PLAYBACK_MODE_NOLOOP = 1, // don't loop
+ PLAYBACK_MODE_LOOP_SINGLE = 2, // loop single track
+};
+
typedef struct {
int event;
- double time;
+ time_t time;
} DB_event_t;
typedef struct {
DB_event_t ev;
- DB_playItem_t *song;
-} DB_event_song_t;
+ int index;
+ DB_playItem_t *track;
+} DB_event_track_t;
+
+typedef struct {
+ DB_event_t ev;
+ int from;
+ int to;
+} DB_event_trackchange_t;
+
+typedef struct {
+ DB_event_t ev;
+ int state;
+} DB_event_state_t;
typedef struct DB_conf_item_s {
char *key;
@@ -122,11 +160,17 @@ enum {
DB_EV_SONGFINISHED = 3, // triggers when song finished playing (for scrobblers and such)
DB_EV_TRACKDELETED = 4, // triggers when track is to be deleted from playlist
DB_EV_CONFIGCHANGED = 5, // configuration option changed
+ DB_EV_ACTIVATE = 6, // will be fired every time player is activated
+ DB_EV_TRACKINFOCHANGED = 7, // notify plugins that trackinfo was changed
+ DB_EV_PAUSED = 8, // player was paused or unpaused
+ DB_EV_PLAYLISTCHANGED = 9, // playlist contents were changed
+ DB_EV_VOLUMECHANGED = 10, // volume was changed
+ DB_EV_OUTPUTCHANGED = 11, // sound output plugin changed
DB_EV_MAX
};
// preset columns, working using IDs
-enum {
+enum pl_column_t {
DB_COLUMN_PLAYING = 1,
DB_COLUMN_ARTIST_ALBUM = 2,
DB_COLUMN_ARTIST = 3,
@@ -147,11 +191,6 @@ enum {
M_PAUSESONG,
M_PLAYRANDOM,
M_SONGCHANGED, // p1=from, p2=to
- M_ADDDIR, // ctx = pointer to string, which must be freed by g_free
- M_ADDFILES, // ctx = GSList pointer, must be freed with g_slist_free
- M_ADDDIRS, // ctx = GSList pointer, must be freed with g_slist_free
- M_OPENFILES, // ctx = GSList pointer, must be freed with g_slist_free
- M_FMDRAGDROP, // ctx = char* ptr, must be freed with standard free, p1 is length of data, p2 is drop_y
M_TERMINATE, // must be sent to player thread to terminate
M_PLAYLISTREFRESH,
M_REINIT_SOUND,
@@ -185,6 +224,7 @@ typedef struct {
void (*md5) (uint8_t sig[16], const char *in, int len);
void (*md5_to_str) (char *str, const uint8_t sig[16]);
// playback control
+ struct DB_output_s* (*get_output) (void);
void (*playback_next) (void);
void (*playback_prev) (void);
void (*playback_pause) (void);
@@ -193,13 +233,22 @@ typedef struct {
void (*playback_random) (void);
float (*playback_get_pos) (void); // [0..100]
void (*playback_set_pos) (float pos); // [0..100]
- int (*playback_get_samplerate) (void); // output samplerate
- void (*playback_update_bitrate) (float bitrate);
+ // streamer access
+ // FIXME: needs to be thread-safe
+ DB_playItem_t *(*streamer_get_playing_track) (void);
+ DB_playItem_t *(*streamer_get_streaming_track) (void);
+ float (*streamer_get_playpos) (void);
+ void (*streamer_seek) (float time);
+ int (*streamer_ok_to_read) (int len);
+ void (*streamer_reset) (int full);
+ int (*streamer_read) (char *bytes, int size);
+ void (*streamer_set_bitrate) (int bitrate);
+ int (*streamer_get_apx_bitrate) (void);
// process control
const char *(*get_config_dir) (void);
void (*quit) (void);
// threading
- intptr_t (*thread_start) (void (*fn)(uintptr_t ctx), uintptr_t ctx);
+ intptr_t (*thread_start) (void (*fn)(void *ctx), void *ctx);
int (*thread_join) (intptr_t tid);
uintptr_t (*mutex_create) (void);
void (*mutex_free) (uintptr_t mtx);
@@ -214,14 +263,70 @@ typedef struct {
DB_playItem_t * (*pl_item_alloc) (void);
void (*pl_item_free) (DB_playItem_t *it);
void (*pl_item_copy) (DB_playItem_t *out, DB_playItem_t *in);
+ int (*pl_add_file) (const char *fname, int (*cb)(DB_playItem_t *it, void *data), void *user_data);
+ int (*pl_add_dir) (const char *dirname, int (*cb)(DB_playItem_t *it, void *data), void *user_data);
DB_playItem_t *(*pl_insert_item) (DB_playItem_t *after, DB_playItem_t *it);
+ DB_playItem_t *(*pl_insert_dir) (DB_playItem_t *after, const char *dirname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data);
+ DB_playItem_t *(*pl_insert_file) (DB_playItem_t *after, const char *fname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data);
int (*pl_get_idx_of) (DB_playItem_t *it);
+ DB_playItem_t * (*pl_get_for_idx) (int idx);
+ DB_playItem_t * (*pl_get_for_idx_and_iter) (int idx, int iter);
+ float (*pl_get_totaltime) (void);
+ int (*pl_getcount) (void);
+ DB_playItem_t *(*pl_getcurrent) (void);
+ int (*pl_delete_selected) (void);
+ void (*pl_set_cursor) (int iter, int cursor);
+ int (*pl_get_cursor) (int iter);
+ void (*pl_set_selected) (DB_playItem_t *it, int sel);
+ int (*pl_is_selected) (DB_playItem_t *it);
+ void (*pl_free) (void);
+ int (*pl_load) (const char *name);
+ int (*pl_save) (const char *name);
+ void (*pl_select_all) (void);
+ void (*pl_crop_selected) (void);
+ int (*pl_getselcount) (void);
+ DB_playItem_t *(*pl_get_first) (int iter);
+ DB_playItem_t *(*pl_get_last) (int iter);
+ DB_playItem_t *(*pl_get_next) (DB_playItem_t *it, int iter);
+ DB_playItem_t *(*pl_get_prev) (DB_playItem_t *it, int iter);
+ /*
+ this function formats line for display in playlist
+ @it pointer to playlist item
+ @s output buffer
+ @size size of output buffer
+ @id one of IDs defined in pl_column_id_t enum, can be -1
+ @fmt format string, used if id is -1
+ format is printf-alike. specification:
+ %a artist
+ %t title
+ %b album
+ %n track
+ %l length (duration)
+ more to come
+ */
+ int (*pl_format_title) (DB_playItem_t *it, char *s, int size, int id, const char *fmt);
+ void (*pl_format_item_display_name) (DB_playItem_t *it, char *str, int len);
+// void (*pl_set_next) (DB_playItem_t *it, DB_playItem_t *next, int iter);
+// void (*pl_set_prev) (DB_playItem_t *it, DB_playItem_t *prev, int iter);
+// void (*pl_set_head) (DB_playItem_t *it, int iter);
+// void (*pl_set_tail) (DB_playItem_t *it, int iter);
+// DB_playItem_t* (*pl_get_head) (void);
+// DB_playItem_t* (*pl_get_tail) (void);
+ void (*pl_move_items) (int iter, DB_playItem_t *drop_before, uint32_t *indexes, int count);
+ int (*pl_process_search) (const char *text);
// metainfo
void (*pl_add_meta) (DB_playItem_t *it, const char *key, const char *value);
const char *(*pl_find_meta) (DB_playItem_t *song, const char *meta);
void (*pl_delete_all_meta) (DB_playItem_t *it);
void (*pl_set_item_duration) (DB_playItem_t *it, float duration);
float (*pl_get_item_duration) (DB_playItem_t *it);
+ void (*pl_sort) (int iter, int id, const char *format, int ascending);
+ // playqueue support
+ int (*pl_playqueue_push) (DB_playItem_t *it);
+ void (*pl_playqueue_clear) (void);
+ void (*pl_playqueue_pop) (void);
+ void (*pl_playqueue_remove) (DB_playItem_t *it);
+ int (*pl_playqueue_test) (DB_playItem_t *it);
// cuesheet support
DB_playItem_t *(*pl_insert_cue_from_buffer) (DB_playItem_t *after, const char *fname, const uint8_t *buffer, int buffersize, struct DB_decoder_s *decoder, const char *ftype, int numsamples, int samplerate);
DB_playItem_t * (*pl_insert_cue) (DB_playItem_t *after, const char *filename, struct DB_decoder_s *decoder, const char *ftype, int numsamples, int samplerate);
@@ -230,6 +335,7 @@ typedef struct {
float (*volume_get_db) (void);
void (*volume_set_amp) (float amp);
float (*volume_get_amp) (void);
+ float (*volume_get_min_db) (void);
// junk reading
int (*junk_read_id3v1) (DB_playItem_t *it, DB_FILE *fp);
int (*junk_read_id3v2) (DB_playItem_t *it, DB_FILE *fp);
@@ -254,10 +360,14 @@ typedef struct {
float (*conf_get_float) (const char *key, float def);
int (*conf_get_int) (const char *key, int def);
void (*conf_set_str) (const char *key, const char *val);
+ void (*conf_set_int) (const char *key, int val);
DB_conf_item_t * (*conf_find) (const char *group, DB_conf_item_t *prev);
- // gui locking
- void (*gui_lock) (void);
- void (*gui_unlock) (void);
+ void (*conf_remove_items) (const char *key);
+ // plugin communication
+ struct DB_decoder_s **(*plug_get_decoder_list) (void);
+ struct DB_output_s **(*plug_get_output_list) (void);
+ struct DB_plugin_s **(*plug_get_list) (void);
+ int (*plug_activate) (struct DB_plugin_s *p, int activate);
// exporting plugin conf options for gui
// all exported options are grouped by plugin, and will be available to user
// from gui
@@ -281,6 +391,8 @@ typedef struct DB_plugin_s {
int16_t version_minor;
// may be deactivated on failures after load
int inactive;
+ // prevent plugin from being dynamically stopped
+ int nostop;
// any of those can be left NULL
// though it's much better to fill them with something useful
const char *name;
@@ -297,6 +409,9 @@ typedef struct DB_plugin_s {
// cmdline is 0-separated list of strings, guaranteed to have 0 at the end
// cmdline_size is number of bytes pointed by cmdline
int (*exec_cmdline) (const char *cmdline, int cmdline_size);
+ // plugin configuration dialog is constructed from this data
+ // can be NULL
+ const char *configdialog;
} DB_plugin_t;
typedef struct {
@@ -352,19 +467,21 @@ typedef struct DB_decoder_s {
} DB_decoder_t;
// output plugin
-typedef struct {
+typedef struct DB_output_s {
DB_plugin_t plugin;
// init is called once at plugin activation
- int (*init) (void (*callback)(char *stream, int len));
+ int (*init) (void);
// free is called if output plugin was changed to another, or unload is about to happen
int (*free) (void);
+ // reconfigure output to another samplerate, if supported
+ int (*change_rate) (int rate);
// play, stop, pause, unpause are called by deadbeef in response to user
// events, or as part of streaming process
- // state must be 0 for stopped, 1 for playing and 2 for paused
int (*play) (void);
int (*stop) (void);
int (*pause) (void);
int (*unpause) (void);
+ // one of output_state_t enum values
int (*state) (void);
// following functions must return output sampling rate, bits per sample and number
// of channels
@@ -372,7 +489,9 @@ typedef struct {
int (*bitspersample) (void);
int (*channels) (void);
// must return 0 for little endian output, or 1 for big endian
- int (*endianess) (void);
+ int (*endianness) (void);
+ // soundcard enumeration (can be NULL)
+ void (*enum_soundcards) (void (*callback)(const char *name, const char *desc, void*), void *userdata);
} DB_output_t;
// dsp plugin
@@ -412,6 +531,13 @@ typedef struct DB_vfs_s {
unsigned streaming : 1;
} DB_vfs_t;
+// gui plugin
+// implements pretty much anything it wants
+// works mostly like misc plugin, except we need separate type for that
+typedef struct DB_gui_s {
+ DB_plugin_t plugin;
+} DB_gui_t;
+
#ifdef __cplusplus
}
#endif
diff --git a/decoder_template.c b/decoder_template.c
new file mode 100644
index 00000000..2f47582e
--- /dev/null
+++ b/decoder_template.c
@@ -0,0 +1,205 @@
+/*
+ DeaDBeeF - ultimate music player for GNU/Linux systems with X11
+ Copyright (C) 2009 Alexey Yakovenko
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+// this is a decoder plugin skeleton
+// use to create new decoder plugins
+
+#include "../../deadbeef.h"
+
+static DB_decoder_t plugin;
+static DB_functions_t *deadbeef;
+
+static const char * exts[] = { "example", NULL }; // e.g. mp3
+static const char *filetypes[] = { "example", NULL }; // e.g. MP3
+
+static int
+example_init (DB_playItem_t *it) {
+ // prepare to decode the track
+ // return -1 on failure
+
+ // fill in mandatory plugin fields
+ plugin.info.bps = deadbeef->get_output ()->bitspersample ();
+ plugin.info.channels = deadbeef->get_output ()->channels ();
+ plugin.info.samplerate = decoder->samplerate;
+ plugin.info.readpos = 0;
+
+ return 0;
+}
+
+static void
+example_free (void) {
+ // free everything allocated in _init
+}
+
+static int
+example_read_int16 (char *bytes, int size) {
+ // try decode `size' bytes
+ // return number of decoded bytes
+ // return 0 on EOF
+
+ plugin.info.readpos = (float)currentsample / plugin.info.samplerate;
+ return size;
+}
+
+static int
+example_seek_sample (int sample) {
+ // seek to specified sample (frame)
+ // return 0 on success
+ // return -1 on failure
+
+ // update readpos
+ plugin.info.readpos = (float)sample / plugin.info.samplerate;
+ return 0;
+}
+
+static int
+example_seek (float time) {
+ // seek to specified time in seconds
+ // return 0 on success
+ // return -1 on failure
+ return example_seek_sample (time * plugin.info.samplerate);
+ return 0;
+}
+
+static DB_playItem_t *
+example_insert (DB_playItem_t *after, const char *fname) {
+ // read information from the track
+ // load/process cuesheet if exists
+ // insert track into playlist
+ // return track pointer on success
+ // return NULL on failure
+
+example:
+
+ // open file
+ DB_FILE *fp = deadbeef->fopen (fname);
+ if (!fp) {
+ trace ("example: failed to fopen %s\n", fname);
+ return NULL;
+ }
+ // setup decoder to use vfs
+ decoder_callbacks_t cb = {
+ open = vfs_open_wrapper,
+ .... etc ....
+ }
+ decoder_info_t *di = decoder_open_callbacks (&cb);
+ if (!di) {
+ trace ("example: failed to init decoder\n");
+ return NULL;
+ }
+ // read track info/tags
+ track_info_t ti;
+ if (decoder_read_info (&ti) < 0) {
+ trace ("example: failed to read info\n");
+ decoder_free (di);
+ return NULL;
+ }
+
+ // now we should have track duration, and can try loading cuesheet
+ // 1st try embedded cuesheet
+ if (ti.embeddedcuesheet[0]) {
+ DB_playItem_t *cue = deadbeef->pl_insert_cue_from_buffer (after, fname, ti.embeddedcuesheet, strlen (ti.embeddedcuesheet), &plugin, plugin.filetypes[0], ti.total_num_samples, ti.samplerate);
+ if (cue) {
+ // cuesheet loaded
+ decoder_free (di);
+ return cue;
+ }
+ }
+
+ // embedded cuesheet not found, try external one
+ DB_playItem_t *cue = deadbeef->pl_insert_cue (after, fname, &plugin, plugin.filetypes[0], ti.total_num_samples, ti.samplerate);
+ if (cue) {
+ // cuesheet loaded
+ decoder_free (di);
+ return cue;
+ }
+
+ // no cuesheet, prepare track for addition
+ DB_playItem_t *it = deadbeef->pl_item_alloc ();
+ it->decoder = &plugin;
+ it->fname = strdup (fname);
+ it->filetype = filetypes[0];
+ deadbeef->pl_set_item_duration (it, (float)ti.total_num_samples/ti.samplerate);
+
+ // add metainfo
+ if (!strlen (ti.title)) {
+ // title is empty, this call will set track title to filename without extension
+ deadbeef->pl_add_meta (it, "title", NULL);
+ }
+ else {
+ deadbeef->pl_add_meta (it, "title", ti.title);
+ }
+ deadbeef->pl_add_meta (it, "artist", ti.artist);
+ // ... etc ...
+
+ // free decoder
+ decoder_free (di);
+
+ // now the track is ready, insert into playlist
+ after = deadbeef->pl_insert_item (after, it);
+ return after;
+}
+
+static int
+example_start (void) {
+ // do one-time plugin initialization here
+ // e.g. starting threads for background processing, subscribing to events, etc
+ // return 0 on success
+ // return -1 on failure
+ return 0;
+}
+static int
+example_stop (void) {
+ // undo everything done in _start here
+ // return 0 on success
+ // return -1 on failure
+ return 0;
+}
+
+// define plugin interface
+static DB_decoder_t plugin = {
+ DB_PLUGIN_SET_API_VERSION
+ .plugin.version_major = 0,
+ .plugin.version_minor = 1,
+ .plugin.type = DB_PLUGIN_DECODER,
+ .plugin.name = "short plugin name",
+ .plugin.descr = "plugin description",
+ .plugin.author = "author name",
+ .plugin.email = "author email",
+ .plugin.website = "author/plugin website",
+ .plugin.start = example_start,
+ .plugin.stop = example_stop,
+ .init = example_init,
+ .free = example_free,
+ .read_int16 = example_read_int16,
+// .read_float32 = example_read_float32,
+ .seek = example_seek,
+ .seek_sample = example_seek_sample,
+ .insert = example_insert,
+ .exts = exts,
+ .id = "example",
+ .filetypes = filetypes
+};
+
+DB_plugin_t *
+example_load (DB_functions_t *api) {
+ deadbeef = api;
+ return DB_PLUGIN (&plugin);
+}
+
diff --git a/dumb/dumb-kode54/src/it/reads3m.c b/dumb/dumb-kode54/src/it/reads3m.c
index 283becb1..0dac8331 100644
--- a/dumb/dumb-kode54/src/it/reads3m.c
+++ b/dumb/dumb-kode54/src/it/reads3m.c
@@ -61,7 +61,7 @@ static int it_s3m_read_sample_header(IT_SAMPLE *sample, long *offset, unsigned c
sample->name[28] = 0;
dumbfile_skip(f, 4);
sample->flags &= ~IT_SAMPLE_EXISTS;
- return dumbfile_error(f);
+ return -1; // return error so that another plugin could pick that file up
}
*offset = dumbfile_getc(f) << 20;
@@ -730,12 +730,15 @@ static DUMB_IT_SIGDATA *it_s3m_load_sigdata(DUMBFILE *f, int * cwtv)
break;
case S3M_COMPONENT_SAMPLE:
- if (it_s3m_read_sample_header(&sigdata->sample[component[n].n], &offset, &sample_pack[component[n].n], *cwtv, f)) {
+ {
+ int err = it_s3m_read_sample_header(&sigdata->sample[component[n].n], &offset, &sample_pack[component[n].n], *cwtv, f);
+ if (err) {
free(buffer);
free(component);
_dumb_it_unload_sigdata(sigdata);
return NULL;
}
+ }
if (sigdata->sample[component[n].n].flags & IT_SAMPLE_EXISTS) {
short *sample;
diff --git a/gtksession.c b/gtksession.c
deleted file mode 100644
index e24706b6..00000000
--- a/gtksession.c
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- DeaDBeeF - ultimate music player for GNU/Linux systems with X11
- Copyright (C) 2009 Alexey Yakovenko
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- of the License, or (at your option) any later version.
-
- This program 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 General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-*/
-#include <stdio.h>
-#include <gtk/gtk.h>
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-#include "session.h"
-#include "conf.h"
-
-void
-session_capture_window_attrs (uintptr_t window) {
- GtkWindow *wnd = GTK_WINDOW (window);
- int win_attrs[4];
- gtk_window_get_position (wnd, &win_attrs[0], &win_attrs[1]);
- gtk_window_get_size (wnd, &win_attrs[2], &win_attrs[3]);
- conf_set_int ("mainwin.geometry.x", win_attrs[0]);
- conf_set_int ("mainwin.geometry.y", win_attrs[1]);
- conf_set_int ("mainwin.geometry.w", win_attrs[2]);
- conf_set_int ("mainwin.geometry.h", win_attrs[3]);
-}
-
-void
-session_restore_window_attrs (uintptr_t window) {
- GtkWindow *wnd = GTK_WINDOW (window);
- gtk_window_move (wnd, conf_get_int ("mainwin.geometry.x", 40), conf_get_int ("mainwin.geometry.y", 40));
- gtk_window_resize (wnd, conf_get_int ("mainwin.geometry.w", 500), conf_get_int ("mainwin.geometry.h", 300));
-}
diff --git a/help.txt b/help.txt
index 449ae97d..96cea584 100644
--- a/help.txt
+++ b/help.txt
@@ -27,7 +27,7 @@ hotkeys.key8 XF86AudioLowerVolume: volume_down
full list of actions:
-toggle_pause, play, prev, next, stop, play_random, seek_fwd, seek_back, volume_up, volume_down
+toggle_pause, play, prev, next, stop, play_random, seek_fwd, seek_back, volume_up, volume_down, toggle_stop_after_current
LAST.FM PLUGIN CONFIGURATION
diff --git a/junklib.c b/junklib.c
index f492b5c0..7d22f710 100644
--- a/junklib.c
+++ b/junklib.c
@@ -270,9 +270,13 @@ convstr_id3v2_2to3 (const unsigned char* str, int sz) {
if (*str == 1) {
if (str[1] == 0xff && str[2] == 0xfe) {
enc = "UCS-2LE";
+ str += 2;
+ sz -= 2;
}
else if (str[2] == 0xff && str[1] == 0xfe) {
enc = "UCS-2BE";
+ str += 2;
+ sz -= 2;
}
else {
trace ("invalid ucs-2 signature %x %x\n", (int)str[1], (int)str[2]);
diff --git a/main.c b/main.c
index a2ec5450..33f5f22c 100644
--- a/main.c
+++ b/main.c
@@ -15,12 +15,12 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <gtk/gtk.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
+#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/types.h>
@@ -33,19 +33,13 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
-#include "interface.h"
-#include "callbacks.h"
-#include "support.h"
#include "playlist.h"
#include "playback.h"
#include "unistd.h"
#include "threading.h"
#include "messagepump.h"
-#include "gtkplaylist.h"
#include "codec.h"
#include "streamer.h"
-#include "search.h"
-#include "progress.h"
#include "conf.h"
#include "volume.h"
#include "session.h"
@@ -61,174 +55,134 @@ char dbconfdir[1024]; // $HOME/.config/deadbeef
char defpl[1024]; // $HOME/.config/deadbeef/default.dbpl
char sessfile[1024]; // $HOME/.config/deadbeef/session
-// main widgets
-GtkWidget *mainwin;
-GtkWidget *searchwin;
-GtkStatusIcon *trayicon;
-GtkWidget *traymenu;
-
-// playlist configuration structures
-gtkplaylist_t main_playlist;
-gtkplaylist_t search_playlist;
-
-// update status bar and window title
-static int sb_context_id = -1;
-static char sb_text[512];
-static float last_songpos = -1;
-
-void
-update_songinfo (void) {
- char sbtext_new[512] = "-";
- float songpos = last_songpos;
-
- int daystotal = (int)pl_totaltime / (3600*24);
- int hourtotal = ((int)pl_totaltime / 3600) % 24;
- int mintotal = ((int)pl_totaltime/60) % 60;
- int sectotal = ((int)pl_totaltime) % 60;
-
- char totaltime_str[512] = "";
- if (daystotal == 0)
- snprintf (totaltime_str, sizeof (totaltime_str), "%d:%02d:%02d", hourtotal, mintotal, sectotal);
-
- else if (daystotal == 1)
- snprintf (totaltime_str, sizeof (totaltime_str), "1 day %d:%02d:%02d", hourtotal, mintotal, sectotal);
-
- else
- snprintf (totaltime_str, sizeof (totaltime_str), "%d days %d:%02d:%02d", daystotal, hourtotal, mintotal, sectotal);
-
- if (p_isstopped ()) {
- snprintf (sbtext_new, sizeof (sbtext_new), "Stopped | %d tracks | %s total playtime", pl_getcount (), totaltime_str);
- songpos = 0;
- }
- else if (str_playing_song.decoder) {
-// codec_lock ();
- DB_decoder_t *c = str_playing_song.decoder;
- float playpos = streamer_get_playpos ();
- int minpos = playpos / 60;
- int secpos = playpos - minpos * 60;
- int mindur = str_playing_song._duration / 60;
- int secdur = str_playing_song._duration - mindur * 60;
-
- const char *mode = c->info.channels == 1 ? "Mono" : "Stereo";
- int samplerate = c->info.samplerate;
- int bitspersample = c->info.bps;
- songpos = playpos;
-// codec_unlock ();
-
- char t[100];
- if (str_playing_song._duration >= 0) {
- snprintf (t, sizeof (t), "%d:%02d", mindur, secdur);
- }
- else {
- strcpy (t, "-:--");
- }
-
- char sbitrate[20] = "";
-#if 0 // NOTE: do not enable that for stable branch yet
- int bitrate = streamer_get_bitrate ();
- if (bitrate > 0) {
- snprintf (sbitrate, sizeof (sbitrate), "%d kbps ", bitrate);
- }
-#endif
- const char *spaused = p_ispaused () ? "Paused | " : "";
- snprintf (sbtext_new, sizeof (sbtext_new), "%s%s %s| %dHz | %d bit | %s | %d:%02d / %s | %d tracks | %s total playtime", spaused, str_playing_song.filetype ? str_playing_song.filetype:"-", sbitrate, samplerate, bitspersample, mode, minpos, secpos, t, pl_getcount (), totaltime_str);
- }
-
- if (strcmp (sbtext_new, sb_text)) {
- strcpy (sb_text, sbtext_new);
-
- // form statusline
- GDK_THREADS_ENTER();
- // FIXME: don't update if window is not visible
- GtkStatusbar *sb = GTK_STATUSBAR (lookup_widget (mainwin, "statusbar"));
- if (sb_context_id == -1) {
- sb_context_id = gtk_statusbar_get_context_id (sb, "msg");
+// client-side commandline support
+// -1 error, program must exit with error code -1
+// 0 proceed normally as nothing happened
+// 1 no error, but program must exit with error code 0
+int
+client_exec_command_line (const char *cmdline, int len) {
+ const uint8_t *parg = (const uint8_t *)cmdline;
+ const uint8_t *pend = cmdline + len;
+ int exitcode = 0;
+ int queue = 0;
+ while (parg < pend) {
+ // if (filter == 1) {
+ // help, version and nowplaying are executed with any filter
+ if (!strcmp (parg, "--help") || !strcmp (parg, "-h")) {
+ fprintf (stderr, "Usage: deadbeef [options] [file(s)]\n");
+ fprintf (stderr, "Options:\n");
+ fprintf (stderr, " --help or -h Print help (this message) and exit\n");
+ fprintf (stderr, " --quit Quit player\n");
+ fprintf (stderr, " --version Print version info and exit\n");
+ fprintf (stderr, " --play Start playback\n");
+ fprintf (stderr, " --stop Stop playback\n");
+ fprintf (stderr, " --pause Pause playback\n");
+ fprintf (stderr, " --next Next song in playlist\n");
+ fprintf (stderr, " --prev Previous song in playlist\n");
+ fprintf (stderr, " --random Random song in playlist\n");
+ fprintf (stderr, " --queue Append file(s) to existing playlist\n");
+ fprintf (stderr, " --nowplaying FMT Print formatted track name to stdout\n");
+ fprintf (stderr, " FMT %%-syntax: [a]rtist, [t]itle, al[b]um, [l]ength, track[n]umber\n");
+ fprintf (stderr, " e.g.: --nowplaying \"%%a - %%t\" should print \"artist - title\"\n");
+ return 1;
}
-
- gtk_statusbar_pop (sb, sb_context_id);
- gtk_statusbar_push (sb, sb_context_id, sb_text);
-
- GDK_THREADS_LEAVE();
- }
-
- if (songpos != last_songpos) {
- void seekbar_draw (GtkWidget *widget);
- void seekbar_expose (GtkWidget *widget, int x, int y, int w, int h);
- if (mainwin) {
- GtkWidget *widget = lookup_widget (mainwin, "seekbar");
- // translate volume to seekbar pixels
- songpos /= str_playing_song._duration;
- songpos *= widget->allocation.width;
- if ((int)(songpos*2) != (int)(last_songpos*2)) {
- GDK_THREADS_ENTER();
- seekbar_draw (widget);
- seekbar_expose (widget, 0, 0, widget->allocation.width, widget->allocation.height);
- GDK_THREADS_LEAVE();
- last_songpos = songpos;
- }
+ else if (!strcmp (parg, "--version")) {
+ fprintf (stderr, "DeaDBeeF %s Copyright (C) 2009 Alexey Yakovenko\n", VERSION);
+ return 1;
}
+ parg += strlen (parg);
+ parg++;
}
+ return 0;
}
-
+// this function executes server-side commands only
+// must be called only from within server
// -1 error, program must exit with error code -1
// 0 proceed normally as nothing happened
// 1 no error, but program must exit with error code 0
-// 2 no error, start playback immediately after startup
-// 3 no error, don't start playback immediately after startup
+// 2 don't load playlist on startup
+// when executed in remote server -- error code will be ignored
int
-exec_command_line (const char *cmdline, int len, int filter) {
+server_exec_command_line (const char *cmdline, int len, char *sendback, int sbsize) {
+ if (sendback) {
+ sendback[0] = 0;
+ }
const uint8_t *parg = (const uint8_t *)cmdline;
const uint8_t *pend = cmdline + len;
int exitcode = 0;
int queue = 0;
while (parg < pend) {
- if (filter == 1) {
- if (!strcmp (parg, "--help") || !strcmp (parg, "-h")) {
- printf ("Usage: deadbeef [options] [file(s)]\n");
- printf ("Options:\n");
- printf (" --help or -h Print help (this message) and exit\n");
- printf (" --version Print version info and exit\n");
- printf (" --play Start playback\n");
- printf (" --stop Stop playback\n");
- printf (" --pause Pause playback\n");
- printf (" --next Next song in playlist\n");
- printf (" --prev Previous song in playlist\n");
- printf (" --random Random song in playlist\n");
- printf (" --queue Append file(s) to existing playlist\n");
- return 1;
- }
- else if (!strcmp (parg, "--version")) {
- printf ("DeaDBeeF %s Copyright (C) 2009 Alexey Yakovenko\n", VERSION);
- return 1;
- }
- }
- else if (filter == 0) {
- if (!strcmp (parg, "--next")) {
- messagepump_push (M_NEXTSONG, 0, 0, 0);
- }
- else if (!strcmp (parg, "--prev")) {
- messagepump_push (M_PREVSONG, 0, 0, 0);
- }
- else if (!strcmp (parg, "--play")) {
- messagepump_push (M_PLAYSONG, 0, 0, 0);
- }
- else if (!strcmp (parg, "--stop")) {
- messagepump_push (M_STOPSONG, 0, 0, 0);
- }
- else if (!strcmp (parg, "--pause")) {
- messagepump_push (M_PAUSESONG, 0, 0, 0);
- }
- else if (!strcmp (parg, "--random")) {
- messagepump_push (M_PLAYRANDOM, 0, 0, 0);
+ if (!strcmp (parg, "--nowplaying")) {
+ parg += strlen (parg);
+ parg++;
+ if (parg >= pend) {
+ if (sendback) {
+ snprintf (sendback, sbsize, "error --nowplaying expects format argument\n");
+ return 0;
+ }
+ else {
+ fprintf (stderr, "--nowplaying expects format argument\n");
+ return -1;
+ }
}
- else if (!strcmp (parg, "--queue")) {
- queue = 1;
+ if (sendback) {
+ playItem_t *curr = streamer_get_playing_track ();
+ if (curr && curr->decoder) {
+ const char np[] = "nowplaying ";
+ memcpy (sendback, np, sizeof (np)-1);
+ pl_format_title (curr, sendback+sizeof(np)-1, sbsize-sizeof(np)+1, -1, parg);
+ }
+ else {
+ strcpy (sendback, "nowplaying nothing");
+ }
}
- else if (parg[0] != '-') {
- break;
+ else {
+ char out[2048];
+ playItem_t *curr = streamer_get_playing_track ();
+ if (curr && curr->decoder) {
+ pl_format_title (curr, out, sizeof (out), -1, parg);
+ }
+ else {
+ strcpy (out, "nothing");
+ }
+ printf (out);
+ return 1; // exit
}
}
+ else if (!strcmp (parg, "--next")) {
+ messagepump_push (M_NEXTSONG, 0, 0, 0);
+ return 0;
+ }
+ else if (!strcmp (parg, "--prev")) {
+ messagepump_push (M_PREVSONG, 0, 0, 0);
+ return 0;
+ }
+ else if (!strcmp (parg, "--play")) {
+ messagepump_push (M_PLAYSONG, 0, 0, 0);
+ return 0;
+ }
+ else if (!strcmp (parg, "--stop")) {
+ messagepump_push (M_STOPSONG, 0, 0, 0);
+ return 0;
+ }
+ else if (!strcmp (parg, "--pause")) {
+ messagepump_push (M_PAUSESONG, 0, 0, 0);
+ return 0;
+ }
+ else if (!strcmp (parg, "--random")) {
+ messagepump_push (M_PLAYRANDOM, 0, 0, 0);
+ return 0;
+ }
+ else if (!strcmp (parg, "--queue")) {
+ queue = 1;
+ }
+ else if (!strcmp (parg, "--quit")) {
+ messagepump_push (M_TERMINATE, 0, 0, 0);
+ }
+ else if (parg[0] != '-') {
+ break; // unknown option is filename
+ }
parg += strlen (parg);
parg++;
}
@@ -236,7 +190,7 @@ exec_command_line (const char *cmdline, int len, int filter) {
// add files
if (!queue) {
pl_free ();
- main_playlist.row = -1;
+ pl_reset_cursor ();
}
while (parg < pend) {
char resolved[PATH_MAX];
@@ -247,23 +201,19 @@ exec_command_line (const char *cmdline, int len, int filter) {
else {
pname = parg;
}
- if (pl_add_file (pname, NULL, NULL) >= 0) {
- if (queue) {
- exitcode = 3;
- }
- else {
- exitcode = 2;
- }
+ if (pl_add_file (pname, NULL, NULL) < 0) {
+ fprintf (stderr, "failed to add file %s\n", pname);
}
parg += strlen (parg);
parg++;
}
- }
- if (exitcode == 2 || exitcode == 3) {
- // added some files, need to redraw
messagepump_push (M_PLAYLISTREFRESH, 0, 0, 0);
+ if (!queue) {
+ messagepump_push (M_PLAYSONG, 0, 0, 0);
+ return 2; // don't reload playlist at startup
+ }
}
- return exitcode;
+ return 0;
}
static struct sockaddr_un srv_local;
@@ -313,24 +263,24 @@ server_update (void) {
}
else if (s2 != -1) {
char str[2048];
+ char sendback[1024];
int size;
if ((size = recv (s2, str, 2048, 0)) >= 0) {
if (size == 1 && str[0] == 0) {
- GDK_THREADS_ENTER();
- gtk_widget_show (mainwin);
- gtk_window_present (GTK_WINDOW (mainwin));
- GDK_THREADS_LEAVE();
+ // FIXME: that should be called right after activation of gui plugin
+ plug_trigger_event (DB_EV_ACTIVATE, 0);
}
else {
- int res = exec_command_line (str, size, 0);
- if (res == 2) {
- GDK_THREADS_ENTER();
- gtkpl_playsong (&main_playlist);
- GDK_THREADS_LEAVE();
- }
+ server_exec_command_line (str, size, sendback, sizeof (sendback));
}
}
- send (s2, "", 1, 0);
+ if (sendback[0]) {
+ // send nowplaying back to client
+ send (s2, sendback, strlen (sendback)+1, 0);
+ }
+ else {
+ send (s2, "", 1, 0);
+ }
close(s2);
}
return 0;
@@ -347,7 +297,6 @@ player_thread (uintptr_t ctx) {
messagepump_push (M_TERMINATE, 0, 0, 0);
}
}
- plug_trigger_event (DB_EV_FRAMEUPDATE, 0);
uint32_t msg;
uintptr_t ctx;
uint32_t p1;
@@ -355,213 +304,61 @@ player_thread (uintptr_t ctx) {
while (messagepump_pop(&msg, &ctx, &p1, &p2) != -1) {
switch (msg) {
case M_REINIT_SOUND:
- {
- int play = 0;
- if (!palsa_ispaused () && !palsa_isstopped ()) {
- play = 1;
- }
-
- palsa_free ();
- palsa_init ();
- if (play) {
- palsa_play ();
- }
- }
+ plug_reinit_sound ();
break;
case M_TERMINATE:
- GDK_THREADS_ENTER();
- gtk_widget_hide (mainwin);
- gtk_main_quit ();
- GDK_THREADS_LEAVE();
return;
case M_SONGCHANGED:
- {
- int from = p1;
- int to = p2;
- gtkpl_songchanged_wrapper (from, to);
- plug_trigger_event (DB_EV_SONGCHANGED, 0);
- }
+ plug_trigger_event_trackchange (p1, p2);
break;
case M_PLAYSONG:
- gtkpl_playsong (&main_playlist);
- if (playlist_current_ptr) {
- GDK_THREADS_ENTER();
- gtkpl_redraw_pl_row (&main_playlist, pl_get_idx_of (playlist_current_ptr), playlist_current_ptr);
- GDK_THREADS_LEAVE();
- }
+ streamer_play_current_track ();
break;
case M_TRACKCHANGED:
- {
- playItem_t *it = pl_get_for_idx (p1);
- if (it) {
- GDK_THREADS_ENTER();
- gtkpl_redraw_pl_row (&main_playlist, p1, it);
- if (it == playlist_current_ptr) {
- gtkpl_current_track_changed (it);
- }
- GDK_THREADS_LEAVE();
- }
- }
+ plug_trigger_event_trackinfochanged (p1);
break;
case M_PLAYSONGNUM:
- GDK_THREADS_ENTER();
- gtkpl_playsongnum (p1);
- GDK_THREADS_LEAVE();
+ p_stop ();
+ streamer_set_nextsong (p1, 1);
break;
case M_STOPSONG:
- //p_stop ();
streamer_set_nextsong (-2, 0);
break;
case M_NEXTSONG:
- GDK_THREADS_ENTER();
p_stop ();
pl_nextsong (1);
- GDK_THREADS_LEAVE();
break;
case M_PREVSONG:
- GDK_THREADS_ENTER();
p_stop ();
pl_prevsong ();
- GDK_THREADS_LEAVE();
break;
case M_PAUSESONG:
- if (p_ispaused ()) {
+ if (p_get_state () == OUTPUT_STATE_PAUSED) {
p_unpause ();
+ plug_trigger_event_paused (0);
}
else {
p_pause ();
+ plug_trigger_event_paused (1);
}
-
- GDK_THREADS_ENTER();
- if (playlist_current_ptr) {
- gtkpl_redraw_pl_row (&main_playlist, pl_get_idx_of (playlist_current_ptr), playlist_current_ptr);
- }
- GDK_THREADS_LEAVE();
break;
case M_PLAYRANDOM:
- GDK_THREADS_ENTER();
- gtkpl_randomsong ();
- GDK_THREADS_LEAVE();
- break;
- case M_ADDDIR:
- {
- // long time processing
-// float t1 = (float)clock () / CLOCKS_PER_SEC;
- gtkpl_add_dir (&main_playlist, (char *)ctx);
-// float t2 = (float)clock () / CLOCKS_PER_SEC;
-// printf ("time: %f\n", t2-t1);
- }
- break;
- case M_ADDDIRS:
- {
- // long time processing
-// float t1 = (float)clock () / CLOCKS_PER_SEC;
- gtkpl_add_dirs (&main_playlist, (GSList *)ctx);
-// float t2 = (float)clock () / CLOCKS_PER_SEC;
-// printf ("time: %f\n", t2-t1);
- }
- break;
- case M_ADDFILES:
- gtkpl_add_files (&main_playlist, (GSList *)ctx);
- break;
- case M_OPENFILES:
p_stop ();
- gtkpl_add_files (&main_playlist, (GSList *)ctx);
- gtkpl_playsong (&main_playlist);
- break;
- case M_FMDRAGDROP:
- gtkpl_add_fm_dropped_files (&main_playlist, (char *)ctx, p1, p2);
+ pl_randomsong ();
break;
case M_PLAYLISTREFRESH:
- GDK_THREADS_ENTER();
- playlist_refresh ();
- search_refresh ();
- GDK_THREADS_LEAVE();
+ plug_trigger_event_playlistchanged ();
break;
case M_CONFIGCHANGED:
- palsa_configchanged ();
+ //plug_get_output ()->configchanged ();
streamer_configchanged ();
plug_trigger_event (DB_EV_CONFIGCHANGED, 0);
break;
}
}
usleep(50000);
- update_songinfo ();
- }
-}
-
-gboolean
-on_trayicon_scroll_event (GtkWidget *widget,
- GdkEventScroll *event,
- gpointer user_data)
-{
- float vol = volume_get_db ();
- if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_RIGHT) {
- vol += 1;
- }
- else if (event->direction == GDK_SCROLL_DOWN || event->direction == GDK_SCROLL_LEFT) {
- vol -= 1;
- }
- if (vol > 0) {
- vol = 0;
- }
- else if (vol < -60) {
- vol = -60;
- }
- volume_set_db (vol);
- GtkWidget *volumebar = lookup_widget (mainwin, "volumebar");
- volumebar_draw (volumebar);
- volumebar_expose (volumebar, 0, 0, volumebar->allocation.width, volumebar->allocation.height);
- return FALSE;
-}
-
-#if GTK_MINOR_VERSION<=14
-gboolean
-on_trayicon_activate (GtkWidget *widget,
- GdkEvent *event,
- gpointer user_data)
-{
- if (GTK_WIDGET_VISIBLE (mainwin)) {
- gtk_widget_hide (mainwin);
- }
- else {
- gtk_widget_show (mainwin);
- session_restore_window_attrs ((uintptr_t)mainwin);
- gtk_window_present (GTK_WINDOW (mainwin));
- }
- return FALSE;
-}
-#endif
-
-gboolean
-on_trayicon_button_press_event (GtkWidget *widget,
- GdkEventButton *event,
- gpointer user_data)
-{
- if (event->button == 1) {
- if (GTK_WIDGET_VISIBLE (mainwin)) {
- gtk_widget_hide (mainwin);
- }
- else {
- gtk_widget_show (mainwin);
- session_restore_window_attrs ((uintptr_t)mainwin);
- gtk_window_present (GTK_WINDOW (mainwin));
- }
- }
- else if (event->button == 2) {
- messagepump_push (M_PAUSESONG, 0, 0, 0);
+ plug_trigger_event (DB_EV_FRAMEUPDATE, 0);
}
- return FALSE;
-}
-
-gboolean
-on_trayicon_popup_menu (GtkWidget *widget,
- guint button,
- guint time,
- gpointer user_data)
-{
- gtk_menu_popup (GTK_MENU (traymenu), NULL, NULL, gtk_status_icon_position_menu, trayicon, button, time);
- return FALSE;
}
void
@@ -635,6 +432,9 @@ main (int argc, char *argv[]) {
// path of this process
if (argv[i][0] != '-' && realpath (argv[i], resolved)) {
len = strlen (resolved);
+ if (len >= size) {
+ break;
+ }
memcpy (p, resolved, len+1);
}
else {
@@ -644,10 +444,15 @@ main (int argc, char *argv[]) {
size -= len;
}
size = 2048 - size + 1;
- if (exec_command_line (cmdline, size, 1) == 1) {
- return 0; // if it was help request
- }
}
+ int res = client_exec_command_line (cmdline, size);
+ if (res == 1) {
+ return 0;
+ }
+ else if (res < 0) {
+ return res;
+ }
+
// try to connect to remote player
int s, t, len;
struct sockaddr_un remote;
@@ -672,111 +477,93 @@ main (int argc, char *argv[]) {
perror ("send");
exit (-1);
}
- char out[1];
- if (recv(s, out, 1, 0) == -1) {
+ char out[2048];
+ if (recv(s, out, sizeof (out), 0) == -1) {
fprintf (stderr, "failed to pass args to remote!\n");
exit (-1);
}
+ else {
+ // check if that's nowplaying response
+ const char np[] = "nowplaying ";
+ const char err[] = "error ";
+ if (!strncmp (out, np, sizeof (np)-1)) {
+ const char *prn = &out[sizeof (np)-1];
+ printf (prn);
+ }
+ else if (!strncmp (out, err, sizeof (err)-1)) {
+ const char *prn = &out[sizeof (err)-1];
+ fprintf (stderr, prn);
+ }
+ else if (out[0]) {
+ fprintf (stderr, "got unkown response:\n%s\n", out);
+ }
+ }
close (s);
exit (0);
}
close(s);
signal (SIGTERM, sigterm_handler);
+
+ conf_load (); // required by some plugin at startup
+ messagepump_init (); // required to push messages while handling commandline
+ plug_load_all (); // required to add files to playlist from commandline
+
+ // execute server commands in local context
+ int noloadpl = 0;
+ if (argc > 1) {
+ int res = server_exec_command_line (cmdline, size, NULL, 0);
+ // some of the server commands ran on 1st instance should terminate it
+ if (res == 2) {
+ noloadpl = 1;
+ }
+ else if (res > 0) {
+ exit (0);
+ }
+ else if (res < 0) {
+ exit (-1);
+ }
+ }
+
// become a server
server_start ();
- conf_load ();
- plug_load_all ();
- pl_load (defpl);
+ // start all subsystems
+ volume_set_db (conf_get_float ("playback.volume", 0));
+ if (!noloadpl) {
+ pl_load (defpl);
+ }
+ plug_trigger_event_playlistchanged ();
session_load (sessfile);
- messagepump_init ();
codec_init_locking ();
streamer_init ();
-// p_init ();
- thread_start (player_thread, 0);
-
- g_thread_init (NULL);
- add_pixmap_directory (PREFIX "/share/deadbeef/pixmaps");
- gdk_threads_init ();
- gdk_threads_enter ();
- gtk_set_locale ();
- gtk_init (&argc, &argv);
-
- // system tray icon
- traymenu = create_traymenu ();
- GdkPixbuf *trayicon_pixbuf = create_pixbuf ("play_24.png");
- trayicon = gtk_status_icon_new_from_pixbuf (trayicon_pixbuf);
- set_tray_tooltip ("DeaDBeeF");
- //gtk_status_icon_set_title (GTK_STATUS_ICON (trayicon), "DeaDBeeF");
-#if GTK_MINOR_VERSION <= 14
- g_signal_connect ((gpointer)trayicon, "activate", G_CALLBACK (on_trayicon_activate), NULL);
-#else
- g_signal_connect ((gpointer)trayicon, "scroll_event", G_CALLBACK (on_trayicon_scroll_event), NULL);
- g_signal_connect ((gpointer)trayicon, "button_press_event", G_CALLBACK (on_trayicon_button_press_event), NULL);
-#endif
- g_signal_connect ((gpointer)trayicon, "popup_menu", G_CALLBACK (on_trayicon_popup_menu), NULL);
-
- gtkpl_init ();
- mainwin = create_mainwin ();
- GdkPixbuf *mainwin_icon_pixbuf;
- mainwin_icon_pixbuf = create_pixbuf ("play_24.png");
- if (mainwin_icon_pixbuf)
- {
- gtk_window_set_icon (GTK_WINDOW (mainwin), mainwin_icon_pixbuf);
- gdk_pixbuf_unref (mainwin_icon_pixbuf);
- }
- session_restore_window_attrs ((uintptr_t)mainwin);
- volume_set_db (conf_get_float ("playback.volume", 0));
- // order and looping
- const char *orderwidgets[3] = { "order_linear", "order_shuffle", "order_random" };
- const char *loopingwidgets[3] = { "loop_all", "loop_disable", "loop_single" };
- const char *w;
- w = orderwidgets[conf_get_int ("playback.order", 0)];
- gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (lookup_widget (mainwin, w)), TRUE);
- w = loopingwidgets[conf_get_int ("playback.loop", 0)];
- gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (lookup_widget (mainwin, w)), TRUE);
- gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (lookup_widget (mainwin, "scroll_follows_playback")), conf_get_int ("playlist.scroll.followplayback", 0) ? TRUE : FALSE);
-
- searchwin = create_searchwin ();
- gtk_window_set_transient_for (GTK_WINDOW (searchwin), GTK_WINDOW (mainwin));
- extern void main_playlist_init (GtkWidget *widget);
- main_playlist_init (lookup_widget (mainwin, "playlist"));
- extern void search_playlist_init (GtkWidget *widget);
- search_playlist_init (lookup_widget (searchwin, "searchlist"));
-
- progress_init ();
-
- if (argc > 1) {
- int res = exec_command_line (cmdline, size, 0);
- if (res == -1) {
- server_close ();
- return -1;
- }
- if (res == 2) {
- messagepump_push (M_PLAYSONG, 0, 0, 0);
- }
- }
+ // this runs in main thread (blocks right here)
+ player_thread (0);
- gtk_widget_show (mainwin);
- gtk_main ();
// save config
pl_save (defpl);
conf_save ();
+
// stop receiving messages from outside
server_close ();
- // at this point we can simply do exit(0), but let's clean up for debugging
- gtkpl_free (&main_playlist);
- gtkpl_free (&search_playlist);
- gdk_threads_leave ();
- messagepump_free ();
+
+ // stop streaming and playback before unloading plugins
+ p_stop ();
p_free ();
streamer_free ();
+
+ // plugins might still hood references to playitems,
+ // and query configuration in background
+ // so unload everything 1st before final cleanup
+ plug_unload_all ();
+
+ // at this point we can simply do exit(0), but let's clean up for debugging
codec_free_locking ();
session_save (sessfile);
pl_free ();
conf_free ();
- plug_unload_all ();
+ messagepump_free ();
+ fprintf (stderr, "hej-hej!\n");
return 0;
}
diff --git a/playback.h b/playback.h
index 2a4ce722..da12504f 100644
--- a/playback.h
+++ b/playback.h
@@ -18,27 +18,16 @@
#ifndef __PLAYBACK_H
#define __PLAYBACK_H
-#if USE_SDL
-#include "psdl.h"
-#define p_init psdl_init
-#define p_free psdl_free
-#define p_play psdl_play
-#define p_stop psdl_stop
-#define p_ispaused psdl_ispaused
-#define p_pause psdl_pause
-#define p_unpause psdl_unpause
-#define p_get_rate psdl_get_rate
-#else
-#include "palsa.h"
-#define p_init palsa_init
-#define p_free palsa_free
-#define p_play palsa_play
-#define p_stop palsa_stop
-#define p_ispaused palsa_ispaused
-#define p_pause palsa_pause
-#define p_unpause palsa_unpause
-#define p_get_rate palsa_get_rate
-#define p_isstopped palsa_isstopped
-#endif
+#define p_init plug_get_output ()->init
+#define p_free plug_get_output ()->free
+#define p_play plug_get_output ()->play
+#define p_stop plug_get_output ()->stop
+#define p_pause plug_get_output ()->pause
+#define p_unpause plug_get_output ()->unpause
+#define p_get_rate plug_get_output ()->samplerate
+#define p_get_state plug_get_output ()->state
+
+#define p_isstopped() (plug_get_output ()->state () == OUTPUT_STATE_STOPPED)
+#define p_ispaused() (plug_get_output ()->state () == OUTPUT_STATE_PAUSED)
#endif // __PLAYBACK_H
diff --git a/playlist.c b/playlist.c
index 95f315c6..6a29cc93 100644
--- a/playlist.c
+++ b/playlist.c
@@ -35,6 +35,7 @@
#include "junklib.h"
#include "vfs.h"
#include "conf.h"
+#include "utf8.h"
// 1.0->1.1 changelog:
// added sample-accurate seek positions for sub-tracks
@@ -50,11 +51,15 @@
playItem_t *playlist_head[PL_MAX_ITERATORS];
playItem_t *playlist_tail[PL_MAX_ITERATORS];
+int playlist_current_row[PL_MAX_ITERATORS];
+
playItem_t *playlist_current_ptr;
-int pl_count = 0;
-float pl_totaltime = 0;
-//static int pl_order = 0; // 0 = linear, 1 = shuffle, 2 = random
-//static int pl_loop_mode = 0; // 0 = loop, 1 = don't loop, 2 = loop single
+static int pl_count = 0;
+static float pl_totaltime = 0;
+
+#define PLAYQUEUE_SIZE 100
+static playItem_t *playqueue[100];
+static int playqueue_count = 0;
void
pl_free (void) {
@@ -122,39 +127,22 @@ pl_get_value_from_cue (const char *p, int sz, char *out) {
static float
pl_cue_parse_time (const char *p) {
- char tmp[3] = {0};
- const char *next = p;
- int s;
- while (*next && *next != ':') {
- next++;
- }
- if ((next - p) != 2) {
+ char *endptr;
+ long mins = strtol(p, &endptr, 10);
+ if (endptr - p < 2 || *endptr != ':') {
return -1;
}
- strncpy (tmp, p, 2);
- tmp[next-p] = 0;
- float mins = atoi (tmp);
- next++;
- p = next;
- while (*next && *next != ':') {
- next++;
- }
- if ((next - p) != 2) {
+ p = endptr + 1;
+ long sec = strtol(p, &endptr, 10);
+ if (endptr - p != 2 || *endptr != ':') {
return -1;
}
- strncpy (tmp, p, 2);
- float sec = atoi (tmp);
- next++;
- p = next;
- while (*next && *next != ':') {
- next++;
- }
- if ((next - p) != 2) {
+ p = endptr + 1;
+ long frm = strtol(p, &endptr, 10);
+ if (endptr - p != 2 || *endptr != '\0') {
return -1;
}
- strncpy (tmp, p, 2);
- float frm = atoi (tmp);
- return mins * 60 + sec + frm / 75.f;
+ return mins * 60.f + sec + frm / 75.f;
}
static playItem_t *
@@ -177,7 +165,7 @@ pl_process_cue_track (playItem_t *after, const char *fname, playItem_t **prev, c
}
*p = 0;
// check that indexes have valid timestamps
- float f_index00 = index00[0] ? pl_cue_parse_time (index00) : 0;
+ //float f_index00 = index00[0] ? pl_cue_parse_time (index00) : 0;
float f_index01 = index01[0] ? pl_cue_parse_time (index01) : 0;
float f_pregap = pregap[0] ? pl_cue_parse_time (pregap) : 0;
if (*prev) {
@@ -225,12 +213,7 @@ pl_process_cue_track (playItem_t *after, const char *fname, playItem_t **prev, c
it->decoder = decoder;
it->fname = strdup (fname);
it->tracknum = atoi (track);
- float t = 0;
- if (index01[0]) {
- t = f_index01;
- }
- it->startsample = t * samplerate;
-
+ it->startsample = index01[0] ? f_index01 * samplerate : 0;
it->endsample = -1; // will be filled by next read, or by decoder
it->filetype = ftype;
after = pl_insert_item (after, it);
@@ -261,7 +244,7 @@ pl_insert_cue_from_buffer (playItem_t *after, const char *fname, const uint8_t *
}
// skip linebreak(s)
while (p - buffer < buffersize && *p < 0x20) {
- *p++;
+ p++;
}
if (p-buffer > 2048) { // huge string, ignore
buffer = p;
@@ -474,7 +457,6 @@ pl_insert_pls (playItem_t *after, const char *fname, int *pabort, int (*cb)(play
char url[1024] = "";
char title[1024] = "";
char length[20] = "";
- int nfile = 1;
while (p < end) {
if (p >= end) {
break;
@@ -622,12 +604,10 @@ pl_insert_file (playItem_t *after, const char *fname, int *pabort, int (*cb)(pla
const char **exts = decoders[i]->exts;
for (int e = 0; exts[e]; e++) {
if (!strcasecmp (exts[e], eol)) {
- playItem_t *inserted = NULL;
- if ((inserted = (playItem_t *)decoders[i]->insert (DB_PLAYITEM (after), fname)) != NULL) {
- if (cb) {
- if (cb (inserted, user_data) < 0) {
- *pabort = 1;
- }
+ playItem_t *inserted = (playItem_t *)decoders[i]->insert (DB_PLAYITEM (after), fname);
+ if (inserted != NULL) {
+ if (cb && cb (inserted, user_data) < 0) {
+ *pabort = 1;
}
return inserted;
}
@@ -635,7 +615,7 @@ pl_insert_file (playItem_t *after, const char *fname, int *pabort, int (*cb)(pla
}
}
}
- fprintf (stderr, "no decoder found for %s\n", fname);
+ trace ("no decoder found for %s\n", fname);
return NULL;
}
@@ -667,10 +647,8 @@ pl_insert_dir (playItem_t *after, const char *dirname, int *pabort, int (*cb)(pl
// no hidden files
if (namelist[i]->d_name[0] != '.')
{
- char fullname[1024];
- strcpy (fullname, dirname);
- strncat (fullname, "/", 1024);
- strncat (fullname, namelist[i]->d_name, 1024);
+ char fullname[PATH_MAX];
+ snprintf (fullname, sizeof (fullname), "%s/%s", dirname, namelist[i]->d_name);
playItem_t *inserted = pl_insert_dir (after, fullname, pabort, cb, user_data);
if (!inserted) {
inserted = pl_insert_file (after, fullname, pabort, cb, user_data);
@@ -716,6 +694,7 @@ pl_remove (playItem_t *it) {
if (playlist_current_ptr == it) {
playlist_current_ptr = NULL;
}
+ pl_playqueue_remove (it);
// remove from linear list
if (it->prev[PL_MAIN]) {
@@ -760,16 +739,21 @@ pl_getselcount (void) {
}
playItem_t *
-pl_get_for_idx (int idx) {
- playItem_t *it = playlist_head[PL_MAIN];
+pl_get_for_idx_and_iter (int idx, int iter) {
+ playItem_t *it = playlist_head[iter];
while (idx--) {
if (!it)
return NULL;
- it = it->next[PL_MAIN];
+ it = it->next[iter];
}
return it;
}
+playItem_t *
+pl_get_for_idx (int idx) {
+ return pl_get_for_idx_and_iter (idx, PL_MAIN);
+}
+
int
pl_get_idx_of (playItem_t *it) {
playItem_t *c = playlist_head[PL_MAIN];
@@ -784,19 +768,6 @@ pl_get_idx_of (playItem_t *it) {
return idx;
}
-int
-pl_append_item (playItem_t *it) {
- if (!playlist_tail[PL_MAIN]) {
- playlist_tail[PL_MAIN] = playlist_head[PL_MAIN] = it;
- }
- else {
- playlist_tail[PL_MAIN]->next[PL_MAIN] = it;
- it->prev[PL_MAIN] = playlist_tail[PL_MAIN];
- playlist_tail[PL_MAIN] = it;
- }
- pl_count++;
-}
-
playItem_t *
pl_insert_item (playItem_t *after, playItem_t *it) {
if (!after) {
@@ -901,13 +872,14 @@ pl_item_free (playItem_t *it) {
int
pl_prevsong (void) {
+ pl_playqueue_clear ();
if (!playlist_head[PL_MAIN]) {
streamer_set_nextsong (-2, 1);
return 0;
}
int pl_order = conf_get_int ("playback.order", 0);
int pl_loop_mode = conf_get_int ("playback.loop", 0);
- if (pl_order == 1) { // shuffle
+ if (pl_order == PLAYBACK_ORDER_SHUFFLE) { // shuffle
if (!playlist_current_ptr) {
return pl_nextsong (1);
}
@@ -917,7 +889,6 @@ pl_prevsong (void) {
int rating = playlist_current_ptr->shufflerating;
playItem_t *pmax = NULL; // played maximum
playItem_t *amax = NULL; // absolute maximum
- playItem_t *i = NULL;
for (playItem_t *i = playlist_head[PL_MAIN]; i; i = i->next[PL_MAIN]) {
if (i != playlist_current_ptr && i->played && (!amax || i->shufflerating > amax->shufflerating)) {
amax = i;
@@ -932,7 +903,7 @@ pl_prevsong (void) {
playItem_t *it = pmax;
if (!it) {
// that means 1st in playlist, take amax
- if (pl_loop_mode == 0) {
+ if (pl_loop_mode == PLAYBACK_MODE_LOOP_ALL) {
if (!amax) {
pl_reshuffle (NULL, &amax);
}
@@ -948,13 +919,13 @@ pl_prevsong (void) {
return 0;
}
}
- else if (pl_order == 0) { // linear
+ else if (pl_order == PLAYBACK_ORDER_LINEAR) { // linear
playItem_t *it = NULL;
if (playlist_current_ptr) {
it = playlist_current_ptr->prev[PL_MAIN];
}
if (!it) {
- if (pl_loop_mode == 0) {
+ if (pl_loop_mode == PLAYBACK_MODE_LOOP_ALL) {
it = playlist_tail[PL_MAIN];
}
}
@@ -965,7 +936,7 @@ pl_prevsong (void) {
streamer_set_nextsong (r, 1);
return 0;
}
- else if (pl_order == 2) { // random
+ else if (pl_order == PLAYBACK_ORDER_RANDOM) { // random
pl_randomsong ();
}
return -1;
@@ -973,6 +944,14 @@ pl_prevsong (void) {
int
pl_nextsong (int reason) {
+ if (playqueue_count > 0) {
+ playItem_t *it = playqueue[0];
+ pl_playqueue_pop ();
+ int r = pl_get_idx_of (it);
+ streamer_set_nextsong (r, 1);
+ return 0;
+ }
+
playItem_t *curr = streamer_get_streaming_track ();
if (!playlist_head[PL_MAIN]) {
streamer_set_nextsong (-2, 1);
@@ -980,11 +959,10 @@ pl_nextsong (int reason) {
}
int pl_order = conf_get_int ("playback.order", 0);
int pl_loop_mode = conf_get_int ("playback.loop", 0);
- if (pl_order == 1) { // shuffle
+ if (pl_order == PLAYBACK_ORDER_SHUFFLE) { // shuffle
if (!curr) {
// find minimal notplayed
playItem_t *pmin = NULL; // notplayed minimum
- playItem_t *i = NULL;
for (playItem_t *i = playlist_head[PL_MAIN]; i; i = i->next[PL_MAIN]) {
if (i->played) {
continue;
@@ -996,7 +974,7 @@ pl_nextsong (int reason) {
playItem_t *it = pmin;
if (!it) {
// all songs played, reshuffle and try again
- if (pl_loop_mode == 0) { // loop
+ if (pl_loop_mode == PLAYBACK_MODE_LOOP_ALL) { // loop
pl_reshuffle (&it, NULL);
}
}
@@ -1009,7 +987,7 @@ pl_nextsong (int reason) {
}
else {
trace ("pl_next_song: reason=%d, loop=%d\n", reason, pl_loop_mode);
- if (reason == 0 && pl_loop_mode == 2) { // song finished, loop mode is "loop 1 track"
+ if (reason == 0 && pl_loop_mode == PLAYBACK_MODE_LOOP_SINGLE) { // song finished, loop mode is "loop 1 track"
int r = pl_get_idx_of (curr);
streamer_set_nextsong (r, 1);
return 0;
@@ -1017,7 +995,6 @@ pl_nextsong (int reason) {
// find minimal notplayed above current
int rating = curr->shufflerating;
playItem_t *pmin = NULL; // notplayed minimum
- playItem_t *i = NULL;
for (playItem_t *i = playlist_head[PL_MAIN]; i; i = i->next[PL_MAIN]) {
if (i->played || i->shufflerating < rating) {
continue;
@@ -1030,7 +1007,7 @@ pl_nextsong (int reason) {
if (!it) {
trace ("all songs played! reshuffle\n");
// all songs played, reshuffle and try again
- if (pl_loop_mode == 0) { // loop
+ if (pl_loop_mode == PLAYBACK_MODE_LOOP_ALL) { // loop
pl_reshuffle (&it, NULL);
}
}
@@ -1042,10 +1019,10 @@ pl_nextsong (int reason) {
return 0;
}
}
- else if (pl_order == 0) { // linear
+ else if (pl_order == PLAYBACK_ORDER_LINEAR) { // linear
playItem_t *it = NULL;
if (curr) {
- if (reason == 0 && pl_loop_mode == 2) { // loop same track
+ if (reason == 0 && pl_loop_mode == PLAYBACK_MODE_LOOP_SINGLE) { // loop same track
int r = pl_get_idx_of (curr);
streamer_set_nextsong (r, 1);
return 0;
@@ -1053,7 +1030,8 @@ pl_nextsong (int reason) {
it = curr->next[PL_MAIN];
}
if (!it) {
- if (pl_loop_mode == 0) {
+ trace ("pl_nextsong: was last track\n");
+ if (pl_loop_mode == PLAYBACK_MODE_LOOP_ALL) {
it = playlist_head[PL_MAIN];
}
else {
@@ -1068,8 +1046,8 @@ pl_nextsong (int reason) {
streamer_set_nextsong (r, 1);
return 0;
}
- else if (pl_order == 2) { // random
- if (reason == 0 && pl_loop_mode == 2 && curr) {
+ else if (pl_order == PLAYBACK_ORDER_RANDOM) { // random
+ if (reason == 0 && pl_loop_mode == PLAYBACK_MODE_LOOP_SINGLE && curr) {
int r = pl_get_idx_of (curr);
streamer_set_nextsong (r, 1);
return 0;
@@ -1103,7 +1081,6 @@ pl_add_meta (playItem_t *it, const char *key, const char *value) {
char str[256];
if (!value || !*value) {
if (!strcasecmp (key, "title")) {
- int len = 256;
// cut filename without path and extension
const char *pext = it->fname + strlen (it->fname) - 1;
while (pext >= it->fname && *pext != '.') {
@@ -1460,6 +1437,7 @@ pl_load (const char *fname) {
"track",
"band",
"cuesheet",
+ "copyright",
NULL
};
@@ -1569,7 +1547,123 @@ pl_get_item_duration (playItem_t *it) {
}
int
-pl_format_title (playItem_t *it, char *s, int size, const char *fmt) {
+pl_format_item_queue (playItem_t *it, char *s, int size) {
+ *s = 0;
+ if (!playqueue_count) {
+ return 0;
+ }
+ int init = 1;
+ int initsize = size;
+ int len;
+ for (int i = 0; i < playqueue_count; i++) {
+ if (size <= 0) {
+ break;
+ }
+ if (playqueue[i] == it) {
+ if (init) {
+ init = 0;
+ s[0] = '(';
+ s++;
+ size--;
+ len = snprintf (s, size, "%d", i+1);
+ }
+ else {
+ len = snprintf (s, size, ",%d", i+1);
+ }
+ s += len;
+ size -= len;
+ }
+ }
+ if (size != initsize && size > 0) {
+ len = snprintf (s, size, ")");
+ s += len;
+ size -= len;
+ }
+ return initsize-size;
+}
+
+static const char *
+pl_get_meta_cached (playItem_t *it, const char *meta, const char *ret, const char *def) {
+ if (!ret) {
+ ret = pl_find_meta (it, meta);
+ if (!ret) {
+ ret = def;
+ }
+ }
+ return ret;
+}
+
+static const char *
+pl_format_duration (playItem_t *it, const char *ret, char *dur, int size) {
+ if (ret) {
+ return ret;
+ }
+ if (it->_duration >= 0) {
+ int hourdur = it->_duration / (60 * 60);
+ int mindur = (it->_duration - hourdur * 60 * 60) / 60;
+ int secdur = it->_duration - hourdur*60*60 - mindur * 60;
+
+ if (hourdur) {
+ snprintf (dur, size, "%d:%02d:%02d", hourdur, mindur, secdur);
+ }
+ else {
+ snprintf (dur, size, "%d:%02d", mindur, secdur);
+ }
+ }
+ else {
+ strcpy (dur, "-:--");
+ }
+ return dur;
+}
+
+int
+pl_format_title (playItem_t *it, char *s, int size, int id, const char *fmt) {
+ char dur[50];
+ const char *artist = NULL;
+ const char *album = NULL;
+ const char *track = NULL;
+ const char *title = NULL;
+ const char *duration = NULL;
+
+ if (id != -1) {
+ const char *text = NULL;
+ switch (id) {
+ case DB_COLUMN_PLAYING:
+ return pl_format_item_queue (it, s, size);
+ case DB_COLUMN_ARTIST_ALBUM:
+ {
+ char artistalbum[1024];
+ artist = pl_get_meta_cached (it, "artist", artist, "?");
+ album = pl_get_meta_cached (it, "album", album, "?");
+ snprintf (artistalbum, sizeof (artistalbum), "%s - %s", artist, album);
+ text = artistalbum;
+ }
+ break;
+ case DB_COLUMN_ARTIST:
+ text = (artist = pl_get_meta_cached (it, "artist", artist, "?"));
+ break;
+ case DB_COLUMN_ALBUM:
+ text = (album = pl_get_meta_cached (it, "album", artist, "?"));
+ break;
+ case DB_COLUMN_TITLE:
+ text = (title = pl_get_meta_cached (it, "title", artist, "?"));
+ break;
+ case DB_COLUMN_DURATION:
+ text = (duration = pl_format_duration (it, duration, dur, sizeof (dur)));
+ break;
+ case DB_COLUMN_TRACK:
+ text = (track = pl_get_meta_cached (it, "track", track, ""));
+ break;
+ }
+ if (text) {
+ strncpy (s, text, size);
+ return strlen (s);
+ }
+ else {
+ s[0] = 0;
+ }
+ return 0;
+ }
int n = size-1;
while (*fmt && n) {
if (*fmt != '%') {
@@ -1583,35 +1677,19 @@ pl_format_title (playItem_t *it, char *s, int size, const char *fmt) {
break;
}
else if (*fmt == 'a') {
- meta = "artist";
+ meta = (artist = pl_get_meta_cached (it, "artist", artist, "?"));
}
else if (*fmt == 't') {
- meta = "title";
+ meta = (title = pl_get_meta_cached (it, "title", title, "?"));
}
else if (*fmt == 'b') {
- meta = "album";
+ meta = (album = pl_get_meta_cached (it, "album", album, "?"));
}
else if (*fmt == 'n') {
- meta = "track";
+ meta = (track = pl_get_meta_cached (it, "track", track, ""));
}
else if (*fmt == 'l') {
- char dur[50];
- if (it->_duration >= 0) {
- int hourdur = it->_duration / (60 * 60);
- int mindur = (it->_duration - hourdur * 60 * 60) / 60;
- int secdur = it->_duration - hourdur*60*60 - mindur * 60;
-
- if (hourdur) {
- snprintf (dur, sizeof (dur), "%d:%02d:%02d", hourdur, mindur, secdur);
- }
- else {
- snprintf (dur, sizeof (dur), "%d:%02d", mindur, secdur);
- }
- }
- else {
- strcpy (dur, "-:--");
- }
- const char *value = dur;
+ const char *value = (duration = pl_format_duration (it, duration, dur, sizeof (dur)));
while (n > 0 && *value) {
*s++ = *value++;
n--;
@@ -1623,10 +1701,7 @@ pl_format_title (playItem_t *it, char *s, int size, const char *fmt) {
}
if (meta) {
- const char *value = pl_find_meta (it, meta);
- if (!value) {
- value = "?";
- }
+ const char *value = meta;
while (n > 0 && *value) {
*s++ = *value++;
n--;
@@ -1641,6 +1716,259 @@ pl_format_title (playItem_t *it, char *s, int size, const char *fmt) {
}
void
-pl_sort (const char *meta) {
+pl_sort (int iter, int id, const char *format, int ascending) {
+ int sorted = 0;
+ do {
+ sorted = 1;
+ playItem_t *it;
+ for (it = playlist_head[iter]; it; it = it->next[iter]) {
+ playItem_t *next = it->next[iter];
+ if (!next) {
+ break;
+ }
+ char title1[1024];
+ char title2[1024];
+ pl_format_title (it, title1, sizeof (title1), id, format);
+ pl_format_title (next, title2, sizeof (title2), id, format);
+// const char *meta1 = pl_find_meta (it, meta);
+// const char *meta2 = pl_find_meta (next, meta);
+ int cmp = ascending ? strcmp (title1, title2) < 0 : strcmp (title1, title2) > 0;
+ if (cmp) {
+// printf ("%p %p swapping %s and %s\n", it, next, meta1, meta2);
+ sorted = 0;
+ // swap them
+ if (it->prev[iter]) {
+ it->prev[iter]->next[iter] = next;
+// printf ("it->prev->next = it->next\n");
+ }
+ else {
+ playlist_head[iter] = next;
+ next->prev[iter] = NULL;
+// printf ("head = it->next\n");
+ }
+ if (next->next[iter]) {
+ next->next[iter]->prev[iter] = it;
+// printf ("it->next->next->prev = it\n");
+ }
+ else {
+ playlist_tail[iter] = it;
+ it->next[iter] = NULL;
+// printf ("tail = it\n");
+ }
+ playItem_t *it_prev = it->prev[iter];
+ it->next[iter] = next->next[iter];
+ it->prev[iter] = next;
+ next->next[iter] = it;
+ next->prev[iter] = it_prev;
+ it = next;
+ }
+ }
+ } while (!sorted);
+}
+
+void
+pl_reset_cursor (void) {
+ int i;
+ for (i = 0; i < PL_MAX_ITERATORS; i++) {
+ playlist_current_row[i] = -1;
+ }
+}
+
+float
+pl_get_totaltime (void) {
+ return pl_totaltime;
+}
+
+playItem_t *
+pl_getcurrent (void) {
+ return playlist_current_ptr;
+}
+
+void
+pl_set_selected (playItem_t *it, int sel) {
+ it->selected = sel;
+}
+
+int
+pl_is_selected (playItem_t *it) {
+ return it->selected;
+}
+
+playItem_t *
+pl_get_first (int iter) {
+ return playlist_head[iter];
+}
+
+playItem_t *
+pl_get_last (int iter) {
+ return playlist_tail[iter];
+}
+
+playItem_t *
+pl_get_next (playItem_t *it, int iter) {
+ return it ? it->next[iter] : NULL;
+}
+
+playItem_t *
+pl_get_prev (playItem_t *it, int iter) {
+ return it ? it->prev[iter] : NULL;
+}
+
+int
+pl_get_cursor (int iter) {
+ return playlist_current_row[iter];
}
+void
+pl_set_cursor (int iter, int cursor) {
+ playlist_current_row[iter] = cursor;
+}
+
+// this function must move items in playlist
+// list of items is indexes[count]
+// drop_before is insertion point
+void
+pl_move_items (int iter, playItem_t *drop_before, uint32_t *indexes, int count) {
+ // unlink items from playlist, and link together
+ playItem_t *head = NULL;
+ playItem_t *tail = NULL;
+ int processed = 0;
+ int idx = 0;
+ playItem_t *next = NULL;
+ for (playItem_t *it = playlist_head[iter]; it && processed < count; it = next, idx++) {
+ next = it->next[iter];
+ if (idx == indexes[processed]) {
+ if (it->prev[iter]) {
+ it->prev[iter]->next[iter] = it->next[iter];
+ }
+ else {
+ playlist_head[iter] = it->next[iter];
+ }
+ if (it->next[iter]) {
+ it->next[iter]->prev[iter] = it->prev[iter];
+ }
+ else {
+ playlist_tail[iter] = it->prev[iter];
+ }
+ if (tail) {
+ tail->next[iter] = it;
+ it->prev[iter] = tail;
+ tail = it;
+ }
+ else {
+ head = tail = it;
+ it->prev[iter] = it->next[iter] = NULL;
+ }
+ processed++;
+ }
+ }
+ // find insertion point
+ playItem_t *drop_after = NULL;
+ if (drop_before) {
+ drop_after = drop_before->prev[iter];
+ }
+ else {
+ drop_after = playlist_tail[iter];
+ }
+ // insert in between
+ head->prev[iter] = drop_after;
+ if (drop_after) {
+ drop_after->next[iter] = head;
+ }
+ else {
+ playlist_head[iter] = head;
+ }
+ tail->next[iter] = drop_before;
+ if (drop_before) {
+ drop_before->prev[iter] = tail;
+ }
+ else {
+ playlist_tail[iter] = tail;
+ }
+}
+
+int
+pl_process_search (const char *text) {
+ playlist_head[PL_SEARCH] = NULL;
+ playlist_tail[PL_SEARCH] = NULL;
+ int search_count = 0;
+ if (*text) {
+ for (playItem_t *it = playlist_head[PL_MAIN]; it; it = it->next[PL_MAIN]) {
+ it->selected = 0;
+ for (metaInfo_t *m = it->meta; m; m = m->next) {
+// if (strcasestr (m->value, text)) {
+ if (utfcasestr (m->value, text)) {
+ // add to list
+ it->next[PL_SEARCH] = NULL;
+ if (playlist_tail[PL_SEARCH]) {
+ playlist_tail[PL_SEARCH]->next[PL_SEARCH] = it;
+ playlist_tail[PL_SEARCH] = it;
+ }
+ else {
+ playlist_head[PL_SEARCH] = playlist_tail[PL_SEARCH] = it;
+ }
+ it->selected = 1;
+ search_count++;
+ break;
+ }
+ }
+ }
+ }
+ return search_count;
+}
+
+int
+pl_playqueue_push (playItem_t *it) {
+ if (playqueue_count == PLAYQUEUE_SIZE) {
+ trace ("playqueue is full\n");
+ return -1;
+ }
+ playqueue[playqueue_count++] = it;
+ return 0;
+}
+
+void
+pl_playqueue_clear (void) {
+ playqueue_count = 0;
+}
+
+void
+pl_playqueue_pop (void) {
+ if (!playqueue_count) {
+ return;
+ }
+ if (playqueue_count == 1) {
+ playqueue_count = 0;
+ return;
+ }
+ memmove (&playqueue[0], &playqueue[1], (playqueue_count-1) * sizeof (playItem_t*));
+ playqueue_count--;
+}
+
+void
+pl_playqueue_remove (playItem_t *it) {
+ for (;;) {
+ int i;
+ for (i = 0; i < playqueue_count; i++) {
+ if (playqueue[i] == it) {
+ if (i < playqueue_count-1) {
+ memmove (&playqueue[i], &playqueue[i+1], (playqueue_count-i) * sizeof (playItem_t*));
+ }
+ playqueue_count--;
+ break;
+ }
+ }
+ if (i == playqueue_count) {
+ break;
+ }
+ }
+}
+int
+pl_playqueue_test (playItem_t *it) {
+ for (int i = 0; i < playqueue_count; i++) {
+ if (playqueue[i] == it) {
+ return i;
+ }
+ }
+ return -1;
+}
diff --git a/playlist.h b/playlist.h
index 6ee004b6..3758fa03 100644
--- a/playlist.h
+++ b/playlist.h
@@ -28,8 +28,6 @@ typedef struct metaInfo_s {
} metaInfo_t;
#define PL_MAX_ITERATORS 2
-#define PL_MAIN 0
-#define PL_SEARCH 1
typedef struct playItem_s {
char *fname; // full pathname
@@ -57,11 +55,9 @@ typedef struct playItem_s {
extern playItem_t *playlist_head[PL_MAX_ITERATORS]; // head of linked list
extern playItem_t *playlist_tail[PL_MAX_ITERATORS]; // tail of linked list
+extern int playlist_current_row[PL_MAX_ITERATORS]; // current row (cursor)
extern playItem_t *playlist_current_ptr; // pointer to a real current playlist item (or NULL)
-extern int pl_count;
-extern float pl_totaltime;
-
int
pl_add_dir (const char *dirname, int (*cb)(playItem_t *it, void *data), void *user_data);
@@ -78,9 +74,6 @@ playItem_t *
pl_insert_item (playItem_t *after, playItem_t *it);
int
-pl_append_item (playItem_t *it);
-
-int
pl_remove (playItem_t *i);
playItem_t *
@@ -104,6 +97,9 @@ pl_getselcount (void);
playItem_t *
pl_get_for_idx (int idx);
+playItem_t *
+pl_get_for_idx_and_iter (int idx, int iter);
+
int
pl_get_idx_of (playItem_t *it);
@@ -168,6 +164,64 @@ pl_get_item_duration (playItem_t *it);
// returns number of characters printed, not including trailing 0
// [a]rtist, [t]itle, al[b]um, [l]ength, track[n]umber
int
-pl_format_title (playItem_t *it, char *s, int size, const char *fmt);
+pl_format_title (playItem_t *it, char *s, int size, int id, const char *fmt);
+
+void
+pl_reset_cursor (void);
+
+float
+pl_get_totaltime (void);
+
+playItem_t *
+pl_getcurrent (void);
+
+void
+pl_set_selected (playItem_t *it, int sel);
+
+int
+pl_is_selected (playItem_t *it);
+
+playItem_t *
+pl_get_first (int iter);
+
+playItem_t *
+pl_get_last (int iter);
+
+playItem_t *
+pl_get_next (playItem_t *it, int iter);
+
+playItem_t *
+pl_get_prev (playItem_t *it, int iter);
+
+int
+pl_get_cursor (int iter);
+
+void
+pl_set_cursor (int iter, int cursor);
+
+void
+pl_move_items (int iter, playItem_t *drop_before, uint32_t *indexes, int count);
+
+int
+pl_process_search (const char *text);
+
+void
+pl_sort (int iter, int id, const char *format, int ascending);
+
+// playqueue support functions
+int
+pl_playqueue_push (playItem_t *it);
+
+void
+pl_playqueue_clear (void);
+
+void
+pl_playqueue_pop (void);
+
+void
+pl_playqueue_remove (playItem_t *it);
+
+int
+pl_playqueue_test (playItem_t *it);
#endif // __PLAYLIST_H
diff --git a/plugins.c b/plugins.c
index bbee94d1..032bed99 100644
--- a/plugins.c
+++ b/plugins.c
@@ -22,7 +22,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <gtk/gtk.h>
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
@@ -30,7 +29,6 @@
#include "md5/md5.h"
#include "messagepump.h"
#include "threading.h"
-#include "progress.h"
#include "playlist.h"
#include "volume.h"
#include "streamer.h"
@@ -53,6 +51,7 @@ static DB_functions_t deadbeef_api = {
.ev_unsubscribe = plug_ev_unsubscribe,
.md5 = plug_md5,
.md5_to_str = plug_md5_to_str,
+ .get_output = plug_get_output,
.playback_next = plug_playback_next,
.playback_prev = plug_playback_prev,
.playback_pause = plug_playback_pause,
@@ -61,8 +60,17 @@ static DB_functions_t deadbeef_api = {
.playback_random = plug_playback_random,
.playback_get_pos = plug_playback_get_pos,
.playback_set_pos = plug_playback_set_pos,
- .playback_get_samplerate = p_get_rate,
- .playback_update_bitrate = streamer_update_bitrate,
+ // streamer access
+ .streamer_get_playing_track = (DB_playItem_t *(*) (void))streamer_get_playing_track,
+ .streamer_get_streaming_track = (DB_playItem_t *(*) (void))streamer_get_streaming_track,
+ .streamer_get_playpos = streamer_get_playpos,
+ .streamer_seek = streamer_set_seek,
+ .streamer_ok_to_read = streamer_ok_to_read,
+ .streamer_reset = streamer_reset,
+ .streamer_read = streamer_read,
+ .streamer_set_bitrate = streamer_set_bitrate,
+ .streamer_get_apx_bitrate = streamer_get_apx_bitrate,
+ // process control
.get_config_dir = plug_get_config_dir,
.quit = plug_quit,
// threading
@@ -81,10 +89,39 @@ static DB_functions_t deadbeef_api = {
.pl_item_alloc = (DB_playItem_t* (*)(void))pl_item_alloc,
.pl_item_free = (void (*)(DB_playItem_t *))pl_item_free,
.pl_item_copy = (void (*)(DB_playItem_t *, DB_playItem_t *))pl_item_copy,
+ .pl_add_file = (int (*) (const char *, int (*cb)(DB_playItem_t *it, void *data), void *))pl_add_file,
+ .pl_add_dir = (int (*) (const char *dirname, int (*cb)(DB_playItem_t *it, void *data), void *user_data))pl_add_dir,
.pl_insert_item = (DB_playItem_t *(*) (DB_playItem_t *after, DB_playItem_t *it))pl_insert_item,
+ .pl_insert_dir = (DB_playItem_t *(*) (DB_playItem_t *after, const char *dirname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data))pl_insert_dir,
+ .pl_insert_file = (DB_playItem_t *(*) (DB_playItem_t *after, const char *fname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data))pl_insert_file,
.pl_get_idx_of = (int (*) (DB_playItem_t *it))pl_get_idx_of,
+ .pl_get_for_idx = (DB_playItem_t * (*)(int))pl_get_for_idx,
+ .pl_get_for_idx_and_iter = (DB_playItem_t * (*) (int idx, int iter))pl_get_for_idx_and_iter,
.pl_set_item_duration = (void (*) (DB_playItem_t *it, float duration))pl_set_item_duration,
.pl_get_item_duration = (float (*) (DB_playItem_t *it))pl_get_item_duration,
+ .pl_sort = pl_sort,
+ .pl_get_totaltime = pl_get_totaltime,
+ .pl_getcount = pl_getcount,
+ .pl_getcurrent = (DB_playItem_t *(*)(void))pl_getcurrent,
+ .pl_delete_selected = pl_delete_selected,
+ .pl_set_cursor = pl_set_cursor,
+ .pl_get_cursor = pl_get_cursor,
+ .pl_set_selected = (void (*) (DB_playItem_t *, int))pl_set_selected,
+ .pl_is_selected = (int (*) (DB_playItem_t *))pl_is_selected,
+ .pl_free = pl_free,
+ .pl_load = pl_load,
+ .pl_save = pl_save,
+ .pl_select_all = pl_select_all,
+ .pl_crop_selected = pl_crop_selected,
+ .pl_getselcount = pl_getselcount,
+ .pl_get_first = (DB_playItem_t *(*) (int))pl_get_first,
+ .pl_get_last = (DB_playItem_t *(*) (int))pl_get_last,
+ .pl_get_next = (DB_playItem_t *(*) (DB_playItem_t *, int))pl_get_next,
+ .pl_get_prev = (DB_playItem_t *(*) (DB_playItem_t *, int))pl_get_prev,
+ .pl_format_title = (int (*) (DB_playItem_t *it, char *s, int size, int id, const char *fmt))pl_format_title,
+ .pl_format_item_display_name = (void (*) (DB_playItem_t *it, char *str, int len))pl_format_item_display_name,
+ .pl_move_items = (void (*) (int iter, DB_playItem_t *drop_before, uint32_t *indexes, int count))pl_move_items,
+ .pl_process_search = pl_process_search,
// metainfo
.pl_add_meta = (void (*) (DB_playItem_t *, const char *, const char *))pl_add_meta,
.pl_find_meta = (const char *(*) (DB_playItem_t *, const char *))pl_find_meta,
@@ -92,11 +129,17 @@ static DB_functions_t deadbeef_api = {
// cuesheet support
.pl_insert_cue_from_buffer = (DB_playItem_t *(*) (DB_playItem_t *after, const char *fname, const uint8_t *buffer, int buffersize, struct DB_decoder_s *decoder, const char *ftype, int numsamples, int samplerate))pl_insert_cue_from_buffer,
.pl_insert_cue = (DB_playItem_t *(*)(DB_playItem_t *, const char *, struct DB_decoder_s *, const char *ftype, int numsamples, int samplerate))pl_insert_cue,
+ .pl_playqueue_push = (int (*) (DB_playItem_t *))pl_playqueue_push,
+ .pl_playqueue_clear = pl_playqueue_clear,
+ .pl_playqueue_pop = pl_playqueue_pop,
+ .pl_playqueue_remove = (void (*) (DB_playItem_t *))pl_playqueue_remove,
+ .pl_playqueue_test = (int (*) (DB_playItem_t *))pl_playqueue_test,
// volume control
.volume_set_db = plug_volume_set_db,
.volume_get_db = volume_get_db,
.volume_set_amp = plug_volume_set_amp,
.volume_get_amp = volume_get_amp,
+ .volume_get_min_db = volume_get_min_db,
// junk reading
.junk_read_id3v1 = (int (*)(DB_playItem_t *it, DB_FILE *fp))junk_read_id3v1,
.junk_read_id3v2 = (int (*)(DB_playItem_t *it, DB_FILE *fp))junk_read_id3v2,
@@ -121,9 +164,14 @@ static DB_functions_t deadbeef_api = {
.conf_get_float = conf_get_float,
.conf_get_int = conf_get_int,
.conf_set_str = conf_set_str,
+ .conf_set_int = conf_set_int,
.conf_find = conf_find,
- .gui_lock = plug_gui_lock,
- .gui_unlock = plug_gui_unlock,
+ .conf_remove_items = conf_remove_items,
+ // plugin communication
+ .plug_get_decoder_list = plug_get_decoder_list,
+ .plug_get_output_list = plug_get_output_list,
+ .plug_get_list = plug_get_list,
+ .plug_activate = plug_activate,
};
DB_functions_t *deadbeef = &deadbeef_api;
@@ -134,18 +182,15 @@ plug_get_config_dir (void) {
}
void
-volumebar_notify_changed (void);
-
-void
plug_volume_set_db (float db) {
volume_set_db (db);
- volumebar_notify_changed ();
+ plug_trigger_event_volumechanged ();
}
void
plug_volume_set_amp (float amp) {
volume_set_amp (amp);
- volumebar_notify_changed ();
+ plug_trigger_event_volumechanged ();
}
#define MAX_PLUGINS 100
@@ -157,15 +202,9 @@ DB_decoder_t *g_decoder_plugins[MAX_DECODER_PLUGINS+1];
#define MAX_VFS_PLUGINS 10
DB_vfs_t *g_vfs_plugins[MAX_VFS_PLUGINS+1];
-void
-plug_gui_lock (void) {
- gdk_threads_enter ();
-}
-
-void
-plug_gui_unlock (void) {
- gdk_threads_leave ();
-}
+#define MAX_OUTPUT_PLUGINS 10
+DB_output_t *g_output_plugins[MAX_OUTPUT_PLUGINS+1];
+DB_output_t *output_plugin = NULL;
void
plug_md5 (uint8_t sig[16], const char *in, int len) {
@@ -277,43 +316,90 @@ plug_playback_set_pos (float pos) {
void
plug_quit (void) {
- progress_abort ();
+ // FIXME progress_abort ();
messagepump_push (M_TERMINATE, 0, 0, 0);
}
/////// non-api functions (plugin support)
void
-plug_trigger_event (int ev, uintptr_t param) {
+plug_event_call (DB_event_t *ev) {
+ ev->time = time (NULL);
+// printf ("plug_event_call enter %d\n", ev->event);
mutex_lock (mutex);
+ for (int i = 0; i < MAX_HANDLERS; i++) {
+ if (handlers[ev->event][i].plugin && !handlers[ev->event][i].plugin->inactive) {
+ handlers[ev->event][i].callback (ev, handlers[ev->event][i].data);
+ }
+ }
+ mutex_unlock (mutex);
+// printf ("plug_event_call leave %d\n", ev->event);
+}
+
+void
+plug_trigger_event (int ev, uintptr_t param) {
DB_event_t *event;
switch (ev) {
case DB_EV_SONGSTARTED:
case DB_EV_SONGFINISHED:
{
- DB_event_song_t *pev = malloc (sizeof (DB_event_song_t));
- pev->song = DB_PLAYITEM (&str_playing_song);
+ DB_event_track_t *pev = alloca (sizeof (DB_event_track_t));
+ pev->index = -1;
+ pev->track = DB_PLAYITEM (&str_playing_song);
event = DB_EVENT (pev);
}
break;
case DB_EV_TRACKDELETED:
{
- DB_event_song_t *pev = malloc (sizeof (DB_event_song_t));
- pev->song = DB_PLAYITEM (param);
+ DB_event_track_t *pev = alloca (sizeof (DB_event_track_t));
+ pev->index = -1; // FIXME
+ pev->track = DB_PLAYITEM (param);
event = DB_EVENT (pev);
}
break;
default:
- event = malloc (sizeof (DB_event_t));
+ event = alloca (sizeof (DB_event_t));
}
event->event = ev;
- event->time = (double)clock () / CLOCKS_PER_SEC;
- for (int i = 0; i < MAX_HANDLERS; i++) {
- if (handlers[ev][i].plugin && !handlers[ev][i].plugin->inactive) {
- handlers[ev][i].callback (event, handlers[ev][i].data);
- }
- }
- free (event);
- mutex_unlock (mutex);
+ plug_event_call (event);
+}
+
+void
+plug_trigger_event_trackchange (int from, int to) {
+ DB_event_trackchange_t event;
+ event.ev.event = DB_EV_SONGCHANGED;
+ event.from = from;
+ event.to = to;
+ plug_event_call (DB_EVENT (&event));
+}
+void
+plug_trigger_event_trackinfochanged (int trk) {
+ DB_event_track_t event;
+ event.ev.event = DB_EV_TRACKINFOCHANGED;
+ event.index = trk;
+ event.track = DB_PLAYITEM (pl_get_for_idx (trk));
+ plug_event_call (DB_EVENT (&event));
+}
+
+void
+plug_trigger_event_paused (int paused) {
+ DB_event_state_t event;
+ event.ev.event = DB_EV_PAUSED;
+ event.state = paused;
+ plug_event_call (DB_EVENT (&event));
+}
+
+void
+plug_trigger_event_playlistchanged (void) {
+ DB_event_t event;
+ event.event = DB_EV_PLAYLISTCHANGED;
+ plug_event_call (DB_EVENT (&event));
+}
+
+void
+plug_trigger_event_volumechanged (void) {
+ DB_event_t event;
+ event.event = DB_EV_VOLUMECHANGED;
+ plug_event_call (DB_EVENT (&event));
}
int
@@ -437,18 +523,16 @@ plug_load_all (void) {
fprintf (stderr, "plugin %s is blacklisted in config file\n", d_name);
break;
}
- char fullname[1024];
- strcpy (fullname, plugdir);
- strncat (fullname, "/", 1024);
- strncat (fullname, d_name, 1024);
- printf ("loading plugin %s\n", d_name);
+ char fullname[PATH_MAX];
+ snprintf (fullname, PATH_MAX, "%s/%s", plugdir, d_name);
+ fprintf (stderr, "loading plugin %s\n", d_name);
void *handle = dlopen (fullname, RTLD_NOW);
if (!handle) {
fprintf (stderr, "dlopen error: %s\n", dlerror ());
break;
}
d_name[l-3] = 0;
- printf ("module name is %s\n", d_name);
+ fprintf (stderr, "module name is %s\n", d_name);
strcat (d_name, "_load");
DB_plugin_t *(*plug_load)(DB_functions_t *api) = dlsym (handle, d_name);
if (!plug_load) {
@@ -481,6 +565,7 @@ plug_load_all (void) {
int numplugins = 0;
int numdecoders = 0;
int numvfs = 0;
+ int numoutput = 0;
for (plugin_t *plug = plugins; plug; plug = plug->next) {
g_plugins[numplugins++] = plug->plugin;
if (plug->plugin->type == DB_PLUGIN_DECODER) {
@@ -497,11 +582,25 @@ plug_load_all (void) {
}
g_vfs_plugins[numvfs++] = (DB_vfs_t *)plug->plugin;
}
+ else if (plug->plugin->type == DB_PLUGIN_OUTPUT) {
+ fprintf (stderr, "found output plugin %s\n", plug->plugin->name);
+ if (numvfs >= MAX_OUTPUT_PLUGINS) {
+ break;
+ }
+ g_output_plugins[numoutput++] = (DB_output_t *)plug->plugin;
+ }
}
// fprintf (stderr, "numplugins: %d, numdecoders: %d, numvfs: %d\n", numplugins, numdecoders, numvfs);
g_plugins[numplugins] = NULL;
g_decoder_plugins[numdecoders] = NULL;
g_vfs_plugins[numvfs] = NULL;
+ g_output_plugins[numoutput] = NULL;
+
+ // select output plugin
+ if (plug_select_output () < 0) {
+ fprintf (stderr, "failed to find output plugin!\n");
+ exit (-1);
+ }
}
void
@@ -509,7 +608,9 @@ plug_unload_all (void) {
while (plugins) {
plugin_t *next = plugins->next;
if (plugins->plugin->stop) {
+ fprintf (stderr, "stopping %s...", plugins->plugin->name);
plugins->plugin->stop ();
+ fprintf (stderr, " [OK]\n");
}
if (plugins->handle) {
dlclose (plugins->handle);
@@ -517,6 +618,7 @@ plug_unload_all (void) {
plugins = next;
}
mutex_free (mutex);
+ fprintf (stderr, "all plugins had been unloaded\n");
}
struct DB_decoder_s **
@@ -524,6 +626,11 @@ plug_get_decoder_list (void) {
return g_decoder_plugins;
}
+struct DB_output_s **
+plug_get_output_list (void) {
+ return g_output_plugins;
+}
+
struct DB_vfs_s **
plug_get_vfs_list (void) {
return g_vfs_plugins;
@@ -533,3 +640,92 @@ struct DB_plugin_s **
plug_get_list (void) {
return g_plugins;
}
+
+int
+plug_activate (DB_plugin_t *plug, int activate) {
+ if (plug->inactive && !activate) {
+ return -1;
+ }
+ if (!plug->inactive && activate) {
+ return -1;
+ }
+ if (activate) {
+ if (plug->start) {
+ if (!plug->start ()) {
+ plug->inactive = 0;
+ }
+ else {
+ fprintf (stderr, "failed to start plugin %s\n", plug->name);
+ return -1;
+ }
+ return 0;
+ }
+ else {
+ return -1;
+ }
+ }
+ else {
+ if (plug->stop) {
+ if (!plug->stop ()) {
+ plug->inactive = 1;
+ }
+ else {
+ fprintf (stderr, "failed to stop plugin %s\n", plug->name);
+ return -1;
+ }
+ return 0;
+ }
+ else {
+ return -1;
+ }
+ }
+}
+
+DB_output_t *
+plug_get_output (void) {
+ return output_plugin;
+}
+
+int
+plug_select_output (void) {
+ const char *outplugname = conf_get_str ("output_plugin", "ALSA output plugin");
+ for (int i = 0; g_output_plugins[i]; i++) {
+ DB_output_t *p = g_output_plugins[i];
+ if (!strcmp (p->plugin.name, outplugname)) {
+ fprintf (stderr, "selected output plugin: %s\n", outplugname);
+ output_plugin = p;
+ break;
+ }
+ }
+ if (!output_plugin) {
+ output_plugin = g_output_plugins[0];
+ if (output_plugin) {
+ fprintf (stderr, "selected output plugin: %s\n", output_plugin->plugin.name);
+ conf_set_str ("output_plugin", output_plugin->plugin.name);
+ }
+ }
+ if (!output_plugin) {
+ return -1;
+ }
+ plug_trigger_event (DB_EV_OUTPUTCHANGED, 0);
+ return 0;
+}
+
+void
+plug_reinit_sound (void) {
+ int state = p_get_state ();
+
+ p_free ();
+
+ DB_output_t *prev = plug_get_output ();
+ if (plug_select_output () < 0) {
+ const char *outplugname = conf_get_str ("output_plugin", "ALSA output plugin");
+ fprintf (stderr, "failed to select output plugin %s\nreverted to %s\n", outplugname, prev->plugin.name);
+ output_plugin = prev;
+ }
+ p_init ();
+
+ if (state != OUTPUT_STATE_PAUSED && state != OUTPUT_STATE_STOPPED) {
+ p_play ();
+ }
+}
diff --git a/plugins.h b/plugins.h
index 671da5f8..62b0ec89 100644
--- a/plugins.h
+++ b/plugins.h
@@ -38,6 +38,21 @@ void
plug_trigger_event (int ev, uintptr_t param);
void
+plug_trigger_event_trackchange (int from, int to);
+
+void
+plug_trigger_event_trackinfochanged (int trk);
+
+void
+plug_trigger_event_paused (int paused);
+
+void
+plug_trigger_event_playlistchanged (void);
+
+void
+plug_trigger_event_volumechanged (void);
+
+void
plug_md5 (uint8_t sig[16], const char *in, int len);
void
@@ -76,6 +91,9 @@ plug_get_list (void);
struct DB_decoder_s **
plug_get_decoder_list (void);
+struct DB_output_s **
+plug_get_output_list (void);
+
struct DB_vfs_s **
plug_get_vfs_list (void);
@@ -88,10 +106,16 @@ plug_volume_set_amp (float amp);
const char *
plug_get_config_dir (void);
-void
-plug_gui_lock (void);
+int
+plug_activate (DB_plugin_t *plug, int activate);
+
+DB_output_t *
+plug_get_output (void);
void
-plug_gui_unlock (void);
+plug_reinit_sound (void);
+
+int
+plug_select_output (void);
#endif // __PLUGINS_H
diff --git a/plugins/adplug/Makefile.am b/plugins/adplug/Makefile.am
new file mode 100644
index 00000000..52cbb90d
--- /dev/null
+++ b/plugins/adplug/Makefile.am
@@ -0,0 +1,71 @@
+adlibdir = $(libdir)/$(PACKAGE)
+pkglib_LTLIBRARIES = adplug.la
+adplug_la_SOURCES = adplug-db.cpp\
+ plugin.c\
+ adplug/adplug.cpp\
+ adplug/emuopl.cpp\
+ adplug/fmopl.c\
+ adplug/diskopl.cpp\
+ adplug/debug.c\
+ adplug/debug.h\
+ adplug/fprovide.cpp\
+ adplug/player.cpp\
+ adplug/database.cpp\
+ adplug/hsc.cpp\
+ adplug/sng.cpp\
+ adplug/imf.cpp\
+ adplug/players.cpp\
+ adplug/protrack.cpp\
+ adplug/a2m.cpp\
+ adplug/adtrack.cpp\
+ adplug/amd.cpp\
+ adplug/bam.cpp\
+ adplug/d00.cpp\
+ adplug/dfm.cpp\
+ adplug/hsp.cpp\
+ adplug/ksm.cpp\
+ adplug/mad.cpp\
+ adplug/mid.cpp\
+ adplug/mkj.cpp\
+ adplug/cff.cpp\
+ adplug/dmo.cpp\
+ adplug/s3m.cpp\
+ adplug/dtm.cpp\
+ adplug/fmc.cpp\
+ adplug/mtk.cpp\
+ adplug/rad.cpp\
+ adplug/raw.cpp\
+ adplug/sa2.cpp\
+ adplug/xad.cpp\
+ adplug/flash.cpp\
+ adplug/bmf.cpp\
+ adplug/hybrid.cpp\
+ adplug/hyp.cpp\
+ adplug/psi.cpp\
+ adplug/rat.cpp\
+ adplug/u6m.cpp\
+ adplug/rol.cpp\
+ adplug/mididata.h\
+ adplug/xsm.cpp\
+ adplug/adlibemu.c\
+ adplug/dro.cpp\
+ adplug/lds.cpp\
+ adplug/realopl.cpp\
+ adplug/analopl.cpp\
+ adplug/temuopl.cpp\
+ adplug/msc.cpp\
+ adplug/rix.cpp\
+ adplug/adl.cpp\
+ libbinio/binfile.h\
+ libbinio/binio.h\
+ libbinio/binstr.h\
+ libbinio/binwrap.h\
+ libbinio/binfile.cpp\
+ libbinio/binio.cpp\
+ libbinio/binstr.cpp\
+ libbinio/binwrap.cpp
+
+adplug_la_LDFLAGS = -module
+
+AM_CXXFLAGS = $(CFLAGS) -Dstricmp=strcasecmp -DVERSION=\"2.1\" -I adplug -I libbinio
+AM_CFLAGS = $(CFLAGS) -std=c99
diff --git a/plugins/adplug/adplug-db.cpp b/plugins/adplug/adplug-db.cpp
new file mode 100644
index 00000000..6c15804d
--- /dev/null
+++ b/plugins/adplug/adplug-db.cpp
@@ -0,0 +1,251 @@
+/*
+ DeaDBeeF - ultimate music player for GNU/Linux systems with X11
+ Copyright (C) 2009 Alexey Yakovenko
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include "../../deadbeef.h"
+#include "adplug.h"
+#include "emuopl.h"
+#include "silentopl.h"
+
+#define min(x,y) ((x)<(y)?(x):(y))
+#define max(x,y) ((x)>(y)?(x):(y))
+
+#define trace(...) { fprintf (stderr, __VA_ARGS__); }
+//#define trace(fmt,...)
+
+extern "C" {
+
+extern DB_decoder_t adplug_plugin;
+static DB_functions_t *deadbeef;
+
+
+const char *adplug_exts[] = { "A2M", "ADL", "AMD", "BAM", "CFF", "CMF", "D00", "DFM", "DMO", "DRO", "DTM", "HSC", "HSP", "IMF", "KSM", "LAA", "LDS", "M", "MAD", "MID", "MKJ", "MSC", "MTK", "RAD", "RAW", "RIX", "ROL", "S3M", "SA2", "SAT", "SCI", "SNG", "SNG", "SNG", "XAD", "XMS", "XSM", NULL };
+
+const char *adplug_filetypes[] = { "A2M", "ADL", "AMD", "BAM", "CFF", "CMF", "D00", "DFM", "DMO", "DRO", "DTM", "HSC", "HSP", "IMF", "KSM", "LAA", "LDS", "M", "MAD", "MID", "MKJ", "MSC", "MTK", "RAD", "RAW", "RIX", "ROL", "S3M", "SA2", "SAT", "SCI", "SNG", "SNG", "SNG", "XAD", "XMS", "XSM", NULL };
+
+static CEmuopl *opl;
+static CPlayer *decoder;
+static int totalsamples;
+static int currentsample;
+static int subsong;
+static int toadd;
+
+int
+adplug_init (DB_playItem_t *it) {
+ // prepare to decode the track
+ // return -1 on failure
+
+ int samplerate = deadbeef->get_output ()->samplerate ();
+ int bps = deadbeef->get_output ()->bitspersample ();
+ opl = new CEmuopl (samplerate, bps, deadbeef->get_output ()->channels () == 2);
+ decoder = CAdPlug::factory (it->fname, opl, CAdPlug::players);
+ if (!decoder) {
+ trace ("adplug: failed to open %s\n", it->fname);
+ return NULL;
+ }
+
+ subsong = it->tracknum;
+ decoder->rewind (subsong);
+ totalsamples = decoder->songlength (subsong) * samplerate / 1000;
+ currentsample = 0;
+ toadd = 0;
+
+ // fill in mandatory plugin fields
+ adplug_plugin.info.bps = bps;
+ adplug_plugin.info.channels = deadbeef->get_output ()->channels ();
+ adplug_plugin.info.samplerate = samplerate;
+ adplug_plugin.info.readpos = 0;
+
+ return 0;
+}
+
+void
+adplug_free (void) {
+ // free everything allocated in _init
+ if (decoder) {
+ delete decoder;
+ decoder = NULL;
+ }
+ if (opl) {
+ delete opl;
+ opl = NULL;
+ }
+}
+
+int
+adplug_read_int16 (char *bytes, int size) {
+ // try decode `size' bytes
+ // return number of decoded bytes
+ // return 0 on EOF
+ bool playing = true;
+ int i;
+ int sampsize = 4;
+
+ int initsize = size;
+ if (currentsample + size/4 >= totalsamples) {
+ // clip
+ size = (totalsamples - currentsample) * sampsize;
+ }
+
+ int towrite = size/sampsize;
+ char *sndbufpos = bytes;
+
+
+ while (towrite > 0)
+ {
+ while (toadd < 0)
+ {
+ toadd += adplug_plugin.info.samplerate;
+ playing = decoder->update ();
+// decoder->time_ms += 1000 / plr.p->getrefresh ();
+ }
+ i = min (towrite, (long) (toadd / decoder->getrefresh () + 4) & ~3);
+ opl->update ((short *) sndbufpos, i);
+ sndbufpos += i * sampsize;
+ towrite -= i;
+ toadd -= (long) (decoder->getrefresh () * i);
+ }
+ currentsample += size/4;
+ adplug_plugin.info.readpos = (float)currentsample / adplug_plugin.info.samplerate;
+ return initsize-size;
+}
+
+int
+adplug_seek_sample (int sample) {
+ // seek to specified sample (frame)
+ // return 0 on success
+ // return -1 on failure
+
+ if (sample < currentsample) {
+ decoder->rewind (subsong);
+ currentsample = 0;
+ }
+
+ while (currentsample < sample) {
+ decoder->update ();
+ currentsample += 1000/decoder->getrefresh ();
+ }
+ if (currentsample >= totalsamples) {
+ return -1;
+ }
+ toadd = 0;
+
+ // update readpos
+ adplug_plugin.info.readpos = (float)currentsample / adplug_plugin.info.samplerate;
+ return 0;
+}
+
+int
+adplug_seek (float time) {
+ // seek to specified time in seconds
+ // return 0 on success
+ // return -1 on failure
+ return adplug_seek_sample (time * adplug_plugin.info.samplerate);
+ return 0;
+}
+
+static const char *
+adplug_get_extension (const char *fname) {
+ const char *e = fname + strlen (fname);
+ while (*e != '.' && e != fname) {
+ e--;
+ }
+ if (*e == '.') {
+ e++;
+ // now find ext in list
+ for (int i = 0; adplug_exts[i]; i++) {
+ if (!strcasecmp (e, adplug_exts[i])) {
+ return adplug_filetypes[i];
+ }
+ }
+ }
+ return "adplug-unknown";
+}
+
+DB_playItem_t *
+adplug_insert (DB_playItem_t *after, const char *fname) {
+ // read information from the track
+ // load/process cuesheet if exists
+ // insert track into playlist
+ // return track pointer on success
+ // return NULL on failure
+
+ CSilentopl opl;
+ CPlayer *p = CAdPlug::factory (fname, &opl, CAdPlug::players);
+ if (!p) {
+ trace ("adplug: failed to open %s\n", fname);
+ return NULL;
+ }
+
+ int subsongs = p->getsubsongs ();
+ for (int i = 0; i < subsongs; i++) {
+ // prepare track for addition
+ DB_playItem_t *it = deadbeef->pl_item_alloc ();
+ it->decoder = &adplug_plugin;
+ it->fname = strdup (fname);
+ it->filetype = adplug_get_extension (fname);
+ it->tracknum = i;
+ deadbeef->pl_set_item_duration (it, p->songlength (i)/1000.f);
+ // add metainfo
+ if (!p->gettitle().empty()) {
+ deadbeef->pl_add_meta (it, "title", p->gettitle().c_str());
+ }
+ else if (!p->getdesc().empty()) {
+ deadbeef->pl_add_meta (it, "title", p->getdesc().c_str());
+ }
+ else {
+ deadbeef->pl_add_meta (it, "title", NULL);
+ }
+ deadbeef->pl_add_meta (it, "artist", p->getauthor().c_str());
+ // insert
+ after = deadbeef->pl_insert_item (after, it);
+ }
+
+ // free decoder
+ delete p;
+
+ // now the track is ready, insert into playlist
+ return after;
+}
+
+int
+adplug_start (void) {
+ // do one-time plugin initialization here
+ // e.g. starting threads for background processing, subscribing to events, etc
+ // return 0 on success
+ // return -1 on failure
+ return 0;
+}
+
+int
+adplug_stop (void) {
+ // undo everything done in _start here
+ // return 0 on success
+ // return -1 on failure
+ return 0;
+}
+
+DB_plugin_t *
+adplug_load (DB_functions_t *api) {
+ deadbeef = api;
+ return DB_PLUGIN (&adplug_plugin);
+}
+
+}
diff --git a/plugins/adplug/adplug/COPYING b/plugins/adplug/adplug/COPYING
new file mode 100644
index 00000000..aaf2c633
--- /dev/null
+++ b/plugins/adplug/adplug/COPYING
@@ -0,0 +1,509 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+^L
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard. To achieve this, non-free programs must
+be allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+^L
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control
+compilation and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+^L
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+^L
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at least
+ three years, to give the same user the materials specified in
+ Subsection 6a, above, for a charge no more than the cost of
+ performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+^L
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+^L
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply, and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License
+may add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+^L
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+^L
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms
+of the ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library.
+It is safest to attach them to the start of each source file to most
+effectively convey the exclusion of warranty; and each file should
+have at least the "copyright" line and a pointer to where the full
+notice is found.
+
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or
+your school, if any, to sign a "copyright disclaimer" for the library,
+if necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James
+ Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/plugins/adplug/adplug/a2m.cpp b/plugins/adplug/adplug/a2m.cpp
new file mode 100644
index 00000000..3fd68d98
--- /dev/null
+++ b/plugins/adplug/adplug/a2m.cpp
@@ -0,0 +1,483 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * a2m.cpp - A2M Loader by Simon Peter <dn.tlp@gmx.net>
+ *
+ * NOTES:
+ * This loader detects and loads version 1, 4, 5 & 8 files.
+ *
+ * version 1-4 files:
+ * Following commands are ignored: FF1 - FF9, FAx - FEx
+ *
+ * version 5-8 files:
+ * Instrument panning is ignored. Flags byte is ignored.
+ * Following commands are ignored: Gxy, Hxy, Kxy - &xy
+ */
+
+#include <string.h>
+#include "a2m.h"
+
+const unsigned int Ca2mLoader::MAXFREQ = 2000,
+Ca2mLoader::MINCOPY = ADPLUG_A2M_MINCOPY,
+Ca2mLoader::MAXCOPY = ADPLUG_A2M_MAXCOPY,
+Ca2mLoader::COPYRANGES = ADPLUG_A2M_COPYRANGES,
+Ca2mLoader::CODESPERRANGE = ADPLUG_A2M_CODESPERRANGE,
+Ca2mLoader::TERMINATE = 256,
+Ca2mLoader::FIRSTCODE = ADPLUG_A2M_FIRSTCODE,
+Ca2mLoader::MAXCHAR = FIRSTCODE + COPYRANGES * CODESPERRANGE - 1,
+Ca2mLoader::SUCCMAX = MAXCHAR + 1,
+Ca2mLoader::TWICEMAX = ADPLUG_A2M_TWICEMAX,
+Ca2mLoader::ROOT = 1, Ca2mLoader::MAXBUF = 42 * 1024,
+Ca2mLoader::MAXDISTANCE = 21389, Ca2mLoader::MAXSIZE = 21389 + MAXCOPY;
+
+const unsigned short Ca2mLoader::bitvalue[14] =
+ {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192};
+
+const signed short Ca2mLoader::copybits[COPYRANGES] =
+ {4, 6, 8, 10, 12, 14};
+
+const signed short Ca2mLoader::copymin[COPYRANGES] =
+ {0, 16, 80, 336, 1360, 5456};
+
+CPlayer *Ca2mLoader::factory(Copl *newopl)
+{
+ return new Ca2mLoader(newopl);
+}
+
+bool Ca2mLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ char id[10];
+ int i,j,k,t;
+ unsigned int l;
+ unsigned char *org, *orgptr, flags = 0, numpats, version;
+ unsigned long crc, alength;
+ unsigned short len[9], *secdata, *secptr;
+ const unsigned char convfx[16] = {0,1,2,23,24,3,5,4,6,9,17,13,11,19,7,14};
+ const unsigned char convinf1[16] = {0,1,2,6,7,8,9,4,5,3,10,11,12,13,14,15};
+ const unsigned char newconvfx[] = {0,1,2,3,4,5,6,23,24,21,10,11,17,13,7,19,
+ 255,255,22,25,255,15,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,14,255};
+
+ // read header
+ f->readString(id, 10); crc = f->readInt(4);
+ version = f->readInt(1); numpats = f->readInt(1);
+
+ // file validation section
+ if(strncmp(id,"_A2module_",10) || (version != 1 && version != 5 &&
+ version != 4 && version != 8)) {
+ fp.close(f);
+ return false;
+ }
+
+ // load, depack & convert section
+ nop = numpats; length = 128; restartpos = 0;
+ if(version < 5) {
+ for(i=0;i<5;i++) len[i] = f->readInt(2);
+ t = 9;
+ } else { // version >= 5
+ for(i=0;i<9;i++) len[i] = f->readInt(2);
+ t = 18;
+ }
+
+ // block 0
+ secdata = new unsigned short [len[0] / 2];
+ if(version == 1 || version == 5) {
+ for(i=0;i<len[0]/2;i++) secdata[i] = f->readInt(2);
+ org = new unsigned char [MAXBUF]; orgptr = org;
+ sixdepak(secdata,org,len[0]);
+ } else {
+ orgptr = (unsigned char *)secdata;
+ for(i=0;i<len[0];i++) orgptr[i] = f->readInt(1);
+ }
+ memcpy(songname,orgptr,43); orgptr += 43;
+ memcpy(author,orgptr,43); orgptr += 43;
+ memcpy(instname,orgptr,250*33); orgptr += 250*33;
+
+ for(i=0;i<250;i++) { // instruments
+ inst[i].data[0] = *(orgptr+i*13+10);
+ inst[i].data[1] = *(orgptr+i*13);
+ inst[i].data[2] = *(orgptr+i*13+1);
+ inst[i].data[3] = *(orgptr+i*13+4);
+ inst[i].data[4] = *(orgptr+i*13+5);
+ inst[i].data[5] = *(orgptr+i*13+6);
+ inst[i].data[6] = *(orgptr+i*13+7);
+ inst[i].data[7] = *(orgptr+i*13+8);
+ inst[i].data[8] = *(orgptr+i*13+9);
+ inst[i].data[9] = *(orgptr+i*13+2);
+ inst[i].data[10] = *(orgptr+i*13+3);
+
+ if(version < 5)
+ inst[i].misc = *(orgptr+i*13+11);
+ else { // version >= 5 -> OPL3 format
+ int pan = *(orgptr+i*13+11);
+
+ if(pan)
+ inst[i].data[0] |= (pan & 3) << 4; // set pan
+ else
+ inst[i].data[0] |= 48; // enable both speakers
+ }
+
+ inst[i].slide = *(orgptr+i*13+12);
+ }
+
+ orgptr += 250*13;
+ memcpy(order,orgptr,128); orgptr += 128;
+ bpm = *orgptr; orgptr++;
+ initspeed = *orgptr; orgptr++;
+ if(version >= 5) flags = *orgptr;
+ if(version == 1 || version == 5) delete [] org;
+ delete [] secdata;
+
+ // blocks 1-4 or 1-8
+ alength = len[1];
+ for(i = 0; i < (version < 5 ? numpats / 16 : numpats / 8); i++)
+ alength += len[i+2];
+
+ secdata = new unsigned short [alength / 2];
+ if(version == 1 || version == 5) {
+ for(l=0;l<alength/2;l++) secdata[l] = f->readInt(2);
+ org = new unsigned char [MAXBUF * (numpats / (version == 1 ? 16 : 8) + 1)];
+ orgptr = org; secptr = secdata;
+ orgptr += sixdepak(secptr,orgptr,len[1]); secptr += len[1] / 2;
+ if(version == 1) {
+ if(numpats > 16)
+ orgptr += sixdepak(secptr,orgptr,len[2]); secptr += len[2] / 2;
+ if(numpats > 32)
+ orgptr += sixdepak(secptr,orgptr,len[3]); secptr += len[3] / 2;
+ if(numpats > 48)
+ sixdepak(secptr,orgptr,len[4]);
+ } else {
+ if(numpats > 8)
+ orgptr += sixdepak(secptr,orgptr,len[2]); secptr += len[2] / 2;
+ if(numpats > 16)
+ orgptr += sixdepak(secptr,orgptr,len[3]); secptr += len[3] / 2;
+ if(numpats > 24)
+ orgptr += sixdepak(secptr,orgptr,len[4]); secptr += len[4] / 2;
+ if(numpats > 32)
+ orgptr += sixdepak(secptr,orgptr,len[5]); secptr += len[5] / 2;
+ if(numpats > 40)
+ orgptr += sixdepak(secptr,orgptr,len[6]); secptr += len[6] / 2;
+ if(numpats > 48)
+ orgptr += sixdepak(secptr,orgptr,len[7]); secptr += len[7] / 2;
+ if(numpats > 56)
+ sixdepak(secptr,orgptr,len[8]);
+ }
+ delete [] secdata;
+ } else {
+ org = (unsigned char *)secdata;
+ for(l=0;l<alength;l++) org[l] = f->readInt(1);
+ }
+
+ if(version < 5) {
+ for(i=0;i<numpats;i++)
+ for(j=0;j<64;j++)
+ for(k=0;k<9;k++) {
+ struct Tracks *track = &tracks[i * 9 + k][j];
+ unsigned char *o = &org[i*64*t*4+j*t*4+k*4];
+
+ track->note = o[0] == 255 ? 127 : o[0];
+ track->inst = o[1];
+ track->command = convfx[o[2]];
+ track->param2 = o[3] & 0x0f;
+ if(track->command != 14)
+ track->param1 = o[3] >> 4;
+ else {
+ track->param1 = convinf1[o[3] >> 4];
+ if(track->param1 == 15 && !track->param2) { // convert key-off
+ track->command = 8;
+ track->param1 = 0;
+ track->param2 = 0;
+ }
+ }
+ if(track->command == 14) {
+ switch(track->param1) {
+ case 2: // convert define waveform
+ track->command = 25;
+ track->param1 = track->param2;
+ track->param2 = 0xf;
+ break;
+ case 8: // convert volume slide up
+ track->command = 26;
+ track->param1 = track->param2;
+ track->param2 = 0;
+ break;
+ case 9: // convert volume slide down
+ track->command = 26;
+ track->param1 = 0;
+ break;
+ }
+ }
+ }
+ } else { // version >= 5
+ realloc_patterns(64, 64, 18);
+
+ for(i=0;i<numpats;i++)
+ for(j=0;j<18;j++)
+ for(k=0;k<64;k++) {
+ struct Tracks *track = &tracks[i * 18 + j][k];
+ unsigned char *o = &org[i*64*t*4+j*64*4+k*4];
+
+ track->note = o[0] == 255 ? 127 : o[0];
+ track->inst = o[1];
+ track->command = newconvfx[o[2]];
+ track->param1 = o[3] >> 4;
+ track->param2 = o[3] & 0x0f;
+
+ // Convert '&' command
+ if(o[2] == 36)
+ switch(track->param1) {
+ case 0: // pattern delay (frames)
+ track->command = 29;
+ track->param1 = 0;
+ // param2 already set correctly
+ break;
+
+ case 1: // pattern delay (rows)
+ track->command = 14;
+ track->param1 = 8;
+ // param2 already set correctly
+ break;
+ }
+ }
+ }
+
+ init_trackord();
+
+ if(version == 1 || version == 5)
+ delete [] org;
+ else
+ delete [] secdata;
+
+ // Process flags
+ if(version >= 5) {
+ CmodPlayer::flags |= Opl3; // All versions >= 5 are OPL3
+ if(flags & 8) CmodPlayer::flags |= Tremolo; // Tremolo depth
+ if(flags & 16) CmodPlayer::flags |= Vibrato; // Vibrato depth
+ }
+
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+float Ca2mLoader::getrefresh()
+{
+ if(tempo != 18)
+ return (float) (tempo);
+ else
+ return 18.2f;
+}
+
+/*** private methods *************************************/
+
+void Ca2mLoader::inittree()
+{
+ unsigned short i;
+
+ for(i=2;i<=TWICEMAX;i++) {
+ dad[i] = i / 2;
+ freq[i] = 1;
+ }
+
+ for(i=1;i<=MAXCHAR;i++) {
+ leftc[i] = 2 * i;
+ rghtc[i] = 2 * i + 1;
+ }
+}
+
+void Ca2mLoader::updatefreq(unsigned short a,unsigned short b)
+{
+ do {
+ freq[dad[a]] = freq[a] + freq[b];
+ a = dad[a];
+ if(a != ROOT)
+ if(leftc[dad[a]] == a)
+ b = rghtc[dad[a]];
+ else
+ b = leftc[dad[a]];
+ } while(a != ROOT);
+
+ if(freq[ROOT] == MAXFREQ)
+ for(a=1;a<=TWICEMAX;a++)
+ freq[a] >>= 1;
+}
+
+void Ca2mLoader::updatemodel(unsigned short code)
+{
+ unsigned short a=code+SUCCMAX,b,c,code1,code2;
+
+ freq[a]++;
+ if(dad[a] != ROOT) {
+ code1 = dad[a];
+ if(leftc[code1] == a)
+ updatefreq(a,rghtc[code1]);
+ else
+ updatefreq(a,leftc[code1]);
+
+ do {
+ code2 = dad[code1];
+ if(leftc[code2] == code1)
+ b = rghtc[code2];
+ else
+ b = leftc[code2];
+
+ if(freq[a] > freq[b]) {
+ if(leftc[code2] == code1)
+ rghtc[code2] = a;
+ else
+ leftc[code2] = a;
+
+ if(leftc[code1] == a) {
+ leftc[code1] = b;
+ c = rghtc[code1];
+ } else {
+ rghtc[code1] = b;
+ c = leftc[code1];
+ }
+
+ dad[b] = code1;
+ dad[a] = code2;
+ updatefreq(b,c);
+ a = b;
+ }
+
+ a = dad[a];
+ code1 = dad[a];
+ } while(code1 != ROOT);
+ }
+}
+
+unsigned short Ca2mLoader::inputcode(unsigned short bits)
+{
+ unsigned short i,code=0;
+
+ for(i=1;i<=bits;i++) {
+ if(!ibitcount) {
+ if(ibitcount == MAXBUF)
+ ibufcount = 0;
+ ibitbuffer = wdbuf[ibufcount];
+ ibufcount++;
+ ibitcount = 15;
+ } else
+ ibitcount--;
+
+ if(ibitbuffer > 0x7fff)
+ code |= bitvalue[i-1];
+ ibitbuffer <<= 1;
+ }
+
+ return code;
+}
+
+unsigned short Ca2mLoader::uncompress()
+{
+ unsigned short a=1;
+
+ do {
+ if(!ibitcount) {
+ if(ibufcount == MAXBUF)
+ ibufcount = 0;
+ ibitbuffer = wdbuf[ibufcount];
+ ibufcount++;
+ ibitcount = 15;
+ } else
+ ibitcount--;
+
+ if(ibitbuffer > 0x7fff)
+ a = rghtc[a];
+ else
+ a = leftc[a];
+ ibitbuffer <<= 1;
+ } while(a <= MAXCHAR);
+
+ a -= SUCCMAX;
+ updatemodel(a);
+ return a;
+}
+
+void Ca2mLoader::decode()
+{
+ unsigned short i,j,k,t,c,count=0,dist,len,index;
+
+ inittree();
+ c = uncompress();
+
+ while(c != TERMINATE) {
+ if(c < 256) {
+ obuf[obufcount] = (unsigned char)c;
+ obufcount++;
+ if(obufcount == MAXBUF) {
+ output_size = MAXBUF;
+ obufcount = 0;
+ }
+
+ buf[count] = (unsigned char)c;
+ count++;
+ if(count == MAXSIZE)
+ count = 0;
+ } else {
+ t = c - FIRSTCODE;
+ index = t / CODESPERRANGE;
+ len = t + MINCOPY - index * CODESPERRANGE;
+ dist = inputcode(copybits[index]) + len + copymin[index];
+
+ j = count;
+ k = count - dist;
+ if(count < dist)
+ k += MAXSIZE;
+
+ for(i=0;i<=len-1;i++) {
+ obuf[obufcount] = buf[k];
+ obufcount++;
+ if(obufcount == MAXBUF) {
+ output_size = MAXBUF;
+ obufcount = 0;
+ }
+
+ buf[j] = buf[k];
+ j++; k++;
+ if(j == MAXSIZE) j = 0;
+ if(k == MAXSIZE) k = 0;
+ }
+
+ count += len;
+ if(count >= MAXSIZE)
+ count -= MAXSIZE;
+ }
+ c = uncompress();
+ }
+ output_size = obufcount;
+}
+
+unsigned short Ca2mLoader::sixdepak(unsigned short *source, unsigned char *dest,
+ unsigned short size)
+{
+ if((unsigned int)size + 4096 > MAXBUF)
+ return 0;
+
+ buf = new unsigned char [MAXSIZE];
+ input_size = size;
+ ibitcount = 0; ibitbuffer = 0;
+ obufcount = 0; ibufcount = 0;
+ wdbuf = source; obuf = dest;
+
+ decode();
+ delete [] buf;
+ return output_size;
+}
diff --git a/plugins/adplug/adplug/a2m.h b/plugins/adplug/adplug/a2m.h
new file mode 100644
index 00000000..9e032f61
--- /dev/null
+++ b/plugins/adplug/adplug/a2m.h
@@ -0,0 +1,83 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * a2m.h - A2M Loader by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_A2MLOADER
+#define H_ADPLUG_A2MLOADER
+
+#include "protrack.h"
+
+class Ca2mLoader: public CmodPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ Ca2mLoader(Copl *newopl): CmodPlayer(newopl)
+ { }
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ float getrefresh();
+
+ std::string gettype()
+ { return std::string("AdLib Tracker 2"); }
+ std::string gettitle()
+ { if(*songname) return std::string(songname,1,*songname); else return std::string(); }
+ std::string getauthor()
+ { if(*author) return std::string(author,1,*author); else return std::string(); }
+ unsigned int getinstruments()
+ { return 250; }
+ std::string getinstrument(unsigned int n)
+ { return std::string(instname[n],1,*instname[n]); }
+
+private:
+
+#define ADPLUG_A2M_COPYRANGES 6
+#define ADPLUG_A2M_FIRSTCODE 257
+#define ADPLUG_A2M_MINCOPY 3
+#define ADPLUG_A2M_MAXCOPY 255
+#define ADPLUG_A2M_CODESPERRANGE (ADPLUG_A2M_MAXCOPY - ADPLUG_A2M_MINCOPY + 1)
+#define ADPLUG_A2M_MAXCHAR (ADPLUG_A2M_FIRSTCODE + ADPLUG_A2M_COPYRANGES * ADPLUG_A2M_CODESPERRANGE - 1)
+#define ADPLUG_A2M_TWICEMAX (2 * ADPLUG_A2M_MAXCHAR + 1)
+
+ static const unsigned int MAXFREQ, MINCOPY, MAXCOPY, COPYRANGES,
+ CODESPERRANGE, TERMINATE, FIRSTCODE, MAXCHAR, SUCCMAX, TWICEMAX, ROOT,
+ MAXBUF, MAXDISTANCE, MAXSIZE;
+
+ static const unsigned short bitvalue[14];
+ static const signed short copybits[ADPLUG_A2M_COPYRANGES],
+ copymin[ADPLUG_A2M_COPYRANGES];
+
+ void inittree();
+ void updatefreq(unsigned short a,unsigned short b);
+ void updatemodel(unsigned short code);
+ unsigned short inputcode(unsigned short bits);
+ unsigned short uncompress();
+ void decode();
+ unsigned short sixdepak(unsigned short *source,unsigned char *dest,unsigned short size);
+
+ char songname[43], author[43], instname[250][33];
+
+ unsigned short ibitcount, ibitbuffer, ibufcount, obufcount, input_size,
+ output_size, leftc[ADPLUG_A2M_MAXCHAR+1], rghtc[ADPLUG_A2M_MAXCHAR+1],
+ dad[ADPLUG_A2M_TWICEMAX+1], freq[ADPLUG_A2M_TWICEMAX+1], *wdbuf;
+ unsigned char *obuf, *buf;
+};
+
+#endif
diff --git a/plugins/adplug/adplug/adl.cpp b/plugins/adplug/adplug/adl.cpp
new file mode 100644
index 00000000..40896fdb
--- /dev/null
+++ b/plugins/adplug/adplug/adl.cpp
@@ -0,0 +1,2431 @@
+/*
+ * adl.cpp - ADL player adaption by Simon Peter <dn.tlp@gmx.net>
+ *
+ * Original ADL player by Torbjorn Andersson and Johannes Schickel
+ * 'lordhoto' <lordhoto at scummvm dot org> of the ScummVM project.
+ */
+
+/* ScummVM - Scumm Interpreter
+ *
+ * This file is licensed under both GPL and LGPL
+ * Copyright (C) 2006 The ScummVM project
+ * Copyright (C) 2006 Torbjorn Andersson and Johannes Schickel
+ *
+ * GPL License
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program 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 General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * LPGL License
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * $URL: https://svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/engines/kyra/sound_adlib.cpp $
+ * $Id: adl.cpp,v 1.9 2006/08/16 00:20:45 dynamite Exp $
+ *
+ */
+
+#include <inttypes.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+
+#include "adl.h"
+#include "debug.h"
+
+#ifdef ADL_DEBUG
+# define warning(...) AdPlug_LogWrite(__VA_ARGS__); \
+AdPlug_LogWrite("\n")
+
+# define debugC(i1, i2, ...) AdPlug_LogWrite(__VA_ARGS__); \
+AdPlug_LogWrite("\n")
+#else
+# define kDebugLevelSound 1
+
+static inline void warning(const char *str, ...)
+{
+}
+
+static inline void debugC(int i1, int i2, const char *str, ...)
+{
+}
+#endif
+
+// #define warning(...)
+// #define debugC(i1, i2, ...)
+
+#define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0])))
+
+// Basic Adlib Programming:
+// http://www.gamedev.net/reference/articles/article446.asp
+
+#define CALLBACKS_PER_SECOND 72
+
+typedef uint8_t uint8;
+typedef int8_t int8;
+typedef uint16_t uint16;
+typedef int16_t int16;
+typedef uint32_t uint32;
+typedef int32_t int32;
+typedef uint8_t byte;
+
+static inline uint16 READ_LE_UINT16(const void *ptr) {
+ const byte *b = (const byte *)ptr;
+ return (b[1] << 8) + b[0];
+}
+
+static inline uint16 READ_BE_UINT16(const void *ptr) {
+ const byte *b = (const byte *)ptr;
+ return (b[0] << 8) + b[1];
+}
+
+class AdlibDriver {
+public:
+ AdlibDriver(Copl *opl);
+ ~AdlibDriver();
+
+ int callback(int opcode, ...);
+ void callback();
+
+ // AudioStream API
+ // int readBuffer(int16 *buffer, const int numSamples) {
+ // int32 samplesLeft = numSamples;
+ // memset(buffer, 0, sizeof(int16) * numSamples);
+ // while (samplesLeft) {
+ // if (!_samplesTillCallback) {
+ // callback();
+ // _samplesTillCallback = _samplesPerCallback;
+ // _samplesTillCallbackRemainder += _samplesPerCallbackRemainder;
+ // if (_samplesTillCallbackRemainder >= CALLBACKS_PER_SECOND) {
+ // _samplesTillCallback++;
+ // _samplesTillCallbackRemainder -= CALLBACKS_PER_SECOND;
+ // }
+ // }
+
+ // int32 render = MIN(samplesLeft, _samplesTillCallback);
+ // samplesLeft -= render;
+ // _samplesTillCallback -= render;
+ // YM3812UpdateOne(_adlib, buffer, render);
+ // buffer += render;
+ // }
+ // return numSamples;
+ // }
+
+ bool isStereo() const { return false; }
+ bool endOfData() const { return false; }
+ // int getRate() const { return _mixer->getOutputRate(); }
+
+ struct OpcodeEntry {
+ typedef int (AdlibDriver::*DriverOpcode)(va_list &list);
+ DriverOpcode function;
+ const char *name;
+ };
+
+ void setupOpcodeList();
+ const OpcodeEntry *_opcodeList;
+ int _opcodesEntries;
+
+ int snd_ret0x100(va_list &list);
+ int snd_ret0x1983(va_list &list);
+ int snd_initDriver(va_list &list);
+ int snd_deinitDriver(va_list &list);
+ int snd_setSoundData(va_list &list);
+ int snd_unkOpcode1(va_list &list);
+ int snd_startSong(va_list &list);
+ int snd_unkOpcode2(va_list &list);
+ int snd_unkOpcode3(va_list &list);
+ int snd_readByte(va_list &list);
+ int snd_writeByte(va_list &list);
+ int snd_getSoundTrigger(va_list &list);
+ int snd_unkOpcode4(va_list &list);
+ int snd_dummy(va_list &list);
+ int snd_getNullvar4(va_list &list);
+ int snd_setNullvar3(va_list &list);
+ int snd_setFlag(va_list &list);
+ int snd_clearFlag(va_list &list);
+
+ // These variables have not yet been named, but some of them are partly
+ // known nevertheless:
+ //
+ // unk16 - Sound-related. Possibly some sort of pitch bend.
+ // unk18 - Sound-effect. Used for secondaryEffect1()
+ // unk19 - Sound-effect. Used for secondaryEffect1()
+ // unk20 - Sound-effect. Used for secondaryEffect1()
+ // unk21 - Sound-effect. Used for secondaryEffect1()
+ // unk22 - Sound-effect. Used for secondaryEffect1()
+ // unk29 - Sound-effect. Used for primaryEffect1()
+ // unk30 - Sound-effect. Used for primaryEffect1()
+ // unk31 - Sound-effect. Used for primaryEffect1()
+ // unk32 - Sound-effect. Used for primaryEffect2()
+ // unk33 - Sound-effect. Used for primaryEffect2()
+ // unk34 - Sound-effect. Used for primaryEffect2()
+ // unk35 - Sound-effect. Used for primaryEffect2()
+ // unk36 - Sound-effect. Used for primaryEffect2()
+ // unk37 - Sound-effect. Used for primaryEffect2()
+ // unk38 - Sound-effect. Used for primaryEffect2()
+ // unk39 - Currently unused, except for updateCallback56()
+ // unk40 - Currently unused, except for updateCallback56()
+ // unk41 - Sound-effect. Used for primaryEffect2()
+
+ struct Channel {
+ uint8 opExtraLevel2;
+ uint8 *dataptr;
+ uint8 duration;
+ uint8 repeatCounter;
+ int8 baseOctave;
+ uint8 priority;
+ uint8 dataptrStackPos;
+ uint8 *dataptrStack[4];
+ int8 baseNote;
+ uint8 unk29;
+ uint8 unk31;
+ uint16 unk30;
+ uint16 unk37;
+ uint8 unk33;
+ uint8 unk34;
+ uint8 unk35;
+ uint8 unk36;
+ uint8 unk32;
+ uint8 unk41;
+ uint8 unk38;
+ uint8 opExtraLevel1;
+ uint8 spacing2;
+ uint8 baseFreq;
+ uint8 tempo;
+ uint8 position;
+ uint8 regAx;
+ uint8 regBx;
+ typedef void (AdlibDriver::*Callback)(Channel&);
+ Callback primaryEffect;
+ Callback secondaryEffect;
+ uint8 fractionalSpacing;
+ uint8 opLevel1;
+ uint8 opLevel2;
+ uint8 opExtraLevel3;
+ uint8 twoChan;
+ uint8 unk39;
+ uint8 unk40;
+ uint8 spacing1;
+ uint8 durationRandomness;
+ uint8 unk19;
+ uint8 unk18;
+ int8 unk20;
+ int8 unk21;
+ uint8 unk22;
+ uint16 offset;
+ uint8 tempoReset;
+ uint8 rawNote;
+ int8 unk16;
+ };
+
+ void primaryEffect1(Channel &channel);
+ void primaryEffect2(Channel &channel);
+ void secondaryEffect1(Channel &channel);
+
+ void resetAdlibState();
+ void writeOPL(byte reg, byte val);
+ void initChannel(Channel &channel);
+ void noteOff(Channel &channel);
+ void unkOutput2(uint8 num);
+
+ uint16 getRandomNr();
+ void setupDuration(uint8 duration, Channel &channel);
+
+ void setupNote(uint8 rawNote, Channel &channel, bool flag = false);
+ void setupInstrument(uint8 regOffset, uint8 *dataptr, Channel &channel);
+ void noteOn(Channel &channel);
+
+ void adjustVolume(Channel &channel);
+
+ uint8 calculateOpLevel1(Channel &channel);
+ uint8 calculateOpLevel2(Channel &channel);
+
+ uint16 checkValue(int16 val) {
+ if (val < 0)
+ val = 0;
+ else if (val > 0x3F)
+ val = 0x3F;
+ return val;
+ }
+
+ // The sound data has at least two lookup tables:
+ //
+ // * One for programs, starting at offset 0.
+ // * One for instruments, starting at offset 500.
+
+ uint8 *getProgram(int progId) {
+ return _soundData + READ_LE_UINT16(_soundData + 2 * progId);
+ }
+
+ uint8 *getInstrument(int instrumentId) {
+ return _soundData + READ_LE_UINT16(_soundData + 500 + 2 * instrumentId);
+ }
+
+ void setupPrograms();
+ void executePrograms();
+
+ struct ParserOpcode {
+ typedef int (AdlibDriver::*POpcode)(uint8 *&dataptr, Channel &channel, uint8 value);
+ POpcode function;
+ const char *name;
+ };
+
+ void setupParserOpcodeTable();
+ const ParserOpcode *_parserOpcodeTable;
+ int _parserOpcodeTableSize;
+
+ int update_setRepeat(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_checkRepeat(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupProgram(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_jump(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_jumpToSubroutine(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_returnFromSubroutine(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setBaseOctave(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_stopChannel(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_playRest(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_writeAdlib(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupNoteAndDuration(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setBaseNote(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_stopOtherChannel(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_waitForEndOfProgram(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupInstrument(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupPrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_removePrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setBaseFreq(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupPrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setPriority(uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback23(uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback24(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupDuration(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_playNote(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setFractionalNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setTempo(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_removeSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setExtraLevel3(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_changeExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setAMDepth(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setVibratoDepth(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_changeExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback38(uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback39(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_removePrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback41(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_resetToGlobalTempo(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_nop1(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setDurationRandomness(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_changeChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback46(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_nop2(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_playRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_removeRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback51(uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback52(uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback53(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setSoundTrigger(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setTempoReset(uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback56(uint8 *&dataptr, Channel &channel, uint8 value);
+
+ // These variables have not yet been named, but some of them are partly
+ // known nevertheless:
+ //
+ // _unkValue1 - Unknown. Used for updating _unkValue2
+ // _unkValue2 - Unknown. Used for updating _unkValue4
+ // _unkValue3 - Unknown. Used for updating _unkValue2
+ // _unkValue4 - Unknown. Used for updating _unkValue5
+ // _unkValue5 - Unknown. Used for controlling updateCallback24().
+ // _unkValue6 - Unknown. Rhythm section volume?
+ // _unkValue7 - Unknown. Rhythm section volume?
+ // _unkValue8 - Unknown. Rhythm section volume?
+ // _unkValue9 - Unknown. Rhythm section volume?
+ // _unkValue10 - Unknown. Rhythm section volume?
+ // _unkValue11 - Unknown. Rhythm section volume?
+ // _unkValue12 - Unknown. Rhythm section volume?
+ // _unkValue13 - Unknown. Rhythm section volume?
+ // _unkValue14 - Unknown. Rhythm section volume?
+ // _unkValue15 - Unknown. Rhythm section volume?
+ // _unkValue16 - Unknown. Rhythm section volume?
+ // _unkValue17 - Unknown. Rhythm section volume?
+ // _unkValue18 - Unknown. Rhythm section volume?
+ // _unkValue19 - Unknown. Rhythm section volume?
+ // _unkValue20 - Unknown. Rhythm section volume?
+ // _unkTable[] - Probably frequences for the 12-tone scale.
+ // _unkTable2[] - Unknown. Currently only used by updateCallback46()
+ // _unkTable2_1[] - One of the tables in _unkTable2[]
+ // _unkTable2_2[] - One of the tables in _unkTable2[]
+ // _unkTable2_3[] - One of the tables in _unkTable2[]
+
+ int32 _samplesPerCallback;
+ int32 _samplesPerCallbackRemainder;
+ int32 _samplesTillCallback;
+ int32 _samplesTillCallbackRemainder;
+
+ int _lastProcessed;
+ int8 _flagTrigger;
+ int _curChannel;
+ uint8 _soundTrigger;
+ int _soundsPlaying;
+
+ uint16 _rnd;
+
+ uint8 _unkValue1;
+ uint8 _unkValue2;
+ uint8 _unkValue3;
+ uint8 _unkValue4;
+ uint8 _unkValue5;
+ uint8 _unkValue6;
+ uint8 _unkValue7;
+ uint8 _unkValue8;
+ uint8 _unkValue9;
+ uint8 _unkValue10;
+ uint8 _unkValue11;
+ uint8 _unkValue12;
+ uint8 _unkValue13;
+ uint8 _unkValue14;
+ uint8 _unkValue15;
+ uint8 _unkValue16;
+ uint8 _unkValue17;
+ uint8 _unkValue18;
+ uint8 _unkValue19;
+ uint8 _unkValue20;
+
+ int _flags;
+
+ uint8 *_soundData;
+
+ uint8 _soundIdTable[0x10];
+ Channel _channels[10];
+
+ uint8 _vibratoAndAMDepthBits;
+ uint8 _rhythmSectionBits;
+
+ uint8 _curRegOffset;
+ uint8 _tempo;
+
+ const uint8 *_tablePtr1;
+ const uint8 *_tablePtr2;
+
+ static const uint8 _regOffset[];
+ static const uint16 _unkTable[];
+ static const uint8 *_unkTable2[];
+ static const uint8 _unkTable2_1[];
+ static const uint8 _unkTable2_2[];
+ static const uint8 _unkTable2_3[];
+ static const uint8 _unkTables[][32];
+
+ Copl *opl;
+};
+
+AdlibDriver::AdlibDriver(Copl *newopl)
+ : opl(newopl)
+{
+ setupOpcodeList();
+ setupParserOpcodeTable();
+
+ // _mixer = mixer;
+
+ _flags = 0;
+ // _adlib = makeAdlibOPL(getRate());
+ // assert(_adlib);
+
+ memset(_channels, 0, sizeof(_channels));
+ _soundData = 0;
+
+ _vibratoAndAMDepthBits = _curRegOffset = 0;
+
+ _lastProcessed = _flagTrigger = _curChannel = _rhythmSectionBits = 0;
+ _soundsPlaying = 0;
+ _rnd = 0x1234;
+
+ _tempo = 0;
+ _soundTrigger = 0;
+
+ _unkValue3 = 0xFF;
+ _unkValue1 = _unkValue2 = _unkValue4 = _unkValue5 = 0;
+ _unkValue6 = _unkValue7 = _unkValue8 = _unkValue9 = _unkValue10 = 0;
+ _unkValue11 = _unkValue12 = _unkValue13 = _unkValue14 = _unkValue15 =
+ _unkValue16 = _unkValue17 = _unkValue18 = _unkValue19 = _unkValue20 = 0;
+
+ _tablePtr1 = _tablePtr2 = 0;
+
+ // _mixer->setupPremix(this);
+
+ // _samplesPerCallback = getRate() / CALLBACKS_PER_SECOND;
+ // _samplesPerCallbackRemainder = getRate() % CALLBACKS_PER_SECOND;
+ _samplesTillCallback = 0;
+ _samplesTillCallbackRemainder = 0;
+}
+
+AdlibDriver::~AdlibDriver() {
+ // _mixer->setupPremix(0);
+ // OPLDestroy(_adlib);
+ // _adlib = 0;
+}
+
+int AdlibDriver::callback(int opcode, ...) {
+ // lock();
+ if (opcode >= _opcodesEntries || opcode < 0) {
+ warning("AdlibDriver: calling unknown opcode '%d'", opcode);
+ return 0;
+ }
+
+ debugC(9, kDebugLevelSound, "Calling opcode '%s' (%d)", _opcodeList[opcode].name, opcode);
+
+ va_list args;
+ va_start(args, opcode);
+ int returnValue = (this->*(_opcodeList[opcode].function))(args);
+ va_end(args);
+ // unlock();
+ return returnValue;
+}
+
+// Opcodes
+
+int AdlibDriver::snd_ret0x100(va_list &list) {
+ return 0x100;
+}
+
+int AdlibDriver::snd_ret0x1983(va_list &list) {
+ return 0x1983;
+}
+
+int AdlibDriver::snd_initDriver(va_list &list) {
+ _lastProcessed = _soundsPlaying = 0;
+ resetAdlibState();
+ return 0;
+}
+
+int AdlibDriver::snd_deinitDriver(va_list &list) {
+ resetAdlibState();
+ return 0;
+}
+
+int AdlibDriver::snd_setSoundData(va_list &list) {
+ if (_soundData) {
+ delete [] _soundData;
+ _soundData = 0;
+ }
+ _soundData = va_arg(list, uint8*);
+ return 0;
+}
+
+int AdlibDriver::snd_unkOpcode1(va_list &list) {
+ warning("unimplemented snd_unkOpcode1");
+ return 0;
+}
+
+int AdlibDriver::snd_startSong(va_list &list) {
+ int songId = va_arg(list, int);
+ _flags |= 8;
+ _flagTrigger = 1;
+
+ uint8 *ptr = getProgram(songId);
+ uint8 chan = *ptr;
+
+ if ((songId << 1) != 0) {
+ if (chan == 9) {
+ if (_flags & 2)
+ return 0;
+ } else {
+ if (_flags & 1)
+ return 0;
+ }
+ }
+
+ _soundIdTable[_soundsPlaying++] = songId;
+ _soundsPlaying &= 0x0F;
+
+ return 0;
+}
+
+int AdlibDriver::snd_unkOpcode2(va_list &list) {
+ warning("unimplemented snd_unkOpcode2");
+ return 0;
+}
+
+int AdlibDriver::snd_unkOpcode3(va_list &list) {
+ int value = va_arg(list, int);
+ int loop = value;
+ if (value < 0) {
+ value = 0;
+ loop = 9;
+ }
+ loop -= value;
+ ++loop;
+
+ while (loop--) {
+ _curChannel = value;
+ Channel &channel = _channels[_curChannel];
+ channel.priority = 0;
+ channel.dataptr = 0;
+ if (value != 9) {
+ noteOff(channel);
+ }
+ ++value;
+ }
+
+ return 0;
+}
+
+int AdlibDriver::snd_readByte(va_list &list) {
+ int a = va_arg(list, int);
+ int b = va_arg(list, int);
+ uint8 *ptr = getProgram(a) + b;
+ return *ptr;
+}
+
+int AdlibDriver::snd_writeByte(va_list &list) {
+ int a = va_arg(list, int);
+ int b = va_arg(list, int);
+ int c = va_arg(list, int);
+ uint8 *ptr = getProgram(a) + b;
+ uint8 oldValue = *ptr;
+ *ptr = (uint8)c;
+ return oldValue;
+}
+
+int AdlibDriver::snd_getSoundTrigger(va_list &list) {
+ return _soundTrigger;
+}
+
+int AdlibDriver::snd_unkOpcode4(va_list &list) {
+ warning("unimplemented snd_unkOpcode4");
+ return 0;
+}
+
+int AdlibDriver::snd_dummy(va_list &list) {
+ return 0;
+}
+
+int AdlibDriver::snd_getNullvar4(va_list &list) {
+ warning("unimplemented snd_getNullvar4");
+ return 0;
+}
+
+int AdlibDriver::snd_setNullvar3(va_list &list) {
+ warning("unimplemented snd_setNullvar3");
+ return 0;
+}
+
+int AdlibDriver::snd_setFlag(va_list &list) {
+ int oldFlags = _flags;
+ _flags |= va_arg(list, int);
+ return oldFlags;
+}
+
+int AdlibDriver::snd_clearFlag(va_list &list) {
+ int oldFlags = _flags;
+ _flags &= ~(va_arg(list, int));
+ return oldFlags;
+}
+
+// timer callback
+
+void AdlibDriver::callback() {
+ // lock();
+ --_flagTrigger;
+ if (_flagTrigger < 0)
+ _flags &= ~8;
+ setupPrograms();
+ executePrograms();
+
+ uint8 temp = _unkValue3;
+ _unkValue3 += _tempo;
+ if (_unkValue3 < temp) {
+ if (!(--_unkValue2)) {
+ _unkValue2 = _unkValue1;
+ ++_unkValue4;
+ }
+ }
+ // unlock();
+}
+
+void AdlibDriver::setupPrograms() {
+ while (_lastProcessed != _soundsPlaying) {
+ uint8 *ptr = getProgram(_soundIdTable[_lastProcessed]);
+ uint8 chan = *ptr++;
+ uint8 priority = *ptr++;
+
+ // Only start this sound if its priority is higher than the one
+ // already playing.
+
+ Channel &channel = _channels[chan];
+
+ if (priority >= channel.priority) {
+ initChannel(channel);
+ channel.priority = priority;
+ channel.dataptr = ptr;
+ channel.tempo = 0xFF;
+ channel.position = 0xFF;
+ channel.duration = 1;
+ unkOutput2(chan);
+ }
+
+ ++_lastProcessed;
+ _lastProcessed &= 0x0F;
+ }
+}
+
+// A few words on opcode parsing and timing:
+//
+// First of all, We simulate a timer callback 72 times per second. Each timeout
+// we update each channel that has something to play.
+//
+// Each channel has its own individual tempo, which is added to its position.
+// This will frequently cause the position to "wrap around" but that is
+// intentional. In fact, it's the signal to go ahead and do more stuff with
+// that channel.
+//
+// Each channel also has a duration, indicating how much time is left on the
+// its current task. This duration is decreased by one. As long as it still has
+// not reached zero, the only thing that can happen is that the note is turned
+// off depending on manual or automatic note spacing. Once the duration reaches
+// zero, a new set of musical opcodes are executed.
+//
+// An opcode is one byte, followed by a variable number of parameters. Since
+// most opcodes have at least one one-byte parameter, we read that as well. Any
+// opcode that doesn't have that one parameter is responsible for moving the
+// data pointer back again.
+//
+// If the most significant bit of the opcode is 1, it's a function; call it.
+// The opcode functions return either 0 (continue), 1 (stop) or 2 (stop, and do
+// not run the effects callbacks).
+//
+// If the most significant bit of the opcode is 0, it's a note, and the first
+// parameter is its duration. (There are cases where the duration is modified
+// but that's an exception.) The note opcode is assumed to return 1, and is the
+// last opcode unless its duration is zero.
+//
+// Finally, most of the times that the callback is called, it will invoke the
+// effects callbacks. The final opcode in a set can prevent this, if it's a
+// function and it returns anything other than 1.
+
+void AdlibDriver::executePrograms() {
+ // Each channel runs its own program. There are ten channels: One for
+ // each Adlib channel (0-8), plus one "control channel" (9) which is
+ // the one that tells the other channels what to do.
+
+ for (_curChannel = 9; _curChannel >= 0; --_curChannel) {
+ int result = 1;
+
+ if (!_channels[_curChannel].dataptr) {
+ continue;
+ }
+
+ Channel &channel = _channels[_curChannel];
+ _curRegOffset = _regOffset[_curChannel];
+
+ if (channel.tempoReset) {
+ channel.tempo = _tempo;
+ }
+
+ uint8 backup = channel.position;
+ channel.position += channel.tempo;
+ if (channel.position < backup) {
+ if (--channel.duration) {
+ if (channel.duration == channel.spacing2)
+ noteOff(channel);
+ if (channel.duration == channel.spacing1 && _curChannel != 9)
+ noteOff(channel);
+ } else {
+ // An opcode is not allowed to modify its own
+ // data pointer except through the 'dataptr'
+ // parameter. To enforce that, we have to work
+ // on a copy of the data pointer.
+ //
+ // This fixes a subtle music bug where the
+ // wrong music would play when getting the
+ // quill in Kyra 1.
+ uint8 *dataptr = channel.dataptr;
+ while (dataptr) {
+ uint8 opcode = *dataptr++;
+ uint8 param = *dataptr++;
+
+ if (opcode & 0x80) {
+ opcode &= 0x7F;
+ if (opcode >= _parserOpcodeTableSize)
+ opcode = _parserOpcodeTableSize - 1;
+ debugC(9, kDebugLevelSound, "Calling opcode '%s' (%d) (channel: %d)", _parserOpcodeTable[opcode].name, opcode, _curChannel);
+ result = (this->*(_parserOpcodeTable[opcode].function))(dataptr, channel, param);
+ channel.dataptr = dataptr;
+ if (result)
+ break;
+ } else {
+ debugC(9, kDebugLevelSound, "Note on opcode 0x%02X (duration: %d) (channel: %d)", opcode, param, _curChannel);
+ setupNote(opcode, channel);
+ noteOn(channel);
+ setupDuration(param, channel);
+ if (param) {
+ channel.dataptr = dataptr;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (result == 1) {
+ if (channel.primaryEffect)
+ (this->*(channel.primaryEffect))(channel);
+ if (channel.secondaryEffect)
+ (this->*(channel.secondaryEffect))(channel);
+ }
+ }
+}
+
+//
+
+void AdlibDriver::resetAdlibState() {
+ debugC(9, kDebugLevelSound, "resetAdlibState()");
+ _rnd = 0x1234;
+
+ // Authorize the control of the waveforms
+ writeOPL(0x01, 0x20);
+
+ // Select FM music mode
+ writeOPL(0x08, 0x00);
+
+ // I would guess the main purpose of this is to turn off the rhythm,
+ // thus allowing us to use 9 melodic voices instead of 6.
+ writeOPL(0xBD, 0x00);
+
+ int loop = 10;
+ while (loop--) {
+ if (loop != 9) {
+ // Silence the channel
+ writeOPL(0x40 + _regOffset[loop], 0x3F);
+ writeOPL(0x43 + _regOffset[loop], 0x3F);
+ }
+ initChannel(_channels[loop]);
+ }
+}
+
+// Old calling style: output0x388(0xABCD)
+// New calling style: writeOPL(0xAB, 0xCD)
+
+void AdlibDriver::writeOPL(byte reg, byte val) {
+ opl->write(reg, val);
+}
+
+void AdlibDriver::initChannel(Channel &channel) {
+ debugC(9, kDebugLevelSound, "initChannel(%lu)", (long)(&channel - _channels));
+ memset(&channel.dataptr, 0, sizeof(Channel) - ((char*)&channel.dataptr - (char*)&channel));
+
+ channel.tempo = 0xFF;
+ channel.priority = 0;
+ // normally here are nullfuncs but we set 0 for now
+ channel.primaryEffect = 0;
+ channel.secondaryEffect = 0;
+ channel.spacing1 = 1;
+}
+
+void AdlibDriver::noteOff(Channel &channel) {
+ debugC(9, kDebugLevelSound, "noteOff(%lu)", (long)(&channel - _channels));
+
+ // The control channel has no corresponding Adlib channel
+
+ if (_curChannel >= 9)
+ return;
+
+ // When the rhythm section is enabled, channels 6, 7 and 8 are special.
+
+ if (_rhythmSectionBits && _curChannel >= 6)
+ return;
+
+ // This means the "Key On" bit will always be 0
+ channel.regBx &= 0xDF;
+
+ // Octave / F-Number / Key-On
+ writeOPL(0xB0 + _curChannel, channel.regBx);
+}
+
+void AdlibDriver::unkOutput2(uint8 chan) {
+ debugC(9, kDebugLevelSound, "unkOutput2(%d)", chan);
+
+ // The control channel has no corresponding Adlib channel
+
+ if (chan >= 9)
+ return;
+
+ // I believe this has to do with channels 6, 7, and 8 being special
+ // when Adlib's rhythm section is enabled.
+
+ if (_rhythmSectionBits && chan >= 6)
+ return;
+
+ uint8 offset = _regOffset[chan];
+
+ // The channel is cleared: First the attack/delay rate, then the
+ // sustain level/release rate, and finally the note is turned off.
+
+ writeOPL(0x60 + offset, 0xFF);
+ writeOPL(0x63 + offset, 0xFF);
+
+ writeOPL(0x80 + offset, 0xFF);
+ writeOPL(0x83 + offset, 0xFF);
+
+ writeOPL(0xB0 + chan, 0x00);
+
+ // ...and then the note is turned on again, with whatever value is
+ // still lurking in the A0 + chan register, but everything else -
+ // including the two most significant frequency bit, and the octave -
+ // set to zero.
+ //
+ // This is very strange behaviour, and causes problems with the ancient
+ // FMOPL code we borrowed from AdPlug. I've added a workaround. See
+ // fmopl.cpp for more details.
+ //
+ // More recent versions of the MAME FMOPL don't seem to have this
+ // problem, but cannot currently be used because of licensing and
+ // performance issues.
+ //
+ // Ken Silverman's Adlib emulator (which can be found on his Web page -
+ // http://www.advsys.net/ken - and as part of AdPlug) also seems to be
+ // immune, but is apparently not as feature complete as MAME's.
+
+ writeOPL(0xB0 + chan, 0x20);
+}
+
+// I believe this is a random number generator. It actually does seem to
+// generate an even distribution of almost all numbers from 0 through 65535,
+// though in my tests some numbers were never generated.
+
+uint16 AdlibDriver::getRandomNr() {
+ _rnd += 0x9248;
+ uint16 lowBits = _rnd & 7;
+ _rnd >>= 3;
+ _rnd |= (lowBits << 13);
+ return _rnd;
+}
+
+void AdlibDriver::setupDuration(uint8 duration, Channel &channel) {
+ debugC(9, kDebugLevelSound, "setupDuration(%d, %lu)", duration, (long)(&channel - _channels));
+ if (channel.durationRandomness) {
+ channel.duration = duration + (getRandomNr() & channel.durationRandomness);
+ return;
+ }
+ if (channel.fractionalSpacing) {
+ channel.spacing2 = (duration >> 3) * channel.fractionalSpacing;
+ }
+ channel.duration = duration;
+}
+
+// This function may or may not play the note. It's usually followed by a call
+// to noteOn(), which will always play the current note.
+
+void AdlibDriver::setupNote(uint8 rawNote, Channel &channel, bool flag) {
+ debugC(9, kDebugLevelSound, "setupNote(%d, %lu)", rawNote, (long)(&channel - _channels));
+
+ channel.rawNote = rawNote;
+
+ int8 note = (rawNote & 0x0F) + channel.baseNote;
+ int8 octave = ((rawNote + channel.baseOctave) >> 4) & 0x0F;
+
+ // There are only twelve notes. If we go outside that, we have to
+ // adjust the note and octave.
+
+ if (note >= 12) {
+ note -= 12;
+ octave++;
+ } else if (note < 0) {
+ note += 12;
+ octave--;
+ }
+
+ // The calculation of frequency looks quite different from the original
+ // disassembly at a first glance, but when you consider that the
+ // largest possible value would be 0x0246 + 0xFF + 0x47 (and that's if
+ // baseFreq is unsigned), freq is still a 10-bit value, just as it
+ // should be to fit in the Ax and Bx registers.
+ //
+ // If it were larger than that, it could have overflowed into the
+ // octave bits, and that could possibly have been used in some sound.
+ // But as it is now, I can't see any way it would happen.
+
+ uint16 freq = _unkTable[note] + channel.baseFreq;
+
+ // When called from callback 41, the behaviour is slightly different:
+ // We adjust the frequency, even when channel.unk16 is 0.
+
+ if (channel.unk16 || flag) {
+ const uint8 *table;
+
+ if (channel.unk16 >= 0) {
+ table = _unkTables[(channel.rawNote & 0x0F) + 2];
+ freq += table[channel.unk16];
+ } else {
+ table = _unkTables[channel.rawNote & 0x0F];
+ freq -= table[-channel.unk16];
+ }
+ }
+
+ channel.regAx = freq & 0xFF;
+ channel.regBx = (channel.regBx & 0x20) | (octave << 2) | ((freq >> 8) & 0x03);
+
+ // Keep the note on or off
+ writeOPL(0xA0 + _curChannel, channel.regAx);
+ writeOPL(0xB0 + _curChannel, channel.regBx);
+}
+
+void AdlibDriver::setupInstrument(uint8 regOffset, uint8 *dataptr, Channel &channel) {
+ debugC(9, kDebugLevelSound, "setupInstrument(%d, %p, %lu)", regOffset, (const void *)dataptr, (long)(&channel - _channels));
+ // Amplitude Modulation / Vibrato / Envelope Generator Type /
+ // Keyboard Scaling Rate / Modulator Frequency Multiple
+ writeOPL(0x20 + regOffset, *dataptr++);
+ writeOPL(0x23 + regOffset, *dataptr++);
+
+ uint8 temp = *dataptr++;
+
+ // Feedback / Algorithm
+
+ // It is very likely that _curChannel really does refer to the same
+ // channel as regOffset, but there's only one Cx register per channel.
+
+ writeOPL(0xC0 + _curChannel, temp);
+
+ // The algorithm bit. I don't pretend to understand this fully, but
+ // "If set to 0, operator 1 modulates operator 2. In this case,
+ // operator 2 is the only one producing sound. If set to 1, both
+ // operators produce sound directly. Complex sounds are more easily
+ // created if the algorithm is set to 0."
+
+ channel.twoChan = temp & 1;
+
+ // Waveform Select
+ writeOPL(0xE0 + regOffset, *dataptr++);
+ writeOPL(0xE3 + regOffset, *dataptr++);
+
+ channel.opLevel1 = *dataptr++;
+ channel.opLevel2 = *dataptr++;
+
+ // Level Key Scaling / Total Level
+ writeOPL(0x40 + regOffset, calculateOpLevel1(channel));
+ writeOPL(0x43 + regOffset, calculateOpLevel2(channel));
+
+ // Attack Rate / Decay Rate
+ writeOPL(0x60 + regOffset, *dataptr++);
+ writeOPL(0x63 + regOffset, *dataptr++);
+
+ // Sustain Level / Release Rate
+ writeOPL(0x80 + regOffset, *dataptr++);
+ writeOPL(0x83 + regOffset, *dataptr++);
+}
+
+// Apart from playing the note, this function also updates the variables for
+// primary effect 2.
+
+void AdlibDriver::noteOn(Channel &channel) {
+ debugC(9, kDebugLevelSound, "noteOn(%lu)", (long)(&channel - _channels));
+
+ // The "note on" bit is set, and the current note is played.
+
+ channel.regBx |= 0x20;
+ writeOPL(0xB0 + _curChannel, channel.regBx);
+
+ int8 shift = 9 - channel.unk33;
+ uint16 temp = channel.regAx | (channel.regBx << 8);
+ channel.unk37 = ((temp & 0x3FF) >> shift) & 0xFF;
+ channel.unk38 = channel.unk36;
+}
+
+void AdlibDriver::adjustVolume(Channel &channel) {
+ debugC(9, kDebugLevelSound, "adjustVolume(%lu)", (long)(&channel - _channels));
+ // Level Key Scaling / Total Level
+
+ writeOPL(0x43 + _regOffset[_curChannel], calculateOpLevel2(channel));
+ if (channel.twoChan)
+ writeOPL(0x40 + _regOffset[_curChannel], calculateOpLevel1(channel));
+}
+
+// This is presumably only used for some sound effects, e.g. Malcolm blowing up
+// the trees in the intro (but not the effect where he "booby-traps" the big
+// tree) and turning Kallak to stone. Related functions and variables:
+//
+// update_setupPrimaryEffect1()
+// - Initialises unk29, unk30 and unk31
+// - unk29 is not further modified
+// - unk30 is not further modified, except by update_removePrimaryEffect1()
+//
+// update_removePrimaryEffect1()
+// - Deinitialises unk30
+//
+// unk29 - determines how often the notes are played
+// unk30 - modifies the frequency
+// unk31 - determines how often the notes are played
+
+void AdlibDriver::primaryEffect1(Channel &channel) {
+ debugC(9, kDebugLevelSound, "Calling primaryEffect1 (channel: %d)", _curChannel);
+ uint8 temp = channel.unk31;
+ channel.unk31 += channel.unk29;
+ if (channel.unk31 >= temp)
+ return;
+
+ // Initialise unk1 to the current frequency
+ uint16 unk1 = ((channel.regBx & 3) << 8) | channel.regAx;
+
+ // This is presumably to shift the "note on" bit so far to the left
+ // that it won't be affected by any of the calculations below.
+ uint16 unk2 = ((channel.regBx & 0x20) << 8) | (channel.regBx & 0x1C);
+
+ int16 unk3 = (int16)channel.unk30;
+
+ if (unk3 >= 0) {
+ unk1 += unk3;
+ if (unk1 >= 734) {
+ // The new frequency is too high. Shift it down and go
+ // up one octave.
+ unk1 >>= 1;
+ if (!(unk1 & 0x3FF))
+ ++unk1;
+ unk2 = (unk2 & 0xFF00) | ((unk2 + 4) & 0xFF);
+ unk2 &= 0xFF1C;
+ }
+ } else {
+ unk1 += unk3;
+ if (unk1 < 388) {
+ // The new frequency is too low. Shift it up and go
+ // down one octave.
+ unk1 <<= 1;
+ if (!(unk1 & 0x3FF))
+ --unk1;
+ unk2 = (unk2 & 0xFF00) | ((unk2 - 4) & 0xFF);
+ unk2 &= 0xFF1C;
+ }
+ }
+
+ // Make sure that the new frequency is still a 10-bit value.
+ unk1 &= 0x3FF;
+
+ writeOPL(0xA0 + _curChannel, unk1 & 0xFF);
+ channel.regAx = unk1 & 0xFF;
+
+ // Shift down the "note on" bit again.
+ uint8 value = unk1 >> 8;
+ value |= (unk2 >> 8) & 0xFF;
+ value |= unk2 & 0xFF;
+
+ writeOPL(0xB0 + _curChannel, value);
+ channel.regBx = value;
+}
+
+// This is presumably only used for some sound effects, e.g. Malcolm entering
+// and leaving Kallak's hut. Related functions and variables:
+//
+// update_setupPrimaryEffect2()
+// - Initialises unk32, unk33, unk34, unk35 and unk36
+// - unk32 is not further modified
+// - unk33 is not further modified
+// - unk34 is a countdown that gets reinitialised to unk35 on zero
+// - unk35 is based on unk34 and not further modified
+// - unk36 is not further modified
+//
+// noteOn()
+// - Plays the current note
+// - Updates unk37 with a new (lower?) frequency
+// - Copies unk36 to unk38. The unk38 variable is a countdown.
+//
+// unk32 - determines how often the notes are played
+// unk33 - modifies the frequency
+// unk34 - countdown, updates frequency on zero
+// unk35 - initialiser for unk34 countdown
+// unk36 - initialiser for unk38 countdown
+// unk37 - frequency
+// unk38 - countdown, begins playing on zero
+// unk41 - determines how often the notes are played
+//
+// Note that unk41 is never initialised. Not that it should matter much, but it
+// is a bit sloppy.
+
+void AdlibDriver::primaryEffect2(Channel &channel) {
+ debugC(9, kDebugLevelSound, "Calling primaryEffect2 (channel: %d)", _curChannel);
+ if (channel.unk38) {
+ --channel.unk38;
+ return;
+ }
+
+ uint8 temp = channel.unk41;
+ channel.unk41 += channel.unk32;
+ if (channel.unk41 < temp) {
+ uint16 unk1 = channel.unk37;
+ if (!(--channel.unk34)) {
+ unk1 ^= 0xFFFF;
+ ++unk1;
+ channel.unk37 = unk1;
+ channel.unk34 = channel.unk35;
+ }
+
+ uint16 unk2 = (channel.regAx | (channel.regBx << 8)) & 0x3FF;
+ unk2 += unk1;
+
+ channel.regAx = unk2 & 0xFF;
+ channel.regBx = (channel.regBx & 0xFC) | (unk2 >> 8);
+
+ // Octave / F-Number / Key-On
+ writeOPL(0xA0 + _curChannel, channel.regAx);
+ writeOPL(0xB0 + _curChannel, channel.regBx);
+ }
+}
+
+// I don't know where this is used. The same operation is performed several
+// times on the current channel, using a chunk of the _soundData[] buffer for
+// parameters. The parameters are used starting at the end of the chunk.
+//
+// Since we use _curRegOffset to specify the final register, it's quite
+// unlikely that this function is ever used to play notes. It's probably only
+// used to modify the sound. Another thing that supports this idea is that it
+// can be combined with any of the effects callbacks above.
+//
+// Related functions and variables:
+//
+// update_setupSecondaryEffect1()
+// - Initialies unk18, unk19, unk20, unk21, unk22 and offset
+// - unk19 is not further modified
+// - unk20 is not further modified
+// - unk22 is not further modified
+// - offset is not further modified
+//
+// unk18 - determines how often the operation is performed
+// unk19 - determines how often the operation is performed
+// unk20 - the start index into the data chunk
+// unk21 - the current index into the data chunk
+// unk22 - the operation to perform
+// offset - the offset to the data chunk
+
+void AdlibDriver::secondaryEffect1(Channel &channel) {
+ debugC(9, kDebugLevelSound, "Calling secondaryEffect1 (channel: %d)", _curChannel);
+ uint8 temp = channel.unk18;
+ channel.unk18 += channel.unk19;
+ if (channel.unk18 < temp) {
+ if (--channel.unk21 < 0) {
+ channel.unk21 = channel.unk20;
+ }
+ writeOPL(channel.unk22 + _curRegOffset, _soundData[channel.offset + channel.unk21]);
+ }
+}
+
+uint8 AdlibDriver::calculateOpLevel1(Channel &channel) {
+ int8 value = channel.opLevel1 & 0x3F;
+
+ if (channel.twoChan) {
+ value += channel.opExtraLevel1;
+ value += channel.opExtraLevel2;
+ value += channel.opExtraLevel3;
+ }
+
+ // Preserve the scaling level bits from opLevel1
+
+ return checkValue(value) | (channel.opLevel1 & 0xC0);
+}
+
+uint8 AdlibDriver::calculateOpLevel2(Channel &channel) {
+ int8 value = channel.opLevel2 & 0x3F;
+
+ value += channel.opExtraLevel1;
+ value += channel.opExtraLevel2;
+ value += channel.opExtraLevel3;
+
+ // Preserve the scaling level bits from opLevel2
+
+ return checkValue(value) | (channel.opLevel2 & 0xC0);
+}
+
+// parser opcodes
+
+int AdlibDriver::update_setRepeat(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.repeatCounter = value;
+ return 0;
+}
+
+int AdlibDriver::update_checkRepeat(uint8 *&dataptr, Channel &channel, uint8 value) {
+ ++dataptr;
+ if (--channel.repeatCounter) {
+ int16 add = READ_LE_UINT16(dataptr - 2);
+ dataptr += add;
+ }
+ return 0;
+}
+
+int AdlibDriver::update_setupProgram(uint8 *&dataptr, Channel &channel, uint8 value) {
+ if (value == 0xFF)
+ return 0;
+
+ uint8 *ptr = getProgram(value);
+ uint8 chan = *ptr++;
+ uint8 priority = *ptr++;
+
+ Channel &channel2 = _channels[chan];
+
+ if (priority >= channel2.priority) {
+ _flagTrigger = 1;
+ _flags |= 8;
+ initChannel(channel2);
+ channel2.priority = priority;
+ channel2.dataptr = ptr;
+ channel2.tempo = 0xFF;
+ channel2.position = 0xFF;
+ channel2.duration = 1;
+ unkOutput2(chan);
+ }
+
+ return 0;
+}
+
+int AdlibDriver::update_setNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.spacing1 = value;
+ return 0;
+}
+
+int AdlibDriver::update_jump(uint8 *&dataptr, Channel &channel, uint8 value) {
+ --dataptr;
+ int16 add = READ_LE_UINT16(dataptr); dataptr += 2;
+ dataptr += add;
+ return 0;
+}
+
+int AdlibDriver::update_jumpToSubroutine(uint8 *&dataptr, Channel &channel, uint8 value) {
+ --dataptr;
+ int16 add = READ_LE_UINT16(dataptr); dataptr += 2;
+ channel.dataptrStack[channel.dataptrStackPos++] = dataptr;
+ dataptr += add;
+ return 0;
+}
+
+int AdlibDriver::update_returnFromSubroutine(uint8 *&dataptr, Channel &channel, uint8 value) {
+ dataptr = channel.dataptrStack[--channel.dataptrStackPos];
+ return 0;
+}
+
+int AdlibDriver::update_setBaseOctave(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.baseOctave = value;
+ return 0;
+}
+
+int AdlibDriver::update_stopChannel(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.priority = 0;
+ if (_curChannel != 9) {
+ noteOff(channel);
+ }
+ dataptr = 0;
+ return 2;
+}
+
+int AdlibDriver::update_playRest(uint8 *&dataptr, Channel &channel, uint8 value) {
+ setupDuration(value, channel);
+ noteOff(channel);
+ return (value != 0);
+}
+
+int AdlibDriver::update_writeAdlib(uint8 *&dataptr, Channel &channel, uint8 value) {
+ writeOPL(value, *dataptr++);
+ return 0;
+}
+
+int AdlibDriver::update_setupNoteAndDuration(uint8 *&dataptr, Channel &channel, uint8 value) {
+ setupNote(value, channel);
+ value = *dataptr++;
+ setupDuration(value, channel);
+ return (value != 0);
+}
+
+int AdlibDriver::update_setBaseNote(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.baseNote = value;
+ return 0;
+}
+
+int AdlibDriver::update_setupSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.unk18 = value;
+ channel.unk19 = value;
+ channel.unk20 = channel.unk21 = *dataptr++;
+ channel.unk22 = *dataptr++;
+ channel.offset = READ_LE_UINT16(dataptr); dataptr += 2;
+ channel.secondaryEffect = &AdlibDriver::secondaryEffect1;
+ return 0;
+}
+
+int AdlibDriver::update_stopOtherChannel(uint8 *&dataptr, Channel &channel, uint8 value) {
+ Channel &channel2 = _channels[value];
+ channel2.duration = 0;
+ channel2.priority = 0;
+ channel2.dataptr = 0;
+ return 0;
+}
+
+int AdlibDriver::update_waitForEndOfProgram(uint8 *&dataptr, Channel &channel, uint8 value) {
+ uint8 *ptr = getProgram(value);
+ uint8 chan = *ptr;
+
+ if (!_channels[chan].dataptr) {
+ return 0;
+ }
+
+ dataptr -= 2;
+ return 2;
+}
+
+int AdlibDriver::update_setupInstrument(uint8 *&dataptr, Channel &channel, uint8 value) {
+ setupInstrument(_curRegOffset, getInstrument(value), channel);
+ return 0;
+}
+
+int AdlibDriver::update_setupPrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.unk29 = value;
+ channel.unk30 = READ_BE_UINT16(dataptr);
+ dataptr += 2;
+ channel.primaryEffect = &AdlibDriver::primaryEffect1;
+ channel.unk31 = 0xFF;
+ return 0;
+}
+
+int AdlibDriver::update_removePrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) {
+ --dataptr;
+ channel.primaryEffect = 0;
+ channel.unk30 = 0;
+ return 0;
+}
+
+int AdlibDriver::update_setBaseFreq(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.baseFreq = value;
+ return 0;
+}
+
+int AdlibDriver::update_setupPrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.unk32 = value;
+ channel.unk33 = *dataptr++;
+ uint8 temp = *dataptr++;
+ channel.unk34 = temp + 1;
+ channel.unk35 = temp << 1;
+ channel.unk36 = *dataptr++;
+ channel.primaryEffect = &AdlibDriver::primaryEffect2;
+ return 0;
+}
+
+int AdlibDriver::update_setPriority(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.priority = value;
+ return 0;
+}
+
+int AdlibDriver::updateCallback23(uint8 *&dataptr, Channel &channel, uint8 value) {
+ value >>= 1;
+ _unkValue1 = _unkValue2 = value;
+ _unkValue3 = 0xFF;
+ _unkValue4 = _unkValue5 = 0;
+ return 0;
+}
+
+int AdlibDriver::updateCallback24(uint8 *&dataptr, Channel &channel, uint8 value) {
+ if (_unkValue5) {
+ if (_unkValue4 & value) {
+ _unkValue5 = 0;
+ return 0;
+ }
+ }
+
+ if (!(value & _unkValue4)) {
+ ++_unkValue5;
+ }
+
+ dataptr -= 2;
+ channel.duration = 1;
+ return 2;
+}
+
+int AdlibDriver::update_setExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.opExtraLevel1 = value;
+ adjustVolume(channel);
+ return 0;
+}
+
+int AdlibDriver::update_setupDuration(uint8 *&dataptr, Channel &channel, uint8 value) {
+ setupDuration(value, channel);
+ return (value != 0);
+}
+
+int AdlibDriver::update_playNote(uint8 *&dataptr, Channel &channel, uint8 value) {
+ setupDuration(value, channel);
+ noteOn(channel);
+ return (value != 0);
+}
+
+int AdlibDriver::update_setFractionalNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.fractionalSpacing = value & 7;
+ return 0;
+}
+
+int AdlibDriver::update_setTempo(uint8 *&dataptr, Channel &channel, uint8 value) {
+ _tempo = value;
+ return 0;
+}
+
+int AdlibDriver::update_removeSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) {
+ --dataptr;
+ channel.secondaryEffect = 0;
+ return 0;
+}
+
+int AdlibDriver::update_setChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.tempo = value;
+ return 0;
+}
+
+int AdlibDriver::update_setExtraLevel3(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.opExtraLevel3 = value;
+ return 0;
+}
+
+int AdlibDriver::update_setExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value) {
+ int channelBackUp = _curChannel;
+
+ _curChannel = value;
+ Channel &channel2 = _channels[value];
+ channel2.opExtraLevel2 = *dataptr++;
+ adjustVolume(channel2);
+
+ _curChannel = channelBackUp;
+ return 0;
+}
+
+int AdlibDriver::update_changeExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value) {
+ int channelBackUp = _curChannel;
+
+ _curChannel = value;
+ Channel &channel2 = _channels[value];
+ channel2.opExtraLevel2 += *dataptr++;
+ adjustVolume(channel2);
+
+ _curChannel = channelBackUp;
+ return 0;
+}
+
+// Apart from initialising to zero, these two functions are the only ones that
+// modify _vibratoAndAMDepthBits.
+
+int AdlibDriver::update_setAMDepth(uint8 *&dataptr, Channel &channel, uint8 value) {
+ if (value & 1)
+ _vibratoAndAMDepthBits |= 0x80;
+ else
+ _vibratoAndAMDepthBits &= 0x7F;
+
+ writeOPL(0xBD, _vibratoAndAMDepthBits);
+ return 0;
+}
+
+int AdlibDriver::update_setVibratoDepth(uint8 *&dataptr, Channel &channel, uint8 value) {
+ if (value & 1)
+ _vibratoAndAMDepthBits |= 0x40;
+ else
+ _vibratoAndAMDepthBits &= 0xBF;
+
+ writeOPL(0xBD, _vibratoAndAMDepthBits);
+ return 0;
+}
+
+int AdlibDriver::update_changeExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.opExtraLevel1 += value;
+ adjustVolume(channel);
+ return 0;
+}
+
+int AdlibDriver::updateCallback38(uint8 *&dataptr, Channel &channel, uint8 value) {
+ int channelBackUp = _curChannel;
+
+ _curChannel = value;
+ Channel &channel2 = _channels[value];
+ channel2.duration = channel2.priority = 0;
+ channel2.dataptr = 0;
+ channel2.opExtraLevel2 = 0;
+
+ if (value != 9) {
+ uint8 outValue = _regOffset[value];
+
+ // Feedback strength / Connection type
+ writeOPL(0xC0 + _curChannel, 0x00);
+
+ // Key scaling level / Operator output level
+ writeOPL(0x43 + outValue, 0x3F);
+
+ // Sustain Level / Release Rate
+ writeOPL(0x83 + outValue, 0xFF);
+
+ // Key On / Octave / Frequency
+ writeOPL(0xB0 + _curChannel, 0x00);
+ }
+
+ _curChannel = channelBackUp;
+ return 0;
+}
+
+int AdlibDriver::updateCallback39(uint8 *&dataptr, Channel &channel, uint8 value) {
+ uint16 unk = *dataptr++;
+ unk |= value << 8;
+ unk &= getRandomNr();
+
+ uint16 unk2 = ((channel.regBx & 0x1F) << 8) | channel.regAx;
+ unk2 += unk;
+ unk2 |= ((channel.regBx & 0x20) << 8);
+
+ // Frequency
+ writeOPL(0xA0 + _curChannel, unk2 & 0xFF);
+
+ // Key On / Octave / Frequency
+ writeOPL(0xB0 + _curChannel, (unk2 & 0xFF00) >> 8);
+
+ return 0;
+}
+
+int AdlibDriver::update_removePrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value) {
+ --dataptr;
+ channel.primaryEffect = 0;
+ return 0;
+}
+
+int AdlibDriver::updateCallback41(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.unk16 = value;
+ setupNote(channel.rawNote, channel, true);
+ return 0;
+}
+
+int AdlibDriver::update_resetToGlobalTempo(uint8 *&dataptr, Channel &channel, uint8 value) {
+ --dataptr;
+ channel.tempo = _tempo;
+ return 0;
+}
+
+int AdlibDriver::update_nop1(uint8 *&dataptr, Channel &channel, uint8 value) {
+ --dataptr;
+ return 0;
+}
+
+int AdlibDriver::update_setDurationRandomness(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.durationRandomness = value;
+ return 0;
+}
+
+int AdlibDriver::update_changeChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value) {
+ int tempo = channel.tempo + (int8)value;
+
+ if (tempo <= 0)
+ tempo = 1;
+ else if (tempo > 255)
+ tempo = 255;
+
+ channel.tempo = tempo;
+ return 0;
+}
+
+int AdlibDriver::updateCallback46(uint8 *&dataptr, Channel &channel, uint8 value) {
+ uint8 entry = *dataptr++;
+ _tablePtr1 = _unkTable2[entry++];
+ _tablePtr2 = _unkTable2[entry];
+ if (value == 2) {
+ // Frequency
+ writeOPL(0xA0, _tablePtr2[0]);
+ }
+ return 0;
+}
+
+// TODO: This is really the same as update_nop1(), so they should be combined
+// into one single update_nop().
+
+int AdlibDriver::update_nop2(uint8 *&dataptr, Channel &channel, uint8 value) {
+ --dataptr;
+ return 0;
+}
+
+int AdlibDriver::update_setupRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value) {
+ int channelBackUp = _curChannel;
+ int regOffsetBackUp = _curRegOffset;
+
+ _curChannel = 6;
+ _curRegOffset = _regOffset[6];
+
+ setupInstrument(_curRegOffset, getInstrument(value), channel);
+ _unkValue6 = channel.opLevel2;
+
+ _curChannel = 7;
+ _curRegOffset = _regOffset[7];
+
+ setupInstrument(_curRegOffset, getInstrument(*dataptr++), channel);
+ _unkValue7 = channel.opLevel1;
+ _unkValue8 = channel.opLevel2;
+
+ _curChannel = 8;
+ _curRegOffset = _regOffset[8];
+
+ setupInstrument(_curRegOffset, getInstrument(*dataptr++), channel);
+ _unkValue9 = channel.opLevel1;
+ _unkValue10 = channel.opLevel2;
+
+ // Octave / F-Number / Key-On for channels 6, 7 and 8
+
+ _channels[6].regBx = *dataptr++ & 0x2F;
+ writeOPL(0xB6, _channels[6].regBx);
+ writeOPL(0xA6, *dataptr++);
+
+ _channels[7].regBx = *dataptr++ & 0x2F;
+ writeOPL(0xB7, _channels[7].regBx);
+ writeOPL(0xA7, *dataptr++);
+
+ _channels[8].regBx = *dataptr++ & 0x2F;
+ writeOPL(0xB8, _channels[8].regBx);
+ writeOPL(0xA8, *dataptr++);
+
+ _rhythmSectionBits = 0x20;
+
+ _curRegOffset = regOffsetBackUp;
+ _curChannel = channelBackUp;
+ return 0;
+}
+
+int AdlibDriver::update_playRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value) {
+ // Any instrument that we want to play, and which was already playing,
+ // is temporarily keyed off. Instruments that were off already, or
+ // which we don't want to play, retain their old on/off status. This is
+ // probably so that the instrument's envelope is played from its
+ // beginning again...
+
+ writeOPL(0xBD, (_rhythmSectionBits & ~(value & 0x1F)) | 0x20);
+
+ // ...but since we only set the rhythm instrument bits, and never clear
+ // them (until the entire rhythm section is disabled), I'm not sure how
+ // useful the cleverness above is. We could perhaps simply turn off all
+ // the rhythm instruments instead.
+
+ _rhythmSectionBits |= value;
+
+ writeOPL(0xBD, _vibratoAndAMDepthBits | 0x20 | _rhythmSectionBits);
+ return 0;
+}
+
+int AdlibDriver::update_removeRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value) {
+ --dataptr;
+ _rhythmSectionBits = 0;
+
+ // All the rhythm bits are cleared. The AM and Vibrato depth bits
+ // remain unchanged.
+
+ writeOPL(0xBD, _vibratoAndAMDepthBits);
+ return 0;
+}
+
+int AdlibDriver::updateCallback51(uint8 *&dataptr, Channel &channel, uint8 value) {
+ uint8 value2 = *dataptr++;
+
+ if (value & 1) {
+ _unkValue12 = value2;
+
+ // Channel 7, op1: Level Key Scaling / Total Level
+ writeOPL(0x51, checkValue(value2 + _unkValue7 + _unkValue11 + _unkValue12));
+ }
+
+ if (value & 2) {
+ _unkValue14 = value2;
+
+ // Channel 8, op2: Level Key Scaling / Total Level
+ writeOPL(0x55, checkValue(value2 + _unkValue10 + _unkValue13 + _unkValue14));
+ }
+
+ if (value & 4) {
+ _unkValue15 = value2;
+
+ // Channel 8, op1: Level Key Scaling / Total Level
+ writeOPL(0x52, checkValue(value2 + _unkValue9 + _unkValue16 + _unkValue15));
+ }
+
+ if (value & 8) {
+ _unkValue18 = value2;
+
+ // Channel 7, op2: Level Key Scaling / Total Level
+ writeOPL(0x54, checkValue(value2 + _unkValue8 + _unkValue17 + _unkValue18));
+ }
+
+ if (value & 16) {
+ _unkValue20 = value2;
+
+ // Channel 6, op2: Level Key Scaling / Total Level
+ writeOPL(0x53, checkValue(value2 + _unkValue6 + _unkValue19 + _unkValue20));
+ }
+
+ return 0;
+}
+
+int AdlibDriver::updateCallback52(uint8 *&dataptr, Channel &channel, uint8 value) {
+ uint8 value2 = *dataptr++;
+
+ if (value & 1) {
+ _unkValue11 = checkValue(value2 + _unkValue7 + _unkValue11 + _unkValue12);
+
+ // Channel 7, op1: Level Key Scaling / Total Level
+ writeOPL(0x51, _unkValue11);
+ }
+
+ if (value & 2) {
+ _unkValue13 = checkValue(value2 + _unkValue10 + _unkValue13 + _unkValue14);
+
+ // Channel 8, op2: Level Key Scaling / Total Level
+ writeOPL(0x55, _unkValue13);
+ }
+
+ if (value & 4) {
+ _unkValue16 = checkValue(value2 + _unkValue9 + _unkValue16 + _unkValue15);
+
+ // Channel 8, op1: Level Key Scaling / Total Level
+ writeOPL(0x52, _unkValue16);
+ }
+
+ if (value & 8) {
+ _unkValue17 = checkValue(value2 + _unkValue8 + _unkValue17 + _unkValue18);
+
+ // Channel 7, op2: Level Key Scaling / Total Level
+ writeOPL(0x54, _unkValue17);
+ }
+
+ if (value & 16) {
+ _unkValue19 = checkValue(value2 + _unkValue6 + _unkValue19 + _unkValue20);
+
+ // Channel 6, op2: Level Key Scaling / Total Level
+ writeOPL(0x53, _unkValue19);
+ }
+
+ return 0;
+}
+
+int AdlibDriver::updateCallback53(uint8 *&dataptr, Channel &channel, uint8 value) {
+ uint8 value2 = *dataptr++;
+
+ if (value & 1) {
+ _unkValue11 = value2;
+
+ // Channel 7, op1: Level Key Scaling / Total Level
+ writeOPL(0x51, checkValue(value2 + _unkValue7 + _unkValue12));
+ }
+
+ if (value & 2) {
+ _unkValue13 = value2;
+
+ // Channel 8, op2: Level Key Scaling / Total Level
+ writeOPL(0x55, checkValue(value2 + _unkValue10 + _unkValue14));
+ }
+
+ if (value & 4) {
+ _unkValue16 = value2;
+
+ // Channel 8, op1: Level Key Scaling / Total Level
+ writeOPL(0x52, checkValue(value2 + _unkValue9 + _unkValue15));
+ }
+
+ if (value & 8) {
+ _unkValue17 = value2;
+
+ // Channel 7, op2: Level Key Scaling / Total Level
+ writeOPL(0x54, checkValue(value2 + _unkValue8 + _unkValue18));
+ }
+
+ if (value & 16) {
+ _unkValue19 = value2;
+
+ // Channel 6, op2: Level Key Scaling / Total Level
+ writeOPL(0x53, checkValue(value2 + _unkValue6 + _unkValue20));
+ }
+
+ return 0;
+}
+
+int AdlibDriver::update_setSoundTrigger(uint8 *&dataptr, Channel &channel, uint8 value) {
+ _soundTrigger = value;
+ return 0;
+}
+
+int AdlibDriver::update_setTempoReset(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.tempoReset = value;
+ return 0;
+}
+
+int AdlibDriver::updateCallback56(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.unk39 = value;
+ channel.unk40 = *dataptr++;
+ return 0;
+}
+
+// static res
+
+#define COMMAND(x) { &AdlibDriver::x, #x }
+
+void AdlibDriver::setupOpcodeList() {
+ static const OpcodeEntry opcodeList[] = {
+ COMMAND(snd_ret0x100),
+ COMMAND(snd_ret0x1983),
+ COMMAND(snd_initDriver),
+ COMMAND(snd_deinitDriver),
+ COMMAND(snd_setSoundData),
+ COMMAND(snd_unkOpcode1),
+ COMMAND(snd_startSong),
+ COMMAND(snd_unkOpcode2),
+ COMMAND(snd_unkOpcode3),
+ COMMAND(snd_readByte),
+ COMMAND(snd_writeByte),
+ COMMAND(snd_getSoundTrigger),
+ COMMAND(snd_unkOpcode4),
+ COMMAND(snd_dummy),
+ COMMAND(snd_getNullvar4),
+ COMMAND(snd_setNullvar3),
+ COMMAND(snd_setFlag),
+ COMMAND(snd_clearFlag)
+ };
+
+ _opcodeList = opcodeList;
+ _opcodesEntries = ARRAYSIZE(opcodeList);
+}
+
+void AdlibDriver::setupParserOpcodeTable() {
+ static const ParserOpcode parserOpcodeTable[] = {
+ // 0
+ COMMAND(update_setRepeat),
+ COMMAND(update_checkRepeat),
+ COMMAND(update_setupProgram),
+ COMMAND(update_setNoteSpacing),
+
+ // 4
+ COMMAND(update_jump),
+ COMMAND(update_jumpToSubroutine),
+ COMMAND(update_returnFromSubroutine),
+ COMMAND(update_setBaseOctave),
+
+ // 8
+ COMMAND(update_stopChannel),
+ COMMAND(update_playRest),
+ COMMAND(update_writeAdlib),
+ COMMAND(update_setupNoteAndDuration),
+
+ // 12
+ COMMAND(update_setBaseNote),
+ COMMAND(update_setupSecondaryEffect1),
+ COMMAND(update_stopOtherChannel),
+ COMMAND(update_waitForEndOfProgram),
+
+ // 16
+ COMMAND(update_setupInstrument),
+ COMMAND(update_setupPrimaryEffect1),
+ COMMAND(update_removePrimaryEffect1),
+ COMMAND(update_setBaseFreq),
+
+ // 20
+ COMMAND(update_stopChannel),
+ COMMAND(update_setupPrimaryEffect2),
+ COMMAND(update_stopChannel),
+ COMMAND(update_stopChannel),
+
+ // 24
+ COMMAND(update_stopChannel),
+ COMMAND(update_stopChannel),
+ COMMAND(update_setPriority),
+ COMMAND(update_stopChannel),
+
+ // 28
+ COMMAND(updateCallback23),
+ COMMAND(updateCallback24),
+ COMMAND(update_setExtraLevel1),
+ COMMAND(update_stopChannel),
+
+ // 32
+ COMMAND(update_setupDuration),
+ COMMAND(update_playNote),
+ COMMAND(update_stopChannel),
+ COMMAND(update_stopChannel),
+
+ // 36
+ COMMAND(update_setFractionalNoteSpacing),
+ COMMAND(update_stopChannel),
+ COMMAND(update_setTempo),
+ COMMAND(update_removeSecondaryEffect1),
+
+ // 40
+ COMMAND(update_stopChannel),
+ COMMAND(update_setChannelTempo),
+ COMMAND(update_stopChannel),
+ COMMAND(update_setExtraLevel3),
+
+ // 44
+ COMMAND(update_setExtraLevel2),
+ COMMAND(update_changeExtraLevel2),
+ COMMAND(update_setAMDepth),
+ COMMAND(update_setVibratoDepth),
+
+ // 48
+ COMMAND(update_changeExtraLevel1),
+ COMMAND(update_stopChannel),
+ COMMAND(update_stopChannel),
+ COMMAND(updateCallback38),
+
+ // 52
+ COMMAND(update_stopChannel),
+ COMMAND(updateCallback39),
+ COMMAND(update_removePrimaryEffect2),
+ COMMAND(update_stopChannel),
+
+ // 56
+ COMMAND(update_stopChannel),
+ COMMAND(updateCallback41),
+ COMMAND(update_resetToGlobalTempo),
+ COMMAND(update_nop1),
+
+ // 60
+ COMMAND(update_setDurationRandomness),
+ COMMAND(update_changeChannelTempo),
+ COMMAND(update_stopChannel),
+ COMMAND(updateCallback46),
+
+ // 64
+ COMMAND(update_nop2),
+ COMMAND(update_setupRhythmSection),
+ COMMAND(update_playRhythmSection),
+ COMMAND(update_removeRhythmSection),
+
+ // 68
+ COMMAND(updateCallback51),
+ COMMAND(updateCallback52),
+ COMMAND(updateCallback53),
+ COMMAND(update_setSoundTrigger),
+
+ // 72
+ COMMAND(update_setTempoReset),
+ COMMAND(updateCallback56),
+ COMMAND(update_stopChannel)
+ };
+
+ _parserOpcodeTable = parserOpcodeTable;
+ _parserOpcodeTableSize = ARRAYSIZE(parserOpcodeTable);
+}
+#undef COMMAND
+
+// This table holds the register offset for operator 1 for each of the nine
+// channels. To get the register offset for operator 2, simply add 3.
+
+const uint8 AdlibDriver::_regOffset[] = {
+ 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11,
+ 0x12
+};
+
+// Given the size of this table, and the range of its values, it's probably the
+// F-Numbers (10 bits) for the notes of the 12-tone scale. However, it does not
+// match the table in the Adlib documentation I've seen.
+
+const uint16 AdlibDriver::_unkTable[] = {
+ 0x0134, 0x0147, 0x015A, 0x016F, 0x0184, 0x019C, 0x01B4, 0x01CE, 0x01E9,
+ 0x0207, 0x0225, 0x0246
+};
+
+// These tables are currently only used by updateCallback46(), which only ever
+// uses the first element of one of the sub-tables.
+
+const uint8 *AdlibDriver::_unkTable2[] = {
+ AdlibDriver::_unkTable2_1,
+ AdlibDriver::_unkTable2_2,
+ AdlibDriver::_unkTable2_1,
+ AdlibDriver::_unkTable2_2,
+ AdlibDriver::_unkTable2_3,
+ AdlibDriver::_unkTable2_2
+};
+
+const uint8 AdlibDriver::_unkTable2_1[] = {
+ 0x50, 0x50, 0x4F, 0x4F, 0x4E, 0x4E, 0x4D, 0x4D,
+ 0x4C, 0x4C, 0x4B, 0x4B, 0x4A, 0x4A, 0x49, 0x49,
+ 0x48, 0x48, 0x47, 0x47, 0x46, 0x46, 0x45, 0x45,
+ 0x44, 0x44, 0x43, 0x43, 0x42, 0x42, 0x41, 0x41,
+ 0x40, 0x40, 0x3F, 0x3F, 0x3E, 0x3E, 0x3D, 0x3D,
+ 0x3C, 0x3C, 0x3B, 0x3B, 0x3A, 0x3A, 0x39, 0x39,
+ 0x38, 0x38, 0x37, 0x37, 0x36, 0x36, 0x35, 0x35,
+ 0x34, 0x34, 0x33, 0x33, 0x32, 0x32, 0x31, 0x31,
+ 0x30, 0x30, 0x2F, 0x2F, 0x2E, 0x2E, 0x2D, 0x2D,
+ 0x2C, 0x2C, 0x2B, 0x2B, 0x2A, 0x2A, 0x29, 0x29,
+ 0x28, 0x28, 0x27, 0x27, 0x26, 0x26, 0x25, 0x25,
+ 0x24, 0x24, 0x23, 0x23, 0x22, 0x22, 0x21, 0x21,
+ 0x20, 0x20, 0x1F, 0x1F, 0x1E, 0x1E, 0x1D, 0x1D,
+ 0x1C, 0x1C, 0x1B, 0x1B, 0x1A, 0x1A, 0x19, 0x19,
+ 0x18, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15,
+ 0x14, 0x14, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11,
+ 0x10, 0x10
+};
+
+// no don't ask me WHY this table exsits!
+const uint8 AdlibDriver::_unkTable2_2[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x6F,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F
+};
+
+const uint8 AdlibDriver::_unkTable2_3[] = {
+ 0x40, 0x40, 0x40, 0x3F, 0x3F, 0x3F, 0x3E, 0x3E,
+ 0x3E, 0x3D, 0x3D, 0x3D, 0x3C, 0x3C, 0x3C, 0x3B,
+ 0x3B, 0x3B, 0x3A, 0x3A, 0x3A, 0x39, 0x39, 0x39,
+ 0x38, 0x38, 0x38, 0x37, 0x37, 0x37, 0x36, 0x36,
+ 0x36, 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x33,
+ 0x33, 0x33, 0x32, 0x32, 0x32, 0x31, 0x31, 0x31,
+ 0x30, 0x30, 0x30, 0x2F, 0x2F, 0x2F, 0x2E, 0x2E,
+ 0x2E, 0x2D, 0x2D, 0x2D, 0x2C, 0x2C, 0x2C, 0x2B,
+ 0x2B, 0x2B, 0x2A, 0x2A, 0x2A, 0x29, 0x29, 0x29,
+ 0x28, 0x28, 0x28, 0x27, 0x27, 0x27, 0x26, 0x26,
+ 0x26, 0x25, 0x25, 0x25, 0x24, 0x24, 0x24, 0x23,
+ 0x23, 0x23, 0x22, 0x22, 0x22, 0x21, 0x21, 0x21,
+ 0x20, 0x20, 0x20, 0x1F, 0x1F, 0x1F, 0x1E, 0x1E,
+ 0x1E, 0x1D, 0x1D, 0x1D, 0x1C, 0x1C, 0x1C, 0x1B,
+ 0x1B, 0x1B, 0x1A, 0x1A, 0x1A, 0x19, 0x19, 0x19,
+ 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x16, 0x16,
+ 0x16, 0x15
+};
+
+// This table is used to modify the frequency of the notes, depending on the
+// note value and unk16. In theory, we could very well try to access memory
+// outside this table, but in reality that probably won't happen.
+//
+// This could be some sort of pitch bend, but I have yet to see it used for
+// anything so it's hard to say.
+
+const uint8 AdlibDriver::_unkTables[][32] = {
+ // 0
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x19,
+ 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21 },
+ // 1
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x07, 0x09,
+ 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11,
+ 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x1A,
+ 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x22, 0x24 },
+ // 2
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x09,
+ 0x0A, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x12, 0x13,
+ 0x14, 0x15, 0x16, 0x17, 0x19, 0x1A, 0x1C, 0x1D,
+ 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x24, 0x25, 0x26 },
+ // 3
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0A,
+ 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x12, 0x13,
+ 0x14, 0x15, 0x16, 0x17, 0x18, 0x1A, 0x1C, 0x1D,
+ 0x1E, 0x1F, 0x20, 0x21, 0x23, 0x25, 0x27, 0x28 },
+ // 4
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0A,
+ 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x13, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x1B, 0x1D, 0x1F, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x28, 0x2A },
+ // 5
+ { 0x00, 0x01, 0x02, 0x03, 0x05, 0x07, 0x09, 0x0B,
+ 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x1B, 0x1D, 0x1F, 0x20,
+ 0x21, 0x22, 0x23, 0x25, 0x27, 0x29, 0x2B, 0x2D },
+ // 6
+ { 0x00, 0x01, 0x02, 0x03, 0x05, 0x07, 0x09, 0x0B,
+ 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15,
+ 0x16, 0x17, 0x18, 0x1A, 0x1C, 0x1E, 0x21, 0x24,
+ 0x25, 0x26, 0x27, 0x29, 0x2B, 0x2D, 0x2F, 0x30 },
+ // 7
+ { 0x00, 0x01, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C,
+ 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15, 0x18,
+ 0x19, 0x1A, 0x1C, 0x1D, 0x1F, 0x21, 0x23, 0x25,
+ 0x26, 0x27, 0x29, 0x2B, 0x2D, 0x2F, 0x30, 0x32 },
+ // 8
+ { 0x00, 0x01, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0D,
+ 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x14, 0x17, 0x1A,
+ 0x19, 0x1A, 0x1C, 0x1E, 0x20, 0x22, 0x25, 0x28,
+ 0x29, 0x2A, 0x2B, 0x2D, 0x2F, 0x31, 0x33, 0x35 },
+ // 9
+ { 0x00, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0E,
+ 0x0F, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1B,
+ 0x1C, 0x1D, 0x1E, 0x20, 0x22, 0x24, 0x26, 0x29,
+ 0x2A, 0x2C, 0x2E, 0x30, 0x32, 0x34, 0x36, 0x39 },
+ // 10
+ { 0x00, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0E,
+ 0x0F, 0x10, 0x12, 0x14, 0x16, 0x19, 0x1B, 0x1E,
+ 0x1F, 0x21, 0x23, 0x25, 0x27, 0x29, 0x2B, 0x2D,
+ 0x2E, 0x2F, 0x31, 0x32, 0x34, 0x36, 0x39, 0x3C },
+ // 11
+ { 0x00, 0x01, 0x03, 0x05, 0x07, 0x0A, 0x0C, 0x0F,
+ 0x10, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1E,
+ 0x1F, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2B, 0x2E,
+ 0x2F, 0x30, 0x32, 0x34, 0x36, 0x39, 0x3C, 0x3F },
+ // 12
+ { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0B, 0x0D, 0x10,
+ 0x11, 0x12, 0x14, 0x16, 0x18, 0x1B, 0x1E, 0x21,
+ 0x22, 0x23, 0x25, 0x27, 0x29, 0x2C, 0x2F, 0x32,
+ 0x33, 0x34, 0x36, 0x38, 0x3B, 0x34, 0x41, 0x44 },
+ // 13
+ { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0B, 0x0D, 0x11,
+ 0x12, 0x13, 0x15, 0x17, 0x1A, 0x1D, 0x20, 0x23,
+ 0x24, 0x25, 0x27, 0x29, 0x2C, 0x2F, 0x32, 0x35,
+ 0x36, 0x37, 0x39, 0x3B, 0x3E, 0x41, 0x44, 0x47 }
+};
+
+// #pragma mark -
+
+// At the time of writing, the only known case where Kyra 1 uses sound triggers
+// is in the castle, to cycle between three different songs.
+
+const int CadlPlayer::_kyra1SoundTriggers[] = {
+ 0, 4, 5, 3
+};
+
+const int CadlPlayer::_kyra1NumSoundTriggers = ARRAYSIZE(CadlPlayer::_kyra1SoundTriggers);
+
+CadlPlayer::CadlPlayer(Copl *newopl)
+ : CPlayer(newopl), numsubsongs(0), _trackEntries(), _soundDataPtr(0)
+{
+ memset(_trackEntries, 0, sizeof(_trackEntries));
+ _driver = new AdlibDriver(newopl);
+ assert(_driver);
+
+ _sfxPlayingSound = -1;
+ // _soundFileLoaded = "";
+
+ _soundTriggers = _kyra1SoundTriggers;
+ _numSoundTriggers = _kyra1NumSoundTriggers;
+
+ init();
+}
+
+CadlPlayer::~CadlPlayer() {
+ delete [] _soundDataPtr;
+ delete _driver;
+}
+
+bool CadlPlayer::init() {
+ _driver->callback(2);
+ _driver->callback(16, int(4));
+ return true;
+}
+
+void CadlPlayer::process() {
+ uint8 trigger = _driver->callback(11);
+
+ if (trigger < _numSoundTriggers) {
+ int soundId = _soundTriggers[trigger];
+
+ if (soundId) {
+ playTrack(soundId);
+ }
+ } else {
+ warning("Unknown sound trigger %d", trigger);
+ // TODO: At this point, we really want to clear the trigger...
+ }
+}
+
+// void CadlPlayer::setVolume(int volume) {
+// }
+
+// int CadlPlayer::getVolume() {
+// return 0;
+// }
+
+// void CadlPlayer::loadMusicFile(const char *file) {
+// loadSoundFile(file);
+// }
+
+void CadlPlayer::playTrack(uint8 track) {
+ play(track);
+}
+
+// void CadlPlayer::haltTrack() {
+// unk1();
+// unk2();
+// //_engine->_system->delayMillis(3 * 60);
+// }
+
+void CadlPlayer::playSoundEffect(uint8_t track) {
+ play(track);
+}
+
+void CadlPlayer::play(uint8_t track) {
+ uint8 soundId = _trackEntries[track];
+ if ((int8)soundId == -1 || !_soundDataPtr)
+ return;
+ soundId &= 0xFF;
+ _driver->callback(16, 0);
+ // while ((_driver->callback(16, 0) & 8)) {
+ // We call the system delay and not the game delay to avoid concurrency issues.
+ // _engine->_system->delayMillis(10);
+ // }
+ if (_sfxPlayingSound != -1) {
+ // Restore the sounds's normal values.
+ _driver->callback(10, _sfxPlayingSound, int(1), int(_sfxPriority));
+ _driver->callback(10, _sfxPlayingSound, int(3), int(_sfxFourthByteOfSong));
+ _sfxPlayingSound = -1;
+ }
+
+ int chan = _driver->callback(9, soundId, int(0));
+
+ if (chan != 9) {
+ _sfxPlayingSound = soundId;
+ _sfxPriority = _driver->callback(9, soundId, int(1));
+ _sfxFourthByteOfSong = _driver->callback(9, soundId, int(3));
+
+ // In the cases I've seen, the mysterious fourth byte has been
+ // the parameter for the update_setExtraLevel3() callback.
+ //
+ // The extra level is part of the channels "total level", which
+ // is a six-bit value where larger values means softer volume.
+ //
+ // So what seems to be happening here is that sounds which are
+ // started by this function are given a slightly lower priority
+ // and a slightly higher (i.e. softer) extra level 3 than they
+ // would have if they were started from anywhere else. Strange.
+
+ int newVal = ((((-_sfxFourthByteOfSong) + 63) * 0xFF) >> 8) & 0xFF;
+ newVal = -newVal + 63;
+ _driver->callback(10, soundId, int(3), newVal);
+ newVal = ((_sfxPriority * 0xFF) >> 8) & 0xFF;
+ _driver->callback(10, soundId, int(1), newVal);
+ }
+
+ _driver->callback(6, soundId);
+}
+
+// void CadlPlayer::beginFadeOut() {
+// playSoundEffect(1);
+// }
+
+bool CadlPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename);
+
+ // file validation section
+ if(!f || !fp.extension(filename, ".adl")) {
+ fp.close(f);
+ return false;
+ }
+
+ // if (_soundFileLoaded == file)
+ // return;
+
+ // if (_soundDataPtr) {
+ // haltTrack();
+ // }
+
+ uint8 *file_data = 0; uint32 file_size = 0;
+
+ // char filename[25];
+ // sprintf(filename, "%s.ADL", file);
+
+ // file_data = _engine->resource()->fileData(filename, &file_size);
+ // if (!file_data) {
+ // warning("Couldn't find music file: '%s'", filename);
+ // return;
+ // }
+
+ unk2();
+ unk1();
+
+ file_size = fp.filesize(f);
+ file_data = new uint8 [file_size];
+ f->readString((char *)file_data, file_size);
+
+ _driver->callback(8, int(-1));
+ _soundDataPtr = 0;
+
+ uint8 *p = file_data;
+ memcpy(_trackEntries, p, 120*sizeof(uint8));
+ p += 120;
+
+ int soundDataSize = file_size - 120;
+
+ _soundDataPtr = new uint8[soundDataSize];
+ assert(_soundDataPtr);
+
+ memcpy(_soundDataPtr, p, soundDataSize*sizeof(uint8));
+
+ delete [] file_data;
+ file_data = p = 0;
+ file_size = 0;
+
+ _driver->callback(4, _soundDataPtr);
+
+ // _soundFileLoaded = file;
+
+ for(int i = 0; i < 200; i++)
+ if(_trackEntries[i] != 0xff)
+ numsubsongs = i + 1;
+
+ fp.close(f);
+ return true;
+}
+
+void CadlPlayer::rewind(int subsong)
+{
+ opl->init();
+ opl->write(1,32);
+ playSoundEffect(subsong);
+ cursubsong = subsong;
+ update();
+}
+
+unsigned int CadlPlayer::getsubsongs()
+{
+ return numsubsongs;
+}
+
+bool CadlPlayer::update()
+{
+ bool songend = true;
+
+// if(_trackEntries[cursubsong] == 0xff)
+// return false;
+
+ _driver->callback();
+
+ for(int i = 0; i < 10; i++)
+ if(_driver->_channels[i].dataptr != NULL)
+ songend = false;
+
+ return !songend;
+}
+
+void CadlPlayer::unk1() {
+ playSoundEffect(0);
+ //_engine->_system->delayMillis(5 * 60);
+}
+
+void CadlPlayer::unk2() {
+ playSoundEffect(0);
+}
+
+CPlayer *CadlPlayer::factory(Copl *newopl)
+{
+ return new CadlPlayer(newopl);
+}
diff --git a/plugins/adplug/adplug/adl.h b/plugins/adplug/adplug/adl.h
new file mode 100644
index 00000000..b0030c43
--- /dev/null
+++ b/plugins/adplug/adplug/adl.h
@@ -0,0 +1,65 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * adl.h - ADL player adaption by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_ADLPLAYER
+#define H_ADPLUG_ADLPLAYER
+
+#include <inttypes.h>
+
+#include "player.h"
+
+class AdlibDriver;
+
+class CadlPlayer: public CPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ CadlPlayer(Copl *newopl);
+ ~CadlPlayer();
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+
+ // refresh rate is fixed at 72Hz
+ float getrefresh()
+ {
+ return 72.0f;
+ }
+
+ unsigned int getsubsongs();
+ std::string gettype() { return std::string("Westwood ADL"); }
+
+ private:
+ int numsubsongs, cursubsong;
+
+ AdlibDriver *_driver;
+
+ uint8_t _trackEntries[120];
+ uint8_t *_soundDataPtr;
+ int _sfxPlayingSound;
+
+ uint8_t _sfxPriority;
+ uint8_t _sfxFourthByteOfSong;
+
+ int _numSoundTriggers;
+ const int *_soundTriggers;
+
+ static const int _kyra1NumSoundTriggers;
+ static const int _kyra1SoundTriggers[];
+
+ bool init();
+ void process();
+ void playTrack(uint8_t track);
+ void playSoundEffect(uint8_t track);
+ void play(uint8_t track);
+ void unk1();
+ void unk2();
+};
+
+#endif
diff --git a/plugins/adplug/adplug/adlibemu.c b/plugins/adplug/adplug/adlibemu.c
new file mode 100644
index 00000000..c35d6da7
--- /dev/null
+++ b/plugins/adplug/adplug/adlibemu.c
@@ -0,0 +1,600 @@
+/*
+ * ADLIBEMU.C
+ * Copyright (C) 1998-2001 Ken Silverman
+ * Ken Silverman's official web site: "http://www.advsys.net/ken"
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+This file is a digital Adlib emulator for OPL2 and possibly OPL3
+
+Features that could be added in a future version:
+- Amplitude and Frequency Vibrato Bits (not hard, but a big speed hit)
+- Global Keyboard Split Number Bit (need to research this one some more)
+- 2nd Adlib chip for OPL3 (simply need to make my cell array bigger)
+- Advanced connection modes of OPL3 (Just need to add more "docell" cases)
+- L/R Stereo bits of OPL3 (Need adlibgetsample to return stereo)
+
+Features that aren't worth supporting:
+- Anything related to adlib timers&interrupts (Sorry - I always used IRQ0)
+- Composite sine wave mode (CSM) (Supported only on ancient cards)
+
+I'm not sure about a few things in my code:
+- Attack curve. What function is this anyway? I chose to use an order-3
+ polynomial to approximate but this doesn't seem right.
+- Attack/Decay/Release constants - my constants may not be exact
+- What should ADJUSTSPEED be?
+- Haven't verified that Global Keyboard Split Number Bit works yet
+- Some of the drums don't always sound right. It's pretty hard to guess
+ the exact waveform of drums when you look at random data which is
+ slightly randomized due to digital ADC recording.
+- Adlib seems to have a lot more treble than my emulator does. I'm not
+ sure if this is simply unfixable due to the sound blaster's different
+ filtering on FM and digital playback or if it's a serious bug in my
+ code.
+*/
+
+#include <math.h>
+#include <string.h>
+
+#if !defined(max) && !defined(__cplusplus)
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+#if !defined(min) && !defined(__cplusplus)
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#define PI 3.141592653589793
+#define MAXCELLS 18
+#define WAVPREC 2048
+
+static float AMPSCALE=(8192.0);
+#define FRQSCALE (49716/512.0)
+
+//Constants for Ken's Awe32, on a PII-266 (Ken says: Use these for KSM's!)
+#define MODFACTOR 4.0 //How much of modulator cell goes into carrier
+#define MFBFACTOR 1.0 //How much feedback goes back into modulator
+#define ADJUSTSPEED 0.75 //0<=x<=1 Simulate finite rate of change of state
+
+//Constants for Ken's Awe64G, on a P-133
+//#define MODFACTOR 4.25 //How much of modulator cell goes into carrier
+//#define MFBFACTOR 0.5 //How much feedback goes back into modulator
+//#define ADJUSTSPEED 0.85 //0<=x<=1 Simulate finite rate of change of state
+
+typedef struct
+{
+ float val, t, tinc, vol, sustain, amp, mfb;
+ float a0, a1, a2, a3, decaymul, releasemul;
+ short *waveform;
+ long wavemask;
+ void (*cellfunc)(void *, float);
+ unsigned char flags, dum0, dum1, dum2;
+} celltype;
+
+static long numspeakers, bytespersample;
+static float recipsamp;
+static celltype cell[MAXCELLS];
+static signed short wavtable[WAVPREC*3];
+static float kslmul[4] = {0.0,0.5,0.25,1.0};
+static float frqmul[16] = {.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15}, nfrqmul[16];
+static unsigned char adlibreg[256], ksl[8][16];
+static unsigned char modulatorbase[9] = {0,1,2,8,9,10,16,17,18};
+static unsigned char odrumstat = 0;
+static unsigned char base2cell[22] = {0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8};
+
+float lvol[9] = {1,1,1,1,1,1,1,1,1}; //Volume multiplier on left speaker
+float rvol[9] = {1,1,1,1,1,1,1,1,1}; //Volume multiplier on right speaker
+long lplc[9] = {0,0,0,0,0,0,0,0,0}; //Samples to delay on left speaker
+long rplc[9] = {0,0,0,0,0,0,0,0,0}; //Samples to delay on right speaker
+
+long nlvol[9], nrvol[9];
+long nlplc[9], nrplc[9];
+long rend = 0;
+#define FIFOSIZ 256
+static float *rptr[9], *nrptr[9];
+static float rbuf[9][FIFOSIZ*2];
+static float snd[FIFOSIZ*2];
+
+#ifndef USING_ASM
+#define _inline
+#endif
+
+#ifdef USING_ASM
+static _inline void ftol (float f, long *a)
+{
+ _asm
+ {
+ mov eax, a
+ fld f
+ fistp dword ptr [eax]
+ }
+}
+#else
+static void ftol(float f, long *a) {
+ *a=f;
+}
+#endif
+
+#define ctc ((celltype *)c) //A rare attempt to make code easier to read!
+void docell4 (void *c, float modulator) { }
+void docell3 (void *c, float modulator)
+{
+ long i;
+
+ ftol(ctc->t+modulator,&i);
+ ctc->t += ctc->tinc;
+ ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED;
+}
+void docell2 (void *c, float modulator)
+{
+ long i;
+
+ ftol(ctc->t+modulator,&i);
+
+ if (*(long *)&ctc->amp <= 0x37800000)
+ {
+ ctc->amp = 0;
+ ctc->cellfunc = docell4;
+ }
+ ctc->amp *= ctc->releasemul;
+
+ ctc->t += ctc->tinc;
+ ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED;
+}
+void docell1 (void *c, float modulator)
+{
+ long i;
+
+ ftol(ctc->t+modulator,&i);
+
+ if ((*(long *)&ctc->amp) <= (*(long *)&ctc->sustain))
+ {
+ if (ctc->flags&32)
+ {
+ ctc->amp = ctc->sustain;
+ ctc->cellfunc = docell3;
+ }
+ else
+ ctc->cellfunc = docell2;
+ }
+ else
+ ctc->amp *= ctc->decaymul;
+
+ ctc->t += ctc->tinc;
+ ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED;
+}
+void docell0 (void *c, float modulator)
+{
+ long i;
+
+ ftol(ctc->t+modulator,&i);
+
+ ctc->amp = ((ctc->a3*ctc->amp + ctc->a2)*ctc->amp + ctc->a1)*ctc->amp + ctc->a0;
+ if ((*(long *)&ctc->amp) > 0x3f800000)
+ {
+ ctc->amp = 1;
+ ctc->cellfunc = docell1;
+ }
+
+ ctc->t += ctc->tinc;
+ ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED;
+}
+
+
+static long waveform[8] = {WAVPREC,WAVPREC>>1,WAVPREC,(WAVPREC*3)>>2,0,0,(WAVPREC*5)>>2,WAVPREC<<1};
+static long wavemask[8] = {WAVPREC-1,WAVPREC-1,(WAVPREC>>1)-1,(WAVPREC>>1)-1,WAVPREC-1,((WAVPREC*3)>>2)-1,WAVPREC>>1,WAVPREC-1};
+static long wavestart[8] = {0,WAVPREC>>1,0,WAVPREC>>2,0,0,0,WAVPREC>>3};
+static float attackconst[4] = {1/2.82624,1/2.25280,1/1.88416,1/1.59744};
+static float decrelconst[4] = {1/39.28064,1/31.41608,1/26.17344,1/22.44608};
+void cellon (long i, long j, celltype *c, unsigned char iscarrier)
+{
+ long frn, oct, toff;
+ float f;
+
+ frn = ((((long)adlibreg[i+0xb0])&3)<<8) + (long)adlibreg[i+0xa0];
+ oct = ((((long)adlibreg[i+0xb0])>>2)&7);
+ toff = (oct<<1) + ((frn>>9)&((frn>>8)|(((adlibreg[8]>>6)&1)^1)));
+ if (!(adlibreg[j+0x20]&16)) toff >>= 2;
+
+ f = pow(2.0,(adlibreg[j+0x60]>>4)+(toff>>2)-1)*attackconst[toff&3]*recipsamp;
+ c->a0 = .0377*f; c->a1 = 10.73*f+1; c->a2 = -17.57*f; c->a3 = 7.42*f;
+ f = -7.4493*decrelconst[toff&3]*recipsamp;
+ c->decaymul = pow(2.0,f*pow(2.0,(adlibreg[j+0x60]&15)+(toff>>2)));
+ c->releasemul = pow(2.0,f*pow(2.0,(adlibreg[j+0x80]&15)+(toff>>2)));
+ c->wavemask = wavemask[adlibreg[j+0xe0]&7];
+ c->waveform = &wavtable[waveform[adlibreg[j+0xe0]&7]];
+ if (!(adlibreg[1]&0x20)) c->waveform = &wavtable[WAVPREC];
+ c->t = wavestart[adlibreg[j+0xe0]&7];
+ c->flags = adlibreg[j+0x20];
+ c->cellfunc = docell0;
+ c->tinc = (float)(frn<<oct)*nfrqmul[adlibreg[j+0x20]&15];
+ c->vol = pow(2.0,((float)(adlibreg[j+0x40]&63) +
+ (float)kslmul[adlibreg[j+0x40]>>6]*ksl[oct][frn>>6]) * -.125 - 14);
+ c->sustain = pow(2.0,(float)(adlibreg[j+0x80]>>4) * -.5);
+ if (!iscarrier) c->amp = 0;
+ c->mfb = pow(2.0,((adlibreg[i+0xc0]>>1)&7)+5)*(WAVPREC/2048.0)*MFBFACTOR;
+ if (!(adlibreg[i+0xc0]&14)) c->mfb = 0;
+ c->val = 0;
+}
+
+//This function (and bug fix) written by Chris Moeller
+void cellfreq (signed long i, signed long j, celltype *c)
+{
+ long frn, oct;
+
+ frn = ((((long)adlibreg[i+0xb0])&3)<<8) + (long)adlibreg[i+0xa0];
+ oct = ((((long)adlibreg[i+0xb0])>>2)&7);
+
+ c->tinc = (float)(frn<<oct)*nfrqmul[adlibreg[j+0x20]&15];
+ c->vol = pow(2.0,((float)(adlibreg[j+0x40]&63) +
+ (float)kslmul[adlibreg[j+0x40]>>6]*ksl[oct][frn>>6]) * -.125 - 14);
+}
+
+static long initfirstime = 0;
+void adlibinit (long dasamplerate, long danumspeakers, long dabytespersample)
+{
+ long i, j, frn, oct;
+
+ memset((void *)adlibreg,0,sizeof(adlibreg));
+ memset((void *)cell,0,sizeof(celltype)*MAXCELLS);
+ memset((void *)rbuf,0,sizeof(rbuf));
+ rend = 0; odrumstat = 0;
+
+ for(i=0;i<MAXCELLS;i++)
+ {
+ cell[i].cellfunc = docell4;
+ cell[i].amp = 0;
+ cell[i].vol = 0;
+ cell[i].t = 0;
+ cell[i].tinc = 0;
+ cell[i].wavemask = 0;
+ cell[i].waveform = &wavtable[WAVPREC];
+ }
+
+ numspeakers = danumspeakers;
+ bytespersample = dabytespersample;
+
+ recipsamp = 1.0 / (float)dasamplerate;
+ for(i=15;i>=0;i--) nfrqmul[i] = frqmul[i]*recipsamp*FRQSCALE*(WAVPREC/2048.0);
+
+ if (!initfirstime)
+ {
+ initfirstime = 1;
+
+ for(i=0;i<(WAVPREC>>1);i++)
+ {
+ wavtable[i] =
+ wavtable[(i<<1) +WAVPREC] = (signed short)(16384*sin((float)((i<<1) )*PI*2/WAVPREC));
+ wavtable[(i<<1)+1+WAVPREC] = (signed short)(16384*sin((float)((i<<1)+1)*PI*2/WAVPREC));
+ }
+ for(i=0;i<(WAVPREC>>3);i++)
+ {
+ wavtable[i+(WAVPREC<<1)] = wavtable[i+(WAVPREC>>3)]-16384;
+ wavtable[i+((WAVPREC*17)>>3)] = wavtable[i+(WAVPREC>>2)]+16384;
+ }
+
+ //[table in book]*8/3
+ ksl[7][0] = 0; ksl[7][1] = 24; ksl[7][2] = 32; ksl[7][3] = 37;
+ ksl[7][4] = 40; ksl[7][5] = 43; ksl[7][6] = 45; ksl[7][7] = 47;
+ ksl[7][8] = 48; for(i=9;i<16;i++) ksl[7][i] = i+41;
+ for(j=6;j>=0;j--)
+ for(i=0;i<16;i++)
+ {
+ oct = (long)ksl[j+1][i]-8; if (oct < 0) oct = 0;
+ ksl[j][i] = (unsigned char)oct;
+ }
+ }
+ else
+ {
+ for(i=0;i<9;i++)
+ {
+ frn = ((((long)adlibreg[i+0xb0])&3)<<8) + (long)adlibreg[i+0xa0];
+ oct = ((((long)adlibreg[i+0xb0])>>2)&7);
+ cell[i].tinc = (float)(frn<<oct)*nfrqmul[adlibreg[modulatorbase[i]+0x20]&15];
+ }
+ }
+}
+
+void adlib0 (long i, long v)
+{
+ unsigned char tmp = adlibreg[i];
+ adlibreg[i] = v;
+
+ if (i == 0xbd)
+ {
+ if ((v&16) > (odrumstat&16)) //BassDrum
+ {
+ cellon(6,16,&cell[6],0);
+ cellon(6,19,&cell[15],1);
+ cell[15].vol *= 2;
+ }
+ if ((v&8) > (odrumstat&8)) //Snare
+ {
+ cellon(16,20,&cell[16],0);
+ cell[16].tinc *= 2*(nfrqmul[adlibreg[17+0x20]&15] / nfrqmul[adlibreg[20+0x20]&15]);
+ if (((adlibreg[20+0xe0]&7) >= 3) && ((adlibreg[20+0xe0]&7) <= 5)) cell[16].vol = 0;
+ cell[16].vol *= 2;
+ }
+ if ((v&4) > (odrumstat&4)) //TomTom
+ {
+ cellon(8,18,&cell[8],0);
+ cell[8].vol *= 2;
+ }
+ if ((v&2) > (odrumstat&2)) //Cymbal
+ {
+ cellon(17,21,&cell[17],0);
+
+ cell[17].wavemask = wavemask[5];
+ cell[17].waveform = &wavtable[waveform[5]];
+ cell[17].tinc *= 16; cell[17].vol *= 2;
+
+ //cell[17].waveform = &wavtable[WAVPREC]; cell[17].wavemask = 0;
+ //if (((adlibreg[21+0xe0]&7) == 0) || ((adlibreg[21+0xe0]&7) == 6))
+ // cell[17].waveform = &wavtable[(WAVPREC*7)>>2];
+ //if (((adlibreg[21+0xe0]&7) == 2) || ((adlibreg[21+0xe0]&7) == 3))
+ // cell[17].waveform = &wavtable[(WAVPREC*5)>>2];
+ }
+ if ((v&1) > (odrumstat&1)) //Hihat
+ {
+ cellon(7,17,&cell[7],0);
+ if (((adlibreg[17+0xe0]&7) == 1) || ((adlibreg[17+0xe0]&7) == 4) ||
+ ((adlibreg[17+0xe0]&7) == 5) || ((adlibreg[17+0xe0]&7) == 7)) cell[7].vol = 0;
+ if ((adlibreg[17+0xe0]&7) == 6) { cell[7].wavemask = 0; cell[7].waveform = &wavtable[(WAVPREC*7)>>2]; }
+ }
+
+ odrumstat = v;
+ }
+ else if (((unsigned)(i-0x40) < (unsigned)22) && ((i&7) < 6))
+ {
+ if ((i&7) < 3) // Modulator
+ cellfreq(base2cell[i-0x40],i-0x40,&cell[base2cell[i-0x40]]);
+ else // Carrier
+ cellfreq(base2cell[i-0x40],i-0x40,&cell[base2cell[i-0x40]+9]);
+ }
+ else if ((unsigned)(i-0xa0) < (unsigned)9)
+ {
+ cellfreq(i-0xa0,modulatorbase[i-0xa0],&cell[i-0xa0]);
+ cellfreq(i-0xa0,modulatorbase[i-0xa0]+3,&cell[i-0xa0+9]);
+ }
+ else if ((unsigned)(i-0xb0) < (unsigned)9)
+ {
+ if ((v&32) > (tmp&32))
+ {
+ cellon(i-0xb0,modulatorbase[i-0xb0],&cell[i-0xb0],0);
+ cellon(i-0xb0,modulatorbase[i-0xb0]+3,&cell[i-0xb0+9],1);
+ }
+ else if ((v&32) < (tmp&32))
+ cell[i-0xb0].cellfunc = cell[i-0xb0+9].cellfunc = docell2;
+ cellfreq(i-0xb0,modulatorbase[i-0xb0],&cell[i-0xb0]);
+ cellfreq(i-0xb0,modulatorbase[i-0xb0]+3,&cell[i-0xb0+9]);
+ }
+
+ //outdata(i,v);
+}
+
+#ifdef USING_ASM
+static long fpuasm;
+static float fakeadd = 8388608.0+128.0;
+static _inline void clipit8 (float f, long a)
+{
+ _asm
+ {
+ mov edi, a
+ fld dword ptr f
+ fadd dword ptr fakeadd
+ fstp dword ptr fpuasm
+ mov eax, fpuasm
+ test eax, 0x007fff00
+ jz short skipit
+ shr eax, 16
+ xor eax, -1
+ skipit: mov byte ptr [edi], al
+ }
+}
+
+static _inline void clipit16 (float f, long a)
+{
+ _asm
+ {
+ mov eax, a
+ fld dword ptr f
+ fist word ptr [eax]
+ cmp word ptr [eax], 0x8000
+ jne short skipit2
+ fst dword ptr [fpuasm]
+ cmp fpuasm, 0x80000000
+ sbb word ptr [eax], 0
+ skipit2: fstp st
+ }
+}
+#else
+static void clipit8(float f,unsigned char *a) {
+ f/=256.0;
+ f+=128.0;
+ if (f>254.5) *a=255;
+ else if (f<0.5) *a=0;
+ else *a=f;
+}
+
+static void clipit16(float f,short *a) {
+ if (f>32766.5) *a=32767;
+ else if (f<-32767.5) *a=-32768;
+ else *a=f;
+}
+#endif
+
+void adlibsetvolume(int i) {
+ AMPSCALE=i;
+}
+
+void adlibgetsample (unsigned char *sndptr, long numbytes)
+{
+ long i, j, k=0, ns, endsamples, rptrs, numsamples;
+ celltype *cptr;
+ float f;
+ short *sndptr2=(short *)sndptr;
+
+ numsamples = (numbytes>>(numspeakers+bytespersample-2));
+
+ if (bytespersample == 1) f = AMPSCALE/256.0; else f = AMPSCALE;
+ if (numspeakers == 1)
+ {
+ nlvol[0] = lvol[0]*f;
+ for(i=0;i<9;i++) rptr[i] = &rbuf[0][0];
+ rptrs = 1;
+ }
+ else
+ {
+ rptrs = 0;
+ for(i=0;i<9;i++)
+ {
+ if ((!i) || (lvol[i] != lvol[i-1]) || (rvol[i] != rvol[i-1]) ||
+ (lplc[i] != lplc[i-1]) || (rplc[i] != rplc[i-1]))
+ {
+ nlvol[rptrs] = lvol[i]*f;
+ nrvol[rptrs] = rvol[i]*f;
+ nlplc[rptrs] = rend-min(max(lplc[i],0),FIFOSIZ);
+ nrplc[rptrs] = rend-min(max(rplc[i],0),FIFOSIZ);
+ rptrs++;
+ }
+ rptr[i] = &rbuf[rptrs-1][0];
+ }
+ }
+
+
+ //CPU time used to be somewhat less when emulator was only mono!
+ // Because of no delay fifos!
+
+ for(ns=0;ns<numsamples;ns+=endsamples)
+ {
+ endsamples = min(FIFOSIZ*2-rend,FIFOSIZ);
+ endsamples = min(endsamples,numsamples-ns);
+
+ for(i=0;i<9;i++)
+ nrptr[i] = &rptr[i][rend];
+ for(i=0;i<rptrs;i++)
+ memset((void *)&rbuf[i][rend],0,endsamples*sizeof(float));
+
+ if (adlibreg[0xbd]&0x20)
+ {
+ //BassDrum (j=6)
+ if (cell[15].cellfunc != docell4)
+ {
+ if (adlibreg[0xc6]&1)
+ {
+ for(i=0;i<endsamples;i++)
+ {
+ (cell[15].cellfunc)((void *)&cell[15],0.0);
+ nrptr[6][i] += cell[15].val;
+ }
+ }
+ else
+ {
+ for(i=0;i<endsamples;i++)
+ {
+ (cell[6].cellfunc)((void *)&cell[6],cell[6].val*cell[6].mfb);
+ (cell[15].cellfunc)((void *)&cell[15],cell[6].val*WAVPREC*MODFACTOR);
+ nrptr[6][i] += cell[15].val;
+ }
+ }
+ }
+
+ //Snare/Hihat (j=7), Cymbal/TomTom (j=8)
+ if ((cell[7].cellfunc != docell4) || (cell[8].cellfunc != docell4) || (cell[16].cellfunc != docell4) || (cell[17].cellfunc != docell4))
+ {
+ for(i=0;i<endsamples;i++)
+ {
+ k = k*1664525+1013904223;
+ (cell[16].cellfunc)((void *)&cell[16],k&((WAVPREC>>1)-1)); //Snare
+ (cell[7].cellfunc)((void *)&cell[7],k&(WAVPREC-1)); //Hihat
+ (cell[17].cellfunc)((void *)&cell[17],k&((WAVPREC>>3)-1)); //Cymbal
+ (cell[8].cellfunc)((void *)&cell[8],0.0); //TomTom
+ nrptr[7][i] += cell[7].val + cell[16].val;
+ nrptr[8][i] += cell[8].val + cell[17].val;
+ }
+ }
+ }
+ for(j=9-1;j>=0;j--)
+ {
+ if ((adlibreg[0xbd]&0x20) && (j >= 6) && (j < 9)) continue;
+
+ cptr = &cell[j]; k = j;
+ if (adlibreg[0xc0+k]&1)
+ {
+ if ((cptr[9].cellfunc == docell4) && (cptr->cellfunc == docell4)) continue;
+ for(i=0;i<endsamples;i++)
+ {
+ (cptr->cellfunc)((void *)cptr,cptr->val*cptr->mfb);
+ (cptr->cellfunc)((void *)&cptr[9],0);
+ nrptr[j][i] += cptr[9].val + cptr->val;
+ }
+ }
+ else
+ {
+ if (cptr[9].cellfunc == docell4) continue;
+ for(i=0;i<endsamples;i++)
+ {
+ (cptr->cellfunc)((void *)cptr,cptr->val*cptr->mfb);
+ (cptr[9].cellfunc)((void *)&cptr[9],cptr->val*WAVPREC*MODFACTOR);
+ nrptr[j][i] += cptr[9].val;
+ }
+ }
+ }
+
+ if (numspeakers == 1)
+ {
+ if (bytespersample == 1)
+ {
+ for(i=endsamples-1;i>=0;i--)
+ clipit8(nrptr[0][i]*nlvol[0],sndptr+1);
+ }
+ else
+ {
+ for(i=endsamples-1;i>=0;i--)
+ clipit16(nrptr[0][i]*nlvol[0],sndptr2+i);
+ }
+ }
+ else
+ {
+ memset((void *)snd,0,endsamples*sizeof(float)*2);
+ for(j=0;j<rptrs;j++)
+ {
+ for(i=0;i<endsamples;i++)
+ {
+ snd[(i<<1) ] += rbuf[j][(nlplc[j]+i)&(FIFOSIZ*2-1)]*nlvol[j];
+ snd[(i<<1)+1] += rbuf[j][(nrplc[j]+i)&(FIFOSIZ*2-1)]*nrvol[j];
+ }
+ nlplc[j] += endsamples;
+ nrplc[j] += endsamples;
+ }
+
+ if (bytespersample == 1)
+ {
+ for(i=(endsamples<<1)-1;i>=0;i--)
+ clipit8(snd[i],sndptr+i);
+ }
+ else
+ {
+ for(i=(endsamples<<1)-1;i>=0;i--)
+ clipit16(snd[i],sndptr2+i);
+ }
+ }
+
+ sndptr = sndptr+(numspeakers*endsamples);
+ sndptr2 = sndptr2+(numspeakers*endsamples);
+ rend = ((rend+endsamples)&(FIFOSIZ*2-1));
+ }
+}
diff --git a/plugins/adplug/adplug/adlibemu.h b/plugins/adplug/adplug/adlibemu.h
new file mode 100644
index 00000000..8600d787
--- /dev/null
+++ b/plugins/adplug/adplug/adlibemu.h
@@ -0,0 +1,26 @@
+/*
+ * ADLIBEMU.H
+ * Copyright (C) 1998-2001 Ken Silverman
+ * Ken Silverman's official web site: "http://www.advsys.net/ken"
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+void adlibinit(long dasamplerate,long danumspeakers,long dabytespersample);
+void adlib0(long i,long v);
+void adlibgetsample(void *sndptr,long numbytes);
+void adlibsetvolume(int i);
+void randoinsts();
+extern float lvol[9],rvol[9],lplc[9],rplc[9];
diff --git a/plugins/adplug/adplug/adplug.cpp b/plugins/adplug/adplug/adplug.cpp
new file mode 100644
index 00000000..32fea695
--- /dev/null
+++ b/plugins/adplug/adplug/adplug.cpp
@@ -0,0 +1,182 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2006 Simon Peter <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * adplug.cpp - CAdPlug utility class, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string>
+#include <binfile.h>
+#include <string.h>
+
+#include "adplug.h"
+#include "debug.h"
+
+/***** Replayer includes *****/
+
+#include "hsc.h"
+#include "amd.h"
+#include "a2m.h"
+#include "imf.h"
+#include "sng.h"
+#include "adtrack.h"
+#include "bam.h"
+#include "d00.h"
+#include "dfm.h"
+#include "hsp.h"
+#include "ksm.h"
+#include "mad.h"
+#include "mid.h"
+#include "mkj.h"
+#include "cff.h"
+#include "dmo.h"
+#include "s3m.h"
+#include "dtm.h"
+#include "fmc.h"
+#include "mtk.h"
+#include "rad.h"
+#include "raw.h"
+#include "sa2.h"
+#include "bmf.h"
+#include "flash.h"
+#include "hybrid.h"
+#include "hyp.h"
+#include "psi.h"
+#include "rat.h"
+#include "lds.h"
+#include "u6m.h"
+#include "rol.h"
+#include "xsm.h"
+#include "dro.h"
+#include "msc.h"
+#include "rix.h"
+#include "adl.h"
+
+/***** CAdPlug *****/
+
+// List of all players that come with the standard AdPlug distribution
+const CPlayerDesc CAdPlug::allplayers[] = {
+ CPlayerDesc(ChscPlayer::factory, "HSC-Tracker", ".hsc\0"),
+ CPlayerDesc(CsngPlayer::factory, "SNGPlay", ".sng\0"),
+ CPlayerDesc(CimfPlayer::factory, "Apogee IMF", ".imf\0.wlf\0.adlib\0"),
+ CPlayerDesc(Ca2mLoader::factory, "Adlib Tracker 2", ".a2m\0"),
+ CPlayerDesc(CadtrackLoader::factory, "Adlib Tracker", ".sng\0"),
+ CPlayerDesc(CamdLoader::factory, "AMUSIC", ".amd\0"),
+ CPlayerDesc(CbamPlayer::factory, "Bob's Adlib Music", ".bam\0"),
+ CPlayerDesc(Cd00Player::factory, "Packed EdLib", ".d00\0"),
+ CPlayerDesc(CdfmLoader::factory, "Digital-FM", ".dfm\0"),
+ CPlayerDesc(ChspLoader::factory, "HSC Packed", ".hsp\0"),
+ CPlayerDesc(CksmPlayer::factory, "Ken Silverman Music", ".ksm\0"),
+ CPlayerDesc(CmadLoader::factory, "Mlat Adlib Tracker", ".mad\0"),
+ CPlayerDesc(CmidPlayer::factory, "MIDI", ".mid\0.cmf\0.sci\0.laa\0"),
+ CPlayerDesc(CmkjPlayer::factory, "MKJamz", ".mkj\0"),
+ CPlayerDesc(CcffLoader::factory, "Boomtracker", ".cff\0"),
+ CPlayerDesc(CdmoLoader::factory, "TwinTeam", ".dmo\0"),
+ CPlayerDesc(Cs3mPlayer::factory, "Scream Tracker 3", ".s3m\0"),
+ CPlayerDesc(CdtmLoader::factory, "DeFy Adlib Tracker", ".dtm\0"),
+ CPlayerDesc(CfmcLoader::factory, "Faust Music Creator", ".sng\0"),
+ CPlayerDesc(CmtkLoader::factory, "MPU-401 Trakker", ".mtk\0"),
+ CPlayerDesc(CradLoader::factory, "Reality Adlib Tracker", ".rad\0"),
+ CPlayerDesc(CrawPlayer::factory, "RdosPlay RAW", ".raw\0"),
+ CPlayerDesc(Csa2Loader::factory, "Surprise! Adlib Tracker", ".sat\0.sa2\0"),
+ CPlayerDesc(CxadbmfPlayer::factory, "BMF Adlib Tracker", ".xad\0"),
+ CPlayerDesc(CxadflashPlayer::factory, "Flash", ".xad\0"),
+ CPlayerDesc(CxadhybridPlayer::factory, "Hybrid", ".xad\0"),
+ CPlayerDesc(CxadhypPlayer::factory, "Hypnosis", ".xad\0"),
+ CPlayerDesc(CxadpsiPlayer::factory, "PSI", ".xad\0"),
+ CPlayerDesc(CxadratPlayer::factory, "rat", ".xad\0"),
+ CPlayerDesc(CldsPlayer::factory, "LOUDNESS Sound System", ".lds\0"),
+ CPlayerDesc(Cu6mPlayer::factory, "Ultima 6 Music", ".m\0"),
+ CPlayerDesc(CrolPlayer::factory, "Adlib Visual Composer", ".rol\0"),
+ CPlayerDesc(CxsmPlayer::factory, "eXtra Simple Music", ".xsm\0"),
+ CPlayerDesc(CdroPlayer::factory, "DOSBox Raw OPL", ".dro\0"),
+ CPlayerDesc(CmscPlayer::factory, "Adlib MSC Player", ".msc\0"),
+ CPlayerDesc(CrixPlayer::factory, "Softstar RIX OPL Music", ".rix\0"),
+ CPlayerDesc(CadlPlayer::factory, "Westwood ADL", ".adl\0"),
+ CPlayerDesc()
+};
+
+const CPlayers &CAdPlug::init_players(const CPlayerDesc pd[])
+{
+ static CPlayers initplayers;
+ unsigned int i;
+
+ for(i = 0; pd[i].factory; i++)
+ initplayers.push_back(&pd[i]);
+
+ return initplayers;
+}
+
+const CPlayers CAdPlug::players = CAdPlug::init_players(CAdPlug::allplayers);
+CAdPlugDatabase *CAdPlug::database = 0;
+
+CPlayer *CAdPlug::factory(const char *fn, Copl *opl, const CPlayers &pl,
+ const CFileProvider &fp)
+{
+ CPlayer *p;
+ CPlayers::const_iterator i;
+ unsigned int j;
+
+ AdPlug_LogWrite("*** CAdPlug::factory(\"%s\",opl,fp) ***\n", fn);
+
+ // Try a direct hit by file extension
+ for(i = pl.begin(); i != pl.end(); i++)
+ for(j = 0; (*i)->get_extension(j); j++)
+ if(fp.extension(fn, (*i)->get_extension(j))) {
+ AdPlug_LogWrite("Trying direct hit: %s\n", (*i)->filetype.c_str());
+ if((p = (*i)->factory(opl)))
+ if(p->load(fn, fp)) {
+ AdPlug_LogWrite("got it!\n");
+ AdPlug_LogWrite("--- CAdPlug::factory ---\n");
+ return p;
+ } else
+ delete p;
+ }
+
+ // Try all players, one by one
+ for(i = pl.begin(); i != pl.end(); i++) {
+ AdPlug_LogWrite("Trying: %s\n", (*i)->filetype.c_str());
+ if((p = (*i)->factory(opl)))
+ if(p->load(fn, fp)) {
+ AdPlug_LogWrite("got it!\n");
+ AdPlug_LogWrite("--- CAdPlug::factory ---\n");
+ return p;
+ } else
+ delete p;
+ }
+
+ // Unknown file
+ AdPlug_LogWrite("End of list!\n");
+ AdPlug_LogWrite("--- CAdPlug::factory ---\n");
+ return 0;
+}
+
+void CAdPlug::set_database(CAdPlugDatabase *db)
+{
+ database = db;
+}
+
+std::string CAdPlug::get_version()
+{
+ return std::string(VERSION);
+}
+
+void CAdPlug::debug_output(const std::string &filename)
+{
+ AdPlug_LogFile(filename.c_str());
+ AdPlug_LogWrite("CAdPlug::debug_output(\"%s\"): Redirected.\n",filename.c_str());
+}
diff --git a/plugins/adplug/adplug/adplug.h b/plugins/adplug/adplug/adplug.h
new file mode 100644
index 00000000..54ee042a
--- /dev/null
+++ b/plugins/adplug/adplug/adplug.h
@@ -0,0 +1,55 @@
+/*
+ * AdPlug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * adplug.h - AdPlug main header file, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_ADPLUG
+#define H_ADPLUG_ADPLUG
+
+#include <string>
+
+#include "player.h"
+#include "opl.h"
+#include "fprovide.h"
+#include "players.h"
+#include "database.h"
+
+class CAdPlug
+{
+ friend CPlayer::CPlayer(Copl *newopl);
+
+public:
+ static const CPlayers players;
+
+ static CPlayer *factory(const char *fn, Copl *opl,
+ const CPlayers &pl = players,
+ const CFileProvider &fp = CProvider_Filesystem());
+
+ static void set_database(CAdPlugDatabase *db);
+ static std::string get_version();
+ static void debug_output(const std::string &filename);
+
+private:
+ static CAdPlugDatabase *database;
+ static const CPlayerDesc allplayers[];
+
+ static const CPlayers &init_players(const CPlayerDesc pd[]);
+};
+
+#endif
diff --git a/plugins/adplug/adplug/adtrack.cpp b/plugins/adplug/adplug/adtrack.cpp
new file mode 100644
index 00000000..d3f3490a
--- /dev/null
+++ b/plugins/adplug/adplug/adtrack.cpp
@@ -0,0 +1,176 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * adtrack.cpp - Adlib Tracker 1.0 Loader by Simon Peter <dn.tlp@gmx.net>
+ *
+ * NOTES:
+ * The original Adlib Tracker 1.0 is behaving a little different from the
+ * official spec: The 'octave' integer from the instrument file is stored
+ * "minus 1" from the actual value, underflowing from 0 to 0xffff.
+ *
+ * I also noticed that my player is playing everything transposed a few tones
+ * higher than the original tracker. As far as i can see, my player perfectly
+ * follows the official spec, so it "must" be the tracker that does something
+ * wrong here...
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "adtrack.h"
+#include "debug.h"
+
+/*** Public methods ***/
+
+CPlayer *CadtrackLoader::factory(Copl *newopl)
+{
+ return new CadtrackLoader(newopl);
+}
+
+bool CadtrackLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ binistream *instf;
+ char note[2];
+ unsigned short rwp;
+ unsigned char chp, octave, pnote = 0;
+ int i,j;
+ AdTrackInst myinst;
+
+ // file validation
+ if(!fp.extension(filename, ".sng") || fp.filesize(f) != 36000)
+ { fp.close(f); return false; }
+
+ // check for instruments file
+ std::string instfilename(filename, 0, filename.find_last_of('.'));
+ instfilename += ".ins";
+ AdPlug_LogWrite("CadtrackLoader::load(,\"%s\"): Checking for \"%s\"...\n",
+ filename.c_str(), instfilename.c_str());
+ instf = fp.open(instfilename);
+ if(!instf || fp.filesize(instf) != 468) { fp.close(f); return false; }
+
+ // give CmodPlayer a hint on what we're up to
+ realloc_patterns(1,1000,9); realloc_instruments(9); realloc_order(1);
+ init_trackord(); flags = NoKeyOn;
+ (*order) = 0; length = 1; restartpos = 0; bpm = 120; initspeed = 3;
+
+ // load instruments from instruments file
+ for(i=0;i<9;i++) {
+ for(j=0;j<2;j++) {
+ myinst.op[j].appampmod = instf->readInt(2);
+ myinst.op[j].appvib = instf->readInt(2);
+ myinst.op[j].maintsuslvl = instf->readInt(2);
+ myinst.op[j].keybscale = instf->readInt(2);
+ myinst.op[j].octave = instf->readInt(2);
+ myinst.op[j].freqrisevollvldn = instf->readInt(2);
+ myinst.op[j].softness = instf->readInt(2);
+ myinst.op[j].attack = instf->readInt(2);
+ myinst.op[j].decay = instf->readInt(2);
+ myinst.op[j].release = instf->readInt(2);
+ myinst.op[j].sustain = instf->readInt(2);
+ myinst.op[j].feedback = instf->readInt(2);
+ myinst.op[j].waveform = instf->readInt(2);
+ }
+ convert_instrument(i, &myinst);
+ }
+ fp.close(instf);
+
+ // load file
+ for(rwp=0;rwp<1000;rwp++)
+ for(chp=0;chp<9;chp++) {
+ // read next record
+ f->readString(note, 2); octave = f->readInt(1); f->ignore();
+ switch(*note) {
+ case 'C': if(note[1] == '#') pnote = 2; else pnote = 1; break;
+ case 'D': if(note[1] == '#') pnote = 4; else pnote = 3; break;
+ case 'E': pnote = 5; break;
+ case 'F': if(note[1] == '#') pnote = 7; else pnote = 6; break;
+ case 'G': if(note[1] == '#') pnote = 9; else pnote = 8; break;
+ case 'A': if(note[1] == '#') pnote = 11; else pnote = 10; break;
+ case 'B': pnote = 12; break;
+ case '\0':
+ if(note[1] == '\0')
+ tracks[chp][rwp].note = 127;
+ else {
+ fp.close(f);
+ return false;
+ }
+ break;
+ default: fp.close(f); return false;
+ }
+ if((*note) != '\0') {
+ tracks[chp][rwp].note = pnote + (octave * 12);
+ tracks[chp][rwp].inst = chp + 1;
+ }
+ }
+
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+float CadtrackLoader::getrefresh()
+{
+ return 18.2f;
+}
+
+/*** Private methods ***/
+
+void CadtrackLoader::convert_instrument(unsigned int n, AdTrackInst *i)
+{
+ // Carrier "Amp Mod / Vib / Env Type / KSR / Multiple" register
+ inst[n].data[2] = i->op[Carrier].appampmod ? 1 << 7 : 0;
+ inst[n].data[2] += i->op[Carrier].appvib ? 1 << 6 : 0;
+ inst[n].data[2] += i->op[Carrier].maintsuslvl ? 1 << 5 : 0;
+ inst[n].data[2] += i->op[Carrier].keybscale ? 1 << 4 : 0;
+ inst[n].data[2] += (i->op[Carrier].octave + 1) & 0xffff; // Bug in original tracker
+ // Modulator...
+ inst[n].data[1] = i->op[Modulator].appampmod ? 1 << 7 : 0;
+ inst[n].data[1] += i->op[Modulator].appvib ? 1 << 6 : 0;
+ inst[n].data[1] += i->op[Modulator].maintsuslvl ? 1 << 5 : 0;
+ inst[n].data[1] += i->op[Modulator].keybscale ? 1 << 4 : 0;
+ inst[n].data[1] += (i->op[Modulator].octave + 1) & 0xffff; // Bug in original tracker
+
+ // Carrier "Key Scaling / Level" register
+ inst[n].data[10] = (i->op[Carrier].freqrisevollvldn & 3) << 6;
+ inst[n].data[10] += i->op[Carrier].softness & 63;
+ // Modulator...
+ inst[n].data[9] = (i->op[Modulator].freqrisevollvldn & 3) << 6;
+ inst[n].data[9] += i->op[Modulator].softness & 63;
+
+ // Carrier "Attack / Decay" register
+ inst[n].data[4] = (i->op[Carrier].attack & 0x0f) << 4;
+ inst[n].data[4] += i->op[Carrier].decay & 0x0f;
+ // Modulator...
+ inst[n].data[3] = (i->op[Modulator].attack & 0x0f) << 4;
+ inst[n].data[3] += i->op[Modulator].decay & 0x0f;
+
+ // Carrier "Release / Sustain" register
+ inst[n].data[6] = (i->op[Carrier].release & 0x0f) << 4;
+ inst[n].data[6] += i->op[Carrier].sustain & 0x0f;
+ // Modulator...
+ inst[n].data[5] = (i->op[Modulator].release & 0x0f) << 4;
+ inst[n].data[5] += i->op[Modulator].sustain & 0x0f;
+
+ // Channel "Feedback / Connection" register
+ inst[n].data[0] = (i->op[Carrier].feedback & 7) << 1;
+
+ // Carrier/Modulator "Wave Select" registers
+ inst[n].data[8] = i->op[Carrier].waveform & 3;
+ inst[n].data[7] = i->op[Modulator].waveform & 3;
+}
diff --git a/plugins/adplug/adplug/adtrack.h b/plugins/adplug/adplug/adtrack.h
new file mode 100644
index 00000000..289491ff
--- /dev/null
+++ b/plugins/adplug/adplug/adtrack.h
@@ -0,0 +1,53 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * adtrack.h - Adlib Tracker 1.0 Loader by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "protrack.h"
+
+class CadtrackLoader: public CmodPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CadtrackLoader(Copl *newopl)
+ : CmodPlayer(newopl)
+ { };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ float getrefresh();
+
+ std::string gettype()
+ { return std::string("Adlib Tracker 1.0"); };
+ unsigned int getinstruments()
+ { return 9; };
+
+private:
+ enum Operators {Carrier = 1, Modulator = 0};
+
+ typedef struct {
+ struct {
+ unsigned short appampmod, appvib, maintsuslvl, keybscale, octave,
+ freqrisevollvldn, softness, attack, decay, release, sustain,
+ feedback, waveform;
+ } op[2];
+ } AdTrackInst;
+
+ void convert_instrument(unsigned int n, AdTrackInst *i);
+};
diff --git a/plugins/adplug/adplug/amd.cpp b/plugins/adplug/adplug/amd.cpp
new file mode 100644
index 00000000..4f844227
--- /dev/null
+++ b/plugins/adplug/adplug/amd.cpp
@@ -0,0 +1,193 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * amd.cpp - AMD Loader by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string.h>
+
+#include "amd.h"
+#include "debug.h"
+
+CPlayer *CamdLoader::factory(Copl *newopl)
+{
+ return new CamdLoader(newopl);
+}
+
+bool CamdLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ struct {
+ char id[9];
+ unsigned char version;
+ } header;
+ int i, j, k, t, numtrax, maxi = 0;
+ unsigned char buf, buf2, buf3;
+ const unsigned char convfx[10] = {0,1,2,9,17,11,13,18,3,14};
+ const unsigned char convvol[64] = {
+ 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 0xa, 0xa, 0xb,
+ 0xc, 0xc, 0xd, 0xe, 0xe, 0xf, 0x10, 0x10, 0x11, 0x12, 0x13, 0x14, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x21,
+ 0x22, 0x23, 0x25, 0x26, 0x28, 0x29, 0x2b, 0x2d, 0x2e, 0x30, 0x32, 0x35,
+ 0x37, 0x3a, 0x3c, 0x3f
+ };
+
+ // file validation section
+ if(fp.filesize(f) < 1072) { fp.close(f); return false; }
+ f->seek(1062); f->readString(header.id, 9);
+ header.version = f->readInt(1);
+ if(strncmp(header.id, "<o\xefQU\xeeRoR", 9) &&
+ strncmp(header.id, "MaDoKaN96", 9)) { fp.close(f); return false; }
+
+ // load section
+ memset(inst, 0, sizeof(inst));
+ f->seek(0);
+ f->readString(songname, sizeof(songname));
+ f->readString(author, sizeof(author));
+ for(i = 0; i < 26; i++) {
+ f->readString(instname[i], 23);
+ for(j = 0; j < 11; j++) inst[i].data[j] = f->readInt(1);
+ }
+ length = f->readInt(1); nop = f->readInt(1) + 1;
+ for(i=0;i<128;i++) order[i] = f->readInt(1);
+ f->seek(10, binio::Add);
+ if(header.version == 0x10) { // unpacked module
+ maxi = nop * 9;
+ for(i=0;i<64*9;i++)
+ trackord[i/9][i%9] = i+1;
+ t = 0;
+ while(!f->ateof()) {
+ for(j=0;j<64;j++)
+ for(i=t;i<t+9;i++) {
+ buf = f->readInt(1);
+ tracks[i][j].param2 = (buf&127) % 10;
+ tracks[i][j].param1 = (buf&127) / 10;
+ buf = f->readInt(1);
+ tracks[i][j].inst = buf >> 4;
+ tracks[i][j].command = buf & 0x0f;
+ buf = f->readInt(1);
+ if(buf >> 4) // fix bug in AMD save routine
+ tracks[i][j].note = ((buf & 14) >> 1) * 12 + (buf >> 4);
+ else
+ tracks[i][j].note = 0;
+ tracks[i][j].inst += (buf & 1) << 4;
+ }
+ t += 9;
+ }
+ } else { // packed module
+ for(i=0;i<nop;i++)
+ for(j=0;j<9;j++)
+ trackord[i][j] = f->readInt(2) + 1;
+ numtrax = f->readInt(2);
+ for(k=0;k<numtrax;k++) {
+ i = f->readInt(2);
+ if(i > 575) i = 575; // fix corrupted modules
+ maxi = (i + 1 > maxi ? i + 1 : maxi);
+ j = 0;
+ do {
+ buf = f->readInt(1);
+ if(buf & 128) {
+ for(t = j; t < j + (buf & 127) && t < 64; t++) {
+ tracks[i][t].command = 0;
+ tracks[i][t].inst = 0;
+ tracks[i][t].note = 0;
+ tracks[i][t].param1 = 0;
+ tracks[i][t].param2 = 0;
+ }
+ j += buf & 127;
+ continue;
+ }
+ tracks[i][j].param2 = buf % 10;
+ tracks[i][j].param1 = buf / 10;
+ buf = f->readInt(1);
+ tracks[i][j].inst = buf >> 4;
+ tracks[i][j].command = buf & 0x0f;
+ buf = f->readInt(1);
+ if(buf >> 4) // fix bug in AMD save routine
+ tracks[i][j].note = ((buf & 14) >> 1) * 12 + (buf >> 4);
+ else
+ tracks[i][j].note = 0;
+ tracks[i][j].inst += (buf & 1) << 4;
+ j++;
+ } while(j<64);
+ }
+ }
+ fp.close(f);
+
+ // convert to protracker replay data
+ bpm = 50; restartpos = 0; flags = Decimal;
+ for(i=0;i<26;i++) { // convert instruments
+ buf = inst[i].data[0];
+ buf2 = inst[i].data[1];
+ inst[i].data[0] = inst[i].data[10];
+ inst[i].data[1] = buf;
+ buf = inst[i].data[2];
+ inst[i].data[2] = inst[i].data[5];
+ buf3 = inst[i].data[3];
+ inst[i].data[3] = buf;
+ buf = inst[i].data[4];
+ inst[i].data[4] = inst[i].data[7];
+ inst[i].data[5] = buf3;
+ buf3 = inst[i].data[6];
+ inst[i].data[6] = inst[i].data[8];
+ inst[i].data[7] = buf;
+ inst[i].data[8] = inst[i].data[9];
+ inst[i].data[9] = buf2;
+ inst[i].data[10] = buf3;
+ for(j=0;j<23;j++) // convert names
+ if(instname[i][j] == '\xff')
+ instname[i][j] = '\x20';
+ }
+ for(i=0;i<maxi;i++) // convert patterns
+ for(j=0;j<64;j++) {
+ tracks[i][j].command = convfx[tracks[i][j].command];
+ // extended command
+ if(tracks[i][j].command == 14) {
+ if(tracks[i][j].param1 == 2) {
+ tracks[i][j].command = 10;
+ tracks[i][j].param1 = tracks[i][j].param2;
+ tracks[i][j].param2 = 0;
+ }
+
+ if(tracks[i][j].param1 == 3) {
+ tracks[i][j].command = 10;
+ tracks[i][j].param1 = 0;
+ }
+ }
+
+ // fix volume
+ if(tracks[i][j].command == 17) {
+ int vol = convvol[tracks[i][j].param1 * 10 + tracks[i][j].param2];
+
+ if(vol > 63) vol = 63;
+ tracks[i][j].param1 = vol / 10;
+ tracks[i][j].param2 = vol % 10;
+ }
+ }
+
+ rewind(0);
+ return true;
+}
+
+float CamdLoader::getrefresh()
+{
+ if(tempo)
+ return (float) (tempo);
+ else
+ return 18.2f;
+}
diff --git a/plugins/adplug/adplug/amd.h b/plugins/adplug/adplug/amd.h
new file mode 100644
index 00000000..daa630b6
--- /dev/null
+++ b/plugins/adplug/adplug/amd.h
@@ -0,0 +1,49 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * amd.h - AMD Loader by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "protrack.h"
+
+class CamdLoader: public CmodPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CamdLoader(Copl *newopl)
+ : CmodPlayer(newopl)
+ { };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ float getrefresh();
+
+ std::string gettype()
+ { return std::string("AMUSIC Adlib Tracker"); };
+ std::string gettitle()
+ { return std::string(songname,0,24); };
+ std::string getauthor()
+ { return std::string(author,0,24); };
+ unsigned int getinstruments()
+ { return 26; };
+ std::string getinstrument(unsigned int n)
+ { return std::string(instname[n],0,23); };
+
+private:
+ char songname[24],author[24],instname[26][23];
+};
diff --git a/plugins/adplug/adplug/analopl.cpp b/plugins/adplug/adplug/analopl.cpp
new file mode 100644
index 00000000..1a7527df
--- /dev/null
+++ b/plugins/adplug/adplug/analopl.cpp
@@ -0,0 +1,67 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * analopl.cpp - Spectrum analyzing hardware OPL, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "analopl.h"
+
+CAnalopl::CAnalopl(unsigned short initport)
+ : CRealopl(initport)
+{
+ for(int i = 0; i < 9; i++) {
+ keyregs[0][i][0] = 0;
+ keyregs[0][i][1] = 0;
+ keyregs[1][i][0] = 0;
+ keyregs[1][i][1] = 0;
+ }
+}
+
+void CAnalopl::write(int reg, int val)
+{
+ if(nowrite) return;
+
+ if(reg >= 0xb0 && reg <= 0xb8) {
+ if(!keyregs[currChip][reg - 0xb0][0] && (val & 32))
+ keyregs[currChip][reg - 0xb0][1] = 1;
+ else
+ keyregs[currChip][reg - 0xb0][1] = 0;
+ keyregs[currChip][reg - 0xb0][0] = val & 32;
+ }
+
+ CRealopl::write(reg, val);
+}
+
+int CAnalopl::getcarriervol(unsigned int v, unsigned int c)
+{
+ return (hardvols[c][op_table[v]+3][0] & 63);
+}
+
+int CAnalopl::getmodulatorvol(unsigned int v, unsigned int c)
+{
+ return (hardvols[c][op_table[v]][0] & 63);
+}
+
+bool CAnalopl::getkeyon(unsigned int v, unsigned int c)
+{
+ if(keyregs[c][v][1]) {
+ keyregs[c][v][1] = 0;
+ return true;
+ } else
+ return false;
+}
diff --git a/plugins/adplug/adplug/analopl.h b/plugins/adplug/adplug/analopl.h
new file mode 100644
index 00000000..314c4247
--- /dev/null
+++ b/plugins/adplug/adplug/analopl.h
@@ -0,0 +1,44 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * analopl.h - Spectrum analyzing hardware OPL, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_ANALOPL
+#define H_ADPLUG_ANALOPL
+
+#include "realopl.h"
+
+class CAnalopl: public CRealopl
+{
+ public:
+ CAnalopl(unsigned short initport = DFL_ADLIBPORT); // initport = OPL2 hardware baseport
+
+ // get carrier volume of adlib voice v on chip c
+ int getcarriervol(unsigned int v, unsigned int c = 0);
+ // get modulator volume of adlib voice v on chip c
+ int getmodulatorvol(unsigned int v, unsigned int c = 0);
+ bool getkeyon(unsigned int v, unsigned int c = 0);
+
+ void write(int reg, int val);
+
+ protected:
+ unsigned char keyregs[2][9][2]; // shadow key register
+};
+
+#endif
diff --git a/plugins/adplug/adplug/bam.cpp b/plugins/adplug/adplug/bam.cpp
new file mode 100644
index 00000000..ea1aca01
--- /dev/null
+++ b/plugins/adplug/adplug/bam.cpp
@@ -0,0 +1,203 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * bam.cpp - Bob's Adlib Music Player, by Simon Peter <dn.tlp@gmx.net>
+ *
+ * NOTES:
+ * In my player, the loop counter is stored with the label. This can be
+ * dangerous for some situations (see below), but there shouldn't be any BAM
+ * files triggering this situation.
+ *
+ * From SourceForge Bug #476088:
+ * -----------------------------
+ * Using just one loop counter for each label, my player can't
+ * handle files that loop twice to the same label (if that's at
+ * all possible with BAM). Imagine the following situation:
+ *
+ * ... [*] ---- [<- *] ---- [<- *] ...
+ * ^ ^ ^ ^ ^ ^ ^
+ * | | | | | | |
+ * +---|----+-----|-----+-----|----+--- normal song data
+ * +----------|-----------|-------- label 1
+ * +-----------+-------- loop points to label 1
+ *
+ * both loop points loop to the same label. Storing the loop
+ * count with the label would cause chaos with the counter,
+ * when the player executes the inner jump.
+ * ------------------
+ * Not to worry. my reference implementation of BAM does not
+ * support the multiple loop situation you describe, and
+ * neither do any BAM-creation programs. Then both loops point
+ * to the same label, the inner loop's counter is just allowed
+ * to clobber the outer loop's counter. No stack is neccisary.
+ */
+
+#include <string.h>
+#include "bam.h"
+
+const unsigned short CbamPlayer::freq[] = {172,182,193,205,217,230,243,258,274,
+290,307,326,345,365,387,410,435,460,489,517,547,580,614,651,1369,1389,1411,
+1434,1459,1484,1513,1541,1571,1604,1638,1675,2393,2413,2435,2458,2483,2508,
+2537,2565,2595,2628,2662,2699,3417,3437,3459,3482,3507,3532,3561,3589,3619,
+3652,3686,3723,4441,4461,4483,4506,4531,4556,4585,4613,4643,4676,4710,4747,
+5465,5485,5507,5530,5555,5580,5609,5637,5667,5700,5734,5771,6489,6509,6531,
+6554,6579,6604,6633,6661,6691,6724,6758,6795,7513,7533,7555,7578,7603,7628,
+7657,7685,7715,7748,7782,7819,7858,7898,7942,7988,8037,8089,8143,8191,8191,
+8191,8191,8191,8191,8191,8191,8191,8191,8191,8191};
+
+CPlayer *CbamPlayer::factory(Copl *newopl)
+{
+ return new CbamPlayer(newopl);
+}
+
+bool CbamPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ char id[4];
+ unsigned int i;
+
+ size = fp.filesize(f) - 4; // filesize minus header
+ f->readString(id, 4);
+ if(strncmp(id,"CBMF",4)) { fp.close(f); return false; }
+
+ song = new unsigned char [size];
+ for(i = 0; i < size; i++) song[i] = f->readInt(1);
+
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+bool CbamPlayer::update()
+{
+ unsigned char cmd,c;
+
+ if(del) {
+ del--;
+ return !songend;
+ }
+
+ if(pos >= size) { // EOF detection
+ pos = 0;
+ songend = true;
+ }
+
+ while(song[pos] < 128) {
+ cmd = song[pos] & 240;
+ c = song[pos] & 15;
+ switch(cmd) {
+ case 0: // stop song
+ pos = 0;
+ songend = true;
+ break;
+ case 16: // start note
+ if(c < 9) {
+ opl->write(0xa0 + c, freq[song[++pos]] & 255);
+ opl->write(0xb0 + c, (freq[song[pos]] >> 8) + 32);
+ } else
+ pos++;
+ pos++;
+ break;
+ case 32: // stop note
+ if(c < 9)
+ opl->write(0xb0 + c, 0);
+ pos++;
+ break;
+ case 48: // define instrument
+ if(c < 9) {
+ opl->write(0x20 + op_table[c],song[pos+1]);
+ opl->write(0x23 + op_table[c],song[pos+2]);
+ opl->write(0x40 + op_table[c],song[pos+3]);
+ opl->write(0x43 + op_table[c],song[pos+4]);
+ opl->write(0x60 + op_table[c],song[pos+5]);
+ opl->write(0x63 + op_table[c],song[pos+6]);
+ opl->write(0x80 + op_table[c],song[pos+7]);
+ opl->write(0x83 + op_table[c],song[pos+8]);
+ opl->write(0xe0 + op_table[c],song[pos+9]);
+ opl->write(0xe3 + op_table[c],song[pos+10]);
+ opl->write(0xc0 + c,song[pos+11]);
+ }
+ pos += 12;
+ break;
+ case 80: // set label
+ label[c].target = ++pos;
+ label[c].defined = true;
+ break;
+ case 96: // jump
+ if(label[c].defined)
+ switch(song[pos+1]) {
+ case 254: // infinite loop
+ if(label[c].defined) {
+ pos = label[c].target;
+ songend = true;
+ break;
+ }
+ // fall through...
+ case 255: // chorus
+ if(!chorus && label[c].defined) {
+ chorus = true;
+ gosub = pos + 2;
+ pos = label[c].target;
+ break;
+ }
+ // fall through...
+ case 0: // end of loop
+ pos += 2;
+ break;
+ default: // finite loop
+ if(!label[c].count) { // loop elapsed
+ label[c].count = 255;
+ pos += 2;
+ break;
+ }
+ if(label[c].count < 255) // loop defined
+ label[c].count--;
+ else // loop undefined
+ label[c].count = song[pos+1] - 1;
+ pos = label[c].target;
+ break;
+ }
+ break;
+ case 112: // end of chorus
+ if(chorus) {
+ pos = gosub;
+ chorus = false;
+ } else
+ pos++;
+ break;
+ default: // reserved command (skip)
+ pos++;
+ break;
+ }
+ }
+ if(song[pos] >= 128) { // wait
+ del = song[pos] - 127;
+ pos++;
+ }
+ return !songend;
+}
+
+void CbamPlayer::rewind(int subsong)
+{
+ int i;
+
+ pos = 0; songend = false; del = 0; gosub = 0; chorus = false;
+ memset(label, 0, sizeof(label)); label[0].defined = true;
+ for(i = 0; i < 16; i++) label[i].count = 255; // 255 = undefined
+ opl->init(); opl->write(1,32);
+}
diff --git a/plugins/adplug/adplug/bam.h b/plugins/adplug/adplug/bam.h
new file mode 100644
index 00000000..53f321b2
--- /dev/null
+++ b/plugins/adplug/adplug/bam.h
@@ -0,0 +1,56 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * bam.h - Bob's Adlib Music Player, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "player.h"
+
+class CbamPlayer: public CPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CbamPlayer(Copl *newopl)
+ : CPlayer(newopl), song(0)
+ { };
+ ~CbamPlayer()
+ { if(song) delete [] song; };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh()
+ { return 25.0f; };
+
+ std::string gettype()
+ { return std::string("Bob's Adlib Music"); };
+
+private:
+ static const unsigned short freq[];
+
+ unsigned char *song, del;
+ unsigned long pos, size, gosub;
+ bool songend, chorus;
+
+ struct {
+ unsigned long target;
+ bool defined;
+ unsigned char count;
+ } label[16];
+};
diff --git a/plugins/adplug/adplug/bmf.cpp b/plugins/adplug/adplug/bmf.cpp
new file mode 100644
index 00000000..df403c86
--- /dev/null
+++ b/plugins/adplug/adplug/bmf.cpp
@@ -0,0 +1,597 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003, 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * [xad] BMF player, by Riven the Mage <riven@ok.ru>
+ */
+
+/*
+ - discovery -
+
+ file(s) : GAMESNET.COM
+ type : GamesNet advertising intro
+ tune : by (?)The Brain [Razor 1911]
+ player : ver.0.9b by Hammer
+
+ file(s) : 2FAST4U.COM
+ type : Ford Knox BBStro
+ tune : by The Brain [Razor 1911]
+ player : ver.1.1 by ?
+ comment : in original player at 9th channel the feedback adlib register is not C8 but C6.
+
+ file(s) : DATURA.COM
+ type : Datura BBStro
+ tune : by The Brain [Razor 1911]
+ player : ver.1.2 by ?
+ comment : inaccurate replaying, because constant outport; in original player it can be 380 or 382.
+*/
+
+#include <string.h>
+#include "bmf.h"
+#include "debug.h"
+
+const unsigned char CxadbmfPlayer::bmf_adlib_registers[117] =
+{
+ 0x20, 0x23, 0x40, 0x43, 0x60, 0x63, 0x80, 0x83, 0xA0, 0xB0, 0xC0, 0xE0, 0xE3,
+ 0x21, 0x24, 0x41, 0x44, 0x61, 0x64, 0x81, 0x84, 0xA1, 0xB1, 0xC1, 0xE1, 0xE4,
+ 0x22, 0x25, 0x42, 0x45, 0x62, 0x65, 0x82, 0x85, 0xA2, 0xB2, 0xC2, 0xE2, 0xE5,
+ 0x28, 0x2B, 0x48, 0x4B, 0x68, 0x6B, 0x88, 0x8B, 0xA3, 0xB3, 0xC3, 0xE8, 0xEB,
+ 0x29, 0x2C, 0x49, 0x4C, 0x69, 0x6C, 0x89, 0x8C, 0xA4, 0xB4, 0xC4, 0xE9, 0xEC,
+ 0x2A, 0x2D, 0x4A, 0x4D, 0x6A, 0x6D, 0x8A, 0x8D, 0xA5, 0xB5, 0xC5, 0xEA, 0xED,
+ 0x30, 0x33, 0x50, 0x53, 0x70, 0x73, 0x90, 0x93, 0xA6, 0xB6, 0xC6, 0xF0, 0xF3,
+ 0x31, 0x34, 0x51, 0x54, 0x71, 0x74, 0x91, 0x94, 0xA7, 0xB7, 0xC7, 0xF1, 0xF4,
+ 0x32, 0x35, 0x52, 0x55, 0x72, 0x75, 0x92, 0x95, 0xA8, 0xB8, 0xC8, 0xF2, 0xF5
+};
+
+const unsigned short CxadbmfPlayer::bmf_notes[12] =
+{
+ 0x157, 0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5, 0x202, 0x220, 0x241, 0x263, 0x287
+};
+
+/* for 1.1 */
+const unsigned short CxadbmfPlayer::bmf_notes_2[12] =
+{
+ 0x159, 0x16D, 0x183, 0x19A, 0x1B2, 0x1CC, 0x1E8, 0x205, 0x223, 0x244, 0x267, 0x28B
+};
+
+const unsigned char CxadbmfPlayer::bmf_default_instrument[13] =
+{
+ 0x01, 0x01, 0x3F, 0x3F, 0x00, 0x00, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+CPlayer *CxadbmfPlayer::factory(Copl *newopl)
+{
+ return new CxadbmfPlayer(newopl);
+}
+
+bool CxadbmfPlayer::xadplayer_load()
+{
+ unsigned short ptr = 0;
+ int i;
+
+ if(xad.fmt != BMF)
+ return false;
+
+#ifdef DEBUG
+ AdPlug_LogWrite("\nbmf_load():\n\n");
+#endif
+ if (!strncmp((char *)&tune[0],"BMF1.2",6))
+ {
+ bmf.version = BMF1_2;
+ bmf.timer = 70.0f;
+ }
+ else if (!strncmp((char *)&tune[0],"BMF1.1",6))
+ {
+ bmf.version = BMF1_1;
+ bmf.timer = 60.0f;
+ }
+ else
+ {
+ bmf.version = BMF0_9B;
+ bmf.timer = 18.2f;
+ }
+
+ // copy title & author
+ if (bmf.version > BMF0_9B)
+ {
+ ptr = 6;
+
+ strncpy(bmf.title,(char *)&tune[ptr],36);
+
+ while (tune[ptr]) { ptr++; }
+ ptr++;
+
+ strncpy(bmf.author,(char *)&tune[ptr],36);
+
+ while (tune[ptr]) { ptr++; }
+ ptr++;
+ }
+ else
+ {
+ strncpy(bmf.title,xad.title,36);
+ strncpy(bmf.author,xad.author,36);
+ }
+
+ // speed
+ if (bmf.version > BMF0_9B)
+ bmf.speed = tune[ptr++];
+ else
+ bmf.speed = ((tune[ptr++] << 8) / 3) >> 8; // strange, yeh ?
+
+ // load instruments
+ if (bmf.version > BMF0_9B)
+ {
+ unsigned long iflags = (tune[ptr] << 24) | (tune[ptr+1] << 16) | (tune[ptr+2] << 8) | tune[ptr+3];
+ ptr+=4;
+
+ for(i=0;i<32;i++)
+ if (iflags & (1 << (31-i)))
+ {
+ strcpy(bmf.instruments[i].name, (char *)&tune[ptr]);
+ memcpy(bmf.instruments[i].data, &tune[ptr+11], 13);
+ ptr += 24;
+ }
+ else
+ {
+ bmf.instruments[i].name[0] = 0;
+
+ if (bmf.version == BMF1_1)
+ for(int j=0;j<13;j++)
+ bmf.instruments[i].data[j] = bmf_default_instrument[j];
+ else
+ for(int j=0;j<13;j++)
+ bmf.instruments[i].data[j] = 0;
+ }
+ }
+ else
+ {
+ ptr = 6;
+
+ for(i=0;i<32;i++)
+ {
+ bmf.instruments[i].name[0] = 0;
+ memcpy(bmf.instruments[tune[ptr]].data, &tune[ptr+2],13); // bug no.1 (no instrument-table-end detection)
+ ptr+=15;
+ }
+ }
+
+ // load streams
+ if (bmf.version > BMF0_9B)
+ {
+ unsigned long sflags = (tune[ptr] << 24) | (tune[ptr+1] << 16) | (tune[ptr+2] << 8) | tune[ptr+3];
+ ptr+=4;
+
+ for(i=0;i<9;i++)
+ if (sflags & (1 << (31-i)))
+ ptr+=__bmf_convert_stream(&tune[ptr],i);
+ else
+ bmf.streams[i][0].cmd = 0xFF;
+ }
+ else
+ {
+ for(i=0;i<tune[5];i++)
+ ptr+=__bmf_convert_stream(&tune[ptr],i);
+
+ for(i=tune[5];i<9;i++)
+ bmf.streams[i][0].cmd = 0xFF;
+ }
+
+ return true;
+}
+
+void CxadbmfPlayer::xadplayer_rewind(int subsong)
+{
+ int i,j;
+
+ for(i=0; i<9; i++)
+ {
+ bmf.channel[i].stream_position = 0;
+ bmf.channel[i].delay = 0;
+ bmf.channel[i].loop_position = 0;
+ bmf.channel[i].loop_counter = 0;
+ }
+
+ plr.speed = bmf.speed;
+#ifdef DEBUG
+ AdPlug_LogWrite("speed: %x\n",plr.speed);
+#endif
+
+ bmf.active_streams = 9;
+
+ // OPL initialization
+ if (bmf.version > BMF0_9B)
+ {
+ opl_write(0x01, 0x20);
+
+ /* 1.1 */
+ if (bmf.version == BMF1_1)
+ for(i=0;i<9;i++)
+ for(j=0;j<13;j++)
+ opl_write(bmf_adlib_registers[13*i+j], bmf_default_instrument[j]);
+ /* 1.2 */
+ else if (bmf.version == BMF1_2)
+ for(i=0x20; i<0x100; i++)
+ opl_write(i,0xFF); // very interesting, really!
+ }
+
+ /* ALL */
+
+ opl_write(0x08, 0x00);
+ opl_write(0xBD, 0xC0);
+}
+
+void CxadbmfPlayer::xadplayer_update()
+{
+ for(int i=0;i<9;i++)
+ if (bmf.channel[i].stream_position != 0xFFFF)
+ if (bmf.channel[i].delay)
+ bmf.channel[i].delay--;
+ else
+ {
+#ifdef DEBUG
+ AdPlug_LogWrite("channel %02X:\n", i);
+#endif
+ bmf_event event;
+
+ // process so-called cross-events
+ while (true)
+ {
+ memcpy(&event, &bmf.streams[i][bmf.channel[i].stream_position], sizeof(bmf_event));
+#ifdef DEBUG
+ AdPlug_LogWrite("%02X %02X %02X %02X %02X %02X\n",
+ event.note,event.delay,event.volume,event.instrument,
+ event.cmd,event.cmd_data);
+#endif
+
+ if (event.cmd == 0xFF)
+ {
+ bmf.channel[i].stream_position = 0xFFFF;
+ bmf.active_streams--;
+ break;
+ }
+ else if (event.cmd == 0xFE)
+ {
+ bmf.channel[i].loop_position = bmf.channel[i].stream_position+1;
+ bmf.channel[i].loop_counter = event.cmd_data;
+ }
+ else if (event.cmd == 0xFD)
+ {
+ if (bmf.channel[i].loop_counter)
+ {
+ bmf.channel[i].stream_position = bmf.channel[i].loop_position-1;
+ bmf.channel[i].loop_counter--;
+ }
+ }
+ else
+ break;
+
+ bmf.channel[i].stream_position++;
+ } // while (true)
+
+ // process normal event
+ unsigned short pos = bmf.channel[i].stream_position;
+
+ if (pos != 0xFFFF)
+ {
+ bmf.channel[i].delay = bmf.streams[i][pos].delay;
+
+ // command ?
+ if (bmf.streams[i][pos].cmd)
+ {
+ unsigned char cmd = bmf.streams[i][pos].cmd;
+
+ // 0x01: Set Modulator Volume
+ if (cmd == 0x01)
+ {
+ unsigned char reg = bmf_adlib_registers[13*i+2];
+
+ opl_write(reg, (adlib[reg] | 0x3F) - bmf.streams[i][pos].cmd_data);
+ }
+ // 0x10: Set Speed
+ else if (cmd == 0x10)
+ {
+ plr.speed = bmf.streams[i][pos].cmd_data;
+ plr.speed_counter = plr.speed;
+ }
+ } // if (bmf.streams[i][pos].cmd)
+
+ // instrument ?
+ if (bmf.streams[i][pos].instrument)
+ {
+ unsigned char ins = bmf.streams[i][pos].instrument-1;
+
+ if (bmf.version != BMF1_1)
+ opl_write(0xB0+i, adlib[0xB0+i] & 0xDF);
+
+ for(int j=0;j<13;j++)
+ opl_write(bmf_adlib_registers[i*13+j], bmf.instruments[ins].data[j]);
+ } // if (bmf.streams[i][pos].instrument)
+
+ // volume ?
+ if (bmf.streams[i][pos].volume)
+ {
+ unsigned char vol = bmf.streams[i][pos].volume-1;
+ unsigned char reg = bmf_adlib_registers[13*i+3];
+
+ opl_write(reg, (adlib[reg] | 0x3F) - vol);
+ } // if (bmf.streams[i][pos].volume)
+
+ // note ?
+ if (bmf.streams[i][pos].note)
+ {
+ unsigned short note = bmf.streams[i][pos].note;
+ unsigned short freq = 0;
+
+ // mute channel
+ opl_write(0xB0+i, adlib[0xB0+i] & 0xDF);
+
+ // get frequency
+ if (bmf.version == BMF1_1)
+ {
+ if (note <= 0x60)
+ freq = bmf_notes_2[--note % 12];
+ }
+ else
+ {
+ if (note != 0x7F)
+ freq = bmf_notes[--note % 12];
+ }
+
+ // play note
+ if (freq)
+ {
+ opl_write(0xB0+i, (freq >> 8) | ((note / 12) << 2) | 0x20);
+ opl_write(0xA0+i, freq & 0xFF);
+ }
+ } // if (bmf.streams[i][pos].note)
+
+ bmf.channel[i].stream_position++;
+ } // if (pos != 0xFFFF)
+
+ } // if (!bmf.channel[i].delay)
+
+ // is module loop ?
+ if (!bmf.active_streams)
+ {
+ for(int j=0;j<9;j++)
+ bmf.channel[j].stream_position = 0;
+
+ bmf.active_streams = 9;
+
+ plr.looping = 1;
+ }
+}
+
+float CxadbmfPlayer::xadplayer_getrefresh()
+{
+ return bmf.timer;
+}
+
+std::string CxadbmfPlayer::xadplayer_gettype()
+{
+ return std::string("xad: BMF Adlib Tracker");
+}
+
+std::string CxadbmfPlayer::xadplayer_gettitle()
+{
+ return std::string(bmf.title);
+}
+
+std::string CxadbmfPlayer::xadplayer_getauthor()
+{
+ return std::string(bmf.author);
+}
+
+unsigned int CxadbmfPlayer::xadplayer_getinstruments()
+{
+ return 32;
+}
+
+std::string CxadbmfPlayer::xadplayer_getinstrument(unsigned int i)
+{
+ return std::string(bmf.instruments[i].name);
+}
+
+/* -------- Internal Functions ---------------------------- */
+
+int CxadbmfPlayer::__bmf_convert_stream(unsigned char *stream, int channel)
+{
+#ifdef DEBUG
+ AdPlug_LogWrite("channel %02X (note,delay,volume,instrument,command,command_data):\n",channel);
+ unsigned char *last = stream;
+#endif
+ unsigned char *stream_start = stream;
+
+ int pos = 0;
+
+ while (true)
+ {
+ memset(&bmf.streams[channel][pos], 0, sizeof(bmf_event));
+
+ bool is_cmd = false;
+
+ if (*stream == 0xFE)
+ {
+ // 0xFE -> 0xFF: End of Stream
+ bmf.streams[channel][pos].cmd = 0xFF;
+
+ stream++;
+
+ break;
+ }
+ else if (*stream == 0xFC)
+ {
+ // 0xFC -> 0xFE xx: Save Loop Position
+ bmf.streams[channel][pos].cmd = 0xFE;
+ bmf.streams[channel][pos].cmd_data = (*(stream+1) & ((bmf.version == BMF0_9B) ? 0x7F : 0x3F)) - 1;
+
+ stream+=2;
+ }
+ else if (*stream == 0x7D)
+ {
+ // 0x7D -> 0xFD: Loop Saved Position
+ bmf.streams[channel][pos].cmd = 0xFD;
+
+ stream++;
+ }
+ else
+ {
+ if (*stream & 0x80)
+ {
+ if (*(stream+1) & 0x80)
+ {
+ if (*(stream+1) & 0x40)
+ {
+ // byte0: 1aaaaaaa = NOTE
+ bmf.streams[channel][pos].note = *stream & 0x7F;
+ // byte1: 11bbbbbb = DELAY
+ bmf.streams[channel][pos].delay = *(stream+1) & 0x3F;
+ // byte2: cccccccc = COMMAND
+
+ stream+=2;
+
+ is_cmd = true;
+ }
+ else
+ {
+ // byte0: 1aaaaaaa = NOTE
+ bmf.streams[channel][pos].note = *stream & 0x7F;
+ // byte1: 11bbbbbb = DELAY
+ bmf.streams[channel][pos].delay = *(stream+1) & 0x3F;
+
+ stream+=2;
+ } // if (*(stream+1) & 0x40)
+ }
+ else
+ {
+ // byte0: 1aaaaaaa = NOTE
+ bmf.streams[channel][pos].note = *stream & 0x7F;
+ // byte1: 0bbbbbbb = COMMAND
+
+ stream++;
+
+ is_cmd = true;
+ } // if (*(stream+1) & 0x80)
+ }
+ else
+ {
+ // byte0: 0aaaaaaa = NOTE
+ bmf.streams[channel][pos].note = *stream & 0x7F;
+
+ stream++;
+ } // if (*stream & 0x80)
+ } // if (*stream == 0xFE)
+
+ // is command ?
+ if (is_cmd)
+ {
+
+ /* ALL */
+
+ if ((0x20 <= *stream) && (*stream <= 0x3F))
+ {
+ // 0x20 or higher; 0x3F or lower: Set Instrument
+ bmf.streams[channel][pos].instrument = *stream - 0x20 + 1;
+
+ stream++;
+ }
+ else if (0x40 <= *stream)
+ {
+ // 0x40 or higher: Set Volume
+ bmf.streams[channel][pos].volume = *stream - 0x40 + 1;
+
+ stream++;
+ }
+ else
+ {
+
+ /* 0.9b */
+
+ if (bmf.version == BMF0_9B)
+ if (*stream < 0x20)
+ {
+ // 0x1F or lower: ?
+ stream++;
+ }
+
+ /* 1.2 */
+
+ if (bmf.version == BMF1_2)
+ if (*stream == 0x01)
+ {
+ // 0x01: Set Modulator Volume -> 0x01
+ bmf.streams[channel][pos].cmd = 0x01;
+ bmf.streams[channel][pos].cmd_data = *(stream+1);
+
+ stream+=2;
+ }
+ else if (*stream == 0x02)
+ {
+ // 0x02: ?
+ stream+=2;
+ }
+ else if (*stream == 0x03)
+ {
+ // 0x03: ?
+ stream+=2;
+ }
+ else if (*stream == 0x04)
+ {
+ // 0x04: Set Speed -> 0x10
+ bmf.streams[channel][pos].cmd = 0x10;
+ bmf.streams[channel][pos].cmd_data = *(stream+1);
+
+ stream+=2;
+ }
+ else if (*stream == 0x05)
+ {
+ // 0x05: Set Carrier Volume (port 380)
+ bmf.streams[channel][pos].volume = *(stream+1) + 1;
+
+ stream+=2;
+ }
+ else if (*stream == 0x06)
+ {
+ // 0x06: Set Carrier Volume (port 382)
+ bmf.streams[channel][pos].volume = *(stream+1) + 1;
+
+ stream+=2;
+ } // if (bmf.version == BMF1_2)
+
+ } // if ((0x20 <= *stream) && (*stream <= 0x3F))
+
+ } // if (is_cmd)
+
+#ifdef DEBUG
+ AdPlug_LogWrite("%02X %02X %02X %02X %02X %02X <---- ",
+ bmf.streams[channel][pos].note,
+ bmf.streams[channel][pos].delay,
+ bmf.streams[channel][pos].volume,
+ bmf.streams[channel][pos].instrument,
+ bmf.streams[channel][pos].cmd,
+ bmf.streams[channel][pos].cmd_data
+ );
+ for(int zz=0;zz<(stream-last);zz++)
+ AdPlug_LogWrite("%02X ",last[zz]);
+ AdPlug_LogWrite("\n");
+ last=stream;
+#endif
+ pos++;
+ } // while (true)
+
+ return (stream - stream_start);
+}
diff --git a/plugins/adplug/adplug/bmf.h b/plugins/adplug/adplug/bmf.h
new file mode 100644
index 00000000..12cbdfd3
--- /dev/null
+++ b/plugins/adplug/adplug/bmf.h
@@ -0,0 +1,91 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * [xad] BMF player, by Riven the Mage <riven@ok.ru>
+ */
+
+#include "xad.h"
+
+class CxadbmfPlayer: public CxadPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CxadbmfPlayer(Copl *newopl): CxadPlayer(newopl)
+ { };
+ ~CxadbmfPlayer()
+ { };
+
+protected:
+ enum { BMF0_9B, BMF1_1, BMF1_2 };
+ //
+ struct bmf_event
+ {
+ unsigned char note;
+ unsigned char delay;
+ unsigned char volume;
+ unsigned char instrument;
+ unsigned char cmd;
+ unsigned char cmd_data;
+ };
+
+ struct
+ {
+ unsigned char version;
+ char title[36];
+ char author[36];
+ float timer;
+ unsigned char speed;
+
+ struct
+ {
+ char name[11];
+ unsigned char data[13];
+ } instruments[32];
+
+ bmf_event streams[9][1024];
+
+ int active_streams;
+
+ struct
+ {
+ unsigned short stream_position;
+ unsigned char delay;
+ unsigned short loop_position;
+ unsigned char loop_counter;
+ } channel[9];
+ } bmf;
+ //
+ bool xadplayer_load();
+ void xadplayer_rewind(int subsong);
+ void xadplayer_update();
+ float xadplayer_getrefresh();
+ std::string xadplayer_gettype();
+ std::string xadplayer_gettitle();
+ std::string xadplayer_getauthor();
+ std::string xadplayer_getinstrument(unsigned int i);
+ unsigned int xadplayer_getinstruments();
+ //
+private:
+ static const unsigned char bmf_adlib_registers[117];
+ static const unsigned short bmf_notes[12];
+ static const unsigned short bmf_notes_2[12];
+ static const unsigned char bmf_default_instrument[13];
+
+ int __bmf_convert_stream(unsigned char *stream, int channel);
+};
diff --git a/plugins/adplug/adplug/cff.cpp b/plugins/adplug/adplug/cff.cpp
new file mode 100644
index 00000000..37197fd9
--- /dev/null
+++ b/plugins/adplug/adplug/cff.cpp
@@ -0,0 +1,508 @@
+/*
+ AdPlug - Replayer for many OPL2/OPL3 audio file formats.
+ Copyright (C) 1999 - 2006 Simon Peter <dn.tlp@gmx.net>, et al.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ cff.cpp - BoomTracker loader by Riven the Mage <riven@ok.ru>
+*/
+/*
+ NOTE: Conversion of slides is not 100% accurate. Original volume slides
+ have effect on carrier volume only. Also, original arpeggio, frequency & volume
+ slides use previous effect data instead of current.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "cff.h"
+
+/* -------- Public Methods -------------------------------- */
+
+CPlayer *CcffLoader::factory(Copl *newopl)
+{
+ return new CcffLoader(newopl);
+}
+
+bool CcffLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ const unsigned char conv_inst[11] = { 2,1,10,9,4,3,6,5,0,8,7 };
+ const unsigned short conv_note[12] = { 0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5, 0x202, 0x220, 0x241, 0x263, 0x287, 0x2AE };
+
+ int i,j,k,t=0;
+
+ // '<CUD-FM-File>' - signed ?
+ f->readString(header.id, 16);
+ header.version = f->readInt(1); header.size = f->readInt(2);
+ header.packed = f->readInt(1); f->readString((char *)header.reserved, 12);
+ if (memcmp(header.id,"<CUD-FM-File>""\x1A\xDE\xE0",16))
+ { fp.close(f); return false; }
+
+ unsigned char *module = new unsigned char [0x10000];
+
+ // packed ?
+ if (header.packed)
+ {
+ cff_unpacker *unpacker = new cff_unpacker;
+
+ unsigned char *packed_module = new unsigned char [header.size + 4];
+
+ memset(packed_module,0,header.size + 4);
+
+ f->readString((char *)packed_module, header.size);
+ fp.close(f);
+
+ if (!unpacker->unpack(packed_module,module))
+ {
+ delete unpacker;
+ delete packed_module;
+ delete module;
+ return false;
+ }
+
+ delete unpacker;
+ delete packed_module;
+
+ if (memcmp(&module[0x5E1],"CUD-FM-File - SEND A POSTCARD -",31))
+ {
+ delete module;
+ return false;
+ }
+ }
+ else
+ {
+ f->readString((char *)module, header.size);
+ fp.close(f);
+ }
+
+ // init CmodPlayer
+ realloc_instruments(47);
+ realloc_order(64);
+ realloc_patterns(36,64,9);
+ init_notetable(conv_note);
+ init_trackord();
+
+ // load instruments
+ for (i=0;i<47;i++)
+ {
+ memcpy(&instruments[i],&module[i*32],sizeof(cff_instrument));
+
+ for (j=0;j<11;j++)
+ inst[i].data[conv_inst[j]] = instruments[i].data[j];
+
+ instruments[i].name[20] = 0;
+ }
+
+ // number of patterns
+ nop = module[0x5E0];
+
+ // load title & author
+ memcpy(song_title,&module[0x614],20);
+ memcpy(song_author,&module[0x600],20);
+
+ // load order
+ memcpy(order,&module[0x628],64);
+
+ // load tracks
+ for (i=0;i<nop;i++)
+ {
+ unsigned char old_event_byte2[9];
+
+ memset(old_event_byte2,0,9);
+
+ for (j=0;j<9;j++)
+ {
+ for (k=0;k<64;k++)
+ {
+ cff_event *event = (cff_event *)&module[0x669 + ((i*64+k)*9+j)*3];
+
+ // convert note
+ if (event->byte0 == 0x6D)
+ tracks[t][k].note = 127;
+ else
+ if (event->byte0)
+ tracks[t][k].note = event->byte0;
+
+ if (event->byte2)
+ old_event_byte2[j] = event->byte2;
+
+ // convert effect
+ switch (event->byte1)
+ {
+ case 'I': // set instrument
+ tracks[t][k].inst = event->byte2 + 1;
+ tracks[t][k].param1 = tracks[t][k].param2 = 0;
+ break;
+
+ case 'H': // set tempo
+ tracks[t][k].command = 7;
+ if (event->byte2 < 16)
+ {
+ tracks[t][k].param1 = 0x07;
+ tracks[t][k].param2 = 0x0D;
+ }
+ break;
+
+ case 'A': // set speed
+ tracks[t][k].command = 19;
+ tracks[t][k].param1 = event->byte2 >> 4;
+ tracks[t][k].param2 = event->byte2 & 15;
+ break;
+
+ case 'L': // pattern break
+ tracks[t][k].command = 13;
+ tracks[t][k].param1 = event->byte2 >> 4;
+ tracks[t][k].param2 = event->byte2 & 15;
+ break;
+
+ case 'K': // order jump
+ tracks[t][k].command = 11;
+ tracks[t][k].param1 = event->byte2 >> 4;
+ tracks[t][k].param2 = event->byte2 & 15;
+ break;
+
+ case 'M': // set vibrato/tremolo
+ tracks[t][k].command = 27;
+ tracks[t][k].param1 = event->byte2 >> 4;
+ tracks[t][k].param2 = event->byte2 & 15;
+ break;
+
+ case 'C': // set modulator volume
+ tracks[t][k].command = 21;
+ tracks[t][k].param1 = (0x3F - event->byte2) >> 4;
+ tracks[t][k].param2 = (0x3F - event->byte2) & 15;
+ break;
+
+ case 'G': // set carrier volume
+ tracks[t][k].command = 22;
+ tracks[t][k].param1 = (0x3F - event->byte2) >> 4;
+ tracks[t][k].param2 = (0x3F - event->byte2) & 15;
+ break;
+
+ case 'B': // set carrier waveform
+ tracks[t][k].command = 25;
+ tracks[t][k].param1 = event->byte2;
+ tracks[t][k].param2 = 0x0F;
+ break;
+
+ case 'E': // fine frequency slide down
+ tracks[t][k].command = 24;
+ tracks[t][k].param1 = old_event_byte2[j] >> 4;
+ tracks[t][k].param2 = old_event_byte2[j] & 15;
+ break;
+
+ case 'F': // fine frequency slide up
+ tracks[t][k].command = 23;
+ tracks[t][k].param1 = old_event_byte2[j] >> 4;
+ tracks[t][k].param2 = old_event_byte2[j] & 15;
+ break;
+
+ case 'D': // fine volume slide
+ tracks[t][k].command = 14;
+ if (old_event_byte2[j] & 15)
+ {
+ // slide down
+ tracks[t][k].param1 = 5;
+ tracks[t][k].param2 = old_event_byte2[j] & 15;
+ }
+ else
+ {
+ // slide up
+ tracks[t][k].param1 = 4;
+ tracks[t][k].param2 = old_event_byte2[j] >> 4;
+ }
+ break;
+
+ case 'J': // arpeggio
+ tracks[t][k].param1 = old_event_byte2[j] >> 4;
+ tracks[t][k].param2 = old_event_byte2[j] & 15;
+ break;
+ }
+ }
+
+ t++;
+ }
+ }
+
+ delete [] module;
+
+ // order loop
+ restartpos = 0;
+
+ // order length
+ for (i=0;i<64;i++)
+ {
+ if (order[i] >= 0x80)
+ {
+ length = i;
+ break;
+ }
+ }
+
+ // default tempo
+ bpm = 0x7D;
+
+ rewind(0);
+
+ return true;
+}
+
+void CcffLoader::rewind(int subsong)
+{
+ CmodPlayer::rewind(subsong);
+
+ // default instruments
+ for (int i=0;i<9;i++)
+ {
+ channel[i].inst = i;
+
+ channel[i].vol1 = 63 - (inst[i].data[10] & 63);
+ channel[i].vol2 = 63 - (inst[i].data[9] & 63);
+ }
+}
+
+std::string CcffLoader::gettype()
+{
+ if (header.packed)
+ return std::string("BoomTracker 4, packed");
+ else
+ return std::string("BoomTracker 4");
+}
+
+std::string CcffLoader::gettitle()
+{
+ return std::string(song_title,20);
+}
+
+std::string CcffLoader::getauthor()
+{
+ return std::string(song_author,20);
+}
+
+std::string CcffLoader::getinstrument(unsigned int n)
+{
+ return std::string(instruments[n].name);
+}
+
+unsigned int CcffLoader::getinstruments()
+{
+ return 47;
+}
+
+/* -------- Private Methods ------------------------------- */
+
+#ifdef _WIN32
+#pragma warning(disable:4244)
+#pragma warning(disable:4018)
+#endif
+
+/*
+ Lempel-Ziv-Tyr ;-)
+*/
+long CcffLoader::cff_unpacker::unpack(unsigned char *ibuf, unsigned char *obuf)
+{
+ if (memcmp(ibuf,"YsComp""\x07""CUD1997""\x1A\x04",16))
+ return 0;
+
+ input = ibuf + 16;
+ output = obuf;
+
+ output_length = 0;
+
+ heap = (unsigned char *)malloc(0x10000);
+ dictionary = (unsigned char **)malloc(sizeof(unsigned char *)*0x8000);
+
+ memset(heap,0,0x10000);
+ memset(dictionary,0,0x8000);
+
+ cleanup();
+ if(!startup())
+ goto out;
+
+ // LZW
+ while (1)
+ {
+ new_code = get_code();
+
+ // 0x00: end of data
+ if (new_code == 0)
+ break;
+
+ // 0x01: end of block
+ if (new_code == 1)
+ {
+ cleanup();
+ if(!startup())
+ goto out;
+
+ continue;
+ }
+
+ // 0x02: expand code length
+ if (new_code == 2)
+ {
+ code_length++;
+
+ continue;
+ }
+
+ // 0x03: RLE
+ if (new_code == 3)
+ {
+ unsigned char old_code_length = code_length;
+
+ code_length = 2;
+
+ unsigned char repeat_length = get_code() + 1;
+
+ code_length = 4 << get_code();
+
+ unsigned long repeat_counter = get_code();
+
+ if(output_length + repeat_counter * repeat_length > 0x10000) {
+ output_length = 0;
+ goto out;
+ }
+
+ for (unsigned int i=0;i<repeat_counter*repeat_length;i++)
+ output[output_length++] = output[output_length - repeat_length];
+
+ code_length = old_code_length;
+
+ if(!startup())
+ goto out;
+
+ continue;
+ }
+
+ if (new_code >= (0x104 + dictionary_length))
+ {
+ // dictionary <- old.code.string + old.code.char
+ the_string[++the_string[0]] = the_string[1];
+ }
+ else
+ {
+ // dictionary <- old.code.string + new.code.char
+ unsigned char temp_string[256];
+
+ translate_code(new_code,temp_string);
+
+ the_string[++the_string[0]] = temp_string[1];
+ }
+
+ expand_dictionary(the_string);
+
+ // output <- new.code.string
+ translate_code(new_code,the_string);
+
+ if(output_length + the_string[0] > 0x10000) {
+ output_length = 0;
+ goto out;
+ }
+
+ for (int i=0;i<the_string[0];i++)
+ output[output_length++] = the_string[i+1];
+
+ old_code = new_code;
+ }
+
+ out:
+ free(heap);
+ free(dictionary);
+ return output_length;
+}
+
+unsigned long CcffLoader::cff_unpacker::get_code()
+{
+ unsigned long code;
+
+ while (bits_left < code_length)
+ {
+ bits_buffer |= ((*input++) << bits_left);
+ bits_left += 8;
+ }
+
+ code = bits_buffer & ((1 << code_length) - 1);
+
+ bits_buffer >>= code_length;
+ bits_left -= code_length;
+
+ return code;
+}
+
+void CcffLoader::cff_unpacker::translate_code(unsigned long code, unsigned char *string)
+{
+ unsigned char translated_string[256];
+
+ if (code >= 0x104)
+ {
+ memcpy(translated_string,dictionary[code - 0x104],(*(dictionary[code - 0x104])) + 1);
+ }
+ else
+ {
+ translated_string[0] = 1;
+ translated_string[1] = (code - 4) & 0xFF;
+ }
+
+ memcpy(string,translated_string,256);
+}
+
+void CcffLoader::cff_unpacker::cleanup()
+{
+ code_length = 9;
+
+ bits_buffer = 0;
+ bits_left = 0;
+
+ heap_length = 0;
+ dictionary_length = 0;
+}
+
+int CcffLoader::cff_unpacker::startup()
+{
+ old_code = get_code();
+
+ translate_code(old_code,the_string);
+
+ if(output_length + the_string[0] > 0x10000) {
+ output_length = 0;
+ return 0;
+ }
+
+ for (int i=0;i<the_string[0];i++)
+ output[output_length++] = the_string[i+1];
+
+ return 1;
+}
+
+void CcffLoader::cff_unpacker::expand_dictionary(unsigned char *string)
+{
+ if (string[0] >= 0xF0)
+ return;
+
+ memcpy(&heap[heap_length],string,string[0] + 1);
+
+ dictionary[dictionary_length] = &heap[heap_length];
+
+ dictionary_length++;
+
+ heap_length += (string[0] + 1);
+}
+
+#ifdef _WIN32
+#pragma warning(default:4244)
+#pragma warning(default:4018)
+#endif
diff --git a/plugins/adplug/adplug/cff.h b/plugins/adplug/adplug/cff.h
new file mode 100644
index 00000000..04ead8bb
--- /dev/null
+++ b/plugins/adplug/adplug/cff.h
@@ -0,0 +1,103 @@
+/*
+ AdPlug - Replayer for many OPL2/OPL3 audio file formats.
+ Copyright (C) 1999 - 2006 Simon Peter <dn.tlp@gmx.net>, et al.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ cff.h - BoomTracker loader by Riven the Mage <riven@ok.ru>
+*/
+
+#include "protrack.h"
+
+class CcffLoader: public CmodPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ CcffLoader(Copl *newopl) : CmodPlayer(newopl) { };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ void rewind(int subsong);
+
+ std::string gettype();
+ std::string gettitle();
+ std::string getauthor();
+ std::string getinstrument(unsigned int n);
+ unsigned int getinstruments();
+
+ private:
+
+ class cff_unpacker
+ {
+ public:
+
+ long unpack(unsigned char *ibuf, unsigned char *obuf);
+
+ private:
+
+ unsigned long get_code();
+ void translate_code(unsigned long code, unsigned char *string);
+
+ void cleanup();
+ int startup();
+
+ void expand_dictionary(unsigned char *string);
+
+ unsigned char *input;
+ unsigned char *output;
+
+ long output_length;
+
+ unsigned char code_length;
+
+ unsigned long bits_buffer;
+ unsigned int bits_left;
+
+ unsigned char *heap;
+ unsigned char **dictionary;
+
+ unsigned int heap_length;
+ unsigned int dictionary_length;
+
+ unsigned long old_code,new_code;
+
+ unsigned char the_string[256];
+ };
+
+ struct cff_header
+ {
+ char id[16];
+ unsigned char version;
+ unsigned short size;
+ unsigned char packed;
+ unsigned char reserved[12];
+ } header;
+
+ struct cff_instrument
+ {
+ unsigned char data[12];
+ char name[21];
+ } instruments[47];
+
+ char song_title[20];
+ char song_author[20];
+
+ struct cff_event
+ {
+ unsigned char byte0;
+ unsigned char byte1;
+ unsigned char byte2;
+ };
+};
diff --git a/plugins/adplug/adplug/d00.cpp b/plugins/adplug/adplug/d00.cpp
new file mode 100644
index 00000000..7aa042d1
--- /dev/null
+++ b/plugins/adplug/adplug/d00.cpp
@@ -0,0 +1,545 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * d00.c - D00 Player by Simon Peter <dn.tlp@gmx.net>
+ *
+ * NOTES:
+ * Sorry for the goto's, but the code looks so much nicer now.
+ * I tried it with while loops but it was just a mess. If you
+ * can come up with a nicer solution, just tell me.
+ *
+ * BUGS:
+ * Hard restart SR is sometimes wrong
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <inttypes.h>
+
+#include "debug.h"
+#include "d00.h"
+
+#define HIBYTE(val) (val >> 8)
+#define LOBYTE(val) (val & 0xff)
+
+static const unsigned short notetable[12] = // D00 note table
+ {340,363,385,408,432,458,485,514,544,577,611,647};
+
+static inline uint16_t LE_WORD(const uint16_t *val)
+{
+ const uint8_t *b = (const uint8_t *)val;
+ return (b[1] << 8) + b[0];
+}
+
+/*** public methods *************************************/
+
+CPlayer *Cd00Player::factory(Copl *newopl)
+{
+ return new Cd00Player(newopl);
+}
+
+bool Cd00Player::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ d00header *checkhead;
+ d00header1 *ch;
+ unsigned long filesize;
+ int i,ver1=0;
+ char *str;
+
+ // file validation section
+ checkhead = new d00header;
+ f->readString((char *)checkhead, sizeof(d00header));
+
+ // Check for version 2-4 header
+ if(strncmp(checkhead->id,"JCH\x26\x02\x66",6) || checkhead->type ||
+ !checkhead->subsongs || checkhead->soundcard) {
+ // Check for version 0 or 1 header (and .d00 file extension)
+ delete checkhead;
+ if(!fp.extension(filename, ".d00")) { fp.close(f); return false; }
+ ch = new d00header1;
+ f->seek(0); f->readString((char *)ch, sizeof(d00header1));
+ if(ch->version > 1 || !ch->subsongs)
+ { delete ch; fp.close(f); return false; }
+ delete ch;
+ ver1 = 1;
+ } else
+ delete checkhead;
+
+ AdPlug_LogWrite("Cd00Player::load(f,\"%s\"): %s format D00 file detected!\n",
+ filename.c_str(), ver1 ? "Old" : "New");
+
+ // load section
+ filesize = fp.filesize(f); f->seek(0);
+ filedata = new char [filesize + 1]; // 1 byte is needed for old-style DataInfo block
+ f->readString((char *)filedata, filesize);
+ fp.close(f);
+ if(!ver1) { // version 2 and above
+ header = (struct d00header *)filedata;
+ version = header->version;
+ datainfo = (char *)filedata + LE_WORD(&header->infoptr);
+ inst = (struct Sinsts *)((char *)filedata + LE_WORD(&header->instptr));
+ seqptr = (unsigned short *)((char *)filedata + LE_WORD(&header->seqptr));
+ for(i=31;i>=0;i--) // erase whitespace
+ if(header->songname[i] == ' ')
+ header->songname[i] = '\0';
+ else
+ break;
+ for(i=31;i>=0;i--)
+ if(header->author[i] == ' ')
+ header->author[i] = '\0';
+ else
+ break;
+ } else { // version 1
+ header1 = (struct d00header1 *)filedata;
+ version = header1->version;
+ datainfo = (char *)filedata + LE_WORD(&header1->infoptr);
+ inst = (struct Sinsts *)((char *)filedata + LE_WORD(&header1->instptr));
+ seqptr = (unsigned short *)((char *)filedata + LE_WORD(&header1->seqptr));
+ }
+ switch(version) {
+ case 0:
+ levpuls = 0;
+ spfx = 0;
+ header1->speed = 70; // v0 files default to 70Hz
+ break;
+ case 1:
+ levpuls = (struct Slevpuls *)((char *)filedata + LE_WORD(&header1->lpulptr));
+ spfx = 0;
+ break;
+ case 2:
+ levpuls = (struct Slevpuls *)((char *)filedata + LE_WORD(&header->spfxptr));
+ spfx = 0;
+ break;
+ case 3:
+ spfx = 0;
+ levpuls = 0;
+ break;
+ case 4:
+ spfx = (struct Sspfx *)((char *)filedata + LE_WORD(&header->spfxptr));
+ levpuls = 0;
+ break;
+ }
+ if((str = strstr(datainfo,"\xff\xff")))
+ while((*str == '\xff' || *str == ' ') && str >= datainfo) {
+ *str = '\0'; str--;
+ }
+ else // old-style block
+ memset((char *)filedata+filesize,0,1);
+
+ rewind(0);
+ return true;
+}
+
+bool Cd00Player::update()
+{
+ unsigned char c,cnt,trackend=0,fx,note;
+ unsigned short ord,*patt,buf,fxop,pattpos;
+
+ // effect handling (timer dependant)
+ for(c=0;c<9;c++) {
+ channel[c].slideval += channel[c].slide; setfreq(c); // sliding
+ vibrato(c); // vibrato
+
+ if(channel[c].spfx != 0xffff) { // SpFX
+ if(channel[c].fxdel)
+ channel[c].fxdel--;
+ else {
+ channel[c].spfx = LE_WORD(&spfx[channel[c].spfx].ptr);
+ channel[c].fxdel = spfx[channel[c].spfx].duration;
+ channel[c].inst = LE_WORD(&spfx[channel[c].spfx].instnr) & 0xfff;
+ if(spfx[channel[c].spfx].modlev != 0xff)
+ channel[c].modvol = spfx[channel[c].spfx].modlev;
+ setinst(c);
+ if(LE_WORD(&spfx[channel[c].spfx].instnr) & 0x8000) // locked frequency
+ note = spfx[channel[c].spfx].halfnote;
+ else // unlocked frequency
+ note = spfx[channel[c].spfx].halfnote + channel[c].note;
+ channel[c].freq = notetable[note%12] + ((note/12) << 10);
+ setfreq(c);
+ }
+ channel[c].modvol += spfx[channel[c].spfx].modlevadd; channel[c].modvol &= 63;
+ setvolume(c);
+ }
+
+ if(channel[c].levpuls != 0xff) // Levelpuls
+ if(channel[c].frameskip)
+ channel[c].frameskip--;
+ else {
+ channel[c].frameskip = inst[channel[c].inst].timer;
+ if(channel[c].fxdel)
+ channel[c].fxdel--;
+ else {
+ channel[c].levpuls = levpuls[channel[c].levpuls].ptr - 1;
+ channel[c].fxdel = levpuls[channel[c].levpuls].duration;
+ if(levpuls[channel[c].levpuls].level != 0xff)
+ channel[c].modvol = levpuls[channel[c].levpuls].level;
+ }
+ channel[c].modvol += levpuls[channel[c].levpuls].voladd; channel[c].modvol &= 63;
+ setvolume(c);
+ }
+ }
+
+ // song handling
+ for(c=0;c<9;c++)
+ if(version < 3 ? channel[c].del : channel[c].del <= 0x7f) {
+ if(version == 4) // v4: hard restart SR
+ if(channel[c].del == inst[channel[c].inst].timer)
+ if(channel[c].nextnote)
+ opl->write(0x83 + op_table[c], inst[channel[c].inst].sr);
+ if(version < 3)
+ channel[c].del--;
+ else
+ if(channel[c].speed)
+ channel[c].del += channel[c].speed;
+ else {
+ channel[c].seqend = 1;
+ continue;
+ }
+ } else {
+ if(channel[c].speed) {
+ if(version < 3)
+ channel[c].del = channel[c].speed;
+ else {
+ channel[c].del &= 0x7f;
+ channel[c].del += channel[c].speed;
+ }
+ } else {
+ channel[c].seqend = 1;
+ continue;
+ }
+ if(channel[c].rhcnt) { // process pending REST/HOLD events
+ channel[c].rhcnt--;
+ continue;
+ }
+ readorder: // process arrangement (orderlist)
+ ord = LE_WORD(&channel[c].order[channel[c].ordpos]);
+ switch(ord) {
+ case 0xfffe: channel[c].seqend = 1; continue; // end of arrangement stream
+ case 0xffff: // jump to order
+ channel[c].ordpos = LE_WORD(&channel[c].order[channel[c].ordpos + 1]);
+ channel[c].seqend = 1;
+ goto readorder;
+ default:
+ if(ord >= 0x9000) { // set speed
+ channel[c].speed = ord & 0xff;
+ ord = LE_WORD(&channel[c].order[channel[c].ordpos - 1]);
+ channel[c].ordpos++;
+ } else
+ if(ord >= 0x8000) { // transpose track
+ channel[c].transpose = ord & 0xff;
+ if(ord & 0x100)
+ channel[c].transpose = -channel[c].transpose;
+ ord = LE_WORD(&channel[c].order[++channel[c].ordpos]);
+ }
+ patt = (unsigned short *)((char *)filedata + LE_WORD(&seqptr[ord]));
+ break;
+ }
+ channel[c].fxflag = 0;
+ readseq: // process sequence (pattern)
+ if(!version) // v0: always initialize rhcnt
+ channel[c].rhcnt = channel[c].irhcnt;
+ pattpos = LE_WORD(&patt[channel[c].pattpos]);
+ if(pattpos == 0xffff) { // pattern ended?
+ channel[c].pattpos = 0;
+ channel[c].ordpos++;
+ goto readorder;
+ }
+ cnt = HIBYTE(pattpos);
+ note = LOBYTE(pattpos);
+ fx = pattpos >> 12;
+ fxop = pattpos & 0x0fff;
+ channel[c].pattpos++; pattpos = LE_WORD(&patt[channel[c].pattpos]);
+ channel[c].nextnote = LOBYTE(pattpos) & 0x7f;
+ if(version ? cnt < 0x40 : !fx) { // note event
+ switch(note) {
+ case 0: // REST event
+ case 0x80:
+ if(!note || version) {
+ channel[c].key = 0;
+ setfreq(c);
+ }
+ // fall through...
+ case 0x7e: // HOLD event
+ if(version)
+ channel[c].rhcnt = cnt;
+ channel[c].nextnote = 0;
+ break;
+ default: // play note
+ // restart fx
+ if(!(channel[c].fxflag & 1))
+ channel[c].vibdepth = 0;
+ if(!(channel[c].fxflag & 2))
+ channel[c].slideval = channel[c].slide = 0;
+
+ if(version) { // note handling for v1 and above
+ if(note > 0x80) // locked note (no channel transpose)
+ note -= 0x80;
+ else // unlocked note
+ note += channel[c].transpose;
+ channel[c].note = note; // remember note for SpFX
+
+ if(channel[c].ispfx != 0xffff && cnt < 0x20) { // reset SpFX
+ channel[c].spfx = channel[c].ispfx;
+ if(LE_WORD(&spfx[channel[c].spfx].instnr) & 0x8000) // locked frequency
+ note = spfx[channel[c].spfx].halfnote;
+ else // unlocked frequency
+ note += spfx[channel[c].spfx].halfnote;
+ channel[c].inst = LE_WORD(&spfx[channel[c].spfx].instnr) & 0xfff;
+ channel[c].fxdel = spfx[channel[c].spfx].duration;
+ if(spfx[channel[c].spfx].modlev != 0xff)
+ channel[c].modvol = spfx[channel[c].spfx].modlev;
+ else
+ channel[c].modvol = inst[channel[c].inst].data[7] & 63;
+ }
+
+ if(channel[c].ilevpuls != 0xff && cnt < 0x20) { // reset LevelPuls
+ channel[c].levpuls = channel[c].ilevpuls;
+ channel[c].fxdel = levpuls[channel[c].levpuls].duration;
+ channel[c].frameskip = inst[channel[c].inst].timer;
+ if(levpuls[channel[c].levpuls].level != 0xff)
+ channel[c].modvol = levpuls[channel[c].levpuls].level;
+ else
+ channel[c].modvol = inst[channel[c].inst].data[7] & 63;
+ }
+
+ channel[c].freq = notetable[note%12] + ((note/12) << 10);
+ if(cnt < 0x20) // normal note
+ playnote(c);
+ else { // tienote
+ setfreq(c);
+ cnt -= 0x20; // make count proper
+ }
+ channel[c].rhcnt = cnt;
+ } else { // note handling for v0
+ if(cnt < 2) // unlocked note
+ note += channel[c].transpose;
+ channel[c].note = note;
+
+ channel[c].freq = notetable[note%12] + ((note/12) << 10);
+ if(cnt == 1) // tienote
+ setfreq(c);
+ else // normal note
+ playnote(c);
+ }
+ break;
+ }
+ continue; // event is complete
+ } else { // effect event
+ switch(fx) {
+ case 6: // Cut/Stop Voice
+ buf = channel[c].inst;
+ channel[c].inst = 0;
+ playnote(c);
+ channel[c].inst = buf;
+ channel[c].rhcnt = fxop;
+ continue; // no note follows this event
+ case 7: // Vibrato
+ channel[c].vibspeed = fxop & 0xff;
+ channel[c].vibdepth = fxop >> 8;
+ channel[c].trigger = fxop >> 9;
+ channel[c].fxflag |= 1;
+ break;
+ case 8: // v0: Duration
+ if(!version)
+ channel[c].irhcnt = fxop;
+ break;
+ case 9: // New Level
+ channel[c].vol = fxop & 63;
+ if(channel[c].vol + channel[c].cvol < 63) // apply channel volume
+ channel[c].vol += channel[c].cvol;
+ else
+ channel[c].vol = 63;
+ setvolume(c);
+ break;
+ case 0xb: // v4: Set SpFX
+ if(version == 4)
+ channel[c].ispfx = fxop;
+ break;
+ case 0xc: // Set Instrument
+ channel[c].ispfx = 0xffff;
+ channel[c].spfx = 0xffff;
+ channel[c].inst = fxop;
+ channel[c].modvol = inst[fxop].data[7] & 63;
+ if(version < 3 && version && inst[fxop].tunelev) // Set LevelPuls
+ channel[c].ilevpuls = inst[fxop].tunelev - 1;
+ else {
+ channel[c].ilevpuls = 0xff;
+ channel[c].levpuls = 0xff;
+ }
+ break;
+ case 0xd: // Slide up
+ channel[c].slide = fxop;
+ channel[c].fxflag |= 2;
+ break;
+ case 0xe: // Slide down
+ channel[c].slide = -fxop;
+ channel[c].fxflag |= 2;
+ break;
+ }
+ goto readseq; // event is incomplete, note follows
+ }
+ }
+
+ for(c=0;c<9;c++)
+ if(channel[c].seqend)
+ trackend++;
+ if(trackend == 9)
+ songend = 1;
+
+ return !songend;
+}
+
+void Cd00Player::rewind(int subsong)
+{
+ struct Stpoin {
+ unsigned short ptr[9];
+ unsigned char volume[9],dummy[5];
+ } *tpoin;
+ int i;
+
+ if(version > 1) { // do nothing if subsong > number of subsongs
+ if(subsong >= header->subsongs)
+ return;
+ } else
+ if(subsong >= header1->subsongs)
+ return;
+
+ memset(channel,0,sizeof(channel));
+ if(version > 1)
+ tpoin = (struct Stpoin *)((char *)filedata + LE_WORD(&header->tpoin));
+ else
+ tpoin = (struct Stpoin *)((char *)filedata + LE_WORD(&header1->tpoin));
+ for(i=0;i<9;i++) {
+ if(LE_WORD(&tpoin[subsong].ptr[i])) { // track enabled
+ channel[i].speed = LE_WORD((unsigned short *)
+ ((char *)filedata + LE_WORD(&tpoin[subsong].ptr[i])));
+ channel[i].order = (unsigned short *)
+ ((char *)filedata + LE_WORD(&tpoin[subsong].ptr[i]) + 2);
+ } else { // track disabled
+ channel[i].speed = 0;
+ channel[i].order = 0;
+ }
+ channel[i].ispfx = 0xffff; channel[i].spfx = 0xffff; // no SpFX
+ channel[i].ilevpuls = 0xff; channel[i].levpuls = 0xff; // no LevelPuls
+ channel[i].cvol = tpoin[subsong].volume[i] & 0x7f; // our player may savely ignore bit 7
+ channel[i].vol = channel[i].cvol; // initialize volume
+ }
+ songend = 0;
+ opl->init(); opl->write(1,32); // reset OPL chip
+}
+
+std::string Cd00Player::gettype()
+{
+ char tmpstr[40];
+
+ sprintf(tmpstr,"EdLib packed (version %d)",version > 1 ? header->version : header1->version);
+ return std::string(tmpstr);
+}
+
+float Cd00Player::getrefresh()
+{
+ if(version > 1)
+ return header->speed;
+ else
+ return header1->speed;
+}
+
+unsigned int Cd00Player::getsubsongs()
+{
+ if(version <= 1) // return number of subsongs
+ return header1->subsongs;
+ else
+ return header->subsongs;
+}
+
+/*** private methods *************************************/
+
+void Cd00Player::setvolume(unsigned char chan)
+{
+ unsigned char op = op_table[chan];
+ unsigned short insnr = channel[chan].inst;
+
+ opl->write(0x43 + op,(int)(63-((63-(inst[insnr].data[2] & 63))/63.0)*(63-channel[chan].vol)) +
+ (inst[insnr].data[2] & 192));
+ if(inst[insnr].data[10] & 1)
+ opl->write(0x40 + op,(int)(63-((63-channel[chan].modvol)/63.0)*(63-channel[chan].vol)) +
+ (inst[insnr].data[7] & 192));
+ else
+ opl->write(0x40 + op,channel[chan].modvol + (inst[insnr].data[7] & 192));
+}
+
+void Cd00Player::setfreq(unsigned char chan)
+{
+ unsigned short freq = channel[chan].freq;
+
+ if(version == 4) // v4: apply instrument finetune
+ freq += inst[channel[chan].inst].tunelev;
+
+ freq += channel[chan].slideval;
+ opl->write(0xa0 + chan, freq & 255);
+ if(channel[chan].key)
+ opl->write(0xb0 + chan, ((freq >> 8) & 31) | 32);
+ else
+ opl->write(0xb0 + chan, (freq >> 8) & 31);
+}
+
+void Cd00Player::setinst(unsigned char chan)
+{
+ unsigned char op = op_table[chan];
+ unsigned short insnr = channel[chan].inst;
+
+ // set instrument data
+ opl->write(0x63 + op, inst[insnr].data[0]);
+ opl->write(0x83 + op, inst[insnr].data[1]);
+ opl->write(0x23 + op, inst[insnr].data[3]);
+ opl->write(0xe3 + op, inst[insnr].data[4]);
+ opl->write(0x60 + op, inst[insnr].data[5]);
+ opl->write(0x80 + op, inst[insnr].data[6]);
+ opl->write(0x20 + op, inst[insnr].data[8]);
+ opl->write(0xe0 + op, inst[insnr].data[9]);
+ if(version)
+ opl->write(0xc0 + chan, inst[insnr].data[10]);
+ else
+ opl->write(0xc0 + chan, (inst[insnr].data[10] << 1) + (inst[insnr].tunelev & 1));
+}
+
+void Cd00Player::playnote(unsigned char chan)
+{
+ // set misc vars & play
+ opl->write(0xb0 + chan, 0); // stop old note
+ setinst(chan);
+ channel[chan].key = 1;
+ setfreq(chan);
+ setvolume(chan);
+}
+
+void Cd00Player::vibrato(unsigned char chan)
+{
+ if(!channel[chan].vibdepth)
+ return;
+
+ if(channel[chan].trigger)
+ channel[chan].trigger--;
+ else {
+ channel[chan].trigger = channel[chan].vibdepth;
+ channel[chan].vibspeed = -channel[chan].vibspeed;
+ }
+ channel[chan].freq += channel[chan].vibspeed;
+ setfreq(chan);
+}
diff --git a/plugins/adplug/adplug/d00.h b/plugins/adplug/adplug/d00.h
new file mode 100644
index 00000000..50b0de3c
--- /dev/null
+++ b/plugins/adplug/adplug/d00.h
@@ -0,0 +1,109 @@
+/*
+ * AdPlug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * d00.h - D00 Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_D00
+#define H_D00
+
+#include "player.h"
+
+class Cd00Player: public CPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ Cd00Player(Copl *newopl)
+ : CPlayer(newopl), filedata(0)
+ { };
+ ~Cd00Player()
+ { if(filedata) delete [] filedata; };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype();
+ std::string gettitle()
+ { if(version > 1) return std::string(header->songname); else return std::string(); };
+ std::string getauthor()
+ { if(version > 1) return std::string(header->author); else return std::string(); };
+ std::string getdesc()
+ { if(*datainfo) return std::string(datainfo); else return std::string(); };
+ unsigned int getsubsongs();
+
+ protected:
+#pragma pack(1)
+ struct d00header {
+ char id[6];
+ unsigned char type,version,speed,subsongs,soundcard;
+ char songname[32],author[32],dummy[32];
+ unsigned short tpoin,seqptr,instptr,infoptr,spfxptr,endmark;
+ };
+
+ struct d00header1 {
+ unsigned char version,speed,subsongs;
+ unsigned short tpoin,seqptr,instptr,infoptr,lpulptr,endmark;
+ };
+#pragma pack()
+
+ struct {
+ unsigned short *order,ordpos,pattpos,del,speed,rhcnt,key,freq,inst,
+ spfx,ispfx,irhcnt;
+ signed short transpose,slide,slideval,vibspeed;
+ unsigned char seqend,vol,vibdepth,fxdel,modvol,cvol,levpuls,
+ frameskip,nextnote,note,ilevpuls,trigger,fxflag;
+ } channel[9];
+
+ struct Sinsts {
+ unsigned char data[11],tunelev,timer,sr,dummy[2];
+ } *inst;
+
+ struct Sspfx {
+ unsigned short instnr;
+ signed char halfnote;
+ unsigned char modlev;
+ signed char modlevadd;
+ unsigned char duration;
+ unsigned short ptr;
+ } *spfx;
+
+ struct Slevpuls {
+ unsigned char level;
+ signed char voladd;
+ unsigned char duration,ptr;
+ } *levpuls;
+
+ unsigned char songend,version;
+ char *datainfo;
+ unsigned short *seqptr;
+ d00header *header;
+ d00header1 *header1;
+ char *filedata;
+
+ private:
+ void setvolume(unsigned char chan);
+ void setfreq(unsigned char chan);
+ void setinst(unsigned char chan);
+ void playnote(unsigned char chan);
+ void vibrato(unsigned char chan);
+};
+
+#endif
diff --git a/plugins/adplug/adplug/database.cpp b/plugins/adplug/adplug/database.cpp
new file mode 100644
index 00000000..0a2abdad
--- /dev/null
+++ b/plugins/adplug/adplug/database.cpp
@@ -0,0 +1,426 @@
+/*
+ * AdPlug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (c) 1999 - 2006 Simon Peter <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * database.cpp - AdPlug database class
+ * Copyright (c) 2002 Riven the Mage <riven@ok.ru>
+ * Copyright (c) 2002, 2003, 2006 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <binio.h>
+#include <binfile.h>
+#include <string.h>
+
+#include "database.h"
+
+#define DB_FILEID_V10 "AdPlug Module Information Database 1.0\x10"
+
+/***** CAdPlugDatabase *****/
+
+const unsigned short CAdPlugDatabase::hash_radix = 0xfff1; // should be prime
+
+CAdPlugDatabase::CAdPlugDatabase()
+ : linear_index(0), linear_logic_length(0), linear_length(0)
+{
+ db_linear = new DB_Bucket * [hash_radix];
+ db_hashed = new DB_Bucket * [hash_radix];
+ memset(db_linear, 0, sizeof(DB_Bucket *) * hash_radix);
+ memset(db_hashed, 0, sizeof(DB_Bucket *) * hash_radix);
+}
+
+CAdPlugDatabase::~CAdPlugDatabase()
+{
+ unsigned long i;
+
+ for(i = 0; i < linear_length; i++)
+ delete db_linear[i];
+
+ delete [] db_linear;
+ delete [] db_hashed;
+}
+
+bool CAdPlugDatabase::load(std::string db_name)
+{
+ binifstream f(db_name);
+ if(f.error()) return false;
+ return load(f);
+}
+
+bool CAdPlugDatabase::load(binistream &f)
+{
+ unsigned int idlen = strlen(DB_FILEID_V10);
+ char *id = new char [idlen];
+ unsigned long length;
+
+ // Open database as little endian with IEEE floats
+ f.setFlag(binio::BigEndian, false); f.setFlag(binio::FloatIEEE);
+
+ f.readString(id,idlen);
+ if(memcmp(id,DB_FILEID_V10,idlen)) {
+ delete [] id;
+ return false;
+ }
+ delete [] id;
+ length = f.readInt(4);
+
+ // read records
+ for(unsigned long i = 0; i < length; i++)
+ insert(CRecord::factory(f));
+
+ return true;
+}
+
+bool CAdPlugDatabase::save(std::string db_name)
+{
+ binofstream f(db_name);
+ if(f.error()) return false;
+ return save(f);
+}
+
+bool CAdPlugDatabase::save(binostream &f)
+{
+ unsigned long i;
+
+ // Save database as little endian with IEEE floats
+ f.setFlag(binio::BigEndian, false); f.setFlag(binio::FloatIEEE);
+
+ f.writeString(DB_FILEID_V10);
+ f.writeInt(linear_logic_length, 4);
+
+ // write records
+ for(i = 0; i < linear_length; i++)
+ if(!db_linear[i]->deleted)
+ db_linear[i]->record->write(f);
+
+ return true;
+}
+
+CAdPlugDatabase::CRecord *CAdPlugDatabase::search(CKey const &key)
+{
+ if(lookup(key)) return get_record(); else return 0;
+}
+
+bool CAdPlugDatabase::lookup(CKey const &key)
+{
+ unsigned long index = make_hash(key);
+ if(!db_hashed[index]) return false;
+
+ // immediate hit ?
+ DB_Bucket *bucket = db_hashed[index];
+
+ if(!bucket->deleted && bucket->record->key == key) {
+ linear_index = bucket->index;
+ return true;
+ }
+
+ // in-chain hit ?
+ bucket = db_hashed[index]->chain;
+
+ while(bucket) {
+ if(!bucket->deleted && bucket->record->key == key) {
+ linear_index = bucket->index;
+ return true;
+ }
+
+ bucket = bucket->chain;
+ }
+
+ return false;
+}
+
+bool CAdPlugDatabase::insert(CRecord *record)
+{
+ long index;
+
+ // sanity checks
+ if(!record) return false; // null-pointer given
+ if(linear_length == hash_radix) return false; // max. db size exceeded
+ if(lookup(record->key)) return false; // record already in db
+
+ // make bucket
+ DB_Bucket *bucket = new DB_Bucket(linear_length, record);
+ if(!bucket) return false;
+
+ // add to linear list
+ db_linear[linear_length] = bucket;
+ linear_logic_length++; linear_length++;
+
+ // add to hashed list
+ index = make_hash(record->key);
+
+ if(!db_hashed[index]) // First entry in hashtable
+ db_hashed[index] = bucket;
+ else { // Add entry in chained list
+ DB_Bucket *chain = db_hashed[index];
+
+ while(chain->chain) chain = chain->chain;
+ chain->chain = bucket;
+ }
+
+ return true;
+}
+
+void CAdPlugDatabase::wipe(CRecord *record)
+{
+ if(!lookup(record->key)) return;
+ wipe();
+}
+
+void CAdPlugDatabase::wipe()
+{
+ if(!linear_length) return;
+
+ DB_Bucket *bucket = db_linear[linear_index];
+
+ if(!bucket->deleted) {
+ delete bucket->record;
+ linear_logic_length--;
+ bucket->deleted = true;
+ }
+}
+
+CAdPlugDatabase::CRecord *CAdPlugDatabase::get_record()
+{
+ if(!linear_length) return 0;
+ return db_linear[linear_index]->record;
+}
+
+bool CAdPlugDatabase::go_forward()
+{
+ if(linear_index + 1 < linear_length) {
+ linear_index++;
+ return true;
+ } else
+ return false;
+}
+
+bool CAdPlugDatabase::go_backward()
+{
+ if(!linear_index) return false;
+ linear_index--;
+ return true;
+}
+
+void CAdPlugDatabase::goto_begin()
+{
+ if(linear_length) linear_index = 0;
+}
+
+void CAdPlugDatabase::goto_end()
+{
+ if(linear_length) linear_index = linear_length - 1;
+}
+
+inline unsigned long CAdPlugDatabase::make_hash(CKey const &key)
+{
+ return (key.crc32 + key.crc16) % hash_radix;
+}
+
+/***** CAdPlugDatabase::DB_Bucket *****/
+
+CAdPlugDatabase::DB_Bucket::DB_Bucket(unsigned long nindex, CRecord *newrecord, DB_Bucket *newchain)
+ : index(nindex), deleted(false), chain(newchain), record(newrecord)
+{
+}
+
+CAdPlugDatabase::DB_Bucket::~DB_Bucket()
+{
+ if(!deleted) delete record;
+}
+
+/***** CAdPlugDatabase::CRecord *****/
+
+CAdPlugDatabase::CRecord *CAdPlugDatabase::CRecord::factory(RecordType type)
+{
+ switch(type) {
+ case Plain: return new CPlainRecord;
+ case SongInfo: return new CInfoRecord;
+ case ClockSpeed: return new CClockRecord;
+ default: return 0;
+ }
+}
+
+CAdPlugDatabase::CRecord *CAdPlugDatabase::CRecord::factory(binistream &in)
+{
+ RecordType type;
+ unsigned long size;
+ CRecord *rec;
+
+ type = (RecordType)in.readInt(1); size = in.readInt(4);
+ rec = factory(type);
+
+ if(rec) {
+ rec->key.crc16 = in.readInt(2); rec->key.crc32 = in.readInt(4);
+ rec->filetype = in.readString('\0'); rec->comment = in.readString('\0');
+ rec->read_own(in);
+ return rec;
+ } else {
+ // skip this record, cause we don't know about it
+ in.seek(size, binio::Add);
+ return 0;
+ }
+}
+
+void CAdPlugDatabase::CRecord::write(binostream &out)
+{
+ out.writeInt(type, 1);
+ out.writeInt(get_size() + filetype.length() + comment.length() + 8, 4);
+ out.writeInt(key.crc16, 2); out.writeInt(key.crc32, 4);
+ out.writeString(filetype); out.writeInt('\0', 1);
+ out.writeString(comment); out.writeInt('\0', 1);
+
+ write_own(out);
+}
+
+bool CAdPlugDatabase::CRecord::user_read(std::istream &in, std::ostream &out)
+{
+ return user_read_own(in, out);
+}
+
+bool CAdPlugDatabase::CRecord::user_write(std::ostream &out)
+{
+ out << "Record type: ";
+ switch(type) {
+ case Plain: out << "Plain"; break;
+ case SongInfo: out << "SongInfo"; break;
+ case ClockSpeed: out << "ClockSpeed"; break;
+ default: out << "*** Unknown ***"; break;
+ }
+ out << std::endl;
+ out << "Key: " << std::hex << key.crc16 << ":" << key.crc32 << std::dec << std::endl;
+ out << "File type: " << filetype << std::endl;
+ out << "Comment: " << comment << std::endl;
+
+ return user_write_own(out);
+}
+
+/***** CAdPlugDatabase::CRecord::CKey *****/
+
+CAdPlugDatabase::CKey::CKey(binistream &buf)
+{
+ make(buf);
+}
+
+bool CAdPlugDatabase::CKey::operator==(const CKey &key)
+{
+ return ((crc16 == key.crc16) && (crc32 == key.crc32));
+}
+
+void CAdPlugDatabase::CKey::make(binistream &buf)
+// Key is CRC16:CRC32 pair. CRC16 and CRC32 calculation routines (c) Zhengxi
+{
+ static const unsigned short magic16 = 0xa001;
+ static const unsigned long magic32 = 0xedb88320;
+
+ crc16 = 0; crc32 = ~0;
+
+ while(!buf.eof())
+ {
+ unsigned char byte = buf.readInt(1);
+
+ for (int j=0;j<8;j++)
+ {
+ if ((crc16 ^ byte) & 1)
+ crc16 = (crc16 >> 1) ^ magic16;
+ else
+ crc16 >>= 1;
+
+ if ((crc32 ^ byte) & 1)
+ crc32 = (crc32 >> 1) ^ magic32;
+ else
+ crc32 >>= 1;
+
+ byte >>= 1;
+ }
+ }
+
+ crc16 &= 0xffff;
+ crc32 = ~crc32;
+}
+
+/***** CInfoRecord *****/
+
+CInfoRecord::CInfoRecord()
+{
+ type = SongInfo;
+}
+
+void CInfoRecord::read_own(binistream &in)
+{
+ title = in.readString('\0');
+ author = in.readString('\0');
+}
+
+void CInfoRecord::write_own(binostream &out)
+{
+ out.writeString(title); out.writeInt('\0', 1);
+ out.writeString(author); out.writeInt('\0', 1);
+}
+
+unsigned long CInfoRecord::get_size()
+{
+ return title.length() + author.length() + 2;
+}
+
+bool CInfoRecord::user_read_own(std::istream &in, std::ostream &out)
+{
+ out << "Title: "; in >> title;
+ out << "Author: "; in >> author;
+ return true;
+}
+
+bool CInfoRecord::user_write_own(std::ostream &out)
+{
+ out << "Title: " << title << std::endl;
+ out << "Author: " << author << std::endl;
+ return true;
+}
+
+/***** CClockRecord *****/
+
+CClockRecord::CClockRecord()
+ : clock(0.0f)
+{
+ type = ClockSpeed;
+}
+
+void CClockRecord::read_own(binistream &in)
+{
+ clock = in.readFloat(binio::Single);
+}
+
+void CClockRecord::write_own(binostream &out)
+{
+ out.writeFloat(clock, binio::Single);
+}
+
+unsigned long CClockRecord::get_size()
+{
+ return 4;
+}
+
+bool CClockRecord::user_read_own(std::istream &in, std::ostream &out)
+{
+ out << "Clockspeed: "; in >> clock;
+ return true;
+}
+
+bool CClockRecord::user_write_own(std::ostream &out)
+{
+ out << "Clock speed: " << clock << " Hz" << std::endl;
+ return true;
+}
diff --git a/plugins/adplug/adplug/database.h b/plugins/adplug/adplug/database.h
new file mode 100644
index 00000000..050e8959
--- /dev/null
+++ b/plugins/adplug/adplug/database.h
@@ -0,0 +1,169 @@
+/*
+ * AdPlug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (c) 1999 - 2003 Simon Peter <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * database.h - AdPlug database class
+ * Copyright (c) 2002 Riven the Mage <riven@ok.ru>
+ * Copyright (c) 2002, 2003 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_DATABASE
+#define H_ADPLUG_DATABASE
+
+#include <iostream>
+#include <string>
+#include <binio.h>
+
+class CAdPlugDatabase
+{
+public:
+ class CKey
+ {
+ public:
+ unsigned short crc16;
+ unsigned long crc32;
+
+ CKey() {};
+ CKey(binistream &in);
+
+ bool operator==(const CKey &key);
+
+ private:
+ void make(binistream &in);
+ };
+
+ class CRecord
+ {
+ public:
+ typedef enum { Plain, SongInfo, ClockSpeed } RecordType;
+
+ RecordType type;
+ CKey key;
+ std::string filetype, comment;
+
+ static CRecord *factory(RecordType type);
+ static CRecord *factory(binistream &in);
+
+ CRecord() {}
+ virtual ~CRecord() {}
+
+ void write(binostream &out);
+
+ bool user_read(std::istream &in, std::ostream &out);
+ bool user_write(std::ostream &out);
+
+ protected:
+ virtual void read_own(binistream &in) = 0;
+ virtual void write_own(binostream &out) = 0;
+ virtual unsigned long get_size() = 0;
+ virtual bool user_read_own(std::istream &in, std::ostream &out) = 0;
+ virtual bool user_write_own(std::ostream &out) = 0;
+ };
+
+ CAdPlugDatabase();
+ ~CAdPlugDatabase();
+
+ bool load(std::string db_name);
+ bool load(binistream &f);
+ bool save(std::string db_name);
+ bool save(binostream &f);
+
+ bool insert(CRecord *record);
+
+ void wipe(CRecord *record);
+ void wipe();
+
+ CRecord *search(CKey const &key);
+ bool lookup(CKey const &key);
+
+ CRecord *get_record();
+
+ bool go_forward();
+ bool go_backward();
+
+ void goto_begin();
+ void goto_end();
+
+private:
+ static const unsigned short hash_radix;
+
+ class DB_Bucket
+ {
+ public:
+ unsigned long index;
+ bool deleted;
+ DB_Bucket *chain;
+
+ CRecord *record;
+
+ DB_Bucket(unsigned long nindex, CRecord *newrecord, DB_Bucket *newchain = 0);
+ ~DB_Bucket();
+ };
+
+ DB_Bucket **db_linear;
+ DB_Bucket **db_hashed;
+
+ unsigned long linear_index, linear_logic_length, linear_length;
+
+ unsigned long make_hash(CKey const &key);
+};
+
+class CPlainRecord: public CAdPlugDatabase::CRecord
+{
+public:
+ CPlainRecord() { type = Plain; }
+
+protected:
+ virtual void read_own(binistream &in) {}
+ virtual void write_own(binostream &out) {}
+ virtual unsigned long get_size() { return 0; }
+ virtual bool user_read_own(std::istream &in, std::ostream &out) { return true; }
+ virtual bool user_write_own(std::ostream &out) { return true; }
+};
+
+class CInfoRecord: public CAdPlugDatabase::CRecord
+{
+public:
+ std::string title;
+ std::string author;
+
+ CInfoRecord();
+
+protected:
+ virtual void read_own(binistream &in);
+ virtual void write_own(binostream &out);
+ virtual unsigned long get_size();
+ virtual bool user_read_own(std::istream &in, std::ostream &out);
+ virtual bool user_write_own(std::ostream &out);
+};
+
+class CClockRecord: public CAdPlugDatabase::CRecord
+{
+public:
+ float clock;
+
+ CClockRecord();
+
+protected:
+ virtual void read_own(binistream &in);
+ virtual void write_own(binostream &out);
+ virtual unsigned long get_size();
+ virtual bool user_read_own(std::istream &in, std::ostream &out);
+ virtual bool user_write_own(std::ostream &out);
+};
+
+#endif
diff --git a/plugins/adplug/adplug/debug.c b/plugins/adplug/adplug/debug.c
new file mode 100644
index 00000000..bd6598da
--- /dev/null
+++ b/plugins/adplug/adplug/debug.c
@@ -0,0 +1,57 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2002 Simon Peter <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * debug.h - AdPlug Debug Logger
+ * Copyright (c) 2002 Riven the Mage <riven@ok.ru>
+ * Copyright (c) 2002 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifdef DEBUG
+
+#include <stdio.h>
+#include <stdarg.h>
+
+static FILE *log = NULL;
+
+void AdPlug_LogFile(const char *filename)
+{
+ if(log) fclose(log);
+ log = fopen(filename,"wt");
+}
+
+void AdPlug_LogWrite(const char *fmt, ...)
+{
+ va_list argptr;
+
+ va_start(argptr, fmt);
+
+ if(log) {
+ vfprintf(log, fmt, argptr);
+ fflush(log);
+ } else
+ vfprintf(stderr, fmt, argptr);
+
+ va_end(argptr);
+}
+
+#else
+
+void AdPlug_LogFile(char *filename) { }
+void AdPlug_LogWrite(char *fmt, ...) { }
+
+#endif
diff --git a/plugins/adplug/adplug/debug.h b/plugins/adplug/adplug/debug.h
new file mode 100644
index 00000000..e5832f49
--- /dev/null
+++ b/plugins/adplug/adplug/debug.h
@@ -0,0 +1,41 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2002 Simon Peter <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * debug.h - AdPlug Debug Logger
+ * Copyright (c) 2002 Riven the Mage <riven@ok.ru>
+ * Copyright (c) 2002 Simon Peter <dn.tlp@gmx.net>
+ *
+ * NOTES:
+ * This debug logger is used throughout AdPlug to log debug output to stderr
+ * (the default) or to a user-specified logfile.
+ *
+ * To use it, AdPlug has to be compiled with debug logging support enabled.
+ * This is done by defining the DEBUG macro with every source-file. The
+ * LogFile() function can be used to specify a logfile to write to.
+ */
+
+#ifndef H_DEBUG
+#define H_DEBUG
+
+extern "C"
+{
+ void AdPlug_LogFile(const char *filename);
+ void AdPlug_LogWrite(const char *fmt, ...);
+}
+
+#endif
diff --git a/plugins/adplug/adplug/dfm.cpp b/plugins/adplug/adplug/dfm.cpp
new file mode 100644
index 00000000..4a70a032
--- /dev/null
+++ b/plugins/adplug/adplug/dfm.cpp
@@ -0,0 +1,115 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * dfm.cpp - Digital-FM Loader by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "dfm.h"
+#include "debug.h"
+
+CPlayer *CdfmLoader::factory(Copl *newopl)
+{
+ return new CdfmLoader(newopl);
+}
+
+bool CdfmLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ unsigned char npats,n,note,fx,c,r,param;
+ unsigned int i;
+ const unsigned char convfx[8] = {255,255,17,19,23,24,255,13};
+
+ // file validation
+ f->readString(header.id, 4);
+ header.hiver = f->readInt(1); header.lover = f->readInt(1);
+ if(strncmp(header.id,"DFM\x1a",4) || header.hiver > 1)
+ { fp.close(f); return false; }
+
+ // load
+ restartpos = 0; flags = Standard; bpm = 0;
+ init_trackord();
+ f->readString(songinfo, 33);
+ initspeed = f->readInt(1);
+ for(i = 0; i < 32; i++)
+ f->readString(instname[i], 12);
+ for(i = 0; i < 32; i++) {
+ inst[i].data[1] = f->readInt(1);
+ inst[i].data[2] = f->readInt(1);
+ inst[i].data[9] = f->readInt(1);
+ inst[i].data[10] = f->readInt(1);
+ inst[i].data[3] = f->readInt(1);
+ inst[i].data[4] = f->readInt(1);
+ inst[i].data[5] = f->readInt(1);
+ inst[i].data[6] = f->readInt(1);
+ inst[i].data[7] = f->readInt(1);
+ inst[i].data[8] = f->readInt(1);
+ inst[i].data[0] = f->readInt(1);
+ }
+ for(i = 0; i < 128; i++) order[i] = f->readInt(1);
+ for(i = 0; i < 128 && order[i] != 128; i++) ; length = i;
+ npats = f->readInt(1);
+ for(i = 0; i < npats; i++) {
+ n = f->readInt(1);
+ for(r = 0; r < 64; r++)
+ for(c = 0; c < 9; c++) {
+ note = f->readInt(1);
+ if((note & 15) == 15)
+ tracks[n*9+c][r].note = 127; // key off
+ else
+ tracks[n*9+c][r].note = ((note & 127) >> 4) * 12 + (note & 15);
+ if(note & 128) { // additional effect byte
+ fx = f->readInt(1);
+ if(fx >> 5 == 1)
+ tracks[n*9+c][r].inst = (fx & 31) + 1;
+ else {
+ tracks[n*9+c][r].command = convfx[fx >> 5];
+ if(tracks[n*9+c][r].command == 17) { // set volume
+ param = fx & 31;
+ param = 63 - param * 2;
+ tracks[n*9+c][r].param1 = param >> 4;
+ tracks[n*9+c][r].param2 = param & 15;
+ } else {
+ tracks[n*9+c][r].param1 = (fx & 31) >> 4;
+ tracks[n*9+c][r].param2 = fx & 15;
+ }
+ }
+ }
+
+ }
+ }
+
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+std::string CdfmLoader::gettype()
+{
+ char tmpstr[20];
+
+ sprintf(tmpstr,"Digital-FM %d.%d",header.hiver,header.lover);
+ return std::string(tmpstr);
+}
+
+float CdfmLoader::getrefresh()
+{
+ return 125.0f;
+}
diff --git a/plugins/adplug/adplug/dfm.h b/plugins/adplug/adplug/dfm.h
new file mode 100644
index 00000000..4212b2fe
--- /dev/null
+++ b/plugins/adplug/adplug/dfm.h
@@ -0,0 +1,52 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * dfm.h - Digital-FM Loader by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "protrack.h"
+
+class CdfmLoader: public CmodPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CdfmLoader(Copl *newopl)
+ : CmodPlayer(newopl)
+ { };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ float getrefresh();
+
+ std::string gettype();
+ unsigned int getinstruments()
+ { return 32; };
+ std::string getinstrument(unsigned int n)
+ { if(*instname[n]) return std::string(instname[n],1,*instname[n]); else return std::string(); };
+ std::string getdesc()
+ { return std::string(songinfo,1,*songinfo); };
+
+private:
+ struct {
+ char id[4];
+ unsigned char hiver,lover;
+ } header;
+
+ char songinfo[33];
+ char instname[32][12];
+};
diff --git a/plugins/adplug/adplug/diskopl.cpp b/plugins/adplug/adplug/diskopl.cpp
new file mode 100644
index 00000000..3296c0a1
--- /dev/null
+++ b/plugins/adplug/adplug/diskopl.cpp
@@ -0,0 +1,90 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * diskopl.cpp - Disk Writer OPL, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "diskopl.h"
+
+//static const unsigned short note_table[12] = {363,385,408,432,458,485,514,544,577,611,647,686};
+const unsigned char CDiskopl::op_table[9] = {0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12};
+
+CDiskopl::CDiskopl(std::string filename)
+ : old_freq(0.0f), del(1), nowrite(false)
+{
+ unsigned short clock = 0xffff;
+
+ currType = TYPE_OPL3;
+ f = fopen(filename.c_str(),"wb");
+ fwrite("RAWADATA",8,1,f);
+ fwrite(&clock,sizeof(clock),1,f);
+}
+
+CDiskopl::~CDiskopl()
+{
+ fclose(f);
+}
+
+void CDiskopl::update(CPlayer *p)
+{
+ unsigned short clock;
+ unsigned int wait;
+
+ if(p->getrefresh() != old_freq) {
+ old_freq = p->getrefresh();
+ del = wait = (unsigned int)(18.2f / old_freq);
+ clock = (unsigned short)(1192737/(old_freq*(wait+1)));
+ fputc(0,f); fputc(2,f);
+ fwrite(&clock,2,1,f);
+ }
+ if(!nowrite) {
+ fputc(del+1,f);
+ fputc(0,f);
+ }
+}
+
+void CDiskopl::setchip(int n)
+{
+ Copl::setchip(n);
+
+ if(!nowrite) {
+ fputc(currChip + 1, f);
+ fputc(2, f);
+ }
+}
+
+void CDiskopl::write(int reg, int val)
+{
+ if(!nowrite)
+ diskwrite(reg,val);
+}
+
+void CDiskopl::init()
+{
+ for (int i=0;i<9;i++) { // stop instruments
+ diskwrite(0xb0 + i,0); // key off
+ diskwrite(0x80 + op_table[i],0xff); // fastest release
+ }
+ diskwrite(0xbd,0); // clear misc. register
+}
+
+void CDiskopl::diskwrite(int reg, int val)
+{
+ fputc(val,f);
+ fputc(reg,f);
+}
diff --git a/plugins/adplug/adplug/diskopl.h b/plugins/adplug/adplug/diskopl.h
new file mode 100644
index 00000000..929f2906
--- /dev/null
+++ b/plugins/adplug/adplug/diskopl.h
@@ -0,0 +1,52 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * diskopl.h - Disk Writer OPL, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string>
+#include <stdio.h>
+#include "opl.h"
+#include "player.h"
+
+class CDiskopl: public Copl
+{
+ public:
+ CDiskopl(std::string filename);
+ virtual ~CDiskopl();
+
+ void update(CPlayer *p); // write to file
+ void setnowrite(bool nw = true) // set file write status
+ { nowrite = nw; };
+
+ void setchip(int n);
+
+ // template methods
+ void write(int reg, int val);
+ void init();
+
+ private:
+ static const unsigned char op_table[9];
+
+ FILE *f;
+ float old_freq;
+ unsigned char del;
+ bool nowrite; // don't write to file, if true
+
+ void diskwrite(int reg, int val);
+};
diff --git a/plugins/adplug/adplug/dmo.cpp b/plugins/adplug/adplug/dmo.cpp
new file mode 100644
index 00000000..c25293c4
--- /dev/null
+++ b/plugins/adplug/adplug/dmo.cpp
@@ -0,0 +1,403 @@
+/*
+ Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ Copyright (C) 1999 - 2004, 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ dmo.cpp - TwinTeam loader by Riven the Mage <riven@ok.ru>
+*/
+/*
+ NOTES:
+ Panning is ignored.
+
+ A WORD ist 16 bits, a DWORD is 32 bits and a BYTE is 8 bits in this context.
+*/
+
+#include <string.h>
+#include <binstr.h>
+
+#include "dmo.h"
+#include "debug.h"
+
+#define LOWORD(l) ((l) & 0xffff)
+#define HIWORD(l) ((l) >> 16)
+#define LOBYTE(w) ((w) & 0xff)
+#define HIBYTE(w) ((w) >> 8)
+
+#define ARRAY_AS_DWORD(a, i) \
+((a[i + 3] << 24) + (a[i + 2] << 16) + (a[i + 1] << 8) + a[i])
+#define ARRAY_AS_WORD(a, i) ((a[i + 1] << 8) + a[i])
+
+#define CHARP_AS_WORD(p) (((*(p + 1)) << 8) + (*p))
+
+/* -------- Public Methods -------------------------------- */
+
+CPlayer *CdmoLoader::factory(Copl *newopl)
+{
+ return new CdmoLoader(newopl);
+}
+
+bool CdmoLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ int i,j;
+ binistream *f;
+
+ // check header
+ dmo_unpacker *unpacker = new dmo_unpacker;
+ unsigned char chkhdr[16];
+
+ if(!fp.extension(filename, ".dmo")) return false;
+ f = fp.open(filename); if(!f) return false;
+
+ f->readString((char *)chkhdr, 16);
+
+ if (!unpacker->decrypt(chkhdr, 16))
+ {
+ delete unpacker;
+ fp.close(f);
+ return false;
+ }
+
+ // get file size
+ long packed_length = fp.filesize(f);
+ f->seek(0);
+
+ unsigned char *packed_module = new unsigned char [packed_length];
+
+ // load file
+ f->readString((char *)packed_module, packed_length);
+ fp.close(f);
+
+ // decrypt
+ unpacker->decrypt(packed_module,packed_length);
+
+ long unpacked_length = 0x2000 * ARRAY_AS_WORD(packed_module, 12);
+ unsigned char *module = new unsigned char [unpacked_length];
+
+ // unpack
+ if (!unpacker->unpack(packed_module+12,module,unpacked_length))
+ {
+ delete unpacker;
+ delete [] packed_module;
+ delete [] module;
+ return false;
+ }
+
+ delete unpacker;
+ delete [] packed_module;
+
+ // "TwinTeam" - signed ?
+ if (memcmp(module,"TwinTeam Module File""\x0D\x0A",22))
+ {
+ delete module;
+ return false;
+ }
+
+ // load header
+ binisstream uf(module, unpacked_length);
+ uf.setFlag(binio::BigEndian, false); uf.setFlag(binio::FloatIEEE);
+
+ memset(&header,0,sizeof(s3mheader));
+
+ uf.ignore(22); // ignore DMO header ID string
+ uf.readString(header.name, 28);
+
+ uf.ignore(2); // _unk_1
+ header.ordnum = uf.readInt(2);
+ header.insnum = uf.readInt(2);
+ header.patnum = uf.readInt(2);
+ uf.ignore(2); // _unk_2
+ header.is = uf.readInt(2);
+ header.it = uf.readInt(2);
+
+ memset(header.chanset,0xFF,32);
+
+ for (i=0;i<9;i++)
+ header.chanset[i] = 0x10 + i;
+
+ uf.ignore(32); // ignore panning settings for all 32 channels
+
+ // load orders
+ for(i = 0; i < 256; i++) orders[i] = uf.readInt(1);
+
+ orders[header.ordnum] = 0xFF;
+
+ // load pattern lengths
+ unsigned short my_patlen[100];
+ for(i = 0; i < 100; i++) my_patlen[i] = uf.readInt(2);
+
+ // load instruments
+ for (i = 0; i < header.insnum; i++)
+ {
+ memset(&inst[i],0,sizeof(s3minst));
+
+ uf.readString(inst[i].name, 28);
+
+ inst[i].volume = uf.readInt(1);
+ inst[i].dsk = uf.readInt(1);
+ inst[i].c2spd = uf.readInt(4);
+ inst[i].type = uf.readInt(1);
+ inst[i].d00 = uf.readInt(1);
+ inst[i].d01 = uf.readInt(1);
+ inst[i].d02 = uf.readInt(1);
+ inst[i].d03 = uf.readInt(1);
+ inst[i].d04 = uf.readInt(1);
+ inst[i].d05 = uf.readInt(1);
+ inst[i].d06 = uf.readInt(1);
+ inst[i].d07 = uf.readInt(1);
+ inst[i].d08 = uf.readInt(1);
+ inst[i].d09 = uf.readInt(1);
+ inst[i].d0a = uf.readInt(1);
+ /*
+ * Originally, riven sets d0b = d0a and ignores 1 byte in the
+ * stream, but i guess this was a typo, so i read it here.
+ */
+ inst[i].d0b = uf.readInt(1);
+ }
+
+ // load patterns
+ for (i = 0; i < header.patnum; i++) {
+ long cur_pos = uf.pos();
+
+ for (j = 0; j < 64; j++) {
+ while (1) {
+ unsigned char token = uf.readInt(1);
+
+ if (!token)
+ break;
+
+ unsigned char chan = token & 31;
+
+ // note + instrument ?
+ if (token & 32) {
+ unsigned char bufbyte = uf.readInt(1);
+
+ pattern[i][j][chan].note = bufbyte & 15;
+ pattern[i][j][chan].oct = bufbyte >> 4;
+ pattern[i][j][chan].instrument = uf.readInt(1);
+ }
+
+ // volume ?
+ if (token & 64)
+ pattern[i][j][chan].volume = uf.readInt(1);
+
+ // command ?
+ if (token & 128) {
+ pattern[i][j][chan].command = uf.readInt(1);
+ pattern[i][j][chan].info = uf.readInt(1);
+ }
+ }
+ }
+
+ uf.seek(cur_pos + my_patlen[i]);
+ }
+
+ delete [] module;
+ rewind(0);
+ return true;
+}
+
+std::string CdmoLoader::gettype()
+{
+ return std::string("TwinTeam (packed S3M)");
+}
+
+std::string CdmoLoader::getauthor()
+{
+ /*
+ All available .DMO modules written by one composer. And because all .DMO
+ stuff was lost due to hd crash (TwinTeam guys said this), there are
+ never(?) be another.
+ */
+ return std::string("Benjamin GERARDIN");
+}
+
+/* -------- Private Methods ------------------------------- */
+
+unsigned short CdmoLoader::dmo_unpacker::brand(unsigned short range)
+{
+ unsigned short ax,bx,cx,dx;
+
+ ax = LOWORD(bseed);
+ bx = HIWORD(bseed);
+ cx = ax;
+ ax = LOWORD(cx * 0x8405);
+ dx = HIWORD(cx * 0x8405);
+ cx <<= 3;
+ cx = (((HIBYTE(cx) + LOBYTE(cx)) & 0xFF) << 8) + LOBYTE(cx);
+ dx += cx;
+ dx += bx;
+ bx <<= 2;
+ dx += bx;
+ dx = (((HIBYTE(dx) + LOBYTE(bx)) & 0xFF) << 8) + LOBYTE(dx);
+ bx <<= 5;
+ dx = (((HIBYTE(dx) + LOBYTE(bx)) & 0xFF) << 8) + LOBYTE(dx);
+ ax += 1;
+ if (!ax) dx += 1;
+
+ // leave it that way or amd64 might get it wrong
+ bseed = dx;
+ bseed <<= 16;
+ bseed += ax;
+
+ return HIWORD(HIWORD(LOWORD(bseed) * range) + HIWORD(bseed) * range);
+}
+
+bool CdmoLoader::dmo_unpacker::decrypt(unsigned char *buf, long len)
+{
+ unsigned long seed = 0;
+ int i;
+
+ bseed = ARRAY_AS_DWORD(buf, 0);
+
+ for (i=0; i < ARRAY_AS_WORD(buf, 4) + 1; i++)
+ seed += brand(0xffff);
+
+ bseed = seed ^ ARRAY_AS_DWORD(buf, 6);
+
+ if (ARRAY_AS_WORD(buf, 10) != brand(0xffff))
+ return false;
+
+ for (i=0;i<(len-12);i++)
+ buf[12+i] ^= brand(0x100);
+
+ buf[len - 2] = buf[len - 1] = 0;
+
+ return true;
+}
+
+short CdmoLoader::dmo_unpacker::unpack_block(unsigned char *ibuf, long ilen, unsigned char *obuf)
+{
+ unsigned char code,par1,par2;
+ unsigned short ax,bx,cx;
+
+ unsigned char *ipos = ibuf;
+ unsigned char *opos = obuf;
+
+ // LZ77 child
+ while (ipos - ibuf < ilen)
+ {
+ code = *ipos++;
+
+ // 00xxxxxx: copy (xxxxxx + 1) bytes
+ if ((code >> 6) == 0)
+ {
+ cx = (code & 0x3F) + 1;
+
+ if(opos + cx >= oend)
+ return -1;
+
+ for (int i=0;i<cx;i++)
+ *opos++ = *ipos++;
+
+ continue;
+ }
+
+ // 01xxxxxx xxxyyyyy: copy (Y + 3) bytes from (X + 1)
+ if ((code >> 6) == 1)
+ {
+ par1 = *ipos++;
+
+ ax = ((code & 0x3F) << 3) + ((par1 & 0xE0) >> 5) + 1;
+ cx = (par1 & 0x1F) + 3;
+
+ if(opos + cx >= oend)
+ return -1;
+
+ for(int i=0;i<cx;i++)
+ *opos++ = *(opos - ax);
+
+ continue;
+ }
+
+ // 10xxxxxx xyyyzzzz: copy (Y + 3) bytes from (X + 1); copy Z bytes
+ if ((code >> 6) == 2)
+ {
+ int i;
+
+ par1 = *ipos++;
+
+ ax = ((code & 0x3F) << 1) + (par1 >> 7) + 1;
+ cx = ((par1 & 0x70) >> 4) + 3;
+ bx = par1 & 0x0F;
+
+ if(opos + bx + cx >= oend)
+ return -1;
+
+ for(i=0;i<cx;i++)
+ *opos++ = *(opos - ax);
+
+ for (i=0;i<bx;i++)
+ *opos++ = *ipos++;
+
+ continue;
+ }
+
+ // 11xxxxxx xxxxxxxy yyyyzzzz: copy (Y + 4) from X; copy Z bytes
+ if ((code >> 6) == 3)
+ {
+ int i;
+
+ par1 = *ipos++;
+ par2 = *ipos++;
+
+ bx = ((code & 0x3F) << 7) + (par1 >> 1);
+ cx = ((par1 & 0x01) << 4) + (par2 >> 4) + 4;
+ ax = par2 & 0x0F;
+
+ if(opos + ax + cx >= oend)
+ return -1;
+
+ for(i=0;i<cx;i++)
+ *opos++ = *(opos - bx);
+
+ for (i=0;i<ax;i++)
+ *opos++ = *ipos++;
+
+ continue;
+ }
+ }
+
+ return opos - obuf;
+}
+
+long CdmoLoader::dmo_unpacker::unpack(unsigned char *ibuf, unsigned char *obuf,
+ unsigned long outputsize)
+{
+ long olen = 0;
+ unsigned short block_count = CHARP_AS_WORD(ibuf);
+
+ ibuf += 2;
+ unsigned char *block_length = ibuf;
+ ibuf += 2 * block_count;
+
+ oend = obuf + outputsize;
+
+ for (int i=0;i<block_count;i++)
+ {
+ unsigned short bul = CHARP_AS_WORD(ibuf);
+
+ if(unpack_block(ibuf + 2,CHARP_AS_WORD(block_length) - 2,obuf) != bul)
+ return 0;
+
+ obuf += bul;
+ olen += bul;
+
+ ibuf += CHARP_AS_WORD(block_length);
+ block_length += 2;
+ }
+
+ return olen;
+}
diff --git a/plugins/adplug/adplug/dmo.h b/plugins/adplug/adplug/dmo.h
new file mode 100644
index 00000000..8591cfcc
--- /dev/null
+++ b/plugins/adplug/adplug/dmo.h
@@ -0,0 +1,51 @@
+/*
+ Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ dmo.cpp - TwinTeam loader by Riven the Mage <riven@ok.ru>
+*/
+
+#include "s3m.h"
+
+class CdmoLoader: public Cs3mPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ CdmoLoader(Copl *newopl) : Cs3mPlayer(newopl) { };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+
+ std::string gettype();
+ std::string getauthor();
+
+ private:
+
+ class dmo_unpacker {
+ public:
+ bool decrypt(unsigned char *buf, long len);
+ long unpack(unsigned char *ibuf, unsigned char *obuf,
+ unsigned long outputsize);
+
+ private:
+ unsigned short brand(unsigned short range);
+ short unpack_block(unsigned char *ibuf, long ilen, unsigned char *obuf);
+
+ unsigned long bseed;
+ unsigned char *oend;
+ };
+};
diff --git a/plugins/adplug/adplug/dro.cpp b/plugins/adplug/adplug/dro.cpp
new file mode 100644
index 00000000..eb97c0f8
--- /dev/null
+++ b/plugins/adplug/adplug/dro.cpp
@@ -0,0 +1,130 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * dro.c - DOSBox Raw OPL Player by Sjoerd van der Berg <harekiet@zophar.net>
+ *
+ * upgraded by matthew gambrell <zeromus@zeromus.org>
+ *
+ * NOTES: 3-oct-04: the DRO format is not yet finalized. beware.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "dro.h"
+
+/*** public methods *************************************/
+
+CPlayer *CdroPlayer::factory(Copl *newopl)
+{
+ return new CdroPlayer(newopl);
+}
+
+CdroPlayer::CdroPlayer(Copl *newopl)
+ : CPlayer(newopl), data(0)
+{
+ if(opl->gettype() == Copl::TYPE_OPL2)
+ opl3_mode = 0;
+ else
+ opl3_mode = 1;
+}
+
+bool CdroPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ char id[8];
+ unsigned long i;
+
+ // file validation section
+ f->readString(id, 8);
+ if(strncmp(id,"DBRAWOPL",8)) { fp.close (f); return false; }
+ int version = f->readInt(4); // not very useful just yet
+ if(version != 0x10000) { fp.close(f); return false; }
+
+ // load section
+ mstotal = f->readInt(4); // Total milliseconds in file
+ length = f->readInt(4); // Total data bytes in file
+ f->ignore(1); // Type of opl data this can contain - ignored
+ data = new unsigned char [length];
+ for (i=0;i<length;i++)
+ data[i]=f->readInt(1);
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+bool CdroPlayer::update()
+{
+ if (delay>500) {
+ delay-=500;
+ return true;
+ } else
+ delay=0;
+
+ while (pos < length) {
+ unsigned char cmd = data[pos++];
+ switch(cmd) {
+ case 0:
+ delay = 1 + data[pos++];
+ return true;
+ case 1:
+ delay = 1 + data[pos] + (data[pos+1]<<8);
+ pos+=2;
+ return true;
+ case 2:
+ index = 0;
+ opl->setchip(0);
+ break;
+ case 3:
+ index = 1;
+ opl->setchip(1);
+ break;
+ default:
+ if(cmd==4) cmd = data[pos++]; //data override
+ if(index == 0 || opl3_mode)
+ opl->write(cmd,data[pos++]);
+ break;
+ }
+ }
+
+ return pos<length;
+}
+
+void CdroPlayer::rewind(int subsong)
+{
+ delay=1;
+ pos = index = 0;
+ opl->init();
+
+ //dro assumes all registers are initialized to 0
+ //registers not initialized to 0 will be corrected
+ //in the data stream
+ for(int i=0;i<256;i++)
+ opl->write(i,0);
+
+ opl->setchip(1);
+ for(int i=0;i<256;i++)
+ opl->write(i,0);
+ opl->setchip(0);
+}
+
+float CdroPlayer::getrefresh()
+{
+ if (delay > 500) return 1000 / 500;
+ else return 1000 / (double)delay;
+}
diff --git a/plugins/adplug/adplug/dro.h b/plugins/adplug/adplug/dro.h
new file mode 100644
index 00000000..0f59e7b1
--- /dev/null
+++ b/plugins/adplug/adplug/dro.h
@@ -0,0 +1,52 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * dro.h - DOSBox Raw OPL Player by Sjoerd van der Berg <harekiet@zophar.net>
+ */
+
+#include "player.h"
+
+class CdroPlayer: public CPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ CdroPlayer(Copl *newopl);
+ ~CdroPlayer()
+ {
+ if(data)
+ delete [] data;
+ }
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype()
+ {
+ return std::string("DOSBox Raw OPL");
+ }
+
+ protected:
+ unsigned char *data;
+ unsigned long pos,length;
+ unsigned long msdone,mstotal;
+ unsigned short delay;
+ unsigned char index, opl3_mode;
+};
diff --git a/plugins/adplug/adplug/dtm.cpp b/plugins/adplug/adplug/dtm.cpp
new file mode 100644
index 00000000..3d022ae8
--- /dev/null
+++ b/plugins/adplug/adplug/dtm.cpp
@@ -0,0 +1,317 @@
+/*
+ Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ dtm.cpp - DTM loader by Riven the Mage <riven@ok.ru>
+*/
+/*
+ NOTE: Panning (Ex) effect is ignored.
+*/
+
+#include <string.h>
+#include "dtm.h"
+
+/* -------- Public Methods -------------------------------- */
+
+CPlayer *CdtmLoader::factory(Copl *newopl)
+{
+ return new CdtmLoader(newopl);
+}
+
+bool CdtmLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ const unsigned char conv_inst[11] = { 2,1,10,9,4,3,6,5,0,8,7 };
+ const unsigned short conv_note[12] = { 0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5, 0x202, 0x220, 0x241, 0x263, 0x287, 0x2AE };
+ int i,j,k,t=0;
+
+ // read header
+ f->readString(header.id, 12);
+ header.version = f->readInt(1);
+ f->readString(header.title, 20); f->readString(header.author, 20);
+ header.numpat = f->readInt(1); header.numinst = f->readInt(1);
+
+ // signature exists ? good version ?
+ if(memcmp(header.id,"DeFy DTM ",9) || header.version != 0x10)
+ { fp.close (f); return false; }
+
+ header.numinst++;
+
+ // load description
+ memset(desc,0,80*16);
+
+ char bufstr[80];
+
+ for (i=0;i<16;i++)
+ {
+ // get line length
+ unsigned char bufstr_length = f->readInt(1);
+
+ if(bufstr_length > 80) {
+ fp.close(f);
+ return false;
+ }
+
+ // read line
+ if (bufstr_length)
+ {
+ f->readString(bufstr,bufstr_length);
+
+ for (j=0;j<bufstr_length;j++)
+ if (!bufstr[j])
+ bufstr[j] = 0x20;
+
+ bufstr[bufstr_length] = 0;
+
+ strcat(desc,bufstr);
+ }
+
+ strcat(desc,"\n");
+ }
+
+ // init CmodPlayer
+ realloc_instruments(header.numinst);
+ realloc_order(100);
+ realloc_patterns(header.numpat,64,9);
+ init_notetable(conv_note);
+ init_trackord();
+
+ // load instruments
+ for (i=0;i<header.numinst;i++)
+ {
+ unsigned char name_length = f->readInt(1);
+
+ if (name_length)
+ f->readString(instruments[i].name, name_length);
+
+ instruments[i].name[name_length] = 0;
+
+ for(j = 0; j < 12; j++)
+ instruments[i].data[j] = f->readInt(1);
+
+ for (j=0;j<11;j++)
+ inst[i].data[conv_inst[j]] = instruments[i].data[j];
+ }
+
+ // load order
+ for(i = 0; i < 100; i++) order[i] = f->readInt(1);
+
+ nop = header.numpat;
+
+ unsigned char *pattern = new unsigned char [0x480];
+
+ // load tracks
+ for (i=0;i<nop;i++)
+ {
+ unsigned short packed_length;
+
+ packed_length = f->readInt(2);
+
+ unsigned char *packed_pattern = new unsigned char [packed_length];
+
+ for(j = 0; j < packed_length; j++)
+ packed_pattern[j] = f->readInt(1);
+
+ long unpacked_length = unpack_pattern(packed_pattern,packed_length,pattern,0x480);
+
+ delete [] packed_pattern;
+
+ if (!unpacked_length)
+ {
+ delete pattern;
+ fp.close(f);
+ return false;
+ }
+
+ // convert pattern
+ for (j=0;j<9;j++)
+ {
+ for (k=0;k<64;k++)
+ {
+ dtm_event *event = (dtm_event *)&pattern[(k*9+j)*2];
+
+ // instrument
+ if (event->byte0 == 0x80)
+ {
+ if (event->byte1 <= 0x80)
+ tracks[t][k].inst = event->byte1 + 1;
+ }
+
+ // note + effect
+ else
+ {
+ tracks[t][k].note = event->byte0;
+
+ if ((event->byte0 != 0) && (event->byte0 != 127))
+ tracks[t][k].note++;
+
+ // convert effects
+ switch (event->byte1 >> 4)
+ {
+ case 0x0: // pattern break
+ if ((event->byte1 & 15) == 1)
+ tracks[t][k].command = 13;
+ break;
+
+ case 0x1: // freq. slide up
+ tracks[t][k].command = 28;
+ tracks[t][k].param1 = event->byte1 & 15;
+ break;
+
+ case 0x2: // freq. slide down
+ tracks[t][k].command = 28;
+ tracks[t][k].param2 = event->byte1 & 15;
+ break;
+
+ case 0xA: // set carrier volume
+ case 0xC: // set instrument volume
+ tracks[t][k].command = 22;
+ tracks[t][k].param1 = (0x3F - (event->byte1 & 15)) >> 4;
+ tracks[t][k].param2 = (0x3F - (event->byte1 & 15)) & 15;
+ break;
+
+ case 0xB: // set modulator volume
+ tracks[t][k].command = 21;
+ tracks[t][k].param1 = (0x3F - (event->byte1 & 15)) >> 4;
+ tracks[t][k].param2 = (0x3F - (event->byte1 & 15)) & 15;
+ break;
+
+ case 0xE: // set panning
+ break;
+
+ case 0xF: // set speed
+ tracks[t][k].command = 13;
+ tracks[t][k].param2 = event->byte1 & 15;
+ break;
+ }
+ }
+ }
+
+ t++;
+ }
+ }
+
+ delete [] pattern;
+ fp.close(f);
+
+ // order length
+ for (i=0;i<100;i++)
+ {
+ if (order[i] >= 0x80)
+ {
+ length = i;
+
+ if (order[i] == 0xFF)
+ restartpos = 0;
+ else
+ restartpos = order[i] - 0x80;
+
+ break;
+ }
+ }
+
+ // initial speed
+ initspeed = 2;
+
+ rewind(0);
+
+ return true;
+}
+
+void CdtmLoader::rewind(int subsong)
+{
+ CmodPlayer::rewind(subsong);
+
+ // default instruments
+ for (int i=0;i<9;i++)
+ {
+ channel[i].inst = i;
+
+ channel[i].vol1 = 63 - (inst[i].data[10] & 63);
+ channel[i].vol2 = 63 - (inst[i].data[9] & 63);
+ }
+}
+
+float CdtmLoader::getrefresh()
+{
+ return 18.2f;
+}
+
+std::string CdtmLoader::gettype()
+{
+ return std::string("DeFy Adlib Tracker");
+}
+
+std::string CdtmLoader::gettitle()
+{
+ return std::string(header.title);
+}
+
+std::string CdtmLoader::getauthor()
+{
+ return std::string(header.author);
+}
+
+std::string CdtmLoader::getdesc()
+{
+ return std::string(desc);
+}
+
+std::string CdtmLoader::getinstrument(unsigned int n)
+{
+ return std::string(instruments[n].name);
+}
+
+unsigned int CdtmLoader::getinstruments()
+{
+ return header.numinst;
+}
+
+/* -------- Private Methods ------------------------------- */
+
+long CdtmLoader::unpack_pattern(unsigned char *ibuf, long ilen, unsigned char *obuf, long olen)
+{
+ unsigned char *input = ibuf;
+ unsigned char *output = obuf;
+
+ long input_length = 0;
+ long output_length = 0;
+
+ unsigned char repeat_byte, repeat_counter;
+
+ // RLE
+ while (input_length < ilen)
+ {
+ repeat_byte = input[input_length++];
+
+ if ((repeat_byte & 0xF0) == 0xD0)
+ {
+ repeat_counter = repeat_byte & 15;
+ repeat_byte = input[input_length++];
+ }
+ else
+ repeat_counter = 1;
+
+ for (int i=0;i<repeat_counter;i++)
+ {
+ if (output_length < olen)
+ output[output_length++] = repeat_byte;
+ }
+ }
+
+ return output_length;
+}
diff --git a/plugins/adplug/adplug/dtm.h b/plugins/adplug/adplug/dtm.h
new file mode 100644
index 00000000..8429bc4f
--- /dev/null
+++ b/plugins/adplug/adplug/dtm.h
@@ -0,0 +1,69 @@
+/*
+ Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ dtm.h - DTM loader by Riven the Mage <riven@ok.ru>
+*/
+
+#include "protrack.h"
+
+class CdtmLoader: public CmodPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ CdtmLoader(Copl *newopl) : CmodPlayer(newopl) { };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype();
+ std::string gettitle();
+ std::string getauthor();
+ std::string getdesc();
+ std::string getinstrument(unsigned int n);
+ unsigned int getinstruments();
+
+ private:
+
+ struct dtm_header
+ {
+ char id[12];
+ unsigned char version;
+ char title[20];
+ char author[20];
+ unsigned char numpat;
+ unsigned char numinst;
+ } header;
+
+ char desc[80*16];
+
+ struct dtm_instrument
+ {
+ char name[13];
+ unsigned char data[12];
+ } instruments[128];
+
+ struct dtm_event
+ {
+ unsigned char byte0;
+ unsigned char byte1;
+ };
+
+ long unpack_pattern(unsigned char *ibuf, long ilen, unsigned char *obuf, long olen);
+};
diff --git a/plugins/adplug/adplug/emuopl.cpp b/plugins/adplug/adplug/emuopl.cpp
new file mode 100644
index 00000000..0f751e16
--- /dev/null
+++ b/plugins/adplug/adplug/emuopl.cpp
@@ -0,0 +1,147 @@
+/*
+ * AdPlug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 Simon Peter <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * emuopl.cpp - Emulated OPL, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "emuopl.h"
+
+CEmuopl::CEmuopl(int rate, bool bit16, bool usestereo)
+ : use16bit(bit16), stereo(usestereo), mixbufSamples(0)
+{
+ opl[0] = OPLCreate(OPL_TYPE_YM3812, 3579545, rate);
+ opl[1] = OPLCreate(OPL_TYPE_YM3812, 3579545, rate);
+
+ currType = TYPE_DUAL_OPL2;
+
+ init();
+}
+
+CEmuopl::~CEmuopl()
+{
+ OPLDestroy(opl[0]); OPLDestroy(opl[1]);
+
+ if(mixbufSamples) {
+ delete [] mixbuf0;
+ delete [] mixbuf1;
+ }
+}
+
+void CEmuopl::update(short *buf, int samples)
+{
+ int i;
+
+ //ensure that our mix buffers are adequately sized
+ if(mixbufSamples < samples) {
+ if(mixbufSamples) { delete[] mixbuf0; delete[] mixbuf1; }
+ mixbufSamples = samples;
+
+ //*2 = make room for stereo, if we need it
+ mixbuf0 = new short[samples*2];
+ mixbuf1 = new short[samples*2];
+ }
+
+ //data should be rendered to outbuf
+ //tempbuf should be used as a temporary buffer
+ //if we are supposed to generate 16bit output,
+ //then outbuf may point directly to the actual waveform output "buf"
+ //if we are supposed to generate 8bit output,
+ //then outbuf cannot point to "buf" (because there will not be enough room)
+ //and so it must point to a mixbuf instead--
+ //it will be reduced to 8bit and put in "buf" later
+ short *outbuf;
+ short *tempbuf=mixbuf0;
+ short *tempbuf2=mixbuf1;
+ if(use16bit) outbuf = buf;
+ else outbuf = mixbuf1;
+ //...there is a potentially confusing situation where mixbuf1 can be aliased.
+ //beware. it is a little loony.
+
+ //all of the following rendering code produces 16bit output
+
+ switch(currType) {
+ case TYPE_OPL2:
+ //for opl2 mode:
+ //render chip0 to the output buffer
+ YM3812UpdateOne(opl[0],outbuf,samples);
+
+ //if we are supposed to output stereo,
+ //then we need to dup the mono channel
+ if(stereo)
+ for(i=samples-1;i>=0;i--) {
+ outbuf[i*2] = outbuf[i];
+ outbuf[i*2+1] = outbuf[i];
+ }
+ break;
+
+ case TYPE_OPL3: // unsupported
+ break;
+
+ case TYPE_DUAL_OPL2:
+ //for dual opl2 mode:
+ //render each chip to a different tempbuffer
+ YM3812UpdateOne(opl[0],tempbuf2,samples);
+ YM3812UpdateOne(opl[1],tempbuf,samples);
+
+ //output stereo:
+ //then we need to interleave the two buffers
+ if(stereo){
+ //first, spread tempbuf's samples across left channel
+ //left channel
+ for(i=0;i<samples;i++)
+ outbuf[i*2] = tempbuf2[i];
+ //next, insert the samples from tempbuf2 into right channel
+ for(i=0;i<samples;i++)
+ outbuf[i*2+1] = tempbuf[i];
+ } else
+ //output mono:
+ //then we need to mix the two buffers into buf
+ for(i=0;i<samples;i++)
+ outbuf[i] = (tempbuf[i]>>1) + (tempbuf2[i]>>1);
+ break;
+ }
+
+ //now reduce to 8bit if we need to
+ if(!use16bit)
+ for(i=0;i<(stereo ? samples*2 : samples);i++)
+ ((char *)buf)[i] = (outbuf[i] >> 8) ^ 0x80;
+}
+
+void CEmuopl::write(int reg, int val)
+{
+ switch(currType){
+ case TYPE_OPL2:
+ case TYPE_DUAL_OPL2:
+ OPLWrite(opl[currChip], 0, reg);
+ OPLWrite(opl[currChip], 1, val);
+ break;
+ case TYPE_OPL3: // unsupported
+ break;
+ }
+}
+
+void CEmuopl::init()
+{
+ OPLResetChip(opl[0]); OPLResetChip(opl[1]);
+ currChip = 0;
+}
+
+void CEmuopl::settype(ChipType type)
+{
+ currType = type;
+}
diff --git a/plugins/adplug/adplug/emuopl.h b/plugins/adplug/adplug/emuopl.h
new file mode 100644
index 00000000..7cd2479b
--- /dev/null
+++ b/plugins/adplug/adplug/emuopl.h
@@ -0,0 +1,49 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * emuopl.h - Emulated OPL, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_EMUOPL
+#define H_ADPLUG_EMUOPL
+
+#include "opl.h"
+extern "C" {
+#include "fmopl.h"
+}
+
+class CEmuopl: public Copl
+{
+ public:
+ CEmuopl(int rate, bool bit16, bool usestereo); // rate = sample rate
+ virtual ~CEmuopl();
+
+ void update(short *buf, int samples); // fill buffer
+ void write(int reg, int val);
+
+ void init();
+ void settype(ChipType type);
+
+ private:
+ bool use16bit, stereo;
+ FM_OPL *opl[2]; // OPL2 emulator data
+ short *mixbuf0, *mixbuf1;
+ int mixbufSamples;
+};
+
+#endif
diff --git a/plugins/adplug/adplug/flash.cpp b/plugins/adplug/adplug/flash.cpp
new file mode 100644
index 00000000..a6bf511c
--- /dev/null
+++ b/plugins/adplug/adplug/flash.cpp
@@ -0,0 +1,230 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * [xad] FLASH player, by Riven the Mage <riven@ok.ru>
+ */
+
+/*
+ - discovery -
+
+ file(s) : LA-INTRO.EXE
+ type : Lunatic Asylum BBStro
+ tune : by Rogue [Logic Design]
+ player : by Flash [Logic Design]
+*/
+
+#include "flash.h"
+#include "debug.h"
+
+const unsigned char CxadflashPlayer::flash_adlib_registers[99] =
+{
+ 0x23, 0x20, 0x43, 0x40, 0x63, 0x60, 0x83, 0x80, 0xC0, 0xE3, 0xE0,
+ 0x24, 0x21, 0x44, 0x41, 0x64, 0x61, 0x84, 0x81, 0xC1, 0xE4, 0xE1,
+ 0x25, 0x22, 0x45, 0x42, 0x65, 0x62, 0x85, 0x82, 0xC2, 0xE5, 0xE2,
+ 0x2B, 0x28, 0x4B, 0x48, 0x6B, 0x68, 0x8B, 0x88, 0xC3, 0xEB, 0xE8,
+ 0x2C, 0x29, 0x4C, 0x49, 0x6C, 0x69, 0x8C, 0x89, 0xC4, 0xEC, 0xE9,
+ 0x2D, 0x2A, 0x4D, 0x4A, 0x6D, 0x6A, 0x8D, 0x8A, 0xC5, 0xED, 0xEA,
+ 0x33, 0x30, 0x53, 0x50, 0x73, 0x70, 0x93, 0x90, 0xC6, 0xF3, 0xF0,
+ 0x34, 0x31, 0x54, 0x51, 0x74, 0x71, 0x94, 0x91, 0xC7, 0xF4, 0xF1,
+ 0x35, 0x32, 0x55, 0x52, 0x75, 0x72, 0x95, 0x92, 0xC8, 0xF5, 0xF2
+};
+
+const unsigned short CxadflashPlayer::flash_notes_encoded[268] =
+{
+ 0x000,
+ 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700, 0x800, 0x900, 0xA00, 0xB00, 0xC00,
+ 0x101, 0x201, 0x301, 0x401, 0x501, 0x601, 0x701, 0x801, 0x901, 0xA01, 0xB01, 0xC01,
+ 0x102, 0x202, 0x302, 0x402, 0x502, 0x602, 0x702, 0x802, 0x902, 0xA02, 0xB02, 0xC02,
+ 0x103, 0x203, 0x303, 0x403, 0x503, 0x603, 0x703, 0x803, 0x903, 0xA03, 0xB03, 0xC03,
+ 0x104, 0x204, 0x304, 0x404, 0x504, 0x604, 0x704, 0x804, 0x904, 0xA04, 0xB04, 0xC04,
+ 0x105, 0x205, 0x305, 0x405, 0x505, 0x605, 0x705, 0x805, 0x905, 0xA05, 0xB05, 0xC05,
+ 0x106, 0x206, 0x306, 0x406, 0x506, 0x606, 0x706, 0x806, 0x906, 0xA06, 0xB06, 0xC06,
+ 0x107, 0x207, 0x307, 0x407, 0x507, 0x607, 0x707, 0x807, 0x907, 0xA07, 0xB07, 0xC07,
+ 0x108, 0x208, 0x308, 0x408, 0x508, 0x608, 0x708, 0x808, 0x908, 0xA08, 0xB08, 0xC08,
+ 0x109, 0x209, 0x309, 0x409, 0x509, 0x609, 0x709, 0x809, 0x909, 0xA09, 0xB09, 0xC09,
+ 0x10A, 0x20A, 0x30A, 0x40A, 0x50A, 0x60A, 0x70A, 0x80A, 0x90A, 0xA0A, 0xB0A, 0xC0A,
+ 0x10B, 0x20B, 0x30B, 0x40B, 0x50B, 0x60B, 0x70B, 0x80B, 0x90B, 0xA0B, 0xB0B, 0xC0B,
+ 0x10C, 0x20C, 0x30C, 0x40C, 0x50C, 0x60C, 0x70C, 0x80C, 0x90C, 0xA0C, 0xB0C, 0xC0C,
+ 0x10D, 0x20D, 0x30D, 0x40D, 0x50D, 0x60D, 0x70D, 0x80D, 0x90D, 0xA0D, 0xB0D, 0xC0D,
+ 0x10E, 0x20E, 0x30E, 0x40E, 0x50E, 0x60E, 0x70E, 0x80E, 0x90E, 0xA0E, 0xB0E, 0xC0E,
+ 0x10F, 0x20F, 0x30F, 0x40F, 0x50F, 0x60F, 0x70F, 0x80F, 0x90F, 0xA0F, 0xB0F, 0xC0F,
+ 0x110, 0x210, 0x310, 0x410, 0x510, 0x610, 0x710, 0x810, 0x910, 0xA10, 0xB10, 0xC10,
+ 0x111, 0x211, 0x311, 0x411, 0x511, 0x611, 0x711, 0x811, 0x911, 0xA11, 0xB11, 0xC11,
+ 0x112, 0x212, 0x312, 0x412, 0x512, 0x612, 0x712, 0x812, 0x912, 0xA12, 0xB12, 0xC12,
+ 0x113, 0x213, 0x313, 0x413, 0x513, 0x613, 0x713, 0x813, 0x913, 0xA13, 0xB13, 0xC13,
+ 0x114, 0x214, 0x314, 0x414, 0x514, 0x614, 0x714, 0x814, 0x914, 0xA14, 0xB14, 0xC14,
+ 0x115, 0x215, 0x315
+};
+
+const unsigned short CxadflashPlayer::flash_notes[12] =
+{
+ 0x157, 0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5, 0x202, 0x220, 0x241, 0x263, 0x287
+};
+
+const unsigned char CxadflashPlayer::flash_default_instrument[8] =
+{
+ 0x00, 0x00, 0x3F, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+CPlayer *CxadflashPlayer::factory(Copl *newopl)
+{
+ return new CxadflashPlayer(newopl);
+}
+
+void CxadflashPlayer::xadplayer_rewind(int subsong)
+{
+ int i;
+
+ plr.speed = xad.speed;
+
+ flash.order_pos = 0;
+ flash.pattern_pos = 0;
+
+ opl_write(0x08, 0x00);
+ opl_write(0xBD, 0x00);
+
+ // assign default instrument
+ for(i=0; i<9; i++)
+ {
+ opl_write(0xA0+i, 0x00);
+ opl_write(0xB0+i, 0x00);
+ }
+
+ // assign instruments
+ for(i=0; i<9; i++)
+ for(int j=0; j<11; j++)
+ opl_write(flash_adlib_registers[i*11+j], tune[i*12+j]);
+}
+
+void CxadflashPlayer::xadplayer_update()
+{
+ unsigned short event_pos = (tune[0x600+flash.order_pos]*1152) + \
+ (flash.pattern_pos*18) + \
+ 0x633;
+
+ for (int i=0; i<9; i++)
+ {
+ unsigned short flash_channel_freq = (adlib[0xB0+i] << 8) + adlib[0xA0+i];
+
+ unsigned char event_b0 = tune[event_pos++];
+ unsigned char event_b1 = tune[event_pos++];
+#ifdef DEBUG
+ AdPlug_LogWrite("channel %02X, event %02X %02X:\n",i+1,event_b0,event_b1);
+#endif
+
+ if (event_b0 == 0x80) // 0.0x80: Set Instrument
+ {
+ for(int j=0; j<11; j++)
+ opl_write(flash_adlib_registers[i*11+j], tune[event_b1*12+j]);
+ }
+ else
+ {
+ if (event_b1 == 0x01)
+ flash.pattern_pos = 0x3F; // 1.0x01: Pattern Break
+
+ unsigned char fx = (event_b1 >> 4);
+ unsigned char fx_p = (event_b1 & 0x0F);
+
+ switch(fx)
+ {
+ case 0x0A: // 1.0xAy: Set Carrier volume
+ opl_write(flash_adlib_registers[11*i+2], fx_p << 2);
+ break;
+ case 0x0B: // 1.0xBy: Set Modulator volume
+ opl_write(flash_adlib_registers[11*i+3], fx_p << 2);
+ break;
+ case 0x0C: // 1.0xCy: Set both operators volume
+ opl_write(flash_adlib_registers[11*i+2], fx_p << 2);
+ opl_write(flash_adlib_registers[11*i+3], fx_p << 2);
+ break;
+// case 0x0E: // 1.0xEy: ? (increase some value)
+ case 0x0F: // 1.0xFy: Set Speed
+ plr.speed = (fx_p + 1);
+ break;
+ }
+
+ if (event_b0)
+ {
+ // mute channel
+ opl_write(0xA0+i, adlib[0xA0+i]);
+ opl_write(0xB0+i, adlib[0xB0+i] & 0xDF);
+
+ // is note ?
+ if (event_b0 != 0x7F)
+ {
+ unsigned short note_encoded = flash_notes_encoded[event_b0];
+ unsigned short freq = flash_notes[(note_encoded >> 8) - 1];
+
+ flash_channel_freq = freq | ((note_encoded & 0xFF) << 10) | 0x2000;
+
+ opl_write(0xA0+i, flash_channel_freq & 0xFF);
+ opl_write(0xB0+i, flash_channel_freq >> 8);
+ }
+ }
+
+ if (fx == 0x01) // 1.0x1y: Fine Frequency Slide Up
+ {
+ flash_channel_freq += (fx_p << 1);
+
+ opl_write(0xA0+i, flash_channel_freq & 0xFF);
+ opl_write(0xB0+i, flash_channel_freq >> 8);
+ }
+ else if (fx == 0x02) // 1.0x2y: Fine Frequency Slide Down
+ {
+ flash_channel_freq -= (fx_p << 1);
+
+ opl_write(0xA0+i, flash_channel_freq & 0xFF);
+ opl_write(0xB0+i, flash_channel_freq >> 8);
+ }
+ }
+ }
+
+ // next row
+ flash.pattern_pos++;
+
+ // end of pattern ?
+ if (flash.pattern_pos >= 0x40)
+ {
+ flash.pattern_pos = 0;
+
+ flash.order_pos++;
+
+ // end of module ?
+ if (tune[0x600+flash.order_pos] == 0xFF)
+ {
+ flash.order_pos = 0;
+
+ plr.looping = 1;
+ }
+ }
+}
+
+float CxadflashPlayer::xadplayer_getrefresh()
+{
+ return 17.5f;
+}
+
+std::string CxadflashPlayer::xadplayer_gettype()
+{
+ return std::string("xad: flash player");
+}
+
+unsigned int CxadflashPlayer::xadplayer_getinstruments()
+{
+ return 32;
+}
diff --git a/plugins/adplug/adplug/flash.h b/plugins/adplug/adplug/flash.h
new file mode 100644
index 00000000..a3471df4
--- /dev/null
+++ b/plugins/adplug/adplug/flash.h
@@ -0,0 +1,57 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * [xad] FLASH player, by Riven the Mage <riven@ok.ru>
+ */
+
+#include "xad.h"
+
+class CxadflashPlayer: public CxadPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CxadflashPlayer(Copl *newopl): CxadPlayer(newopl)
+ { };
+
+protected:
+ struct
+ {
+ unsigned char order_pos;
+ unsigned char pattern_pos;
+ } flash;
+ //
+ bool xadplayer_load()
+ {
+ if(xad.fmt == FLASH)
+ return true;
+ else
+ return false;
+ }
+ void xadplayer_rewind(int subsong);
+ void xadplayer_update();
+ float xadplayer_getrefresh();
+ std::string xadplayer_gettype();
+ unsigned int xadplayer_getinstruments();
+
+private:
+ static const unsigned char flash_adlib_registers[99];
+ static const unsigned short flash_notes_encoded[268];
+ static const unsigned short flash_notes[12];
+ static const unsigned char flash_default_instrument[8];
+};
diff --git a/plugins/adplug/adplug/fmc.cpp b/plugins/adplug/adplug/fmc.cpp
new file mode 100644
index 00000000..c6839a61
--- /dev/null
+++ b/plugins/adplug/adplug/fmc.cpp
@@ -0,0 +1,224 @@
+/*
+ Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ Copyright (C) 1999 - 2007 Simon Peter <dn.tlp@gmx.net>, et al.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ fmc.cpp - FMC Loader by Riven the Mage <riven@ok.ru>
+*/
+
+#include <string.h>
+#include "fmc.h"
+
+/* -------- Public Methods -------------------------------- */
+
+CPlayer *CfmcLoader::factory(Copl *newopl)
+{
+ return new CfmcLoader(newopl);
+}
+
+bool CfmcLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ const unsigned char conv_fx[16] = {0,1,2,3,4,8,255,255,255,255,26,11,12,13,14,15};
+
+ int i,j,k,t=0;
+
+ // read header
+ f->readString(header.id, 4);
+ f->readString(header.title, 21);
+ header.numchan = f->readInt(1);
+
+ // 'FMC!' - signed ?
+ if (strncmp(header.id,"FMC!",4)) { fp.close(f); return false; }
+
+ // init CmodPlayer
+ realloc_instruments(32);
+ realloc_order(256);
+ realloc_patterns(64,64,header.numchan);
+ init_trackord();
+
+ // load order
+ for(i = 0; i < 256; i++) order[i] = f->readInt(1);
+
+ f->ignore(2);
+
+ // load instruments
+ for(i = 0; i < 32; i++) {
+ instruments[i].synthesis = f->readInt(1);
+ instruments[i].feedback = f->readInt(1);
+
+ instruments[i].mod_attack = f->readInt(1);
+ instruments[i].mod_decay = f->readInt(1);
+ instruments[i].mod_sustain = f->readInt(1);
+ instruments[i].mod_release = f->readInt(1);
+ instruments[i].mod_volume = f->readInt(1);
+ instruments[i].mod_ksl = f->readInt(1);
+ instruments[i].mod_freq_multi = f->readInt(1);
+ instruments[i].mod_waveform = f->readInt(1);
+ instruments[i].mod_sustain_sound = f->readInt(1);
+ instruments[i].mod_ksr = f->readInt(1);
+ instruments[i].mod_vibrato = f->readInt(1);
+ instruments[i].mod_tremolo = f->readInt(1);
+
+ instruments[i].car_attack = f->readInt(1);
+ instruments[i].car_decay = f->readInt(1);
+ instruments[i].car_sustain = f->readInt(1);
+ instruments[i].car_release = f->readInt(1);
+ instruments[i].car_volume = f->readInt(1);
+ instruments[i].car_ksl = f->readInt(1);
+ instruments[i].car_freq_multi = f->readInt(1);
+ instruments[i].car_waveform = f->readInt(1);
+ instruments[i].car_sustain_sound = f->readInt(1);
+ instruments[i].car_ksr = f->readInt(1);
+ instruments[i].car_vibrato = f->readInt(1);
+ instruments[i].car_tremolo = f->readInt(1);
+
+ instruments[i].pitch_shift = f->readInt(1);
+
+ f->readString(instruments[i].name, 21);
+ }
+
+ // load tracks
+ for (i=0;i<64;i++)
+ {
+ if(f->ateof()) break;
+
+ for (j=0;j<header.numchan;j++)
+ {
+ for (k=0;k<64;k++)
+ {
+ fmc_event event;
+
+ // read event
+ event.byte0 = f->readInt(1);
+ event.byte1 = f->readInt(1);
+ event.byte2 = f->readInt(1);
+
+ // convert event
+ tracks[t][k].note = event.byte0 & 0x7F;
+ tracks[t][k].inst = ((event.byte0 & 0x80) >> 3) + (event.byte1 >> 4) + 1;
+ tracks[t][k].command = conv_fx[event.byte1 & 0x0F];
+ tracks[t][k].param1 = event.byte2 >> 4;
+ tracks[t][k].param2 = event.byte2 & 0x0F;
+
+ // fix effects
+ if (tracks[t][k].command == 0x0E) // 0x0E (14): Retrig
+ tracks[t][k].param1 = 3;
+ if (tracks[t][k].command == 0x1A) // 0x1A (26): Volume Slide
+ if (tracks[t][k].param1 > tracks[t][k].param2)
+ {
+ tracks[t][k].param1 -= tracks[t][k].param2;
+ tracks[t][k].param2 = 0;
+ }
+ else
+ {
+ tracks[t][k].param2 -= tracks[t][k].param1;
+ tracks[t][k].param1 = 0;
+ }
+ }
+
+ t++;
+ }
+ }
+ fp.close(f);
+
+ // convert instruments
+ for (i=0;i<31;i++)
+ buildinst(i);
+
+ // order length
+ for (i=0;i<256;i++)
+ {
+ if (order[i] >= 0xFE)
+ {
+ length = i;
+ break;
+ }
+ }
+
+ // data for Protracker
+ activechan = (0xffffffff >> (32 - header.numchan)) << (32 - header.numchan);
+ nop = t / header.numchan;
+ restartpos = 0;
+
+ // flags
+ flags = Faust;
+
+ rewind(0);
+
+ return true;
+}
+
+float CfmcLoader::getrefresh()
+{
+ return 50.0f;
+}
+
+std::string CfmcLoader::gettype()
+{
+ return std::string("Faust Music Creator");
+}
+
+std::string CfmcLoader::gettitle()
+{
+ return std::string(header.title);
+}
+
+std::string CfmcLoader::getinstrument(unsigned int n)
+{
+ return std::string(instruments[n].name);
+}
+
+unsigned int CfmcLoader::getinstruments()
+{
+ return 32;
+}
+
+/* -------- Private Methods ------------------------------- */
+
+void CfmcLoader::buildinst(unsigned char i)
+{
+ inst[i].data[0] = ((instruments[i].synthesis & 1) ^ 1);
+ inst[i].data[0] |= ((instruments[i].feedback & 7) << 1);
+
+ inst[i].data[3] = ((instruments[i].mod_attack & 15) << 4);
+ inst[i].data[3] |= (instruments[i].mod_decay & 15);
+ inst[i].data[5] = ((15 - (instruments[i].mod_sustain & 15)) << 4);
+ inst[i].data[5] |= (instruments[i].mod_release & 15);
+ inst[i].data[9] = (63 - (instruments[i].mod_volume & 63));
+ inst[i].data[9] |= ((instruments[i].mod_ksl & 3) << 6);
+ inst[i].data[1] = (instruments[i].mod_freq_multi & 15);
+ inst[i].data[7] = (instruments[i].mod_waveform & 3);
+ inst[i].data[1] |= ((instruments[i].mod_sustain_sound & 1) << 5);
+ inst[i].data[1] |= ((instruments[i].mod_ksr & 1) << 4);
+ inst[i].data[1] |= ((instruments[i].mod_vibrato & 1) << 6);
+ inst[i].data[1] |= ((instruments[i].mod_tremolo & 1) << 7);
+
+ inst[i].data[4] = ((instruments[i].car_attack & 15) << 4);
+ inst[i].data[4] |= (instruments[i].car_decay & 15);
+ inst[i].data[6] = ((15 - (instruments[i].car_sustain & 15)) << 4);
+ inst[i].data[6] |= (instruments[i].car_release & 15);
+ inst[i].data[10] = (63 - (instruments[i].car_volume & 63));
+ inst[i].data[10] |= ((instruments[i].car_ksl & 3) << 6);
+ inst[i].data[2] = (instruments[i].car_freq_multi & 15);
+ inst[i].data[8] = (instruments[i].car_waveform & 3);
+ inst[i].data[2] |= ((instruments[i].car_sustain_sound & 1) << 5);
+ inst[i].data[2] |= ((instruments[i].car_ksr & 1) << 4);
+ inst[i].data[2] |= ((instruments[i].car_vibrato & 1) << 6);
+ inst[i].data[2] |= ((instruments[i].car_tremolo & 1) << 7);
+
+ inst[i].slide = instruments[i].pitch_shift;
+}
diff --git a/plugins/adplug/adplug/fmc.h b/plugins/adplug/adplug/fmc.h
new file mode 100644
index 00000000..ae7d5c5b
--- /dev/null
+++ b/plugins/adplug/adplug/fmc.h
@@ -0,0 +1,91 @@
+/*
+ Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ fmc.h - FMC loader by Riven the Mage <riven@ok.ru>
+*/
+
+#include "protrack.h"
+
+class CfmcLoader: public CmodPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ CfmcLoader(Copl *newopl) : CmodPlayer(newopl) { };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ float getrefresh();
+
+ std::string gettype();
+ std::string gettitle();
+ std::string getinstrument(unsigned int n);
+ unsigned int getinstruments();
+
+ private:
+
+ struct fmc_event
+ {
+ unsigned char byte0;
+ unsigned char byte1;
+ unsigned char byte2;
+ };
+
+ struct fmc_header
+ {
+ char id[4];
+ char title[21];
+ unsigned char numchan;
+ } header;
+
+ struct fmc_instrument
+ {
+ unsigned char synthesis;
+ unsigned char feedback;
+
+ unsigned char mod_attack;
+ unsigned char mod_decay;
+ unsigned char mod_sustain;
+ unsigned char mod_release;
+ unsigned char mod_volume;
+ unsigned char mod_ksl;
+ unsigned char mod_freq_multi;
+ unsigned char mod_waveform;
+ unsigned char mod_sustain_sound;
+ unsigned char mod_ksr;
+ unsigned char mod_vibrato;
+ unsigned char mod_tremolo;
+ unsigned char car_attack;
+ unsigned char car_decay;
+ unsigned char car_sustain;
+ unsigned char car_release;
+ unsigned char car_volume;
+ unsigned char car_ksl;
+ unsigned char car_freq_multi;
+ unsigned char car_waveform;
+ unsigned char car_sustain_sound;
+ unsigned char car_ksr;
+ unsigned char car_vibrato;
+ unsigned char car_tremolo;
+
+ signed char pitch_shift;
+
+ char name[21];
+ } instruments[32];
+
+ void buildinst(unsigned char i);
+};
diff --git a/plugins/adplug/adplug/fmopl.c b/plugins/adplug/adplug/fmopl.c
new file mode 100644
index 00000000..2b0e82b0
--- /dev/null
+++ b/plugins/adplug/adplug/fmopl.c
@@ -0,0 +1,1390 @@
+/*
+**
+** File: fmopl.c -- software implementation of FM sound generator
+**
+** Copyright (C) 1999,2000 Tatsuyuki Satoh , MultiArcadeMachineEmurator development
+**
+** Version 0.37a
+**
+*/
+
+/*
+ preliminary :
+ Problem :
+ note:
+*/
+
+/* This version of fmopl.c is a fork of the MAME one, relicensed under the LGPL.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define INLINE __inline
+#define HAS_YM3812 1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <math.h>
+//#include "driver.h" /* use M.A.M.E. */
+#include "fmopl.h"
+
+#ifndef PI
+#define PI 3.14159265358979323846
+#endif
+
+/* -------------------- for debug --------------------- */
+/* #define OPL_OUTPUT_LOG */
+#ifdef OPL_OUTPUT_LOG
+static FILE *opl_dbg_fp = NULL;
+static FM_OPL *opl_dbg_opl[16];
+static int opl_dbg_maxchip,opl_dbg_chip;
+#endif
+
+/* -------------------- preliminary define section --------------------- */
+/* attack/decay rate time rate */
+#define OPL_ARRATE 141280 /* RATE 4 = 2826.24ms @ 3.6MHz */
+#define OPL_DRRATE 1956000 /* RATE 4 = 39280.64ms @ 3.6MHz */
+
+#define DELTAT_MIXING_LEVEL (1) /* DELTA-T ADPCM MIXING LEVEL */
+
+#define FREQ_BITS 24 /* frequency turn */
+
+/* counter bits = 20 , octerve 7 */
+#define FREQ_RATE (1<<(FREQ_BITS-20))
+#define TL_BITS (FREQ_BITS+2)
+
+/* final output shift , limit minimum and maximum */
+#define OPL_OUTSB (TL_BITS+3-16) /* OPL output final shift 16bit */
+#define OPL_MAXOUT (0x7fff<<OPL_OUTSB)
+#define OPL_MINOUT (-0x8000<<OPL_OUTSB)
+
+/* -------------------- quality selection --------------------- */
+
+/* sinwave entries */
+/* used static memory = SIN_ENT * 4 (byte) */
+#define SIN_ENT 2048
+
+/* output level entries (envelope,sinwave) */
+/* envelope counter lower bits */
+#define ENV_BITS 16
+/* envelope output entries */
+#define EG_ENT 4096
+/* used dynamic memory = EG_ENT*4*4(byte)or EG_ENT*6*4(byte) */
+/* used static memory = EG_ENT*4 (byte) */
+
+#define EG_OFF ((2*EG_ENT)<<ENV_BITS) /* OFF */
+#define EG_DED EG_OFF
+#define EG_DST (EG_ENT<<ENV_BITS) /* DECAY START */
+#define EG_AED EG_DST
+#define EG_AST 0 /* ATTACK START */
+
+#define EG_STEP (96.0/EG_ENT) /* OPL is 0.1875 dB step */
+
+/* LFO table entries */
+#define VIB_ENT 512
+#define VIB_SHIFT (32-9)
+#define AMS_ENT 512
+#define AMS_SHIFT (32-9)
+
+#define VIB_RATE 256
+
+/* -------------------- local defines , macros --------------------- */
+
+/* register number to channel number , slot offset */
+#define SLOT1 0
+#define SLOT2 1
+
+/* envelope phase */
+#define ENV_MOD_RR 0x00
+#define ENV_MOD_DR 0x01
+#define ENV_MOD_AR 0x02
+
+/* -------------------- tables --------------------- */
+static const int slot_array[32]=
+{
+ 0, 2, 4, 1, 3, 5,-1,-1,
+ 6, 8,10, 7, 9,11,-1,-1,
+ 12,14,16,13,15,17,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1
+};
+
+/* key scale level */
+/* table is 3dB/OCT , DV converts this in TL step at 6dB/OCT */
+#define DV (EG_STEP/2)
+static const UINT32 KSL_TABLE[8*16]=
+{
+ /* OCT 0 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ /* OCT 1 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV,
+ 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV,
+ /* OCT 2 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV,
+ 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV,
+ 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV,
+ /* OCT 3 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV,
+ 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV,
+ 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV,
+ 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV,
+ /* OCT 4 */
+ 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV,
+ 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV,
+ 9.000/DV, 9.750/DV,10.125/DV,10.500/DV,
+ 10.875/DV,11.250/DV,11.625/DV,12.000/DV,
+ /* OCT 5 */
+ 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV,
+ 9.000/DV,10.125/DV,10.875/DV,11.625/DV,
+ 12.000/DV,12.750/DV,13.125/DV,13.500/DV,
+ 13.875/DV,14.250/DV,14.625/DV,15.000/DV,
+ /* OCT 6 */
+ 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV,
+ 12.000/DV,13.125/DV,13.875/DV,14.625/DV,
+ 15.000/DV,15.750/DV,16.125/DV,16.500/DV,
+ 16.875/DV,17.250/DV,17.625/DV,18.000/DV,
+ /* OCT 7 */
+ 0.000/DV, 9.000/DV,12.000/DV,13.875/DV,
+ 15.000/DV,16.125/DV,16.875/DV,17.625/DV,
+ 18.000/DV,18.750/DV,19.125/DV,19.500/DV,
+ 19.875/DV,20.250/DV,20.625/DV,21.000/DV
+};
+#undef DV
+
+/* sustain lebel table (3db per step) */
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
+#define SC(db) (db*((3/EG_STEP)*(1<<ENV_BITS)))+EG_DST
+static const INT32 SL_TABLE[16]={
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
+};
+#undef SC
+
+#define TL_MAX (EG_ENT*2) /* limit(tl + ksr + envelope) + sinwave */
+/* TotalLevel : 48 24 12 6 3 1.5 0.75 (dB) */
+/* TL_TABLE[ 0 to TL_MAX ] : plus section */
+/* TL_TABLE[ TL_MAX to TL_MAX+TL_MAX-1 ] : minus section */
+static INT32 *TL_TABLE;
+
+/* pointers to TL_TABLE with sinwave output offset */
+static INT32 **SIN_TABLE;
+
+/* LFO table */
+static INT32 *AMS_TABLE;
+static INT32 *VIB_TABLE;
+
+/* envelope output curve table */
+/* attack + decay + OFF */
+static INT32 ENV_CURVE[2*EG_ENT+1];
+
+/* multiple table */
+#define ML 2
+static const UINT32 MUL_TABLE[16]= {
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 */
+ 0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML,
+ 8.00*ML, 9.00*ML,10.00*ML,10.00*ML,12.00*ML,12.00*ML,15.00*ML,15.00*ML
+};
+#undef ML
+
+/* dummy attack / decay rate ( when rate == 0 ) */
+static INT32 RATE_0[16]=
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+/* -------------------- static state --------------------- */
+
+/* lock level of common table */
+static int num_lock = 0;
+
+/* work table */
+static void *cur_chip = NULL; /* current chip point */
+/* currenct chip state */
+/* static OPLSAMPLE *bufL,*bufR; */
+static OPL_CH *S_CH;
+static OPL_CH *E_CH;
+OPL_SLOT *SLOT7_1,*SLOT7_2,*SLOT8_1,*SLOT8_2;
+
+static INT32 outd[1];
+static INT32 ams;
+static INT32 vib;
+INT32 *ams_table;
+INT32 *vib_table;
+static INT32 amsIncr;
+static INT32 vibIncr;
+static INT32 feedback2; /* connect for SLOT 2 */
+
+/* log output level */
+#define LOG_ERR 3 /* ERROR */
+#define LOG_WAR 2 /* WARNING */
+#define LOG_INF 1 /* INFORMATION */
+
+//#define LOG_LEVEL LOG_INF
+#define LOG_LEVEL LOG_ERR
+
+//#define LOG(n,x) if( (n)>=LOG_LEVEL ) logerror x
+#define LOG(n,x)
+
+/* --------------------- subroutines --------------------- */
+
+INLINE int Limit( int val, int max, int min ) {
+ if ( val > max )
+ val = max;
+ else if ( val < min )
+ val = min;
+
+ return val;
+}
+
+/* status set and IRQ handling */
+INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag)
+{
+ /* set status flag */
+ OPL->status |= flag;
+ if(!(OPL->status & 0x80))
+ {
+ if(OPL->status & OPL->statusmask)
+ { /* IRQ on */
+ OPL->status |= 0x80;
+ /* callback user interrupt handler (IRQ is OFF to ON) */
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1);
+ }
+ }
+}
+
+/* status reset and IRQ handling */
+INLINE void OPL_STATUS_RESET(FM_OPL *OPL,int flag)
+{
+ /* reset status flag */
+ OPL->status &=~flag;
+ if((OPL->status & 0x80))
+ {
+ if (!(OPL->status & OPL->statusmask) )
+ {
+ OPL->status &= 0x7f;
+ /* callback user interrupt handler (IRQ is ON to OFF) */
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0);
+ }
+ }
+}
+
+/* IRQ mask set */
+INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag)
+{
+ OPL->statusmask = flag;
+ /* IRQ handling check */
+ OPL_STATUS_SET(OPL,0);
+ OPL_STATUS_RESET(OPL,0);
+}
+
+/* ----- key on ----- */
+INLINE void OPL_KEYON(OPL_SLOT *SLOT)
+{
+ /* sin wave restart */
+ SLOT->Cnt = 0;
+ /* set attack */
+ SLOT->evm = ENV_MOD_AR;
+ SLOT->evs = SLOT->evsa;
+ SLOT->evc = EG_AST;
+ SLOT->eve = EG_AED;
+}
+/* ----- key off ----- */
+INLINE void OPL_KEYOFF(OPL_SLOT *SLOT)
+{
+ if( SLOT->evm > ENV_MOD_RR)
+ {
+ /* set envelope counter from envleope output */
+ SLOT->evm = ENV_MOD_RR;
+ if( !(SLOT->evc&EG_DST) )
+ //SLOT->evc = (ENV_CURVE[SLOT->evc>>ENV_BITS]<<ENV_BITS) + EG_DST;
+ SLOT->evc = EG_DST;
+ SLOT->eve = EG_DED;
+ SLOT->evs = SLOT->evsr;
+ }
+}
+
+/* ---------- calcrate Envelope Generator & Phase Generator ---------- */
+/* return : envelope output */
+INLINE UINT32 OPL_CALC_SLOT( OPL_SLOT *SLOT )
+{
+ /* calcrate envelope generator */
+ if( (SLOT->evc+=SLOT->evs) >= SLOT->eve )
+ {
+ switch( SLOT->evm ){
+ case ENV_MOD_AR: /* ATTACK -> DECAY1 */
+ /* next DR */
+ SLOT->evm = ENV_MOD_DR;
+ SLOT->evc = EG_DST;
+ SLOT->eve = SLOT->SL;
+ SLOT->evs = SLOT->evsd;
+ break;
+ case ENV_MOD_DR: /* DECAY -> SL or RR */
+ SLOT->evc = SLOT->SL;
+ SLOT->eve = EG_DED;
+ if(SLOT->eg_typ)
+ {
+ SLOT->evs = 0;
+ }
+ else
+ {
+ SLOT->evm = ENV_MOD_RR;
+ SLOT->evs = SLOT->evsr;
+ }
+ break;
+ case ENV_MOD_RR: /* RR -> OFF */
+ SLOT->evc = EG_OFF;
+ SLOT->eve = EG_OFF+1;
+ SLOT->evs = 0;
+ break;
+ }
+ }
+ /* calcrate envelope */
+ return SLOT->TLL+ENV_CURVE[SLOT->evc>>ENV_BITS]+(SLOT->ams ? ams : 0);
+}
+
+/* set algorythm connection */
+static void set_algorythm( OPL_CH *CH)
+{
+ INT32 *carrier = &outd[0];
+ CH->connect1 = CH->CON ? carrier : &feedback2;
+ CH->connect2 = carrier;
+}
+
+/* ---------- frequency counter for operater update ---------- */
+INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT)
+{
+ int ksr;
+
+ /* frequency step counter */
+ SLOT->Incr = CH->fc * SLOT->mul;
+ ksr = CH->kcode >> SLOT->KSR;
+
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+ /* attack , decay rate recalcration */
+ SLOT->evsa = SLOT->AR[ksr];
+ SLOT->evsd = SLOT->DR[ksr];
+ SLOT->evsr = SLOT->RR[ksr];
+ }
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+}
+
+/* set multi,am,vib,EG-TYP,KSR,mul */
+INLINE void set_mul(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->mul = MUL_TABLE[v&0x0f];
+ SLOT->KSR = (v&0x10) ? 0 : 2;
+ SLOT->eg_typ = (v&0x20)>>5;
+ SLOT->vib = (v&0x40);
+ SLOT->ams = (v&0x80);
+ CALC_FCSLOT(CH,SLOT);
+}
+
+/* set ksl & tl */
+INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+ int ksl = v>>6; /* 0 / 1.5 / 3 / 6 db/OCT */
+
+ SLOT->ksl = ksl ? 3-ksl : 31;
+ SLOT->TL = (v&0x3f)*(0.75/EG_STEP); /* 0.75db step */
+
+ if( !(OPL->mode&0x80) )
+ { /* not CSM latch total level */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+ }
+}
+
+/* set attack rate & decay rate */
+INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+ int ar = v>>4;
+ int dr = v&0x0f;
+
+ SLOT->AR = ar ? &OPL->AR_TABLE[ar<<2] : RATE_0;
+ SLOT->evsa = SLOT->AR[SLOT->ksr];
+ if( SLOT->evm == ENV_MOD_AR ) SLOT->evs = SLOT->evsa;
+
+ SLOT->DR = dr ? &OPL->DR_TABLE[dr<<2] : RATE_0;
+ SLOT->evsd = SLOT->DR[SLOT->ksr];
+ if( SLOT->evm == ENV_MOD_DR ) SLOT->evs = SLOT->evsd;
+}
+
+/* set sustain level & release rate */
+INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+ int sl = v>>4;
+ int rr = v & 0x0f;
+
+ SLOT->SL = SL_TABLE[sl];
+ if( SLOT->evm == ENV_MOD_DR ) SLOT->eve = SLOT->SL;
+ SLOT->RR = &OPL->DR_TABLE[rr<<2];
+ SLOT->evsr = SLOT->RR[SLOT->ksr];
+ if( SLOT->evm == ENV_MOD_RR ) SLOT->evs = SLOT->evsr;
+}
+
+/* operator output calcrator */
+#define OP_OUT(slot,env,con) slot->wavetable[((slot->Cnt+con)/(0x1000000/SIN_ENT))&(SIN_ENT-1)][env]
+/* ---------- calcrate one of channel ---------- */
+INLINE void OPL_CALC_CH( OPL_CH *CH )
+{
+ UINT32 env_out;
+ OPL_SLOT *SLOT;
+
+ feedback2 = 0;
+ /* SLOT 1 */
+ SLOT = &CH->SLOT[SLOT1];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if( env_out < EG_ENT-1 )
+ {
+ /* PG */
+ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+ else SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ if(CH->FB)
+ {
+ int feedback1 = (CH->op1_out[0]+CH->op1_out[1])>>CH->FB;
+ CH->op1_out[1] = CH->op1_out[0];
+ *CH->connect1 += CH->op1_out[0] = OP_OUT(SLOT,env_out,feedback1);
+ }
+ else
+ {
+ *CH->connect1 += OP_OUT(SLOT,env_out,0);
+ }
+ }else
+ {
+ CH->op1_out[1] = CH->op1_out[0];
+ CH->op1_out[0] = 0;
+ }
+ /* SLOT 2 */
+ SLOT = &CH->SLOT[SLOT2];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if( env_out < EG_ENT-1 )
+ {
+ /* PG */
+ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+ else SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ outd[0] += OP_OUT(SLOT,env_out, feedback2);
+ }
+}
+
+/* ---------- calcrate rythm block ---------- */
+#define WHITE_NOISE_db 6.0
+INLINE void OPL_CALC_RH( OPL_CH *CH )
+{
+ UINT32 env_tam,env_sd,env_top,env_hh;
+ int whitenoise = (rand()&1)*(WHITE_NOISE_db/EG_STEP);
+ INT32 tone8;
+
+ OPL_SLOT *SLOT;
+ int env_out;
+
+ /* BD : same as FM serial mode and output level is large */
+ feedback2 = 0;
+ /* SLOT 1 */
+ SLOT = &CH[6].SLOT[SLOT1];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if( env_out < EG_ENT-1 )
+ {
+ /* PG */
+ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+ else SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ if(CH[6].FB)
+ {
+ int feedback1 = (CH[6].op1_out[0]+CH[6].op1_out[1])>>CH[6].FB;
+ CH[6].op1_out[1] = CH[6].op1_out[0];
+ feedback2 = CH[6].op1_out[0] = OP_OUT(SLOT,env_out,feedback1);
+ }
+ else
+ {
+ feedback2 = OP_OUT(SLOT,env_out,0);
+ }
+ }else
+ {
+ feedback2 = 0;
+ CH[6].op1_out[1] = CH[6].op1_out[0];
+ CH[6].op1_out[0] = 0;
+ }
+ /* SLOT 2 */
+ SLOT = &CH[6].SLOT[SLOT2];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if( env_out < EG_ENT-1 )
+ {
+ /* PG */
+ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+ else SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ outd[0] += OP_OUT(SLOT,env_out, feedback2)*2;
+ }
+
+ // SD (17) = mul14[fnum7] + white noise
+ // TAM (15) = mul15[fnum8]
+ // TOP (18) = fnum6(mul18[fnum8]+whitenoise)
+ // HH (14) = fnum7(mul18[fnum8]+whitenoise) + white noise
+ env_sd =OPL_CALC_SLOT(SLOT7_2) + whitenoise;
+ env_tam=OPL_CALC_SLOT(SLOT8_1);
+ env_top=OPL_CALC_SLOT(SLOT8_2);
+ env_hh =OPL_CALC_SLOT(SLOT7_1) + whitenoise;
+
+ /* PG */
+ if(SLOT7_1->vib) SLOT7_1->Cnt += (2*SLOT7_1->Incr*vib/VIB_RATE);
+ else SLOT7_1->Cnt += 2*SLOT7_1->Incr;
+ if(SLOT7_2->vib) SLOT7_2->Cnt += ((CH[7].fc*8)*vib/VIB_RATE);
+ else SLOT7_2->Cnt += (CH[7].fc*8);
+ if(SLOT8_1->vib) SLOT8_1->Cnt += (SLOT8_1->Incr*vib/VIB_RATE);
+ else SLOT8_1->Cnt += SLOT8_1->Incr;
+ if(SLOT8_2->vib) SLOT8_2->Cnt += ((CH[8].fc*48)*vib/VIB_RATE);
+ else SLOT8_2->Cnt += (CH[8].fc*48);
+
+ tone8 = OP_OUT(SLOT8_2,whitenoise,0 );
+
+ /* SD */
+ if( env_sd < EG_ENT-1 )
+ outd[0] += OP_OUT(SLOT7_1,env_sd, 0)*8;
+ /* TAM */
+ if( env_tam < EG_ENT-1 )
+ outd[0] += OP_OUT(SLOT8_1,env_tam, 0)*2;
+ /* TOP-CY */
+ if( env_top < EG_ENT-1 )
+ outd[0] += OP_OUT(SLOT7_2,env_top,tone8)*2;
+ /* HH */
+ if( env_hh < EG_ENT-1 )
+ outd[0] += OP_OUT(SLOT7_2,env_hh,tone8)*2;
+}
+
+/* ----------- initialize time tabls ----------- */
+static void init_timetables( FM_OPL *OPL , int ARRATE , int DRRATE )
+{
+ int i;
+ double rate;
+
+ /* make attack rate & decay rate tables */
+ for (i = 0;i < 4;i++) OPL->AR_TABLE[i] = OPL->DR_TABLE[i] = 0;
+ for (i = 4;i <= 60;i++){
+ rate = OPL->freqbase; /* frequency rate */
+ if( i < 60 ) rate *= 1.0+(i&3)*0.25; /* b0-1 : x1 , x1.25 , x1.5 , x1.75 */
+ rate *= 1<<((i>>2)-1); /* b2-5 : shift bit */
+ rate *= (double)(EG_ENT<<ENV_BITS);
+ OPL->AR_TABLE[i] = rate / ARRATE;
+ OPL->DR_TABLE[i] = rate / DRRATE;
+ }
+ for (i = 60;i < 76;i++)
+ {
+ OPL->AR_TABLE[i] = EG_AED-1;
+ OPL->DR_TABLE[i] = OPL->DR_TABLE[60];
+ }
+#if 0
+ for (i = 0;i < 64 ;i++){ /* make for overflow area */
+ LOG(LOG_WAR,("rate %2d , ar %f ms , dr %f ms \n",i,
+ ((double)(EG_ENT<<ENV_BITS) / OPL->AR_TABLE[i]) * (1000.0 / OPL->rate),
+ ((double)(EG_ENT<<ENV_BITS) / OPL->DR_TABLE[i]) * (1000.0 / OPL->rate) ));
+ }
+#endif
+}
+
+/* ---------- generic table initialize ---------- */
+static int OPLOpenTable( void )
+{
+ int s,t;
+ double rate;
+ int i,j;
+ double pom;
+
+ /* allocate dynamic tables */
+ if( (TL_TABLE = malloc(TL_MAX*2*sizeof(INT32))) == NULL)
+ return 0;
+ if( (SIN_TABLE = malloc(SIN_ENT*4 *sizeof(INT32 *))) == NULL)
+ {
+ free(TL_TABLE);
+ return 0;
+ }
+ if( (AMS_TABLE = malloc(AMS_ENT*2 *sizeof(INT32))) == NULL)
+ {
+ free(TL_TABLE);
+ free(SIN_TABLE);
+ return 0;
+ }
+ if( (VIB_TABLE = malloc(VIB_ENT*2 *sizeof(INT32))) == NULL)
+ {
+ free(TL_TABLE);
+ free(SIN_TABLE);
+ free(AMS_TABLE);
+ return 0;
+ }
+ /* make total level table */
+ for (t = 0;t < EG_ENT-1 ;t++){
+ rate = ((1<<TL_BITS)-1)/pow(10,EG_STEP*t/20); /* dB -> voltage */
+ TL_TABLE[ t] = (int)rate;
+ TL_TABLE[TL_MAX+t] = -TL_TABLE[t];
+/* LOG(LOG_INF,("TotalLevel(%3d) = %x\n",t,TL_TABLE[t]));*/
+ }
+ /* fill volume off area */
+ for ( t = EG_ENT-1; t < TL_MAX ;t++){
+ TL_TABLE[t] = TL_TABLE[TL_MAX+t] = 0;
+ }
+
+ /* make sinwave table (total level offet) */
+ /* degree 0 = degree 180 = off */
+ SIN_TABLE[0] = SIN_TABLE[SIN_ENT/2] = &TL_TABLE[EG_ENT-1];
+ for (s = 1;s <= SIN_ENT/4;s++){
+ pom = sin(2*PI*s/SIN_ENT); /* sin */
+ pom = 20*log10(1/pom); /* decibel */
+ j = pom / EG_STEP; /* TL_TABLE steps */
+
+ /* degree 0 - 90 , degree 180 - 90 : plus section */
+ SIN_TABLE[ s] = SIN_TABLE[SIN_ENT/2-s] = &TL_TABLE[j];
+ /* degree 180 - 270 , degree 360 - 270 : minus section */
+ SIN_TABLE[SIN_ENT/2+s] = SIN_TABLE[SIN_ENT -s] = &TL_TABLE[TL_MAX+j];
+/* LOG(LOG_INF,("sin(%3d) = %f:%f db\n",s,pom,(double)j * EG_STEP));*/
+ }
+ for (s = 0;s < SIN_ENT;s++)
+ {
+ SIN_TABLE[SIN_ENT*1+s] = s<(SIN_ENT/2) ? SIN_TABLE[s] : &TL_TABLE[EG_ENT];
+ SIN_TABLE[SIN_ENT*2+s] = SIN_TABLE[s % (SIN_ENT/2)];
+ SIN_TABLE[SIN_ENT*3+s] = (s/(SIN_ENT/4))&1 ? &TL_TABLE[EG_ENT] : SIN_TABLE[SIN_ENT*2+s];
+ }
+
+ /* envelope counter -> envelope output table */
+ for (i=0; i<EG_ENT; i++)
+ {
+ /* ATTACK curve */
+ pom = pow( ((double)(EG_ENT-1-i)/EG_ENT) , 8 ) * EG_ENT;
+ /* if( pom >= EG_ENT ) pom = EG_ENT-1; */
+ ENV_CURVE[i] = (int)pom;
+ /* DECAY ,RELEASE curve */
+ ENV_CURVE[(EG_DST>>ENV_BITS)+i]= i;
+ }
+ /* off */
+ ENV_CURVE[EG_OFF>>ENV_BITS]= EG_ENT-1;
+ /* make LFO ams table */
+ for (i=0; i<AMS_ENT; i++)
+ {
+ pom = (1.0+sin(2*PI*i/AMS_ENT))/2; /* sin */
+ AMS_TABLE[i] = (1.0/EG_STEP)*pom; /* 1dB */
+ AMS_TABLE[AMS_ENT+i] = (4.8/EG_STEP)*pom; /* 4.8dB */
+ }
+ /* make LFO vibrate table */
+ for (i=0; i<VIB_ENT; i++)
+ {
+ /* 100cent = 1seminote = 6% ?? */
+ pom = (double)VIB_RATE*0.06*sin(2*PI*i/VIB_ENT); /* +-100sect step */
+ VIB_TABLE[i] = VIB_RATE + (pom*0.07); /* +- 7cent */
+ VIB_TABLE[VIB_ENT+i] = VIB_RATE + (pom*0.14); /* +-14cent */
+ /* LOG(LOG_INF,("vib %d=%d\n",i,VIB_TABLE[VIB_ENT+i])); */
+ }
+ return 1;
+}
+
+
+static void OPLCloseTable( void )
+{
+ free(TL_TABLE);
+ free(SIN_TABLE);
+ free(AMS_TABLE);
+ free(VIB_TABLE);
+}
+
+/* CSM Key Controll */
+INLINE void CSMKeyControll(OPL_CH *CH)
+{
+ OPL_SLOT *slot1 = &CH->SLOT[SLOT1];
+ OPL_SLOT *slot2 = &CH->SLOT[SLOT2];
+ /* all key off */
+ OPL_KEYOFF(slot1);
+ OPL_KEYOFF(slot2);
+ /* total level latch */
+ slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl);
+ slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl);
+ /* key on */
+ CH->op1_out[0] = CH->op1_out[1] = 0;
+ OPL_KEYON(slot1);
+ OPL_KEYON(slot2);
+}
+
+/* ---------- opl initialize ---------- */
+static void OPL_initalize(FM_OPL *OPL)
+{
+ int fn;
+
+ /* frequency base */
+ OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / OPL->rate) / 72 : 0;
+ /* Timer base time */
+ OPL->TimerBase = 1.0/((double)OPL->clock / 72.0 );
+ /* make time tables */
+ init_timetables( OPL , OPL_ARRATE , OPL_DRRATE );
+ /* make fnumber -> increment counter table */
+ for( fn=0 ; fn < 1024 ; fn++ )
+ {
+ OPL->FN_TABLE[fn] = OPL->freqbase * fn * FREQ_RATE * (1<<7) / 2;
+ }
+ /* LFO freq.table */
+ OPL->amsIncr = OPL->rate ? (double)AMS_ENT*(1<<AMS_SHIFT) / OPL->rate * 3.7 * ((double)OPL->clock/3600000) : 0;
+ OPL->vibIncr = OPL->rate ? (double)VIB_ENT*(1<<VIB_SHIFT) / OPL->rate * 6.4 * ((double)OPL->clock/3600000) : 0;
+}
+
+/* ---------- write a OPL registers ---------- */
+static void OPLWriteReg(FM_OPL *OPL, int r, int v)
+{
+ OPL_CH *CH;
+ int slot;
+ int block_fnum;
+
+ switch(r&0xe0)
+ {
+ case 0x00: /* 00-1f:controll */
+ switch(r&0x1f)
+ {
+ case 0x01:
+ /* wave selector enable */
+ if(OPL->type&OPL_TYPE_WAVESEL)
+ {
+ OPL->wavesel = v&0x20;
+ if(!OPL->wavesel)
+ {
+ /* preset compatible mode */
+ int c;
+ for(c=0;c<OPL->max_ch;c++)
+ {
+ OPL->P_CH[c].SLOT[SLOT1].wavetable = &SIN_TABLE[0];
+ OPL->P_CH[c].SLOT[SLOT2].wavetable = &SIN_TABLE[0];
+ }
+ }
+ }
+ return;
+ case 0x02: /* Timer 1 */
+ OPL->T[0] = (256-v)*4;
+ break;
+ case 0x03: /* Timer 2 */
+ OPL->T[1] = (256-v)*16;
+ return;
+ case 0x04: /* IRQ clear / mask and Timer enable */
+ if(v&0x80)
+ { /* IRQ flag clear */
+ OPL_STATUS_RESET(OPL,0x7f);
+ }
+ else
+ { /* set IRQ mask ,timer enable*/
+ UINT8 st1 = v&1;
+ UINT8 st2 = (v>>1)&1;
+ /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */
+ OPL_STATUS_RESET(OPL,v&0x78);
+ OPL_STATUSMASK_SET(OPL,((~v)&0x78)|0x01);
+ /* timer 2 */
+ if(OPL->st[1] != st2)
+ {
+ double interval = st2 ? (double)OPL->T[1]*OPL->TimerBase : 0.0;
+ OPL->st[1] = st2;
+ if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+1,interval);
+ }
+ /* timer 1 */
+ if(OPL->st[0] != st1)
+ {
+ double interval = st1 ? (double)OPL->T[0]*OPL->TimerBase : 0.0;
+ OPL->st[0] = st1;
+ if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+0,interval);
+ }
+ }
+ return;
+#if BUILD_Y8950
+ case 0x06: /* Key Board OUT */
+ if(OPL->type&OPL_TYPE_KEYBOARD)
+ {
+ if(OPL->keyboardhandler_w)
+ OPL->keyboardhandler_w(OPL->keyboard_param,v);
+ else
+ LOG(LOG_WAR,("OPL:write unmapped KEYBOARD port\n"));
+ }
+ return;
+ case 0x07: /* DELTA-T controll : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
+ return;
+ case 0x08: /* MODE,DELTA-T : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */
+ OPL->mode = v;
+ v&=0x1f; /* for DELTA-T unit */
+ case 0x09: /* START ADD */
+ case 0x0a:
+ case 0x0b: /* STOP ADD */
+ case 0x0c:
+ case 0x0d: /* PRESCALE */
+ case 0x0e:
+ case 0x0f: /* ADPCM data */
+ case 0x10: /* DELTA-N */
+ case 0x11: /* DELTA-N */
+ case 0x12: /* EG-CTRL */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
+ return;
+#if 0
+ case 0x15: /* DAC data */
+ case 0x16:
+ case 0x17: /* SHIFT */
+ return;
+ case 0x18: /* I/O CTRL (Direction) */
+ if(OPL->type&OPL_TYPE_IO)
+ OPL->portDirection = v&0x0f;
+ return;
+ case 0x19: /* I/O DATA */
+ if(OPL->type&OPL_TYPE_IO)
+ {
+ OPL->portLatch = v;
+ if(OPL->porthandler_w)
+ OPL->porthandler_w(OPL->port_param,v&OPL->portDirection);
+ }
+ return;
+ case 0x1a: /* PCM data */
+ return;
+#endif
+#endif
+ }
+ break;
+ case 0x20: /* am,vib,ksr,eg type,mul */
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ set_mul(OPL,slot,v);
+ return;
+ case 0x40:
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ set_ksl_tl(OPL,slot,v);
+ return;
+ case 0x60:
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ set_ar_dr(OPL,slot,v);
+ return;
+ case 0x80:
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ set_sl_rr(OPL,slot,v);
+ return;
+ case 0xa0:
+ switch(r)
+ {
+ case 0xbd:
+ /* amsep,vibdep,r,bd,sd,tom,tc,hh */
+ {
+ UINT8 rkey = OPL->rythm^v;
+ OPL->ams_table = &AMS_TABLE[v&0x80 ? AMS_ENT : 0];
+ OPL->vib_table = &VIB_TABLE[v&0x40 ? VIB_ENT : 0];
+ OPL->rythm = v&0x3f;
+ if(OPL->rythm&0x20)
+ {
+#if 0
+ usrintf_showmessage("OPL Rythm mode select");
+#endif
+ /* BD key on/off */
+ if(rkey&0x10)
+ {
+ if(v&0x10)
+ {
+ OPL->P_CH[6].op1_out[0] = OPL->P_CH[6].op1_out[1] = 0;
+ OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT1]);
+ OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT2]);
+ }
+ else
+ {
+ OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1]);
+ OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2]);
+ }
+ }
+ /* SD key on/off */
+ if(rkey&0x08)
+ {
+ if(v&0x08) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT2]);
+ else OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2]);
+ }/* TAM key on/off */
+ if(rkey&0x04)
+ {
+ if(v&0x04) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT1]);
+ else OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1]);
+ }
+ /* TOP-CY key on/off */
+ if(rkey&0x02)
+ {
+ if(v&0x02) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT2]);
+ else OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2]);
+ }
+ /* HH key on/off */
+ if(rkey&0x01)
+ {
+ if(v&0x01) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT1]);
+ else OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1]);
+ }
+ }
+ }
+ return;
+ }
+ /* keyon,block,fnum */
+ if( (r&0x0f) > 8) return;
+ CH = &OPL->P_CH[r&0x0f];
+ if(!(r&0x10))
+ { /* a0-a8 */
+ block_fnum = (CH->block_fnum&0x1f00) | v;
+ }
+ else
+ { /* b0-b8 */
+ int keyon = (v>>5)&1;
+ block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff);
+ if(CH->keyon != keyon)
+ {
+ if( (CH->keyon=keyon) )
+ {
+ CH->op1_out[0] = CH->op1_out[1] = 0;
+ OPL_KEYON(&CH->SLOT[SLOT1]);
+ OPL_KEYON(&CH->SLOT[SLOT2]);
+ }
+ else
+ {
+ OPL_KEYOFF(&CH->SLOT[SLOT1]);
+ OPL_KEYOFF(&CH->SLOT[SLOT2]);
+ }
+ }
+ }
+ /* update */
+ if(CH->block_fnum != block_fnum)
+ {
+ int blockRv = 7-(block_fnum>>10);
+ int fnum = block_fnum&0x3ff;
+ CH->block_fnum = block_fnum;
+
+ CH->ksl_base = KSL_TABLE[block_fnum>>6];
+ CH->fc = OPL->FN_TABLE[fnum]>>blockRv;
+ CH->kcode = CH->block_fnum>>9;
+ if( (OPL->mode&0x40) && CH->block_fnum&0x100) CH->kcode |=1;
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ }
+ return;
+ case 0xc0:
+ /* FB,C */
+ if( (r&0x0f) > 8) return;
+ CH = &OPL->P_CH[r&0x0f];
+ {
+ int feedback = (v>>1)&7;
+ CH->FB = feedback ? (8+1) - feedback : 0;
+ CH->CON = v&1;
+ set_algorythm(CH);
+ }
+ return;
+ case 0xe0: /* wave type */
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ CH = &OPL->P_CH[slot/2];
+ if(OPL->wavesel)
+ {
+ /* LOG(LOG_INF,("OPL SLOT %d wave select %d\n",slot,v&3)); */
+ CH->SLOT[slot&1].wavetable = &SIN_TABLE[(v&0x03)*SIN_ENT];
+ }
+ return;
+ }
+}
+
+/* lock/unlock for common table */
+static int OPL_LockTable(void)
+{
+ num_lock++;
+ if(num_lock>1) return 0;
+ /* first time */
+ cur_chip = NULL;
+ /* allocate total level table (128kb space) */
+ if( !OPLOpenTable() )
+ {
+ num_lock--;
+ return -1;
+ }
+ return 0;
+}
+
+static void OPL_UnLockTable(void)
+{
+ if(num_lock) num_lock--;
+ if(num_lock) return;
+ /* last time */
+ cur_chip = NULL;
+ OPLCloseTable();
+}
+
+#if (BUILD_YM3812 || BUILD_YM3526)
+/*******************************************************************************/
+/* YM3812 local section */
+/*******************************************************************************/
+
+/* ---------- update one of chip ----------- */
+void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length)
+{
+ int i;
+ int data;
+ OPLSAMPLE *buf = buffer;
+ UINT32 amsCnt = OPL->amsCnt;
+ UINT32 vibCnt = OPL->vibCnt;
+ UINT8 rythm = OPL->rythm&0x20;
+ OPL_CH *CH,*R_CH;
+
+ if( (void *)OPL != cur_chip ){
+ cur_chip = (void *)OPL;
+ /* channel pointers */
+ S_CH = OPL->P_CH;
+ E_CH = &S_CH[9];
+ /* rythm slot */
+ SLOT7_1 = &S_CH[7].SLOT[SLOT1];
+ SLOT7_2 = &S_CH[7].SLOT[SLOT2];
+ SLOT8_1 = &S_CH[8].SLOT[SLOT1];
+ SLOT8_2 = &S_CH[8].SLOT[SLOT2];
+ /* LFO state */
+ amsIncr = OPL->amsIncr;
+ vibIncr = OPL->vibIncr;
+ ams_table = OPL->ams_table;
+ vib_table = OPL->vib_table;
+ }
+ R_CH = rythm ? &S_CH[6] : E_CH;
+ for( i=0; i < length ; i++ )
+ {
+ /* channel A channel B channel C */
+ /* LFO */
+ ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT];
+ vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT];
+ outd[0] = 0;
+ /* FM part */
+ for(CH=S_CH ; CH < R_CH ; CH++)
+ OPL_CALC_CH(CH);
+ /* Rythn part */
+ if(rythm)
+ OPL_CALC_RH(S_CH);
+ /* limit check */
+ data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT );
+ /* store to sound buffer */
+ buf[i] = data >> OPL_OUTSB;
+ }
+
+ OPL->amsCnt = amsCnt;
+ OPL->vibCnt = vibCnt;
+#ifdef OPL_OUTPUT_LOG
+ if(opl_dbg_fp)
+ {
+ for(opl_dbg_chip=0;opl_dbg_chip<opl_dbg_maxchip;opl_dbg_chip++)
+ if( opl_dbg_opl[opl_dbg_chip] == OPL) break;
+ fprintf(opl_dbg_fp,"%c%c%c",0x20+opl_dbg_chip,length&0xff,length/256);
+ }
+#endif
+}
+#endif /* (BUILD_YM3812 || BUILD_YM3526) */
+
+#if BUILD_Y8950
+
+void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length)
+{
+ int i;
+ int data;
+ OPLSAMPLE *buf = buffer;
+ UINT32 amsCnt = OPL->amsCnt;
+ UINT32 vibCnt = OPL->vibCnt;
+ UINT8 rythm = OPL->rythm&0x20;
+ OPL_CH *CH,*R_CH;
+ YM_DELTAT *DELTAT = OPL->deltat;
+
+ /* setup DELTA-T unit */
+ YM_DELTAT_DECODE_PRESET(DELTAT);
+
+ if( (void *)OPL != cur_chip ){
+ cur_chip = (void *)OPL;
+ /* channel pointers */
+ S_CH = OPL->P_CH;
+ E_CH = &S_CH[9];
+ /* rythm slot */
+ SLOT7_1 = &S_CH[7].SLOT[SLOT1];
+ SLOT7_2 = &S_CH[7].SLOT[SLOT2];
+ SLOT8_1 = &S_CH[8].SLOT[SLOT1];
+ SLOT8_2 = &S_CH[8].SLOT[SLOT2];
+ /* LFO state */
+ amsIncr = OPL->amsIncr;
+ vibIncr = OPL->vibIncr;
+ ams_table = OPL->ams_table;
+ vib_table = OPL->vib_table;
+ }
+ R_CH = rythm ? &S_CH[6] : E_CH;
+ for( i=0; i < length ; i++ )
+ {
+ /* channel A channel B channel C */
+ /* LFO */
+ ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT];
+ vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT];
+ outd[0] = 0;
+ /* deltaT ADPCM */
+ if( DELTAT->portstate )
+ YM_DELTAT_ADPCM_CALC(DELTAT);
+ /* FM part */
+ for(CH=S_CH ; CH < R_CH ; CH++)
+ OPL_CALC_CH(CH);
+ /* Rythn part */
+ if(rythm)
+ OPL_CALC_RH(S_CH);
+ /* limit check */
+ data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT );
+ /* store to sound buffer */
+ buf[i] = data >> OPL_OUTSB;
+ }
+ OPL->amsCnt = amsCnt;
+ OPL->vibCnt = vibCnt;
+ /* deltaT START flag */
+ if( !DELTAT->portstate )
+ OPL->status &= 0xfe;
+}
+#endif
+
+/* ---------- reset one of chip ---------- */
+void OPLResetChip(FM_OPL *OPL)
+{
+ int c,s;
+ int i;
+
+ /* reset chip */
+ OPL->mode = 0; /* normal mode */
+ OPL_STATUS_RESET(OPL,0x7f);
+ /* reset with register write */
+ OPLWriteReg(OPL,0x01,0); /* wabesel disable */
+ OPLWriteReg(OPL,0x02,0); /* Timer1 */
+ OPLWriteReg(OPL,0x03,0); /* Timer2 */
+ OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */
+ for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0);
+ /* reset OPerator paramater */
+ for( c = 0 ; c < OPL->max_ch ; c++ )
+ {
+ OPL_CH *CH = &OPL->P_CH[c];
+ /* OPL->P_CH[c].PAN = OPN_CENTER; */
+ for(s = 0 ; s < 2 ; s++ )
+ {
+ /* wave table */
+ CH->SLOT[s].wavetable = &SIN_TABLE[0];
+ /* CH->SLOT[s].evm = ENV_MOD_RR; */
+ CH->SLOT[s].evc = EG_OFF;
+ CH->SLOT[s].eve = EG_OFF+1;
+ CH->SLOT[s].evs = 0;
+ }
+ }
+#if BUILD_Y8950
+ if(OPL->type&OPL_TYPE_ADPCM)
+ {
+ YM_DELTAT *DELTAT = OPL->deltat;
+
+ DELTAT->freqbase = OPL->freqbase;
+ DELTAT->output_pointer = outd;
+ DELTAT->portshift = 5;
+ DELTAT->output_range = DELTAT_MIXING_LEVEL<<TL_BITS;
+ YM_DELTAT_ADPCM_Reset(DELTAT,0);
+ }
+#endif
+}
+
+/* ---------- Create one of vietual YM3812 ---------- */
+/* 'rate' is sampling rate and 'bufsiz' is the size of the */
+FM_OPL *OPLCreate(int type, int clock, int rate)
+{
+ char *ptr;
+ FM_OPL *OPL;
+ int state_size;
+ int max_ch = 9; /* normaly 9 channels */
+
+ if( OPL_LockTable() ==-1) return NULL;
+ /* allocate OPL state space */
+ state_size = sizeof(FM_OPL);
+ state_size += sizeof(OPL_CH)*max_ch;
+#if BUILD_Y8950
+ if(type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT);
+#endif
+ /* allocate memory block */
+ ptr = malloc(state_size);
+ if(ptr==NULL) return NULL;
+ /* clear */
+ memset(ptr,0,state_size);
+ OPL = (FM_OPL *)ptr; ptr+=sizeof(FM_OPL);
+ OPL->P_CH = (OPL_CH *)ptr; ptr+=sizeof(OPL_CH)*max_ch;
+#if BUILD_Y8950
+ if(type&OPL_TYPE_ADPCM) OPL->deltat = (YM_DELTAT *)ptr; ptr+=sizeof(YM_DELTAT);
+#endif
+ /* set channel state pointer */
+ OPL->type = type;
+ OPL->clock = clock;
+ OPL->rate = rate;
+ OPL->max_ch = max_ch;
+ /* init grobal tables */
+ OPL_initalize(OPL);
+ /* reset chip */
+ OPLResetChip(OPL);
+#ifdef OPL_OUTPUT_LOG
+ if(!opl_dbg_fp)
+ {
+ opl_dbg_fp = fopen("opllog.opl","wb");
+ opl_dbg_maxchip = 0;
+ }
+ if(opl_dbg_fp)
+ {
+ opl_dbg_opl[opl_dbg_maxchip] = OPL;
+ fprintf(opl_dbg_fp,"%c%c%c%c%c%c",0x00+opl_dbg_maxchip,
+ type,
+ clock&0xff,
+ (clock/0x100)&0xff,
+ (clock/0x10000)&0xff,
+ (clock/0x1000000)&0xff);
+ opl_dbg_maxchip++;
+ }
+#endif
+ return OPL;
+}
+
+/* ---------- Destroy one of vietual YM3812 ---------- */
+void OPLDestroy(FM_OPL *OPL)
+{
+#ifdef OPL_OUTPUT_LOG
+ if(opl_dbg_fp)
+ {
+ fclose(opl_dbg_fp);
+ opl_dbg_fp = NULL;
+ }
+#endif
+ OPL_UnLockTable();
+ free(OPL);
+}
+
+/* ---------- Option handlers ---------- */
+
+void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset)
+{
+ OPL->TimerHandler = TimerHandler;
+ OPL->TimerParam = channelOffset;
+}
+void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param)
+{
+ OPL->IRQHandler = IRQHandler;
+ OPL->IRQParam = param;
+}
+void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param)
+{
+ OPL->UpdateHandler = UpdateHandler;
+ OPL->UpdateParam = param;
+}
+#if BUILD_Y8950
+void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param)
+{
+ OPL->porthandler_w = PortHandler_w;
+ OPL->porthandler_r = PortHandler_r;
+ OPL->port_param = param;
+}
+
+void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param)
+{
+ OPL->keyboardhandler_w = KeyboardHandler_w;
+ OPL->keyboardhandler_r = KeyboardHandler_r;
+ OPL->keyboard_param = param;
+}
+#endif
+/* ---------- YM3812 I/O interface ---------- */
+int OPLWrite(FM_OPL *OPL,int a,int v)
+{
+ if( !(a&1) )
+ { /* address port */
+ OPL->address = v & 0xff;
+ }
+ else
+ { /* data port */
+ if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
+#ifdef OPL_OUTPUT_LOG
+ if(opl_dbg_fp)
+ {
+ for(opl_dbg_chip=0;opl_dbg_chip<opl_dbg_maxchip;opl_dbg_chip++)
+ if( opl_dbg_opl[opl_dbg_chip] == OPL) break;
+ fprintf(opl_dbg_fp,"%c%c%c",0x10+opl_dbg_chip,OPL->address,v);
+ }
+#endif
+ OPLWriteReg(OPL,OPL->address,v);
+ }
+ return OPL->status>>7;
+}
+
+unsigned char OPLRead(FM_OPL *OPL,int a)
+{
+ if( !(a&1) )
+ { /* status port */
+ return OPL->status & (OPL->statusmask|0x80);
+ }
+ /* data port */
+ switch(OPL->address)
+ {
+ case 0x05: /* KeyBoard IN */
+ if(OPL->type&OPL_TYPE_KEYBOARD)
+ {
+ if(OPL->keyboardhandler_r)
+ return OPL->keyboardhandler_r(OPL->keyboard_param);
+ else
+ LOG(LOG_WAR,("OPL:read unmapped KEYBOARD port\n"));
+ }
+ return 0;
+#if 0
+ case 0x0f: /* ADPCM-DATA */
+ return 0;
+#endif
+ case 0x19: /* I/O DATA */
+ if(OPL->type&OPL_TYPE_IO)
+ {
+ if(OPL->porthandler_r)
+ return OPL->porthandler_r(OPL->port_param);
+ else
+ LOG(LOG_WAR,("OPL:read unmapped I/O port\n"));
+ }
+ return 0;
+ case 0x1a: /* PCM-DATA */
+ return 0;
+ }
+ return 0;
+}
+
+int OPLTimerOver(FM_OPL *OPL,int c)
+{
+ if( c )
+ { /* Timer B */
+ OPL_STATUS_SET(OPL,0x20);
+ }
+ else
+ { /* Timer A */
+ OPL_STATUS_SET(OPL,0x40);
+ /* CSM mode key,TL controll */
+ if( OPL->mode & 0x80 )
+ { /* CSM mode total level latch and auto key on */
+ int ch;
+ if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
+ for(ch=0;ch<9;ch++)
+ CSMKeyControll( &OPL->P_CH[ch] );
+ }
+ }
+ /* reload timer */
+ if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+c,(double)OPL->T[c]*OPL->TimerBase);
+ return OPL->status>>7;
+}
diff --git a/plugins/adplug/adplug/fmopl.h b/plugins/adplug/adplug/fmopl.h
new file mode 100644
index 00000000..a01ff902
--- /dev/null
+++ b/plugins/adplug/adplug/fmopl.h
@@ -0,0 +1,174 @@
+#ifndef __FMOPL_H_
+#define __FMOPL_H_
+
+/* --- select emulation chips --- */
+#define BUILD_YM3812 (HAS_YM3812)
+//#define BUILD_YM3526 (HAS_YM3526)
+//#define BUILD_Y8950 (HAS_Y8950)
+
+/* --- system optimize --- */
+/* select bit size of output : 8 or 16 */
+#define OPL_OUTPUT_BIT 16
+
+/* compiler dependence */
+#ifndef OSD_CPU_H
+#define OSD_CPU_H
+typedef unsigned char UINT8; /* unsigned 8bit */
+typedef unsigned short UINT16; /* unsigned 16bit */
+typedef unsigned int UINT32; /* unsigned 32bit */
+typedef signed char INT8; /* signed 8bit */
+typedef signed short INT16; /* signed 16bit */
+typedef signed int INT32; /* signed 32bit */
+#endif
+
+#if (OPL_OUTPUT_BIT==16)
+typedef INT16 OPLSAMPLE;
+#endif
+#if (OPL_OUTPUT_BIT==8)
+typedef unsigned char OPLSAMPLE;
+#endif
+
+
+#if BUILD_Y8950
+#include "ymdeltat.h"
+#endif
+
+typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec);
+typedef void (*OPL_IRQHANDLER)(int param,int irq);
+typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us);
+typedef void (*OPL_PORTHANDLER_W)(int param,unsigned char data);
+typedef unsigned char (*OPL_PORTHANDLER_R)(int param);
+
+/* !!!!! here is private section , do not access there member direct !!!!! */
+
+#define OPL_TYPE_WAVESEL 0x01 /* waveform select */
+#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */
+#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */
+#define OPL_TYPE_IO 0x08 /* I/O port */
+
+/* Saving is necessary for member of the 'R' mark for suspend/resume */
+/* ---------- OPL one of slot ---------- */
+typedef struct fm_opl_slot {
+ INT32 TL; /* total level :TL << 8 */
+ INT32 TLL; /* adjusted now TL */
+ UINT8 KSR; /* key scale rate :(shift down bit) */
+ INT32 *AR; /* attack rate :&AR_TABLE[AR<<2] */
+ INT32 *DR; /* decay rate :&DR_TALBE[DR<<2] */
+ INT32 SL; /* sustin level :SL_TALBE[SL] */
+ INT32 *RR; /* release rate :&DR_TABLE[RR<<2] */
+ UINT8 ksl; /* keyscale level :(shift down bits) */
+ UINT8 ksr; /* key scale rate :kcode>>KSR */
+ UINT32 mul; /* multiple :ML_TABLE[ML] */
+ UINT32 Cnt; /* frequency count : */
+ UINT32 Incr; /* frequency step : */
+ /* envelope generator state */
+ UINT8 eg_typ; /* envelope type flag */
+ UINT8 evm; /* envelope phase */
+ INT32 evc; /* envelope counter */
+ INT32 eve; /* envelope counter end point */
+ INT32 evs; /* envelope counter step */
+ INT32 evsa; /* envelope step for AR :AR[ksr] */
+ INT32 evsd; /* envelope step for DR :DR[ksr] */
+ INT32 evsr; /* envelope step for RR :RR[ksr] */
+ /* LFO */
+ UINT8 ams; /* ams flag */
+ UINT8 vib; /* vibrate flag */
+ /* wave selector */
+ INT32 **wavetable;
+}OPL_SLOT;
+
+/* ---------- OPL one of channel ---------- */
+typedef struct fm_opl_channel {
+ OPL_SLOT SLOT[2];
+ UINT8 CON; /* connection type */
+ UINT8 FB; /* feed back :(shift down bit) */
+ INT32 *connect1; /* slot1 output pointer */
+ INT32 *connect2; /* slot2 output pointer */
+ INT32 op1_out[2]; /* slot1 output for selfeedback */
+ /* phase generator state */
+ UINT32 block_fnum; /* block+fnum : */
+ UINT8 kcode; /* key code : KeyScaleCode */
+ UINT32 fc; /* Freq. Increment base */
+ UINT32 ksl_base; /* KeyScaleLevel Base step */
+ UINT8 keyon; /* key on/off flag */
+} OPL_CH;
+
+/* OPL state */
+typedef struct fm_opl_f {
+ UINT8 type; /* chip type */
+ int clock; /* master clock (Hz) */
+ int rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+ double TimerBase; /* Timer base time (==sampling time) */
+ UINT8 address; /* address register */
+ UINT8 status; /* status flag */
+ UINT8 statusmask; /* status mask */
+ UINT32 mode; /* Reg.08 : CSM , notesel,etc. */
+ /* Timer */
+ int T[2]; /* timer counter */
+ UINT8 st[2]; /* timer enable */
+ /* FM channel slots */
+ OPL_CH *P_CH; /* pointer of CH */
+ int max_ch; /* maximum channel */
+ /* Rythm sention */
+ UINT8 rythm; /* Rythm mode , key flag */
+#if BUILD_Y8950
+ /* Delta-T ADPCM unit (Y8950) */
+ YM_DELTAT *deltat; /* DELTA-T ADPCM */
+#endif
+ /* Keyboard / I/O interface unit (Y8950) */
+ UINT8 portDirection;
+ UINT8 portLatch;
+ OPL_PORTHANDLER_R porthandler_r;
+ OPL_PORTHANDLER_W porthandler_w;
+ int port_param;
+ OPL_PORTHANDLER_R keyboardhandler_r;
+ OPL_PORTHANDLER_W keyboardhandler_w;
+ int keyboard_param;
+ /* time tables */
+ INT32 AR_TABLE[75]; /* atttack rate tables */
+ INT32 DR_TABLE[75]; /* decay rate tables */
+ UINT32 FN_TABLE[1024]; /* fnumber -> increment counter */
+ /* LFO */
+ INT32 *ams_table;
+ INT32 *vib_table;
+ INT32 amsCnt;
+ INT32 amsIncr;
+ INT32 vibCnt;
+ INT32 vibIncr;
+ /* wave selector enable flag */
+ UINT8 wavesel;
+ /* external event callback handler */
+ OPL_TIMERHANDLER TimerHandler; /* TIMER handler */
+ int TimerParam; /* TIMER parameter */
+ OPL_IRQHANDLER IRQHandler; /* IRQ handler */
+ int IRQParam; /* IRQ parameter */
+ OPL_UPDATEHANDLER UpdateHandler; /* stream update handler */
+ int UpdateParam; /* stream update parameter */
+} FM_OPL;
+
+/* ---------- Generic interface section ---------- */
+#define OPL_TYPE_YM3526 (0)
+#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)
+#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO)
+
+FM_OPL *OPLCreate(int type, int clock, int rate);
+void OPLDestroy(FM_OPL *OPL);
+void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset);
+void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param);
+void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param);
+/* Y8950 port handlers */
+void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param);
+void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param);
+
+void OPLResetChip(FM_OPL *OPL);
+int OPLWrite(FM_OPL *OPL,int a,int v);
+unsigned char OPLRead(FM_OPL *OPL,int a);
+int OPLTimerOver(FM_OPL *OPL,int c);
+
+/* YM3626/YM3812 local section */
+void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
+
+void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
+
+#endif
diff --git a/plugins/adplug/adplug/fprovide.cpp b/plugins/adplug/adplug/fprovide.cpp
new file mode 100644
index 00000000..cbe94e2f
--- /dev/null
+++ b/plugins/adplug/adplug/fprovide.cpp
@@ -0,0 +1,76 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2002 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * fprovide.cpp - File provider class framework, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string.h>
+#include <binio.h>
+#include <binfile.h>
+
+#include "fprovide.h"
+
+/***** CFileProvider *****/
+
+bool CFileProvider::extension(const std::string &filename,
+ const std::string &extension)
+{
+ const char *fname = filename.c_str(), *ext = extension.c_str();
+
+ if(strlen(fname) < strlen(ext) ||
+ stricmp(fname + strlen(fname) - strlen(ext), ext))
+ return false;
+ else
+ return true;
+}
+
+unsigned long CFileProvider::filesize(binistream *f)
+{
+ unsigned long oldpos = f->pos(), size;
+
+ f->seek(0, binio::End);
+ size = f->pos();
+ f->seek(oldpos, binio::Set);
+
+ return size;
+}
+
+/***** CProvider_Filesystem *****/
+
+binistream *CProvider_Filesystem::open(std::string filename) const
+{
+ binifstream *f = new binifstream(filename);
+
+ if(!f) return 0;
+ if(f->error()) { delete f; return 0; }
+
+ // Open all files as little endian with IEEE floats by default
+ f->setFlag(binio::BigEndian, false); f->setFlag(binio::FloatIEEE);
+
+ return f;
+}
+
+void CProvider_Filesystem::close(binistream *f) const
+{
+ binifstream *ff = (binifstream *)f;
+
+ if(f) {
+ ff->close();
+ delete ff;
+ }
+}
diff --git a/plugins/adplug/adplug/fprovide.h b/plugins/adplug/adplug/fprovide.h
new file mode 100644
index 00000000..2f1a0b62
--- /dev/null
+++ b/plugins/adplug/adplug/fprovide.h
@@ -0,0 +1,50 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * fprovide.h - File provider class framework, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_FILEPROVIDER
+#define H_ADPLUG_FILEPROVIDER
+
+#include <string>
+#include <binio.h>
+
+class CFileProvider
+{
+public:
+ virtual ~CFileProvider()
+ {
+ }
+
+ virtual binistream *open(std::string filename) const = 0;
+ virtual void close(binistream *) const = 0;
+
+ static bool extension(const std::string &filename,
+ const std::string &extension);
+ static unsigned long filesize(binistream *f);
+};
+
+class CProvider_Filesystem: public CFileProvider
+{
+public:
+ virtual binistream *open(std::string filename) const;
+ virtual void close(binistream *f) const;
+};
+
+#endif
diff --git a/plugins/adplug/adplug/hsc.cpp b/plugins/adplug/adplug/hsc.cpp
new file mode 100644
index 00000000..1a912ff1
--- /dev/null
+++ b/plugins/adplug/adplug/hsc.cpp
@@ -0,0 +1,317 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2004 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * hsc.cpp - HSC Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string.h>
+
+#include "hsc.h"
+#include "debug.h"
+
+/*** public methods **************************************/
+
+CPlayer *ChscPlayer::factory(Copl *newopl)
+{
+ return new ChscPlayer(newopl);
+}
+
+bool ChscPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename);
+ int i;
+
+ // file validation section
+ if(!f || !fp.extension(filename, ".hsc") || fp.filesize(f) > 59187) {
+ AdPlug_LogWrite("ChscPlayer::load(\"%s\"): Not a HSC file!\n", filename.c_str());
+ fp.close(f);
+ return false;
+ }
+
+ // load section
+ for(i=0;i<128*12;i++) // load instruments
+ *((unsigned char *)instr + i) = f->readInt(1);
+ for (i=0;i<128;i++) { // correct instruments
+ instr[i][2] ^= (instr[i][2] & 0x40) << 1;
+ instr[i][3] ^= (instr[i][3] & 0x40) << 1;
+ instr[i][11] >>= 4; // slide
+ }
+ for(i=0;i<51;i++) song[i] = f->readInt(1); // load tracklist
+ for(i=0;i<50*64*9;i++) // load patterns
+ *((char *)patterns + i) = f->readInt(1);
+
+ fp.close(f);
+ rewind(0); // rewind module
+ return true;
+}
+
+bool ChscPlayer::update()
+{
+ // general vars
+ unsigned char chan,pattnr,note,effect,eff_op,inst,vol,Okt,db;
+ unsigned short Fnr;
+ unsigned long pattoff;
+
+ del--; // player speed handling
+ if(del)
+ return !songend; // nothing done
+
+ if(fadein) // fade-in handling
+ fadein--;
+
+ pattnr = song[songpos];
+ if(pattnr == 0xff) { // arrangement handling
+ songend = 1; // set end-flag
+ songpos = 0;
+ pattnr = song[songpos];
+ } else
+ if ((pattnr & 128) && (pattnr <= 0xb1)) { // goto pattern "nr"
+ songpos = song[songpos] & 127;
+ pattpos = 0;
+ pattnr = song[songpos];
+ songend = 1;
+ }
+
+ pattoff = pattpos*9;
+ for (chan=0;chan<9;chan++) { // handle all channels
+ note = patterns[pattnr][pattoff].note;
+ effect = patterns[pattnr][pattoff].effect;
+ pattoff++;
+
+ if(note & 128) { // set instrument
+ setinstr(chan,effect);
+ continue;
+ }
+ eff_op = effect & 0x0f;
+ inst = channel[chan].inst;
+ if(note)
+ channel[chan].slide = 0;
+
+ switch (effect & 0xf0) { // effect handling
+ case 0: // global effect
+ /* The following fx are unimplemented on purpose:
+ * 02 - Slide Mainvolume up
+ * 03 - Slide Mainvolume down (here: fade in)
+ * 04 - Set Mainvolume to 0
+ *
+ * This is because i've never seen any HSC modules using the fx this way.
+ * All modules use the fx the way, i've implemented it.
+ */
+ switch(eff_op) {
+ case 1: pattbreak++; break; // jump to next pattern
+ case 3: fadein = 31; break; // fade in (divided by 2)
+ case 5: mode6 = 1; break; // 6 voice mode on
+ case 6: mode6 = 0; break; // 6 voice mode off
+ }
+ break;
+ case 0x20:
+ case 0x10: // manual slides
+ if (effect & 0x10) {
+ channel[chan].freq += eff_op;
+ channel[chan].slide += eff_op;
+ } else {
+ channel[chan].freq -= eff_op;
+ channel[chan].slide -= eff_op;
+ }
+ if(!note)
+ setfreq(chan,channel[chan].freq);
+ break;
+ case 0x50: // set percussion instrument (unimplemented)
+ break;
+ case 0x60: // set feedback
+ opl->write(0xc0 + chan, (instr[channel[chan].inst][8] & 1) + (eff_op << 1));
+ break;
+ case 0xa0: // set carrier volume
+ vol = eff_op << 2;
+ opl->write(0x43 + op_table[chan], vol | (instr[channel[chan].inst][2] & ~63));
+ break;
+ case 0xb0: // set modulator volume
+ vol = eff_op << 2;
+ if (instr[inst][8] & 1)
+ opl->write(0x40 + op_table[chan], vol | (instr[channel[chan].inst][3] & ~63));
+ else
+ opl->write(0x40 + op_table[chan],vol | (instr[inst][3] & ~63));
+ break;
+ case 0xc0: // set instrument volume
+ db = eff_op << 2;
+ opl->write(0x43 + op_table[chan], db | (instr[channel[chan].inst][2] & ~63));
+ if (instr[inst][8] & 1)
+ opl->write(0x40 + op_table[chan], db | (instr[channel[chan].inst][3] & ~63));
+ break;
+ case 0xd0: pattbreak++; songpos = eff_op; songend = 1; break; // position jump
+ case 0xf0: // set speed
+ speed = eff_op;
+ del = ++speed;
+ break;
+ }
+
+ if(fadein) // fade-in volume setting
+ setvolume(chan,fadein*2,fadein*2);
+
+ if(!note) // note handling
+ continue;
+ note--;
+
+ if ((note == 0x7f-1) || ((note/12) & ~7)) { // pause (7fh)
+ adl_freq[chan] &= ~32;
+ opl->write(0xb0 + chan,adl_freq[chan]);
+ continue;
+ }
+
+ // play the note
+ if(mtkmode) // imitate MPU-401 Trakker bug
+ note--;
+ Okt = ((note/12) & 7) << 2;
+ Fnr = note_table[(note % 12)] + instr[inst][11] + channel[chan].slide;
+ channel[chan].freq = Fnr;
+ if(!mode6 || chan < 6)
+ adl_freq[chan] = Okt | 32;
+ else
+ adl_freq[chan] = Okt; // never set key for drums
+ opl->write(0xb0 + chan, 0);
+ setfreq(chan,Fnr);
+ if(mode6) {
+ switch(chan) { // play drums
+ case 6: opl->write(0xbd,bd & ~16); bd |= 48; break; // bass drum
+ case 7: opl->write(0xbd,bd & ~1); bd |= 33; break; // hihat
+ case 8: opl->write(0xbd,bd & ~2); bd |= 34; break; // cymbal
+ }
+ opl->write(0xbd,bd);
+ }
+ }
+
+ del = speed; // player speed-timing
+ if(pattbreak) { // do post-effect handling
+ pattpos=0; // pattern break!
+ pattbreak=0;
+ songpos++;
+ songpos %= 50;
+ if(!songpos)
+ songend = 1;
+ } else {
+ pattpos++;
+ pattpos &= 63; // advance in pattern data
+ if (!pattpos) {
+ songpos++;
+ songpos %= 50;
+ if(!songpos)
+ songend = 1;
+ }
+ }
+ return !songend; // still playing
+}
+
+void ChscPlayer::rewind(int subsong)
+{
+ int i; // counter
+
+ // rewind HSC player
+ pattpos = 0; songpos = 0; pattbreak = 0; speed = 2;
+ del = 1; songend = 0; mode6 = 0; bd = 0; fadein = 0;
+
+ opl->init(); // reset OPL chip
+ opl->write(1,32); opl->write(8,128); opl->write(0xbd,0);
+
+ for(i=0;i<9;i++)
+ setinstr((char) i,(char) i); // init channels
+}
+
+unsigned int ChscPlayer::getpatterns()
+{
+ unsigned char poscnt,pattcnt=0;
+
+ // count patterns
+ for(poscnt=0;poscnt<51 && song[poscnt] != 0xff;poscnt++)
+ if(song[poscnt] > pattcnt)
+ pattcnt = song[poscnt];
+
+ return (pattcnt+1);
+}
+
+unsigned int ChscPlayer::getorders()
+{
+ unsigned char poscnt;
+
+ // count positions
+ for(poscnt=0;poscnt<51;poscnt++)
+ if(song[poscnt] == 0xff)
+ break;
+
+ return poscnt;
+}
+
+unsigned int ChscPlayer::getinstruments()
+{
+ unsigned char instcnt,instnum=0,i;
+ bool isinst;
+
+ // count instruments
+ for(instcnt=0;instcnt<128;instcnt++) {
+ isinst = false;
+ for(i=0;i<12;i++)
+ if(instr[instcnt][i])
+ isinst = true;
+ if(isinst)
+ instnum++;
+ }
+
+ return instnum;
+}
+
+/*** private methods *************************************/
+
+void ChscPlayer::setfreq(unsigned char chan, unsigned short freq)
+{
+ adl_freq[chan] = (adl_freq[chan] & ~3) | (freq >> 8);
+
+ opl->write(0xa0 + chan, freq & 0xff);
+ opl->write(0xb0 + chan, adl_freq[chan]);
+}
+
+void ChscPlayer::setvolume(unsigned char chan, int volc, int volm)
+{
+ unsigned char *ins = instr[channel[chan].inst];
+ char op = op_table[chan];
+
+ opl->write(0x43 + op,volc | (ins[2] & ~63));
+ if (ins[8] & 1) // carrier
+ opl->write(0x40 + op,volm | (ins[3] & ~63));
+ else
+ opl->write(0x40 + op, ins[3]); // modulator
+}
+
+void ChscPlayer::setinstr(unsigned char chan, unsigned char insnr)
+{
+ unsigned char *ins = instr[insnr];
+ char op = op_table[chan];
+
+ channel[chan].inst = insnr; // set internal instrument
+ opl->write(0xb0 + chan,0); // stop old note
+
+ // set instrument
+ opl->write(0xc0 + chan, ins[8]);
+ opl->write(0x23 + op, ins[0]); // carrier
+ opl->write(0x20 + op, ins[1]); // modulator
+ opl->write(0x63 + op, ins[4]); // bits 0..3 = decay; 4..7 = attack
+ opl->write(0x60 + op, ins[5]);
+ opl->write(0x83 + op, ins[6]); // 0..3 = release; 4..7 = sustain
+ opl->write(0x80 + op, ins[7]);
+ opl->write(0xe3 + op, ins[9]); // bits 0..1 = Wellenform
+ opl->write(0xe0 + op, ins[10]);
+ setvolume(chan, ins[2] & 63, ins[3] & 63);
+}
diff --git a/plugins/adplug/adplug/hsc.h b/plugins/adplug/adplug/hsc.h
new file mode 100644
index 00000000..d09b8906
--- /dev/null
+++ b/plugins/adplug/adplug/hsc.h
@@ -0,0 +1,75 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2002 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * hsc.h - HSC Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_HSCPLAYER
+#define H_ADPLUG_HSCPLAYER
+
+#include "player.h"
+
+class ChscPlayer: public CPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ ChscPlayer(Copl *newopl): CPlayer(newopl), mtkmode(0) {}
+
+ bool load(const std::string &fn, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh() { return 18.2f; }; // refresh rate is fixed at 18.2Hz
+
+ std::string gettype() { return std::string("HSC Adlib Composer / HSC-Tracker"); }
+ unsigned int getpatterns();
+ unsigned int getpattern() { return song[songpos]; }
+ unsigned int getorders();
+ unsigned int getorder() { return songpos; }
+ unsigned int getrow() { return pattpos; }
+ unsigned int getspeed() { return speed; }
+ unsigned int getinstruments();
+
+ protected:
+ struct hscnote {
+ unsigned char note, effect;
+ }; // note type in HSC pattern
+
+ struct hscchan {
+ unsigned char inst; // current instrument
+ signed char slide; // used for manual slide-effects
+ unsigned short freq; // actual replaying frequency
+ }; // HSC channel data
+
+ hscchan channel[9]; // player channel-info
+ unsigned char instr[128][12]; // instrument data
+ unsigned char song[0x80]; // song-arrangement (MPU-401 Trakker enhanced)
+ hscnote patterns[50][64*9]; // pattern data
+ unsigned char pattpos,songpos, // various bytes & flags
+ pattbreak,songend,mode6,bd,fadein;
+ unsigned int speed,del;
+ unsigned char adl_freq[9]; // adlib frequency registers
+ int mtkmode; // flag: MPU-401 Trakker mode on/off
+
+ private:
+ void setfreq(unsigned char chan, unsigned short freq);
+ void setvolume(unsigned char chan, int volc, int volm);
+ void setinstr(unsigned char chan, unsigned char insnr);
+};
+
+#endif
diff --git a/plugins/adplug/adplug/hsp.cpp b/plugins/adplug/adplug/hsp.cpp
new file mode 100644
index 00000000..0430f6d7
--- /dev/null
+++ b/plugins/adplug/adplug/hsp.cpp
@@ -0,0 +1,68 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2004 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * hsp.cpp - HSP Loader by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string.h>
+
+#include "hsp.h"
+
+CPlayer *ChspLoader::factory(Copl *newopl)
+{
+ return new ChspLoader(newopl);
+}
+
+bool ChspLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ unsigned long i, j, orgsize, filesize;
+ unsigned char *cmp, *org;
+
+ // file validation section
+ if(!fp.extension(filename, ".hsp")) { fp.close(f); return false; }
+
+ filesize = fp.filesize(f);
+ orgsize = f->readInt(2);
+ if(orgsize > 59187) { fp.close(f); return false; }
+
+ // load section
+ cmp = new unsigned char[filesize];
+ for(i = 0; i < filesize; i++) cmp[i] = f->readInt(1);
+ fp.close(f);
+
+ org = new unsigned char[orgsize];
+ for(i = 0, j = 0; i < filesize; j += cmp[i], i += 2) { // RLE decompress
+ if(j >= orgsize) break; // memory boundary check
+ memset(org + j, cmp[i + 1], j + cmp[i] < orgsize ? cmp[i] : orgsize - j - 1);
+ }
+ delete [] cmp;
+
+ memcpy(instr, org, 128 * 12); // instruments
+ for(i = 0; i < 128; i++) { // correct instruments
+ instr[i][2] ^= (instr[i][2] & 0x40) << 1;
+ instr[i][3] ^= (instr[i][3] & 0x40) << 1;
+ instr[i][11] >>= 4; // slide
+ }
+ memcpy(song, org + 128 * 12, 51); // tracklist
+ memcpy(patterns, org + 128 * 12 + 51, orgsize - 128 * 12 - 51); // patterns
+ delete [] org;
+
+ rewind(0);
+ return true;
+}
diff --git a/plugins/adplug/adplug/hsp.h b/plugins/adplug/adplug/hsp.h
new file mode 100644
index 00000000..86fa9d69
--- /dev/null
+++ b/plugins/adplug/adplug/hsp.h
@@ -0,0 +1,39 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2004 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * hsp.h: HSC Packed Loader by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_HSPLOADER
+#define H_ADPLUG_HSPLOADER
+
+#include "hsc.h"
+
+class ChspLoader: public ChscPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ ChspLoader(Copl *newopl)
+ : ChscPlayer(newopl)
+ {};
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+};
+
+#endif
diff --git a/plugins/adplug/adplug/hybrid.cpp b/plugins/adplug/adplug/hybrid.cpp
new file mode 100644
index 00000000..cff08c88
--- /dev/null
+++ b/plugins/adplug/adplug/hybrid.cpp
@@ -0,0 +1,248 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * [xad] HYBRID player, by Riven the Mage <riven@ok.ru>
+ */
+
+/*
+ - discovery -
+
+ file(s) : HYBRID.EXE
+ type : Hybrid cracktro for Apache Longbow CD-RIP
+ tune : from 'Mig-29 Super Fulcrum' game by Domark
+ player : from 'Mig-29 Super Fulcrum' game by Domark
+*/
+
+#include "hybrid.h"
+#include "debug.h"
+
+const unsigned char CxadhybridPlayer::hyb_adlib_registers[99] =
+{
+ 0xE0, 0x60, 0x80, 0x20, 0x40, 0xE3, 0x63, 0x83, 0x23, 0x43, 0xC0,
+ 0xE1, 0x61, 0x81, 0x21, 0x41, 0xE4, 0x64, 0x84, 0x24, 0x44, 0xC1,
+ 0xE2, 0x62, 0x82, 0x22, 0x42, 0xE5, 0x65, 0x85, 0x25, 0x45, 0xC2,
+ 0xE8, 0x68, 0x88, 0x28, 0x48, 0xEB, 0x6B, 0x8B, 0x2B, 0x4B, 0xC3,
+ 0xE9, 0x69, 0x89, 0x29, 0x49, 0xEC, 0x6C, 0x8C, 0x2C, 0x4C, 0xC4,
+ 0xEA, 0x6A, 0x8A, 0x2A, 0x4A, 0xED, 0x6D, 0x8D, 0x2D, 0x4D, 0xC5,
+ 0xF0, 0x70, 0x90, 0x30, 0x50, 0xF3, 0x73, 0x93, 0x33, 0x53, 0xC6,
+ 0xF1, 0x71, 0x91, 0x31, 0x51, 0xF4, 0x74, 0x94, 0x34, 0x54, 0xC7,
+ 0xF2, 0x72, 0x92, 0x32, 0x52, 0xF5, 0x75, 0x95, 0x35, 0x55, 0xC8
+};
+
+const unsigned short CxadhybridPlayer::hyb_notes[98] =
+{
+ 0x0000, 0x0000,
+ 0x016B, 0x0181, 0x0198, 0x01B0, 0x01CA, 0x01E5, 0x0202, 0x0220, 0x0241, 0x0263, 0x0287, 0x02AE,
+ 0x056B, 0x0581, 0x0598, 0x05B0, 0x05CA, 0x05E5, 0x0602, 0x0620, 0x0641, 0x0663, 0x0687, 0x06AE,
+ 0x096B, 0x0981, 0x0998, 0x09B0, 0x09CA, 0x09E5, 0x0A02, 0x0A20, 0x0A41, 0x0A63, 0x0A87, 0x0AAE,
+ 0x0D6B, 0x0D81, 0x0D98, 0x0DB0, 0x0DCA, 0x0DE5, 0x0E02, 0x0E20, 0x0E41, 0x0E63, 0x0E87, 0x0EAE,
+ 0x116B, 0x1181, 0x1198, 0x11B0, 0x11CA, 0x11E5, 0x1202, 0x1220, 0x1241, 0x1263, 0x1287, 0x12AE,
+ 0x156B, 0x1581, 0x1598, 0x15B0, 0x15CA, 0x15E5, 0x1602, 0x1620, 0x1641, 0x1663, 0x1687, 0x16AE,
+ 0x196B, 0x1981, 0x1998, 0x19B0, 0x19CA, 0x19E5, 0x1A02, 0x1A20, 0x1A41, 0x1A63, 0x1A87, 0x1AAE,
+ 0x1D6B, 0x1D81, 0x1D98, 0x1DB0, 0x1DCA, 0x1DE5, 0x1E02, 0x1E20, 0x1E41, 0x1E63, 0x1E87, 0x1EAE
+};
+
+const unsigned char CxadhybridPlayer::hyb_default_instrument[11] =
+{
+ 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00
+};
+
+CPlayer *CxadhybridPlayer::factory(Copl *newopl)
+{
+ return new CxadhybridPlayer(newopl);
+}
+
+bool CxadhybridPlayer::xadplayer_load()
+{
+ if(xad.fmt != HYBRID)
+ return false;
+
+ // load instruments
+ hyb.inst = (hyb_instrument *)&tune[0];
+
+ // load order
+ hyb.order = &tune[0x1D4];
+
+ return true;
+}
+
+void CxadhybridPlayer::xadplayer_rewind(int subsong)
+{
+ int i;
+
+ hyb.order_pos = 0;
+ hyb.pattern_pos = 0;
+
+ hyb.speed = 6;
+ hyb.speed_counter = 1;
+
+ plr.speed = 1;
+
+ // init channel data
+ for(i=0;i<9;i++)
+ {
+ hyb.channel[i].freq = 0x2000;
+ hyb.channel[i].freq_slide = 0x0000;
+ }
+
+ // basic OPL init
+ opl_write(0x01, 0x20);
+ opl_write(0xBD, 0x40);
+ opl_write(0x08, 0x00);
+
+ // init OPL channels
+ for(i=0;i<9;i++)
+ {
+ for(int j=0;j<11;j++)
+ opl_write(hyb_adlib_registers[i*11+j], 0x00 /* hyb_default_instrument[j] */ );
+
+ opl_write(0xA0+i, 0x00);
+ opl_write(0xB0+i, 0x20);
+ }
+}
+
+void CxadhybridPlayer::xadplayer_update()
+{
+ int i,j;
+ unsigned char patpos,ordpos;
+
+ if (--hyb.speed_counter)
+ goto update_slides;
+
+ hyb.speed_counter = hyb.speed;
+
+ patpos = hyb.pattern_pos;
+ ordpos = hyb.order_pos;
+
+ // process channels
+ for(i=0;i<9;i++)
+ {
+ unsigned char *pos = &tune[0xADE + (hyb.order[hyb.order_pos*9 + i] * 64 * 2) + (patpos * 2)];
+ // read event
+ unsigned short event = (pos[1] << 8) + pos[0];
+
+#ifdef DEBUG
+ AdPlug_LogWrite("track %02X, channel %02X, event %04X:\n", hyb.order[hyb.order_pos*9 + i], i, event );
+#endif
+
+ // calculate variables
+ unsigned char note = event >> 9;
+ unsigned char ins = ((event & 0x01F0) >> 4);
+ unsigned char slide = event & 0x000F;
+
+ // play event
+ switch(note)
+ {
+ case 0x7D: // 0x7D: Set Speed
+ hyb.speed = event & 0xFF;
+ break;
+ case 0x7E: // 0x7E: Jump Position
+ hyb.order_pos = event & 0xFF;
+ hyb.pattern_pos = 0x3F;
+
+ // jumpback ?
+ if (hyb.order_pos <= ordpos)
+ plr.looping = 1;
+
+ break;
+ case 0x7F: // 0x7F: Pattern Break
+ hyb.pattern_pos = 0x3F;
+ break;
+ default:
+
+ // is instrument ?
+ if (ins)
+ for(j=0;j<11;j++)
+ opl_write(hyb_adlib_registers[i*11+j], *((unsigned char *)&hyb.inst[ins-1] + 7 + j)); // +7 = skip name...
+
+ // is note ?
+ if (note)
+ {
+ hyb.channel[i].freq = hyb_notes[note];
+ hyb.channel[i].freq_slide = 0;
+ }
+
+ // is slide ?
+ if (slide)
+ {
+ hyb.channel[i].freq_slide = (((slide >> 3) * -1) * (slide & 7)) << 1;
+
+ if (slide & 0x80)
+ slide = -(slide & 7);
+ }
+
+ // set frequency
+ if (!(hyb.channel[i].freq & 0x2000))
+ {
+ opl_write(0xA0+i, hyb.channel[i].freq & 0xFF);
+ opl_write(0xB0+i, hyb.channel[i].freq >> 8);
+
+ hyb.channel[i].freq |= 0x2000;
+
+ opl_write(0xA0+i, hyb.channel[i].freq & 0xFF);
+ opl_write(0xB0+i, hyb.channel[i].freq >> 8);
+ }
+
+ break;
+ }
+ }
+
+ hyb.pattern_pos++;
+
+ // end of pattern ?
+ if (hyb.pattern_pos >= 0x40)
+ {
+ hyb.pattern_pos = 0;
+
+ hyb.order_pos++;
+ }
+
+update_slides:
+#ifdef DEBUG
+ AdPlug_LogWrite("slides:\n");
+#endif
+ // update fine frequency slides
+ for(i=0;i<9;i++)
+ if (hyb.channel[i].freq_slide)
+ {
+ hyb.channel[i].freq = (((hyb.channel[i].freq & 0x1FFF) + hyb.channel[i].freq_slide) & 0x1FFF) | 0x2000;
+
+ opl_write(0xA0+i, hyb.channel[i].freq & 0xFF);
+ opl_write(0xB0+i, hyb.channel[i].freq >> 8);
+ }
+}
+
+float CxadhybridPlayer::xadplayer_getrefresh()
+{
+ return 50.0f;
+}
+
+std::string CxadhybridPlayer::xadplayer_gettype()
+{
+ return (std::string("xad: hybrid player"));
+}
+
+std::string CxadhybridPlayer::xadplayer_getinstrument(unsigned int i)
+{
+ return (std::string(hyb.inst[i].name,7));
+}
+
+unsigned int CxadhybridPlayer::xadplayer_getinstruments()
+{
+ return 26;
+}
diff --git a/plugins/adplug/adplug/hybrid.h b/plugins/adplug/adplug/hybrid.h
new file mode 100644
index 00000000..65d39d34
--- /dev/null
+++ b/plugins/adplug/adplug/hybrid.h
@@ -0,0 +1,80 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * [xad] HYBRID player, by Riven the Mage <riven@ok.ru>
+ */
+
+#include "xad.h"
+
+class CxadhybridPlayer: public CxadPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CxadhybridPlayer(Copl *newopl): CxadPlayer(newopl)
+ { }
+
+protected:
+ struct hyb_instrument
+ {
+ char name[7];
+ unsigned char mod_wave;
+ unsigned char mod_AD;
+ unsigned char mod_SR;
+ unsigned char mod_crtl;
+ unsigned char mod_volume;
+ unsigned char car_wave;
+ unsigned char car_AD;
+ unsigned char car_SR;
+ unsigned char car_crtl;
+ unsigned char car_volume;
+ unsigned char connect;
+ };
+
+ struct
+ {
+ unsigned char order_pos;
+ unsigned char pattern_pos;
+
+ unsigned char *order;
+
+ hyb_instrument *inst;
+
+ struct
+ {
+ unsigned short freq;
+ unsigned short freq_slide;
+ } channel[9];
+
+ unsigned char speed;
+ unsigned char speed_counter;
+ } hyb;
+ //
+ bool xadplayer_load();
+ void xadplayer_rewind(int subsong);
+ void xadplayer_update();
+ float xadplayer_getrefresh();
+ std::string xadplayer_gettype();
+ std::string xadplayer_getinstrument(unsigned int i);
+ unsigned int xadplayer_getinstruments();
+
+private:
+ static const unsigned char hyb_adlib_registers[99];
+ static const unsigned short hyb_notes[98];
+ static const unsigned char hyb_default_instrument[11];
+};
diff --git a/plugins/adplug/adplug/hyp.cpp b/plugins/adplug/adplug/hyp.cpp
new file mode 100644
index 00000000..7ed3a315
--- /dev/null
+++ b/plugins/adplug/adplug/hyp.cpp
@@ -0,0 +1,125 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * [xad] HYP player, by Riven the Mage <riven@ok.ru>
+ */
+
+/*
+ - discovery -
+
+ file(s) : HT-EF2.COM, HT-EF3.COM
+ type : Eiserne Front BBStro
+ tune : by Shadowdancer [Hypnosis]
+ player : by (?)Hetero [LKCC/SAC]
+*/
+
+#include "hyp.h"
+#include "debug.h"
+
+const unsigned char CxadhypPlayer::hyp_adlib_registers[99] =
+{
+ 0x20, 0x23, 0x40, 0x43, 0x60, 0x63, 0x80, 0x83, 0xA0, 0xB0, 0xC0,
+ 0x21, 0x24, 0x41, 0x44, 0x61, 0x64, 0x81, 0x84, 0xA1, 0xB1, 0xC1,
+ 0x22, 0x25, 0x42, 0x45, 0x62, 0x65, 0x82, 0x85, 0xA2, 0xB2, 0xC2,
+ 0x28, 0x2B, 0x48, 0x4B, 0x68, 0x6B, 0x88, 0x8B, 0xA3, 0xB3, 0xC3,
+ 0x29, 0x2C, 0x49, 0x4C, 0x69, 0x6C, 0x89, 0x8C, 0xA4, 0xB4, 0xC4,
+ 0x2A, 0x2D, 0x4A, 0x4D, 0x6A, 0x6D, 0x8A, 0x8D, 0xA5, 0xB5, 0xC5,
+ 0x30, 0x33, 0x50, 0x53, 0x70, 0x73, 0x90, 0x93, 0xA6, 0xB6, 0xC6,
+ 0x31, 0x34, 0x51, 0x54, 0x71, 0x74, 0x91, 0x94, 0xA7, 0xB7, 0xC7,
+ 0x32, 0x35, 0x52, 0x55, 0x72, 0x75, 0x92, 0x95, 0xA8, 0xB8, 0xC8
+};
+
+const unsigned short CxadhypPlayer::hyp_notes[73] =
+{
+ 0x0000, // by riven
+ 0x0956, 0x096B, 0x0980, 0x0998, 0x09B1, 0x09C9, 0x09E5, 0x0A03, 0x0A21,
+ 0x0A41, 0x0A63, 0x0A86, 0x0D56, 0x0D6B, 0x0D80, 0x0D98, 0x0DB1, 0x0DC9,
+ 0x0DE5, 0x0E03, 0x0E21, 0x0E41, 0x0E63, 0x0E86, 0x1156, 0x116B, 0x1180,
+ 0x1198, 0x11B1, 0x11C9, 0x11E5, 0x1203, 0x1221, 0x1241, 0x1263, 0x1286,
+ 0x1556, 0x156B, 0x1580, 0x1598, 0x15B1, 0x15C9, 0x15E5, 0x1603, 0x1621,
+ 0x1641, 0x1663, 0x1686, 0x1956, 0x196B, 0x1980, 0x1998, 0x19B1, 0x19C9,
+ 0x19E5, 0x1A03, 0x1A21, 0x1A41, 0x1A63, 0x1A86, 0x1D56, 0x1D6B, 0x1D80,
+ 0x1D98, 0x1DB1, 0x1DC9, 0x1DE5, 0x1E03, 0x1E21, 0x1E41, 0x1E63, 0x1E86
+};
+
+CPlayer *CxadhypPlayer::factory(Copl *newopl)
+{
+ return new CxadhypPlayer(newopl);
+}
+
+void CxadhypPlayer::xadplayer_rewind(int subsong)
+{
+ int i;
+
+ plr.speed = tune[5];
+
+ opl_write(0xBD,0xC0);
+
+ for(i=0; i<9; i++)
+ adlib[0xB0+i] = 0;
+
+ // define instruments
+ for(i=0; i<99; i++)
+ opl_write(hyp_adlib_registers[i], tune[6+i]);
+
+ hyp.pointer = 0x69;
+}
+
+void CxadhypPlayer::xadplayer_update()
+{
+ for(int i=0; i<9; i++)
+ {
+ unsigned char event = tune[hyp.pointer++];
+
+ if (event)
+ {
+ unsigned short freq = hyp_notes[event & 0x3F];
+
+ unsigned char lofreq = (freq & 0xFF);
+ unsigned char hifreq = (freq >> 8);
+
+ opl_write(0xB0+i, adlib[0xB0+i]);
+
+ if (!(event & 0x40))
+ {
+ opl_write(0xA0+i, lofreq);
+ opl_write(0xB0+i, hifreq | 0x20);
+ }
+
+ adlib[0xB0+i] &= 0xDF;
+ }
+ }
+
+ hyp.pointer += 3;
+
+ if (hyp.pointer >= tune_size)
+ {
+ hyp.pointer = 0x69;
+ plr.looping = 1;
+ }
+}
+
+float CxadhypPlayer::xadplayer_getrefresh()
+{
+ return 60.0f;
+}
+
+std::string CxadhypPlayer::xadplayer_gettype()
+{
+ return std::string("xad: hypnosis player");
+}
diff --git a/plugins/adplug/adplug/hyp.h b/plugins/adplug/adplug/hyp.h
new file mode 100644
index 00000000..e2a5bf5a
--- /dev/null
+++ b/plugins/adplug/adplug/hyp.h
@@ -0,0 +1,53 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * [xad] HYP player, by Riven the Mage <riven@ok.ru>
+ */
+
+#include "xad.h"
+
+class CxadhypPlayer: public CxadPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CxadhypPlayer(Copl *newopl): CxadPlayer(newopl)
+ { }
+
+protected:
+ struct
+ {
+ unsigned short pointer;
+ } hyp;
+ //
+ bool xadplayer_load()
+ {
+ if(xad.fmt == HYP)
+ return true;
+ else
+ return false;
+ }
+ void xadplayer_rewind(int subsong);
+ void xadplayer_update();
+ float xadplayer_getrefresh();
+ std::string xadplayer_gettype();
+
+private:
+ static const unsigned char hyp_adlib_registers[99];
+ static const unsigned short hyp_notes[73];
+};
diff --git a/plugins/adplug/adplug/imf.cpp b/plugins/adplug/adplug/imf.cpp
new file mode 100644
index 00000000..37af7aca
--- /dev/null
+++ b/plugins/adplug/adplug/imf.cpp
@@ -0,0 +1,196 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2006 Simon Peter <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * imf.cpp - IMF Player by Simon Peter <dn.tlp@gmx.net>
+ *
+ * FILE FORMAT:
+ * There seem to be 2 different flavors of IMF formats out there. One version
+ * contains just the raw IMF music data. In this case, the first word of the
+ * file is always 0 (because the music data starts this way). This is already
+ * the music data! So read in the entire file and play it.
+ *
+ * If this word is greater than 0, it specifies the size of the following
+ * song data in bytes. In this case, the file has a footer that contains
+ * arbitrary infos about it. Mostly, this is plain ASCII text with some words
+ * of the author. Read and play the specified amount of song data and display
+ * the remaining data as ASCII text.
+ *
+ * NOTES:
+ * This player handles the two above mentioned formats, as well as a third
+ * type, invented by Martin Fernandez <mfernan@cnba.uba.ar>, that's got a
+ * proper header to add title/game name information. After the header starts
+ * the normal IMF file in one of the two above mentioned formats.
+ *
+ * This player also handles a special footer format by Adam Nielsen,
+ * which has defined fields of information about the song, the author
+ * and more.
+ */
+
+#include <string.h>
+
+#include "imf.h"
+#include "database.h"
+
+/*** public methods *************************************/
+
+CPlayer *CimfPlayer::factory(Copl *newopl)
+{
+ return new CimfPlayer(newopl);
+}
+
+bool CimfPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ unsigned long fsize, flsize, mfsize = 0;
+ unsigned int i;
+
+ // file validation section
+ {
+ char header[5];
+ int version;
+
+ f->readString(header, 5);
+ version = f->readInt(1);
+
+ if(strncmp(header, "ADLIB", 5) || version != 1) {
+ if(!fp.extension(filename, ".imf") && !fp.extension(filename, ".wlf")) {
+ // It's no IMF file at all
+ fp.close(f);
+ return false;
+ } else
+ f->seek(0); // It's a normal IMF file
+ } else {
+ // It's a IMF file with header
+ track_name = f->readString('\0');
+ game_name = f->readString('\0');
+ f->ignore(1);
+ mfsize = f->pos() + 2;
+ }
+ }
+
+ // load section
+ if(mfsize)
+ fsize = f->readInt(4);
+ else
+ fsize = f->readInt(2);
+ flsize = fp.filesize(f);
+ if(!fsize) { // footerless file (raw music data)
+ if(mfsize)
+ f->seek(-4, binio::Add);
+ else
+ f->seek(-2, binio::Add);
+ size = (flsize - mfsize) / 4;
+ } else // file has got a footer
+ size = fsize / 4;
+
+ data = new Sdata[size];
+ for(i = 0; i < size; i++) {
+ data[i].reg = f->readInt(1); data[i].val = f->readInt(1);
+ data[i].time = f->readInt(2);
+ }
+
+ // read footer, if any
+ if(fsize && (fsize < flsize - 2 - mfsize))
+ if(f->readInt(1) == 0x1a) {
+ // Adam Nielsen's footer format
+ track_name = f->readString();
+ author_name = f->readString();
+ remarks = f->readString();
+ } else {
+ // Generic footer
+ unsigned long footerlen = flsize - fsize - 2 - mfsize;
+
+ footer = new char[footerlen + 1];
+ f->readString(footer, footerlen);
+ footer[footerlen] = '\0'; // Make ASCIIZ string
+ }
+
+ rate = getrate(filename, fp, f);
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+bool CimfPlayer::update()
+{
+ do {
+ opl->write(data[pos].reg,data[pos].val);
+ del = data[pos].time;
+ pos++;
+ } while(!del && pos < size);
+
+ if(pos >= size) {
+ pos = 0;
+ songend = true;
+ }
+ else timer = rate / (float)del;
+
+ return !songend;
+}
+
+void CimfPlayer::rewind(int subsong)
+{
+ pos = 0; del = 0; timer = rate; songend = false;
+ opl->init(); opl->write(1,32); // go to OPL2 mode
+}
+
+std::string CimfPlayer::gettitle()
+{
+ std::string title;
+
+ title = track_name;
+
+ if(!track_name.empty() && !game_name.empty())
+ title += " - ";
+
+ title += game_name;
+
+ return title;
+}
+
+std::string CimfPlayer::getdesc()
+{
+ std::string desc;
+
+ if(footer)
+ desc = std::string(footer);
+
+ if(!remarks.empty() && footer)
+ desc += "\n\n";
+
+ desc += remarks;
+
+ return desc;
+}
+
+/*** private methods *************************************/
+
+float CimfPlayer::getrate(const std::string &filename, const CFileProvider &fp, binistream *f)
+{
+ if(db) { // Database available
+ f->seek(0, binio::Set);
+ CClockRecord *record = (CClockRecord *)db->search(CAdPlugDatabase::CKey(*f));
+ if (record && record->type == CAdPlugDatabase::CRecord::ClockSpeed)
+ return record->clock;
+ }
+
+ // Otherwise the database is either unavailable, or there's no entry for this file
+ if (fp.extension(filename, ".imf")) return 560.0f;
+ if (fp.extension(filename, ".wlf")) return 700.0f;
+ return 700.0f; // default speed for unknown files that aren't .IMF or .WLF
+}
diff --git a/plugins/adplug/adplug/imf.h b/plugins/adplug/adplug/imf.h
new file mode 100644
index 00000000..4c7d0daf
--- /dev/null
+++ b/plugins/adplug/adplug/imf.h
@@ -0,0 +1,68 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 Simon Peter <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * imf.h - IMF Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_IMFPLAYER
+#define H_ADPLUG_IMFPLAYER
+
+#include "player.h"
+
+class CimfPlayer: public CPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CimfPlayer(Copl *newopl)
+ : CPlayer(newopl), footer(0), data(0)
+ { }
+ ~CimfPlayer()
+ { if(data) delete [] data; if(footer) delete [] footer; };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh()
+ { return timer; };
+
+ std::string gettype()
+ { return std::string("IMF File Format"); }
+ std::string gettitle();
+ std::string getauthor()
+ { return author_name; }
+ std::string getdesc();
+
+protected:
+ unsigned long pos, size;
+ unsigned short del;
+ bool songend;
+ float rate, timer;
+ char *footer;
+ std::string track_name, game_name, author_name, remarks;
+
+ struct Sdata {
+ unsigned char reg, val;
+ unsigned short time;
+ } *data;
+
+private:
+ float getrate(const std::string &filename, const CFileProvider &fp, binistream *f);
+};
+
+#endif
diff --git a/plugins/adplug/adplug/kemuopl.h b/plugins/adplug/adplug/kemuopl.h
new file mode 100644
index 00000000..d2ca6e28
--- /dev/null
+++ b/plugins/adplug/adplug/kemuopl.h
@@ -0,0 +1,61 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * kemuopl.h - Emulated OPL using Ken Silverman's emulator, by Simon Peter
+ * <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_KEMUOPL
+#define H_ADPLUG_KEMUOPL
+
+#include "opl.h"
+extern "C" {
+#include "adlibemu.h"
+}
+
+class CKemuopl: public Copl
+{
+public:
+ CKemuopl(int rate, bool bit16, bool usestereo)
+ : use16bit(bit16), stereo(usestereo)
+ {
+ adlibinit(rate, usestereo ? 2 : 1, bit16 ? 2 : 1);
+ currType = TYPE_OPL2;
+ };
+
+ void update(short *buf, int samples)
+ {
+ if(use16bit) samples *= 2;
+ if(stereo) samples *= 2;
+ adlibgetsample(buf, samples);
+ }
+
+ // template methods
+ void write(int reg, int val)
+ {
+ if(currChip == 0)
+ adlib0(reg, val);
+ };
+
+ void init() {};
+
+private:
+ bool use16bit,stereo;
+};
+
+#endif
diff --git a/plugins/adplug/adplug/ksm.cpp b/plugins/adplug/adplug/ksm.cpp
new file mode 100644
index 00000000..c12f3d8f
--- /dev/null
+++ b/plugins/adplug/adplug/ksm.cpp
@@ -0,0 +1,336 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ksm.cpp - KSM Player for AdPlug by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string.h>
+
+#include "ksm.h"
+#include "debug.h"
+
+const unsigned int CksmPlayer::adlibfreq[63] = {
+ 0,
+ 2390,2411,2434,2456,2480,2506,2533,2562,2592,2625,2659,2695,
+ 3414,3435,3458,3480,3504,3530,3557,3586,3616,3649,3683,3719,
+ 4438,4459,4482,4504,4528,4554,4581,4610,4640,4673,4707,4743,
+ 5462,5483,5506,5528,5552,5578,5605,5634,5664,5697,5731,5767,
+ 6486,6507,6530,6552,6576,6602,6629,6658,6688,6721,6755,6791,
+ 7510};
+
+/*** public methods **************************************/
+
+CPlayer *CksmPlayer::factory(Copl *newopl)
+{
+ return new CksmPlayer(newopl);
+}
+
+bool CksmPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f;
+ int i;
+ char *fn = new char[filename.length() + 9];
+
+ // file validation section
+ if(!fp.extension(filename, ".ksm")) {
+ AdPlug_LogWrite("CksmPlayer::load(,\"%s\"): File doesn't have '.ksm' "
+ "extension! Rejected!\n", filename.c_str());
+ return false;
+ }
+ AdPlug_LogWrite("*** CksmPlayer::load(,\"%s\") ***\n", filename.c_str());
+
+ // Load instruments from 'insts.dat'
+ strcpy(fn, filename.c_str());
+ for(i = strlen(fn) - 1; i >= 0; i--)
+ if(fn[i] == '/' || fn[i] == '\\')
+ break;
+ strcpy(fn + i + 1, "insts.dat");
+ AdPlug_LogWrite("Instruments file: \"%s\"\n", fn);
+ f = fp.open(fn);
+ delete [] fn;
+ if(!f) {
+ AdPlug_LogWrite("Couldn't open instruments file! Aborting!\n");
+ AdPlug_LogWrite("--- CksmPlayer::load ---\n");
+ return false;
+ }
+ loadinsts(f);
+ fp.close(f);
+
+ f = fp.open(filename); if(!f) return false;
+ for(i = 0; i < 16; i++) trinst[i] = f->readInt(1);
+ for(i = 0; i < 16; i++) trquant[i] = f->readInt(1);
+ for(i = 0; i < 16; i++) trchan[i] = f->readInt(1);
+ f->ignore(16);
+ for(i = 0; i < 16; i++) trvol[i] = f->readInt(1);
+ numnotes = f->readInt(2);
+ note = new unsigned long [numnotes];
+ for(i = 0; i < numnotes; i++) note[i] = f->readInt(4);
+ fp.close(f);
+
+ if(!trchan[11]) {
+ drumstat = 0;
+ numchans = 9;
+ } else {
+ drumstat = 32;
+ numchans = 6;
+ }
+
+ rewind(0);
+ AdPlug_LogWrite("--- CksmPlayer::load ---\n");
+ return true;
+}
+
+bool CksmPlayer::update()
+{
+ int quanter,chan,drumnum,freq,track,volevel,volval;
+ unsigned int i,j,bufnum;
+ unsigned long temp,templong;
+
+ count++;
+ if (count >= countstop)
+ {
+ bufnum = 0;
+ while (count >= countstop)
+ {
+ templong = note[nownote];
+ track = (int)((templong>>8)&15);
+ if ((templong&192) == 0)
+ {
+ i = 0;
+
+ while ((i < numchans) &&
+ ((chanfreq[i] != (templong&63)) ||
+ (chantrack[i] != ((templong>>8)&15))))
+ i++;
+ if (i < numchans)
+ {
+ databuf[bufnum] = (char)0; bufnum++;
+ databuf[bufnum] = (unsigned char)(0xb0+i); bufnum++;
+ databuf[bufnum] = (unsigned char)((adlibfreq[templong&63]>>8)&223); bufnum++;
+ chanfreq[i] = 0;
+ chanage[i] = 0;
+ }
+ }
+ else
+ {
+ volevel = trvol[track];
+ if ((templong&192) == 128)
+ {
+ volevel -= 4;
+ if (volevel < 0)
+ volevel = 0;
+ }
+ if ((templong&192) == 192)
+ {
+ volevel += 4;
+ if (volevel > 63)
+ volevel = 63;
+ }
+ if (track < 11)
+ {
+ temp = 0;
+ i = numchans;
+ for(j=0;j<numchans;j++)
+ if ((countstop - chanage[j] >= temp) && (chantrack[j] == track))
+ {
+ temp = countstop - chanage[j];
+ i = j;
+ }
+ if (i < numchans)
+ {
+ databuf[bufnum] = (char)0, bufnum++;
+ databuf[bufnum] = (unsigned char)(0xb0+i); bufnum++;
+ databuf[bufnum] = (unsigned char)0; bufnum++;
+ volval = (inst[trinst[track]][1]&192)+(volevel^63);
+ databuf[bufnum] = (char)0, bufnum++;
+ databuf[bufnum] = (unsigned char)(0x40+op_table[i]+3); bufnum++;
+ databuf[bufnum] = (unsigned char)volval; bufnum++;
+ databuf[bufnum] = (char)0, bufnum++;
+ databuf[bufnum] = (unsigned char)(0xa0+i); bufnum++;
+ databuf[bufnum] = (unsigned char)(adlibfreq[templong&63]&255); bufnum++;
+ databuf[bufnum] = (char)0, bufnum++;
+ databuf[bufnum] = (unsigned char)(0xb0+i); bufnum++;
+ databuf[bufnum] = (unsigned char)((adlibfreq[templong&63]>>8)|32); bufnum++;
+ chanfreq[i] = templong&63;
+ chanage[i] = countstop;
+ }
+ }
+ else if ((drumstat&32) > 0)
+ {
+ freq = adlibfreq[templong&63];
+ switch(track)
+ {
+ case 11: drumnum = 16; chan = 6; freq -= 2048; break;
+ case 12: drumnum = 8; chan = 7; freq -= 2048; break;
+ case 13: drumnum = 4; chan = 8; break;
+ case 14: drumnum = 2; chan = 8; break;
+ case 15: drumnum = 1; chan = 7; freq -= 2048; break;
+ }
+ databuf[bufnum] = (char)0, bufnum++;
+ databuf[bufnum] = (unsigned char)(0xa0+chan); bufnum++;
+ databuf[bufnum] = (unsigned char)(freq&255); bufnum++;
+ databuf[bufnum] = (char)0, bufnum++;
+ databuf[bufnum] = (unsigned char)(0xb0+chan); bufnum++;
+ databuf[bufnum] = (unsigned char)((freq>>8)&223); bufnum++;
+ databuf[bufnum] = (char)0, bufnum++;
+ databuf[bufnum] = (unsigned char)(0xbd); bufnum++;
+ databuf[bufnum] = (unsigned char)(drumstat&(255-drumnum)); bufnum++;
+ drumstat |= drumnum;
+ if ((track == 11) || (track == 12) || (track == 14))
+ {
+ volval = (inst[trinst[track]][1]&192)+(volevel^63);
+ databuf[bufnum] = (char)0, bufnum++;
+ databuf[bufnum] = (unsigned char)(0x40+op_table[chan]+3); bufnum++;
+ databuf[bufnum] = (unsigned char)(volval); bufnum++;
+ }
+ else
+ {
+ volval = (inst[trinst[track]][6]&192)+(volevel^63);
+ databuf[bufnum] = (char)0, bufnum++;
+ databuf[bufnum] = (unsigned char)(0x40+op_table[chan]); bufnum++;
+ databuf[bufnum] = (unsigned char)(volval); bufnum++;
+ }
+ databuf[bufnum] = (char)0, bufnum++;
+ databuf[bufnum] = (unsigned char)(0xbd); bufnum++;
+ databuf[bufnum] = (unsigned char)(drumstat); bufnum++;
+ }
+ }
+ nownote++;
+ if (nownote >= numnotes) {
+ nownote = 0;
+ songend = true;
+ }
+ templong = note[nownote];
+ if (nownote == 0)
+ count = (templong>>12)-1;
+ quanter = (240/trquant[(templong>>8)&15]);
+ countstop = (((templong>>12)+(quanter>>1)) / quanter) * quanter;
+ }
+ for(i=0;i<bufnum;i+=3)
+ opl->write(databuf[i+1],databuf[i+2]);
+ }
+ return !songend;
+}
+
+void CksmPlayer::rewind(int subsong)
+{
+ unsigned int i,j,k;
+ unsigned char instbuf[11];
+ unsigned long templong;
+
+ songend = false;
+ opl->init(); opl->write(1,32); opl->write(4,0); opl->write(8,0); opl->write(0xbd,drumstat);
+
+ if (trchan[11] == 1) {
+ for(i=0;i<11;i++)
+ instbuf[i] = inst[trinst[11]][i];
+ instbuf[1] = ((instbuf[1]&192)|(trvol[11])^63);
+ setinst(6,instbuf[0],instbuf[1],instbuf[2],instbuf[3],instbuf[4],instbuf[5],instbuf[6],instbuf[7],instbuf[8],instbuf[9],instbuf[10]);
+ for(i=0;i<5;i++)
+ instbuf[i] = inst[trinst[12]][i];
+ for(i=5;i<11;i++)
+ instbuf[i] = inst[trinst[15]][i];
+ instbuf[1] = ((instbuf[1]&192)|(trvol[12])^63);
+ instbuf[6] = ((instbuf[6]&192)|(trvol[15])^63);
+ setinst(7,instbuf[0],instbuf[1],instbuf[2],instbuf[3],instbuf[4],instbuf[5],instbuf[6],instbuf[7],instbuf[8],instbuf[9],instbuf[10]);
+ for(i=0;i<5;i++)
+ instbuf[i] = inst[trinst[14]][i];
+ for(i=5;i<11;i++)
+ instbuf[i] = inst[trinst[13]][i];
+ instbuf[1] = ((instbuf[1]&192)|(trvol[14])^63);
+ instbuf[6] = ((instbuf[6]&192)|(trvol[13])^63);
+ setinst(8,instbuf[0],instbuf[1],instbuf[2],instbuf[3],instbuf[4],instbuf[5],instbuf[6],instbuf[7],instbuf[8],instbuf[9],instbuf[10]);
+ }
+
+ for(i=0;i<numchans;i++)
+ {
+ chantrack[i] = 0;
+ chanage[i] = 0;
+ }
+ j = 0;
+ for(i=0;i<16;i++)
+ if ((trchan[i] > 0) && (j < numchans))
+ {
+ k = trchan[i];
+ while ((j < numchans) && (k > 0))
+ {
+ chantrack[j] = i;
+ k--;
+ j++;
+ }
+ }
+ for(i=0;i<numchans;i++)
+ {
+ for(j=0;j<11;j++)
+ instbuf[j] = inst[trinst[chantrack[i]]][j];
+ instbuf[1] = ((instbuf[1]&192)|(63-trvol[chantrack[i]]));
+ setinst(i,instbuf[0],instbuf[1],instbuf[2],instbuf[3],instbuf[4],instbuf[5],instbuf[6],instbuf[7],instbuf[8],instbuf[9],instbuf[10]);
+ chanfreq[i] = 0;
+ }
+ k = 0;
+ templong = *note;
+ count = (templong>>12)-1;
+ countstop = (templong>>12)-1;
+ nownote = 0;
+}
+
+std::string CksmPlayer::getinstrument(unsigned int n)
+{
+ if(trchan[n])
+ return std::string(instname[trinst[n]]);
+ else
+ return std::string();
+}
+
+/*** private methods *************************************/
+
+void CksmPlayer::loadinsts(binistream *f)
+{
+ int i, j;
+
+ for(i = 0; i < 256; i++) {
+ f->readString(instname[i], 20);
+ for(j = 0; j < 11; j++) inst[i][j] = f->readInt(1);
+ f->ignore(2);
+ }
+}
+
+void CksmPlayer::setinst(int chan,
+ unsigned char v0,unsigned char v1,unsigned char v2,
+ unsigned char v3,unsigned char v4,unsigned char v5,
+ unsigned char v6,unsigned char v7,unsigned char v8,
+ unsigned char v9,unsigned char v10)
+{
+ int offs;
+
+ opl->write(0xa0+chan,0);
+ opl->write(0xb0+chan,0);
+ opl->write(0xc0+chan,v10);
+ offs = op_table[chan];
+ opl->write(0x20+offs,v5);
+ opl->write(0x40+offs,v6);
+ opl->write(0x60+offs,v7);
+ opl->write(0x80+offs,v8);
+ opl->write(0xe0+offs,v9);
+ offs+=3;
+ opl->write(0x20+offs,v0);
+ opl->write(0x40+offs,v1);
+ opl->write(0x60+offs,v2);
+ opl->write(0x80+offs,v3);
+ opl->write(0xe0+offs,v4);
+}
diff --git a/plugins/adplug/adplug/ksm.h b/plugins/adplug/adplug/ksm.h
new file mode 100644
index 00000000..98d643ea
--- /dev/null
+++ b/plugins/adplug/adplug/ksm.h
@@ -0,0 +1,62 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ksm.h - KSM Player for AdPlug by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "player.h"
+
+class CksmPlayer: public CPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CksmPlayer(Copl *newopl)
+ : CPlayer(newopl), note(0)
+ { };
+ ~CksmPlayer()
+ { if(note) delete [] note; };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh()
+ { return 240.0f; };
+
+ std::string gettype()
+ { return std::string("Ken Silverman's Music Format"); };
+ unsigned int getinstruments()
+ { return 16; };
+ std::string getinstrument(unsigned int n);
+
+private:
+ static const unsigned int adlibfreq[63];
+
+ unsigned long count,countstop,chanage[18],*note;
+ unsigned short numnotes;
+ unsigned int nownote,numchans,drumstat;
+ unsigned char trinst[16],trquant[16],trchan[16],trvol[16],inst[256][11],databuf[2048],chanfreq[18],chantrack[18];
+ char instname[256][20];
+
+ bool songend;
+
+ void loadinsts(binistream *f);
+ void setinst(int chan,unsigned char v0,unsigned char v1,unsigned char v2,unsigned char v3,
+ unsigned char v4,unsigned char v5,unsigned char v6,unsigned char v7,
+ unsigned char v8,unsigned char v9,unsigned char v10);
+};
diff --git a/plugins/adplug/adplug/lds.cpp b/plugins/adplug/adplug/lds.cpp
new file mode 100644
index 00000000..fb70a067
--- /dev/null
+++ b/plugins/adplug/adplug/lds.cpp
@@ -0,0 +1,676 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2004 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * lds.cpp - LOUDNESS Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string.h>
+
+#include "lds.h"
+#include "debug.h"
+
+// Note frequency table (16 notes / octave)
+const unsigned short CldsPlayer::frequency[] = {
+ 343, 344, 345, 347, 348, 349, 350, 352, 353, 354, 356, 357, 358,
+ 359, 361, 362, 363, 365, 366, 367, 369, 370, 371, 373, 374, 375,
+ 377, 378, 379, 381, 382, 384, 385, 386, 388, 389, 391, 392, 393,
+ 395, 396, 398, 399, 401, 402, 403, 405, 406, 408, 409, 411, 412,
+ 414, 415, 417, 418, 420, 421, 423, 424, 426, 427, 429, 430, 432,
+ 434, 435, 437, 438, 440, 442, 443, 445, 446, 448, 450, 451, 453,
+ 454, 456, 458, 459, 461, 463, 464, 466, 468, 469, 471, 473, 475,
+ 476, 478, 480, 481, 483, 485, 487, 488, 490, 492, 494, 496, 497,
+ 499, 501, 503, 505, 506, 508, 510, 512, 514, 516, 518, 519, 521,
+ 523, 525, 527, 529, 531, 533, 535, 537, 538, 540, 542, 544, 546,
+ 548, 550, 552, 554, 556, 558, 560, 562, 564, 566, 568, 571, 573,
+ 575, 577, 579, 581, 583, 585, 587, 589, 591, 594, 596, 598, 600,
+ 602, 604, 607, 609, 611, 613, 615, 618, 620, 622, 624, 627, 629,
+ 631, 633, 636, 638, 640, 643, 645, 647, 650, 652, 654, 657, 659,
+ 662, 664, 666, 669, 671, 674, 676, 678, 681, 683
+};
+
+// Vibrato (sine) table
+const unsigned char CldsPlayer::vibtab[] = {
+ 0, 13, 25, 37, 50, 62, 74, 86, 98, 109, 120, 131, 142, 152, 162,
+ 171, 180, 189, 197, 205, 212, 219, 225, 231, 236, 240, 244, 247,
+ 250, 252, 254, 255, 255, 255, 254, 252, 250, 247, 244, 240, 236,
+ 231, 225, 219, 212, 205, 197, 189, 180, 171, 162, 152, 142, 131,
+ 120, 109, 98, 86, 74, 62, 50, 37, 25, 13
+};
+
+// Tremolo (sine * sine) table
+const unsigned char CldsPlayer::tremtab[] = {
+ 0, 0, 1, 1, 2, 4, 5, 7, 10, 12, 15, 18, 21, 25, 29, 33, 37, 42, 47,
+ 52, 57, 62, 67, 73, 79, 85, 90, 97, 103, 109, 115, 121, 128, 134,
+ 140, 146, 152, 158, 165, 170, 176, 182, 188, 193, 198, 203, 208,
+ 213, 218, 222, 226, 230, 234, 237, 240, 243, 245, 248, 250, 251,
+ 253, 254, 254, 255, 255, 255, 254, 254, 253, 251, 250, 248, 245,
+ 243, 240, 237, 234, 230, 226, 222, 218, 213, 208, 203, 198, 193,
+ 188, 182, 176, 170, 165, 158, 152, 146, 140, 134, 127, 121, 115,
+ 109, 103, 97, 90, 85, 79, 73, 67, 62, 57, 52, 47, 42, 37, 33, 29,
+ 25, 21, 18, 15, 12, 10, 7, 5, 4, 2, 1, 1, 0
+};
+
+// 'maxsound' is maximum number of patches (instruments)
+// 'maxpos' is maximum number of entries in position list (orderlist)
+const unsigned short CldsPlayer::maxsound = 0x3f, CldsPlayer::maxpos = 0xff;
+
+/*** public methods *************************************/
+
+CldsPlayer::CldsPlayer(Copl *newopl)
+ : CPlayer(newopl), soundbank(0), positions(0), patterns(0)
+{
+}
+
+CldsPlayer::~CldsPlayer()
+{
+ if(soundbank) delete [] soundbank;
+ if(positions) delete [] positions;
+ if(patterns) delete [] patterns;
+}
+
+bool CldsPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f;
+ unsigned int i, j;
+ SoundBank *sb;
+
+ // file validation section (actually just an extension check)
+ if(!fp.extension(filename, ".lds")) return false;
+ f = fp.open(filename); if(!f) return false;
+
+ // file load section (header)
+ mode = f->readInt(1);
+ if(mode > 2) { fp.close(f); return false; }
+ speed = f->readInt(2);
+ tempo = f->readInt(1);
+ pattlen = f->readInt(1);
+ for(i = 0; i < 9; i++) chandelay[i] = f->readInt(1);
+ regbd = f->readInt(1);
+
+ // load patches
+ numpatch = f->readInt(2);
+ soundbank = new SoundBank[numpatch];
+ for(i = 0; i < numpatch; i++) {
+ sb = &soundbank[i];
+ sb->mod_misc = f->readInt(1); sb->mod_vol = f->readInt(1);
+ sb->mod_ad = f->readInt(1); sb->mod_sr = f->readInt(1);
+ sb->mod_wave = f->readInt(1); sb->car_misc = f->readInt(1);
+ sb->car_vol = f->readInt(1); sb->car_ad = f->readInt(1);
+ sb->car_sr = f->readInt(1); sb->car_wave = f->readInt(1);
+ sb->feedback = f->readInt(1); sb->keyoff = f->readInt(1);
+ sb->portamento = f->readInt(1); sb->glide = f->readInt(1);
+ sb->finetune = f->readInt(1); sb->vibrato = f->readInt(1);
+ sb->vibdelay = f->readInt(1); sb->mod_trem = f->readInt(1);
+ sb->car_trem = f->readInt(1); sb->tremwait = f->readInt(1);
+ sb->arpeggio = f->readInt(1);
+ for(j = 0; j < 12; j++) sb->arp_tab[j] = f->readInt(1);
+ sb->start = f->readInt(2); sb->size = f->readInt(2);
+ sb->fms = f->readInt(1); sb->transp = f->readInt(2);
+ sb->midinst = f->readInt(1); sb->midvelo = f->readInt(1);
+ sb->midkey = f->readInt(1); sb->midtrans = f->readInt(1);
+ sb->middum1 = f->readInt(1); sb->middum2 = f->readInt(1);
+ }
+
+ // load positions
+ numposi = f->readInt(2);
+ positions = new Position[9 * numposi];
+ for(i = 0; i < numposi; i++)
+ for(j = 0; j < 9; j++) {
+ /*
+ * patnum is a pointer inside the pattern space, but patterns are 16bit
+ * word fields anyway, so it ought to be an even number (hopefully) and
+ * we can just divide it by 2 to get our array index of 16bit words.
+ */
+ positions[i * 9 + j].patnum = f->readInt(2) / 2;
+ positions[i * 9 + j].transpose = f->readInt(1);
+ }
+
+ AdPlug_LogWrite("CldsPlayer::load(\"%s\",fp): loading LOUDNESS file: mode = "
+ "%d, pattlen = %d, numpatch = %d, numposi = %d\n",
+ filename.c_str(), mode, pattlen, numpatch, numposi);
+
+ // load patterns
+ f->ignore(2); // ignore # of digital sounds (not played by this player)
+ patterns = new unsigned short[(fp.filesize(f) - f->pos()) / 2 + 1];
+ for(i = 0; !f->eof(); i++)
+ patterns[i] = f->readInt(2);
+
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+bool CldsPlayer::update()
+{
+ unsigned short comword, freq, octave, chan, tune, wibc, tremc, arpreg;
+ bool vbreak;
+ unsigned char level, regnum, comhi, comlo;
+ int i;
+ Channel *c;
+
+ if(!playing) return false;
+
+ // handle fading
+ if(fadeonoff)
+ if(fadeonoff <= 128) {
+ if(allvolume > fadeonoff || allvolume == 0)
+ allvolume -= fadeonoff;
+ else {
+ allvolume = 1;
+ fadeonoff = 0;
+ if(hardfade != 0) {
+ playing = false;
+ hardfade = 0;
+ for(i = 0; i < 9; i++)
+ channel[i].keycount = 1;
+ }
+ }
+ } else
+ if(((allvolume + (0x100 - fadeonoff)) & 0xff) <= mainvolume)
+ allvolume += 0x100 - fadeonoff;
+ else {
+ allvolume = mainvolume;
+ fadeonoff = 0;
+ }
+
+ // handle channel delay
+ for(chan = 0; chan < 9; chan++) {
+ c = &channel[chan];
+ if(c->chancheat.chandelay)
+ if(!(--c->chancheat.chandelay))
+ playsound(c->chancheat.sound, chan, c->chancheat.high);
+ }
+
+ // handle notes
+ if(!tempo_now) {
+ vbreak = false;
+ for(chan = 0; chan < 9; chan++) {
+ c = &channel[chan];
+ if(!c->packwait) {
+ unsigned short patnum = positions[posplay * 9 + chan].patnum;
+ unsigned char transpose = positions[posplay * 9 + chan].transpose;
+
+ comword = patterns[patnum + c->packpos];
+ comhi = comword >> 8; comlo = comword & 0xff;
+ if(comword)
+ if(comhi == 0x80)
+ c->packwait = comlo;
+ else
+ if(comhi >= 0x80) {
+ switch(comhi) {
+ case 0xff:
+ c->volcar = (((c->volcar & 0x3f) * comlo) >> 6) & 0x3f;
+ if(fmchip[0xc0 + chan] & 1)
+ c->volmod = (((c->volmod & 0x3f) * comlo) >> 6) & 0x3f;
+ break;
+ case 0xfe:
+ tempo = comword & 0x3f;
+ break;
+ case 0xfd:
+ c->nextvol = comlo;
+ break;
+ case 0xfc:
+ playing = false;
+ // in real player there's also full keyoff here, but we don't need it
+ break;
+ case 0xfb:
+ c->keycount = 1;
+ break;
+ case 0xfa:
+ vbreak = true;
+ jumppos = (posplay + 1) & maxpos;
+ break;
+ case 0xf9:
+ vbreak = true;
+ jumppos = comlo & maxpos;
+ jumping = 1;
+ if(jumppos < posplay) songlooped = true;
+ break;
+ case 0xf8:
+ c->lasttune = 0;
+ break;
+ case 0xf7:
+ c->vibwait = 0;
+ // PASCAL: c->vibspeed = ((comlo >> 4) & 15) + 2;
+ c->vibspeed = (comlo >> 4) + 2;
+ c->vibrate = (comlo & 15) + 1;
+ break;
+ case 0xf6:
+ c->glideto = comlo;
+ break;
+ case 0xf5:
+ c->finetune = comlo;
+ break;
+ case 0xf4:
+ if(!hardfade) {
+ allvolume = mainvolume = comlo;
+ fadeonoff = 0;
+ }
+ break;
+ case 0xf3:
+ if(!hardfade) fadeonoff = comlo;
+ break;
+ case 0xf2:
+ c->trmstay = comlo;
+ break;
+ case 0xf1: // panorama
+ case 0xf0: // progch
+ // MIDI commands (unhandled)
+ AdPlug_LogWrite("CldsPlayer(): not handling MIDI command 0x%x, "
+ "value = 0x%x\n", comhi);
+ break;
+ default:
+ if(comhi < 0xa0)
+ c->glideto = comhi & 0x1f;
+ else
+ AdPlug_LogWrite("CldsPlayer(): unknown command 0x%x encountered!"
+ " value = 0x%x\n", comhi, comlo);
+ break;
+ }
+ } else {
+ unsigned char sound;
+ unsigned short high;
+ signed char transp = transpose & 127;
+
+ /*
+ * Originally, in assembler code, the player first shifted
+ * logically left the transpose byte by 1 and then shifted
+ * arithmetically right the same byte to achieve the final,
+ * signed transpose value. Since we can't do arithmetic shifts
+ * in C, we just duplicate the 7th bit into the 8th one and
+ * discard the 8th one completely.
+ */
+
+ if(transpose & 64) transp |= 128;
+
+ if(transpose & 128) {
+ sound = (comlo + transp) & maxsound;
+ high = comhi << 4;
+ } else {
+ sound = comlo & maxsound;
+ high = (comhi + transp) << 4;
+ }
+
+ /*
+ PASCAL:
+ sound = comlo & maxsound;
+ high = (comhi + (((transpose + 0x24) & 0xff) - 0x24)) << 4;
+ */
+
+ if(!chandelay[chan])
+ playsound(sound, chan, high);
+ else {
+ c->chancheat.chandelay = chandelay[chan];
+ c->chancheat.sound = sound;
+ c->chancheat.high = high;
+ }
+ }
+
+ c->packpos++;
+ } else
+ c->packwait--;
+ }
+
+ tempo_now = tempo;
+ /*
+ The continue table is updated here, but this is only used in the
+ original player, which can be paused in the middle of a song and then
+ unpaused. Since AdPlug does all this for us automatically, we don't
+ have a continue table here. The continue table update code is noted
+ here for reference only.
+
+ if(!pattplay) {
+ conttab[speed & maxcont].position = posplay & 0xff;
+ conttab[speed & maxcont].tempo = tempo;
+ }
+ */
+ pattplay++;
+ if(vbreak) {
+ pattplay = 0;
+ for(i = 0; i < 9; i++) channel[i].packpos = channel[i].packwait = 0;
+ posplay = jumppos;
+ } else
+ if(pattplay >= pattlen) {
+ pattplay = 0;
+ for(i = 0; i < 9; i++) channel[i].packpos = channel[i].packwait = 0;
+ posplay = (posplay + 1) & maxpos;
+ }
+ } else
+ tempo_now--;
+
+ // make effects
+ for(chan = 0; chan < 9; chan++) {
+ c = &channel[chan];
+ regnum = op_table[chan];
+ if(c->keycount > 0) {
+ if(c->keycount == 1)
+ setregs_adv(0xb0 + chan, 0xdf, 0);
+ c->keycount--;
+ }
+
+ // arpeggio
+ if(c->arp_size == 0)
+ arpreg = 0;
+ else {
+ arpreg = c->arp_tab[c->arp_pos] << 4;
+ if(arpreg == 0x800) {
+ if(c->arp_pos > 0) c->arp_tab[0] = c->arp_tab[c->arp_pos - 1];
+ c->arp_size = 1; c->arp_pos = 0;
+ arpreg = c->arp_tab[0] << 4;
+ }
+
+ if(c->arp_count == c->arp_speed) {
+ c->arp_pos++;
+ if(c->arp_pos >= c->arp_size) c->arp_pos = 0;
+ c->arp_count = 0;
+ } else
+ c->arp_count++;
+ }
+
+ // glide & portamento
+ if(c->lasttune && (c->lasttune != c->gototune)) {
+ if(c->lasttune > c->gototune) {
+ if(c->lasttune - c->gototune < c->portspeed)
+ c->lasttune = c->gototune;
+ else
+ c->lasttune -= c->portspeed;
+ } else {
+ if(c->gototune - c->lasttune < c->portspeed)
+ c->lasttune = c->gototune;
+ else
+ c->lasttune += c->portspeed;
+ }
+
+ if(arpreg >= 0x800)
+ arpreg = c->lasttune - (arpreg ^ 0xff0) - 16;
+ else
+ arpreg += c->lasttune;
+
+ freq = frequency[arpreg % (12 * 16)];
+ octave = arpreg / (12 * 16) - 1;
+ setregs(0xa0 + chan, freq & 0xff);
+ setregs_adv(0xb0 + chan, 0x20, ((octave << 2) + (freq >> 8)) & 0xdf);
+ } else {
+ // vibrato
+ if(!c->vibwait) {
+ if(c->vibrate) {
+ wibc = vibtab[c->vibcount & 0x3f] * c->vibrate;
+
+ if((c->vibcount & 0x40) == 0)
+ tune = c->lasttune + (wibc >> 8);
+ else
+ tune = c->lasttune - (wibc >> 8);
+
+ if(arpreg >= 0x800)
+ tune = tune - (arpreg ^ 0xff0) - 16;
+ else
+ tune += arpreg;
+
+ freq = frequency[tune % (12 * 16)];
+ octave = tune / (12 * 16) - 1;
+ setregs(0xa0 + chan, freq & 0xff);
+ setregs_adv(0xb0 + chan, 0x20, ((octave << 2) + (freq >> 8)) & 0xdf);
+ c->vibcount += c->vibspeed;
+ } else
+ if(c->arp_size != 0) { // no vibrato, just arpeggio
+ if(arpreg >= 0x800)
+ tune = c->lasttune - (arpreg ^ 0xff0) - 16;
+ else
+ tune = c->lasttune + arpreg;
+
+ freq = frequency[tune % (12 * 16)];
+ octave = tune / (12 * 16) - 1;
+ setregs(0xa0 + chan, freq & 0xff);
+ setregs_adv(0xb0 + chan, 0x20, ((octave << 2) + (freq >> 8)) & 0xdf);
+ }
+ } else { // no vibrato, just arpeggio
+ c->vibwait--;
+
+ if(c->arp_size != 0) {
+ if(arpreg >= 0x800)
+ tune = c->lasttune - (arpreg ^ 0xff0) - 16;
+ else
+ tune = c->lasttune + arpreg;
+
+ freq = frequency[tune % (12 * 16)];
+ octave = tune / (12 * 16) - 1;
+ setregs(0xa0 + chan, freq & 0xff);
+ setregs_adv(0xb0 + chan, 0x20, ((octave << 2) + (freq >> 8)) & 0xdf);
+ }
+ }
+ }
+
+ // tremolo (modulator)
+ if(!c->trmwait) {
+ if(c->trmrate) {
+ tremc = tremtab[c->trmcount & 0x7f] * c->trmrate;
+ if((tremc >> 8) <= (c->volmod & 0x3f))
+ level = (c->volmod & 0x3f) - (tremc >> 8);
+ else
+ level = 0;
+
+ if(allvolume != 0 && (fmchip[0xc0 + chan] & 1))
+ setregs_adv(0x40 + regnum, 0xc0, ((level * allvolume) >> 8) ^ 0x3f);
+ else
+ setregs_adv(0x40 + regnum, 0xc0, level ^ 0x3f);
+
+ c->trmcount += c->trmspeed;
+ } else
+ if(allvolume != 0 && (fmchip[0xc0 + chan] & 1))
+ setregs_adv(0x40 + regnum, 0xc0, ((((c->volmod & 0x3f) * allvolume) >> 8) ^ 0x3f) & 0x3f);
+ else
+ setregs_adv(0x40 + regnum, 0xc0, (c->volmod ^ 0x3f) & 0x3f);
+ } else {
+ c->trmwait--;
+ if(allvolume != 0 && (fmchip[0xc0 + chan] & 1))
+ setregs_adv(0x40 + regnum, 0xc0, ((((c->volmod & 0x3f) * allvolume) >> 8) ^ 0x3f) & 0x3f);
+ }
+
+ // tremolo (carrier)
+ if(!c->trcwait) {
+ if(c->trcrate) {
+ tremc = tremtab[c->trccount & 0x7f] * c->trcrate;
+ if((tremc >> 8) <= (c->volcar & 0x3f))
+ level = (c->volcar & 0x3f) - (tremc >> 8);
+ else
+ level = 0;
+
+ if(allvolume != 0)
+ setregs_adv(0x43 + regnum, 0xc0, ((level * allvolume) >> 8) ^ 0x3f);
+ else
+ setregs_adv(0x43 + regnum, 0xc0, level ^ 0x3f);
+ c->trccount += c->trcspeed;
+ } else
+ if(allvolume != 0)
+ setregs_adv(0x43 + regnum, 0xc0, ((((c->volcar & 0x3f) * allvolume) >> 8) ^ 0x3f) & 0x3f);
+ else
+ setregs_adv(0x43 + regnum, 0xc0, (c->volcar ^ 0x3f) & 0x3f);
+ } else {
+ c->trcwait--;
+ if(allvolume != 0)
+ setregs_adv(0x43 + regnum, 0xc0, ((((c->volcar & 0x3f) * allvolume) >> 8) ^ 0x3f) & 0x3f);
+ }
+ }
+
+ return (!playing || songlooped) ? false : true;
+}
+
+void CldsPlayer::rewind(int subsong)
+{
+ int i;
+
+ // init all with 0
+ tempo_now = 3; playing = true; songlooped = false;
+ jumping = fadeonoff = allvolume = hardfade = pattplay = posplay = jumppos =
+ mainvolume = 0;
+ memset(channel, 0, sizeof(channel));
+ memset(fmchip, 0, sizeof(fmchip));
+
+ // OPL2 init
+ opl->init(); // Reset OPL chip
+ opl->write(1, 0x20);
+ opl->write(8, 0);
+ opl->write(0xbd, regbd);
+
+ for(i = 0; i < 9; i++) {
+ opl->write(0x20 + op_table[i], 0);
+ opl->write(0x23 + op_table[i], 0);
+ opl->write(0x40 + op_table[i], 0x3f);
+ opl->write(0x43 + op_table[i], 0x3f);
+ opl->write(0x60 + op_table[i], 0xff);
+ opl->write(0x63 + op_table[i], 0xff);
+ opl->write(0x80 + op_table[i], 0xff);
+ opl->write(0x83 + op_table[i], 0xff);
+ opl->write(0xe0 + op_table[i], 0);
+ opl->write(0xe3 + op_table[i], 0);
+ opl->write(0xa0 + i, 0);
+ opl->write(0xb0 + i, 0);
+ opl->write(0xc0 + i, 0);
+ }
+}
+
+/*** private methods *************************************/
+
+void CldsPlayer::playsound(int inst_number, int channel_number, int tunehigh)
+{
+ Channel *c = &channel[channel_number]; // current channel
+ SoundBank *i = &soundbank[inst_number]; // current instrument
+ unsigned int regnum = op_table[channel_number]; // channel's OPL2 register
+ unsigned char volcalc, octave;
+ unsigned short freq;
+
+ // set fine tune
+ tunehigh += ((i->finetune + c->finetune + 0x80) & 0xff) - 0x80;
+
+ // arpeggio handling
+ if(!i->arpeggio) {
+ unsigned short arpcalc = i->arp_tab[0] << 4;
+
+ if(arpcalc > 0x800)
+ tunehigh = tunehigh - (arpcalc ^ 0xff0) - 16;
+ else
+ tunehigh += arpcalc;
+ }
+
+ // glide handling
+ if(c->glideto != 0) {
+ c->gototune = tunehigh;
+ c->portspeed = c->glideto;
+ c->glideto = c->finetune = 0;
+ return;
+ }
+
+ // set modulator registers
+ setregs(0x20 + regnum, i->mod_misc);
+ volcalc = i->mod_vol;
+ if(!c->nextvol || !(i->feedback & 1))
+ c->volmod = volcalc;
+ else
+ c->volmod = (volcalc & 0xc0) | ((((volcalc & 0x3f) * c->nextvol) >> 6));
+
+ if((i->feedback & 1) == 1 && allvolume != 0)
+ setregs(0x40 + regnum, ((c->volmod & 0xc0) | (((c->volmod & 0x3f) * allvolume) >> 8)) ^ 0x3f);
+ else
+ setregs(0x40 + regnum, c->volmod ^ 0x3f);
+ setregs(0x60 + regnum, i->mod_ad);
+ setregs(0x80 + regnum, i->mod_sr);
+ setregs(0xe0 + regnum, i->mod_wave);
+
+ // Set carrier registers
+ setregs(0x23 + regnum, i->car_misc);
+ volcalc = i->car_vol;
+ if(!c->nextvol)
+ c->volcar = volcalc;
+ else
+ c->volcar = (volcalc & 0xc0) | ((((volcalc & 0x3f) * c->nextvol) >> 6));
+
+ if(allvolume)
+ setregs(0x43 + regnum, ((c->volcar & 0xc0) | (((c->volcar & 0x3f) * allvolume) >> 8)) ^ 0x3f);
+ else
+ setregs(0x43 + regnum, c->volcar ^ 0x3f);
+ setregs(0x63 + regnum, i->car_ad);
+ setregs(0x83 + regnum, i->car_sr);
+ setregs(0xe3 + regnum, i->car_wave);
+ setregs(0xc0 + channel_number, i->feedback);
+ setregs_adv(0xb0 + channel_number, 0xdf, 0); // key off
+
+ freq = frequency[tunehigh % (12 * 16)];
+ octave = tunehigh / (12 * 16) - 1;
+ if(!i->glide) {
+ if(!i->portamento || !c->lasttune) {
+ setregs(0xa0 + channel_number, freq & 0xff);
+ setregs(0xb0 + channel_number, (octave << 2) + 0x20 + (freq >> 8));
+ c->lasttune = c->gototune = tunehigh;
+ } else {
+ c->gototune = tunehigh;
+ c->portspeed = i->portamento;
+ setregs_adv(0xb0 + channel_number, 0xdf, 0x20); // key on
+ }
+ } else {
+ setregs(0xa0 + channel_number, freq & 0xff);
+ setregs(0xb0 + channel_number, (octave << 2) + 0x20 + (freq >> 8));
+ c->lasttune = tunehigh;
+ c->gototune = tunehigh + ((i->glide + 0x80) & 0xff) - 0x80; // set destination
+ c->portspeed = i->portamento;
+ }
+
+ if(!i->vibrato)
+ c->vibwait = c->vibspeed = c->vibrate = 0;
+ else {
+ c->vibwait = i->vibdelay;
+ // PASCAL: c->vibspeed = ((i->vibrato >> 4) & 15) + 1;
+ c->vibspeed = (i->vibrato >> 4) + 2;
+ c->vibrate = (i->vibrato & 15) + 1;
+ }
+
+ if(!(c->trmstay & 0xf0)) {
+ c->trmwait = (i->tremwait & 0xf0) >> 3;
+ // PASCAL: c->trmspeed = (i->mod_trem >> 4) & 15;
+ c->trmspeed = i->mod_trem >> 4;
+ c->trmrate = i->mod_trem & 15;
+ c->trmcount = 0;
+ }
+
+ if(!(c->trmstay & 0x0f)) {
+ c->trcwait = (i->tremwait & 15) << 1;
+ // PASCAL: c->trcspeed = (i->car_trem >> 4) & 15;
+ c->trcspeed = i->car_trem >> 4;
+ c->trcrate = i->car_trem & 15;
+ c->trccount = 0;
+ }
+
+ c->arp_size = i->arpeggio & 15;
+ c->arp_speed = i->arpeggio >> 4;
+ memcpy(c->arp_tab, i->arp_tab, 12);
+ c->keycount = i->keyoff;
+ c->nextvol = c->glideto = c->finetune = c->vibcount = c->arp_pos = c->arp_count = 0;
+}
+
+inline void CldsPlayer::setregs(unsigned char reg, unsigned char val)
+{
+ if(fmchip[reg] == val) return;
+
+ fmchip[reg] = val;
+ opl->write(reg, val);
+}
+
+inline void CldsPlayer::setregs_adv(unsigned char reg, unsigned char mask,
+ unsigned char val)
+{
+ setregs(reg, (fmchip[reg] & mask) | val);
+}
diff --git a/plugins/adplug/adplug/lds.h b/plugins/adplug/adplug/lds.h
new file mode 100644
index 00000000..ec61ad29
--- /dev/null
+++ b/plugins/adplug/adplug/lds.h
@@ -0,0 +1,91 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2004 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * lds.h - LOUDNESS Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "player.h"
+
+class CldsPlayer: public CPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl) { return new CldsPlayer(newopl); }
+
+ CldsPlayer(Copl *newopl);
+ virtual ~CldsPlayer();
+
+ bool load(const std::string &fn, const CFileProvider &fp);
+ virtual bool update();
+ virtual void rewind(int subsong = -1);
+ float getrefresh() { return 70.0f; }
+
+ std::string gettype() { return std::string("LOUDNESS Sound System"); }
+ unsigned int getorders() { return numposi; }
+ unsigned int getorder() { return posplay; }
+ unsigned int getrow() { return pattplay; }
+ unsigned int getspeed() { return speed; }
+ unsigned int getinstruments() { return numpatch; }
+
+ private:
+ typedef struct {
+ unsigned char mod_misc, mod_vol, mod_ad, mod_sr, mod_wave,
+ car_misc, car_vol, car_ad, car_sr, car_wave, feedback, keyoff,
+ portamento, glide, finetune, vibrato, vibdelay, mod_trem, car_trem,
+ tremwait, arpeggio, arp_tab[12];
+ unsigned short start, size;
+ unsigned char fms;
+ unsigned short transp;
+ unsigned char midinst, midvelo, midkey, midtrans, middum1, middum2;
+ } SoundBank;
+
+ typedef struct {
+ unsigned short gototune, lasttune, packpos;
+ unsigned char finetune, glideto, portspeed, nextvol, volmod, volcar,
+ vibwait, vibspeed, vibrate, trmstay, trmwait, trmspeed, trmrate, trmcount,
+ trcwait, trcspeed, trcrate, trccount, arp_size, arp_speed, keycount,
+ vibcount, arp_pos, arp_count, packwait, arp_tab[12];
+
+ struct {
+ unsigned char chandelay, sound;
+ unsigned short high;
+ } chancheat;
+ } Channel;
+
+ typedef struct {
+ unsigned short patnum;
+ unsigned char transpose;
+ } Position;
+
+ static const unsigned short frequency[];
+ static const unsigned char vibtab[], tremtab[];
+ static const unsigned short maxsound, maxpos;
+
+ SoundBank *soundbank;
+ Channel channel[9];
+ Position *positions;
+ unsigned char fmchip[0xff], jumping, fadeonoff, allvolume, hardfade,
+ tempo_now, pattplay, tempo, regbd, chandelay[9], mode, pattlen;
+ unsigned short posplay, jumppos, *patterns, speed;
+ bool playing, songlooped;
+ unsigned int numpatch, numposi, patterns_size, mainvolume;
+
+ void playsound(int inst_number, int channel_number, int tunehigh);
+ inline void setregs(unsigned char reg, unsigned char val);
+ inline void setregs_adv(unsigned char reg, unsigned char mask,
+ unsigned char val);
+};
diff --git a/plugins/adplug/adplug/mad.cpp b/plugins/adplug/adplug/mad.cpp
new file mode 100644
index 00000000..d5a60c43
--- /dev/null
+++ b/plugins/adplug/adplug/mad.cpp
@@ -0,0 +1,127 @@
+/*
+ Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ mad.cpp - MAD loader by Riven the Mage <riven@ok.ru>
+*/
+
+#include <string.h>
+#include "mad.h"
+
+/* -------- Public Methods -------------------------------- */
+
+CPlayer *CmadLoader::factory(Copl *newopl)
+{
+ return new CmadLoader(newopl);
+}
+
+bool CmadLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ const unsigned char conv_inst[10] = { 2,1,10,9,4,3,6,5,8,7 };
+ unsigned int i, j, k, t = 0;
+
+ // 'MAD+' - signed ?
+ char id[4]; f->readString(id, 4);
+ if (strncmp(id,"MAD+",4)) { fp.close(f); return false; }
+
+ // load instruments
+ for(i = 0; i < 9; i++) {
+ f->readString(instruments[i].name, 8);
+ for(j = 0; j < 12; j++) instruments[i].data[j] = f->readInt(1);
+ }
+
+ f->ignore(1);
+
+ // data for Protracker
+ length = f->readInt(1); nop = f->readInt(1); timer = f->readInt(1);
+
+ // init CmodPlayer
+ realloc_instruments(9);
+ realloc_order(length);
+ realloc_patterns(nop,32,9);
+ init_trackord();
+
+ // load tracks
+ for(i = 0; i < nop; i++)
+ for(k = 0; k < 32; k++)
+ for(j = 0; j < 9; j++) {
+ t = i * 9 + j;
+
+ // read event
+ unsigned char event = f->readInt(1);
+
+ // convert event
+ if (event < 0x61)
+ tracks[t][k].note = event;
+ if (event == 0xFF) // 0xFF: Release note
+ tracks[t][k].command = 8;
+ if (event == 0xFE) // 0xFE: Pattern Break
+ tracks[t][k].command = 13;
+ }
+
+ // load order
+ for(i = 0; i < length; i++) order[i] = f->readInt(1) - 1;
+
+ fp.close(f);
+
+ // convert instruments
+ for(i = 0; i < 9; i++)
+ for(j = 0; j < 10; j++)
+ inst[i].data[conv_inst[j]] = instruments[i].data[j];
+
+ // data for Protracker
+ restartpos = 0;
+ initspeed = 1;
+
+ rewind(0);
+ return true;
+}
+
+void CmadLoader::rewind(int subsong)
+{
+ CmodPlayer::rewind(subsong);
+
+ // default instruments
+ for (int i=0;i<9;i++)
+ {
+ channel[i].inst = i;
+
+ channel[i].vol1 = 63 - (inst[i].data[10] & 63);
+ channel[i].vol2 = 63 - (inst[i].data[9] & 63);
+ }
+}
+
+float CmadLoader::getrefresh()
+{
+ return (float)timer;
+}
+
+std::string CmadLoader::gettype()
+{
+ return std::string("Mlat Adlib Tracker");
+}
+
+std::string CmadLoader::getinstrument(unsigned int n)
+{
+ return std::string(instruments[n].name,8);
+}
+
+unsigned int CmadLoader::getinstruments()
+{
+ return 9;
+}
diff --git a/plugins/adplug/adplug/mad.h b/plugins/adplug/adplug/mad.h
new file mode 100644
index 00000000..98d2a445
--- /dev/null
+++ b/plugins/adplug/adplug/mad.h
@@ -0,0 +1,48 @@
+/*
+ Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ mad.h - MAD loader by Riven the Mage <riven@ok.ru>
+*/
+
+#include "protrack.h"
+
+class CmadLoader: public CmodPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CmadLoader(Copl *newopl) : CmodPlayer(newopl) { };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype();
+ std::string getinstrument(unsigned int n);
+ unsigned int getinstruments();
+
+private:
+
+ struct mad_instrument
+ {
+ char name[8];
+ unsigned char data[12]; // last two unused
+ } instruments[9];
+
+ unsigned char timer;
+};
diff --git a/plugins/adplug/adplug/mid.cpp b/plugins/adplug/adplug/mid.cpp
new file mode 100644
index 00000000..e3dd88c5
--- /dev/null
+++ b/plugins/adplug/adplug/mid.cpp
@@ -0,0 +1,1090 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * MIDI & MIDI-like file player - Last Update: 10/15/2005
+ * by Phil Hassey - www.imitationpickles.org
+ * philhassey@hotmail.com
+ *
+ * Can play the following
+ * .LAA - a raw save of a Lucas Arts Adlib music
+ * or
+ * a raw save of a LucasFilm Adlib music
+ * .MID - a "midi" save of a Lucas Arts Adlib music
+ * - or general MIDI files
+ * .CMF - Creative Music Format
+ * .SCI - the sierra "midi" format.
+ * Files must be in the form
+ * xxxNAME.sci
+ * So that the loader can load the right patch file:
+ * xxxPATCH.003 (patch.003 must be saved from the
+ * sierra resource from each game.)
+ *
+ * 6/2/2000: v1.0 relased by phil hassey
+ * Status: LAA is almost perfect
+ * - some volumes are a bit off (intrument too quiet)
+ * MID is fine (who wants to listen to MIDI vid adlib anyway)
+ * CMF is okay (still needs the adlib rythm mode implemented
+ * for real)
+ * 6/6/2000:
+ * Status: SCI: there are two SCI formats, orginal and advanced.
+ * original: (Found in SCI/EGA Sierra Adventures)
+ * played almost perfectly, I believe
+ * there is one mistake in the instrument
+ * loader that causes some sounds to
+ * not be quite right. Most sounds are fine.
+ * advanced: (Found in SCI/VGA Sierra Adventures)
+ * These are multi-track files. (Thus the
+ * player had to be modified to work with
+ * them.) This works fine.
+ * There are also multiple tunes in each file.
+ * I think some of them are supposed to be
+ * played at the same time, but I'm not sure
+ * when.
+ * 8/16/2000:
+ * Status: LAA: now EGA and VGA lucas games work pretty well
+ *
+ * 10/15/2005: Changes by Simon Peter
+ * Added rhythm mode support for CMF format.
+ *
+ * Other acknowledgements:
+ * Allegro - for the midi instruments and the midi volume table
+ * SCUMM Revisited - for getting the .LAA / .MIDs out of those
+ * LucasArts files.
+ * FreeSCI - for some information on the sci music files
+ * SD - the SCI Decoder (to get all .sci out of the Sierra files)
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include "mid.h"
+#include "mididata.h"
+
+/*#define TESTING*/
+#ifdef TESTING
+#define midiprintf printf
+#else
+void CmidPlayer::midiprintf(const char *format, ...)
+ {
+ }
+#endif
+
+#define LUCAS_STYLE 1
+#define CMF_STYLE 2
+#define MIDI_STYLE 4
+#define SIERRA_STYLE 8
+
+// AdLib melodic and rhythm mode defines
+#define ADLIB_MELODIC 0
+#define ADLIB_RYTHM 1
+
+// File types
+#define FILE_LUCAS 1
+#define FILE_MIDI 2
+#define FILE_CMF 3
+#define FILE_SIERRA 4
+#define FILE_ADVSIERRA 5
+#define FILE_OLDLUCAS 6
+
+// AdLib standard operator table
+const unsigned char CmidPlayer::adlib_opadd[] = {0x00 ,0x01 ,0x02 ,0x08 ,0x09 ,0x0A ,0x10 ,0x11 ,0x12};
+
+// dunno
+const int CmidPlayer::ops[] = {0x20,0x20,0x40,0x40,0x60,0x60,0x80,0x80,0xe0,0xe0,0xc0};
+
+// map CMF drum channels 12 - 15 to corresponding AdLib drum operators
+// bass drum (channel 11) not mapped, cause it's handled like a normal instrument
+const int CmidPlayer::map_chan[] = { 0x14, 0x12, 0x15, 0x11 };
+
+// Standard AdLib frequency table
+const int CmidPlayer::fnums[] = { 0x16b,0x181,0x198,0x1b0,0x1ca,0x1e5,0x202,0x220,0x241,0x263,0x287,0x2ae };
+
+// Map CMF drum channels 11 - 15 to corresponding AdLib drum channels
+const int CmidPlayer::percussion_map[] = { 6, 7, 8, 8, 7 };
+
+CPlayer *CmidPlayer::factory(Copl *newopl)
+{
+ return new CmidPlayer(newopl);
+}
+
+CmidPlayer::CmidPlayer(Copl *newopl)
+ : CPlayer(newopl), author(&emptystr), title(&emptystr), remarks(&emptystr),
+ emptystr('\0'), flen(0), data(0)
+{
+}
+
+unsigned char CmidPlayer::datalook(long pos)
+{
+ if (pos<0 || pos >= flen) return(0);
+ return(data[pos]);
+}
+
+unsigned long CmidPlayer::getnexti(unsigned long num)
+{
+ unsigned long v=0;
+ unsigned long i;
+
+ for (i=0; i<num; i++)
+ {
+ v+=(datalook(pos)<<(8*i)); pos++;
+ }
+ return(v);
+}
+
+unsigned long CmidPlayer::getnext(unsigned long num)
+{
+ unsigned long v=0;
+ unsigned long i;
+
+ for (i=0; i<num; i++)
+ {
+ v<<=8;
+ v+=datalook(pos); pos++;
+ }
+ return(v);
+}
+
+unsigned long CmidPlayer::getval()
+{
+ int v=0;
+ unsigned char b;
+
+ b=(unsigned char)getnext(1);
+ v=b&0x7f;
+ while ((b&0x80) !=0)
+ {
+ b=(unsigned char)getnext(1);
+ v = (v << 7) + (b & 0x7F);
+ }
+ return(v);
+}
+
+bool CmidPlayer::load_sierra_ins(const std::string &fname, const CFileProvider &fp)
+{
+ long i,j,k,l;
+ unsigned char ins[28];
+ char *pfilename;
+ binistream *f;
+
+ pfilename = (char *)malloc(fname.length()+9);
+ strcpy(pfilename,fname.c_str());
+ j=0;
+ for(i=strlen(pfilename)-1; i >= 0; i--)
+ if(pfilename[i] == '/' || pfilename[i] == '\\') {
+ j = i+1;
+ break;
+ }
+ sprintf(pfilename+j+3,"patch.003");
+
+ f = fp.open(pfilename);
+ free(pfilename);
+ if(!f) return false;
+
+ f->ignore(2);
+ stins = 0;
+ for (i=0; i<2; i++)
+ {
+ for (k=0; k<48; k++)
+ {
+ l=i*48+k;
+ midiprintf ("\n%2d: ",l);
+ for (j=0; j<28; j++)
+ ins[j] = f->readInt(1);
+
+ myinsbank[l][0]=
+ (ins[9]*0x80) + (ins[10]*0x40) +
+ (ins[5]*0x20) + (ins[11]*0x10) +
+ ins[1]; //1=ins5
+ myinsbank[l][1]=
+ (ins[22]*0x80) + (ins[23]*0x40) +
+ (ins[18]*0x20) + (ins[24]*0x10) +
+ ins[14]; //1=ins18
+
+ myinsbank[l][2]=(ins[0]<<6)+ins[8];
+ myinsbank[l][3]=(ins[13]<<6)+ins[21];
+
+ myinsbank[l][4]=(ins[3]<<4)+ins[6];
+ myinsbank[l][5]=(ins[16]<<4)+ins[19];
+ myinsbank[l][6]=(ins[4]<<4)+ins[7];
+ myinsbank[l][7]=(ins[17]<<4)+ins[20];
+
+ myinsbank[l][8]=ins[26];
+ myinsbank[l][9]=ins[27];
+
+ myinsbank[l][10]=((ins[2]<<1))+(1-(ins[12]&1));
+ //(ins[12] ? 0:1)+((ins[2]<<1));
+
+ for (j=0; j<11; j++)
+ midiprintf ("%02X ",myinsbank[l][j]);
+ stins++;
+ }
+ f->ignore(2);
+ }
+
+ fp.close(f);
+ memcpy(smyinsbank, myinsbank, 128 * 16);
+ return true;
+}
+
+void CmidPlayer::sierra_next_section()
+{
+ int i,j;
+
+ for (i=0; i<16; i++)
+ track[i].on=0;
+
+ midiprintf("\n\nnext adv sierra section:\n");
+
+ pos=sierra_pos;
+ i=0;j=0;
+ while (i!=0xff)
+ {
+ getnext(1);
+ curtrack=j; j++;
+ track[curtrack].on=1;
+ track[curtrack].spos = getnext(1);
+ track[curtrack].spos += (getnext(1) << 8) + 4; //4 best usually +3? not 0,1,2 or 5
+// track[curtrack].spos=getnext(1)+(getnext(1)<<8)+4; // dynamite!: doesn't optimize correctly!!
+ track[curtrack].tend=flen; //0xFC will kill it
+ track[curtrack].iwait=0;
+ track[curtrack].pv=0;
+ midiprintf ("track %d starts at %lx\n",curtrack,track[curtrack].spos);
+
+ getnext(2);
+ i=getnext(1);
+ }
+ getnext(2);
+ deltas=0x20;
+ sierra_pos=pos;
+ //getch();
+
+ fwait=0;
+ doing=1;
+}
+
+bool CmidPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ int good;
+ unsigned char s[6];
+
+ f->readString((char *)s, 6);
+ good=0;
+ subsongs=0;
+ switch(s[0])
+ {
+ case 'A':
+ if (s[1]=='D' && s[2]=='L') good=FILE_LUCAS;
+ break;
+ case 'M':
+ if (s[1]=='T' && s[2]=='h' && s[3]=='d') good=FILE_MIDI;
+ break;
+ case 'C':
+ if (s[1]=='T' && s[2]=='M' && s[3]=='F') good=FILE_CMF;
+ break;
+ case 0x84:
+ if (s[1]==0x00 && load_sierra_ins(filename, fp))
+ if (s[2]==0xf0)
+ good=FILE_ADVSIERRA;
+ else
+ good=FILE_SIERRA;
+ break;
+ default:
+ if (s[4]=='A' && s[5]=='D') good=FILE_OLDLUCAS;
+ break;
+ }
+
+ if (good!=0)
+ subsongs=1;
+ else {
+ fp.close(f);
+ return false;
+ }
+
+ type=good;
+ f->seek(0);
+ flen = fp.filesize(f);
+ data = new unsigned char [flen];
+ f->readString((char *)data, flen);
+
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+void CmidPlayer::midi_write_adlib(unsigned int r, unsigned char v)
+{
+ opl->write(r,v);
+ adlib_data[r]=v;
+}
+
+void CmidPlayer::midi_fm_instrument(int voice, unsigned char *inst)
+{
+ if ((adlib_style&SIERRA_STYLE)!=0)
+ midi_write_adlib(0xbd,0); //just gotta make sure this happens..
+ //'cause who knows when it'll be
+ //reset otherwise.
+
+
+ midi_write_adlib(0x20+adlib_opadd[voice],inst[0]);
+ midi_write_adlib(0x23+adlib_opadd[voice],inst[1]);
+
+ if ((adlib_style&LUCAS_STYLE)!=0)
+ {
+ midi_write_adlib(0x43+adlib_opadd[voice],0x3f);
+ if ((inst[10] & 1)==0)
+ midi_write_adlib(0x40+adlib_opadd[voice],inst[2]);
+ else
+ midi_write_adlib(0x40+adlib_opadd[voice],0x3f);
+ }
+ else
+ {
+ if ((adlib_style&SIERRA_STYLE)!=0)
+ {
+ midi_write_adlib(0x40+adlib_opadd[voice],inst[2]);
+ midi_write_adlib(0x43+adlib_opadd[voice],inst[3]);
+ }
+ else
+ {
+ midi_write_adlib(0x40+adlib_opadd[voice],inst[2]);
+ if ((inst[10] & 1)==0)
+ midi_write_adlib(0x43+adlib_opadd[voice],inst[3]);
+ else
+ midi_write_adlib(0x43+adlib_opadd[voice],0);
+ }
+ }
+
+ midi_write_adlib(0x60+adlib_opadd[voice],inst[4]);
+ midi_write_adlib(0x63+adlib_opadd[voice],inst[5]);
+ midi_write_adlib(0x80+adlib_opadd[voice],inst[6]);
+ midi_write_adlib(0x83+adlib_opadd[voice],inst[7]);
+ midi_write_adlib(0xe0+adlib_opadd[voice],inst[8]);
+ midi_write_adlib(0xe3+adlib_opadd[voice],inst[9]);
+
+ midi_write_adlib(0xc0+voice,inst[10]);
+}
+
+void CmidPlayer::midi_fm_percussion(int ch, unsigned char *inst)
+{
+ int opadd = map_chan[ch - 12];
+
+ midi_write_adlib(0x20 + opadd, inst[0]);
+ midi_write_adlib(0x40 + opadd, inst[2]);
+ midi_write_adlib(0x60 + opadd, inst[4]);
+ midi_write_adlib(0x80 + opadd, inst[6]);
+ midi_write_adlib(0xe0 + opadd, inst[8]);
+ midi_write_adlib(0xc0 + opadd, inst[10]);
+}
+
+void CmidPlayer::midi_fm_volume(int voice, int volume)
+{
+ int vol;
+
+ if ((adlib_style&SIERRA_STYLE)==0) //sierra likes it loud!
+ {
+ vol=volume>>2;
+
+ if ((adlib_style&LUCAS_STYLE)!=0)
+ {
+ if ((adlib_data[0xc0+voice]&1)==1)
+ midi_write_adlib(0x40+adlib_opadd[voice], (unsigned char)((63-vol) |
+ (adlib_data[0x40+adlib_opadd[voice]]&0xc0)));
+ midi_write_adlib(0x43+adlib_opadd[voice], (unsigned char)((63-vol) |
+ (adlib_data[0x43+adlib_opadd[voice]]&0xc0)));
+ }
+ else
+ {
+ if ((adlib_data[0xc0+voice]&1)==1)
+ midi_write_adlib(0x40+adlib_opadd[voice], (unsigned char)((63-vol) |
+ (adlib_data[0x40+adlib_opadd[voice]]&0xc0)));
+ midi_write_adlib(0x43+adlib_opadd[voice], (unsigned char)((63-vol) |
+ (adlib_data[0x43+adlib_opadd[voice]]&0xc0)));
+ }
+ }
+}
+
+void CmidPlayer::midi_fm_playnote(int voice, int note, int volume)
+{
+ int freq=fnums[note%12];
+ int oct=note/12;
+ int c;
+
+ midi_fm_volume(voice,volume);
+ midi_write_adlib(0xa0+voice,(unsigned char)(freq&0xff));
+
+ c=((freq&0x300) >> 8)+(oct<<2) + (adlib_mode == ADLIB_MELODIC || voice < 6 ? (1<<5) : 0);
+ midi_write_adlib(0xb0+voice,(unsigned char)c);
+}
+
+void CmidPlayer::midi_fm_endnote(int voice)
+{
+ //midi_fm_volume(voice,0);
+ //midi_write_adlib(0xb0+voice,0);
+
+ midi_write_adlib(0xb0+voice,(unsigned char)(adlib_data[0xb0+voice]&(255-32)));
+}
+
+void CmidPlayer::midi_fm_reset()
+{
+ int i;
+
+ opl->init();
+
+ for (i=0; i<256; i++)
+ midi_write_adlib(i,0);
+
+ midi_write_adlib(0x01, 0x20);
+ midi_write_adlib(0xBD,0xc0);
+}
+
+bool CmidPlayer::update()
+{
+ long w,v,note,vel,ctrl,nv,x,l,lnum;
+ int i=0,j,c;
+ int on,onl,numchan;
+ int ret;
+
+ if (doing == 1)
+ {
+ // just get the first wait and ignore it :>
+ for (curtrack=0; curtrack<16; curtrack++)
+ if (track[curtrack].on)
+ {
+ pos=track[curtrack].pos;
+ if (type != FILE_SIERRA && type !=FILE_ADVSIERRA)
+ track[curtrack].iwait+=getval();
+ else
+ track[curtrack].iwait+=getnext(1);
+ track[curtrack].pos=pos;
+ }
+ doing=0;
+ }
+
+ iwait=0;
+ ret=1;
+
+ while (iwait==0 && ret==1)
+ {
+ for (curtrack=0; curtrack<16; curtrack++)
+ if (track[curtrack].on && track[curtrack].iwait==0 &&
+ track[curtrack].pos < track[curtrack].tend)
+ {
+ pos=track[curtrack].pos;
+
+ v=getnext(1);
+
+ // This is to do implied MIDI events.
+ if (v<0x80) {v=track[curtrack].pv; pos--;}
+ track[curtrack].pv=(unsigned char)v;
+
+ c=v&0x0f;
+ midiprintf ("[%2X]",v);
+ switch(v&0xf0)
+ {
+ case 0x80: /*note off*/
+ note=getnext(1); vel=getnext(1);
+ for (i=0; i<9; i++)
+ if (chp[i][0]==c && chp[i][1]==note)
+ {
+ midi_fm_endnote(i);
+ chp[i][0]=-1;
+ }
+ break;
+ case 0x90: /*note on*/
+ // doing=0;
+ note=getnext(1); vel=getnext(1);
+
+ if(adlib_mode == ADLIB_RYTHM)
+ numchan = 6;
+ else
+ numchan = 9;
+
+ if (ch[c].on!=0)
+ {
+ for (i=0; i<18; i++)
+ chp[i][2]++;
+
+ if(c < 11 || adlib_mode == ADLIB_MELODIC) {
+ j=0;
+ on=-1;onl=0;
+ for (i=0; i<numchan; i++)
+ if (chp[i][0]==-1 && chp[i][2]>onl)
+ { onl=chp[i][2]; on=i; j=1; }
+
+ if (on==-1)
+ {
+ onl=0;
+ for (i=0; i<numchan; i++)
+ if (chp[i][2]>onl)
+ { onl=chp[i][2]; on=i; }
+ }
+
+ if (j==0)
+ midi_fm_endnote(on);
+ } else
+ on = percussion_map[c - 11];
+
+ if (vel!=0 && ch[c].inum>=0 && ch[c].inum<128)
+ {
+ if (adlib_mode == ADLIB_MELODIC || c < 12)
+ midi_fm_instrument(on,ch[c].ins);
+ else
+ midi_fm_percussion(c, ch[c].ins);
+
+ if ((adlib_style&MIDI_STYLE)!=0)
+ {
+ nv=((ch[c].vol*vel)/128);
+ if ((adlib_style&LUCAS_STYLE)!=0)
+ nv*=2;
+ if (nv>127) nv=127;
+ nv=my_midi_fm_vol_table[nv];
+ if ((adlib_style&LUCAS_STYLE)!=0)
+ nv=(int)((float)sqrt((float)nv)*11);
+ }
+ else
+ {
+ nv=vel;
+ }
+
+ midi_fm_playnote(on,note+ch[c].nshift,nv*2);
+ chp[on][0]=c;
+ chp[on][1]=note;
+ chp[on][2]=0;
+
+ if(adlib_mode == ADLIB_RYTHM && c >= 11) {
+ midi_write_adlib(0xbd, adlib_data[0xbd] & ~(0x10 >> (c - 11)));
+ midi_write_adlib(0xbd, adlib_data[0xbd] | (0x10 >> (c - 11)));
+ }
+
+ }
+ else
+ {
+ if (vel==0) //same code as end note
+ {
+ for (i=0; i<9; i++)
+ if (chp[i][0]==c && chp[i][1]==note)
+ {
+ // midi_fm_volume(i,0); // really end the note
+ midi_fm_endnote(i);
+ chp[i][0]=-1;
+ }
+ }
+ else
+ { // i forget what this is for.
+ chp[on][0]=-1;
+ chp[on][2]=0;
+ }
+ }
+ midiprintf(" [%d:%d:%d:%d]\n",c,ch[c].inum,note,vel);
+ }
+ else
+ midiprintf ("off");
+ break;
+ case 0xa0: /*key after touch */
+ note=getnext(1); vel=getnext(1);
+ /* //this might all be good
+ for (i=0; i<9; i++)
+ if (chp[i][0]==c & chp[i][1]==note)
+
+midi_fm_playnote(i,note+cnote[c],my_midi_fm_vol_table[(cvols[c]*vel)/128]*2);
+ */
+ break;
+ case 0xb0: /*control change .. pitch bend? */
+ ctrl=getnext(1); vel=getnext(1);
+
+ switch(ctrl)
+ {
+ case 0x07:
+ midiprintf ("(pb:%d: %d %d)",c,ctrl,vel);
+ ch[c].vol=vel;
+ midiprintf("vol");
+ break;
+ case 0x67:
+ midiprintf ("\n\nhere:%d\n\n",vel);
+ if ((adlib_style&CMF_STYLE)!=0) {
+ adlib_mode=vel;
+ if(adlib_mode == ADLIB_RYTHM)
+ midi_write_adlib(0xbd, adlib_data[0xbd] | (1 << 5));
+ else
+ midi_write_adlib(0xbd, adlib_data[0xbd] & ~(1 << 5));
+ }
+ break;
+ }
+ break;
+ case 0xc0: /*patch change*/
+ x=getnext(1);
+ ch[c].inum=x;
+ for (j=0; j<11; j++)
+ ch[c].ins[j]=myinsbank[ch[c].inum][j];
+ break;
+ case 0xd0: /*chanel touch*/
+ x=getnext(1);
+ break;
+ case 0xe0: /*pitch wheel*/
+ x=getnext(1);
+ x=getnext(1);
+ break;
+ case 0xf0:
+ switch(v)
+ {
+ case 0xf0:
+ case 0xf7: /*sysex*/
+ l=getval();
+ if (datalook(pos+l)==0xf7)
+ i=1;
+ midiprintf("{%d}",l);
+ midiprintf("\n");
+
+ if (datalook(pos)==0x7d &&
+ datalook(pos+1)==0x10 &&
+ datalook(pos+2)<16)
+ {
+ adlib_style=LUCAS_STYLE|MIDI_STYLE;
+ for (i=0; i<l; i++)
+ {
+ midiprintf ("%x ",datalook(pos+i));
+ if ((i-3)%10 == 0) midiprintf("\n");
+ }
+ midiprintf ("\n");
+ getnext(1);
+ getnext(1);
+ c=getnext(1);
+ getnext(1);
+
+ // getnext(22); //temp
+ ch[c].ins[0]=(unsigned char)((getnext(1)<<4)+getnext(1));
+ ch[c].ins[2]=(unsigned char)(0xff-(((getnext(1)<<4)+getnext(1))&0x3f));
+ ch[c].ins[4]=(unsigned char)(0xff-((getnext(1)<<4)+getnext(1)));
+ ch[c].ins[6]=(unsigned char)(0xff-((getnext(1)<<4)+getnext(1)));
+ ch[c].ins[8]=(unsigned char)((getnext(1)<<4)+getnext(1));
+
+ ch[c].ins[1]=(unsigned char)((getnext(1)<<4)+getnext(1));
+ ch[c].ins[3]=(unsigned char)(0xff-(((getnext(1)<<4)+getnext(1))&0x3f));
+ ch[c].ins[5]=(unsigned char)(0xff-((getnext(1)<<4)+getnext(1)));
+ ch[c].ins[7]=(unsigned char)(0xff-((getnext(1)<<4)+getnext(1)));
+ ch[c].ins[9]=(unsigned char)((getnext(1)<<4)+getnext(1));
+
+ i=(getnext(1)<<4)+getnext(1);
+ ch[c].ins[10]=i;
+
+ //if ((i&1)==1) ch[c].ins[10]=1;
+
+ midiprintf ("\n%d: ",c);
+ for (i=0; i<11; i++)
+ midiprintf ("%2X ",ch[c].ins[i]);
+ getnext(l-26);
+ }
+ else
+ {
+ midiprintf("\n");
+ for (j=0; j<l; j++)
+ midiprintf ("%2X ",getnext(1));
+ }
+
+ midiprintf("\n");
+ if(i==1)
+ getnext(1);
+ break;
+ case 0xf1:
+ break;
+ case 0xf2:
+ getnext(2);
+ break;
+ case 0xf3:
+ getnext(1);
+ break;
+ case 0xf4:
+ break;
+ case 0xf5:
+ break;
+ case 0xf6: /*something*/
+ case 0xf8:
+ case 0xfa:
+ case 0xfb:
+ case 0xfc:
+ //this ends the track for sierra.
+ if (type == FILE_SIERRA ||
+ type == FILE_ADVSIERRA)
+ {
+ track[curtrack].tend=pos;
+ midiprintf ("endmark: %ld -- %lx\n",pos,pos);
+ }
+ break;
+ case 0xfe:
+ break;
+ case 0xfd:
+ break;
+ case 0xff:
+ v=getnext(1);
+ l=getval();
+ midiprintf ("\n");
+ midiprintf("{%X_%X}",v,l);
+ if (v==0x51)
+ {
+ lnum=getnext(l);
+ msqtr=lnum; /*set tempo*/
+ midiprintf ("(qtr=%ld)",msqtr);
+ }
+ else
+ {
+ for (i=0; i<l; i++)
+ midiprintf ("%2X ",getnext(1));
+ }
+ break;
+ }
+ break;
+ default: midiprintf("!",v); /* if we get down here, a error occurred */
+ break;
+ }
+
+ if (pos < track[curtrack].tend)
+ {
+ if (type != FILE_SIERRA && type !=FILE_ADVSIERRA)
+ w=getval();
+ else
+ w=getnext(1);
+ track[curtrack].iwait=w;
+ /*
+ if (w!=0)
+ {
+ midiprintf("\n<%d>",w);
+ f =
+((float)w/(float)deltas)*((float)msqtr/(float)1000000);
+ if (doing==1) f=0; //not playing yet. don't wait yet
+ }
+ */
+ }
+ else
+ track[curtrack].iwait=0;
+
+ track[curtrack].pos=pos;
+ }
+
+
+ ret=0; //end of song.
+ iwait=0;
+ for (curtrack=0; curtrack<16; curtrack++)
+ if (track[curtrack].on == 1 &&
+ track[curtrack].pos < track[curtrack].tend)
+ ret=1; //not yet..
+
+ if (ret==1)
+ {
+ iwait=0xffffff; // bigger than any wait can be!
+ for (curtrack=0; curtrack<16; curtrack++)
+ if (track[curtrack].on == 1 &&
+ track[curtrack].pos < track[curtrack].tend &&
+ track[curtrack].iwait < iwait)
+ iwait=track[curtrack].iwait;
+ }
+ }
+
+
+ if (iwait !=0 && ret==1)
+ {
+ for (curtrack=0; curtrack<16; curtrack++)
+ if (track[curtrack].on)
+ track[curtrack].iwait-=iwait;
+
+
+fwait=1.0f/(((float)iwait/(float)deltas)*((float)msqtr/(float)1000000));
+ }
+ else
+ fwait=50; // 1/50th of a second
+
+ midiprintf ("\n");
+ for (i=0; i<16; i++)
+ if (track[i].on)
+ if (track[i].pos < track[i].tend)
+ midiprintf ("<%d>",track[i].iwait);
+ else
+ midiprintf("stop");
+
+ /*
+ if (ret==0 && type==FILE_ADVSIERRA)
+ if (datalook(sierra_pos-2)!=0xff)
+ {
+ midiprintf ("next sectoin!");
+ sierra_next_section(p);
+ fwait=50;
+ ret=1;
+ }
+ */
+
+ if(ret)
+ return true;
+ else
+ return false;
+}
+
+float CmidPlayer::getrefresh()
+{
+ return (fwait > 0.01f ? fwait : 0.01f);
+}
+
+void CmidPlayer::rewind(int subsong)
+{
+ long i,j,n,m,l;
+ long o_sierra_pos;
+ unsigned char ins[16];
+
+ pos=0; tins=0;
+ adlib_style=MIDI_STYLE|CMF_STYLE;
+ adlib_mode=ADLIB_MELODIC;
+ for (i=0; i<128; i++)
+ for (j=0; j<16; j++)
+ myinsbank[i][j]=midi_fm_instruments[i][j];
+ for (i=0; i<16; i++)
+ {
+ ch[i].inum=0;
+ for (j=0; j<11; j++)
+ ch[i].ins[j]=myinsbank[ch[i].inum][j];
+ ch[i].vol=127;
+ ch[i].nshift=-25;
+ ch[i].on=1;
+ }
+
+ /* General init */
+ for (i=0; i<9; i++)
+ {
+ chp[i][0]=-1;
+ chp[i][2]=0;
+ }
+
+ deltas=250; // just a number, not a standard
+ msqtr=500000;
+ fwait=123; // gotta be a small thing.. sorta like nothing
+ iwait=0;
+
+ subsongs=1;
+
+ for (i=0; i<16; i++)
+ {
+ track[i].tend=0;
+ track[i].spos=0;
+ track[i].pos=0;
+ track[i].iwait=0;
+ track[i].on=0;
+ track[i].pv=0;
+ }
+ curtrack=0;
+
+ /* specific to file-type init */
+
+ pos=0;
+ i=getnext(1);
+ switch(type)
+ {
+ case FILE_LUCAS:
+ getnext(24); //skip junk and get to the midi.
+ adlib_style=LUCAS_STYLE|MIDI_STYLE;
+ //note: no break, we go right into midi headers...
+ case FILE_MIDI:
+ if (type != FILE_LUCAS)
+ tins=128;
+ getnext(11); /*skip header*/
+ deltas=getnext(2);
+ midiprintf ("deltas:%ld\n",deltas);
+ getnext(4);
+
+ curtrack=0;
+ track[curtrack].on=1;
+ track[curtrack].tend=getnext(4);
+ track[curtrack].spos=pos;
+ midiprintf ("tracklen:%ld\n",track[curtrack].tend);
+ break;
+ case FILE_CMF:
+ getnext(3); // ctmf
+ getnexti(2); //version
+ n=getnexti(2); // instrument offset
+ m=getnexti(2); // music offset
+ deltas=getnexti(2); //ticks/qtr note
+ msqtr=1000000/getnexti(2)*deltas;
+ //the stuff in the cmf is click ticks per second..
+
+ i=getnexti(2);
+ if(i) title = (char *)data+i;
+ i=getnexti(2);
+ if(i) author = (char *)data+i;
+ i=getnexti(2);
+ if(i) remarks = (char *)data+i;
+
+ getnext(16); // channel in use table ..
+ i=getnexti(2); // num instr
+ if (i>128) i=128; // to ward of bad numbers...
+ getnexti(2); //basic tempo
+
+ midiprintf("\nioff:%d\nmoff%d\ndeltas:%ld\nmsqtr:%ld\nnumi:%d\n",
+ n,m,deltas,msqtr,i);
+ pos=n; // jump to instruments
+ tins=i;
+ for (j=0; j<i; j++)
+ {
+ midiprintf ("\n%d: ",j);
+ for (l=0; l<16; l++)
+ {
+ myinsbank[j][l]=(unsigned char)getnext(1);
+ midiprintf ("%2X ",myinsbank[j][l]);
+ }
+ }
+
+ for (i=0; i<16; i++)
+ ch[i].nshift=-13;
+
+ adlib_style=CMF_STYLE;
+
+ curtrack=0;
+ track[curtrack].on=1;
+ track[curtrack].tend=flen; // music until the end of the file
+ track[curtrack].spos=m; //jump to midi music
+ break;
+ case FILE_OLDLUCAS:
+ msqtr=250000;
+ pos=9;
+ deltas=getnext(1);
+
+ i=8;
+ pos=0x19; // jump to instruments
+ tins=i;
+ for (j=0; j<i; j++)
+ {
+ midiprintf ("\n%d: ",j);
+ for (l=0; l<16; l++)
+ ins[l]=(unsigned char)getnext(1);
+
+ myinsbank[j][10]=ins[2];
+ myinsbank[j][0]=ins[3];
+ myinsbank[j][2]=ins[4];
+ myinsbank[j][4]=ins[5];
+ myinsbank[j][6]=ins[6];
+ myinsbank[j][8]=ins[7];
+ myinsbank[j][1]=ins[8];
+ myinsbank[j][3]=ins[9];
+ myinsbank[j][5]=ins[10];
+ myinsbank[j][7]=ins[11];
+ myinsbank[j][9]=ins[12];
+
+ for (l=0; l<11; l++)
+ midiprintf ("%2X ",myinsbank[j][l]);
+ }
+
+ for (i=0; i<16; i++)
+ {
+ if (i<tins)
+ {
+ ch[i].inum=i;
+ for (j=0; j<11; j++)
+ ch[i].ins[j]=myinsbank[ch[i].inum][j];
+ }
+ }
+
+ adlib_style=LUCAS_STYLE|MIDI_STYLE;
+
+ curtrack=0;
+ track[curtrack].on=1;
+ track[curtrack].tend=flen; // music until the end of the file
+ track[curtrack].spos=0x98; //jump to midi music
+ break;
+ case FILE_ADVSIERRA:
+ memcpy(myinsbank, smyinsbank, 128 * 16);
+ tins = stins;
+ deltas=0x20;
+ getnext(11); //worthless empty space and "stuff" :)
+
+ o_sierra_pos=sierra_pos=pos;
+ sierra_next_section();
+ while (datalook(sierra_pos-2)!=0xff)
+ {
+ sierra_next_section();
+ subsongs++;
+ }
+
+ if (subsong < 0 || subsong >= subsongs) subsong=0;
+
+ sierra_pos=o_sierra_pos;
+ sierra_next_section();
+ i=0;
+ while (i != subsong)
+ {
+ sierra_next_section();
+ i++;
+ }
+
+ adlib_style=SIERRA_STYLE|MIDI_STYLE; //advanced sierra tunes use volume
+ break;
+ case FILE_SIERRA:
+ memcpy(myinsbank, smyinsbank, 128 * 16);
+ tins = stins;
+ getnext(2);
+ deltas=0x20;
+
+ curtrack=0;
+ track[curtrack].on=1;
+ track[curtrack].tend=flen; // music until the end of the file
+
+ for (i=0; i<16; i++)
+ {
+ ch[i].nshift=-13;
+ ch[i].on=getnext(1);
+ ch[i].inum=getnext(1);
+ for (j=0; j<11; j++)
+ ch[i].ins[j]=myinsbank[ch[i].inum][j];
+ }
+
+ track[curtrack].spos=pos;
+ adlib_style=SIERRA_STYLE|MIDI_STYLE;
+ break;
+ }
+
+
+/* sprintf(info,"%s\r\nTicks/Quarter Note: %ld\r\n",info,deltas);
+ sprintf(info,"%sms/Quarter Note: %ld",info,msqtr); */
+
+ for (i=0; i<16; i++)
+ if (track[i].on)
+ {
+ track[i].pos=track[i].spos;
+ track[i].pv=0;
+ track[i].iwait=0;
+ }
+
+ doing=1;
+ midi_fm_reset();
+}
+
+std::string CmidPlayer::gettype()
+{
+ switch(type) {
+ case FILE_LUCAS:
+ return std::string("LucasArts AdLib MIDI");
+ case FILE_MIDI:
+ return std::string("General MIDI");
+ case FILE_CMF:
+ return std::string("Creative Music Format (CMF MIDI)");
+ case FILE_OLDLUCAS:
+ return std::string("Lucasfilm Adlib MIDI");
+ case FILE_ADVSIERRA:
+ return std::string("Sierra On-Line VGA MIDI");
+ case FILE_SIERRA:
+ return std::string("Sierra On-Line EGA MIDI");
+ default:
+ return std::string("MIDI unknown");
+ }
+}
diff --git a/plugins/adplug/adplug/mid.h b/plugins/adplug/adplug/mid.h
new file mode 100644
index 00000000..d28f7252
--- /dev/null
+++ b/plugins/adplug/adplug/mid.h
@@ -0,0 +1,112 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * mid.h - LAA, SCI, MID & CMF Player by Philip Hassey <philhassey@hotmail.com>
+ */
+
+#include "player.h"
+
+class CmidPlayer: public CPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CmidPlayer(Copl *newopl);
+ ~CmidPlayer()
+ { if(data) delete [] data; }
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype();
+ std::string gettitle()
+ { return std::string(title); }
+ std::string getauthor()
+ { return std::string(author); }
+ std::string getdesc()
+ { return std::string(remarks); }
+ unsigned int getinstruments()
+ { return tins; }
+ unsigned int getsubsongs()
+ { return subsongs; }
+
+ protected:
+ static const unsigned char adlib_opadd[];
+ static const int ops[], map_chan[], fnums[], percussion_map[];
+
+ struct midi_channel {
+ int inum;
+ unsigned char ins[11];
+ int vol;
+ int nshift;
+ int on;
+ };
+
+ struct midi_track {
+ unsigned long tend;
+ unsigned long spos;
+ unsigned long pos;
+ unsigned long iwait;
+ int on;
+ unsigned char pv;
+ };
+
+ char *author,*title,*remarks,emptystr;
+ long flen;
+ unsigned long pos;
+ unsigned long sierra_pos; //sierras gotta be special.. :>
+ int subsongs;
+ unsigned char *data;
+
+ unsigned char adlib_data[256];
+ int adlib_style;
+ int adlib_mode;
+ unsigned char myinsbank[128][16], smyinsbank[128][16];
+ midi_channel ch[16];
+ int chp[18][3];
+
+ long deltas;
+ long msqtr;
+
+ midi_track track[16];
+ unsigned int curtrack;
+
+ float fwait;
+ unsigned long iwait;
+ int doing;
+
+ int type,tins,stins;
+
+ private:
+ bool load_sierra_ins(const std::string &fname, const CFileProvider &fp);
+ void midiprintf(const char *format, ...);
+ unsigned char datalook(long pos);
+ unsigned long getnexti(unsigned long num);
+ unsigned long getnext(unsigned long num);
+ unsigned long getval();
+ void sierra_next_section();
+ void midi_write_adlib(unsigned int r, unsigned char v);
+ void midi_fm_instrument(int voice, unsigned char *inst);
+ void midi_fm_percussion(int ch, unsigned char *inst);
+ void midi_fm_volume(int voice, int volume);
+ void midi_fm_playnote(int voice, int note, int volume);
+ void midi_fm_endnote(int voice);
+ void midi_fm_reset();
+};
diff --git a/plugins/adplug/adplug/mididata.h b/plugins/adplug/adplug/mididata.h
new file mode 100644
index 00000000..2a83cd99
--- /dev/null
+++ b/plugins/adplug/adplug/mididata.h
@@ -0,0 +1,174 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999, 2000, 2001 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * FM instrument definitions below borrowed from the Allegro library by
+ * Phil Hassey, <philhassey@hotmail.com> - see "adplug/players/mid.cpp"
+ * for further acknowledgements.
+ */
+
+unsigned char midi_fm_instruments[128][14] =
+{
+
+ /* This set of GM instrument patches was provided by Jorrit Rouwe...
+ */
+
+ { 0x21, 0x21, 0x8f, 0x0c, 0xf2, 0xf2, 0x45, 0x76, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Acoustic Grand */
+ { 0x31, 0x21, 0x4b, 0x09, 0xf2, 0xf2, 0x54, 0x56, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Bright Acoustic */
+ { 0x31, 0x21, 0x49, 0x09, 0xf2, 0xf2, 0x55, 0x76, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Electric Grand */
+ { 0xb1, 0x61, 0x0e, 0x09, 0xf2, 0xf3, 0x3b, 0x0b, 0x00, 0x00, 0x06, 0, 0, 0 }, /* Honky-Tonk */
+ { 0x01, 0x21, 0x57, 0x09, 0xf1, 0xf1, 0x38, 0x28, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Electric Piano 1 */
+ { 0x01, 0x21, 0x93, 0x09, 0xf1, 0xf1, 0x38, 0x28, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Electric Piano 2 */
+ { 0x21, 0x36, 0x80, 0x17, 0xa2, 0xf1, 0x01, 0xd5, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Harpsichord */
+ { 0x01, 0x01, 0x92, 0x09, 0xc2, 0xc2, 0xa8, 0x58, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Clav */
+ { 0x0c, 0x81, 0x5c, 0x09, 0xf6, 0xf3, 0x54, 0xb5, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Celesta */
+ { 0x07, 0x11, 0x97, 0x89, 0xf6, 0xf5, 0x32, 0x11, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Glockenspiel */
+ { 0x17, 0x01, 0x21, 0x09, 0x56, 0xf6, 0x04, 0x04, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Music Box */
+ { 0x18, 0x81, 0x62, 0x09, 0xf3, 0xf2, 0xe6, 0xf6, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Vibraphone */
+ { 0x18, 0x21, 0x23, 0x09, 0xf7, 0xe5, 0x55, 0xd8, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Marimba */
+ { 0x15, 0x01, 0x91, 0x09, 0xf6, 0xf6, 0xa6, 0xe6, 0x00, 0x00, 0x04, 0, 0, 0 }, /* Xylophone */
+ { 0x45, 0x81, 0x59, 0x89, 0xd3, 0xa3, 0x82, 0xe3, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Tubular Bells */
+ { 0x03, 0x81, 0x49, 0x89, 0x74, 0xb3, 0x55, 0x05, 0x01, 0x00, 0x04, 0, 0, 0 }, /* Dulcimer */
+ { 0x71, 0x31, 0x92, 0x09, 0xf6, 0xf1, 0x14, 0x07, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Drawbar Organ */
+ { 0x72, 0x30, 0x14, 0x09, 0xc7, 0xc7, 0x58, 0x08, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Percussive Organ */
+ { 0x70, 0xb1, 0x44, 0x09, 0xaa, 0x8a, 0x18, 0x08, 0x00, 0x00, 0x04, 0, 0, 0 }, /* Rock Organ */
+ { 0x23, 0xb1, 0x93, 0x09, 0x97, 0x55, 0x23, 0x14, 0x01, 0x00, 0x04, 0, 0, 0 }, /* Church Organ */
+ { 0x61, 0xb1, 0x13, 0x89, 0x97, 0x55, 0x04, 0x04, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Reed Organ */
+ { 0x24, 0xb1, 0x48, 0x09, 0x98, 0x46, 0x2a, 0x1a, 0x01, 0x00, 0x0c, 0, 0, 0 }, /* Accoridan */
+ { 0x61, 0x21, 0x13, 0x09, 0x91, 0x61, 0x06, 0x07, 0x01, 0x00, 0x0a, 0, 0, 0 }, /* Harmonica */
+ { 0x21, 0xa1, 0x13, 0x92, 0x71, 0x61, 0x06, 0x07, 0x00, 0x00, 0x06, 0, 0, 0 }, /* Tango Accordian */
+ { 0x02, 0x41, 0x9c, 0x89, 0xf3, 0xf3, 0x94, 0xc8, 0x01, 0x00, 0x0c, 0, 0, 0 }, /* Acoustic Guitar(nylon) */
+ { 0x03, 0x11, 0x54, 0x09, 0xf3, 0xf1, 0x9a, 0xe7, 0x01, 0x00, 0x0c, 0, 0, 0 }, /* Acoustic Guitar(steel) */
+ { 0x23, 0x21, 0x5f, 0x09, 0xf1, 0xf2, 0x3a, 0xf8, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Electric Guitar(jazz) */
+ { 0x03, 0x21, 0x87, 0x89, 0xf6, 0xf3, 0x22, 0xf8, 0x01, 0x00, 0x06, 0, 0, 0 }, /* Electric Guitar(clean) */
+ { 0x03, 0x21, 0x47, 0x09, 0xf9, 0xf6, 0x54, 0x3a, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Electric Guitar(muted) */
+ { 0x23, 0x21, 0x4a, 0x0e, 0x91, 0x84, 0x41, 0x19, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Overdriven Guitar */
+ { 0x23, 0x21, 0x4a, 0x09, 0x95, 0x94, 0x19, 0x19, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Distortion Guitar */
+ { 0x09, 0x84, 0xa1, 0x89, 0x20, 0xd1, 0x4f, 0xf8, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Guitar Harmonics */
+ { 0x21, 0xa2, 0x1e, 0x09, 0x94, 0xc3, 0x06, 0xa6, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Acoustic Bass */
+ { 0x31, 0x31, 0x12, 0x09, 0xf1, 0xf1, 0x28, 0x18, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Electric Bass(finger) */
+ { 0x31, 0x31, 0x8d, 0x09, 0xf1, 0xf1, 0xe8, 0x78, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Electric Bass(pick) */
+ { 0x31, 0x32, 0x5b, 0x09, 0x51, 0x71, 0x28, 0x48, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Fretless Bass */
+ { 0x01, 0x21, 0x8b, 0x49, 0xa1, 0xf2, 0x9a, 0xdf, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Slap Bass 1 */
+ { 0x21, 0x21, 0x8b, 0x11, 0xa2, 0xa1, 0x16, 0xdf, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Slap Bass 2 */
+ { 0x31, 0x31, 0x8b, 0x09, 0xf4, 0xf1, 0xe8, 0x78, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Synth Bass 1 */
+ { 0x31, 0x31, 0x12, 0x09, 0xf1, 0xf1, 0x28, 0x18, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Synth Bass 2 */
+ { 0x31, 0x21, 0x15, 0x09, 0xdd, 0x56, 0x13, 0x26, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Violin */
+ { 0x31, 0x21, 0x16, 0x09, 0xdd, 0x66, 0x13, 0x06, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Viola */
+ { 0x71, 0x31, 0x49, 0x09, 0xd1, 0x61, 0x1c, 0x0c, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Cello */
+ { 0x21, 0x23, 0x4d, 0x89, 0x71, 0x72, 0x12, 0x06, 0x01, 0x00, 0x02, 0, 0, 0 }, /* Contrabass */
+ { 0xf1, 0xe1, 0x40, 0x09, 0xf1, 0x6f, 0x21, 0x16, 0x01, 0x00, 0x02, 0, 0, 0 }, /* Tremolo Strings */
+ { 0x02, 0x01, 0x1a, 0x89, 0xf5, 0x85, 0x75, 0x35, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Pizzicato Strings */
+ { 0x02, 0x01, 0x1d, 0x89, 0xf5, 0xf3, 0x75, 0xf4, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Orchestral Strings */
+ { 0x10, 0x11, 0x41, 0x09, 0xf5, 0xf2, 0x05, 0xc3, 0x01, 0x00, 0x02, 0, 0, 0 }, /* Timpani */
+ { 0x21, 0xa2, 0x9b, 0x0a, 0xb1, 0x72, 0x25, 0x08, 0x01, 0x00, 0x0e, 0, 0, 0 }, /* String Ensemble 1 */
+ { 0xa1, 0x21, 0x98, 0x09, 0x7f, 0x3f, 0x03, 0x07, 0x01, 0x01, 0x00, 0, 0, 0 }, /* String Ensemble 2 */
+ { 0xa1, 0x61, 0x93, 0x09, 0xc1, 0x4f, 0x12, 0x05, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* SynthStrings 1 */
+ { 0x21, 0x61, 0x18, 0x09, 0xc1, 0x4f, 0x22, 0x05, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* SynthStrings 2 */
+ { 0x31, 0x72, 0x5b, 0x8c, 0xf4, 0x8a, 0x15, 0x05, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Choir Aahs */
+ { 0xa1, 0x61, 0x90, 0x09, 0x74, 0x71, 0x39, 0x67, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Voice Oohs */
+ { 0x71, 0x72, 0x57, 0x09, 0x54, 0x7a, 0x05, 0x05, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Synth Voice */
+ { 0x90, 0x41, 0x00, 0x09, 0x54, 0xa5, 0x63, 0x45, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Orchestra Hit */
+ { 0x21, 0x21, 0x92, 0x0a, 0x85, 0x8f, 0x17, 0x09, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Trumpet */
+ { 0x21, 0x21, 0x94, 0x0e, 0x75, 0x8f, 0x17, 0x09, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Trombone */
+ { 0x21, 0x61, 0x94, 0x09, 0x76, 0x82, 0x15, 0x37, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Tuba */
+ { 0x31, 0x21, 0x43, 0x09, 0x9e, 0x62, 0x17, 0x2c, 0x01, 0x01, 0x02, 0, 0, 0 }, /* Muted Trumpet */
+ { 0x21, 0x21, 0x9b, 0x09, 0x61, 0x7f, 0x6a, 0x0a, 0x00, 0x00, 0x02, 0, 0, 0 }, /* French Horn */
+ { 0x61, 0x22, 0x8a, 0x0f, 0x75, 0x74, 0x1f, 0x0f, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Brass Section */
+ { 0xa1, 0x21, 0x86, 0x8c, 0x72, 0x71, 0x55, 0x18, 0x01, 0x00, 0x00, 0, 0, 0 }, /* SynthBrass 1 */
+ { 0x21, 0x21, 0x4d, 0x09, 0x54, 0xa6, 0x3c, 0x1c, 0x00, 0x00, 0x08, 0, 0, 0 }, /* SynthBrass 2 */
+ { 0x31, 0x61, 0x8f, 0x09, 0x93, 0x72, 0x02, 0x0b, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Soprano Sax */
+ { 0x31, 0x61, 0x8e, 0x09, 0x93, 0x72, 0x03, 0x09, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Alto Sax */
+ { 0x31, 0x61, 0x91, 0x09, 0x93, 0x82, 0x03, 0x09, 0x01, 0x00, 0x0a, 0, 0, 0 }, /* Tenor Sax */
+ { 0x31, 0x61, 0x8e, 0x09, 0x93, 0x72, 0x0f, 0x0f, 0x01, 0x00, 0x0a, 0, 0, 0 }, /* Baritone Sax */
+ { 0x21, 0x21, 0x4b, 0x09, 0xaa, 0x8f, 0x16, 0x0a, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Oboe */
+ { 0x31, 0x21, 0x90, 0x09, 0x7e, 0x8b, 0x17, 0x0c, 0x01, 0x01, 0x06, 0, 0, 0 }, /* English Horn */
+ { 0x31, 0x32, 0x81, 0x09, 0x75, 0x61, 0x19, 0x19, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Bassoon */
+ { 0x32, 0x21, 0x90, 0x09, 0x9b, 0x72, 0x21, 0x17, 0x00, 0x00, 0x04, 0, 0, 0 }, /* Clarinet */
+ { 0xe1, 0xe1, 0x1f, 0x09, 0x85, 0x65, 0x5f, 0x1a, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Piccolo */
+ { 0xe1, 0xe1, 0x46, 0x09, 0x88, 0x65, 0x5f, 0x1a, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Flute */
+ { 0xa1, 0x21, 0x9c, 0x09, 0x75, 0x75, 0x1f, 0x0a, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Recorder */
+ { 0x31, 0x21, 0x8b, 0x09, 0x84, 0x65, 0x58, 0x1a, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Pan Flute */
+ { 0xe1, 0xa1, 0x4c, 0x09, 0x66, 0x65, 0x56, 0x26, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Blown Bottle */
+ { 0x62, 0xa1, 0xcb, 0x09, 0x76, 0x55, 0x46, 0x36, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Skakuhachi */
+ { 0x62, 0xa1, 0xa2, 0x09, 0x57, 0x56, 0x07, 0x07, 0x00, 0x00, 0x0b, 0, 0, 0 }, /* Whistle */
+ { 0x62, 0xa1, 0x9c, 0x09, 0x77, 0x76, 0x07, 0x07, 0x00, 0x00, 0x0b, 0, 0, 0 }, /* Ocarina */
+ { 0x22, 0x21, 0x59, 0x09, 0xff, 0xff, 0x03, 0x0f, 0x02, 0x00, 0x00, 0, 0, 0 }, /* Lead 1 (square) */
+ { 0x21, 0x21, 0x0e, 0x09, 0xff, 0xff, 0x0f, 0x0f, 0x01, 0x01, 0x00, 0, 0, 0 }, /* Lead 2 (sawtooth) */
+ { 0x22, 0x21, 0x46, 0x89, 0x86, 0x64, 0x55, 0x18, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Lead 3 (calliope) */
+ { 0x21, 0xa1, 0x45, 0x09, 0x66, 0x96, 0x12, 0x0a, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Lead 4 (chiff) */
+ { 0x21, 0x22, 0x8b, 0x09, 0x92, 0x91, 0x2a, 0x2a, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Lead 5 (charang) */
+ { 0xa2, 0x61, 0x9e, 0x49, 0xdf, 0x6f, 0x05, 0x07, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Lead 6 (voice) */
+ { 0x20, 0x60, 0x1a, 0x09, 0xef, 0x8f, 0x01, 0x06, 0x00, 0x02, 0x00, 0, 0, 0 }, /* Lead 7 (fifths) */
+ { 0x21, 0x21, 0x8f, 0x86, 0xf1, 0xf4, 0x29, 0x09, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Lead 8 (bass+lead) */
+ { 0x77, 0xa1, 0xa5, 0x09, 0x53, 0xa0, 0x94, 0x05, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Pad 1 (new age) */
+ { 0x61, 0xb1, 0x1f, 0x89, 0xa8, 0x25, 0x11, 0x03, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Pad 2 (warm) */
+ { 0x61, 0x61, 0x17, 0x09, 0x91, 0x55, 0x34, 0x16, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Pad 3 (polysynth) */
+ { 0x71, 0x72, 0x5d, 0x09, 0x54, 0x6a, 0x01, 0x03, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Pad 4 (choir) */
+ { 0x21, 0xa2, 0x97, 0x09, 0x21, 0x42, 0x43, 0x35, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Pad 5 (bowed) */
+ { 0xa1, 0x21, 0x1c, 0x09, 0xa1, 0x31, 0x77, 0x47, 0x01, 0x01, 0x00, 0, 0, 0 }, /* Pad 6 (metallic) */
+ { 0x21, 0x61, 0x89, 0x0c, 0x11, 0x42, 0x33, 0x25, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Pad 7 (halo) */
+ { 0xa1, 0x21, 0x15, 0x09, 0x11, 0xcf, 0x47, 0x07, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Pad 8 (sweep) */
+ { 0x3a, 0x51, 0xce, 0x09, 0xf8, 0x86, 0xf6, 0x02, 0x00, 0x00, 0x02, 0, 0, 0 }, /* FX 1 (rain) */
+ { 0x21, 0x21, 0x15, 0x09, 0x21, 0x41, 0x23, 0x13, 0x01, 0x00, 0x00, 0, 0, 0 }, /* FX 2 (soundtrack) */
+ { 0x06, 0x01, 0x5b, 0x09, 0x74, 0xa5, 0x95, 0x72, 0x00, 0x00, 0x00, 0, 0, 0 }, /* FX 3 (crystal) */
+ { 0x22, 0x61, 0x92, 0x8c, 0xb1, 0xf2, 0x81, 0x26, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* FX 4 (atmosphere) */
+ { 0x41, 0x42, 0x4d, 0x09, 0xf1, 0xf2, 0x51, 0xf5, 0x01, 0x00, 0x00, 0, 0, 0 }, /* FX 5 (brightness) */
+ { 0x61, 0xa3, 0x94, 0x89, 0x11, 0x11, 0x51, 0x13, 0x01, 0x00, 0x06, 0, 0, 0 }, /* FX 6 (goblins) */
+ { 0x61, 0xa1, 0x8c, 0x89, 0x11, 0x1d, 0x31, 0x03, 0x00, 0x00, 0x06, 0, 0, 0 }, /* FX 7 (echoes) */
+ { 0xa4, 0x61, 0x4c, 0x09, 0xf3, 0x81, 0x73, 0x23, 0x01, 0x00, 0x04, 0, 0, 0 }, /* FX 8 (sci-fi) */
+ { 0x02, 0x07, 0x85, 0x0c, 0xd2, 0xf2, 0x53, 0xf6, 0x00, 0x01, 0x00, 0, 0, 0 }, /* Sitar */
+ { 0x11, 0x13, 0x0c, 0x89, 0xa3, 0xa2, 0x11, 0xe5, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Banjo */
+ { 0x11, 0x11, 0x06, 0x09, 0xf6, 0xf2, 0x41, 0xe6, 0x01, 0x02, 0x04, 0, 0, 0 }, /* Shamisen */
+ { 0x93, 0x91, 0x91, 0x09, 0xd4, 0xeb, 0x32, 0x11, 0x00, 0x01, 0x08, 0, 0, 0 }, /* Koto */
+ { 0x04, 0x01, 0x4f, 0x09, 0xfa, 0xc2, 0x56, 0x05, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Kalimba */
+ { 0x21, 0x22, 0x49, 0x09, 0x7c, 0x6f, 0x20, 0x0c, 0x00, 0x01, 0x06, 0, 0, 0 }, /* Bagpipe */
+ { 0x31, 0x21, 0x85, 0x09, 0xdd, 0x56, 0x33, 0x16, 0x01, 0x00, 0x0a, 0, 0, 0 }, /* Fiddle */
+ { 0x20, 0x21, 0x04, 0x8a, 0xda, 0x8f, 0x05, 0x0b, 0x02, 0x00, 0x06, 0, 0, 0 }, /* Shanai */
+ { 0x05, 0x03, 0x6a, 0x89, 0xf1, 0xc3, 0xe5, 0xe5, 0x00, 0x00, 0x06, 0, 0, 0 }, /* Tinkle Bell */
+ { 0x07, 0x02, 0x15, 0x09, 0xec, 0xf8, 0x26, 0x16, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Agogo */
+ { 0x05, 0x01, 0x9d, 0x09, 0x67, 0xdf, 0x35, 0x05, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Steel Drums */
+ { 0x18, 0x12, 0x96, 0x09, 0xfa, 0xf8, 0x28, 0xe5, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Woodblock */
+ { 0x10, 0x00, 0x86, 0x0c, 0xa8, 0xfa, 0x07, 0x03, 0x00, 0x00, 0x06, 0, 0, 0 }, /* Taiko Drum */
+ { 0x11, 0x10, 0x41, 0x0c, 0xf8, 0xf3, 0x47, 0x03, 0x02, 0x00, 0x04, 0, 0, 0 }, /* Melodic Tom */
+ { 0x01, 0x10, 0x8e, 0x09, 0xf1, 0xf3, 0x06, 0x02, 0x02, 0x00, 0x0e, 0, 0, 0 }, /* Synth Drum */
+ { 0x0e, 0xc0, 0x00, 0x09, 0x1f, 0x1f, 0x00, 0xff, 0x00, 0x03, 0x0e, 0, 0, 0 }, /* Reverse Cymbal */
+ { 0x06, 0x03, 0x80, 0x91, 0xf8, 0x56, 0x24, 0x84, 0x00, 0x02, 0x0e, 0, 0, 0 }, /* Guitar Fret Noise */
+ { 0x0e, 0xd0, 0x00, 0x0e, 0xf8, 0x34, 0x00, 0x04, 0x00, 0x03, 0x0e, 0, 0, 0 }, /* Breath Noise */
+ { 0x0e, 0xc0, 0x00, 0x09, 0xf6, 0x1f, 0x00, 0x02, 0x00, 0x03, 0x0e, 0, 0, 0 }, /* Seashore */
+ { 0xd5, 0xda, 0x95, 0x49, 0x37, 0x56, 0xa3, 0x37, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Bird Tweet */
+ { 0x35, 0x14, 0x5c, 0x11, 0xb2, 0xf4, 0x61, 0x15, 0x02, 0x00, 0x0a, 0, 0, 0 }, /* Telephone ring */
+ { 0x0e, 0xd0, 0x00, 0x09, 0xf6, 0x4f, 0x00, 0xf5, 0x00, 0x03, 0x0e, 0, 0, 0 }, /* Helicopter */
+ { 0x26, 0xe4, 0x00, 0x09, 0xff, 0x12, 0x01, 0x16, 0x00, 0x01, 0x0e, 0, 0, 0 }, /* Applause */
+ { 0x00, 0x00, 0x00, 0x09, 0xf3, 0xf6, 0xf0, 0xc9, 0x00, 0x02, 0x0e, 0, 0, 0 } /* Gunshot */
+
+};
+
+/* logarithmic relationship between midi and FM volumes */
+static int my_midi_fm_vol_table[128] = {
+ 0, 11, 16, 19, 22, 25, 27, 29, 32, 33, 35, 37, 39, 40, 42, 43,
+ 45, 46, 48, 49, 50, 51, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 64, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 75, 76, 77,
+ 78, 79, 80, 80, 81, 82, 83, 83, 84, 85, 86, 86, 87, 88, 89, 89,
+ 90, 91, 91, 92, 93, 93, 94, 95, 96, 96, 97, 97, 98, 99, 99, 100,
+ 101, 101, 102, 103, 103, 104, 104, 105, 106, 106, 107, 107, 108,
+ 109, 109, 110, 110, 111, 112, 112, 113, 113, 114, 114, 115, 115,
+ 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122,
+ 123, 123, 124, 124, 125, 125, 126, 126, 127
+};
+
diff --git a/plugins/adplug/adplug/mkj.cpp b/plugins/adplug/adplug/mkj.cpp
new file mode 100644
index 00000000..32d7e27c
--- /dev/null
+++ b/plugins/adplug/adplug/mkj.cpp
@@ -0,0 +1,164 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2004 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * mkj.cpp - MKJamz Player, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include "mkj.h"
+#include "debug.h"
+
+CPlayer *CmkjPlayer::factory(Copl *newopl)
+{
+ return new CmkjPlayer(newopl);
+}
+
+bool CmkjPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ char id[6];
+ float ver;
+ int i, j;
+ short inst[8];
+
+ // file validation
+ f->readString(id, 6);
+ if(strncmp(id,"MKJamz",6)) { fp.close(f); return false; }
+ ver = f->readFloat(binio::Single);
+ if(ver > 1.12) { fp.close(f); return false; }
+
+ // load
+ maxchannel = f->readInt(2);
+ opl->init(); opl->write(1, 32);
+ for(i = 0; i < maxchannel; i++) {
+ for(j = 0; j < 8; j++) inst[j] = f->readInt(2);
+ opl->write(0x20+op_table[i],inst[4]);
+ opl->write(0x23+op_table[i],inst[0]);
+ opl->write(0x40+op_table[i],inst[5]);
+ opl->write(0x43+op_table[i],inst[1]);
+ opl->write(0x60+op_table[i],inst[6]);
+ opl->write(0x63+op_table[i],inst[2]);
+ opl->write(0x80+op_table[i],inst[7]);
+ opl->write(0x83+op_table[i],inst[3]);
+ }
+ maxnotes = f->readInt(2);
+ songbuf = new short [(maxchannel+1)*maxnotes];
+ for(i = 0; i < maxchannel; i++) channel[i].defined = f->readInt(2);
+ for(i = 0; i < (maxchannel + 1) * maxnotes; i++)
+ songbuf[i] = f->readInt(2);
+
+ AdPlug_LogWrite("CmkjPlayer::load(\"%s\"): loaded file ver %.2f, %d channels,"
+ " %d notes/channel.\n", filename.c_str(), ver, maxchannel,
+ maxnotes);
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+bool CmkjPlayer::update()
+{
+ int c, i;
+ short note;
+
+ for(c = 0; c < maxchannel; c++) {
+ if(!channel[c].defined) // skip if channel is disabled
+ continue;
+
+ if(channel[c].pstat) {
+ channel[c].pstat--;
+ continue;
+ }
+
+ opl->write(0xb0 + c, 0); // key off
+ do {
+ assert(channel[c].songptr < (maxchannel + 1) * maxnotes);
+ note = songbuf[channel[c].songptr];
+ if(channel[c].songptr - c > maxchannel)
+ if(note && note < 250)
+ channel[c].pstat = channel[c].speed;
+ switch(note) {
+ // normal notes
+ case 68: opl->write(0xa0 + c,0x81); opl->write(0xb0 + c,0x21 + 4 * channel[c].octave); break;
+ case 69: opl->write(0xa0 + c,0xb0); opl->write(0xb0 + c,0x21 + 4 * channel[c].octave); break;
+ case 70: opl->write(0xa0 + c,0xca); opl->write(0xb0 + c,0x21 + 4 * channel[c].octave); break;
+ case 71: opl->write(0xa0 + c,0x2); opl->write(0xb0 + c,0x22 + 4 * channel[c].octave); break;
+ case 65: opl->write(0xa0 + c,0x41); opl->write(0xb0 + c,0x22 + 4 * channel[c].octave); break;
+ case 66: opl->write(0xa0 + c,0x87); opl->write(0xb0 + c,0x22 + 4 * channel[c].octave); break;
+ case 67: opl->write(0xa0 + c,0xae); opl->write(0xb0 + c,0x22 + 4 * channel[c].octave); break;
+ case 17: opl->write(0xa0 + c,0x6b); opl->write(0xb0 + c,0x21 + 4 * channel[c].octave); break;
+ case 18: opl->write(0xa0 + c,0x98); opl->write(0xb0 + c,0x21 + 4 * channel[c].octave); break;
+ case 20: opl->write(0xa0 + c,0xe5); opl->write(0xb0 + c,0x21 + 4 * channel[c].octave); break;
+ case 21: opl->write(0xa0 + c,0x20); opl->write(0xb0 + c,0x22 + 4 * channel[c].octave); break;
+ case 15: opl->write(0xa0 + c,0x63); opl->write(0xb0 + c,0x22 + 4 * channel[c].octave); break;
+ case 255: // delay
+ channel[c].songptr += maxchannel;
+ channel[c].pstat = songbuf[channel[c].songptr];
+ break;
+ case 254: // set octave
+ channel[c].songptr += maxchannel;
+ channel[c].octave = songbuf[channel[c].songptr];
+ break;
+ case 253: // set speed
+ channel[c].songptr += maxchannel;
+ channel[c].speed = songbuf[channel[c].songptr];
+ break;
+ case 252: // set waveform
+ channel[c].songptr += maxchannel;
+ channel[c].waveform = songbuf[channel[c].songptr] - 300;
+ if(c > 2)
+ opl->write(0xe0 + c + (c+6),channel[c].waveform);
+ else
+ opl->write(0xe0 + c,channel[c].waveform);
+ break;
+ case 251: // song end
+ for(i = 0; i < maxchannel; i++) channel[i].songptr = i;
+ songend = true;
+ return false;
+ }
+
+ if(channel[c].songptr - c < maxnotes)
+ channel[c].songptr += maxchannel;
+ else
+ channel[c].songptr = c;
+ } while(!channel[c].pstat);
+ }
+
+ return !songend;
+}
+
+void CmkjPlayer::rewind(int subsong)
+{
+ int i;
+
+ for(i = 0; i < maxchannel; i++) {
+ channel[i].pstat = 0;
+ channel[i].speed = 0;
+ channel[i].waveform = 0;
+ channel[i].songptr = i;
+ channel[i].octave = 4;
+ }
+
+ songend = false;
+}
+
+float CmkjPlayer::getrefresh()
+{
+ return 100.0f;
+}
diff --git a/plugins/adplug/adplug/mkj.h b/plugins/adplug/adplug/mkj.h
new file mode 100644
index 00000000..a7a2ffad
--- /dev/null
+++ b/plugins/adplug/adplug/mkj.h
@@ -0,0 +1,50 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * mkj.h - MKJamz Player, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "player.h"
+
+class CmkjPlayer: public CPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CmkjPlayer(Copl *newopl)
+ : CPlayer(newopl), songbuf(0)
+ { }
+ ~CmkjPlayer()
+ { if(songbuf) delete [] songbuf; }
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype()
+ { return std::string("MKJamz Audio File"); }
+
+private:
+ short maxchannel,maxnotes,*songbuf;
+ bool songend;
+
+ struct {
+ short defined,songptr,octave,waveform,pstat,speed,delay;
+ } channel[9];
+};
diff --git a/plugins/adplug/adplug/msc.cpp b/plugins/adplug/adplug/msc.cpp
new file mode 100644
index 00000000..3702adcf
--- /dev/null
+++ b/plugins/adplug/adplug/msc.cpp
@@ -0,0 +1,313 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * msc.c - MSC Player by Lubomir Bulej (pallas@kadan.cz)
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "msc.h"
+#include "debug.h"
+
+const unsigned char CmscPlayer::msc_signature [MSC_SIGN_LEN] = {
+ 'C', 'e', 'r', 'e', 's', ' ', '\x13', ' ',
+ 'M', 'S', 'C', 'p', 'l', 'a', 'y', ' ' };
+
+/*** public methods *************************************/
+
+CPlayer *CmscPlayer::factory (Copl * newopl)
+{
+ return new CmscPlayer (newopl);
+}
+
+CmscPlayer::CmscPlayer(Copl * newopl) : CPlayer (newopl)
+{
+ desc = NULL;
+ msc_data = NULL;
+ raw_data = NULL;
+ nr_blocks = 0;
+}
+
+CmscPlayer::~CmscPlayer()
+{
+ if (raw_data != NULL)
+ delete [] raw_data;
+
+ if (msc_data != NULL) {
+ // free compressed blocks
+ for (int blk_num = 0; blk_num < nr_blocks; blk_num++) {
+ if (msc_data [blk_num].mb_data != NULL)
+ delete [] msc_data [blk_num].mb_data;
+ }
+
+ delete [] msc_data;
+ }
+
+ if (desc != NULL)
+ delete [] desc;
+}
+
+bool CmscPlayer::load(const std::string & filename, const CFileProvider & fp)
+{
+ binistream * bf;
+ msc_header hdr;
+
+ // open and validate the file
+ bf = fp.open (filename);
+ if (! bf)
+ return false;
+
+ if (! load_header (bf, & hdr)) {
+ fp.close (bf);
+ return false;
+ }
+
+ // get stuff from the header
+ version = hdr.mh_ver;
+ timer_div = hdr.mh_timer;
+ nr_blocks = hdr.mh_nr_blocks;
+ block_len = hdr.mh_block_len;
+
+ if (! nr_blocks) {
+ fp.close (bf);
+ return false;
+ }
+
+ // load compressed data blocks
+ msc_data = new msc_block [nr_blocks];
+ raw_data = new u8 [block_len];
+
+ for (int blk_num = 0; blk_num < nr_blocks; blk_num++) {
+ msc_block blk;
+
+ blk.mb_length = bf->readInt (2);
+ blk.mb_data = new u8 [blk.mb_length];
+ for (int oct_num = 0; oct_num < blk.mb_length; oct_num++) {
+ blk.mb_data [oct_num] = bf->readInt (1);
+ }
+
+ msc_data [blk_num] = blk;
+ }
+
+ // clean up & initialize
+ fp.close (bf);
+ rewind (0);
+
+ return true;
+}
+
+bool CmscPlayer::update()
+{
+ // output data
+ while (! delay) {
+ u8 cmnd;
+ u8 data;
+
+ // decode data
+ if (! decode_octet (& cmnd))
+ return false;
+
+ if (! decode_octet (& data))
+ return false;
+
+ // check for special commands
+ switch (cmnd) {
+
+ // delay
+ case 0xff:
+ delay = 1 + (u8) (data - 1);
+ break;
+
+ // play command & data
+ default:
+ opl->write (cmnd, data);
+
+ } // command switch
+ } // play pass
+
+
+ // count delays
+ if (delay)
+ delay--;
+
+ // advance player position
+ play_pos++;
+ return true;
+}
+
+void CmscPlayer::rewind(int subsong)
+{
+ // reset state
+ dec_prefix = 0;
+ block_num = 0;
+ block_pos = 0;
+ play_pos = 0;
+ raw_pos = 0;
+ delay = 0;
+
+ // init the OPL chip and go to OPL2 mode
+ opl->init();
+ opl->write(1, 32);
+}
+
+float CmscPlayer::getrefresh()
+{
+ // PC timer oscillator frequency / wait register
+ return 1193180 / (float) (timer_div ? timer_div : 0xffff);
+}
+
+std::string CmscPlayer::gettype()
+{
+ char vstr [40];
+
+ sprintf(vstr, "AdLib MSCplay (version %d)", version);
+ return std::string (vstr);
+}
+
+/*** private methods *************************************/
+
+bool CmscPlayer::load_header(binistream * bf, msc_header * hdr)
+{
+ // check signature
+ bf->readString ((char *) hdr->mh_sign, sizeof (hdr->mh_sign));
+ if (memcmp (msc_signature, hdr->mh_sign, MSC_SIGN_LEN) != 0)
+ return false;
+
+ // check version
+ hdr->mh_ver = bf->readInt (2);
+ if (hdr->mh_ver != 0)
+ return false;
+
+ bf->readString ((char *) hdr->mh_desc, sizeof (hdr->mh_desc));
+ hdr->mh_timer = bf->readInt (2);
+ hdr->mh_nr_blocks = bf->readInt (2);
+ hdr->mh_block_len = bf->readInt (2);
+ return true;
+}
+
+bool CmscPlayer::decode_octet(u8 * output)
+{
+ msc_block blk; // compressed data block
+
+ if (block_num >= nr_blocks)
+ return false;
+
+ blk = msc_data [block_num];
+ while (1) {
+ u8 octet; // decoded octet
+ u8 len_corr; // length correction
+
+ // advance to next block if necessary
+ if (block_pos >= blk.mb_length && dec_len == 0) {
+ block_num++;
+ if (block_num >= nr_blocks)
+ return false;
+
+ blk = msc_data [block_num];
+ block_pos = 0;
+ raw_pos = 0;
+ }
+
+ // decode the compressed music data
+ switch (dec_prefix) {
+
+ // decode prefix
+ case 155:
+ case 175:
+ octet = blk.mb_data [block_pos++];
+ if (octet == 0) {
+ // invalid prefix, output original
+ octet = dec_prefix;
+ dec_prefix = 0;
+ break;
+ }
+
+ // isolate length and distance
+ dec_len = (octet & 0x0F);
+ len_corr = 2;
+
+ dec_dist = (octet & 0xF0) >> 4;
+ if (dec_prefix == 155)
+ dec_dist++;
+
+ // next decode step for respective prefix type
+ dec_prefix++;
+ continue;
+
+
+ // check for extended length
+ case 156:
+ if (dec_len == 15)
+ dec_len += blk.mb_data [block_pos++];
+
+ // add length correction and go for copy mode
+ dec_len += len_corr;
+ dec_prefix = 255;
+ continue;
+
+
+ // get extended distance
+ case 176:
+ dec_dist += 17 + 16 * blk.mb_data [block_pos++];
+ len_corr = 3;
+
+ // check for extended length
+ dec_prefix = 156;
+ continue;
+
+
+ // prefix copy mode
+ case 255:
+ if((int)raw_pos >= dec_dist)
+ octet = raw_data [raw_pos - dec_dist];
+ else {
+ AdPlug_LogWrite("error! read before raw_data buffer.\n");
+ octet = 0;
+ }
+
+ dec_len--;
+ if (dec_len == 0) {
+ // back to normal mode
+ dec_prefix = 0;
+ }
+
+ break;
+
+
+ // normal mode
+ default:
+ octet = blk.mb_data [block_pos++];
+ if (octet == 155 || octet == 175) {
+ // it's a prefix, restart
+ dec_prefix = octet;
+ continue;
+ }
+ } // prefix switch
+
+
+ // output the octet
+ if (output != NULL)
+ *output = octet;
+
+ raw_data [raw_pos++] = octet;
+ break;
+ }; // decode pass
+
+ return true;
+}
diff --git a/plugins/adplug/adplug/msc.h b/plugins/adplug/adplug/msc.h
new file mode 100644
index 00000000..a42ec750
--- /dev/null
+++ b/plugins/adplug/adplug/msc.h
@@ -0,0 +1,87 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * msc.h - MSC Player by Lubomir Bulej (pallas@kadan.cz)
+ */
+
+#include "player.h"
+
+#define MSC_SIGN_LEN 16
+#define MSC_DESC_LEN 64
+
+class CmscPlayer: public CPlayer
+{
+ public:
+ static CPlayer * factory(Copl * newopl);
+
+ CmscPlayer(Copl * newopl);
+ ~CmscPlayer();
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype ();
+
+ protected:
+ typedef unsigned char u8;
+ typedef unsigned short u16;
+
+ struct msc_header {
+ u8 mh_sign [MSC_SIGN_LEN];
+ u16 mh_ver;
+ u8 mh_desc [MSC_DESC_LEN];
+ u16 mh_timer;
+ u16 mh_nr_blocks;
+ u16 mh_block_len;
+ };
+
+ struct msc_block {
+ u16 mb_length;
+ u8 * mb_data;
+ };
+
+ // file data
+ char * desc; // song desctiption
+ unsigned short version; // file version
+ unsigned short nr_blocks; // number of music blocks
+ unsigned short block_len; // maximal block length
+ unsigned short timer_div; // timer divisor
+ msc_block * msc_data; // compressed music data
+
+ // decoder state
+ unsigned long block_num; // active block
+ unsigned long block_pos; // position in block
+ unsigned long raw_pos; // position in data buffer
+ u8 * raw_data; // decompression buffer
+
+ u8 dec_prefix; // prefix / state
+ int dec_dist; // prefix distance
+ unsigned int dec_len; // prefix length
+
+ // player state
+ unsigned char delay; // active delay
+ unsigned long play_pos; // player position
+
+ private:
+ static const u8 msc_signature [MSC_SIGN_LEN];
+
+ bool load_header (binistream * bf, msc_header * hdr);
+ bool decode_octet (u8 * output);
+};
diff --git a/plugins/adplug/adplug/mtk.cpp b/plugins/adplug/adplug/mtk.cpp
new file mode 100644
index 00000000..410e00e0
--- /dev/null
+++ b/plugins/adplug/adplug/mtk.cpp
@@ -0,0 +1,141 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * mtk.cpp - MPU-401 Trakker Loader by Simon Peter (dn.tlp@gmx.net)
+ */
+
+#include <string.h>
+#include "mtk.h"
+
+/*** public methods **************************************/
+
+CPlayer *CmtkLoader::factory(Copl *newopl)
+{
+ return new CmtkLoader(newopl);
+}
+
+bool CmtkLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ struct {
+ char id[18];
+ unsigned short crc,size;
+ } header;
+ struct mtkdata {
+ char songname[34],composername[34],instname[0x80][34];
+ unsigned char insts[0x80][12],order[0x80],dummy,patterns[0x32][0x40][9];
+ } *data;
+ unsigned char *cmp,*org;
+ unsigned int i;
+ unsigned long cmpsize,cmpptr=0,orgptr=0;
+ unsigned short ctrlbits=0,ctrlmask=0,cmd,cnt,offs;
+
+ // read header
+ f->readString(header.id, 18);
+ header.crc = f->readInt(2);
+ header.size = f->readInt(2);
+
+ // file validation section
+ if(strncmp(header.id,"mpu401tr\x92kk\xeer@data",18))
+ { fp.close(f); return false; }
+
+ // load section
+ cmpsize = fp.filesize(f) - 22;
+ cmp = new unsigned char[cmpsize];
+ org = new unsigned char[header.size];
+ for(i = 0; i < cmpsize; i++) cmp[i] = f->readInt(1);
+ fp.close(f);
+
+ while(cmpptr < cmpsize) { // decompress
+ ctrlmask >>= 1;
+ if(!ctrlmask) {
+ ctrlbits = cmp[cmpptr] + (cmp[cmpptr + 1] << 8);
+ cmpptr += 2;
+ ctrlmask = 0x8000;
+ }
+ if(!(ctrlbits & ctrlmask)) { // uncompressed data
+ if(orgptr >= header.size)
+ goto err;
+
+ org[orgptr] = cmp[cmpptr];
+ orgptr++; cmpptr++;
+ continue;
+ }
+
+ // compressed data
+ cmd = (cmp[cmpptr] >> 4) & 0x0f;
+ cnt = cmp[cmpptr] & 0x0f;
+ cmpptr++;
+ switch(cmd) {
+ case 0:
+ if(orgptr + cnt > header.size) goto err;
+ cnt += 3;
+ memset(&org[orgptr],cmp[cmpptr],cnt);
+ cmpptr++; orgptr += cnt;
+ break;
+
+ case 1:
+ if(orgptr + cnt > header.size) goto err;
+ cnt += (cmp[cmpptr] << 4) + 19;
+ memset(&org[orgptr],cmp[++cmpptr],cnt);
+ cmpptr++; orgptr += cnt;
+ break;
+
+ case 2:
+ if(orgptr + cnt > header.size) goto err;
+ offs = (cnt+3) + (cmp[cmpptr] << 4);
+ cnt = cmp[++cmpptr] + 16; cmpptr++;
+ memcpy(&org[orgptr],&org[orgptr - offs],cnt);
+ orgptr += cnt;
+ break;
+
+ default:
+ if(orgptr + cmd > header.size) goto err;
+ offs = (cnt+3) + (cmp[cmpptr++] << 4);
+ memcpy(&org[orgptr],&org[orgptr-offs],cmd);
+ orgptr += cmd;
+ break;
+ }
+ }
+ delete [] cmp;
+ data = (struct mtkdata *) org;
+
+ // convert to HSC replay data
+ memset(title,0,34); strncpy(title,data->songname+1,33);
+ memset(composer,0,34); strncpy(composer,data->composername+1,33);
+ memset(instname,0,0x80*34);
+ for(i=0;i<0x80;i++)
+ strncpy(instname[i],data->instname[i]+1,33);
+ memcpy(instr,data->insts,0x80 * 12);
+ memcpy(song,data->order,0x80);
+ memcpy(patterns,data->patterns,header.size-6084);
+ for (i=0;i<128;i++) { // correct instruments
+ instr[i][2] ^= (instr[i][2] & 0x40) << 1;
+ instr[i][3] ^= (instr[i][3] & 0x40) << 1;
+ instr[i][11] >>= 4; // make unsigned
+ }
+
+ delete [] org;
+ rewind(0);
+ return true;
+
+ err:
+ delete [] cmp;
+ delete [] org;
+ return false;
+}
diff --git a/plugins/adplug/adplug/mtk.h b/plugins/adplug/adplug/mtk.h
new file mode 100644
index 00000000..175576a2
--- /dev/null
+++ b/plugins/adplug/adplug/mtk.h
@@ -0,0 +1,50 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * mtk.h - MPU-401 Trakker Loader by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "hsc.h"
+
+class CmtkLoader: public ChscPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ CmtkLoader(Copl *newopl)
+ : ChscPlayer(newopl)
+ {
+ mtkmode = 1;
+ };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+
+ std::string gettype()
+ { return std::string("MPU-401 Trakker"); };
+ std::string gettitle()
+ { return std::string(title); };
+ std::string getauthor()
+ { return std::string(composer); };
+ unsigned int getinstruments()
+ { return 128; };
+ std::string getinstrument(unsigned int n)
+ { return std::string(instname[n]); };
+
+ private:
+ char title[34],composer[34],instname[0x80][34];
+};
diff --git a/plugins/adplug/adplug/opl.h b/plugins/adplug/adplug/opl.h
new file mode 100644
index 00000000..401bcb98
--- /dev/null
+++ b/plugins/adplug/adplug/opl.h
@@ -0,0 +1,69 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * opl.h - OPL base class, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_OPL
+#define H_ADPLUG_OPL
+
+class Copl
+{
+ public:
+ typedef enum {
+ TYPE_OPL2, TYPE_OPL3, TYPE_DUAL_OPL2
+ } ChipType;
+
+ Copl()
+ : currChip(0), currType(TYPE_OPL2)
+ {
+ }
+
+ virtual ~Copl()
+ {
+ }
+
+ virtual void write(int reg, int val) = 0; // combined register select + data write
+ virtual void setchip(int n) // select OPL chip
+ {
+ if(n < 2)
+ currChip = n;
+ }
+
+ virtual int getchip() // returns current OPL chip
+ {
+ return currChip;
+ }
+
+ virtual void init(void) = 0; // reinitialize OPL chip(s)
+
+ // return this OPL chip's type
+ ChipType gettype()
+ {
+ return currType;
+ }
+
+ // Emulation only: fill buffer
+ virtual void update(short *buf, int samples) {}
+
+ protected:
+ int currChip; // currently selected OPL chip number
+ ChipType currType; // this OPL chip's type
+};
+
+#endif
diff --git a/plugins/adplug/adplug/player.cpp b/plugins/adplug/adplug/player.cpp
new file mode 100644
index 00000000..9a0061af
--- /dev/null
+++ b/plugins/adplug/adplug/player.cpp
@@ -0,0 +1,70 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * player.cpp - Replayer base class, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "player.h"
+#include "adplug.h"
+#include "silentopl.h"
+
+/***** CPlayer *****/
+
+const unsigned short CPlayer::note_table[12] =
+ {363, 385, 408, 432, 458, 485, 514, 544, 577, 611, 647, 686};
+
+const unsigned char CPlayer::op_table[9] =
+ {0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12};
+
+CPlayer::CPlayer(Copl *newopl)
+ : opl(newopl), db(CAdPlug::database)
+{
+}
+
+CPlayer::~CPlayer()
+{
+}
+
+unsigned long CPlayer::songlength(int subsong)
+{
+ CSilentopl tempopl;
+ Copl *saveopl = opl;
+ float slength = 0.0f;
+
+ // save original OPL from being overwritten
+ opl = &tempopl;
+
+ // get song length
+ rewind(subsong);
+ while(update() && slength < 600000) // song length limit: 10 minutes
+ slength += 1000.0f / getrefresh();
+ rewind(subsong);
+
+ // restore original OPL and return
+ opl = saveopl;
+ return (unsigned long)slength;
+}
+
+void CPlayer::seek(unsigned long ms)
+{
+ float pos = 0.0f;
+
+ rewind();
+ while(pos < ms && update()) // seek to new position
+ pos += 1000/getrefresh();
+}
diff --git a/plugins/adplug/adplug/player.h b/plugins/adplug/adplug/player.h
new file mode 100644
index 00000000..6b99c34b
--- /dev/null
+++ b/plugins/adplug/adplug/player.h
@@ -0,0 +1,83 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * player.h - Replayer base class, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_PLAYER
+#define H_ADPLUG_PLAYER
+
+#include <string>
+
+#include "fprovide.h"
+#include "opl.h"
+#include "database.h"
+
+class CPlayer
+{
+public:
+ CPlayer(Copl *newopl);
+ virtual ~CPlayer();
+
+/***** Operational methods *****/
+ void seek(unsigned long ms);
+
+ virtual bool load(const std::string &fn, // loads file
+ const CFileProvider &fp = CProvider_Filesystem()) = 0;
+ virtual bool update() = 0; // executes replay code for 1 tick
+ virtual void rewind(int subsong = -1) = 0; // rewinds to specified subsong
+ virtual float getrefresh() = 0; // returns needed timer refresh rate
+
+/***** Informational methods *****/
+ unsigned long songlength(int subsong = -1);
+
+ virtual std::string gettype() = 0; // returns file type
+ virtual std::string gettitle() // returns song title
+ { return std::string(); }
+ virtual std::string getauthor() // returns song author name
+ { return std::string(); }
+ virtual std::string getdesc() // returns song description
+ { return std::string(); }
+ virtual unsigned int getpatterns() // returns number of patterns
+ { return 0; }
+ virtual unsigned int getpattern() // returns currently playing pattern
+ { return 0; }
+ virtual unsigned int getorders() // returns size of orderlist
+ { return 0; }
+ virtual unsigned int getorder() // returns currently playing song position
+ { return 0; }
+ virtual unsigned int getrow() // returns currently playing row
+ { return 0; }
+ virtual unsigned int getspeed() // returns current song speed
+ { return 0; }
+ virtual unsigned int getsubsongs() // returns number of subsongs
+ { return 1; }
+ virtual unsigned int getinstruments() // returns number of instruments
+ { return 0; }
+ virtual std::string getinstrument(unsigned int n) // returns n-th instrument name
+ { return std::string(); }
+
+protected:
+ Copl *opl; // our OPL chip
+ CAdPlugDatabase *db; // AdPlug Database
+
+ static const unsigned short note_table[12]; // standard adlib note table
+ static const unsigned char op_table[9]; // the 9 operators as expected by the OPL
+};
+
+#endif
diff --git a/plugins/adplug/adplug/players.cpp b/plugins/adplug/adplug/players.cpp
new file mode 100644
index 00000000..09c32b73
--- /dev/null
+++ b/plugins/adplug/adplug/players.cpp
@@ -0,0 +1,105 @@
+/*
+ * AdPlug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * players.h - Players enumeration, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "players.h"
+
+/***** CPlayerDesc *****/
+
+CPlayerDesc::CPlayerDesc()
+ : factory(0), extensions(0), extlength(0)
+{
+}
+
+CPlayerDesc::CPlayerDesc(const CPlayerDesc &pd)
+ : factory(pd.factory), filetype(pd.filetype), extlength(pd.extlength)
+{
+ if(pd.extensions) {
+ extensions = (char *)malloc(extlength);
+ memcpy(extensions, pd.extensions, extlength);
+ } else
+ extensions = 0;
+}
+
+CPlayerDesc::CPlayerDesc(Factory f, const std::string &type, const char *ext)
+ : factory(f), filetype(type), extensions(0)
+{
+ const char *i = ext;
+
+ // Determine length of passed extensions list
+ while(*i) i += strlen(i) + 1;
+ extlength = i - ext + 1; // length = difference between last and first char + 1
+
+ extensions = (char *)malloc(extlength);
+ memcpy(extensions, ext, extlength);
+}
+
+CPlayerDesc::~CPlayerDesc()
+{
+ if(extensions) free(extensions);
+}
+
+void CPlayerDesc::add_extension(const char *ext)
+{
+ unsigned long newlength = extlength + strlen(ext) + 1;
+
+ extensions = (char *)realloc(extensions, newlength);
+ strcpy(extensions + extlength - 1, ext);
+ extensions[newlength - 1] = '\0';
+ extlength = newlength;
+}
+
+const char *CPlayerDesc::get_extension(unsigned int n) const
+{
+ const char *i = extensions;
+ unsigned int j;
+
+ for(j = 0; j < n && (*i); j++, i += strlen(i) + 1) ;
+ return (*i != '\0' ? i : 0);
+}
+
+/***** CPlayers *****/
+
+const CPlayerDesc *CPlayers::lookup_filetype(const std::string &ftype) const
+{
+ const_iterator i;
+
+ for(i = begin(); i != end(); i++)
+ if((*i)->filetype == ftype)
+ return *i;
+
+ return 0;
+}
+
+const CPlayerDesc *CPlayers::lookup_extension(const std::string &extension) const
+{
+ const_iterator i;
+ unsigned int j;
+
+ for(i = begin(); i != end(); i++)
+ for(j = 0; (*i)->get_extension(j); j++)
+ if(!stricmp(extension.c_str(), (*i)->get_extension(j)))
+ return *i;
+
+ return 0;
+}
diff --git a/plugins/adplug/adplug/players.h b/plugins/adplug/adplug/players.h
new file mode 100644
index 00000000..9d686aef
--- /dev/null
+++ b/plugins/adplug/adplug/players.h
@@ -0,0 +1,60 @@
+/*
+ * AdPlug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * players.h - Players enumeration, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_PLAYERS
+#define H_ADPLUG_PLAYERS
+
+#include <string>
+#include <list>
+
+#include "opl.h"
+#include "player.h"
+
+class CPlayerDesc
+{
+public:
+ typedef CPlayer *(*Factory)(Copl *);
+
+ Factory factory;
+ std::string filetype;
+
+ CPlayerDesc();
+ CPlayerDesc(const CPlayerDesc &pd);
+ CPlayerDesc(Factory f, const std::string &type, const char *ext);
+
+ ~CPlayerDesc();
+
+ void add_extension(const char *ext);
+ const char *get_extension(unsigned int n) const;
+
+private:
+ char *extensions;
+ unsigned long extlength;
+};
+
+class CPlayers: public std::list<const CPlayerDesc *>
+{
+public:
+ const CPlayerDesc *lookup_filetype(const std::string &ftype) const;
+ const CPlayerDesc *lookup_extension(const std::string &extension) const;
+};
+
+#endif
diff --git a/plugins/adplug/adplug/protrack.cpp b/plugins/adplug/adplug/protrack.cpp
new file mode 100644
index 00000000..86716ef8
--- /dev/null
+++ b/plugins/adplug/adplug/protrack.cpp
@@ -0,0 +1,820 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * protrack.cpp - Generic Protracker Player
+ *
+ * NOTES:
+ * This is a generic Protracker-based formats player. It offers all Protracker
+ * features, plus a good set of extensions to be compatible to other Protracker
+ * derivatives. It is derived from the former SA2 player. If you got a
+ * Protracker-like format, this is most certainly the player you want to use.
+ */
+
+#include <string.h>
+#include "protrack.h"
+#include "debug.h"
+
+#define SPECIALARPLEN 256 // Standard length of special arpeggio lists
+#define JUMPMARKER 0x80 // Orderlist jump marker
+
+// SA2 compatible adlib note table
+const unsigned short CmodPlayer::sa2_notetable[12] =
+ {340,363,385,408,432,458,485,514,544,577,611,647};
+
+// SA2 compatible vibrato rate table
+const unsigned char CmodPlayer::vibratotab[32] =
+ {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1};
+
+/*** public methods *************************************/
+
+CmodPlayer::CmodPlayer(Copl *newopl)
+ : CPlayer(newopl), inst(0), order(0), arplist(0), arpcmd(0), initspeed(6),
+ nop(0), activechan(0xffffffff), flags(Standard), curchip(opl->getchip()),
+ nrows(0), npats(0), nchans(0)
+{
+ realloc_order(128);
+ realloc_patterns(64, 64, 9);
+ realloc_instruments(250);
+ init_notetable(sa2_notetable);
+}
+
+CmodPlayer::~CmodPlayer()
+{
+ dealloc();
+}
+
+bool CmodPlayer::update()
+{
+ unsigned char pattbreak=0, donote, pattnr, chan, oplchan, info1,
+ info2, info, pattern_delay;
+ unsigned short track;
+ unsigned long row;
+
+ if(!speed) // song full stop
+ return !songend;
+
+ // effect handling (timer dependant)
+ for(chan = 0; chan < nchans; chan++) {
+ oplchan = set_opl_chip(chan);
+
+ if(arplist && arpcmd && inst[channel[chan].inst].arpstart) // special arpeggio
+ if(channel[chan].arpspdcnt)
+ channel[chan].arpspdcnt--;
+ else
+ if(arpcmd[channel[chan].arppos] != 255) {
+ switch(arpcmd[channel[chan].arppos]) {
+ case 252: channel[chan].vol1 = arplist[channel[chan].arppos]; // set volume
+ if(channel[chan].vol1 > 63) // ?????
+ channel[chan].vol1 = 63;
+ channel[chan].vol2 = channel[chan].vol1;
+ setvolume(chan);
+ break;
+ case 253: channel[chan].key = 0; setfreq(chan); break; // release sustaining note
+ case 254: channel[chan].arppos = arplist[channel[chan].arppos]; break; // arpeggio loop
+ default: if(arpcmd[channel[chan].arppos]) {
+ if(arpcmd[channel[chan].arppos] / 10)
+ opl->write(0xe3 + op_table[oplchan], arpcmd[channel[chan].arppos] / 10 - 1);
+ if(arpcmd[channel[chan].arppos] % 10)
+ opl->write(0xe0 + op_table[oplchan], (arpcmd[channel[chan].arppos] % 10) - 1);
+ if(arpcmd[channel[chan].arppos] < 10) // ?????
+ opl->write(0xe0 + op_table[oplchan], arpcmd[channel[chan].arppos] - 1);
+ }
+ }
+ if(arpcmd[channel[chan].arppos] != 252) {
+ if(arplist[channel[chan].arppos] <= 96)
+ setnote(chan,channel[chan].note + arplist[channel[chan].arppos]);
+ if(arplist[channel[chan].arppos] >= 100)
+ setnote(chan,arplist[channel[chan].arppos] - 100);
+ } else
+ setnote(chan,channel[chan].note);
+ setfreq(chan);
+ if(arpcmd[channel[chan].arppos] != 255)
+ channel[chan].arppos++;
+ channel[chan].arpspdcnt = inst[channel[chan].inst].arpspeed - 1;
+ }
+
+ info1 = channel[chan].info1;
+ info2 = channel[chan].info2;
+ if(flags & Decimal)
+ info = channel[chan].info1 * 10 + channel[chan].info2;
+ else
+ info = (channel[chan].info1 << 4) + channel[chan].info2;
+ switch(channel[chan].fx) {
+ case 0: if(info) { // arpeggio
+ if(channel[chan].trigger < 2)
+ channel[chan].trigger++;
+ else
+ channel[chan].trigger = 0;
+ switch(channel[chan].trigger) {
+ case 0: setnote(chan,channel[chan].note); break;
+ case 1: setnote(chan,channel[chan].note + info1); break;
+ case 2: setnote(chan,channel[chan].note + info2);
+ }
+ setfreq(chan);
+ }
+ break;
+ case 1: slide_up(chan,info); setfreq(chan); break; // slide up
+ case 2: slide_down(chan,info); setfreq(chan); break; // slide down
+ case 3: tone_portamento(chan,channel[chan].portainfo); break; // tone portamento
+ case 4: vibrato(chan,channel[chan].vibinfo1,channel[chan].vibinfo2); break; // vibrato
+ case 5: // tone portamento & volume slide
+ case 6: if(channel[chan].fx == 5) // vibrato & volume slide
+ tone_portamento(chan,channel[chan].portainfo);
+ else
+ vibrato(chan,channel[chan].vibinfo1,channel[chan].vibinfo2);
+ case 10: if(del % 4) // SA2 volume slide
+ break;
+ if(info1)
+ vol_up(chan,info1);
+ else
+ vol_down(chan,info2);
+ setvolume(chan);
+ break;
+ case 14: if(info1 == 3) // retrig note
+ if(!(del % (info2+1)))
+ playnote(chan);
+ break;
+ case 16: if(del % 4) // AMD volume slide
+ break;
+ if(info1)
+ vol_up_alt(chan,info1);
+ else
+ vol_down_alt(chan,info2);
+ setvolume(chan);
+ break;
+ case 20: // RAD volume slide
+ if(info < 50)
+ vol_down_alt(chan,info);
+ else
+ vol_up_alt(chan,info - 50);
+ setvolume(chan);
+ break;
+ case 26: // volume slide
+ if(info1)
+ vol_up(chan,info1);
+ else
+ vol_down(chan,info2);
+ setvolume(chan);
+ break;
+ case 28:
+ if (info1) {
+ slide_up(chan,1); channel[chan].info1--;
+ }
+ if (info2) {
+ slide_down(chan,1); channel[chan].info2--;
+ }
+ setfreq(chan);
+ break;
+ }
+ }
+
+ if(del) { // speed compensation
+ del--;
+ return !songend;
+ }
+
+ // arrangement handling
+ if(!resolve_order()) return !songend;
+ pattnr = order[ord];
+
+ if(!rw) AdPlug_LogWrite("\nCmodPlayer::update(): Pattern: %d, Order: %d\n", pattnr, ord);
+ AdPlug_LogWrite("CmodPlayer::update():%3d|", rw);
+
+ // play row
+ pattern_delay = 0;
+ row = rw;
+ for(chan = 0; chan < nchans; chan++) {
+ oplchan = set_opl_chip(chan);
+
+ if(!(activechan >> (31 - chan)) & 1) { // channel active?
+ AdPlug_LogWrite("N/A|");
+ continue;
+ }
+ if(!(track = trackord[pattnr][chan])) { // resolve track
+ AdPlug_LogWrite("------------|");
+ continue;
+ } else
+ track--;
+
+ AdPlug_LogWrite("%3d%3d%2X%2X%2X|", tracks[track][row].note,
+ tracks[track][row].inst, tracks[track][row].command,
+ tracks[track][row].param1, tracks[track][row].param2);
+
+ donote = 0;
+ if(tracks[track][row].inst) {
+ channel[chan].inst = tracks[track][row].inst - 1;
+ if (!(flags & Faust)) {
+ channel[chan].vol1 = 63 - (inst[channel[chan].inst].data[10] & 63);
+ channel[chan].vol2 = 63 - (inst[channel[chan].inst].data[9] & 63);
+ setvolume(chan);
+ }
+ }
+
+ if(tracks[track][row].note && tracks[track][row].command != 3) { // no tone portamento
+ channel[chan].note = tracks[track][row].note;
+ setnote(chan,tracks[track][row].note);
+ channel[chan].nextfreq = channel[chan].freq;
+ channel[chan].nextoct = channel[chan].oct;
+ channel[chan].arppos = inst[channel[chan].inst].arpstart;
+ channel[chan].arpspdcnt = 0;
+ if(tracks[track][row].note != 127) // handle key off
+ donote = 1;
+ }
+ channel[chan].fx = tracks[track][row].command;
+ channel[chan].info1 = tracks[track][row].param1;
+ channel[chan].info2 = tracks[track][row].param2;
+
+ if(donote)
+ playnote(chan);
+
+ // command handling (row dependant)
+ info1 = channel[chan].info1;
+ info2 = channel[chan].info2;
+ if(flags & Decimal)
+ info = channel[chan].info1 * 10 + channel[chan].info2;
+ else
+ info = (channel[chan].info1 << 4) + channel[chan].info2;
+ switch(channel[chan].fx) {
+ case 3: // tone portamento
+ if(tracks[track][row].note) {
+ if(tracks[track][row].note < 13)
+ channel[chan].nextfreq = notetable[tracks[track][row].note - 1];
+ else
+ if(tracks[track][row].note % 12 > 0)
+ channel[chan].nextfreq = notetable[(tracks[track][row].note % 12) - 1];
+ else
+ channel[chan].nextfreq = notetable[11];
+ channel[chan].nextoct = (tracks[track][row].note - 1) / 12;
+ if(tracks[track][row].note == 127) { // handle key off
+ channel[chan].nextfreq = channel[chan].freq;
+ channel[chan].nextoct = channel[chan].oct;
+ }
+ }
+ if(info) // remember vars
+ channel[chan].portainfo = info;
+ break;
+
+ case 4: // vibrato (remember vars)
+ if(info) {
+ channel[chan].vibinfo1 = info1;
+ channel[chan].vibinfo2 = info2;
+ }
+ break;
+
+ case 7: tempo = info; break; // set tempo
+
+ case 8: channel[chan].key = 0; setfreq(chan); break; // release sustaining note
+
+ case 9: // set carrier/modulator volume
+ if(info1)
+ channel[chan].vol1 = info1 * 7;
+ else
+ channel[chan].vol2 = info2 * 7;
+ setvolume(chan);
+ break;
+
+ case 11: // position jump
+ pattbreak = 1; rw = 0; if(info < ord) songend = 1; ord = info; break;
+
+ case 12: // set volume
+ channel[chan].vol1 = info;
+ channel[chan].vol2 = info;
+ if(channel[chan].vol1 > 63)
+ channel[chan].vol1 = 63;
+ if(channel[chan].vol2 > 63)
+ channel[chan].vol2 = 63;
+ setvolume(chan);
+ break;
+
+ case 13: // pattern break
+ if(!pattbreak) { pattbreak = 1; rw = info; ord++; } break;
+
+ case 14: // extended command
+ switch(info1) {
+ case 0: // define cell-tremolo
+ if(info2)
+ regbd |= 128;
+ else
+ regbd &= 127;
+ opl->write(0xbd,regbd);
+ break;
+
+ case 1: // define cell-vibrato
+ if(info2)
+ regbd |= 64;
+ else
+ regbd &= 191;
+ opl->write(0xbd,regbd);
+ break;
+
+ case 4: // increase volume fine
+ vol_up_alt(chan,info2);
+ setvolume(chan);
+ break;
+
+ case 5: // decrease volume fine
+ vol_down_alt(chan,info2);
+ setvolume(chan);
+ break;
+
+ case 6: // manual slide up
+ slide_up(chan,info2);
+ setfreq(chan);
+ break;
+
+ case 7: // manual slide down
+ slide_down(chan,info2);
+ setfreq(chan);
+ break;
+
+ case 8: // pattern delay (rows)
+ pattern_delay = info2 * speed;
+ break;
+ }
+ break;
+
+ case 15: // SA2 set speed
+ if(info <= 0x1f)
+ speed = info;
+ if(info >= 0x32)
+ tempo = info;
+ if(!info)
+ songend = 1;
+ break;
+
+ case 17: // alternate set volume
+ channel[chan].vol1 = info;
+ if(channel[chan].vol1 > 63)
+ channel[chan].vol1 = 63;
+ if(inst[channel[chan].inst].data[0] & 1) {
+ channel[chan].vol2 = info;
+ if(channel[chan].vol2 > 63)
+ channel[chan].vol2 = 63;
+ }
+
+ setvolume(chan);
+ break;
+
+ case 18: // AMD set speed
+ if(info <= 31 && info > 0)
+ speed = info;
+ if(info > 31 || !info)
+ tempo = info;
+ break;
+
+ case 19: // RAD/A2M set speed
+ speed = (info ? info : info + 1);
+ break;
+
+ case 21: // set modulator volume
+ if(info <= 63)
+ channel[chan].vol2 = info;
+ else
+ channel[chan].vol2 = 63;
+ setvolume(chan);
+ break;
+
+ case 22: // set carrier volume
+ if(info <= 63)
+ channel[chan].vol1 = info;
+ else
+ channel[chan].vol1 = 63;
+ setvolume(chan);
+ break;
+
+ case 23: // fine frequency slide up
+ slide_up(chan,info);
+ setfreq(chan);
+ break;
+
+ case 24: // fine frequency slide down
+ slide_down(chan,info);
+ setfreq(chan);
+ break;
+
+ case 25: // set carrier/modulator waveform
+ if(info1 != 0x0f)
+ opl->write(0xe3 + op_table[oplchan],info1);
+ if(info2 != 0x0f)
+ opl->write(0xe0 + op_table[oplchan],info2);
+ break;
+
+ case 27: // set chip tremolo/vibrato
+ if(info1)
+ regbd |= 128;
+ else
+ regbd &= 127;
+ if(info2)
+ regbd |= 64;
+ else
+ regbd &= 191;
+ opl->write(0xbd,regbd);
+ break;
+
+ case 29: // pattern delay (frames)
+ pattern_delay = info;
+ break;
+ }
+ }
+
+ // speed compensation
+ del = speed - 1 + pattern_delay;
+
+ if(!pattbreak) { // next row (only if no manual advance)
+ rw++;
+ if(rw >= nrows) {
+ rw = 0;
+ ord++;
+ }
+ }
+
+ resolve_order(); // so we can report songend right away
+ AdPlug_LogWrite("\n");
+ return !songend;
+}
+
+unsigned char CmodPlayer::set_opl_chip(unsigned char chan)
+ /*
+ * Sets OPL chip according to channel number. Channels 0-8 are on first chip,
+ * channels 9-17 are on second chip. Returns corresponding OPL channel
+ * number.
+ */
+{
+ int newchip = chan < 9 ? 0 : 1;
+
+ if(newchip != curchip) {
+ opl->setchip(newchip);
+ curchip = newchip;
+ }
+
+ return chan % 9;
+}
+
+bool CmodPlayer::resolve_order()
+ /*
+ * Resolves current orderlist entry, checking for jumps and loops.
+ *
+ * Returns true on correct processing, false if immediate recursive loop
+ * has been detected.
+ */
+{
+ if(ord < length) {
+ while(order[ord] >= JUMPMARKER) { // jump to order
+ unsigned long neword = order[ord] - JUMPMARKER;
+
+ if(neword <= ord) songend = 1;
+ if(neword == ord) return false;
+ ord = neword;
+ }
+ } else {
+ songend = 1;
+ ord = restartpos;
+ }
+
+ return true;
+}
+
+void CmodPlayer::rewind(int subsong)
+{
+ unsigned long i;
+
+ // Reset playing variables
+ songend = del = ord = rw = regbd = 0;
+ tempo = bpm; speed = initspeed;
+
+ // Reset channel data
+ memset(channel,0,sizeof(Channel)*nchans);
+
+ // Compute number of patterns, if needed
+ if(!nop)
+ for(i=0;i<length;i++)
+ nop = (order[i] > nop ? order[i] : nop);
+
+ opl->init(); // Reset OPL chip
+ opl->write(1, 32); // Go to ym3812 mode
+
+ // Enable OPL3 extensions if flagged
+ if(flags & Opl3) {
+ opl->setchip(1);
+ opl->write(1, 32);
+ opl->write(5, 1);
+ opl->setchip(0);
+ }
+
+ // Enable tremolo/vibrato depth if flagged
+ if(flags & Tremolo) regbd |= 128;
+ if(flags & Vibrato) regbd |= 64;
+ if(regbd) opl->write(0xbd, regbd);
+}
+
+float CmodPlayer::getrefresh()
+{
+ return (float) (tempo / 2.5);
+}
+
+void CmodPlayer::init_trackord()
+{
+ unsigned long i;
+
+ for(i=0;i<npats*nchans;i++)
+ trackord[i / nchans][i % nchans] = i + 1;
+}
+
+bool CmodPlayer::init_specialarp()
+{
+ arplist = new unsigned char[SPECIALARPLEN];
+ arpcmd = new unsigned char[SPECIALARPLEN];
+
+ return true;
+}
+
+void CmodPlayer::init_notetable(const unsigned short *newnotetable)
+{
+ memcpy(notetable, newnotetable, 12 * 2);
+}
+
+bool CmodPlayer::realloc_order(unsigned long len)
+{
+ if(order) delete [] order;
+ order = new unsigned char[len];
+ return true;
+}
+
+bool CmodPlayer::realloc_patterns(unsigned long pats, unsigned long rows, unsigned long chans)
+{
+ unsigned long i;
+
+ dealloc_patterns();
+
+ // set new number of tracks, rows and channels
+ npats = pats; nrows = rows; nchans = chans;
+
+ // alloc new patterns
+ tracks = new Tracks *[pats * chans];
+ for(i=0;i<pats*chans;i++) tracks[i] = new Tracks[rows];
+ trackord = new unsigned short *[pats];
+ for(i=0;i<pats;i++) trackord[i] = new unsigned short[chans];
+ channel = new Channel[chans];
+
+ // initialize new patterns
+ for(i=0;i<pats*chans;i++) memset(tracks[i],0,sizeof(Tracks)*rows);
+ for(i=0;i<pats;i++) memset(trackord[i],0,chans*2);
+
+ return true;
+}
+
+void CmodPlayer::dealloc_patterns()
+{
+ unsigned long i;
+
+ // dealloc everything previously allocated
+ if(npats && nrows && nchans) {
+ for(i=0;i<npats*nchans;i++) delete [] tracks[i];
+ delete [] tracks;
+ for(i=0;i<npats;i++) delete [] trackord[i];
+ delete [] trackord;
+ delete [] channel;
+ }
+}
+
+bool CmodPlayer::realloc_instruments(unsigned long len)
+{
+ // dealloc previous instance, if any
+ if(inst) delete [] inst;
+
+ inst = new Instrument[len];
+ memset(inst,0,sizeof(Instrument)*len); // reset instruments
+ return true;
+}
+
+void CmodPlayer::dealloc()
+{
+ if(inst) delete [] inst;
+ if(order) delete [] order;
+ if(arplist) delete [] arplist;
+ if(arpcmd) delete [] arpcmd;
+ dealloc_patterns();
+}
+
+/*** private methods *************************************/
+
+void CmodPlayer::setvolume(unsigned char chan)
+{
+ unsigned char oplchan = set_opl_chip(chan);
+
+ if(flags & Faust)
+ setvolume_alt(chan);
+ else {
+ opl->write(0x40 + op_table[oplchan], 63-channel[chan].vol2 + (inst[channel[chan].inst].data[9] & 192));
+ opl->write(0x43 + op_table[oplchan], 63-channel[chan].vol1 + (inst[channel[chan].inst].data[10] & 192));
+ }
+}
+
+void CmodPlayer::setvolume_alt(unsigned char chan)
+{
+ unsigned char oplchan = set_opl_chip(chan);
+ unsigned char ivol2 = inst[channel[chan].inst].data[9] & 63;
+ unsigned char ivol1 = inst[channel[chan].inst].data[10] & 63;
+
+ opl->write(0x40 + op_table[oplchan], (((63 - channel[chan].vol2 & 63) + ivol2) >> 1) + (inst[channel[chan].inst].data[9] & 192));
+ opl->write(0x43 + op_table[oplchan], (((63 - channel[chan].vol1 & 63) + ivol1) >> 1) + (inst[channel[chan].inst].data[10] & 192));
+}
+
+void CmodPlayer::setfreq(unsigned char chan)
+{
+ unsigned char oplchan = set_opl_chip(chan);
+
+ opl->write(0xa0 + oplchan, channel[chan].freq & 255);
+ if(channel[chan].key)
+ opl->write(0xb0 + oplchan, ((channel[chan].freq & 768) >> 8) + (channel[chan].oct << 2) | 32);
+ else
+ opl->write(0xb0 + oplchan, ((channel[chan].freq & 768) >> 8) + (channel[chan].oct << 2));
+}
+
+void CmodPlayer::playnote(unsigned char chan)
+{
+ unsigned char oplchan = set_opl_chip(chan);
+ unsigned char op = op_table[oplchan], insnr = channel[chan].inst;
+
+ if(!(flags & NoKeyOn))
+ opl->write(0xb0 + oplchan, 0); // stop old note
+
+ // set instrument data
+ opl->write(0x20 + op, inst[insnr].data[1]);
+ opl->write(0x23 + op, inst[insnr].data[2]);
+ opl->write(0x60 + op, inst[insnr].data[3]);
+ opl->write(0x63 + op, inst[insnr].data[4]);
+ opl->write(0x80 + op, inst[insnr].data[5]);
+ opl->write(0x83 + op, inst[insnr].data[6]);
+ opl->write(0xe0 + op, inst[insnr].data[7]);
+ opl->write(0xe3 + op, inst[insnr].data[8]);
+ opl->write(0xc0 + oplchan, inst[insnr].data[0]);
+ opl->write(0xbd, inst[insnr].misc); // set misc. register
+
+ // set frequency, volume & play
+ channel[chan].key = 1;
+ setfreq(chan);
+
+ if (flags & Faust) {
+ channel[chan].vol2 = 63;
+ channel[chan].vol1 = 63;
+ }
+ setvolume(chan);
+}
+
+void CmodPlayer::setnote(unsigned char chan, int note)
+{
+ if(note > 96)
+ if(note == 127) { // key off
+ channel[chan].key = 0;
+ setfreq(chan);
+ return;
+ } else
+ note = 96;
+
+ if(note < 13)
+ channel[chan].freq = notetable[note - 1];
+ else
+ if(note % 12 > 0)
+ channel[chan].freq = notetable[(note % 12) - 1];
+ else
+ channel[chan].freq = notetable[11];
+ channel[chan].oct = (note - 1) / 12;
+ channel[chan].freq += inst[channel[chan].inst].slide; // apply pre-slide
+}
+
+void CmodPlayer::slide_down(unsigned char chan, int amount)
+{
+ channel[chan].freq -= amount;
+ if(channel[chan].freq <= 342)
+ if(channel[chan].oct) {
+ channel[chan].oct--;
+ channel[chan].freq <<= 1;
+ } else
+ channel[chan].freq = 342;
+}
+
+void CmodPlayer::slide_up(unsigned char chan, int amount)
+{
+ channel[chan].freq += amount;
+ if(channel[chan].freq >= 686)
+ if(channel[chan].oct < 7) {
+ channel[chan].oct++;
+ channel[chan].freq >>= 1;
+ } else
+ channel[chan].freq = 686;
+}
+
+void CmodPlayer::tone_portamento(unsigned char chan, unsigned char info)
+{
+ if(channel[chan].freq + (channel[chan].oct << 10) < channel[chan].nextfreq +
+ (channel[chan].nextoct << 10)) {
+ slide_up(chan,info);
+ if(channel[chan].freq + (channel[chan].oct << 10) > channel[chan].nextfreq +
+ (channel[chan].nextoct << 10)) {
+ channel[chan].freq = channel[chan].nextfreq;
+ channel[chan].oct = channel[chan].nextoct;
+ }
+ }
+ if(channel[chan].freq + (channel[chan].oct << 10) > channel[chan].nextfreq +
+ (channel[chan].nextoct << 10)) {
+ slide_down(chan,info);
+ if(channel[chan].freq + (channel[chan].oct << 10) < channel[chan].nextfreq +
+ (channel[chan].nextoct << 10)) {
+ channel[chan].freq = channel[chan].nextfreq;
+ channel[chan].oct = channel[chan].nextoct;
+ }
+ }
+ setfreq(chan);
+}
+
+void CmodPlayer::vibrato(unsigned char chan, unsigned char speed, unsigned char depth)
+{
+ int i;
+
+ if(!speed || !depth)
+ return;
+
+ if(depth > 14)
+ depth = 14;
+
+ for(i=0;i<speed;i++) {
+ channel[chan].trigger++;
+ while(channel[chan].trigger >= 64)
+ channel[chan].trigger -= 64;
+ if(channel[chan].trigger >= 16 && channel[chan].trigger < 48)
+ slide_down(chan,vibratotab[channel[chan].trigger - 16] / (16-depth));
+ if(channel[chan].trigger < 16)
+ slide_up(chan,vibratotab[channel[chan].trigger + 16] / (16-depth));
+ if(channel[chan].trigger >= 48)
+ slide_up(chan,vibratotab[channel[chan].trigger - 48] / (16-depth));
+ }
+ setfreq(chan);
+}
+
+void CmodPlayer::vol_up(unsigned char chan, int amount)
+{
+ if(channel[chan].vol1 + amount < 63)
+ channel[chan].vol1 += amount;
+ else
+ channel[chan].vol1 = 63;
+
+ if(channel[chan].vol2 + amount < 63)
+ channel[chan].vol2 += amount;
+ else
+ channel[chan].vol2 = 63;
+}
+
+void CmodPlayer::vol_down(unsigned char chan, int amount)
+{
+ if(channel[chan].vol1 - amount > 0)
+ channel[chan].vol1 -= amount;
+ else
+ channel[chan].vol1 = 0;
+
+ if(channel[chan].vol2 - amount > 0)
+ channel[chan].vol2 -= amount;
+ else
+ channel[chan].vol2 = 0;
+}
+
+void CmodPlayer::vol_up_alt(unsigned char chan, int amount)
+{
+ if(channel[chan].vol1 + amount < 63)
+ channel[chan].vol1 += amount;
+ else
+ channel[chan].vol1 = 63;
+ if(inst[channel[chan].inst].data[0] & 1)
+ if(channel[chan].vol2 + amount < 63)
+ channel[chan].vol2 += amount;
+ else
+ channel[chan].vol2 = 63;
+}
+
+void CmodPlayer::vol_down_alt(unsigned char chan, int amount)
+{
+ if(channel[chan].vol1 - amount > 0)
+ channel[chan].vol1 -= amount;
+ else
+ channel[chan].vol1 = 0;
+ if(inst[channel[chan].inst].data[0] & 1)
+ if(channel[chan].vol2 - amount > 0)
+ channel[chan].vol2 -= amount;
+ else
+ channel[chan].vol2 = 0;
+}
diff --git a/plugins/adplug/adplug/protrack.h b/plugins/adplug/adplug/protrack.h
new file mode 100644
index 00000000..8da9489e
--- /dev/null
+++ b/plugins/adplug/adplug/protrack.h
@@ -0,0 +1,119 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * protrack.h - Generic Protracker Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_PROTRACK
+#define H_PROTRACK
+
+#include "player.h"
+
+class CmodPlayer: public CPlayer
+{
+public:
+ CmodPlayer(Copl *newopl);
+ virtual ~CmodPlayer();
+
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ unsigned int getpatterns()
+ { return nop; }
+ unsigned int getpattern()
+ { return order[ord]; }
+ unsigned int getorders()
+ { return length; }
+ unsigned int getorder()
+ { return ord; }
+ unsigned int getrow()
+ { return rw; }
+ unsigned int getspeed()
+ { return speed; }
+
+ protected:
+ enum Flags {
+ Standard = 0,
+ Decimal = 1 << 0,
+ Faust = 1 << 1,
+ NoKeyOn = 1 << 2,
+ Opl3 = 1 << 3,
+ Tremolo = 1 << 4,
+ Vibrato = 1 << 5,
+ Percussion = 1 << 6
+ };
+
+ struct Instrument {
+ unsigned char data[11],arpstart,arpspeed,arppos,arpspdcnt,misc;
+ signed char slide;
+ } *inst;
+
+ struct Tracks {
+ unsigned char note,command,inst,param2,param1;
+ } **tracks;
+
+ unsigned char *order, *arplist, *arpcmd, initspeed;
+ unsigned short tempo, **trackord, bpm, nop;
+ unsigned long length, restartpos, activechan;
+ int flags, curchip;
+
+ struct Channel {
+ unsigned short freq,nextfreq;
+ unsigned char oct,vol1,vol2,inst,fx,info1,info2,key,nextoct,
+ note,portainfo,vibinfo1,vibinfo2,arppos,arpspdcnt;
+ signed char trigger;
+ } *channel;
+
+ void init_trackord();
+ bool init_specialarp();
+ void init_notetable(const unsigned short *newnotetable);
+ bool realloc_order(unsigned long len);
+ bool realloc_patterns(unsigned long pats, unsigned long rows, unsigned long chans);
+ bool realloc_instruments(unsigned long len);
+
+ void dealloc();
+
+ private:
+ static const unsigned short sa2_notetable[12];
+ static const unsigned char vibratotab[32];
+
+ unsigned char speed, del, songend, regbd;
+ unsigned short rows, notetable[12];
+ unsigned long rw, ord, nrows, npats, nchans;
+
+ void setvolume(unsigned char chan);
+ void setvolume_alt(unsigned char chan);
+ void setfreq(unsigned char chan);
+ void playnote(unsigned char chan);
+ void setnote(unsigned char chan, int note);
+ void slide_down(unsigned char chan, int amount);
+ void slide_up(unsigned char chan, int amount);
+ void tone_portamento(unsigned char chan, unsigned char info);
+ void vibrato(unsigned char chan, unsigned char speed, unsigned char depth);
+ void vol_up(unsigned char chan, int amount);
+ void vol_down(unsigned char chan, int amount);
+ void vol_up_alt(unsigned char chan, int amount);
+ void vol_down_alt(unsigned char chan, int amount);
+
+ void dealloc_patterns();
+ bool resolve_order();
+ unsigned char set_opl_chip(unsigned char chan);
+};
+
+#endif
diff --git a/plugins/adplug/adplug/psi.cpp b/plugins/adplug/adplug/psi.cpp
new file mode 100644
index 00000000..00184526
--- /dev/null
+++ b/plugins/adplug/adplug/psi.cpp
@@ -0,0 +1,177 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * [xad] PSI player, by Riven the Mage <riven@ok.ru>
+ */
+
+/*
+ - discovery -
+
+ file(s) : 4BIDDEN.COM, PGRID.EXE
+ type : Forbidden Dreams BBStro
+ Power Grid BBStro
+ tune : by Friar Tuck [Shadow Faction/ICE]
+ player : by Psi [Future Crew]
+ comment : seems to me what 4bidden tune & player was ripped from pgrid
+
+ file(s) : MYSTRUNE.COM
+ type : Mystical Runes BBStro
+ tune : by ?
+ player : by Psi [Future Crew]
+*/
+
+#include "psi.h"
+#include "debug.h"
+
+const unsigned char CxadpsiPlayer::psi_adlib_registers[99] =
+{
+ 0x20, 0x23, 0x40, 0x43, 0x60, 0x63, 0x80, 0x83, 0xE0, 0xE3, 0xC0,
+ 0x21, 0x24, 0x41, 0x44, 0x61, 0x64, 0x81, 0x84, 0xE1, 0xE4, 0xC1,
+ 0x22, 0x25, 0x42, 0x45, 0x62, 0x65, 0x82, 0x85, 0xE2, 0xE5, 0xC2,
+ 0x28, 0x2B, 0x48, 0x4B, 0x68, 0x6B, 0x88, 0x8B, 0xE8, 0xEB, 0xC3,
+ 0x29, 0x2C, 0x49, 0x4C, 0x69, 0x6C, 0x89, 0x8C, 0xE9, 0xEC, 0xC4,
+ 0x2A, 0x2D, 0x4A, 0x4D, 0x6A, 0x6D, 0x8A, 0x8D, 0xEA, 0xED, 0xC5,
+ 0x30, 0x33, 0x50, 0x53, 0x70, 0x73, 0x90, 0x93, 0xF0, 0xF3, 0xC6,
+ 0x31, 0x34, 0x51, 0x54, 0x71, 0x74, 0x91, 0x94, 0xF1, 0xF4, 0xC7,
+ 0x32, 0x35, 0x52, 0x55, 0x72, 0x75, 0x92, 0x95, 0xF2, 0xF5, 0xC8
+};
+
+const unsigned short CxadpsiPlayer::psi_notes[16] =
+{
+ 0x216B, 0x2181, 0x2198, 0x21B0, 0x21CA, 0x21E5, 0x2202, 0x2220,
+ 0x2241, 0x2263, 0x2287, 0x2364,
+ 0x0000, 0x0000, 0x0000, 0x0000 // by riven
+};
+
+CPlayer *CxadpsiPlayer::factory(Copl *newopl)
+{
+ return new CxadpsiPlayer(newopl);
+}
+
+void CxadpsiPlayer::xadplayer_rewind(int subsong)
+{
+ opl_write(0x01, 0x20);
+ opl_write(0x08, 0x00);
+ opl_write(0xBD, 0x00);
+
+ // get header
+ header.instr_ptr = (tune[1] << 8) + tune[0];
+ header.seq_ptr = (tune[3] << 8) + tune[2];
+
+ // define instruments
+ psi.instr_table = &tune[header.instr_ptr];
+
+ for(int i=0; i<8; i++)
+ {
+ for(int j=0; j<11; j++) {
+ unsigned short inspos = (psi.instr_table[i * 2 + 1] << 8) + psi.instr_table[i * 2];
+
+ opl_write(psi_adlib_registers[i*11 + j],tune[inspos + j]);
+ }
+
+ opl_write(0xA0+i, 0x00);
+ opl_write(0xB0+i, 0x00);
+
+ psi.note_delay[i] = 1;
+ psi.note_curdelay[i] = 1;
+ psi.looping[i] = 0;
+ }
+
+ // calculate sequence pointer
+ psi.seq_table = &tune[header.seq_ptr];
+}
+
+void CxadpsiPlayer::xadplayer_update()
+{
+ unsigned short ptr;
+
+ for(int i=0; i<8; i++)
+ {
+ ptr = (psi.seq_table[(i<<1) * 2 + 1] << 8) + psi.seq_table[(i<<1) * 2];
+
+ psi.note_curdelay[i]--;
+
+ if (!psi.note_curdelay[i])
+ {
+ opl_write(0xA0+i, 0x00);
+ opl_write(0xB0+i, 0x00);
+
+ unsigned char event = tune[ptr++];
+#ifdef DEBUG
+ AdPlug_LogWrite("channel %02X, event %02X:\n",i+1,event);
+#endif
+
+ // end of sequence ?
+ if (!event)
+ {
+ ptr = (psi.seq_table[(i<<1) * 2 + 3] << 8) + psi.seq_table[(i<<1) * 2 + 2];
+
+ event = tune[ptr++];
+#ifdef DEBUG
+ AdPlug_LogWrite(" channel %02X, event %02X:\n",i+1,event);
+#endif
+
+ // set sequence loop flag
+ psi.looping[i] = 1;
+
+ // module loop ?
+ plr.looping = 1;
+ for(int j=0; j<8; j++)
+ plr.looping &= psi.looping[j];
+ }
+
+ // new note delay ?
+ if (event & 0x80)
+ {
+ psi.note_delay[i] = (event & 0x7F);
+
+ event = tune[ptr++];
+#ifdef DEBUG
+ AdPlug_LogWrite(" channel %02X, event %02X:\n",i+1,event);
+#endif
+ }
+
+ psi.note_curdelay[i] = psi.note_delay[i];
+
+ // play note
+ unsigned short note = psi_notes[event & 0x0F];
+
+ opl_write(0xA0+i, note & 0xFF);
+ opl_write(0xB0+i, (note >> 8) + ((event >> 2) & 0xFC));
+
+ // save position
+ psi.seq_table[(i<<1) * 2] = ptr & 0xff;
+ psi.seq_table[(i<<1) * 2 + 1] = ptr >> 8;
+ }
+ }
+}
+
+float CxadpsiPlayer::xadplayer_getrefresh()
+{
+ return 70.0f;
+}
+
+std::string CxadpsiPlayer::xadplayer_gettype()
+{
+ return std::string("xad: psi player");
+}
+
+unsigned int CxadpsiPlayer::xadplayer_getinstruments()
+{
+ return 8;
+}
diff --git a/plugins/adplug/adplug/psi.h b/plugins/adplug/adplug/psi.h
new file mode 100644
index 00000000..9714ed12
--- /dev/null
+++ b/plugins/adplug/adplug/psi.h
@@ -0,0 +1,64 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * [xad] PSI player, by Riven the Mage <riven@ok.ru>
+ */
+
+#include "xad.h"
+
+class CxadpsiPlayer: public CxadPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CxadpsiPlayer(Copl *newopl): CxadPlayer(newopl)
+ { }
+
+protected:
+ struct psi_header
+ {
+ unsigned short instr_ptr;
+ unsigned short seq_ptr;
+ } header;
+
+ struct
+ {
+ unsigned char *instr_table;
+ unsigned char *seq_table;
+ unsigned char note_delay[9];
+ unsigned char note_curdelay[9];
+ unsigned char looping[9];
+ } psi;
+ //
+ bool xadplayer_load()
+ {
+ if(xad.fmt == PSI)
+ return true;
+ else
+ return false;
+ }
+ void xadplayer_rewind(int subsong);
+ void xadplayer_update();
+ float xadplayer_getrefresh();
+ std::string xadplayer_gettype();
+ unsigned int xadplayer_getinstruments();
+
+private:
+ static const unsigned char psi_adlib_registers[99];
+ static const unsigned short psi_notes[16];
+};
diff --git a/plugins/adplug/adplug/rad.cpp b/plugins/adplug/adplug/rad.cpp
new file mode 100644
index 00000000..0178a6c3
--- /dev/null
+++ b/plugins/adplug/adplug/rad.cpp
@@ -0,0 +1,125 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * rad.cpp - RAD Loader by Simon Peter <dn.tlp@gmx.net>
+ *
+ * BUGS:
+ * some volumes are dropped out
+ */
+
+#include <string.h>
+#include "rad.h"
+
+CPlayer *CradLoader::factory(Copl *newopl)
+{
+ return new CradLoader(newopl);
+}
+
+bool CradLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ char id[16];
+ unsigned char buf,ch,c,b,inp;
+ char bufstr[2] = "\0";
+ unsigned int i,j;
+ unsigned short patofs[32];
+ const unsigned char convfx[16] = {255,1,2,3,255,5,255,255,255,255,20,255,17,0xd,255,19};
+
+ // file validation section
+ f->readString(id, 16); version = f->readInt(1);
+ if(strncmp(id,"RAD by REALiTY!!",16) || version != 0x10)
+ { fp.close(f); return false; }
+
+ // load section
+ radflags = f->readInt(1);
+ if(radflags & 128) { // description
+ memset(desc,0,80*22);
+ while((buf = f->readInt(1)))
+ if(buf == 1)
+ strcat(desc,"\n");
+ else
+ if(buf >= 2 && buf <= 0x1f)
+ for(i=0;i<buf;i++)
+ strcat(desc," ");
+ else {
+ *bufstr = buf;
+ strcat(desc,bufstr);
+ }
+ }
+ while((buf = f->readInt(1))) { // instruments
+ buf--;
+ inst[buf].data[2] = f->readInt(1); inst[buf].data[1] = f->readInt(1);
+ inst[buf].data[10] = f->readInt(1); inst[buf].data[9] = f->readInt(1);
+ inst[buf].data[4] = f->readInt(1); inst[buf].data[3] = f->readInt(1);
+ inst[buf].data[6] = f->readInt(1); inst[buf].data[5] = f->readInt(1);
+ inst[buf].data[0] = f->readInt(1);
+ inst[buf].data[8] = f->readInt(1); inst[buf].data[7] = f->readInt(1);
+ }
+ length = f->readInt(1);
+ for(i = 0; i < length; i++) order[i] = f->readInt(1); // orderlist
+ for(i = 0; i < 32; i++) patofs[i] = f->readInt(2); // pattern offset table
+ init_trackord(); // patterns
+ for(i=0;i<32;i++)
+ if(patofs[i]) {
+ f->seek(patofs[i]);
+ do {
+ buf = f->readInt(1); b = buf & 127;
+ do {
+ ch = f->readInt(1); c = ch & 127;
+ inp = f->readInt(1);
+ tracks[i*9+c][b].note = inp & 127;
+ tracks[i*9+c][b].inst = (inp & 128) >> 3;
+ inp = f->readInt(1);
+ tracks[i*9+c][b].inst += inp >> 4;
+ tracks[i*9+c][b].command = inp & 15;
+ if(inp & 15) {
+ inp = f->readInt(1);
+ tracks[i*9+c][b].param1 = inp / 10;
+ tracks[i*9+c][b].param2 = inp % 10;
+ }
+ } while(!(ch & 128));
+ } while(!(buf & 128));
+ } else
+ memset(trackord[i],0,9*2);
+ fp.close(f);
+
+ // convert replay data
+ for(i=0;i<32*9;i++) // convert patterns
+ for(j=0;j<64;j++) {
+ if(tracks[i][j].note == 15)
+ tracks[i][j].note = 127;
+ if(tracks[i][j].note > 16 && tracks[i][j].note < 127)
+ tracks[i][j].note -= 4 * (tracks[i][j].note >> 4);
+ if(tracks[i][j].note && tracks[i][j].note < 126)
+ tracks[i][j].note++;
+ tracks[i][j].command = convfx[tracks[i][j].command];
+ }
+ restartpos = 0; initspeed = radflags & 31;
+ bpm = radflags & 64 ? 0 : 50; flags = Decimal;
+
+ rewind(0);
+ return true;
+}
+
+float CradLoader::getrefresh()
+{
+ if(tempo)
+ return (float) (tempo);
+ else
+ return 18.2f;
+}
diff --git a/plugins/adplug/adplug/rad.h b/plugins/adplug/adplug/rad.h
new file mode 100644
index 00000000..07814b8a
--- /dev/null
+++ b/plugins/adplug/adplug/rad.h
@@ -0,0 +1,44 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * rad.h - RAD Loader by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "protrack.h"
+
+class CradLoader: public CmodPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CradLoader(Copl *newopl)
+ : CmodPlayer(newopl)
+ { *desc = '\0'; };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ float getrefresh();
+
+ std::string gettype()
+ { return std::string("Reality ADlib Tracker"); };
+ std::string getdesc()
+ { return std::string(desc); };
+
+private:
+ unsigned char version,radflags;
+ char desc[80*22];
+};
diff --git a/plugins/adplug/adplug/rat.cpp b/plugins/adplug/adplug/rat.cpp
new file mode 100644
index 00000000..b20af250
--- /dev/null
+++ b/plugins/adplug/adplug/rat.cpp
@@ -0,0 +1,293 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * [xad] RAT player, by Riven the Mage <riven@ok.ru>
+ */
+
+/*
+ - discovery -
+
+ file(s) : PINA.EXE
+ type : Experimental Connection BBStro tune
+ tune : by (?)Ratt/GRIF
+ player : by (?)Ratt/GRIF
+ comment : there are bug in original replayer's adlib_init(): wrong frequency registers.
+*/
+
+#include <string.h>
+#include "rat.h"
+#include "debug.h"
+
+const unsigned char CxadratPlayer::rat_adlib_bases[18] =
+{
+ 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12,
+ 0x03, 0x04, 0x05, 0x0B, 0x0C, 0x0D, 0x13, 0x14, 0x15
+};
+
+const unsigned short CxadratPlayer::rat_notes[16] =
+{
+ 0x157, 0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5, 0x202, 0x220, 0x241, 0x263, 0x287,
+ 0x000, 0x000, 0x000, 0x000 // by riven
+};
+
+CPlayer *CxadratPlayer::factory(Copl *newopl)
+{
+ return new CxadratPlayer(newopl);
+}
+
+bool CxadratPlayer::xadplayer_load()
+{
+ if(xad.fmt != RAT)
+ return false;
+
+ // load header
+ memcpy(&rat.hdr, &tune[0], sizeof(rat_header));
+
+ // is 'RAT'-signed ?
+ if (strncmp(rat.hdr.id,"RAT",3))
+ return false;
+
+ // is version 1.0 ?
+ if (rat.hdr.version != 0x10)
+ return false;
+
+ // load order
+ rat.order = &tune[0x40];
+
+ // load instruments
+ rat.inst = (rat_instrument *)&tune[0x140];
+
+ // load pattern data
+ unsigned short patseg = (rat.hdr.patseg[1] << 8) + rat.hdr.patseg[0];
+ unsigned char *event_ptr = &tune[patseg << 4];
+
+ for(int i=0;i<rat.hdr.numpat;i++)
+ for(int j=0;j<64;j++)
+ for(int k=0;k<rat.hdr.numchan;k++)
+ {
+ memcpy(&rat.tracks[i][j][k], event_ptr, sizeof(rat_event));
+
+ event_ptr += sizeof(rat_event);
+ }
+
+ return true;
+}
+
+void CxadratPlayer::xadplayer_rewind(int subsong)
+{
+ int i;
+
+ rat.order_pos = rat.hdr.order_start;
+ rat.pattern_pos = 0;
+ rat.volume = rat.hdr.volume;
+
+ plr.speed = rat.hdr.speed;
+
+ // clear channel data
+ memset(&rat.channel, 0, sizeof(rat.channel[0])*9);
+
+ // init OPL
+ opl_write(0x01, 0x20);
+ opl_write(0x08, 0x00);
+ opl_write(0xBD, 0x00);
+
+ // set default frequencies
+ for(i=0;i<9;i++)
+ {
+ opl_write(0xA0+i, 0x00);
+ opl_write(0xA3+i, 0x00);
+ opl_write(0xB0+i, 0x00);
+ opl_write(0xB3+i, 0x00);
+ }
+
+ // set default volumes
+ for(i=0;i<0x1F;i++)
+ opl_write(0x40+i, 0x3F);
+}
+
+void CxadratPlayer::xadplayer_update()
+{
+ int i;
+
+ rat_event event;
+
+ // process events
+ for(i=0;i<rat.hdr.numchan;i++)
+ {
+ memcpy(&event,&rat.tracks[rat.order[rat.order_pos]][rat.pattern_pos][i],sizeof(rat_event));
+#ifdef DEBUG
+ AdPlug_LogWrite("order %02X, pattern %02X, row %02X, channel %02X, event %02X %02X %02X %02X %02X:\n",
+ rat.order_pos, rat.order[rat.order_pos], rat.pattern_pos, i, event.note, event.instrument, event.volume, event.fx, event.fxp
+ );
+#endif
+
+ // is instrument ?
+ if (event.instrument != 0xFF)
+ {
+ rat.channel[i].instrument = event.instrument - 1;
+ rat.channel[i].volume = rat.inst[event.instrument - 1].volume;
+ }
+
+ // is volume ?
+ if (event.volume != 0xFF)
+ rat.channel[i].volume = event.volume;
+
+ // is note ?
+ if (event.note != 0xFF)
+ {
+ // mute channel
+ opl_write(0xB0+i, 0x00);
+ opl_write(0xA0+i, 0x00);
+
+ // if note != 0xFE then play
+ if (event.note != 0xFE)
+ {
+ unsigned char ins = rat.channel[i].instrument;
+
+ // synthesis/feedback
+ opl_write(0xC0+i, rat.inst[ins].connect);
+
+ // controls
+ opl_write(0x20+rat_adlib_bases[i], rat.inst[ins].mod_ctrl);
+ opl_write(0x20+rat_adlib_bases[i+9], rat.inst[ins].car_ctrl);
+
+ // volumes
+ opl_write(0x40+rat_adlib_bases[i], __rat_calc_volume(rat.inst[ins].mod_volume,rat.channel[i].volume,rat.volume));
+ opl_write(0x40+rat_adlib_bases[i+9], __rat_calc_volume(rat.inst[ins].car_volume,rat.channel[i].volume,rat.volume));
+
+ // attack/decay
+ opl_write(0x60+rat_adlib_bases[i], rat.inst[ins].mod_AD);
+ opl_write(0x60+rat_adlib_bases[i+9], rat.inst[ins].car_AD);
+
+ // sustain/release
+ opl_write(0x80+rat_adlib_bases[i], rat.inst[ins].mod_SR);
+ opl_write(0x80+rat_adlib_bases[i+9], rat.inst[ins].car_SR);
+
+ // waveforms
+ opl_write(0xE0+rat_adlib_bases[i], rat.inst[ins].mod_wave);
+ opl_write(0xE0+rat_adlib_bases[i+9], rat.inst[ins].car_wave);
+
+ // octave/frequency
+ unsigned short insfreq = (rat.inst[ins].freq[1] << 8) + rat.inst[ins].freq[0];
+ unsigned short freq = insfreq * rat_notes[event.note & 0x0F] / 0x20AB;
+
+ opl_write(0xA0+i, freq & 0xFF);
+ opl_write(0xB0+i, (freq >> 8) | ((event.note & 0xF0) >> 2) | 0x20);
+ }
+ }
+
+ // is effect ?
+ if (event.fx != 0xFF)
+ {
+ rat.channel[i].fx = event.fx;
+ rat.channel[i].fxp = event.fxp;
+ }
+ }
+
+ // next row
+ rat.pattern_pos++;
+
+ // process effects
+ for(i=0;i<rat.hdr.numchan;i++)
+ {
+ unsigned char old_order_pos = rat.order_pos;
+
+ switch (rat.channel[i].fx)
+ {
+ case 0x01: // 0x01: Set Speed
+ plr.speed = rat.channel[i].fxp;
+ break;
+ case 0x02: // 0x02: Position Jump
+ if (rat.channel[i].fxp < rat.hdr.order_end)
+ rat.order_pos = rat.channel[i].fxp;
+ else
+ rat.order_pos = 0;
+
+ // jumpback ?
+ if (rat.order_pos <= old_order_pos)
+ plr.looping = 1;
+
+ rat.pattern_pos = 0;
+ break;
+ case 0x03: // 0x03: Pattern Break (?)
+ rat.pattern_pos = 0x40;
+ break;
+ }
+
+ rat.channel[i].fx = 0;
+ }
+
+ // end of pattern ?
+ if (rat.pattern_pos >= 0x40)
+ {
+ rat.pattern_pos = 0;
+
+ rat.order_pos++;
+
+ // end of module ?
+ if (rat.order_pos == rat.hdr.order_end)
+ {
+ rat.order_pos = rat.hdr.order_loop;
+
+ plr.looping = 1;
+ }
+ }
+}
+
+float CxadratPlayer::xadplayer_getrefresh()
+{
+ return 60.0f;
+}
+
+std::string CxadratPlayer::xadplayer_gettype()
+{
+ return (std::string("xad: rat player"));
+}
+
+std::string CxadratPlayer::xadplayer_gettitle()
+{
+ return (std::string(rat.hdr.title,32));
+}
+
+unsigned int CxadratPlayer::xadplayer_getinstruments()
+{
+ return rat.hdr.numinst;
+}
+
+/* -------- Internal Functions ---------------------------- */
+
+unsigned char CxadratPlayer::__rat_calc_volume(unsigned char ivol, unsigned char cvol, unsigned char gvol)
+{
+#ifdef DEBUG
+ AdPlug_LogWrite("volumes: instrument %02X, channel %02X, global %02X:\n", ivol, cvol, gvol);
+#endif
+ unsigned short vol;
+
+ vol = ivol;
+ vol &= 0x3F;
+ vol ^= 0x3F;
+ vol *= cvol;
+ vol >>= 6;
+ vol *= gvol;
+ vol >>= 6;
+ vol ^= 0x3F;
+
+ vol |= ivol & 0xC0;
+
+ return vol;
+}
diff --git a/plugins/adplug/adplug/rat.h b/plugins/adplug/adplug/rat.h
new file mode 100644
index 00000000..cc9ddf71
--- /dev/null
+++ b/plugins/adplug/adplug/rat.h
@@ -0,0 +1,121 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * [xad] RAT player, by Riven the Mage <riven@ok.ru>
+ */
+
+#include "xad.h"
+
+class CxadratPlayer: public CxadPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CxadratPlayer(Copl *newopl): CxadPlayer(newopl)
+ { }
+
+protected:
+ struct rat_header
+ {
+ char id[3];
+ unsigned char version;
+ char title[32];
+ unsigned char numchan;
+ unsigned char reserved_25;
+ unsigned char order_end;
+ unsigned char reserved_27;
+ unsigned char numinst; // ?: Number of Instruments
+ unsigned char reserved_29;
+ unsigned char numpat; // ?: Number of Patterns
+ unsigned char reserved_2B;
+ unsigned char order_start;
+ unsigned char reserved_2D;
+ unsigned char order_loop;
+ unsigned char reserved_2F;
+ unsigned char volume;
+ unsigned char speed;
+ unsigned char reserved_32[12];
+ unsigned char patseg[2];
+ };
+
+ struct rat_event
+ {
+ unsigned char note;
+ unsigned char instrument;
+ unsigned char volume;
+ unsigned char fx;
+ unsigned char fxp;
+ };
+
+ struct rat_instrument
+ {
+ unsigned char freq[2];
+ unsigned char reserved_2[2];
+ unsigned char mod_ctrl;
+ unsigned char car_ctrl;
+ unsigned char mod_volume;
+ unsigned char car_volume;
+ unsigned char mod_AD;
+ unsigned char car_AD;
+ unsigned char mod_SR;
+ unsigned char car_SR;
+ unsigned char mod_wave;
+ unsigned char car_wave;
+ unsigned char connect;
+ unsigned char reserved_F;
+ unsigned char volume;
+ unsigned char reserved_11[3];
+ };
+
+ struct
+ {
+ rat_header hdr;
+
+ unsigned char volume;
+ unsigned char order_pos;
+ unsigned char pattern_pos;
+
+ unsigned char *order;
+
+ rat_instrument *inst;
+
+ rat_event tracks[256][64][9];
+
+ struct
+ {
+ unsigned char instrument;
+ unsigned char volume;
+ unsigned char fx;
+ unsigned char fxp;
+ } channel[9];
+ } rat;
+ //
+ bool xadplayer_load();
+ void xadplayer_rewind(int subsong);
+ void xadplayer_update();
+ float xadplayer_getrefresh();
+ std::string xadplayer_gettype();
+ std::string xadplayer_gettitle();
+ unsigned int xadplayer_getinstruments();
+ //
+private:
+ static const unsigned char rat_adlib_bases[18];
+ static const unsigned short rat_notes[16];
+
+ unsigned char __rat_calc_volume(unsigned char ivol, unsigned char cvol, unsigned char gvol);
+};
diff --git a/plugins/adplug/adplug/raw.cpp b/plugins/adplug/adplug/raw.cpp
new file mode 100644
index 00000000..1672a1d0
--- /dev/null
+++ b/plugins/adplug/adplug/raw.cpp
@@ -0,0 +1,104 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * raw.c - RAW Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string.h>
+#include "raw.h"
+
+/*** public methods *************************************/
+
+CPlayer *CrawPlayer::factory(Copl *newopl)
+{
+ return new CrawPlayer(newopl);
+}
+
+bool CrawPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ char id[8];
+ unsigned long i;
+
+ // file validation section
+ f->readString(id, 8);
+ if(strncmp(id,"RAWADATA",8)) { fp.close (f); return false; }
+
+ // load section
+ clock = f->readInt(2); // clock speed
+ length = (fp.filesize(f) - 10) / 2;
+ data = new Tdata [length];
+ for(i = 0; i < length; i++) {
+ data[i].param = f->readInt(1);
+ data[i].command = f->readInt(1);
+ }
+
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+bool CrawPlayer::update()
+{
+ bool setspeed;
+
+ if(pos >= length) return false;
+
+ if(del) {
+ del--;
+ return !songend;
+ }
+
+ do {
+ setspeed = false;
+ switch(data[pos].command) {
+ case 0: del = data[pos].param - 1; break;
+ case 2:
+ if(!data[pos].param) {
+ pos++;
+ speed = data[pos].param + (data[pos].command << 8);
+ setspeed = true;
+ } else
+ opl->setchip(data[pos].param - 1);
+ break;
+ case 0xff:
+ if(data[pos].param == 0xff) {
+ rewind(0); // auto-rewind song
+ songend = true;
+ return !songend;
+ }
+ break;
+ default:
+ opl->write(data[pos].command,data[pos].param);
+ break;
+ }
+ } while(data[pos++].command || setspeed);
+
+ return !songend;
+}
+
+void CrawPlayer::rewind(int subsong)
+{
+ pos = del = 0; speed = clock; songend = false;
+ opl->init(); opl->write(1, 32); // go to 9 channel mode
+}
+
+float CrawPlayer::getrefresh()
+{
+ return 1193180.0 / (speed ? speed : 0xffff); // timer oscillator speed / wait register = clock frequency
+}
diff --git a/plugins/adplug/adplug/raw.h b/plugins/adplug/adplug/raw.h
new file mode 100644
index 00000000..e05f1fe7
--- /dev/null
+++ b/plugins/adplug/adplug/raw.h
@@ -0,0 +1,52 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * raw.h - RAW Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "player.h"
+
+class CrawPlayer: public CPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CrawPlayer(Copl *newopl)
+ : CPlayer(newopl), data(0)
+ { };
+ ~CrawPlayer()
+ { if(data) delete [] data; };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype()
+ { return std::string("RdosPlay RAW"); };
+
+protected:
+ struct Tdata {
+ unsigned char param, command;
+ } *data;
+
+ unsigned long pos, length;
+ unsigned short clock, speed;
+ unsigned char del;
+ bool songend;
+};
diff --git a/plugins/adplug/adplug/realopl.cpp b/plugins/adplug/adplug/realopl.cpp
new file mode 100644
index 00000000..e8dcda81
--- /dev/null
+++ b/plugins/adplug/adplug/realopl.cpp
@@ -0,0 +1,208 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * realopl.cpp - Real hardware OPL, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifdef _MSC_VER // Microsoft Visual C++
+# include <conio.h>
+# define INP _inp
+# define OUTP _outp
+#elif defined(__WATCOMC__) // Watcom C/C++ and OpenWatcom
+# include <conio.h>
+# define INP inp
+# define OUTP outp
+#elif defined(WIN32) && defined(__MSVCRT__) && defined(__MINGW32__) // MinGW32
+/*
+int __cdecl _inp(unsigned short);
+int __cdecl _outp(unsigned short, int);
+# define INP _inp
+# define OUTP _outp
+*/
+# define INP inb
+# define OUTP(reg, val) outb(val, reg)
+#elif defined(DJGPP) // DJGPP
+# include <pc.h>
+# define INP inportb
+# define OUTP outportb
+#else // no support on other platforms
+# define INP(reg) 0
+# define OUTP(reg, val)
+#endif
+
+#include "realopl.h"
+
+#define SHORTDELAY 6 // short delay in I/O port-reads after OPL hardware output
+#define LONGDELAY 35 // long delay in I/O port-reads after OPL hardware output
+
+const unsigned char CRealopl::op_table[9] =
+ {0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12};
+
+#if defined(WIN32) && defined(__MINGW32__)
+static __inline unsigned char inb(unsigned short int port)
+{
+ unsigned char _v;
+
+ __asm__ __volatile__ ("inb %w1,%0":"=a" (_v):"Nd" (port));
+ return _v;
+}
+
+static __inline void outb(unsigned char value, unsigned short int port)
+{
+ __asm__ __volatile__ ("outb %b0,%w1": :"a" (value), "Nd" (port));
+}
+#endif
+
+CRealopl::CRealopl(unsigned short initport)
+ : adlport(initport), hardvol(0), bequiet(false), nowrite(false)
+{
+ for(int i=0;i<22;i++) {
+ hardvols[0][i][0] = 0;
+ hardvols[0][i][1] = 0;
+ hardvols[1][i][0] = 0;
+ hardvols[1][i][1] = 0;
+ }
+
+ currType = TYPE_OPL3;
+}
+
+bool CRealopl::harddetect()
+{
+ unsigned char stat1, stat2, i;
+ unsigned short adp = (currChip == 0 ? adlport : adlport + 2);
+
+ hardwrite(4,0x60); hardwrite(4,0x80);
+ stat1 = INP(adp);
+ hardwrite(2,0xff); hardwrite(4,0x21);
+ for(i=0;i<80;i++) // wait for adlib
+ INP(adp);
+ stat2 = INP(adp);
+ hardwrite(4,0x60); hardwrite(4,0x80);
+
+ if(((stat1 & 0xe0) == 0) && ((stat2 & 0xe0) == 0xc0))
+ return true;
+ else
+ return false;
+}
+
+bool CRealopl::detect()
+{
+ unsigned char stat;
+
+ setchip(0);
+ if(harddetect()) {
+ // is at least OPL2, check for OPL3
+ currType = TYPE_OPL2;
+
+ stat = INP(adlport);
+ if(stat & 6) {
+ // not OPL3, try dual-OPL2
+ setchip(1);
+ if(harddetect())
+ currType = TYPE_DUAL_OPL2;
+ } else
+ currType = TYPE_OPL3;
+
+ setchip(0);
+ return true;
+ } else
+ return false;
+}
+
+void CRealopl::setvolume(int volume)
+{
+ int i, j;
+
+ hardvol = volume;
+ for(j = 0; j < 2; j++)
+ for(i = 0; i < 9; i++) {
+ hardwrite(0x43+op_table[i],((hardvols[j][op_table[i]+3][0] & 63) + volume) > 63 ? 63 : hardvols[j][op_table[i]+3][0] + volume);
+ if(hardvols[j][i][1] & 1) // modulator too?
+ hardwrite(0x40+op_table[i],((hardvols[j][op_table[i]][0] & 63) + volume) > 63 ? 63 : hardvols[j][op_table[i]][0] + volume);
+ }
+}
+
+void CRealopl::setquiet(bool quiet)
+{
+ bequiet = quiet;
+
+ if(quiet) {
+ oldvol = hardvol;
+ setvolume(63);
+ } else
+ setvolume(oldvol);
+}
+
+void CRealopl::hardwrite(int reg, int val)
+{
+ int i;
+ unsigned short adp = (currChip == 0 ? adlport : adlport + 2);
+
+ OUTP(adp,reg); // set register
+ for(i=0;i<SHORTDELAY;i++) // wait for adlib
+ INP(adp);
+ OUTP(adp+1,val); // set value
+ for(i=0;i<LONGDELAY;i++) // wait for adlib
+ INP(adp);
+}
+
+void CRealopl::write(int reg, int val)
+{
+ int i;
+
+ if(nowrite)
+ return;
+
+ if(currType == TYPE_OPL2 && currChip > 0)
+ return;
+
+ if(bequiet && (reg >= 0xb0 && reg <= 0xb8)) // filter all key-on commands
+ val &= ~32;
+ if(reg >= 0x40 && reg <= 0x55) // cache volumes
+ hardvols[currChip][reg-0x40][0] = val;
+ if(reg >= 0xc0 && reg <= 0xc8)
+ hardvols[currChip][reg-0xc0][1] = val;
+ if(hardvol) // reduce volume
+ for(i=0;i<9;i++) {
+ if(reg == 0x43 + op_table[i])
+ val = ((val & 63) + hardvol) > 63 ? 63 : val + hardvol;
+ else
+ if((reg == 0x40 + op_table[i]) && (hardvols[currChip][i][1] & 1))
+ val = ((val & 63) + hardvol) > 63 ? 63 : val + hardvol;
+ }
+
+ hardwrite(reg,val);
+}
+
+void CRealopl::init()
+{
+ int i, j;
+
+ for(j = 0; j < 2; j++) {
+ setchip(j);
+
+ for(i=0;i<9;i++) { // stop instruments
+ hardwrite(0xb0 + i,0); // key off
+ hardwrite(0x80 + op_table[i],0xff); // fastest release
+ }
+
+ hardwrite(0xbd,0); // clear misc. register
+ }
+
+ setchip(0);
+}
diff --git a/plugins/adplug/adplug/realopl.h b/plugins/adplug/adplug/realopl.h
new file mode 100644
index 00000000..e675fd92
--- /dev/null
+++ b/plugins/adplug/adplug/realopl.h
@@ -0,0 +1,72 @@
+/*
+ * AdPlug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 Simon Peter <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * realopl.h - Real hardware OPL, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_REALOPL
+#define H_ADPLUG_REALOPL
+
+#include "opl.h"
+
+#define DFL_ADLIBPORT 0x388 // default adlib baseport
+
+class CRealopl: public Copl
+{
+ public:
+ CRealopl(unsigned short initport = DFL_ADLIBPORT); // initport = OPL2 hardware baseport
+
+ bool detect(); // returns true if adlib compatible board is found, else false
+ void setvolume(int volume); // set adlib master volume (0 - 63) 0 = loudest, 63 = softest
+ void setquiet(bool quiet = true); // sets the OPL2 quiet, while still writing to the registers
+ void setport(unsigned short port) // set new OPL2 hardware baseport
+ {
+ adlport = port;
+ }
+ void setnowrite(bool nw = true) // set hardware write status
+ {
+ nowrite = nw;
+ }
+
+ int getvolume() // get adlib master volume
+ {
+ return hardvol;
+ }
+
+ // template methods
+ void write(int reg, int val);
+ void init();
+ void settype(ChipType type)
+ {
+ currType = type;
+ }
+
+ protected:
+ void hardwrite(int reg, int val); // write to OPL2 hardware registers
+ bool harddetect(); // do real hardware detection
+
+ static const unsigned char op_table[9]; // the 9 operators as expected by the OPL2
+
+ unsigned short adlport; // adlib hardware baseport
+ int hardvol, oldvol; // hardware master volume
+ bool bequiet; // quiet status cache
+ char hardvols[2][22][2]; // volume cache
+ bool nowrite; // don't write to hardware, if true
+};
+
+#endif
diff --git a/plugins/adplug/adplug/rix.cpp b/plugins/adplug/adplug/rix.cpp
new file mode 100644
index 00000000..02ebc8d5
--- /dev/null
+++ b/plugins/adplug/adplug/rix.cpp
@@ -0,0 +1,495 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * rix.cpp - Softstar RIX OPL Format Player by palxex <palxex.ys168.com>
+ * BSPAL <BSPAL.ys168.com>
+ */
+
+#include <string.h>
+#include "rix.h"
+#include "debug.h"
+
+const unsigned char CrixPlayer::adflag[] = {0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1};
+const unsigned char CrixPlayer::reg_data[] = {0,1,2,3,4,5,8,9,10,11,12,13,16,17,18,19,20,21};
+const unsigned char CrixPlayer::ad_C0_offs[] = {0,1,2,0,1,2,3,4,5,3,4,5,6,7,8,6,7,8};
+const unsigned char CrixPlayer::modify[] = {0,3,1,4,2,5,6,9,7,10,8,11,12,15,13,16,14,17,12,\
+ 15,16,0,14,0,17,0,13,0};
+const unsigned char CrixPlayer::bd_reg_data[] = {
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x08,0x04,0x02,0x01,
+ 0x00,0x01,0x01,0x03,0x0F,0x05,0x00,0x01,0x03,0x0F,0x00,
+ 0x00,0x00,0x01,0x00,0x00,0x01,0x01,0x0F,0x07,0x00,0x02,
+ 0x04,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x0A,
+ 0x04,0x00,0x08,0x0C,0x0B,0x00,0x00,0x00,0x01,0x00,0x00,
+ 0x00,0x00,0x0D,0x04,0x00,0x06,0x0F,0x00,0x00,0x00,0x00,
+ 0x01,0x00,0x00,0x0C,0x00,0x0F,0x0B,0x00,0x08,0x05,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x0F,0x0B,0x00,
+ 0x07,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,
+ 0x0F,0x0B,0x00,0x05,0x05,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x01,0x00,0x0F,0x0B,0x00,0x07,0x05,0x00,0x00,0x00,
+ 0x00,0x00,0x00};
+unsigned char CrixPlayer::for40reg[] = {0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
+ 0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F};
+unsigned short CrixPlayer::mus_time = 0x4268;
+
+/*** public methods *************************************/
+
+CPlayer *CrixPlayer::factory(Copl *newopl)
+{
+ return new CrixPlayer(newopl);
+}
+
+CrixPlayer::CrixPlayer(Copl *newopl)
+ : CPlayer(newopl), flag_mkf(0), file_buffer(0), buf_addr(0)
+{
+}
+
+CrixPlayer::~CrixPlayer()
+{
+ if(file_buffer)
+ delete [] file_buffer;
+}
+
+bool CrixPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ unsigned long i=0;
+
+ if(stricmp(filename.substr(filename.length()-4,4).c_str(),".mkf")==0)
+ {
+ flag_mkf=1;
+ f->seek(0);
+ int offset=f->readInt(4);
+ f->seek(offset);
+ }
+ if(f->readInt(2)!=0x55aa){ fp.close(f);return false; }
+ file_buffer = new unsigned char [fp.filesize(f) + 1];
+ f->seek(0);
+ while(!f->eof())
+ file_buffer[i++]=f->readInt(1);
+ length=i;
+ fp.close(f);
+ if(!flag_mkf)
+ buf_addr=file_buffer;
+ rewind(0);
+ return true;
+}
+
+bool CrixPlayer::update()
+{
+ int_08h_entry();
+ return !play_end;
+}
+
+void CrixPlayer::rewind(int subsong)
+{
+ I = 0; T = 0;
+ mus_block = 0;
+ ins_block = 0;
+ rhythm = 0;
+ music_on = 0;
+ pause_flag = 0;
+ band = 0;
+ band_low = 0;
+ e0_reg_flag = 0;
+ bd_modify = 0;
+ sustain = 0;
+ play_end = 0;
+ pos = index = 0;
+
+ memset(f_buffer, 0, sizeof(unsigned short) * 300);
+ memset(a0b0_data2, 0, sizeof(unsigned short) * 11);
+ memset(a0b0_data3, 0, 18);
+ memset(a0b0_data4, 0, 18);
+ memset(a0b0_data5, 0, 96);
+ memset(addrs_head, 0, 96);
+ memset(insbuf, 0, 28 * sizeof(unsigned short));
+ memset(displace, 0, 11 * sizeof(unsigned short));
+ memset(reg_bufs, 0, 18 * sizeof(ADDT));
+
+ if(flag_mkf)
+ {
+ unsigned int *buf_index=(unsigned int *)file_buffer;
+ int offset1=buf_index[subsong],offset2;
+ while((offset2=buf_index[++subsong])==offset1);
+ length=offset2-offset1+1;
+ buf_addr=file_buffer+offset1;
+ }
+ opl->init();
+ opl->write(1,32); // go to OPL2 mode
+ set_new_int();
+ data_initial();
+}
+
+unsigned int CrixPlayer::getsubsongs()
+{
+ if(flag_mkf)
+ {
+ unsigned int *buf_index=(unsigned int *)file_buffer;
+ int songs=buf_index[0]/4,i=0;
+ for(i=0;i<songs;i++)
+ if(buf_index[i+1]==buf_index[i])
+ songs--;
+ return songs;
+ }
+ else
+ return 1;
+}
+
+float CrixPlayer::getrefresh()
+{
+ return 70.0f;
+}
+
+/*------------------Implemention----------------------------*/
+inline void CrixPlayer::set_new_int()
+{
+// if(!ad_initial()) exit(1);
+ ad_initial();
+}
+/*----------------------------------------------------------*/
+inline void CrixPlayer::Pause()
+{
+ register unsigned short i;
+ pause_flag = 1;
+ for(i=0;i<11;i++)
+ switch_ad_bd(i);
+}
+/*----------------------------------------------------------*/
+inline void CrixPlayer::ad_a0b0l_reg_(unsigned short index,unsigned short p2,unsigned short p3)
+{
+// unsigned short i = p2+a0b0_data2[index];
+ a0b0_data4[index] = p3;
+ a0b0_data3[index] = p2;
+}
+inline void CrixPlayer::data_initial()
+{
+ rhythm = buf_addr[2];
+ mus_block = (buf_addr[0x0D]<<8)+buf_addr[0x0C];
+ ins_block = (buf_addr[0x09]<<8)+buf_addr[0x08];
+ I = mus_block+1;
+ if(rhythm != 0)
+ {
+ // ad_a0b0_reg(6);
+ // ad_a0b0_reg(7);
+ // ad_a0b0_reg(8);
+ ad_a0b0l_reg_(8,0x18,0);
+ ad_a0b0l_reg_(7,0x1F,0);
+ }
+ bd_modify = 0;
+ // ad_bd_reg();
+ band = 0; music_on = 1;
+}
+/*----------------------------------------------------------*/
+inline unsigned short CrixPlayer::ad_initial()
+{
+ register unsigned short i,j,k = 0;
+ for(i=0;i<25;i++)
+ {
+ f_buffer[i*12]=(unsigned int)((i*24+10000)*0.27461678223+4)>>3;
+ for(int t=1;t<12;t++)
+ f_buffer[i*12+t]=f_buffer[i*12+t-1]*1.06;
+ }
+ for(i=0;i<8;i++)
+ for(j=0;j<12;j++)
+ {
+ a0b0_data5[k] = i;
+ addrs_head[k] = j;
+ k++;
+ }
+ //ad_bd_reg();
+ //ad_08_reg();
+ //for(i=0;i<9;i++) ad_a0b0_reg(i);
+ e0_reg_flag = 0x20;
+ //for(i=0;i<18;i++) ad_bop(0xE0+reg_data[i],0);
+ //ad_bop(1,e0_reg_flag);
+ return 1;//ad_test();
+}
+/*----------------------------------------------------------*/
+inline void CrixPlayer::ad_bop(unsigned short reg,unsigned short value)
+{
+ if(reg == 2 || reg == 3)
+ AdPlug_LogWrite("switch OPL2/3 mode!\n");
+ opl->write(reg & 0xff, value & 0xff);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::int_08h_entry()
+ {
+ unsigned short band_sus = 1;
+ while(band_sus)
+ {
+ if(sustain <= 0)
+ {
+ band_sus = rix_proc();
+ if(band_sus) sustain += band_sus;
+ else
+ {
+ play_end=1;
+ break;
+ }
+ }
+ else
+ {
+ if(band_sus) sustain -= 14; /* aging */
+ break;
+ }
+ }
+ }
+/*--------------------------------------------------------------*/
+inline unsigned short CrixPlayer::rix_proc()
+{
+ unsigned char ctrl = 0;
+ if(music_on == 0||pause_flag == 1) return 0;
+ band = 0;
+ while(buf_addr[I] != 0x80 && I<length-1)
+ {
+ band_low = buf_addr[I-1];
+ ctrl = buf_addr[I]; I+=2;
+ switch(ctrl&0xF0)
+ {
+ case 0x90: rix_get_ins(); rix_90_pro(ctrl&0x0F); break;
+ case 0xA0: rix_A0_pro(ctrl&0x0F,((unsigned short)band_low)<<6); break;
+ case 0xB0: rix_B0_pro(ctrl&0x0F,band_low); break;
+ case 0xC0: switch_ad_bd(ctrl&0x0F);
+ if(band_low != 0) rix_C0_pro(ctrl&0x0F,band_low);
+ break;
+ default: band = (ctrl<<8)+band_low; break;
+ }
+ if(band != 0) return band;
+ }
+ music_ctrl();
+ I = mus_block+1;
+ band = 0; music_on = 1;
+ return 0;
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::rix_get_ins()
+{
+ int i;
+ unsigned char *baddr = (&buf_addr[ins_block])+(band_low<<6);
+
+ for(i = 0; i < 28; i++)
+ insbuf[i] = (baddr[i * 2 + 1] << 8) + baddr[i * 2];
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::rix_90_pro(unsigned short ctrl_l)
+{
+ if(rhythm == 0 || ctrl_l < 6)
+ {
+ ins_to_reg(modify[ctrl_l*2],insbuf,insbuf[26]);
+ ins_to_reg(modify[ctrl_l*2+1],insbuf+13,insbuf[27]);
+ return;
+ }
+ else if(ctrl_l > 6)
+ {
+ ins_to_reg(modify[ctrl_l*2+6],insbuf,insbuf[26]);
+ return;
+ }
+ else
+ {
+ ins_to_reg(12,insbuf,insbuf[26]);
+ ins_to_reg(15,insbuf+13,insbuf[27]);
+ return;
+ }
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::rix_A0_pro(unsigned short ctrl_l,unsigned short index)
+{
+ if(rhythm == 0 || ctrl_l <= 6)
+ {
+ prepare_a0b0(ctrl_l,index>0x3FFF?0x3FFF:index);
+ ad_a0b0l_reg(ctrl_l,a0b0_data3[ctrl_l],a0b0_data4[ctrl_l]);
+ }
+ else return;
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::prepare_a0b0(unsigned short index,unsigned short v) /* important !*/
+{
+ short high = 0,low = 0; unsigned int res;
+ int res1 = (v-0x2000)*0x19;
+ if(res1 == (int)0xff) return;
+ low = res1/0x2000;
+ if(low < 0)
+ {
+ low = 0x18-low; high = (signed short)low<0?0xFFFF:0;
+ res = high; res<<=16; res+=low;
+ low = ((signed short)res)/(signed short)0xFFE7;
+ a0b0_data2[index] = low;
+ low = res;
+ res = low - 0x18;
+ high = (signed short)res%0x19;
+ low = (signed short)res/0x19;
+ if(high != 0) {low = 0x19; low = low-high;}
+ }
+ else
+ {
+ res = high = low;
+ low = (signed short)res/(signed short)0x19;
+ a0b0_data2[index] = low;
+ res = high;
+ low = (signed short)res%(signed short)0x19;
+ }
+ low = (signed short)low*(signed short)0x18;
+ displace[index] = low;
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::ad_a0b0l_reg(unsigned short index,unsigned short p2,unsigned short p3)
+{
+ unsigned short data; unsigned short i = p2+a0b0_data2[index];
+ a0b0_data4[index] = p3;
+ a0b0_data3[index] = p2;
+ i = ((signed short)i<=0x5F?i:0x5F);
+ i = ((signed short)i>=0?i:0);
+ data = f_buffer[addrs_head[i]+displace[index]/2];
+ ad_bop(0xA0+index,data);
+ data = a0b0_data5[i]*4+(p3<1?0:0x20)+((data>>8)&3);
+ ad_bop(0xB0+index,data);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::rix_B0_pro(unsigned short ctrl_l,unsigned short index)
+{
+ register int temp = 0;
+ if(rhythm == 0 || ctrl_l < 6) temp = modify[ctrl_l*2+1];
+ else
+ {
+ temp = ctrl_l > 6?ctrl_l*2:ctrl_l*2+1;
+ temp = modify[temp+6];
+ }
+ for40reg[temp] = index>0x7F?0x7F:index;
+ ad_40_reg(temp);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::rix_C0_pro(unsigned short ctrl_l,unsigned short index)
+{
+ register unsigned short i = index>=12?index-12:0;
+ if(ctrl_l < 6 || rhythm == 0)
+ {
+ ad_a0b0l_reg(ctrl_l,i,1);
+ return;
+ }
+ else
+ {
+ if(ctrl_l != 6)
+ {
+ if(ctrl_l == 8)
+ {
+ ad_a0b0l_reg(ctrl_l,i,0);
+ ad_a0b0l_reg(7,i+7,0);
+ }
+ }
+ else ad_a0b0l_reg(ctrl_l,i,0);
+ bd_modify |= bd_reg_data[ctrl_l];
+ ad_bd_reg();
+ return;
+ }
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::switch_ad_bd(unsigned short index)
+{
+
+ if(rhythm == 0 || index < 6) ad_a0b0l_reg(index,a0b0_data3[index],0);
+ else
+ {
+ bd_modify &= (~bd_reg_data[index]),
+ ad_bd_reg();
+ }
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::ins_to_reg(unsigned short index,unsigned short* insb,unsigned short value)
+{
+ register unsigned short i;
+ for(i=0;i<13;i++) reg_bufs[index].v[i] = insb[i];
+ reg_bufs[index].v[13] = value&3;
+ ad_bd_reg(),ad_08_reg(),
+ ad_40_reg(index),ad_C0_reg(index),ad_60_reg(index),
+ ad_80_reg(index),ad_20_reg(index),ad_E0_reg(index);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::ad_E0_reg(unsigned short index)
+{
+ unsigned short data = e0_reg_flag == 0?0:(reg_bufs[index].v[13]&3);
+ ad_bop(0xE0+reg_data[index],data);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::ad_20_reg(unsigned short index)
+{
+ unsigned short data = (reg_bufs[index].v[9] < 1?0:0x80);
+ data += (reg_bufs[index].v[10] < 1?0:0x40);
+ data += (reg_bufs[index].v[5] < 1?0:0x20);
+ data += (reg_bufs[index].v[11] < 1?0:0x10);
+ data += (reg_bufs[index].v[1]&0x0F);
+ ad_bop(0x20+reg_data[index],data);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::ad_80_reg(unsigned short index)
+{
+ unsigned short data = (reg_bufs[index].v[7]&0x0F),temp = reg_bufs[index].v[4];
+ data |= (temp << 4);
+ ad_bop(0x80+reg_data[index],data);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::ad_60_reg(unsigned short index)
+{
+ unsigned short data = reg_bufs[index].v[6]&0x0F,temp = reg_bufs[index].v[3];
+ data |= (temp << 4);
+ ad_bop(0x60+reg_data[index],data);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::ad_C0_reg(unsigned short index)
+{
+ unsigned short data = reg_bufs[index].v[2];
+ if(adflag[index] == 1) return;
+ data *= 2,
+ data |= (reg_bufs[index].v[12] < 1?1:0);
+ ad_bop(0xC0+ad_C0_offs[index],data);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::ad_40_reg(unsigned short index)
+{
+ unsigned int res = 0;
+ unsigned short data = 0,temp = reg_bufs[index].v[0];
+ data = 0x3F - (0x3F & reg_bufs[index].v[8]),
+ data *= for40reg[index],
+ data *= 2,
+ data += 0x7F,
+ res = data;
+ data = res/0xFE,
+ data -= 0x3F,
+ data = -data,
+ data |= temp<<6;
+ ad_bop(0x40+reg_data[index],data);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::ad_bd_reg()
+{
+ unsigned short data = rhythm < 1? 0:0x20;
+ data |= bd_modify;
+ ad_bop(0xBD,data);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::ad_a0b0_reg(unsigned short index)
+{
+ ad_bop(0xA0+index,0);
+ ad_bop(0xB0+index,0);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::music_ctrl()
+{
+ register int i;
+ for(i=0;i<11;i++)
+ switch_ad_bd(i);
+}
diff --git a/plugins/adplug/adplug/rix.h b/plugins/adplug/adplug/rix.h
new file mode 100644
index 00000000..68e08c8b
--- /dev/null
+++ b/plugins/adplug/adplug/rix.h
@@ -0,0 +1,112 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * rix.h - Softstar RIX OPL Format Player by palxex <palxex.ys168.com>
+ * BSPAL <BSPAL.ys168.com>
+ */
+
+#include "player.h"
+
+class CrixPlayer: public CPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ CrixPlayer(Copl *newopl);
+ ~CrixPlayer();
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+ unsigned int getsubsongs();
+
+ std::string gettype()
+ { return std::string("Softstar RIX OPL Music Format"); };
+
+ protected:
+ typedef struct {
+ unsigned char v[14];
+ } ADDT;
+
+ int flag_mkf;
+ unsigned char *file_buffer;
+ unsigned char *buf_addr; /* rix files' f_buffer */
+ unsigned short f_buffer[300];//9C0h-C18h
+ unsigned short a0b0_data2[11];
+ unsigned char a0b0_data3[18];
+ unsigned char a0b0_data4[18];
+ unsigned char a0b0_data5[96];
+ unsigned char addrs_head[96];
+ unsigned short insbuf[28];
+ unsigned short displace[11];
+ ADDT reg_bufs[18];
+ unsigned long pos,length;
+ unsigned char index;
+
+ static const unsigned char adflag[18];
+ static const unsigned char reg_data[18];
+ static const unsigned char ad_C0_offs[18];
+ static const unsigned char modify[28];
+ static const unsigned char bd_reg_data[124];
+ static unsigned char for40reg[18];
+ static unsigned short mus_time;
+ unsigned int I,T;
+ unsigned short mus_block;
+ unsigned short ins_block;
+ unsigned char rhythm;
+ unsigned char music_on;
+ unsigned char pause_flag;
+ unsigned short band;
+ unsigned char band_low;
+ unsigned short e0_reg_flag;
+ unsigned char bd_modify;
+ int sustain;
+ int play_end;
+
+#define ad_08_reg() ad_bop(8,0) /**/
+ inline void ad_20_reg(unsigned short); /**/
+ inline void ad_40_reg(unsigned short); /**/
+ inline void ad_60_reg(unsigned short); /**/
+ inline void ad_80_reg(unsigned short); /**/
+ inline void ad_a0b0_reg(unsigned short); /**/
+ inline void ad_a0b0l_reg(unsigned short,unsigned short,unsigned short); /**/
+ inline void ad_a0b0l_reg_(unsigned short,unsigned short,unsigned short); /**/
+ inline void ad_bd_reg(); /**/
+ inline void ad_bop(unsigned short,unsigned short); /**/
+ inline void ad_C0_reg(unsigned short); /**/
+ inline void ad_E0_reg(unsigned short); /**/
+ inline unsigned short ad_initial(); /**/
+ inline unsigned short ad_test(); /**/
+ inline void crc_trans(unsigned short,unsigned short); /**/
+ inline void data_initial(); /* done */
+ inline void init(); /**/
+ inline void ins_to_reg(unsigned short,unsigned short*,unsigned short); /**/
+ inline void int_08h_entry(); /**/
+ inline void music_ctrl(); /**/
+ inline void Pause(); /**/
+ inline void prepare_a0b0(unsigned short,unsigned short); /**/
+ inline void rix_90_pro(unsigned short); /**/
+ inline void rix_A0_pro(unsigned short,unsigned short); /**/
+ inline void rix_B0_pro(unsigned short,unsigned short); /**/
+ inline void rix_C0_pro(unsigned short,unsigned short); /**/
+ inline void rix_get_ins(); /**/
+ inline unsigned short rix_proc(); /**/
+ inline void set_new_int();
+ inline void switch_ad_bd(unsigned short); /**/
+};
diff --git a/plugins/adplug/adplug/rol.cpp b/plugins/adplug/adplug/rol.cpp
new file mode 100644
index 00000000..7507d79b
--- /dev/null
+++ b/plugins/adplug/adplug/rol.cpp
@@ -0,0 +1,723 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * rol.h - ROL Player by OPLx <oplx@yahoo.com>
+ *
+ * Visit: http://tenacity.hispeed.com/aomit/oplx/
+ */
+#include <algorithm>
+
+#include "rol.h"
+#include "debug.h"
+
+int const CrolPlayer::kSizeofDataRecord = 30;
+int const CrolPlayer::kMaxTickBeat = 60;
+int const CrolPlayer::kSilenceNote = -12;
+int const CrolPlayer::kNumMelodicVoices = 9;
+int const CrolPlayer::kNumPercussiveVoices = 11;
+int const CrolPlayer::kBassDrumChannel = 6;
+int const CrolPlayer::kSnareDrumChannel = 7;
+int const CrolPlayer::kTomtomChannel = 8;
+int const CrolPlayer::kTomtomFreq = 2;//4;
+int const CrolPlayer::kSnareDrumFreq = 2;//kTomtomFreq + 7;
+float const CrolPlayer::kDefaultUpdateTme = 18.2f;
+float const CrolPlayer::kPitchFactor = 400.0f;
+
+static const unsigned char drum_table[4] = {0x14, 0x12, 0x15, 0x11};
+
+CrolPlayer::uint16 const CrolPlayer::kNoteTable[12] =
+{
+ 340, // C
+ 363, // C#
+ 385, // D
+ 408, // D#
+ 432, // E
+ 458, // F
+ 485, // F#
+ 514, // G
+ 544, // G#
+ 577, // A
+ 611, // A#
+ 647 // B
+};
+
+/*** public methods **************************************/
+
+CPlayer *CrolPlayer::factory(Copl *newopl)
+{
+ return new CrolPlayer(newopl);
+}
+//---------------------------------------------------------
+CrolPlayer::CrolPlayer(Copl *newopl)
+: CPlayer ( newopl )
+ ,rol_header ( NULL )
+ ,mNextTempoEvent ( 0 )
+ ,mCurrTick ( 0 )
+ ,mTimeOfLastNote ( 0 )
+ ,mRefresh ( kDefaultUpdateTme )
+ ,bdRegister ( 0 )
+{
+ int n;
+
+ memset(bxRegister, 0, sizeof(bxRegister) );
+ memset(volumeCache, 0, sizeof(volumeCache) );
+ memset(freqCache, 0, sizeof(freqCache) );
+
+ for(n=0; n<11; n++)
+ pitchCache[n]=1.0f;
+}
+//---------------------------------------------------------
+CrolPlayer::~CrolPlayer()
+{
+ if( rol_header != NULL )
+ {
+ delete rol_header;
+ rol_header=NULL;
+ }
+}
+//---------------------------------------------------------
+bool CrolPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+
+ char *fn = new char[filename.length()+12];
+ int i;
+ std::string bnk_filename;
+
+ AdPlug_LogWrite("*** CrolPlayer::load(f, \"%s\") ***\n", filename.c_str());
+ strcpy(fn,filename.data());
+ for (i=strlen(fn)-1; i>=0; i--)
+ if (fn[i] == '/' || fn[i] == '\\')
+ break;
+ strcpy(fn+i+1,"standard.bnk");
+ bnk_filename = fn;
+ delete [] fn;
+ AdPlug_LogWrite("bnk_filename = \"%s\"\n",bnk_filename.c_str());
+
+ rol_header = new SRolHeader;
+ memset( rol_header, 0, sizeof(SRolHeader) );
+
+ rol_header->version_major = f->readInt( 2 );
+ rol_header->version_minor = f->readInt( 2 );
+
+ // Version check
+ if(rol_header->version_major != 0 || rol_header->version_minor != 4) {
+ AdPlug_LogWrite("Unsupported file version %d.%d or not a ROL file!\n",
+ rol_header->version_major, rol_header->version_minor);
+ AdPlug_LogWrite("--- CrolPlayer::load ---\n");
+ fp.close(f);
+ return false;
+ }
+
+ f->seek( 40, binio::Add );
+
+ rol_header->ticks_per_beat = f->readInt( 2 );
+ rol_header->beats_per_measure = f->readInt( 2 );
+ rol_header->edit_scale_y = f->readInt( 2 );
+ rol_header->edit_scale_x = f->readInt( 2 );
+
+ f->seek( 1, binio::Add );
+
+ rol_header->mode = f->readInt(1);
+
+ f->seek( 90+38+15, binio::Add );
+
+ rol_header->basic_tempo = f->readFloat( binio::Single );
+
+ load_tempo_events( f );
+
+ mTimeOfLastNote = 0;
+
+ if( load_voice_data( f, bnk_filename, fp ) != true )
+ {
+ AdPlug_LogWrite("CrolPlayer::load_voice_data(f) failed!\n");
+ AdPlug_LogWrite("--- CrolPlayer::load ---\n");
+
+ fp.close( f );
+ return false;
+ }
+
+ fp.close( f );
+
+ rewind( 0 );
+ AdPlug_LogWrite("--- CrolPlayer::load ---\n");
+ return true;
+}
+//---------------------------------------------------------
+bool CrolPlayer::update()
+{
+ if( mNextTempoEvent < mTempoEvents.size() &&
+ mTempoEvents[mNextTempoEvent].time == mCurrTick )
+ {
+ SetRefresh( mTempoEvents[mNextTempoEvent].multiplier );
+ ++mNextTempoEvent;
+ }
+
+ TVoiceData::iterator curr = voice_data.begin();
+ TVoiceData::iterator end = voice_data.end();
+ int voice = 0;
+
+ while( curr != end )
+ {
+ UpdateVoice( voice, *curr );
+ ++curr;
+ ++voice;
+ }
+
+ ++mCurrTick;
+
+ if( mCurrTick > mTimeOfLastNote )
+ {
+ return false;
+ }
+
+ return true;
+ //return ( mCurrTick > mTimeOfLastNote ) ? false : true;
+}
+//---------------------------------------------------------
+void CrolPlayer::rewind( int subsong )
+{
+ TVoiceData::iterator curr = voice_data.begin();
+ TVoiceData::iterator end = voice_data.end();
+
+ while( curr != end )
+ {
+ CVoiceData &voice = *curr;
+
+ voice.Reset();
+ ++curr;
+ }
+
+ memset(bxRegister, 0, sizeof(bxRegister) );
+ memset(volumeCache, 0, sizeof(volumeCache) );
+
+ bdRegister = 0;
+
+ opl->init(); // initialize to melodic by default
+ opl->write(1,0x20); // Enable waveform select (bit 5)
+
+ if( rol_header->mode == 0 )
+ {
+ opl->write( 0xbd, 0x20 ); // select rhythm mode (bit 5)
+ bdRegister = 0x20;
+
+ SetFreq( kTomtomChannel, 24 );
+ SetFreq( kSnareDrumChannel, 31 );
+ }
+
+ mNextTempoEvent = 0;
+ mCurrTick = 0;
+
+ SetRefresh(1.0f);
+}
+//---------------------------------------------------------
+inline float fmin( int const a, int const b )
+{
+ return static_cast<float>( a < b ? a : b );
+}
+//---------------------------------------------------------
+void CrolPlayer::SetRefresh( float const multiplier )
+{
+ float const tickBeat = fmin(kMaxTickBeat, rol_header->ticks_per_beat);
+
+ mRefresh = (tickBeat*rol_header->basic_tempo*multiplier) / 60.0f;
+}
+//---------------------------------------------------------
+float CrolPlayer::getrefresh()
+{
+ return mRefresh;
+}
+//---------------------------------------------------------
+void CrolPlayer::UpdateVoice( int const voice, CVoiceData &voiceData )
+{
+ TNoteEvents const &nEvents = voiceData.note_events;
+
+ if( nEvents.empty() || voiceData.mEventStatus & CVoiceData::kES_NoteEnd )
+ {
+ return; // no note data to process, don't bother doing anything.
+ }
+
+ TInstrumentEvents &iEvents = voiceData.instrument_events;
+ TVolumeEvents &vEvents = voiceData.volume_events;
+ TPitchEvents &pEvents = voiceData.pitch_events;
+
+ if( !(voiceData.mEventStatus & CVoiceData::kES_InstrEnd ) &&
+ iEvents[voiceData.next_instrument_event].time == mCurrTick )
+ {
+ if( voiceData.next_instrument_event < iEvents.size() )
+ {
+ send_ins_data_to_chip( voice, iEvents[voiceData.next_instrument_event].ins_index );
+ ++voiceData.next_instrument_event;
+ }
+ else
+ {
+ voiceData.mEventStatus |= CVoiceData::kES_InstrEnd;
+ }
+ }
+
+ if( !(voiceData.mEventStatus & CVoiceData::kES_VolumeEnd ) &&
+ vEvents[voiceData.next_volume_event].time == mCurrTick )
+ {
+ SVolumeEvent const &volumeEvent = vEvents[voiceData.next_volume_event];
+
+ if( voiceData.next_volume_event < vEvents.size() )
+ {
+ int const volume = (int)(63.0f*(1.0f - volumeEvent.multiplier));
+
+ SetVolume( voice, volume );
+
+ ++voiceData.next_volume_event; // move to next volume event
+ }
+ else
+ {
+ voiceData.mEventStatus |= CVoiceData::kES_VolumeEnd;
+ }
+ }
+
+ if( voiceData.mForceNote || voiceData.current_note_duration > voiceData.mNoteDuration-1 )
+ {
+ if( mCurrTick != 0 )
+ {
+ ++voiceData.current_note;
+ }
+
+ if( voiceData.current_note < nEvents.size() )
+ {
+ SNoteEvent const &noteEvent = nEvents[voiceData.current_note];
+
+ SetNote( voice, noteEvent.number );
+ voiceData.current_note_duration = 0;
+ voiceData.mNoteDuration = noteEvent.duration;
+ voiceData.mForceNote = false;
+ }
+ else
+ {
+ SetNote( voice, kSilenceNote );
+ voiceData.mEventStatus |= CVoiceData::kES_NoteEnd;
+ return;
+ }
+ }
+
+ if( !(voiceData.mEventStatus & CVoiceData::kES_PitchEnd ) &&
+ pEvents[voiceData.next_pitch_event].time == mCurrTick )
+ {
+ if( voiceData.next_pitch_event < pEvents.size() )
+ {
+ SetPitch(voice,pEvents[voiceData.next_pitch_event].variation);
+ ++voiceData.next_pitch_event;
+ }
+ else
+ {
+ voiceData.mEventStatus |= CVoiceData::kES_PitchEnd;
+ }
+ }
+
+ ++voiceData.current_note_duration;
+}
+//---------------------------------------------------------
+void CrolPlayer::SetNote( int const voice, int const note )
+{
+ if( voice < kBassDrumChannel || rol_header->mode )
+ {
+ SetNoteMelodic( voice, note );
+ }
+ else
+ {
+ SetNotePercussive( voice, note );
+ }
+}
+//---------------------------------------------------------
+void CrolPlayer::SetNotePercussive( int const voice, int const note )
+{
+ int const bit_pos = 4-voice+kBassDrumChannel;
+
+ bdRegister &= ~( 1<<bit_pos );
+ opl->write( 0xbd, bdRegister );
+
+ if( note != kSilenceNote )
+ {
+ switch( voice )
+ {
+ case kTomtomChannel:
+ SetFreq( kSnareDrumChannel, note+7 );
+ case kBassDrumChannel:
+ SetFreq( voice, note );
+ break;
+ }
+
+ bdRegister |= 1<<bit_pos;
+ opl->write( 0xbd, bdRegister );
+ }
+}
+//---------------------------------------------------------
+void CrolPlayer::SetNoteMelodic( int const voice, int const note )
+{
+ opl->write( 0xb0+voice, bxRegister[voice] & ~0x20 );
+
+ if( note != kSilenceNote )
+ {
+ SetFreq( voice, note, true );
+ }
+}
+//---------------------------------------------------------
+void CrolPlayer::SetPitch(int const voice, real32 const variation)
+{
+ pitchCache[voice] = variation;
+ freqCache[voice] += (uint16)((((float)freqCache[voice])*(variation-1.0f)) / kPitchFactor);
+
+ opl->write(0xa0+voice,freqCache[voice] & 0xff);
+}
+//---------------------------------------------------------
+void CrolPlayer::SetFreq( int const voice, int const note, bool const keyOn )
+{
+ uint16 freq = kNoteTable[note%12] + ((note/12) << 10);
+ freq += (uint16)((((float)freq)*(pitchCache[voice]-1.0f))/kPitchFactor);
+
+ freqCache[voice] = freq;
+ bxRegister[voice] = ((freq >> 8) & 0x1f);
+
+ opl->write( 0xa0+voice, freq & 0xff );
+ opl->write( 0xb0+voice, bxRegister[voice] | (keyOn ? 0x20 : 0x0) );
+}
+//---------------------------------------------------------
+void CrolPlayer::SetVolume( int const voice, int const volume )
+{
+ volumeCache[voice] = (volumeCache[voice] &0xc0) | volume;
+
+ int const op_offset = ( voice < kSnareDrumChannel || rol_header->mode ) ?
+ op_table[voice]+3 : drum_table[voice-kSnareDrumChannel];
+
+ opl->write( 0x40+op_offset, volumeCache[voice] );
+}
+//---------------------------------------------------------
+void CrolPlayer::send_ins_data_to_chip( int const voice, int const ins_index )
+{
+ SRolInstrument &instrument = ins_list[ins_index].instrument;
+
+ send_operator( voice, instrument.modulator, instrument.carrier );
+}
+//---------------------------------------------------------
+void CrolPlayer::send_operator( int const voice, SOPL2Op const &modulator, SOPL2Op const &carrier )
+{
+ if( voice < kSnareDrumChannel || rol_header->mode )
+ {
+ int const op_offset = op_table[voice];
+
+ opl->write( 0x20+op_offset, modulator.ammulti );
+ opl->write( 0x40+op_offset, modulator.ksltl );
+ opl->write( 0x60+op_offset, modulator.ardr );
+ opl->write( 0x80+op_offset, modulator.slrr );
+ opl->write( 0xc0+voice , modulator.fbc );
+ opl->write( 0xe0+op_offset, modulator.waveform );
+
+ volumeCache[voice] = (carrier.ksltl & 0xc0) | volumeCache[voice] & 0x3f;
+
+ opl->write( 0x23+op_offset, carrier.ammulti );
+ opl->write( 0x43+op_offset, volumeCache[voice] );
+ opl->write( 0x63+op_offset, carrier.ardr );
+ opl->write( 0x83+op_offset, carrier.slrr );
+// opl->write( 0xc3+voice , carrier.fbc ); <- don't bother writing this.
+ opl->write( 0xe3+op_offset, carrier.waveform );
+ }
+ else
+ {
+ int const op_offset = drum_table[voice-kSnareDrumChannel];
+
+ volumeCache[voice] = (modulator.ksltl & 0xc0) | volumeCache[voice] & 0x3f;
+
+ opl->write( 0x20+op_offset, modulator.ammulti );
+ opl->write( 0x40+op_offset, volumeCache[voice] );
+ opl->write( 0x60+op_offset, modulator.ardr );
+ opl->write( 0x80+op_offset, modulator.slrr );
+ opl->write( 0xc0+voice , modulator.fbc );
+ opl->write( 0xe0+op_offset, modulator.waveform );
+ }
+}
+//---------------------------------------------------------
+void CrolPlayer::load_tempo_events( binistream *f )
+{
+ int16 const num_tempo_events = f->readInt( 2 );
+
+ mTempoEvents.reserve( num_tempo_events );
+
+ for(int i=0; i<num_tempo_events; ++i)
+ {
+ STempoEvent event;
+
+ event.time = f->readInt( 2 );
+ event.multiplier = f->readFloat( binio::Single );
+ mTempoEvents.push_back( event );
+ }
+}
+//---------------------------------------------------------
+bool CrolPlayer::load_voice_data( binistream *f, std::string const &bnk_filename, const CFileProvider &fp )
+{
+ SBnkHeader bnk_header;
+ binistream *bnk_file = fp.open( bnk_filename.c_str() );
+
+ if( bnk_file )
+ {
+ load_bnk_info( bnk_file, bnk_header );
+
+ int const numVoices = rol_header->mode ? kNumMelodicVoices : kNumPercussiveVoices;
+
+ voice_data.reserve( numVoices );
+ for(int i=0; i<numVoices; ++i)
+ {
+ CVoiceData voice;
+
+ load_note_events( f, voice );
+ load_instrument_events( f, voice, bnk_file, bnk_header );
+ load_volume_events( f, voice );
+ load_pitch_events( f, voice );
+
+ voice_data.push_back( voice );
+ }
+
+ fp.close(bnk_file);
+
+ return true;
+ }
+
+ return false;
+}
+//---------------------------------------------------------
+void CrolPlayer::load_note_events( binistream *f, CVoiceData &voice )
+{
+ f->seek( 15, binio::Add );
+
+ int16 const time_of_last_note = f->readInt( 2 );
+
+ if( time_of_last_note != 0 )
+ {
+ TNoteEvents &note_events = voice.note_events;
+ int16 total_duration = 0;
+
+ do
+ {
+ SNoteEvent event;
+
+ event.number = f->readInt( 2 );
+ event.duration = f->readInt( 2 );
+
+ event.number += kSilenceNote; // adding -12
+
+ note_events.push_back( event );
+
+ total_duration += event.duration;
+ } while( total_duration < time_of_last_note );
+
+ if( time_of_last_note > mTimeOfLastNote )
+ {
+ mTimeOfLastNote = time_of_last_note;
+ }
+ }
+
+ f->seek( 15, binio::Add );
+}
+//---------------------------------------------------------
+void CrolPlayer::load_instrument_events( binistream *f, CVoiceData &voice,
+ binistream *bnk_file, SBnkHeader const &bnk_header )
+{
+ int16 const number_of_instrument_events = f->readInt( 2 );
+
+ TInstrumentEvents &instrument_events = voice.instrument_events;
+
+ instrument_events.reserve( number_of_instrument_events );
+
+ for(int i=0; i<number_of_instrument_events; ++i)
+ {
+ SInstrumentEvent event;
+ event.time = f->readInt( 2 );
+ f->readString( event.name, 9 );
+
+ std::string event_name = event.name;
+ event.ins_index = load_rol_instrument( bnk_file, bnk_header, event_name );
+
+ instrument_events.push_back( event );
+
+ f->seek( 1+2, binio::Add );
+ }
+
+ f->seek( 15, binio::Add );
+}
+//---------------------------------------------------------
+void CrolPlayer::load_volume_events( binistream *f, CVoiceData &voice )
+{
+ int16 const number_of_volume_events = f->readInt( 2 );
+
+ TVolumeEvents &volume_events = voice.volume_events;
+
+ volume_events.reserve( number_of_volume_events );
+
+ for(int i=0; i<number_of_volume_events; ++i)
+ {
+ SVolumeEvent event;
+ event.time = f->readInt( 2 );
+ event.multiplier = f->readFloat( binio::Single );
+
+ volume_events.push_back( event );
+ }
+
+ f->seek( 15, binio::Add );
+}
+//---------------------------------------------------------
+void CrolPlayer::load_pitch_events( binistream *f, CVoiceData &voice )
+{
+ int16 const number_of_pitch_events = f->readInt( 2 );
+
+ TPitchEvents &pitch_events = voice.pitch_events;
+
+ pitch_events.reserve( number_of_pitch_events );
+
+ for(int i=0; i<number_of_pitch_events; ++i)
+ {
+ SPitchEvent event;
+ event.time = f->readInt( 2 );
+ event.variation = f->readFloat( binio::Single );
+
+ pitch_events.push_back( event );
+ }
+}
+//---------------------------------------------------------
+bool CrolPlayer::load_bnk_info( binistream *f, SBnkHeader &header )
+{
+ header.version_major = f->readInt(1);
+ header.version_minor = f->readInt(1);
+ f->readString( header.signature, 6 );
+
+ header.number_of_list_entries_used = f->readInt( 2 );
+ header.total_number_of_list_entries = f->readInt( 2 );
+
+ header.abs_offset_of_name_list = f->readInt( 4 );
+ header.abs_offset_of_data = f->readInt( 4 );
+
+ f->seek( header.abs_offset_of_name_list, binio::Set );
+
+ TInstrumentNames &ins_name_list = header.ins_name_list;
+ ins_name_list.reserve( header.number_of_list_entries_used );
+
+ for(int i=0; i<header.number_of_list_entries_used; ++i)
+ {
+ SInstrumentName instrument;
+
+ instrument.index = f->readInt( 2 );
+ instrument.record_used = f->readInt(1);
+ f->readString( instrument.name, 9 );
+
+ // printf("%s = #%d\n", instrument.name, i );
+
+ ins_name_list.push_back( instrument );
+ }
+
+ //std::sort( ins_name_list.begin(), ins_name_list.end(), StringCompare() );
+
+ return true;
+}
+//---------------------------------------------------------
+int CrolPlayer::load_rol_instrument( binistream *f, SBnkHeader const &header, std::string &name )
+{
+ TInstrumentNames const &ins_name_list = header.ins_name_list;
+
+ int const ins_index = get_ins_index( name );
+
+ if( ins_index != -1 )
+ {
+ return ins_index;
+ }
+
+ typedef TInstrumentNames::const_iterator TInsIter;
+ typedef std::pair<TInsIter, TInsIter> TInsIterPair;
+
+ TInsIterPair range = std::equal_range( ins_name_list.begin(),
+ ins_name_list.end(),
+ name,
+ StringCompare() );
+
+ if( range.first != range.second )
+ {
+ int const seekOffs = header.abs_offset_of_data + (range.first->index*kSizeofDataRecord);
+ f->seek( seekOffs, binio::Set );
+ }
+
+ SUsedList usedIns;
+ usedIns.name = name;
+
+ if( range.first != range.second )
+ {
+ read_rol_instrument( f, usedIns.instrument );
+ }
+ else
+ {
+ // set up default instrument data here
+ memset( &usedIns.instrument, 0, kSizeofDataRecord );
+ }
+ ins_list.push_back( usedIns );
+
+ return ins_list.size()-1;
+}
+//---------------------------------------------------------
+int CrolPlayer::get_ins_index( std::string const &name ) const
+{
+ for(unsigned int i=0; i<ins_list.size(); ++i)
+ {
+ if( stricmp(ins_list[i].name.c_str(), name.c_str()) == 0 )
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+//---------------------------------------------------------
+void CrolPlayer::read_rol_instrument( binistream *f, SRolInstrument &ins )
+{
+ ins.mode = f->readInt(1);
+ ins.voice_number = f->readInt(1);
+
+ read_fm_operator( f, ins.modulator );
+ read_fm_operator( f, ins.carrier );
+
+ ins.modulator.waveform = f->readInt(1);
+ ins.carrier.waveform = f->readInt(1);
+}
+//---------------------------------------------------------
+void CrolPlayer::read_fm_operator( binistream *f, SOPL2Op &opl2_op )
+{
+ SFMOperator fm_op;
+
+ fm_op.key_scale_level = f->readInt(1);
+ fm_op.freq_multiplier = f->readInt(1);
+ fm_op.feed_back = f->readInt(1);
+ fm_op.attack_rate = f->readInt(1);
+ fm_op.sustain_level = f->readInt(1);
+ fm_op.sustaining_sound = f->readInt(1);
+ fm_op.decay_rate = f->readInt(1);
+ fm_op.release_rate = f->readInt(1);
+ fm_op.output_level = f->readInt(1);
+ fm_op.amplitude_vibrato = f->readInt(1);
+ fm_op.frequency_vibrato = f->readInt(1);
+ fm_op.envelope_scaling = f->readInt(1);
+ fm_op.fm_type = f->readInt(1);
+
+ opl2_op.ammulti = fm_op.amplitude_vibrato << 7 | fm_op.frequency_vibrato << 6 | fm_op.sustaining_sound << 5 | fm_op.envelope_scaling << 4 | fm_op.freq_multiplier;
+ opl2_op.ksltl = fm_op.key_scale_level << 6 | fm_op.output_level;
+ opl2_op.ardr = fm_op.attack_rate << 4 | fm_op.decay_rate;
+ opl2_op.slrr = fm_op.sustain_level << 4 | fm_op.release_rate;
+ opl2_op.fbc = fm_op.feed_back << 1 | (fm_op.fm_type ^ 1);
+}
diff --git a/plugins/adplug/adplug/rol.h b/plugins/adplug/adplug/rol.h
new file mode 100644
index 00000000..fdb9fabb
--- /dev/null
+++ b/plugins/adplug/adplug/rol.h
@@ -0,0 +1,309 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2004 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * rol.h - ROL Player by OPLx <oplx@yahoo.com>
+ *
+ * Visit: http://tenacity.hispeed.com/aomit/oplx/
+ */
+#ifndef H_ROLPLAYER
+#define H_ROLPLAYER
+
+#include <vector>
+#include <string>
+#include <string.h>
+
+#include "player.h"
+
+class CrolPlayer: public CPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CrolPlayer(Copl *newopl);
+
+ ~CrolPlayer();
+
+ bool load (const std::string &filename, const CFileProvider &fp);
+ bool update ();
+ void rewind (int subsong); // rewinds to specified subsong
+ float getrefresh(); // returns needed timer refresh rate
+
+ std::string gettype() { return std::string("Adlib Visual Composer"); }
+
+private:
+ typedef unsigned short uint16;
+ typedef signed short int16;
+#ifdef __x86_64__
+ typedef signed int int32;
+#else
+ typedef signed long int int32;
+#endif
+ typedef float real32;
+
+ typedef struct
+ {
+ uint16 version_major;
+ uint16 version_minor;
+ char unused0[40];
+ uint16 ticks_per_beat;
+ uint16 beats_per_measure;
+ uint16 edit_scale_y;
+ uint16 edit_scale_x;
+ char unused1;
+ char mode;
+ char unused2[90];
+ char filler0[38];
+ char filler1[15];
+ real32 basic_tempo;
+ } SRolHeader;
+
+ typedef struct
+ {
+ int16 time;
+ real32 multiplier;
+ } STempoEvent;
+
+ typedef struct
+ {
+ int16 number;
+ int16 duration;
+ } SNoteEvent;
+
+ typedef struct
+ {
+ int16 time;
+ char name[9];
+ int16 ins_index;
+ } SInstrumentEvent;
+
+ typedef struct
+ {
+ int16 time;
+ real32 multiplier;
+ } SVolumeEvent;
+
+ typedef struct
+ {
+ int16 time;
+ real32 variation;
+ } SPitchEvent;
+
+ typedef std::vector<SNoteEvent> TNoteEvents;
+ typedef std::vector<SInstrumentEvent> TInstrumentEvents;
+ typedef std::vector<SVolumeEvent> TVolumeEvents;
+ typedef std::vector<SPitchEvent> TPitchEvents;
+
+#define bit_pos( pos ) (1<<pos)
+
+ class CVoiceData
+ {
+ public:
+ enum EEventStatus
+ {
+ kES_NoteEnd = bit_pos( 0 ),
+ kES_PitchEnd = bit_pos( 1 ),
+ kES_InstrEnd = bit_pos( 2 ),
+ kES_VolumeEnd = bit_pos( 3 ),
+
+ kES_None = 0
+ };
+
+ explicit CVoiceData()
+ : mForceNote ( true )
+ ,mEventStatus ( kES_None )
+ ,current_note ( 0 )
+ ,current_note_duration( 0 )
+ ,mNoteDuration ( 0 )
+ ,next_instrument_event( 0 )
+ ,next_volume_event ( 0 )
+ ,next_pitch_event ( 0 )
+ {
+ }
+
+ void Reset()
+ {
+ mForceNote = true;
+ mEventStatus = kES_None;
+ current_note = 0;
+ current_note_duration = 0;
+ mNoteDuration = 0;
+ next_instrument_event = 0;
+ next_volume_event = 0;
+ next_pitch_event = 0;
+ }
+
+ TNoteEvents note_events;
+ TInstrumentEvents instrument_events;
+ TVolumeEvents volume_events;
+ TPitchEvents pitch_events;
+
+ bool mForceNote : 1;
+ int mEventStatus;
+ unsigned int current_note;
+ int current_note_duration;
+ int mNoteDuration;
+ unsigned int next_instrument_event;
+ unsigned int next_volume_event;
+ unsigned int next_pitch_event;
+ };
+
+ typedef struct
+ {
+ uint16 index;
+ char record_used;
+ char name[9];
+ } SInstrumentName;
+
+ typedef std::vector<SInstrumentName> TInstrumentNames;
+
+ typedef struct
+ {
+ char version_major;
+ char version_minor;
+ char signature[6];
+ uint16 number_of_list_entries_used;
+ uint16 total_number_of_list_entries;
+ int32 abs_offset_of_name_list;
+ int32 abs_offset_of_data;
+
+ TInstrumentNames ins_name_list;
+ } SBnkHeader;
+
+ typedef struct
+ {
+ unsigned char key_scale_level;
+ unsigned char freq_multiplier;
+ unsigned char feed_back;
+ unsigned char attack_rate;
+ unsigned char sustain_level;
+ unsigned char sustaining_sound;
+ unsigned char decay_rate;
+ unsigned char release_rate;
+ unsigned char output_level;
+ unsigned char amplitude_vibrato;
+ unsigned char frequency_vibrato;
+ unsigned char envelope_scaling;
+ unsigned char fm_type;
+ } SFMOperator;
+
+ typedef struct
+ {
+ unsigned char ammulti;
+ unsigned char ksltl;
+ unsigned char ardr;
+ unsigned char slrr;
+ unsigned char fbc;
+ unsigned char waveform;
+ } SOPL2Op;
+
+ typedef struct
+ {
+ char mode;
+ char voice_number;
+ SOPL2Op modulator;
+ SOPL2Op carrier;
+ } SRolInstrument;
+
+ typedef struct
+ {
+ std::string name;
+ SRolInstrument instrument;
+ } SUsedList;
+
+ void load_tempo_events ( binistream *f );
+ bool load_voice_data ( binistream *f, std::string const &bnk_filename, const CFileProvider &fp );
+ void load_note_events ( binistream *f, CVoiceData &voice );
+ void load_instrument_events( binistream *f, CVoiceData &voice,
+ binistream *bnk_file, SBnkHeader const &bnk_header );
+ void load_volume_events ( binistream *f, CVoiceData &voice );
+ void load_pitch_events ( binistream *f, CVoiceData &voice );
+
+ bool load_bnk_info ( binistream *f, SBnkHeader &header );
+ int load_rol_instrument ( binistream *f, SBnkHeader const &header, std::string &name );
+ void read_rol_instrument ( binistream *f, SRolInstrument &ins );
+ void read_fm_operator ( binistream *f, SOPL2Op &opl2_op );
+ int get_ins_index( std::string const &name ) const;
+
+ void UpdateVoice( int const voice, CVoiceData &voiceData );
+ void SetNote( int const voice, int const note );
+ void SetNoteMelodic( int const voice, int const note );
+ void SetNotePercussive( int const voice, int const note );
+ void SetFreq ( int const voice, int const note, bool const keyOn=false );
+ void SetPitch ( int const voice, real32 const variation );
+ void SetVolume ( int const voice, int const volume );
+ void SetRefresh( float const multiplier );
+ void send_ins_data_to_chip( int const voice, int const ins_index );
+ void send_operator( int const voice, SOPL2Op const &modulator, SOPL2Op const &carrier );
+
+ class StringCompare
+ {
+ public:
+ bool operator()( SInstrumentName const &lhs, SInstrumentName const &rhs ) const
+ {
+ return keyLess(lhs.name, rhs.name);
+ }
+
+ bool operator()( SInstrumentName const &lhs, std::string const &rhs ) const
+ {
+ return keyLess(lhs.name, rhs.c_str());
+ }
+
+ bool operator()( std::string const &lhs, SInstrumentName const &rhs ) const
+ {
+ return keyLess(lhs.c_str(), rhs.name);
+ }
+ private:
+ bool keyLess( const char *const lhs, const char *const rhs ) const
+ {
+ return stricmp(lhs, rhs) < 0;
+ }
+ };
+
+ typedef std::vector<CVoiceData> TVoiceData;
+
+ SRolHeader *rol_header;
+ std::vector<STempoEvent> mTempoEvents;
+ TVoiceData voice_data;
+ std::vector<SUsedList> ins_list;
+
+ unsigned int mNextTempoEvent;
+ int mCurrTick;
+ int mTimeOfLastNote;
+ float mRefresh;
+ unsigned char bdRegister;
+ unsigned char bxRegister[9];
+ unsigned char volumeCache[11];
+ uint16 freqCache[11];
+ real32 pitchCache[11];
+
+ static int const kSizeofDataRecord;
+ static int const kMaxTickBeat;
+ static int const kSilenceNote;
+ static int const kNumMelodicVoices;
+ static int const kNumPercussiveVoices;
+ static int const kBassDrumChannel;
+ static int const kSnareDrumChannel;
+ static int const kTomtomChannel;
+ static int const kTomtomFreq;
+ static int const kSnareDrumFreq;
+ static float const kDefaultUpdateTme;
+ static float const kPitchFactor;
+ static uint16 const kNoteTable[12];
+};
+
+#endif
diff --git a/plugins/adplug/adplug/s3m.cpp b/plugins/adplug/adplug/s3m.cpp
new file mode 100644
index 00000000..ddc2a3b9
--- /dev/null
+++ b/plugins/adplug/adplug/s3m.cpp
@@ -0,0 +1,536 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * s3m.c - S3M Player by Simon Peter <dn.tlp@gmx.net>
+ *
+ * BUGS:
+ * Extra Fine Slides (EEx, FEx) & Fine Vibrato (Uxy) are inaccurate
+ */
+
+#include <string.h>
+#include "s3m.h"
+
+const char Cs3mPlayer::chnresolv[] = // S3M -> adlib channel conversion
+ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,-1,-1,-1,-1,-1,-1,-1};
+
+const unsigned short Cs3mPlayer::notetable[12] = // S3M adlib note table
+ {340,363,385,408,432,458,485,514,544,577,611,647};
+
+const unsigned char Cs3mPlayer::vibratotab[32] = // vibrato rate table
+ {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1};
+
+/*** public methods *************************************/
+
+CPlayer *Cs3mPlayer::factory(Copl *newopl)
+{
+ return new Cs3mPlayer(newopl);
+}
+
+Cs3mPlayer::Cs3mPlayer(Copl *newopl): CPlayer(newopl)
+{
+ int i,j,k;
+
+ memset(pattern,255,sizeof(pattern));
+ memset(orders,255,sizeof(orders));
+
+ for(i=0;i<99;i++) // setup pattern
+ for(j=0;j<64;j++)
+ for(k=0;k<32;k++) {
+ pattern[i][j][k].instrument = 0;
+ pattern[i][j][k].info = 0;
+ }
+}
+
+bool Cs3mPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ unsigned short insptr[99],pattptr[99];
+ int i,row;
+ unsigned char bufval,bufval2;
+ unsigned short ppatlen;
+ s3mheader *checkhead;
+ bool adlibins=false;
+
+ // file validation section
+ checkhead = new s3mheader;
+ load_header(f, checkhead);
+ if(checkhead->kennung != 0x1a || checkhead->typ != 16
+ || checkhead->insnum > 99) {
+ delete checkhead; fp.close(f); return false;
+ } else
+ if(strncmp(checkhead->scrm,"SCRM",4)) {
+ delete checkhead; fp.close(f); return false;
+ } else { // is an adlib module?
+ f->seek(checkhead->ordnum, binio::Add);
+ for(i = 0; i < checkhead->insnum; i++)
+ insptr[i] = f->readInt(2);
+ for(i=0;i<checkhead->insnum;i++) {
+ f->seek(insptr[i]*16);
+ if(f->readInt(1) >= 2) {
+ adlibins = true;
+ break;
+ }
+ }
+ delete checkhead;
+ if(!adlibins) { fp.close(f); return false; }
+ }
+
+ // load section
+ f->seek(0); // rewind for load
+ load_header(f, &header); // read header
+
+ // security check
+ if(header.ordnum > 256 || header.insnum > 99 || header.patnum > 99) {
+ fp.close(f);
+ return false;
+ }
+
+ for(i = 0; i < header.ordnum; i++) orders[i] = f->readInt(1); // read orders
+ for(i = 0; i < header.insnum; i++) insptr[i] = f->readInt(2); // instrument parapointers
+ for(i = 0; i < header.patnum; i++) pattptr[i] = f->readInt(2); // pattern parapointers
+
+ for(i=0;i<header.insnum;i++) { // load instruments
+ f->seek(insptr[i]*16);
+ inst[i].type = f->readInt(1);
+ f->readString(inst[i].filename, 15);
+ inst[i].d00 = f->readInt(1); inst[i].d01 = f->readInt(1);
+ inst[i].d02 = f->readInt(1); inst[i].d03 = f->readInt(1);
+ inst[i].d04 = f->readInt(1); inst[i].d05 = f->readInt(1);
+ inst[i].d06 = f->readInt(1); inst[i].d07 = f->readInt(1);
+ inst[i].d08 = f->readInt(1); inst[i].d09 = f->readInt(1);
+ inst[i].d0a = f->readInt(1); inst[i].d0b = f->readInt(1);
+ inst[i].volume = f->readInt(1); inst[i].dsk = f->readInt(1);
+ f->ignore(2);
+ inst[i].c2spd = f->readInt(4);
+ f->ignore(12);
+ f->readString(inst[i].name, 28);
+ f->readString(inst[i].scri, 4);
+ }
+
+ for(i=0;i<header.patnum;i++) { // depack patterns
+ f->seek(pattptr[i]*16);
+ ppatlen = f->readInt(2);
+ unsigned long pattpos = f->pos();
+ for(row=0;(row<64) && (pattpos-pattptr[i]*16<=ppatlen);row++)
+ do {
+ bufval = f->readInt(1);
+ if(bufval & 32) {
+ bufval2 = f->readInt(1);
+ pattern[i][row][bufval & 31].note = bufval2 & 15;
+ pattern[i][row][bufval & 31].oct = (bufval2 & 240) >> 4;
+ pattern[i][row][bufval & 31].instrument = f->readInt(1);
+ }
+ if(bufval & 64)
+ pattern[i][row][bufval & 31].volume = f->readInt(1);
+ if(bufval & 128) {
+ pattern[i][row][bufval & 31].command = f->readInt(1);
+ pattern[i][row][bufval & 31].info = f->readInt(1);
+ }
+ } while(bufval);
+ }
+
+ fp.close(f);
+ rewind(0);
+ return true; // done
+}
+
+bool Cs3mPlayer::update()
+{
+ unsigned char pattbreak=0,donote; // remember vars
+ unsigned char pattnr,chan,row,info; // cache vars
+ signed char realchan;
+
+ // effect handling (timer dependant)
+ for(realchan=0; realchan<9; realchan++) {
+ info = channel[realchan].info; // fill infobyte cache
+ switch(channel[realchan].fx) {
+ case 11:
+ case 12: if(channel[realchan].fx == 11) // dual command: H00 and Dxy
+ vibrato(realchan,channel[realchan].dualinfo);
+ else // dual command: G00 and Dxy
+ tone_portamento(realchan,channel[realchan].dualinfo);
+ case 4: if(info <= 0x0f) // volume slide down
+ if(channel[realchan].vol - info >= 0)
+ channel[realchan].vol -= info;
+ else
+ channel[realchan].vol = 0;
+ if((info & 0x0f) == 0) // volume slide up
+ if(channel[realchan].vol + (info >> 4) <= 63)
+ channel[realchan].vol += info >> 4;
+ else
+ channel[realchan].vol = 63;
+ setvolume(realchan);
+ break;
+ case 5: if(info == 0xf0 || info <= 0xe0) { // slide down
+ slide_down(realchan,info);
+ setfreq(realchan);
+ }
+ break;
+ case 6: if(info == 0xf0 || info <= 0xe0) { // slide up
+ slide_up(realchan,info);
+ setfreq(realchan);
+ }
+ break;
+ case 7: tone_portamento(realchan,channel[realchan].dualinfo); break; // tone portamento
+ case 8: vibrato(realchan,channel[realchan].dualinfo); break; // vibrato
+ case 10: channel[realchan].nextfreq = channel[realchan].freq; // arpeggio
+ channel[realchan].nextoct = channel[realchan].oct;
+ switch(channel[realchan].trigger) {
+ case 0: channel[realchan].freq = notetable[channel[realchan].note]; break;
+ case 1: if(channel[realchan].note + ((info & 0xf0) >> 4) < 12)
+ channel[realchan].freq = notetable[channel[realchan].note + ((info & 0xf0) >> 4)];
+ else {
+ channel[realchan].freq = notetable[channel[realchan].note + ((info & 0xf0) >> 4) - 12];
+ channel[realchan].oct++;
+ }
+ break;
+ case 2: if(channel[realchan].note + (info & 0x0f) < 12)
+ channel[realchan].freq = notetable[channel[realchan].note + (info & 0x0f)];
+ else {
+ channel[realchan].freq = notetable[channel[realchan].note + (info & 0x0f) - 12];
+ channel[realchan].oct++;
+ }
+ break;
+ }
+ if(channel[realchan].trigger < 2)
+ channel[realchan].trigger++;
+ else
+ channel[realchan].trigger = 0;
+ setfreq(realchan);
+ channel[realchan].freq = channel[realchan].nextfreq;
+ channel[realchan].oct = channel[realchan].nextoct;
+ break;
+ case 21: vibrato(realchan,(unsigned char) (info / 4)); break; // fine vibrato
+ }
+ }
+
+ if(del) { // speed compensation
+ del--;
+ return !songend;
+ }
+
+ // arrangement handling
+ pattnr = orders[ord];
+ if(pattnr == 0xff || ord > header.ordnum) { // "--" end of song
+ songend = 1; // set end-flag
+ ord = 0;
+ pattnr = orders[ord];
+ if(pattnr == 0xff)
+ return !songend;
+ }
+ if(pattnr == 0xfe) { // "++" skip marker
+ ord++; pattnr = orders[ord];
+ }
+
+ // play row
+ row = crow; // fill row cache
+ for(chan=0;chan<32;chan++) {
+ if(!(header.chanset[chan] & 128)) // resolve S3M -> AdLib channels
+ realchan = chnresolv[header.chanset[chan] & 127];
+ else
+ realchan = -1; // channel disabled
+ if(realchan != -1) { // channel playable?
+ // set channel values
+ donote = 0;
+ if(pattern[pattnr][row][chan].note < 14)
+ // tone portamento
+ if(pattern[pattnr][row][chan].command == 7 || pattern[pattnr][row][chan].command == 12) {
+ channel[realchan].nextfreq = notetable[pattern[pattnr][row][chan].note];
+ channel[realchan].nextoct = pattern[pattnr][row][chan].oct;
+ } else { // normal note
+ channel[realchan].note = pattern[pattnr][row][chan].note;
+ channel[realchan].freq = notetable[pattern[pattnr][row][chan].note];
+ channel[realchan].oct = pattern[pattnr][row][chan].oct;
+ channel[realchan].key = 1;
+ donote = 1;
+ }
+ if(pattern[pattnr][row][chan].note == 14) { // key off (is 14 here, cause note is only first 4 bits)
+ channel[realchan].key = 0;
+ setfreq(realchan);
+ }
+ if((channel[realchan].fx != 8 && channel[realchan].fx != 11) && // vibrato begins
+ (pattern[pattnr][row][chan].command == 8 || pattern[pattnr][row][chan].command == 11)) {
+ channel[realchan].nextfreq = channel[realchan].freq;
+ channel[realchan].nextoct = channel[realchan].oct;
+ }
+ if(pattern[pattnr][row][chan].note >= 14)
+ if((channel[realchan].fx == 8 || channel[realchan].fx == 11) && // vibrato ends
+ (pattern[pattnr][row][chan].command != 8 && pattern[pattnr][row][chan].command != 11)) {
+ channel[realchan].freq = channel[realchan].nextfreq;
+ channel[realchan].oct = channel[realchan].nextoct;
+ setfreq(realchan);
+ }
+ if(pattern[pattnr][row][chan].instrument) { // set instrument
+ channel[realchan].inst = pattern[pattnr][row][chan].instrument - 1;
+ if(inst[channel[realchan].inst].volume < 64)
+ channel[realchan].vol = inst[channel[realchan].inst].volume;
+ else
+ channel[realchan].vol = 63;
+ if(pattern[pattnr][row][chan].command != 7)
+ donote = 1;
+ }
+ if(pattern[pattnr][row][chan].volume != 255)
+ if(pattern[pattnr][row][chan].volume < 64) // set volume
+ channel[realchan].vol = pattern[pattnr][row][chan].volume;
+ else
+ channel[realchan].vol = 63;
+ channel[realchan].fx = pattern[pattnr][row][chan].command; // set command
+ if(pattern[pattnr][row][chan].info) // set infobyte
+ channel[realchan].info = pattern[pattnr][row][chan].info;
+
+ // some commands reset the infobyte memory
+ switch(channel[realchan].fx) {
+ case 1:
+ case 2:
+ case 3:
+ case 20:
+ channel[realchan].info = pattern[pattnr][row][chan].info;
+ break;
+ }
+
+ // play note
+ if(donote)
+ playnote(realchan);
+ if(pattern[pattnr][row][chan].volume != 255) // set volume
+ setvolume(realchan);
+
+ // command handling (row dependant)
+ info = channel[realchan].info; // fill infobyte cache
+ switch(channel[realchan].fx) {
+ case 1: speed = info; break; // set speed
+ case 2: if(info <= ord) songend = 1; ord = info; crow = 0; pattbreak = 1; break; // jump to order
+ case 3: if(!pattbreak) { crow = info; ord++; pattbreak = 1; } break; // pattern break
+ case 4: if(info > 0xf0) // fine volume down
+ if(channel[realchan].vol - (info & 0x0f) >= 0)
+ channel[realchan].vol -= info & 0x0f;
+ else
+ channel[realchan].vol = 0;
+ if((info & 0x0f) == 0x0f && info >= 0x1f) // fine volume up
+ if(channel[realchan].vol + ((info & 0xf0) >> 4) <= 63)
+ channel[realchan].vol += (info & 0xf0) >> 4;
+ else
+ channel[realchan].vol = 63;
+ setvolume(realchan);
+ break;
+ case 5: if(info > 0xf0) { // fine slide down
+ slide_down(realchan,(unsigned char) (info & 0x0f));
+ setfreq(realchan);
+ }
+ if(info > 0xe0 && info < 0xf0) { // extra fine slide down
+ slide_down(realchan,(unsigned char) ((info & 0x0f) / 4));
+ setfreq(realchan);
+ }
+ break;
+ case 6: if(info > 0xf0) { // fine slide up
+ slide_up(realchan,(unsigned char) (info & 0x0f));
+ setfreq(realchan);
+ }
+ if(info > 0xe0 && info < 0xf0) { // extra fine slide up
+ slide_up(realchan,(unsigned char) ((info & 0x0f) / 4));
+ setfreq(realchan);
+ }
+ break;
+ case 7: // tone portamento
+ case 8: if((channel[realchan].fx == 7 || // vibrato (remember info for dual commands)
+ channel[realchan].fx == 8) && pattern[pattnr][row][chan].info)
+ channel[realchan].dualinfo = info;
+ break;
+ case 10: channel[realchan].trigger = 0; break; // arpeggio (set trigger)
+ case 19: if(info == 0xb0) // set loop start
+ loopstart = row;
+ if(info > 0xb0 && info <= 0xbf) // pattern loop
+ if(!loopcnt) {
+ loopcnt = info & 0x0f;
+ crow = loopstart;
+ pattbreak = 1;
+ } else
+ if(--loopcnt > 0) {
+ crow = loopstart;
+ pattbreak = 1;
+ }
+ if((info & 0xf0) == 0xe0) // patterndelay
+ del = speed * (info & 0x0f) - 1;
+ break;
+ case 20: tempo = info; break; // set tempo
+ }
+ }
+ }
+
+ if(!del)
+ del = speed - 1; // speed compensation
+ if(!pattbreak) { // next row (only if no manual advance)
+ crow++;
+ if(crow > 63) {
+ crow = 0;
+ ord++;
+ loopstart = 0;
+ }
+ }
+
+ return !songend; // still playing
+}
+
+void Cs3mPlayer::rewind(int subsong)
+{
+ // set basic variables
+ songend = 0; ord = 0; crow = 0; tempo = header.it;
+ speed = header.is; del = 0; loopstart = 0; loopcnt = 0;
+
+ memset(channel,0,sizeof(channel));
+
+ opl->init(); // reset OPL chip
+ opl->write(1,32); // Go to ym3812 mode
+}
+
+std::string Cs3mPlayer::gettype()
+{
+ char filever[5];
+
+ switch(header.cwtv) { // determine version number
+ case 0x1300: strcpy(filever,"3.00"); break;
+ case 0x1301: strcpy(filever,"3.01"); break;
+ case 0x1303: strcpy(filever,"3.03"); break;
+ case 0x1320: strcpy(filever,"3.20"); break;
+ default: strcpy(filever,"3.??");
+ }
+
+ return (std::string("Scream Tracker ") + filever);
+}
+
+float Cs3mPlayer::getrefresh()
+{
+ return (float) (tempo / 2.5);
+}
+
+/*** private methods *************************************/
+
+void Cs3mPlayer::load_header(binistream *f, s3mheader *h)
+{
+ int i;
+
+ f->readString(h->name, 28);
+ h->kennung = f->readInt(1); h->typ = f->readInt(1);
+ f->ignore(2);
+ h->ordnum = f->readInt(2); h->insnum = f->readInt(2);
+ h->patnum = f->readInt(2); h->flags = f->readInt(2);
+ h->cwtv = f->readInt(2); h->ffi = f->readInt(2);
+ f->readString(h->scrm, 4);
+ h->gv = f->readInt(1); h->is = f->readInt(1); h->it = f->readInt(1);
+ h->mv = f->readInt(1); h->uc = f->readInt(1); h->dp = f->readInt(1);
+ f->ignore(8);
+ h->special = f->readInt(2);
+ for(i = 0; i < 32; i++) h->chanset[i] = f->readInt(1);
+}
+
+void Cs3mPlayer::setvolume(unsigned char chan)
+{
+ unsigned char op = op_table[chan], insnr = channel[chan].inst;
+
+ opl->write(0x43 + op,(int)(63-((63-(inst[insnr].d03 & 63))/63.0)*channel[chan].vol) + (inst[insnr].d03 & 192));
+ if(inst[insnr].d0a & 1)
+ opl->write(0x40 + op,(int)(63-((63-(inst[insnr].d02 & 63))/63.0)*channel[chan].vol) + (inst[insnr].d02 & 192));
+}
+
+void Cs3mPlayer::setfreq(unsigned char chan)
+{
+ opl->write(0xa0 + chan, channel[chan].freq & 255);
+ if(channel[chan].key)
+ opl->write(0xb0 + chan, ((channel[chan].freq & 768) >> 8) + (channel[chan].oct << 2) | 32);
+ else
+ opl->write(0xb0 + chan, ((channel[chan].freq & 768) >> 8) + (channel[chan].oct << 2));
+}
+
+void Cs3mPlayer::playnote(unsigned char chan)
+{
+ unsigned char op = op_table[chan], insnr = channel[chan].inst;
+
+ opl->write(0xb0 + chan, 0); // stop old note
+
+ // set instrument data
+ opl->write(0x20 + op, inst[insnr].d00);
+ opl->write(0x23 + op, inst[insnr].d01);
+ opl->write(0x40 + op, inst[insnr].d02);
+ opl->write(0x43 + op, inst[insnr].d03);
+ opl->write(0x60 + op, inst[insnr].d04);
+ opl->write(0x63 + op, inst[insnr].d05);
+ opl->write(0x80 + op, inst[insnr].d06);
+ opl->write(0x83 + op, inst[insnr].d07);
+ opl->write(0xe0 + op, inst[insnr].d08);
+ opl->write(0xe3 + op, inst[insnr].d09);
+ opl->write(0xc0 + chan, inst[insnr].d0a);
+
+ // set frequency & play
+ channel[chan].key = 1;
+ setfreq(chan);
+}
+
+void Cs3mPlayer::slide_down(unsigned char chan, unsigned char amount)
+{
+ if(channel[chan].freq - amount > 340)
+ channel[chan].freq -= amount;
+ else
+ if(channel[chan].oct > 0) {
+ channel[chan].oct--;
+ channel[chan].freq = 684;
+ } else
+ channel[chan].freq = 340;
+}
+
+void Cs3mPlayer::slide_up(unsigned char chan, unsigned char amount)
+{
+ if(channel[chan].freq + amount < 686)
+ channel[chan].freq += amount;
+ else
+ if(channel[chan].oct < 7) {
+ channel[chan].oct++;
+ channel[chan].freq = 341;
+ } else
+ channel[chan].freq = 686;
+}
+
+void Cs3mPlayer::vibrato(unsigned char chan, unsigned char info)
+{
+ unsigned char i,speed,depth;
+
+ speed = info >> 4;
+ depth = (info & 0x0f) / 2;
+
+ for(i=0;i<speed;i++) {
+ channel[chan].trigger++;
+ while(channel[chan].trigger >= 64)
+ channel[chan].trigger -= 64;
+ if(channel[chan].trigger >= 16 && channel[chan].trigger < 48)
+ slide_down(chan,(unsigned char) (vibratotab[channel[chan].trigger - 16] / (16-depth)));
+ if(channel[chan].trigger < 16)
+ slide_up(chan,(unsigned char) (vibratotab[channel[chan].trigger + 16] / (16-depth)));
+ if(channel[chan].trigger >= 48)
+ slide_up(chan,(unsigned char) (vibratotab[channel[chan].trigger - 48] / (16-depth)));
+ }
+ setfreq(chan);
+}
+
+void Cs3mPlayer::tone_portamento(unsigned char chan, unsigned char info)
+{
+ if(channel[chan].freq + (channel[chan].oct << 10) < channel[chan].nextfreq +
+ (channel[chan].nextoct << 10))
+ slide_up(chan,info);
+ if(channel[chan].freq + (channel[chan].oct << 10) > channel[chan].nextfreq +
+ (channel[chan].nextoct << 10))
+ slide_down(chan,info);
+ setfreq(chan);
+}
diff --git a/plugins/adplug/adplug/s3m.h b/plugins/adplug/adplug/s3m.h
new file mode 100644
index 00000000..aefc492e
--- /dev/null
+++ b/plugins/adplug/adplug/s3m.h
@@ -0,0 +1,107 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * s3m.h - AdLib S3M Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_S3M
+#define H_ADPLUG_S3M
+
+#include "player.h"
+
+class Cs3mPlayer: public CPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ Cs3mPlayer(Copl *newopl);
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype();
+ std::string gettitle()
+ { return std::string(header.name); };
+
+ unsigned int getpatterns()
+ { return header.patnum; };
+ unsigned int getpattern()
+ { return orders[ord]; };
+ unsigned int getorders()
+ { return (header.ordnum-1); };
+ unsigned int getorder()
+ { return ord; };
+ unsigned int getrow()
+ { return crow; };
+ unsigned int getspeed()
+ { return speed; };
+ unsigned int getinstruments()
+ { return header.insnum; };
+ std::string getinstrument(unsigned int n)
+ { return std::string(inst[n].name); };
+
+ protected:
+ struct s3mheader {
+ char name[28]; // song name
+ unsigned char kennung,typ,dummy[2];
+ unsigned short ordnum,insnum,patnum,flags,cwtv,ffi;
+ char scrm[4];
+ unsigned char gv,is,it,mv,uc,dp,dummy2[8];
+ unsigned short special;
+ unsigned char chanset[32];
+ };
+
+ struct s3minst {
+ unsigned char type;
+ char filename[15];
+ unsigned char d00,d01,d02,d03,d04,d05,d06,d07,d08,d09,d0a,d0b,volume,dsk,dummy[2];
+ unsigned long c2spd;
+ char dummy2[12], name[28],scri[4];
+ } inst[99];
+
+ struct {
+ unsigned char note,oct,instrument,volume,command,info;
+ } pattern[99][64][32];
+
+ struct {
+ unsigned short freq,nextfreq;
+ unsigned char oct,vol,inst,fx,info,dualinfo,key,nextoct,trigger,note;
+ } channel[9];
+
+ s3mheader header;
+ unsigned char orders[256];
+ unsigned char crow,ord,speed,tempo,del,songend,loopstart,loopcnt;
+
+ private:
+ static const char chnresolv[];
+ static const unsigned short notetable[12];
+ static const unsigned char vibratotab[32];
+
+ void load_header(binistream *f, s3mheader *h);
+ void setvolume(unsigned char chan);
+ void setfreq(unsigned char chan);
+ void playnote(unsigned char chan);
+ void slide_down(unsigned char chan, unsigned char amount);
+ void slide_up(unsigned char chan, unsigned char amount);
+ void vibrato(unsigned char chan, unsigned char info);
+ void tone_portamento(unsigned char chan, unsigned char info);
+};
+
+#endif
diff --git a/plugins/adplug/adplug/sa2.cpp b/plugins/adplug/adplug/sa2.cpp
new file mode 100644
index 00000000..2e1248bc
--- /dev/null
+++ b/plugins/adplug/adplug/sa2.cpp
@@ -0,0 +1,262 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * sa2.cpp - SAdT2 Loader by Simon Peter <dn.tlp@gmx.net>
+ * SAdT Loader by Mamiya <mamiya@users.sourceforge.net>
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include "sa2.h"
+#include "debug.h"
+
+CPlayer *Csa2Loader::factory(Copl *newopl)
+{
+ return new Csa2Loader(newopl);
+}
+
+bool Csa2Loader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ struct {
+ unsigned char data[11],arpstart,arpspeed,arppos,arpspdcnt;
+ } insts;
+ unsigned char buf;
+ int i,j, k, notedis = 0;
+ const unsigned char convfx[16] = {0,1,2,3,4,5,6,255,8,255,10,11,12,13,255,15};
+ unsigned char sat_type;
+ enum SAT_TYPE {
+ HAS_ARPEGIOLIST = (1 << 7),
+ HAS_V7PATTERNS = (1 << 6),
+ HAS_ACTIVECHANNELS = (1 << 5),
+ HAS_TRACKORDER = (1 << 4),
+ HAS_ARPEGIO = (1 << 3),
+ HAS_OLDBPM = (1 << 2),
+ HAS_OLDPATTERNS = (1 << 1),
+ HAS_UNKNOWN127 = (1 << 0)
+ };
+
+ // read header
+ f->readString(header.sadt, 4);
+ header.version = f->readInt(1);
+
+ // file validation section
+ if(strncmp(header.sadt,"SAdT",4)) { fp.close(f); return false; }
+ switch(header.version) {
+ case 1:
+ notedis = +0x18;
+ sat_type = HAS_UNKNOWN127 | HAS_OLDPATTERNS | HAS_OLDBPM;
+ break;
+ case 2:
+ notedis = +0x18;
+ sat_type = HAS_OLDPATTERNS | HAS_OLDBPM;
+ break;
+ case 3:
+ notedis = +0x0c;
+ sat_type = HAS_OLDPATTERNS | HAS_OLDBPM;
+ break;
+ case 4:
+ notedis = +0x0c;
+ sat_type = HAS_ARPEGIO | HAS_OLDPATTERNS | HAS_OLDBPM;
+ break;
+ case 5:
+ notedis = +0x0c;
+ sat_type = HAS_ARPEGIO | HAS_ARPEGIOLIST | HAS_OLDPATTERNS | HAS_OLDBPM;
+ break;
+ case 6:
+ sat_type = HAS_ARPEGIO | HAS_ARPEGIOLIST | HAS_OLDPATTERNS | HAS_OLDBPM;
+ break;
+ case 7:
+ sat_type = HAS_ARPEGIO | HAS_ARPEGIOLIST | HAS_V7PATTERNS;
+ break;
+ case 8:
+ sat_type = HAS_ARPEGIO | HAS_ARPEGIOLIST | HAS_TRACKORDER;
+ break;
+ case 9:
+ sat_type = HAS_ARPEGIO | HAS_ARPEGIOLIST | HAS_TRACKORDER | HAS_ACTIVECHANNELS;
+ break;
+ default: /* unknown */
+ fp.close(f);
+ return false;
+ }
+
+ // load section
+ // instruments
+ for(i = 0; i < 31; i++) {
+ if(sat_type & HAS_ARPEGIO) {
+ for(j = 0; j < 11; j++) insts.data[j] = f->readInt(1);
+ insts.arpstart = f->readInt(1);
+ insts.arpspeed = f->readInt(1);
+ insts.arppos = f->readInt(1);
+ insts.arpspdcnt = f->readInt(1);
+ inst[i].arpstart = insts.arpstart;
+ inst[i].arpspeed = insts.arpspeed;
+ inst[i].arppos = insts.arppos;
+ inst[i].arpspdcnt = insts.arpspdcnt;
+ } else {
+ for(j = 0; j < 11; j++) insts.data[j] = f->readInt(1);
+ inst[i].arpstart = 0;
+ inst[i].arpspeed = 0;
+ inst[i].arppos = 0;
+ inst[i].arpspdcnt = 0;
+ }
+ for(j=0;j<11;j++)
+ inst[i].data[j] = insts.data[j];
+ inst[i].misc = 0;
+ inst[i].slide = 0;
+ }
+
+ // instrument names
+ for(i = 0; i < 29; i++) f->readString(instname[i], 17);
+
+ f->ignore(3); // dummy bytes
+ for(i = 0; i < 128; i++) order[i] = f->readInt(1); // pattern orders
+ if(sat_type & HAS_UNKNOWN127) f->ignore(127);
+
+ // infos
+ nop = f->readInt(2); length = f->readInt(1); restartpos = f->readInt(1);
+
+ // bpm
+ bpm = f->readInt(2);
+ if(sat_type & HAS_OLDBPM) {
+ bpm = bpm * 125 / 50; // cps -> bpm
+ }
+
+ if(sat_type & HAS_ARPEGIOLIST) {
+ init_specialarp();
+ for(i = 0; i < 256; i++) arplist[i] = f->readInt(1); // arpeggio list
+ for(i = 0; i < 256; i++) arpcmd[i] = f->readInt(1); // arpeggio commands
+ }
+
+ for(i=0;i<64;i++) { // track orders
+ for(j=0;j<9;j++) {
+ if(sat_type & HAS_TRACKORDER)
+ trackord[i][j] = f->readInt(1);
+ else
+ {
+ trackord[i][j] = i * 9 + j;
+ }
+ }
+ }
+
+ if(sat_type & HAS_ACTIVECHANNELS)
+ activechan = f->readInt(2) << 16; // active channels
+
+ AdPlug_LogWrite("Csa2Loader::load(\"%s\"): sat_type = %x, nop = %d, "
+ "length = %d, restartpos = %d, activechan = %x, bpm = %d\n",
+ filename.c_str(), sat_type, nop, length, restartpos, activechan, bpm);
+
+ // track data
+ if(sat_type & HAS_OLDPATTERNS) {
+ i = 0;
+ while(!f->ateof()) {
+ for(j=0;j<64;j++) {
+ for(k=0;k<9;k++) {
+ buf = f->readInt(1);
+ tracks[i+k][j].note = buf ? (buf + notedis) : 0;
+ tracks[i+k][j].inst = f->readInt(1);
+ tracks[i+k][j].command = convfx[f->readInt(1) & 0xf];
+ tracks[i+k][j].param1 = f->readInt(1);
+ tracks[i+k][j].param2 = f->readInt(1);
+ }
+ }
+ i+=9;
+ }
+ } else
+ if(sat_type & HAS_V7PATTERNS) {
+ i = 0;
+ while(!f->ateof()) {
+ for(j=0;j<64;j++) {
+ for(k=0;k<9;k++) {
+ buf = f->readInt(1);
+ tracks[i+k][j].note = buf >> 1;
+ tracks[i+k][j].inst = (buf & 1) << 4;
+ buf = f->readInt(1);
+ tracks[i+k][j].inst += buf >> 4;
+ tracks[i+k][j].command = convfx[buf & 0x0f];
+ buf = f->readInt(1);
+ tracks[i+k][j].param1 = buf >> 4;
+ tracks[i+k][j].param2 = buf & 0x0f;
+ }
+ }
+ i+=9;
+ }
+ } else {
+ i = 0;
+ while(!f->ateof()) {
+ for(j=0;j<64;j++) {
+ buf = f->readInt(1);
+ tracks[i][j].note = buf >> 1;
+ tracks[i][j].inst = (buf & 1) << 4;
+ buf = f->readInt(1);
+ tracks[i][j].inst += buf >> 4;
+ tracks[i][j].command = convfx[buf & 0x0f];
+ buf = f->readInt(1);
+ tracks[i][j].param1 = buf >> 4;
+ tracks[i][j].param2 = buf & 0x0f;
+ }
+ i++;
+ }
+ }
+ fp.close(f);
+
+ // fix instrument names
+ for(i=0;i<29;i++)
+ for(j=0;j<17;j++)
+ if(!instname[i][j])
+ instname[i][j] = ' ';
+
+ rewind(0); // rewind module
+ return true;
+}
+
+std::string Csa2Loader::gettype()
+{
+ char tmpstr[40];
+
+ sprintf(tmpstr,"Surprise! Adlib Tracker 2 (version %d)",header.version);
+ return std::string(tmpstr);
+}
+
+std::string Csa2Loader::gettitle()
+{
+ char bufinst[29*17],buf[18];
+ int i,ptr;
+
+ // parse instrument names for song name
+ memset(bufinst,'\0',29*17);
+ for(i=0;i<29;i++) {
+ buf[16] = ' '; buf[17] = '\0';
+ memcpy(buf,instname[i]+1,16);
+ for(ptr=16;ptr>0;ptr--)
+ if(buf[ptr] == ' ')
+ buf[ptr] = '\0';
+ else {
+ if(ptr<16)
+ buf[ptr+1] = ' ';
+ break;
+ }
+ strcat(bufinst,buf);
+ }
+
+ if(strchr(bufinst,'"'))
+ return std::string(bufinst,strchr(bufinst,'"')-bufinst+1,strrchr(bufinst,'"')-strchr(bufinst,'"')-1);
+ else
+ return std::string();
+}
diff --git a/plugins/adplug/adplug/sa2.h b/plugins/adplug/adplug/sa2.h
new file mode 100644
index 00000000..ed904f0d
--- /dev/null
+++ b/plugins/adplug/adplug/sa2.h
@@ -0,0 +1,55 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * sa2.h - SAdT2 Loader by Simon Peter <dn.tlp@gmx.net>
+ * SAdT Loader by Mamiya <mamiya@users.sourceforge.net>
+ */
+
+#include "protrack.h"
+
+class Csa2Loader: public CmodPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ Csa2Loader(Copl *newopl)
+ : CmodPlayer(newopl)
+ { }
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+
+ std::string gettype();
+ std::string gettitle();
+ unsigned int getinstruments()
+ { return 31; }
+ std::string getinstrument(unsigned int n)
+ {
+ if(n < 29)
+ return std::string(instname[n],1,16);
+ else
+ return std::string("-broken-");
+ }
+
+private:
+ struct sa2header {
+ char sadt[4];
+ unsigned char version;
+ } header;
+
+ char instname[29][17];
+};
diff --git a/plugins/adplug/adplug/silentopl.h b/plugins/adplug/adplug/silentopl.h
new file mode 100644
index 00000000..352754ab
--- /dev/null
+++ b/plugins/adplug/adplug/silentopl.h
@@ -0,0 +1,29 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * silentopl.h - Silent OPL device, by Simon Peter (dn.tlp@gmx.net)
+ */
+
+#include "opl.h"
+
+class CSilentopl: public Copl
+{
+public:
+ void write(int reg, int val) {}
+ void init() {}
+};
diff --git a/plugins/adplug/adplug/sng.cpp b/plugins/adplug/adplug/sng.cpp
new file mode 100644
index 00000000..bf0fa699
--- /dev/null
+++ b/plugins/adplug/adplug/sng.cpp
@@ -0,0 +1,85 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2002 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * sng.cpp - SNG Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string.h>
+#include "sng.h"
+
+CPlayer *CsngPlayer::factory(Copl *newopl)
+{
+ return new CsngPlayer(newopl);
+}
+
+bool CsngPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ int i;
+
+ // load header
+ f->readString(header.id, 4);
+ header.length = f->readInt(2); header.start = f->readInt(2);
+ header.loop = f->readInt(2); header.delay = f->readInt(1);
+ header.compressed = f->readInt(1) ? true : false;
+
+ // file validation section
+ if(strncmp(header.id,"ObsM",4)) { fp.close(f); return false; }
+
+ // load section
+ header.length /= 2; header.start /= 2; header.loop /= 2;
+ data = new Sdata [header.length];
+ for(i = 0; i < header.length; i++) {
+ data[i].val = f->readInt(1);
+ data[i].reg = f->readInt(1);
+ }
+
+ rewind(0);
+ fp.close(f);
+ return true;
+}
+
+bool CsngPlayer::update()
+{
+ if(header.compressed && del) {
+ del--;
+ return !songend;
+ }
+
+ while(data[pos].reg) {
+ opl->write(data[pos].reg, data[pos].val);
+ pos++;
+ if(pos >= header.length) {
+ songend = true;
+ pos = header.loop;
+ }
+ }
+
+ if(!header.compressed)
+ opl->write(data[pos].reg, data[pos].val);
+
+ if(data[pos].val) del = data[pos].val - 1; pos++;
+ if(pos >= header.length) { songend = true; pos = header.loop; }
+ return !songend;
+}
+
+void CsngPlayer::rewind(int subsong)
+{
+ pos = header.start; del = header.delay; songend = false;
+ opl->init(); opl->write(1,32); // go to OPL2 mode
+}
diff --git a/plugins/adplug/adplug/sng.h b/plugins/adplug/adplug/sng.h
new file mode 100644
index 00000000..6eef98de
--- /dev/null
+++ b/plugins/adplug/adplug/sng.h
@@ -0,0 +1,64 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2002 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * sng.h - SNG Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_SNGPLAYER
+#define H_ADPLUG_SNGPLAYER
+
+#include "player.h"
+
+class CsngPlayer: public CPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CsngPlayer(Copl *newopl)
+ : CPlayer(newopl), data(0)
+ { };
+ ~CsngPlayer()
+ { if(data) delete [] data; };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh()
+ { return 70.0f; };
+
+ std::string gettype()
+ { return std::string("SNG File Format"); };
+
+protected:
+ struct {
+ char id[4];
+ unsigned short length,start,loop;
+ unsigned char delay;
+ bool compressed;
+ } header;
+
+ struct Sdata {
+ unsigned char val,reg;
+ } *data;
+
+ unsigned char del;
+ unsigned short pos;
+ bool songend;
+};
+
+#endif
diff --git a/plugins/adplug/adplug/temuopl.cpp b/plugins/adplug/adplug/temuopl.cpp
new file mode 100644
index 00000000..13691d78
--- /dev/null
+++ b/plugins/adplug/adplug/temuopl.cpp
@@ -0,0 +1,75 @@
+/*
+ * AdPlug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2004 Simon Peter <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * temuopl.cpp - Tatsuyuki Satoh's OPL2 emulator, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "temuopl.h"
+
+CTemuopl::CTemuopl(int rate, bool bit16, bool usestereo)
+ : use16bit(bit16), stereo(usestereo)
+{
+ opl = OPLCreate(OPL_TYPE_YM3812, 3579545, rate);
+}
+
+CTemuopl::~CTemuopl()
+{
+ OPLDestroy(opl);
+}
+
+void CTemuopl::update(short *buf, int samples)
+{
+ int i;
+
+ if(use16bit) {
+ YM3812UpdateOne(opl,buf,samples);
+
+ if(stereo)
+ for(i=samples-1;i>=0;i--) {
+ buf[i*2] = buf[i];
+ buf[i*2+1] = buf[i];
+ }
+ } else {
+ short *tempbuf = new short[stereo ? samples*2 : samples];
+ int i;
+
+ YM3812UpdateOne(opl,tempbuf,samples);
+
+ if(stereo)
+ for(i=samples-1;i>=0;i--) {
+ tempbuf[i*2] = tempbuf[i];
+ tempbuf[i*2+1] = tempbuf[i];
+ }
+
+ for(i=0;i<(stereo ? samples*2 : samples);i++)
+ ((char *)buf)[i] = (tempbuf[i] >> 8) ^ 0x80;
+
+ delete [] tempbuf;
+ }
+}
+
+void CTemuopl::write(int reg, int val)
+{
+ OPLWrite(opl,0,reg);
+ OPLWrite(opl,1,val);
+}
+
+void CTemuopl::init()
+{
+ OPLResetChip(opl);
+}
diff --git a/plugins/adplug/adplug/temuopl.h b/plugins/adplug/adplug/temuopl.h
new file mode 100644
index 00000000..564fe3d8
--- /dev/null
+++ b/plugins/adplug/adplug/temuopl.h
@@ -0,0 +1,47 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2004 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * temuopl.h - Tatsuyuki Satoh's OPL2 emulator, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_TEMUOPL
+#define H_ADPLUG_TEMUOPL
+
+#include "opl.h"
+extern "C" {
+#include "fmopl.h"
+}
+
+class CTemuopl: public Copl
+{
+ public:
+ CTemuopl(int rate, bool bit16, bool usestereo); // rate = sample rate
+ virtual ~CTemuopl();
+
+ void update(short *buf, int samples); // fill buffer
+
+ // template methods
+ void write(int reg, int val);
+ void init();
+
+ private:
+ bool use16bit,stereo;
+ FM_OPL *opl; // holds emulator data
+};
+
+#endif
diff --git a/plugins/adplug/adplug/u6m.cpp b/plugins/adplug/adplug/u6m.cpp
new file mode 100644
index 00000000..5c0a2ca2
--- /dev/null
+++ b/plugins/adplug/adplug/u6m.cpp
@@ -0,0 +1,934 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * u6m.cpp - Ultima 6 Music Player by Marc Winterrowd.
+ * This code extends the Adlib Winamp plug-in by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "u6m.h"
+
+// Makes security checks on output buffer before writing
+#define SAVE_OUTPUT_ROOT(c, d, p) \
+if(p < d.size) \
+ output_root(c, d.data, p); \
+else \
+ return false;
+
+CPlayer *Cu6mPlayer::factory(Copl *newopl)
+{
+ return new Cu6mPlayer(newopl);
+}
+
+bool Cu6mPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ // file validation section
+ // this section only checks a few *necessary* conditions
+ unsigned long filesize, decompressed_filesize;
+ binistream *f;
+
+ f = fp.open(filename); if(!f) return false;
+ filesize = fp.filesize(f);
+
+ if (filesize >= 6)
+ {
+ // check if the file has a valid pseudo-header
+ unsigned char pseudo_header[6];
+ f->readString((char *)pseudo_header, 6);
+ decompressed_filesize = pseudo_header[0] + (pseudo_header[1] << 8);
+
+ if (!( (pseudo_header[2]==0) && (pseudo_header[3]==0) &&
+ (pseudo_header[4] + ((pseudo_header[5] & 0x1)<<8) == 0x100) &&
+ (decompressed_filesize > (filesize-4)) ))
+ {
+ fp.close(f);
+ return(false);
+ }
+ }
+ else
+ {
+ fp.close(f);
+ return(false);
+ }
+
+ // load section
+ song_data = new unsigned char[decompressed_filesize];
+ unsigned char* compressed_song_data = new unsigned char[filesize-3];
+
+ f->seek(4);
+ f->readString((char *)compressed_song_data, filesize - 4);
+ fp.close(f);
+
+ // attempt to decompress the song data
+ // if unsuccessful, deallocate song_data[] on the spot, and return(false)
+ data_block source, destination;
+ source.size = filesize-4;
+ source.data = compressed_song_data;
+ destination.size = decompressed_filesize;
+ destination.data = song_data;
+
+ if (!lzw_decompress(source,destination))
+ {
+ delete[] compressed_song_data;
+ delete[] song_data;
+ return(false);
+ }
+
+ // deallocation section
+ delete[] compressed_song_data;
+
+ rewind(0);
+ return (true);
+}
+
+
+bool Cu6mPlayer::update()
+{
+ if (!driver_active)
+ {
+ driver_active = true;
+ dec_clip(read_delay);
+ if (read_delay == 0)
+ {
+ command_loop();
+ }
+
+ // on all Adlib channels: freq slide/vibrato, mute factor slide
+ for (int i = 0; i < 9; i++)
+ {
+ if (channel_freq_signed_delta[i]!=0)
+ // frequency slide + mute factor slide
+ {
+ // freq slide
+ freq_slide(i);
+
+ // mute factor slide
+ if (carrier_mf_signed_delta[i]!=0)
+ {
+ mf_slide(i);
+ }
+ }
+ else
+ // vibrato + mute factor slide
+ {
+ // vibrato
+ if ((vb_multiplier[i]!=0) && ((channel_freq[i].hi & 0x20)==0x20))
+ {
+ vibrato(i);
+ }
+
+ // mute factor slide
+ if (carrier_mf_signed_delta[i]!=0)
+ {
+ mf_slide(i);
+ }
+ }
+ }
+
+ driver_active = false;
+ }
+
+ return !songend;
+}
+
+
+void Cu6mPlayer::rewind(int subsong)
+{
+ played_ticks = 0;
+ songend = false;
+
+ // set the driver's internal variables
+ byte_pair freq_word = {0,0};
+
+ driver_active = false;
+ song_pos = 0;
+ loop_position = 0; // position of the loop point
+ read_delay = 0; // delay (in timer ticks) before further song data is read
+
+ for (int i = 0; i < 9; i++)
+ {
+ // frequency
+ channel_freq_signed_delta[i] = 0;
+ channel_freq[i] = freq_word; // Adlib freq settings for each channel
+
+ // vibrato ("vb")
+ vb_current_value[i] = 0;
+ vb_double_amplitude[i] = 0;
+ vb_multiplier[i] = 0;
+ vb_direction_flag[i] = 0;
+
+ // mute factor ("mf") == ~(volume)
+ carrier_mf[i] = 0;
+ carrier_mf_signed_delta[i] = 0;
+ carrier_mf_mod_delay_backup[i] = 0;
+ carrier_mf_mod_delay[i] = 0;
+ }
+
+ while (!subsong_stack.empty()) // empty subsong stack
+ subsong_stack.pop();
+
+ opl->init();
+ out_adlib(1,32); // go to OPL2 mode
+}
+
+
+float Cu6mPlayer::getrefresh()
+{
+ return ((float)60); // the Ultima 6 music driver expects to be called at 60 Hz
+}
+
+
+// ============================================================================================
+//
+//
+// Functions called by load()
+//
+//
+// ============================================================================================
+
+
+// decompress from memory to memory
+bool Cu6mPlayer::lzw_decompress(Cu6mPlayer::data_block source, Cu6mPlayer::data_block dest)
+{
+ bool end_marker_reached = false;
+ int codeword_size = 9;
+ long bits_read = 0;
+ int next_free_codeword = 0x102;
+ int dictionary_size = 0x200;
+ MyDict dictionary = MyDict();
+ std::stack<unsigned char> root_stack;
+
+ long bytes_written = 0;
+
+ int cW;
+ int pW;
+ unsigned char C;
+
+ while (!end_marker_reached)
+ {
+ cW = get_next_codeword(bits_read, source.data, codeword_size);
+ switch (cW)
+ {
+ // re-init the dictionary
+ case 0x100:
+ codeword_size = 9;
+ next_free_codeword = 0x102;
+ dictionary_size = 0x200;
+ dictionary.reset();
+ cW = get_next_codeword(bits_read, source.data, codeword_size);
+ SAVE_OUTPUT_ROOT((unsigned char)cW, dest, bytes_written);
+ break;
+ // end of compressed file has been reached
+ case 0x101:
+ end_marker_reached = true;
+ break;
+ // (cW <> 0x100) && (cW <> 0x101)
+ default:
+ if (cW < next_free_codeword) // codeword is already in the dictionary
+ {
+ // create the string associated with cW (on the stack)
+ get_string(cW,dictionary,root_stack);
+ C = root_stack.top();
+ // output the string represented by cW
+ while (!root_stack.empty())
+ {
+ SAVE_OUTPUT_ROOT(root_stack.top(), dest, bytes_written);
+ root_stack.pop();
+ }
+ // add pW+C to the dictionary
+ dictionary.add(C,pW);
+
+ next_free_codeword++;
+ if (next_free_codeword >= dictionary_size)
+ {
+ if (codeword_size < max_codeword_length)
+ {
+ codeword_size += 1;
+ dictionary_size *= 2;
+ }
+ }
+ }
+ else // codeword is not yet defined
+ {
+ // create the string associated with pW (on the stack)
+ get_string(pW,dictionary,root_stack);
+ C = root_stack.top();
+ // output the string represented by pW
+ while (!root_stack.empty())
+ {
+ SAVE_OUTPUT_ROOT(root_stack.top(), dest, bytes_written);
+ root_stack.pop();
+ }
+ // output the char C
+ SAVE_OUTPUT_ROOT(C, dest, bytes_written);
+
+ // the new dictionary entry must correspond to cW
+ // if it doesn't, something is wrong with the lzw-compressed data.
+ if (cW != next_free_codeword)
+ {
+ /* printf("cW != next_free_codeword!\n");
+ exit(-1); */
+ return false;
+ }
+ // add pW+C to the dictionary
+ dictionary.add(C,pW);
+
+ next_free_codeword++;
+ if (next_free_codeword >= dictionary_size)
+ {
+ if (codeword_size < max_codeword_length)
+ {
+ codeword_size += 1;
+ dictionary_size *= 2;
+ }
+ }
+ };
+ break;
+ }
+ // shift roles - the current cW becomes the new pW
+ pW = cW;
+ }
+
+ return(true); // indicate successful decompression
+}
+
+
+// --------------------
+// Additional functions
+// --------------------
+
+
+// Read the next code word from the source buffer
+int Cu6mPlayer::get_next_codeword (long& bits_read, unsigned char *source, int codeword_size)
+{
+ unsigned char b0,b1,b2;
+ int codeword;
+
+ b0 = source[bits_read/8];
+ b1 = source[bits_read/8+1];
+ b2 = source[bits_read/8+2];
+
+ codeword = ((b2 << 16) + (b1 << 8) + b0);
+ codeword = codeword >> (bits_read % 8);
+ switch (codeword_size)
+ {
+ case 0x9:
+ codeword = codeword & 0x1ff;
+ break;
+ case 0xa:
+ codeword = codeword & 0x3ff;
+ break;
+ case 0xb:
+ codeword = codeword & 0x7ff;
+ break;
+ case 0xc:
+ codeword = codeword & 0xfff;
+ break;
+ default:
+ codeword = -1; // indicates that an error has occurred
+ break;
+ }
+
+ bits_read += codeword_size;
+ return (codeword);
+}
+
+
+// output a root to memory
+void Cu6mPlayer::output_root(unsigned char root, unsigned char *destination, long& position)
+{
+ destination[position] = root;
+ position++;
+}
+
+
+// output the string represented by a codeword
+void Cu6mPlayer::get_string(int codeword, Cu6mPlayer::MyDict& dictionary, std::stack<unsigned char>& root_stack)
+{
+ unsigned char root;
+ int current_codeword;
+
+ current_codeword = codeword;
+
+ while (current_codeword > 0xff)
+ {
+ root = dictionary.get_root(current_codeword);
+ current_codeword = dictionary.get_codeword(current_codeword);
+ root_stack.push(root);
+ }
+
+ // push the root at the leaf
+ root_stack.push((unsigned char)current_codeword);
+}
+
+
+// ============================================================================================
+//
+//
+// Functions called by update()
+//
+//
+// ============================================================================================
+
+
+// This function reads the song data and executes the embedded commands.
+void Cu6mPlayer::command_loop()
+{
+ unsigned char command_byte; // current command byte
+ int command_nibble_hi; // command byte, bits 4-7
+ int command_nibble_lo; // command byte, bite 0-3
+ bool repeat_loop = true; //
+
+ do
+ {
+ // extract low and high command nibbles
+ command_byte = read_song_byte(); // implicitly increments song_pos
+ command_nibble_hi = command_byte >> 4;
+ command_nibble_lo = command_byte & 0xf;
+
+ switch (command_nibble_hi)
+ {
+ case 0x0: command_0(command_nibble_lo); break;
+ case 0x1: command_1(command_nibble_lo); break;
+ case 0x2: command_2(command_nibble_lo); break;
+ case 0x3: command_3(command_nibble_lo); break;
+ case 0x4: command_4(command_nibble_lo); break;
+ case 0x5: command_5(command_nibble_lo); break;
+ case 0x6: command_6(command_nibble_lo); break;
+ case 0x7: command_7(command_nibble_lo); break;
+ case 0x8:
+ switch (command_nibble_lo)
+ {
+ case 1: command_81(); break;
+ case 2: command_82(); repeat_loop = false; break;
+ case 3: command_83(); break;
+ case 5: command_85(); break;
+ case 6: command_86(); break;
+ default: break; // maybe generate an error?
+ }
+ break;
+ case 0xE: command_E(); break;
+ case 0xF: command_F(); break;
+ default: break; // maybe generate an error?
+ }
+
+ } while (repeat_loop);
+}
+
+
+// --------------------------------------------------------
+// The commands supported by the U6 music file format
+// --------------------------------------------------------
+
+// ----------------------------------------
+// Set octave and frequency, note off
+// Format: 0c nn
+// c = channel, nn = packed Adlib frequency
+// ----------------------------------------
+void Cu6mPlayer::command_0(int channel)
+{
+ unsigned char freq_byte;
+ byte_pair freq_word;
+
+ freq_byte = read_song_byte();
+ freq_word = expand_freq_byte(freq_byte);
+ set_adlib_freq(channel,freq_word);
+}
+
+
+// ---------------------------------------------------
+// Set octave and frequency, old note off, new note on
+// Format: 1c nn
+// c = channel, nn = packed Adlib frequency
+// ---------------------------------------------------
+void Cu6mPlayer::command_1(int channel)
+{
+ unsigned char freq_byte;
+ byte_pair freq_word;
+
+ vb_direction_flag[channel] = 0;
+ vb_current_value[channel] = 0;
+
+ freq_byte = read_song_byte();
+ freq_word = expand_freq_byte(freq_byte);
+ set_adlib_freq(channel,freq_word);
+
+ freq_word.hi = freq_word.hi | 0x20; // note on
+ set_adlib_freq(channel,freq_word);
+}
+
+
+// ----------------------------------------
+// Set octave and frequency, note on
+// Format: 2c nn
+// c = channel, nn = packed Adlib frequency
+// ----------------------------------------
+void Cu6mPlayer::command_2(int channel)
+{
+ unsigned char freq_byte;
+ byte_pair freq_word;
+
+ freq_byte = read_song_byte();
+ freq_word = expand_freq_byte(freq_byte);
+ freq_word.hi = freq_word.hi | 0x20; // note on
+ set_adlib_freq(channel,freq_word);
+}
+
+
+// --------------------------------------
+// Set "carrier mute factor"==not(volume)
+// Format: 3c nn
+// c = channel, nn = mute factor
+// --------------------------------------
+void Cu6mPlayer::command_3(int channel)
+{
+ unsigned char mf_byte;
+
+ carrier_mf_signed_delta[channel] = 0;
+ mf_byte = read_song_byte();
+ set_carrier_mf(channel,mf_byte);
+}
+
+
+// ----------------------------------------
+// set "modulator mute factor"==not(volume)
+// Format: 4c nn
+// c = channel, nn = mute factor
+// ----------------------------------------
+void Cu6mPlayer::command_4(int channel)
+{
+ unsigned char mf_byte;
+
+ mf_byte = read_song_byte();
+ set_modulator_mf(channel,mf_byte);
+}
+
+
+// --------------------------------------------
+// Set portamento (pitch slide)
+// Format: 5c nn
+// c = channel, nn = signed channel pitch delta
+// --------------------------------------------
+void Cu6mPlayer::command_5(int channel)
+{
+ channel_freq_signed_delta[channel] = read_signed_song_byte();
+}
+
+
+// --------------------------------------------
+// Set vibrato paramters
+// Format: 6c mn
+// c = channel
+// m = vibrato double amplitude
+// n = vibrato multiplier
+// --------------------------------------------
+void Cu6mPlayer::command_6(int channel)
+{
+ unsigned char vb_parameters;
+
+ vb_parameters = read_song_byte();
+ vb_double_amplitude[channel] = vb_parameters >> 4; // high nibble
+ vb_multiplier[channel] = vb_parameters & 0xF; // low nibble
+}
+
+
+// ----------------------------------------
+// Assign Adlib instrument to Adlib channel
+// Format: 7c nn
+// c = channel, nn = instrument number
+// ----------------------------------------
+void Cu6mPlayer::command_7(int channel)
+{
+ int instrument_offset = instrument_offsets[read_song_byte()];
+ out_adlib_opcell(channel, false, 0x20, *(song_data + instrument_offset+0));
+ out_adlib_opcell(channel, false, 0x40, *(song_data + instrument_offset+1));
+ out_adlib_opcell(channel, false, 0x60, *(song_data + instrument_offset+2));
+ out_adlib_opcell(channel, false, 0x80, *(song_data + instrument_offset+3));
+ out_adlib_opcell(channel, false, 0xE0, *(song_data + instrument_offset+4));
+ out_adlib_opcell(channel, true, 0x20, *(song_data + instrument_offset+5));
+ out_adlib_opcell(channel, true, 0x40, *(song_data + instrument_offset+6));
+ out_adlib_opcell(channel, true, 0x60, *(song_data + instrument_offset+7));
+ out_adlib_opcell(channel, true, 0x80, *(song_data + instrument_offset+8));
+ out_adlib_opcell(channel, true, 0xE0, *(song_data + instrument_offset+9));
+ out_adlib(0xC0+channel, *(song_data + instrument_offset+10));
+}
+
+
+// -------------------------------------------
+// Branch to a new subsong
+// Format: 81 nn aa bb
+// nn == number of times to repeat the subsong
+// aa == subsong offset (low byte)
+// bb == subsong offset (high byte)
+// -------------------------------------------
+void Cu6mPlayer::command_81()
+{
+ subsong_info new_ss_info;
+
+ new_ss_info.subsong_repetitions = read_song_byte();
+ new_ss_info.subsong_start = read_song_byte(); new_ss_info.subsong_start += read_song_byte() << 8;
+ new_ss_info.continue_pos = song_pos;
+
+ subsong_stack.push(new_ss_info);
+ song_pos = new_ss_info.subsong_start;
+}
+
+
+// ------------------------------------------------------------
+// Stop interpreting commands for this timer tick
+// Format: 82 nn
+// nn == delay (in timer ticks) until further data will be read
+// ------------------------------------------------------------
+void Cu6mPlayer::command_82()
+{
+ read_delay = read_song_byte();
+}
+
+
+// -----------------------------
+// Adlib instrument data follows
+// Format: 83 nn <11 bytes>
+// nn == instrument number
+// -----------------------------
+void Cu6mPlayer::command_83()
+{
+ unsigned char instrument_number = read_song_byte();
+ instrument_offsets[instrument_number] = song_pos;
+ song_pos += 11;
+}
+
+
+// ----------------------------------------------
+// Set -1 mute factor slide (upward volume slide)
+// Format: 85 cn
+// c == channel
+// n == slide delay
+// ----------------------------------------------
+void Cu6mPlayer::command_85()
+{
+ unsigned char data_byte = read_song_byte();
+ int channel = data_byte >> 4; // high nibble
+ unsigned char slide_delay = data_byte & 0xF; // low nibble
+ carrier_mf_signed_delta[channel] = +1;
+ carrier_mf_mod_delay[channel] = slide_delay + 1;
+ carrier_mf_mod_delay_backup[channel] = slide_delay + 1;
+}
+
+
+// ------------------------------------------------
+// Set +1 mute factor slide (downward volume slide)
+// Format: 86 cn
+// c == channel
+// n == slide speed
+// ------------------------------------------------
+void Cu6mPlayer::command_86()
+{
+ unsigned char data_byte = read_song_byte();
+ int channel = data_byte >> 4; // high nibble
+ unsigned char slide_delay = data_byte & 0xF; // low nibble
+ carrier_mf_signed_delta[channel] = -1;
+ carrier_mf_mod_delay[channel] = slide_delay + 1;
+ carrier_mf_mod_delay_backup[channel] = slide_delay + 1;
+}
+
+
+// --------------
+// Set loop point
+// Format: E?
+// --------------
+void Cu6mPlayer::command_E()
+{
+ loop_position = song_pos;
+}
+
+
+// ---------------------------
+// Return from current subsong
+// Format: F?
+// ---------------------------
+void Cu6mPlayer::command_F()
+{
+ if (!subsong_stack.empty())
+ {
+ subsong_info temp = subsong_stack.top();
+ subsong_stack.pop();
+ temp.subsong_repetitions--;
+ if (temp.subsong_repetitions==0)
+ {
+ song_pos = temp.continue_pos;
+ }
+ else
+ {
+ song_pos = temp.subsong_start;
+ subsong_stack.push(temp);
+ }
+ }
+ else
+ {
+ song_pos = loop_position;
+ songend = true;
+ }
+}
+
+
+// --------------------
+// Additional functions
+// --------------------
+
+// This function decrements its argument, without allowing it to become negative.
+void Cu6mPlayer::dec_clip(int& param)
+{
+ param--;
+ if (param < 0) { param = 0; }
+}
+
+
+// Returns the byte at the current song position.
+// Side effect: increments song_pos.
+unsigned char Cu6mPlayer::read_song_byte()
+{
+ unsigned char song_byte;
+ song_byte = song_data[song_pos];
+ song_pos++;
+ return(song_byte);
+}
+
+
+// Same as read_song_byte(), except that it returns a signed byte
+signed char Cu6mPlayer::read_signed_song_byte()
+{
+ unsigned char song_byte;
+ int signed_value;
+ song_byte = *(song_data + song_pos);
+ song_pos++;
+ if (song_byte <= 127)
+ {
+ signed_value = song_byte;
+ }
+ else
+ {
+ signed_value = (int)song_byte - 0x100;
+ }
+ return((signed char)signed_value);
+}
+
+
+Cu6mPlayer::byte_pair Cu6mPlayer::expand_freq_byte(unsigned char freq_byte)
+{
+ const byte_pair freq_table[24] =
+ {
+ {0x00,0x00}, {0x58,0x01}, {0x82,0x01}, {0xB0,0x01},
+ {0xCC,0x01}, {0x03,0x02}, {0x41,0x02}, {0x86,0x02},
+ {0x00,0x00}, {0x6A,0x01}, {0x96,0x01}, {0xC7,0x01},
+ {0xE4,0x01}, {0x1E,0x02}, {0x5F,0x02}, {0xA8,0x02},
+ {0x00,0x00}, {0x47,0x01}, {0x6E,0x01}, {0x9A,0x01},
+ {0xB5,0x01}, {0xE9,0x01}, {0x24,0x02}, {0x66,0x02}
+ };
+
+ int packed_freq;
+ int octave;
+ byte_pair freq_word;
+
+ packed_freq = freq_byte & 0x1F;
+ octave = freq_byte >> 5;
+
+ // range check (not present in the original U6 music driver)
+ if (packed_freq >= 24) { packed_freq = 0; }
+
+ freq_word.hi = freq_table[packed_freq].hi + (octave << 2);
+ freq_word.lo = freq_table[packed_freq].lo;
+
+ return(freq_word);
+}
+
+
+void Cu6mPlayer::set_adlib_freq(int channel,Cu6mPlayer::byte_pair freq_word)
+{
+ out_adlib(0xA0+channel,freq_word.lo);
+ out_adlib(0xB0+channel,freq_word.hi);
+ // update the Adlib register backups
+ channel_freq[channel] = freq_word;
+}
+
+
+// this function sets the Adlib frequency, but does not update the register backups
+void Cu6mPlayer::set_adlib_freq_no_update(int channel,Cu6mPlayer::byte_pair freq_word)
+{
+ out_adlib(0xA0+channel,freq_word.lo);
+ out_adlib(0xB0+channel,freq_word.hi);
+}
+
+
+void Cu6mPlayer::set_carrier_mf(int channel,unsigned char mute_factor)
+{
+ out_adlib_opcell(channel,true,0x40,mute_factor);
+ carrier_mf[channel] = mute_factor;
+}
+
+
+void Cu6mPlayer::set_modulator_mf(int channel,unsigned char mute_factor)
+{
+ out_adlib_opcell(channel,false,0x40,mute_factor);
+}
+
+
+void Cu6mPlayer::freq_slide(int channel)
+{
+ byte_pair freq = channel_freq[channel];
+
+ long freq_word = freq.lo + (freq.hi << 8) + channel_freq_signed_delta[channel];
+ if (freq_word < 0) { freq_word += 0x10000; }
+ if (freq_word > 0xFFFF) { freq_word -= 0x10000; }
+
+ freq.lo = freq_word & 0xFF;
+ freq.hi = (freq_word >> 8) & 0xFF;
+ set_adlib_freq(channel,freq);
+}
+
+
+void Cu6mPlayer::vibrato(int channel)
+{
+ byte_pair freq;
+
+ if (vb_current_value[channel] >= vb_double_amplitude[channel])
+ { vb_direction_flag[channel] = 1; }
+ else if (vb_current_value[channel] <= 0)
+ { vb_direction_flag[channel] = 0; }
+
+ if (vb_direction_flag[channel]==0)
+ { vb_current_value[channel]++; }
+ else
+ { vb_current_value[channel]--; }
+
+ long freq_word = channel_freq[channel].lo + (channel_freq[channel].hi << 8);
+ freq_word += (vb_current_value[channel] - (vb_double_amplitude[channel] >> 1))
+ * vb_multiplier[channel];
+ if (freq_word < 0) { freq_word += 0x10000; }
+ if (freq_word > 0xFFFF) { freq_word -= 0x10000; }
+
+ freq.lo = freq_word & 0xFF;
+ freq.hi = (freq_word >> 8) & 0xFF;
+ set_adlib_freq_no_update(channel,freq);
+}
+
+
+void Cu6mPlayer::mf_slide(int channel)
+{
+ carrier_mf_mod_delay[channel]--;
+ if (carrier_mf_mod_delay[channel]==0)
+ {
+ carrier_mf_mod_delay[channel] = carrier_mf_mod_delay_backup[channel];
+ int current_mf = carrier_mf[channel] + carrier_mf_signed_delta[channel];
+ if (current_mf > 0x3F)
+ {
+ current_mf = 0x3F;
+ carrier_mf_signed_delta[channel] = 0;
+ }
+ else if (current_mf < 0)
+ {
+ current_mf = 0;
+ carrier_mf_signed_delta[channel] = 0;
+ }
+
+ set_carrier_mf(channel,(unsigned char)current_mf);
+ }
+}
+
+
+void Cu6mPlayer::out_adlib(unsigned char adlib_register, unsigned char adlib_data)
+{
+ opl->write(adlib_register,adlib_data);
+}
+
+
+void Cu6mPlayer::out_adlib_opcell(int channel, bool carrier, unsigned char adlib_register, unsigned char out_byte)
+{
+ const unsigned char adlib_channel_to_carrier_offset[9] =
+ {0x03,0x04,0x05,0x0B,0x0C,0x0D,0x13,0x14,0x15};
+ const unsigned char adlib_channel_to_modulator_offset[9] =
+ {0x00,0x01,0x02,0x08,0x09,0x0A,0x10,0x11,0x12};
+
+ if (carrier)
+ {
+ out_adlib(adlib_register+adlib_channel_to_carrier_offset[channel],out_byte);
+ }
+ else
+ {
+ out_adlib(adlib_register+adlib_channel_to_modulator_offset[channel],out_byte);
+ }
+}
+
+
+// ============================================================================================
+//
+//
+// The Dictionary
+//
+//
+// ============================================================================================
+
+
+Cu6mPlayer::MyDict::MyDict()
+{
+ dict_size = default_dict_size;
+ dictionary = new dict_entry[dict_size-0x100]; // don't allocate space for the roots
+ contains = 0x102;
+}
+
+
+Cu6mPlayer::MyDict::MyDict(int max_size)
+{
+ dict_size = max_size;
+ dictionary = new dict_entry[dict_size-0x100]; // don't allocate space for the roots
+ contains = 0x102;
+}
+
+
+Cu6mPlayer::MyDict::~MyDict()
+{
+ delete [] dictionary;
+}
+
+// re-initializes the dictionary
+void Cu6mPlayer::MyDict::reset()
+{
+ contains = 0x102;
+}
+
+
+// Note: If the dictionary is already full, this function does nothing.
+void Cu6mPlayer::MyDict::add(unsigned char root, int codeword)
+{
+ if (contains < dict_size)
+ {
+ dictionary[contains-0x100].root = root;
+ dictionary[contains-0x100].codeword = codeword;
+ contains++;
+ }
+}
+
+
+unsigned char Cu6mPlayer::MyDict::get_root(int codeword)
+{
+ return (dictionary[codeword-0x100].root);
+}
+
+
+int Cu6mPlayer::MyDict::get_codeword(int codeword)
+{
+ return (dictionary[codeword-0x100].codeword);
+}
diff --git a/plugins/adplug/adplug/u6m.h b/plugins/adplug/adplug/u6m.h
new file mode 100644
index 00000000..30e7f3b5
--- /dev/null
+++ b/plugins/adplug/adplug/u6m.h
@@ -0,0 +1,168 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * u6m.h - Ultima 6 Music Player by Marc Winterrowd.
+ * This code extends the Adlib Winamp plug-in by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <stack>
+
+#include "player.h"
+
+#define default_dict_size 4096 // because maximum codeword size == 12 bits
+#define max_codeword_length 12 // maximum codeword length in bits
+
+class Cu6mPlayer: public CPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ Cu6mPlayer(Copl *newopl) : CPlayer(newopl), song_data(0)
+ {
+ };
+
+
+ ~Cu6mPlayer()
+ {
+ if(song_data) delete[] song_data;
+ };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype()
+ {
+ return std::string("Ultima 6 Music");
+ };
+
+ protected:
+
+ struct byte_pair
+ {
+ unsigned char lo;
+ unsigned char hi;
+ };
+
+ struct subsong_info // information about a subsong
+ {
+ int continue_pos;
+ int subsong_repetitions;
+ int subsong_start;
+ };
+
+ struct dict_entry // dictionary entry
+ {
+ unsigned char root;
+ int codeword;
+ };
+
+ struct data_block //
+ {
+ long size;
+ unsigned char *data;
+ };
+
+ class MyDict
+ {
+ private:
+ // The actual number of dictionary entries allocated
+ // is (dictionary_size-256), because there are 256 roots
+ // that do not need to be stored.
+ int contains; // number of entries currently in the dictionary
+ int dict_size; // max number of entries that will fit into the dictionary
+ dict_entry* dictionary;
+
+ public:
+ MyDict(); // use dictionary size of 4096
+ MyDict(int); // let the caller specify a dictionary size
+ ~MyDict();
+ void reset(); // re-initializes the dictionary
+ void add(unsigned char, int);
+ unsigned char get_root(int);
+ int get_codeword(int);
+ };
+
+
+ // class variables
+ long played_ticks;
+
+ unsigned char* song_data; // the uncompressed .m file (the "song")
+ bool driver_active; // flag to prevent reentrancy
+ bool songend; // indicates song end
+ int song_pos; // current offset within the song
+ int loop_position; // position of the loop point
+ int read_delay; // delay (in timer ticks) before further song data is read
+ std::stack<subsong_info> subsong_stack;
+
+ int instrument_offsets[9]; // offsets of the adlib instrument data
+ // vibrato ("vb")
+ unsigned char vb_current_value[9];
+ unsigned char vb_double_amplitude[9];
+ unsigned char vb_multiplier[9];
+ unsigned char vb_direction_flag[9];
+ // mute factor ("mf") = not(volume)
+ unsigned char carrier_mf[9];
+ signed char carrier_mf_signed_delta[9];
+ unsigned char carrier_mf_mod_delay_backup[9];
+ unsigned char carrier_mf_mod_delay[9];
+ // frequency
+ byte_pair channel_freq[9]; // adlib freq settings for each channel
+ signed char channel_freq_signed_delta[9];
+
+ // protected functions used by update()
+ void command_loop();
+ unsigned char read_song_byte();
+ signed char read_signed_song_byte();
+ void dec_clip(int&);
+ byte_pair expand_freq_byte(unsigned char);
+ void set_adlib_freq(int channel,byte_pair freq_word);
+ void set_adlib_freq_no_update(int channel,byte_pair freq_word);
+ void set_carrier_mf(int channel,unsigned char mute_factor);
+ void set_modulator_mf(int channel,unsigned char mute_factor);
+ void freq_slide(int channel);
+ void vibrato(int channel);
+ void mf_slide(int channel);
+
+ void command_0(int channel);
+ void command_1(int channel);
+ void command_2(int channel);
+ void command_3(int channel);
+ void command_4(int channel);
+ void command_5(int channel);
+ void command_6(int channel);
+ void command_7(int channel);
+ void command_81();
+ void command_82();
+ void command_83();
+ void command_85();
+ void command_86();
+ void command_E();
+ void command_F();
+
+ void out_adlib(unsigned char adlib_register, unsigned char adlib_data);
+ void out_adlib_opcell(int channel, bool carrier, unsigned char adlib_register, unsigned char out_byte);
+
+ // protected functions used by load()
+ bool lzw_decompress(data_block source, data_block dest);
+ int get_next_codeword (long& bits_read, unsigned char *source, int codeword_size);
+ void output_root(unsigned char root, unsigned char *destination, long& position);
+ void get_string(int codeword, MyDict& dictionary, std::stack<unsigned char>& root_stack);
+};
+
diff --git a/plugins/adplug/adplug/xad.cpp b/plugins/adplug/adplug/xad.cpp
new file mode 100644
index 00000000..81c7c0a7
--- /dev/null
+++ b/plugins/adplug/adplug/xad.cpp
@@ -0,0 +1,140 @@
+/*
+ Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ xad.cpp - XAD shell player by Riven the Mage <riven@ok.ru>
+*/
+
+#include "xad.h"
+#include "debug.h"
+
+/* -------- Public Methods -------------------------------- */
+
+CxadPlayer::CxadPlayer(Copl * newopl) : CPlayer(newopl)
+{
+ tune = 0;
+}
+
+CxadPlayer::~CxadPlayer()
+{
+ if (tune)
+ delete [] tune;
+}
+
+bool CxadPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ bool ret = false;
+
+ // load header
+ xad.id = f->readInt(4);
+ f->readString(xad.title, 36);
+ f->readString(xad.author, 36);
+ xad.fmt = f->readInt(2);
+ xad.speed = f->readInt(1);
+ xad.reserved_a = f->readInt(1);
+
+ // 'XAD!' - signed ?
+ if(xad.id != 0x21444158) { fp.close(f); return false; }
+
+ // get file size
+ tune_size = fp.filesize(f) - 80;
+
+ // load()
+ tune = new unsigned char [tune_size];
+ f->readString((char *)tune, tune_size);
+ fp.close(f);
+
+ ret = xadplayer_load();
+
+ if (ret)
+ rewind(0);
+
+ return ret;
+}
+
+void CxadPlayer::rewind(int subsong)
+{
+ opl->init();
+
+ plr.speed = xad.speed;
+ plr.speed_counter = 1;
+ plr.playing = 1;
+ plr.looping = 0;
+
+ // rewind()
+ xadplayer_rewind(subsong);
+
+#ifdef DEBUG
+ AdPlug_LogWrite("-----------\n");
+#endif
+}
+
+bool CxadPlayer::update()
+{
+ if (--plr.speed_counter)
+ goto update_end;
+
+ plr.speed_counter = plr.speed;
+
+ // update()
+ xadplayer_update();
+
+update_end:
+ return (plr.playing && (!plr.looping));
+}
+
+float CxadPlayer::getrefresh()
+{
+ return xadplayer_getrefresh();
+}
+
+std::string CxadPlayer::gettype()
+{
+ return xadplayer_gettype();
+}
+
+std::string CxadPlayer::gettitle()
+{
+ return xadplayer_gettitle();
+}
+
+std::string CxadPlayer::getauthor()
+{
+ return xadplayer_getauthor();
+}
+
+std::string CxadPlayer::getinstrument(unsigned int i)
+{
+ return xadplayer_getinstrument(i);
+}
+
+unsigned int CxadPlayer::getinstruments()
+{
+ return xadplayer_getinstruments();
+}
+
+/* -------- Protected Methods ------------------------------- */
+
+void CxadPlayer::opl_write(int reg, int val)
+{
+ adlib[reg] = val;
+#ifdef DEBUG
+ AdPlug_LogWrite("[ %02X ] = %02X\n",reg,val);
+#endif
+ opl->write(reg,val);
+}
diff --git a/plugins/adplug/adplug/xad.h b/plugins/adplug/adplug/xad.h
new file mode 100644
index 00000000..16872558
--- /dev/null
+++ b/plugins/adplug/adplug/xad.h
@@ -0,0 +1,97 @@
+/*
+ AdPlug - Replayer for many OPL2/OPL3 audio file formats.
+ Copyright (C) 1999 - 2003 Simon Peter <dn.tlp@gmx.net>, et al.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ xad.h - XAD shell player by Riven the Mage <riven@ok.ru>
+*/
+
+#ifndef H_ADPLUG_XAD
+#define H_ADPLUG_XAD
+
+#include "player.h"
+
+class CxadPlayer: public CPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CxadPlayer(Copl * newopl);
+ ~CxadPlayer();
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype();
+ std::string gettitle();
+ std::string getauthor();
+ std::string getinstrument(unsigned int i);
+ unsigned int getinstruments();
+
+protected:
+ virtual void xadplayer_rewind(int subsong) = 0;
+ virtual bool xadplayer_load() = 0;
+ virtual void xadplayer_update() = 0;
+ virtual float xadplayer_getrefresh() = 0;
+ virtual std::string xadplayer_gettype() = 0;
+ virtual std::string xadplayer_gettitle()
+ {
+ return std::string(xad.title);
+ }
+ virtual std::string xadplayer_getauthor()
+ {
+ return std::string(xad.author);
+ }
+ virtual std::string xadplayer_getinstrument(unsigned int i)
+ {
+ return std::string("");
+ }
+ virtual unsigned int xadplayer_getinstruments()
+ {
+ return 0;
+ }
+
+ enum { HYP=1, PSI, FLASH, BMF, RAT, HYBRID };
+
+ struct xad_header
+ {
+ unsigned long id;
+ char title[36];
+ char author[36];
+ unsigned short fmt;
+ unsigned char speed;
+ unsigned char reserved_a;
+ } xad;
+
+ unsigned char * tune;
+ unsigned long tune_size;
+
+ struct
+ {
+ int playing;
+ int looping;
+ unsigned char speed;
+ unsigned char speed_counter;
+ } plr;
+
+ unsigned char adlib[256];
+
+ void opl_write(int reg, int val);
+};
+
+#endif
diff --git a/plugins/adplug/adplug/xsm.cpp b/plugins/adplug/adplug/xsm.cpp
new file mode 100644
index 00000000..3c97de04
--- /dev/null
+++ b/plugins/adplug/adplug/xsm.cpp
@@ -0,0 +1,117 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * xsm.cpp - eXtra Simple Music Player, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string.h>
+
+#include "xsm.h"
+
+CxsmPlayer::CxsmPlayer(Copl *newopl)
+ : CPlayer(newopl), music(0)
+{
+}
+
+CxsmPlayer::~CxsmPlayer()
+{
+ if(music) delete [] music;
+}
+
+bool CxsmPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ char id[6];
+ int i, j;
+
+ // check if header matches
+ f->readString(id, 6); songlen = f->readInt(2);
+ if(strncmp(id, "ofTAZ!", 6) || songlen > 3200) { fp.close(f); return false; }
+
+ // read and set instruments
+ for(i = 0; i < 9; i++) {
+ opl->write(0x20 + op_table[i], f->readInt(1));
+ opl->write(0x23 + op_table[i], f->readInt(1));
+ opl->write(0x40 + op_table[i], f->readInt(1));
+ opl->write(0x43 + op_table[i], f->readInt(1));
+ opl->write(0x60 + op_table[i], f->readInt(1));
+ opl->write(0x63 + op_table[i], f->readInt(1));
+ opl->write(0x80 + op_table[i], f->readInt(1));
+ opl->write(0x83 + op_table[i], f->readInt(1));
+ opl->write(0xe0 + op_table[i], f->readInt(1));
+ opl->write(0xe3 + op_table[i], f->readInt(1));
+ opl->write(0xc0 + op_table[i], f->readInt(1));
+ f->ignore(5);
+ }
+
+ // read song data
+ music = new char [songlen * 9];
+ for(i = 0; i < 9; i++)
+ for(j = 0; j < songlen; j++)
+ music[j * 9 + i] = f->readInt(1);
+
+ // success
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+bool CxsmPlayer::update()
+{
+ int c;
+
+ if(notenum >= songlen) {
+ songend = true;
+ notenum = last = 0;
+ }
+
+ for(c = 0; c < 9; c++)
+ if(music[notenum * 9 + c] != music[last * 9 + c])
+ opl->write(0xb0 + c, 0);
+
+ for(c = 0; c < 9; c++) {
+ if(music[notenum * 9 + c])
+ play_note(c, music[notenum * 9 + c] % 12, music[notenum * 9 + c] / 12);
+ else
+ play_note(c, 0, 0);
+ }
+
+ last = notenum;
+ notenum++;
+ return !songend;
+}
+
+void CxsmPlayer::rewind(int subsong)
+{
+ notenum = last = 0;
+ songend = false;
+}
+
+float CxsmPlayer::getrefresh()
+{
+ return 5.0f;
+}
+
+void CxsmPlayer::play_note(int c, int note, int octv)
+{
+ int freq = note_table[note];
+
+ if(!note && !octv) freq = 0;
+ opl->write(0xa0 + c, freq & 0xff);
+ opl->write(0xb0 + c, (freq / 0xff) | 32 | (octv * 4));
+}
diff --git a/plugins/adplug/adplug/xsm.h b/plugins/adplug/adplug/xsm.h
new file mode 100644
index 00000000..15129606
--- /dev/null
+++ b/plugins/adplug/adplug/xsm.h
@@ -0,0 +1,46 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * xsm.h - eXtra Simple Music Player, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "player.h"
+
+class CxsmPlayer: public CPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl) { return new CxsmPlayer(newopl); }
+
+ CxsmPlayer(Copl *newopl);
+ ~CxsmPlayer();
+
+ bool load(const std::string &fn, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype() { return std::string("eXtra Simple Music"); }
+
+private:
+ unsigned short songlen;
+ char *music;
+ unsigned int last, notenum;
+ bool songend;
+
+ void play_note(int c, int note, int octv);
+};
diff --git a/plugins/adplug/libbinio/COPYING b/plugins/adplug/libbinio/COPYING
new file mode 100644
index 00000000..cf9b6b99
--- /dev/null
+++ b/plugins/adplug/libbinio/COPYING
@@ -0,0 +1,510 @@
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+^L
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard. To achieve this, non-free programs must
+be allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+^L
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control
+compilation and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+^L
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+^L
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at least
+ three years, to give the same user the materials specified in
+ Subsection 6a, above, for a charge no more than the cost of
+ performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+^L
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+^L
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply, and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License
+may add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+^L
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+^L
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms
+of the ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library.
+It is safest to attach them to the start of each source file to most
+effectively convey the exclusion of warranty; and each file should
+have at least the "copyright" line and a pointer to where the full
+notice is found.
+
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or
+your school, if any, to sign a "copyright disclaimer" for the library,
+if necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James
+ Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/plugins/adplug/libbinio/binfile.cpp b/plugins/adplug/libbinio/binfile.cpp
new file mode 100644
index 00000000..446f899f
--- /dev/null
+++ b/plugins/adplug/libbinio/binfile.cpp
@@ -0,0 +1,247 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * binfile.h - Binary file I/O
+ * Copyright (C) 2002, 2003 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "binfile.h"
+
+/***** binfbase *****/
+
+binfbase::binfbase()
+ : f(NULL)
+{
+}
+
+binfbase::~binfbase()
+{
+ if(f != NULL) close();
+}
+
+void binfbase::close()
+{
+ if(f != NULL) {
+ if(fclose(f) == EOF) err |= Fatal; else f = NULL;
+ } else
+ err |= NotOpen;
+}
+
+void binfbase::seek(long pos, Offset offs)
+{
+ int error;
+
+ if(f == NULL) { err |= NotOpen; return; }
+
+ switch(offs) {
+ case Set: error = fseek(f, pos, SEEK_SET); break;
+ case Add: error = fseek(f, pos, SEEK_CUR); break;
+ case End: error = fseek(f, pos, SEEK_END); break;
+ }
+
+ if(error == -1) err |= Fatal;
+}
+
+long binfbase::pos()
+{
+ long pos;
+
+ if(f == NULL) { err |= NotOpen; return 0; }
+
+ pos = ftell(f);
+
+ if(pos == -1) {
+ err |= Fatal;
+ return 0;
+ } else
+ return pos;
+}
+
+/***** binifstream *****/
+
+binifstream::binifstream()
+{
+}
+
+binifstream::binifstream(const char *filename, const Mode mode)
+{
+ open(filename, mode);
+}
+
+#if BINIO_ENABLE_STRING
+binifstream::binifstream(const std::string &filename, const Mode mode)
+{
+ open(filename, mode);
+}
+#endif
+
+binifstream::~binifstream()
+{
+}
+
+void binifstream::open(const char *filename, const Mode mode)
+{
+ f = fopen(filename, "rb");
+
+ if(f == NULL)
+ switch(errno) {
+ case ENOENT: err |= NotFound; break;
+ case EACCES: err |= Denied; break;
+ default: err |= NotOpen; break;
+ }
+}
+
+#if BINIO_ENABLE_STRING
+void binifstream::open(const std::string &filename, const Mode mode)
+{
+ open(filename.c_str(), mode);
+}
+#endif
+
+binifstream::Byte binifstream::getByte()
+{
+ int read;
+
+ if(f != NULL) {
+ read = fgetc(f);
+ if(read == EOF) err |= Eof;
+ return (Byte)read;
+ } else {
+ err |= NotOpen;
+ return 0;
+ }
+}
+
+/***** binofstream *****/
+
+binofstream::binofstream()
+{
+}
+
+binofstream::binofstream(const char *filename, const Mode mode)
+{
+ open(filename, mode);
+}
+
+#if BINIO_ENABLE_STRING
+binofstream::binofstream(const std::string &filename, const Mode mode)
+{
+ open(filename, mode);
+}
+#endif
+
+binofstream::~binofstream()
+{
+}
+
+void binofstream::open(const char *filename, const Mode mode)
+{
+ char *modestr = "wb";
+
+ // Check if append mode is desired
+ if(mode & Append) modestr = "ab";
+
+ f = fopen(filename, modestr);
+
+ if(f == NULL)
+ switch(errno) {
+ case EEXIST:
+ case EACCES:
+ case EROFS:
+ err |= Denied;
+ break;
+ case ENOENT: err |= NotFound; break;
+ default: err |= NotOpen; break;
+ }
+}
+
+#if BINIO_ENABLE_STRING
+void binofstream::open(const std::string &filename, const Mode mode)
+{
+ open(filename.c_str(), mode);
+}
+#endif
+
+void binofstream::putByte(Byte b)
+{
+ if(f == NULL) { err |= NotOpen; return; }
+
+ if(fputc(b, f) == EOF)
+ err |= Fatal;
+}
+
+/***** binfstream *****/
+
+binfstream::binfstream()
+{
+}
+
+binfstream::binfstream(const char *filename, const Mode mode)
+{
+ open(filename, mode);
+}
+
+#if BINIO_ENABLE_STRING
+binfstream::binfstream(const std::string &filename, const Mode mode)
+{
+ open(filename, mode);
+}
+#endif
+
+binfstream::~binfstream()
+{
+}
+
+void binfstream::open(const char *filename, const Mode mode)
+{
+ char *modestr = "w+b"; // Create & at beginning
+ int ferror = 0;
+
+ // Apply desired mode
+ if(mode & NoCreate) {
+ if(!(mode & Append))
+ modestr[0] = 'r'; // NoCreate & at beginning
+ } else
+ if(mode & Append) // Create & append
+ modestr[0] = 'a';
+
+ f = fopen(filename, modestr);
+
+ // NoCreate & append (emulated -- not possible with standard C fopen())
+ if(f != NULL && (mode & Append) && (mode & NoCreate))
+ ferror = fseek(f, 0, SEEK_END);
+
+ if(f == NULL || ferror == -1) {
+ switch(errno) {
+ case EEXIST:
+ case EACCES:
+ case EROFS:
+ err |= Denied;
+ break;
+ case ENOENT: err |= NotFound; break;
+ default: err |= NotOpen; break;
+ }
+ }
+}
+
+#if BINIO_ENABLE_STRING
+void binfstream::open(const std::string &filename, const Mode mode)
+{
+ open(filename.c_str(), mode);
+}
+#endif
diff --git a/plugins/adplug/libbinio/binfile.h b/plugins/adplug/libbinio/binfile.h
new file mode 100644
index 00000000..ad24baee
--- /dev/null
+++ b/plugins/adplug/libbinio/binfile.h
@@ -0,0 +1,110 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * binfile.h - Binary file I/O
+ * Copyright (C) 2002, 2003 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_BINIO_BINFILE
+#define H_BINIO_BINFILE
+
+#include <stdio.h>
+
+#include "binio.h"
+
+class binfbase: virtual public binio
+{
+public:
+ typedef enum {
+ Append = 1 << 0,
+ NoCreate = 1 << 1
+ } ModeFlags;
+
+ typedef int Mode;
+
+ binfbase();
+ virtual ~binfbase();
+
+ virtual void open(const char *filename, const Mode mode) = 0;
+#if BINIO_ENABLE_STRING
+ virtual void open(const std::string &filename, const Mode mode) = 0;
+#endif
+ void close();
+
+ virtual void seek(long pos, Offset offs = Set);
+ virtual long pos();
+
+protected:
+ FILE *f;
+};
+
+class binifstream: public binistream, virtual public binfbase
+{
+public:
+ binifstream();
+ binifstream(const char *filename, const Mode mode = NoCreate);
+#if BINIO_ENABLE_STRING
+ binifstream(const std::string &filename, const Mode mode = NoCreate);
+#endif
+
+ virtual ~binifstream();
+
+ virtual void open(const char *filename, const Mode mode = NoCreate);
+#if BINIO_ENABLE_STRING
+ virtual void open(const std::string &filename, const Mode mode = NoCreate);
+#endif
+
+protected:
+ virtual Byte getByte();
+};
+
+class binofstream: public binostream, virtual public binfbase
+{
+public:
+ binofstream();
+ binofstream(const char *filename, const Mode mode = 0);
+#if BINIO_ENABLE_STRING
+ binofstream(const std::string &filename, const Mode mode = 0);
+#endif
+
+ virtual ~binofstream();
+
+ virtual void open(const char *filename, const Mode mode = 0);
+#if BINIO_ENABLE_STRING
+ virtual void open(const std::string &filename, const Mode mode = 0);
+#endif
+
+protected:
+ virtual void putByte(Byte b);
+};
+
+class binfstream: public binifstream, public binofstream
+{
+public:
+ binfstream();
+ binfstream(const char *filename, const Mode mode = 0);
+#if BINIO_ENABLE_STRING
+ binfstream(const std::string &filename, const Mode mode = 0);
+#endif
+
+ virtual ~binfstream();
+
+ virtual void open(const char *filename, const Mode mode = 0);
+#if BINIO_ENABLE_STRING
+ virtual void open(const std::string &filename, const Mode mode = 0);
+#endif
+};
+
+#endif
diff --git a/plugins/adplug/libbinio/binio.cpp b/plugins/adplug/libbinio/binio.cpp
new file mode 100644
index 00000000..75489788
--- /dev/null
+++ b/plugins/adplug/libbinio/binio.cpp
@@ -0,0 +1,640 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * binio.cpp - Binary stream I/O classes
+ * Copyright (C) 2002, 2003 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string.h>
+
+#include "binio.h"
+
+#if BINIO_WITH_MATH
+
+#include <math.h>
+
+#ifdef __QNXNTO__
+ #define pow std::powf
+#endif // __QNXNTO__
+
+// If 'math.h' doesn't define HUGE_VAL, we try to use HUGE instead.
+#ifndef HUGE_VAL
+# define HUGE_VAL HUGE
+#endif
+
+#endif
+
+/***** Defines *****/
+
+#if BINIO_ENABLE_STRING
+// String buffer size for std::string readString() method
+#define STRINGBUFSIZE 256
+#endif
+
+/***** binio *****/
+
+const binio::Flags binio::system_flags = binio::detect_system_flags();
+
+const binio::Flags binio::detect_system_flags()
+{
+ Flags f = 0;
+
+ // Endian test
+ union {
+ int word;
+ Byte byte;
+ } endian_test;
+
+ endian_test.word = 1;
+ if(endian_test.byte != 1) f |= BigEndian;
+
+ // IEEE-754 floating-point test
+ float fl = 6.5;
+ Byte *dat = (Byte *)&fl;
+
+ if(sizeof(float) == 4 && sizeof(double) == 8)
+ if(f & BigEndian) {
+ if(dat[0] == 0x40 && dat[1] == 0xD0 && !dat[2] && !dat[3])
+ f |= FloatIEEE;
+ } else
+ if(dat[3] == 0x40 && dat[2] == 0xD0 && !dat[1] && !dat[0])
+ f |= FloatIEEE;
+
+ return f;
+}
+
+binio::binio()
+ : my_flags(system_flags), err(NoError)
+{
+}
+
+binio::~binio()
+{
+}
+
+void binio::setFlag(Flag f, bool set)
+{
+ if(set)
+ my_flags |= f;
+ else
+ my_flags &= !f;
+}
+
+bool binio::getFlag(Flag f)
+{
+ return (my_flags & f ? true : false);
+}
+
+binio::Error binio::error()
+{
+ Error e = err;
+
+ err = NoError;
+ return e;
+}
+
+bool binio::eof()
+{
+ return (err & Eof ? true : false);
+}
+
+/***** binistream *****/
+
+binistream::binistream()
+{
+}
+
+binistream::~binistream()
+{
+}
+
+binistream::Int binistream::readInt(unsigned int size)
+{
+ unsigned int i;
+ Int val = 0, in;
+
+ // Check if 'size' doesn't exceed our system's biggest type.
+ if(size > sizeof(Int)) {
+ err |= Unsupported;
+ return 0;
+ }
+
+ for(i = 0; i < size; i++) {
+ in = getByte();
+ if(getFlag(BigEndian))
+ val <<= 8;
+ else
+ in <<= i * 8;
+ val |= in;
+ }
+
+ return val;
+}
+
+binistream::Float binistream::readFloat(FType ft)
+{
+ if(getFlag(FloatIEEE)) { // Read IEEE-754 floating-point value
+ unsigned int i, size;
+ Byte in[8];
+ bool swap;
+
+ // Determine appropriate size for given type.
+ switch(ft) {
+ case Single: size = 4; break; // 32 bits
+ case Double: size = 8; break; // 64 bits
+ }
+
+ // Determine byte ordering, depending on what we do next
+ if(system_flags & FloatIEEE)
+ swap = getFlag(BigEndian) ^ (system_flags & BigEndian);
+ else
+ swap = !getFlag(BigEndian);
+
+ // Read the float byte by byte, converting endianess
+ for(i = 0; i < size; i++)
+ if(swap)
+ in[size - i - 1] = getByte();
+ else
+ in[i] = getByte();
+
+ if(system_flags & FloatIEEE) {
+ // Compatible system, let the hardware do the conversion
+ switch(ft) {
+ case Single: return *(float *)in;
+ case Double: return *(double *)in;
+ }
+ } else { // Incompatible system, convert manually
+ switch(ft) {
+ case Single: return ieee_single2float(in);
+ case Double: return ieee_double2float(in);
+ }
+ }
+ }
+
+ // User tried to read a (yet) unsupported floating-point type. Bail out.
+ err |= Unsupported; return 0.0;
+}
+
+binistream::Float binistream::ieee_single2float(Byte *data)
+{
+ signed int sign = data[0] >> 7 ? -1 : 1;
+ unsigned int exp = ((data[0] << 1) & 0xff) | ((data[1] >> 7) & 1),
+ fracthi7 = data[1] & 0x7f;
+ Float fract = fracthi7 * 65536.0 + data[2] * 256.0 + data[3];
+
+ // Signed and unsigned zero
+ if(!exp && !fracthi7 && !data[2] && !data[3]) return sign * 0.0;
+
+ // Signed and unsigned infinity (maybe unsupported on non-IEEE systems)
+ if(exp == 255)
+ if(!fracthi7 && !data[2] && !data[3]) {
+#ifdef HUGE_VAL
+ if(sign == -1) return -HUGE_VAL; else return HUGE_VAL;
+#else
+ err |= Unsupported;
+ if(sign == -1) return -1.0; else return 1.0;
+#endif
+ } else { // Not a number (maybe unsupported on non-IEEE systems)
+#ifdef NAN
+ return NAN;
+#else
+ err |= Unsupported; return 0.0;
+#endif
+ }
+
+ if(!exp) // Unnormalized float values
+ return sign * pow(2, -126) * fract * pow(2, -23);
+ else // Normalized float values
+ return sign * pow(2, exp - 127) * (fract * pow(2, -23) + 1);
+
+ err |= Fatal; return 0.0;
+}
+
+binistream::Float binistream::ieee_double2float(Byte *data)
+{
+ signed int sign = data[0] >> 7 ? -1 : 1;
+ unsigned int exp = ((unsigned int)(data[0] & 0x7f) << 4) | (data[1] >> 4),
+ fracthi4 = data[1] & 0xf;
+ Float fract = fracthi4 * pow(2, 48) + data[2] * pow(2, 40) + data[3] *
+ pow(2, 32) + data[4] * pow(2, 24) + data[5] * pow(2, 16) + data[6] *
+ pow(2, 8) + data[7];
+
+ // Signed and unsigned zero
+ if(!exp && !fracthi4 && !data[2] && !data[3] && !data[4] && !data[5] &&
+ !data[6] && !data[7]) return sign * 0.0;
+
+ // Signed and unsigned infinity (maybe unsupported on non-IEEE systems)
+ if(exp == 2047)
+ if(!fracthi4 && !data[2] && !data[3] && !data[4] && !data[5] && !data[6] &&
+ !data[7]) {
+#ifdef HUGE_VAL
+ if(sign == -1) return -HUGE_VAL; else return HUGE_VAL;
+#else
+ err |= Unsupported;
+ if(sign == -1) return -1.0; else return 1.0;
+#endif
+ } else { // Not a number (maybe unsupported on non-IEEE systems)
+#ifdef NAN
+ return NAN;
+#else
+ err |= Unsupported; return 0.0;
+#endif
+ }
+
+ if(!exp) // Unnormalized float values
+ return sign * pow(2, -1022) * fract * pow(2, -52);
+ else // Normalized float values
+ return sign * pow(2, exp - 1023) * (fract * pow(2, -52) + 1);
+
+ err |= Fatal; return 0.0;
+}
+
+#if !BINIO_WITH_MATH
+binio::Float binio::pow(Float base, signed int exp)
+/* Our own, stripped-down version of pow() for not having to depend on 'math.h'.
+ * This one handles float values for the base and an integer exponent, both
+ * positive and negative.
+ */
+{
+ int i;
+ Float val = base;
+
+ if(!exp) return 1.0;
+
+ for(i = 1; i < (exp < 0 ? -exp : exp); i++)
+ val *= base;
+
+ if(exp < 0) val = 1.0 / val;
+
+ return val;
+}
+#endif
+
+unsigned long binistream::readString(char *str, unsigned long maxlen)
+{
+ unsigned long i;
+
+ for(i = 0; i < maxlen; i++) {
+ str[i] = (char)getByte();
+ if(err) { str[i] = '\0'; return i; }
+ }
+
+ return maxlen;
+}
+
+unsigned long binistream::readString(char *str, unsigned long maxlen,
+ const char delim)
+{
+ unsigned long i;
+
+ for(i = 0; i < maxlen; i++) {
+ str[i] = (char)getByte();
+ if(str[i] == delim || err) { str[i] = '\0'; return i; }
+ }
+
+ str[maxlen] = '\0';
+ return maxlen;
+}
+
+#if BINIO_ENABLE_STRING
+std::string binistream::readString(const char delim)
+{
+ char buf[STRINGBUFSIZE + 1];
+ std::string tempstr;
+ unsigned long read;
+
+ do {
+ read = readString(buf, STRINGBUFSIZE, delim);
+ tempstr.append(buf, read);
+ } while(read == STRINGBUFSIZE);
+
+ return tempstr;
+}
+#endif
+
+binistream::Int binistream::peekInt(unsigned int size)
+{
+ Int val = readInt(size);
+ if(!err) seek(-(long)size, Add);
+ return val;
+}
+
+binistream::Float binistream::peekFloat(FType ft)
+{
+ Float val = readFloat(ft);
+
+ if(!err)
+ switch(ft) {
+ case Single: seek(-4, Add); break;
+ case Double: seek(-8, Add); break;
+ }
+
+ return val;
+}
+
+bool binistream::ateof()
+{
+ Error olderr = err; // Save current error state
+ bool eof_then;
+
+ peekInt(1);
+ eof_then = eof(); // Get error state of next byte
+ err = olderr; // Restore original error state
+ return eof_then;
+}
+
+void binistream::ignore(unsigned long amount)
+{
+ unsigned long i;
+
+ for(i = 0; i < amount; i++)
+ getByte();
+}
+
+/***** binostream *****/
+
+binostream::binostream()
+{
+}
+
+binostream::~binostream()
+{
+}
+
+void binostream::writeInt(Int val, unsigned int size)
+{
+ unsigned int i;
+
+ // Check if 'size' doesn't exceed our system's biggest type.
+ if(size > sizeof(Int)) { err |= Unsupported; return; }
+
+ for(i = 0; i < size; i++) {
+ if(getFlag(BigEndian))
+ putByte((val >> (size - i - 1) * 8) & 0xff);
+ else {
+ putByte(val & 0xff);
+ val >>= 8;
+ }
+ }
+}
+
+void binostream::writeFloat(Float f, FType ft)
+{
+ if(getFlag(FloatIEEE)) { // Write IEEE-754 floating-point value
+ unsigned int i, size;
+ Byte *out;
+ bool swap;
+
+ if(system_flags & FloatIEEE) {
+ // compatible system, let the hardware do the conversion
+ float outf = f;
+ double outd = f;
+
+ // Hardware could be big or little endian, convert appropriately
+ swap = getFlag(BigEndian) ^ (system_flags & BigEndian);
+
+ // Determine appropriate size for given type and convert by hardware
+ switch(ft) {
+ case Single: size = 4; out = (Byte *)&outf; break; // 32 bits
+ case Double: size = 8; out = (Byte *)&outd; break; // 64 bits
+ }
+ } else {
+#if BINIO_WITH_MATH
+ // incompatible system, do the conversion manually
+ Byte buf[8];
+
+ // Our own value is always big endian, just check whether we have to
+ // convert for a different stream format.
+ swap = !getFlag(BigEndian);
+
+ // Convert system's float to requested IEEE-754 float
+ switch(ft) {
+ case Single: size = 4; float2ieee_single(f, buf); break;
+ case Double: size = 8; float2ieee_double(f, buf); break;
+ }
+
+ out = buf; // Make the value ready for writing
+#else
+ // No necessary support routines to do the conversion, bail out!
+ err |= Unsupported; return;
+#endif
+ }
+
+ // Write the float byte by byte, converting endianess
+ if(swap) out += size - 1;
+ for(i = 0; i < size; i++) {
+ putByte(*out);
+ if(swap) out--; else out++;
+ }
+
+ return; // We're done.
+ }
+
+ // User tried to write an unsupported floating-point type. Bail out.
+ err |= Unsupported;
+}
+
+#ifdef BINIO_WITH_MATH
+
+/*
+ * Single and double floating-point to IEEE-754 equivalent conversion functions
+ * courtesy of Ken Turkowski.
+ *
+ * Copyright (C) 1989-1991 Ken Turkowski. <turk@computer.org>
+ *
+ * All rights reserved.
+ *
+ * Warranty Information
+ * Even though I have reviewed this software, I make no warranty
+ * or representation, either express or implied, with respect to this
+ * software, its quality, accuracy, merchantability, or fitness for a
+ * particular purpose. As a result, this software is provided "as is,"
+ * and you, its user, are assuming the entire risk as to its quality
+ * and accuracy.
+ *
+ * This code may be used and freely distributed as long as it includes
+ * this copyright notice and the above warranty information.
+ */
+
+/****************************************************************
+ * The following two routines make up for deficiencies in many
+ * compilers to convert properly between unsigned integers and
+ * floating-point. Some compilers which have this bug are the
+ * THINK_C compiler for the Macintosh and the C compiler for the
+ * Silicon Graphics MIPS-based Iris.
+ ****************************************************************/
+
+#ifdef applec /* The Apple C compiler works */
+# define FloatToUnsigned(f) ((unsigned long)(f))
+#else
+# define FloatToUnsigned(f) ((unsigned long)(((long)((f) - 2147483648.0)) + 2147483647L + 1))
+#endif
+
+#define SEXP_MAX 255
+#define SEXP_OFFSET 127
+#define SEXP_SIZE 8
+#define SEXP_POSITION (32-SEXP_SIZE-1)
+
+void binostream::float2ieee_single(Float num, Byte *bytes)
+{
+ long sign;
+ register long bits;
+
+ if (num < 0) { /* Can't distinguish a negative zero */
+ sign = 0x80000000;
+ num *= -1;
+ } else {
+ sign = 0;
+ }
+
+ if (num == 0) {
+ bits = 0;
+ } else {
+ Float fMant;
+ int expon;
+
+ fMant = frexp(num, &expon);
+
+ if ((expon > (SEXP_MAX-SEXP_OFFSET+1)) || !(fMant < 1)) {
+ /* NaN's and infinities fail second test */
+ bits = sign | 0x7F800000; /* +/- infinity */
+ }
+
+ else {
+ long mantissa;
+
+ if (expon < -(SEXP_OFFSET-2)) { /* Smaller than normalized */
+ int shift = (SEXP_POSITION+1) + (SEXP_OFFSET-2) + expon;
+ if (shift < 0) { /* Way too small: flush to zero */
+ bits = sign;
+ }
+ else { /* Nonzero denormalized number */
+ mantissa = (long)(fMant * (1L << shift));
+ bits = sign | mantissa;
+ }
+ }
+
+ else { /* Normalized number */
+ mantissa = (long)floor(fMant * (1L << (SEXP_POSITION+1)));
+ mantissa -= (1L << SEXP_POSITION); /* Hide MSB */
+ bits = sign | ((long)((expon + SEXP_OFFSET - 1)) << SEXP_POSITION) | mantissa;
+ }
+ }
+ }
+
+ bytes[0] = bits >> 24; /* Copy to byte string */
+ bytes[1] = bits >> 16;
+ bytes[2] = bits >> 8;
+ bytes[3] = bits;
+}
+
+#define DEXP_MAX 2047
+#define DEXP_OFFSET 1023
+#define DEXP_SIZE 11
+#define DEXP_POSITION (32-DEXP_SIZE-1)
+
+void binostream::float2ieee_double(Float num, Byte *bytes)
+{
+ long sign;
+ long first, second;
+
+ if (num < 0) { /* Can't distinguish a negative zero */
+ sign = 0x80000000;
+ num *= -1;
+ } else {
+ sign = 0;
+ }
+
+ if (num == 0) {
+ first = 0;
+ second = 0;
+ } else {
+ Float fMant, fsMant;
+ int expon;
+
+ fMant = frexp(num, &expon);
+
+ if ((expon > (DEXP_MAX-DEXP_OFFSET+1)) || !(fMant < 1)) {
+ /* NaN's and infinities fail second test */
+ first = sign | 0x7FF00000; /* +/- infinity */
+ second = 0;
+ }
+
+ else {
+ long mantissa;
+
+ if (expon < -(DEXP_OFFSET-2)) { /* Smaller than normalized */
+ int shift = (DEXP_POSITION+1) + (DEXP_OFFSET-2) + expon;
+ if (shift < 0) { /* Too small for something in the MS word */
+ first = sign;
+ shift += 32;
+ if (shift < 0) { /* Way too small: flush to zero */
+ second = 0;
+ }
+ else { /* Pretty small demorn */
+ second = FloatToUnsigned(floor(ldexp(fMant, shift)));
+ }
+ }
+ else { /* Nonzero denormalized number */
+ fsMant = ldexp(fMant, shift);
+ mantissa = (long)floor(fsMant);
+ first = sign | mantissa;
+ second = FloatToUnsigned(floor(ldexp(fsMant - mantissa, 32)));
+ }
+ }
+
+ else { /* Normalized number */
+ fsMant = ldexp(fMant, DEXP_POSITION+1);
+ mantissa = (long)floor(fsMant);
+ mantissa -= (1L << DEXP_POSITION); /* Hide MSB */
+ fsMant -= (1L << DEXP_POSITION);
+ first = sign | ((long)((expon + DEXP_OFFSET - 1)) << DEXP_POSITION) | mantissa;
+ second = FloatToUnsigned(floor(ldexp(fsMant - mantissa, 32)));
+ }
+ }
+ }
+
+ bytes[0] = first >> 24;
+ bytes[1] = first >> 16;
+ bytes[2] = first >> 8;
+ bytes[3] = first;
+ bytes[4] = second >> 24;
+ bytes[5] = second >> 16;
+ bytes[6] = second >> 8;
+ bytes[7] = second;
+}
+
+#endif // BINIO_WITH_MATH
+
+unsigned long binostream::writeString(const char *str, unsigned long amount)
+{
+ unsigned int i;
+
+ if(!amount) amount = strlen(str);
+
+ for(i = 0; i < amount; i++) {
+ putByte(str[i]);
+ if(err) return i;
+ }
+
+ return amount;
+}
+
+#if BINIO_ENABLE_STRING
+unsigned long binostream::writeString(const std::string &str)
+{
+ return writeString(str.c_str());
+}
+#endif
diff --git a/plugins/adplug/libbinio/binio.h b/plugins/adplug/libbinio/binio.h
new file mode 100644
index 00000000..31bcfac6
--- /dev/null
+++ b/plugins/adplug/libbinio/binio.h
@@ -0,0 +1,175 @@
+/* -*-C++-*-
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * binio.h - Binary stream I/O classes
+ * Copyright (C) 2002, 2003 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_BINIO_BINIO
+#define H_BINIO_BINIO
+
+/***** Configuration *****/
+
+// BINIO_ENABLE_STRING - Build std::string supporting methods
+//
+// Set to 1 to build std::string supporting methods. You need the STL to
+// do this.
+#define BINIO_ENABLE_STRING 1
+
+// BINIO_ENABLE_IOSTREAM - Build iostream wrapper classes
+//
+// Set to 1 to build the iostream wrapper classes. You need the standard
+// C++ library to do this.
+#define BINIO_ENABLE_IOSTREAM 1
+
+// BINIO_ISO_STDLIB - Build with ISO C++ standard library compliance
+//
+// Set to 1 to build for the ISO standard C++ library (i.e. namespaces, STL and
+// templatized iostream). Set to 0 to build for the traditional C++ library.
+#define BINIO_ISO_STDLIB 1
+
+// BINIO_WITH_MATH - Build with 'math.h' dependency to allow float conversions
+//
+// Set to 1 to also build routines that depend on the 'math.h' standard C header
+// file (this sometimes also implies a 'libm' or 'libmath' dependency). These
+// routines are needed in order to write IEEE-754 floating-point numbers on a
+// system that doesn't support this format natively. For only reading these
+// numbers, however, these routines are not needed. If set to 0, writing
+// IEEE-754 numbers on an incompatible system will be disabled.
+#define BINIO_WITH_MATH 1
+
+/***** Implementation *****/
+
+// Disable annoying multiple inheritance compiler warning on MSVC6
+#ifdef _MSC_VER
+# pragma warning(disable: 4250)
+#endif
+
+#if BINIO_ENABLE_STRING
+#include <string>
+#endif
+
+class binio
+{
+public:
+ typedef enum {
+ BigEndian = 1 << 0,
+ FloatIEEE = 1 << 1
+ } Flag;
+
+ typedef enum {
+ NoError = 0,
+ Fatal = 1 << 0,
+ Unsupported = 1 << 1,
+ NotOpen = 1 << 2,
+ Denied = 1 << 3,
+ NotFound = 1 << 4,
+ Eof = 1 << 5
+ } ErrorCode;
+
+ typedef enum { Set, Add, End } Offset;
+ typedef enum { Single, Double } FType;
+ typedef int Error;
+
+ binio();
+ virtual ~binio();
+
+ void setFlag(Flag f, bool set = true);
+ bool getFlag(Flag f);
+
+ Error error();
+ bool eof();
+
+ virtual void seek(long, Offset = Set) = 0;
+ virtual long pos() = 0;
+
+protected:
+ typedef long long Int;
+ typedef long double Float;
+ typedef unsigned char Byte; // has to be unsigned!
+
+ typedef int Flags;
+
+ Flags my_flags;
+ static const Flags system_flags;
+ Error err;
+
+ // Some math.h emulation functions...
+#if !BINIO_WITH_MATH
+ Float pow(Float base, signed int exp);
+ Float ldexp(Float x, signed int exp) { return x * pow(2, exp); }
+#endif
+
+private:
+ static const Flags detect_system_flags();
+};
+
+class binistream: virtual public binio
+{
+public:
+ binistream();
+ virtual ~binistream();
+
+ Int readInt(unsigned int size);
+ Float readFloat(FType ft);
+ unsigned long readString(char *str, unsigned long amount);
+ unsigned long readString(char *str, unsigned long maxlen, const char delim);
+#if BINIO_ENABLE_STRING
+ std::string readString(const char delim = '\0');
+#endif
+
+ Int peekInt(unsigned int size);
+ Float peekFloat(FType ft);
+
+ bool ateof();
+ void ignore(unsigned long amount = 1);
+
+protected:
+ virtual Byte getByte() = 0;
+
+private:
+ Float ieee_single2float(Byte *data);
+ Float ieee_double2float(Byte *data);
+};
+
+class binostream: virtual public binio
+{
+public:
+ binostream();
+ virtual ~binostream();
+
+ void writeInt(Int val, unsigned int size);
+ void writeFloat(Float f, FType ft);
+ unsigned long writeString(const char *str, unsigned long amount = 0);
+#if BINIO_ENABLE_STRING
+ unsigned long writeString(const std::string &str);
+#endif
+
+protected:
+ virtual void putByte(Byte) = 0;
+
+private:
+ void float2ieee_single(Float f, Byte *data);
+ void float2ieee_double(Float f, Byte *data);
+};
+
+class binstream: public binistream, public binostream
+{
+public:
+ binstream();
+ virtual ~binstream();
+};
+
+#endif
diff --git a/plugins/adplug/libbinio/binstr.cpp b/plugins/adplug/libbinio/binstr.cpp
new file mode 100644
index 00000000..0e3a9f86
--- /dev/null
+++ b/plugins/adplug/libbinio/binstr.cpp
@@ -0,0 +1,114 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * binstr.cpp - Binary I/O on standard C strings in memory
+ * Copyright (C) 2003 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "binstr.h"
+
+/***** binsbase *****/
+
+binsbase::binsbase(void *str, unsigned long len)
+ : data((Byte *)str), spos((Byte *)str), length(len)
+{
+}
+
+binsbase::~binsbase()
+{
+}
+
+void binsbase::seek(long p, Offset offs)
+{
+ switch(offs) {
+ case Set: spos = data + p; break;
+ case Add: spos += p; break;
+ case End: spos = data + length - 1 + p; break;
+ }
+
+ // Seek before start of data
+ if(spos < data) {
+ err |= Eof;
+ spos = data;
+ return;
+ }
+
+ // Seek after end of data
+ if(spos - data >= length) {
+ err |= Eof;
+ spos = data + length - 1;
+ }
+}
+
+long binsbase::pos()
+{
+ return (long)(spos - data);
+}
+
+/***** binisstream *****/
+
+binisstream::binisstream(void *str, unsigned long len)
+ : binsbase(str, len)
+{
+}
+
+binisstream::~binisstream()
+{
+}
+
+binisstream::Byte binisstream::getByte()
+{
+ Byte in = 0;
+
+ if(spos - data >= length)
+ err |= Eof;
+ else {
+ in = *spos;
+ spos++;
+ }
+
+ return in;
+}
+
+/***** binosstream *****/
+
+binosstream::binosstream(void *str, unsigned long len)
+ : binsbase(str, len)
+{
+}
+
+binosstream::~binosstream()
+{
+}
+
+void binosstream::putByte(Byte b)
+{
+ *spos = b;
+ spos++;
+
+ if(spos - data >= length)
+ spos = data + length - 1;
+}
+
+/***** binsstream *****/
+
+binsstream::binsstream(void *str, unsigned long len)
+ : binsbase(str, len), binisstream(str, len), binosstream(str, len)
+{
+}
+
+binsstream::~binsstream()
+{
+}
diff --git a/plugins/adplug/libbinio/binstr.h b/plugins/adplug/libbinio/binstr.h
new file mode 100644
index 00000000..33e4b12c
--- /dev/null
+++ b/plugins/adplug/libbinio/binstr.h
@@ -0,0 +1,66 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * binstr.h - Binary I/O on standard C strings in memory
+ * Copyright (C) 2003 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_BINIO_BINSTR
+#define H_BINIO_BINSTR
+
+#include "binio.h"
+
+class binsbase: virtual public binio
+{
+public:
+ binsbase(void *str, unsigned long len);
+ virtual ~binsbase();
+
+ virtual void seek(long p, Offset offs = Set);
+ virtual long pos();
+
+protected:
+ Byte *data, *spos;
+ long length;
+};
+
+class binisstream: public binistream, virtual public binsbase
+{
+public:
+ binisstream(void *str, unsigned long len);
+ virtual ~binisstream();
+
+protected:
+ virtual Byte getByte();
+};
+
+class binosstream: public binostream, virtual public binsbase
+{
+public:
+ binosstream(void *str, unsigned long len);
+ virtual ~binosstream();
+
+protected:
+ virtual void putByte(Byte b);
+};
+
+class binsstream: public binisstream, public binosstream
+{
+public:
+ binsstream(void *str, unsigned long len);
+ virtual ~binsstream();
+};
+
+#endif
diff --git a/plugins/adplug/libbinio/binwrap.cpp b/plugins/adplug/libbinio/binwrap.cpp
new file mode 100644
index 00000000..740ee982
--- /dev/null
+++ b/plugins/adplug/libbinio/binwrap.cpp
@@ -0,0 +1,132 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * binwrap.cpp - Binary I/O wrapper, using standard iostreams library
+ * Copyright (C) 2002, 2003 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <stdio.h>
+#include "binwrap.h"
+
+#if BINIO_ENABLE_IOSTREAM
+
+/***** biniwstream *****/
+
+biniwstream::biniwstream(istream *istr)
+ : in(istr)
+{
+}
+
+biniwstream::~biniwstream()
+{
+}
+
+void biniwstream::seek(long pos, Offset offs)
+{
+ if(!in) { err = NotOpen; return; }
+
+ switch(offs) {
+ case Set: in->seekg(pos, ios::beg); break;
+ case Add: in->seekg(pos, ios::cur); break;
+ case End: in->seekg(pos, ios::end); break;
+ }
+}
+
+biniwstream::Byte biniwstream::getByte()
+{
+ if(!in) { err = NotOpen; return 0; }
+
+ int i = in->get();
+ if(i == EOF) err |= Eof;
+ return (Byte)i;
+}
+
+long biniwstream::pos()
+{
+ if(!in) { err = NotOpen; return 0; }
+ return (long)in->tellg();
+}
+
+/***** binowstream *****/
+
+binowstream::binowstream(ostream *ostr)
+ : out(ostr)
+{
+}
+
+binowstream::~binowstream()
+{
+}
+
+void binowstream::seek(long pos, Offset offs)
+{
+ if(!out) { err = NotOpen; return; }
+
+ switch(offs) {
+ case Set: out->seekp(pos, ios::beg); break;
+ case Add: out->seekp(pos, ios::cur); break;
+ case End: out->seekp(pos, ios::end); break;
+ }
+}
+
+void binowstream::putByte(binio::Byte b)
+{
+ if(!out) { err = NotOpen; return; }
+ out->put((char)b);
+}
+
+long binowstream::pos()
+{
+ if(!out) { err = NotOpen; return 0; }
+ return (long)out->tellp();
+}
+
+/***** binwstream *****/
+
+binwstream::binwstream(iostream *str)
+ : biniwstream(str), binowstream(str), io(str)
+{
+}
+
+binwstream::~binwstream()
+{
+}
+
+void binwstream::seek(long pos, Offset offs)
+{
+ biniwstream::seek(pos, offs);
+ binowstream::seek(pos, offs);
+}
+
+long binwstream::pos()
+{
+ if(!io) { err = NotOpen; return 0; }
+ return (long)io->tellg();
+}
+
+binwstream::Byte binwstream::getByte()
+{
+ Byte in = biniwstream::getByte();
+ binowstream::seek(biniwstream::pos(), Set); // sync stream position
+ return in;
+}
+
+void binwstream::putByte(Byte b)
+{
+ binowstream::putByte(b);
+ biniwstream::seek(binowstream::pos(), Set); // sync stream position
+}
+
+#endif
diff --git a/plugins/adplug/libbinio/binwrap.h b/plugins/adplug/libbinio/binwrap.h
new file mode 100644
index 00000000..74b009f6
--- /dev/null
+++ b/plugins/adplug/libbinio/binwrap.h
@@ -0,0 +1,92 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * binwrap.h - Binary I/O wrapper, using standard iostreams library
+ * Copyright (C) 2002, 2003 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_BINIO_BINWRAP
+#define H_BINIO_BINWRAP
+
+#include "binio.h"
+
+#if BINIO_ENABLE_IOSTREAM
+
+#if BINIO_ISO_STDLIB
+#include <iostream>
+
+using std::iostream;
+using std::ostream;
+using std::istream;
+using std::ios;
+using std::streambuf;
+#else
+
+#include <iostream.h>
+
+#endif
+
+class biniwstream: public binistream
+{
+public:
+ biniwstream(istream *istr);
+ virtual ~biniwstream();
+
+ virtual void seek(long pos, Offset offs = Set);
+ virtual long pos();
+
+protected:
+ virtual Byte getByte();
+
+private:
+ istream *in;
+};
+
+class binowstream: public binostream
+{
+public:
+ binowstream(ostream *ostr);
+ virtual ~binowstream();
+
+ virtual void seek(long pos, Offset offs = Set);
+ virtual long pos();
+
+protected:
+ virtual void putByte(Byte b);
+
+private:
+ ostream *out;
+};
+
+class binwstream: public biniwstream, public binowstream
+{
+public:
+ binwstream(iostream *str);
+ virtual ~binwstream();
+
+ virtual void seek(long pos, Offset offs = Set);
+ virtual long pos();
+
+protected:
+ virtual Byte getByte();
+ virtual void putByte(Byte b);
+
+private:
+ iostream *io;
+};
+
+#endif
+
+#endif
diff --git a/plugins/adplug/plugin.c b/plugins/adplug/plugin.c
new file mode 100644
index 00000000..b16254bb
--- /dev/null
+++ b/plugins/adplug/plugin.c
@@ -0,0 +1,68 @@
+/*
+ DeaDBeeF - ultimate music player for GNU/Linux systems with X11
+ Copyright (C) 2009 Alexey Yakovenko
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+// this is a decoder plugin skeleton
+// use to create new decoder plugins
+
+#include "../../deadbeef.h"
+
+extern const char *adplug_exts[];
+extern const char *adplug_filetypes[];
+
+int
+adplug_init (DB_playItem_t *it);
+void
+adplug_free (void);
+int
+adplug_read_int16 (char *bytes, int size);
+int
+adplug_seek_sample (int sample);
+int
+adplug_seek (float time);
+DB_playItem_t *
+adplug_insert (DB_playItem_t *after, const char *fname);
+int
+adplug_start (void);
+int
+adplug_stop (void);
+
+// define plugin interface
+DB_decoder_t adplug_plugin = {
+ DB_PLUGIN_SET_API_VERSION
+ .plugin.version_major = 0,
+ .plugin.version_minor = 1,
+ .plugin.type = DB_PLUGIN_DECODER,
+ .plugin.name = "Adplug player",
+ .plugin.descr = "Adplug player (ADLIB OPL2/OPL3 emulator)",
+ .plugin.author = "Alexey Yakovenko",
+ .plugin.email = "waker@users.sourceforge.net",
+ .plugin.website = "http://deadbeef.sf.net",
+ .plugin.start = adplug_start,
+ .plugin.stop = adplug_stop,
+ .init = adplug_init,
+ .free = adplug_free,
+ .read_int16 = adplug_read_int16,
+ .seek = adplug_seek,
+ .seek_sample = adplug_seek_sample,
+ .insert = adplug_insert,
+ .exts = adplug_exts,
+ .id = "adplug",
+ .filetypes = adplug_filetypes
+};
+
diff --git a/plugins/alsa/Makefile.am b/plugins/alsa/Makefile.am
new file mode 100644
index 00000000..265c9b50
--- /dev/null
+++ b/plugins/alsa/Makefile.am
@@ -0,0 +1,9 @@
+if HAVE_ALSA
+alsadir = $(libdir)/$(PACKAGE)
+pkglib_LTLIBRARIES = alsa.la
+alsa_la_SOURCES = alsa.c
+alsa_la_LDFLAGS = -module
+alsa_la_LIBADD = $(LDADD) $(ALSA_DEPS_LIBS)
+
+AM_CFLAGS = $(CFLAGS) -std=c99 $(ALSA_DEPS_CFLAGS)
+endif
diff --git a/palsa.c b/plugins/alsa/alsa.c
index e214ce5a..22c3e9b3 100644
--- a/palsa.c
+++ b/plugins/alsa/alsa.c
@@ -20,19 +20,13 @@
#include <stdint.h>
#include <unistd.h>
#include <sys/prctl.h>
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-#include "palsa.h"
-#include "threading.h"
-#include "streamer.h"
-#include "conf.h"
-#include "volume.h"
-#include "messagepump.h"
#include "deadbeef.h"
-//#define trace(...) { fprintf(stderr, __VA_ARGS__); }
-#define trace(fmt,...)
+#define trace(...) { fprintf(stderr, __VA_ARGS__); }
+//#define trace(fmt,...)
+
+static DB_output_t plugin;
+DB_functions_t *deadbeef;
static snd_pcm_t *audio;
static int bufsize = -1;
@@ -49,7 +43,49 @@ static void
palsa_callback (char *stream, int len);
static void
-palsa_thread (uintptr_t context);
+palsa_thread (void *context);
+
+static int
+palsa_init (void);
+
+static int
+palsa_free (void);
+
+static int
+palsa_change_rate (int rate);
+
+static int
+palsa_play (void);
+
+static int
+palsa_stop (void);
+
+static int
+palsa_isstopped (void);
+
+static int
+palsa_ispaused (void);
+
+static int
+palsa_pause (void);
+
+static int
+palsa_unpause (void);
+
+static int
+palsa_get_rate (void);
+
+static int
+palsa_get_bps (void);
+
+static int
+palsa_get_channels (void);
+
+static int
+palsa_get_endianness (void);
+
+static void
+palsa_enum_soundcards (void (*callback)(const char *name, const char *desc, void*), void *userdata);
static int
palsa_set_hw_params (int samplerate) {
@@ -129,7 +165,7 @@ palsa_set_hw_params (int samplerate) {
trace ("Unable to get buffer size for playback: %s\n", snd_strerror(err));
goto error;
}
- trace ("alsa buffer size: %d frames\n", size);
+ trace ("alsa buffer size: %d frames\n", (int)size);
bufsize = size;
if ((err = snd_pcm_hw_params (audio, hw_params)) < 0) {
@@ -149,8 +185,8 @@ palsa_init (void) {
int err;
// get and cache conf variables
- strcpy (conf_alsa_soundcard, conf_get_str ("alsa_soundcard", "default"));
- conf_alsa_resample = conf_get_int ("alsa.resample", 0);
+ strcpy (conf_alsa_soundcard, deadbeef->conf_get_str ("alsa_soundcard", "default"));
+ conf_alsa_resample = deadbeef->conf_get_int ("alsa.resample", 0);
trace ("alsa_soundcard: %s\n", conf_alsa_soundcard);
trace ("alsa.resample: %d\n", conf_alsa_resample);
@@ -163,7 +199,7 @@ palsa_init (void) {
return -1;
}
- mutex = mutex_create ();
+ mutex = deadbeef->mutex_create ();
if (palsa_set_hw_params (alsa_rate) < 0) {
goto open_error;
@@ -191,7 +227,7 @@ palsa_init (void) {
snd_strerror (err));
goto open_error;
}
- trace ("alsa period size: %d frames\n", av);
+ trace ("alsa period size: %d frames\n", (int)av);
if ((err = snd_pcm_sw_params_set_start_threshold (audio, sw_params, 0U)) < 0) {
trace ("cannot set start mode (%s)\n",
@@ -219,7 +255,7 @@ palsa_init (void) {
snd_pcm_start (audio);
alsa_terminate = 0;
- alsa_tid = thread_start (palsa_thread, 0);
+ alsa_tid = deadbeef->thread_start (palsa_thread, NULL);
return 0;
@@ -244,13 +280,13 @@ palsa_change_rate (int rate) {
return rate;
}
trace ("trying to change samplerate to: %d\n", rate);
- mutex_lock (mutex);
+ deadbeef->mutex_lock (mutex);
snd_pcm_drop (audio);
int ret = palsa_set_hw_params (rate);
if (state != 0) {
snd_pcm_start (audio);
}
- mutex_unlock (mutex);
+ deadbeef->mutex_unlock (mutex);
if (ret < 0) {
return -1;
}
@@ -258,19 +294,21 @@ palsa_change_rate (int rate) {
return alsa_rate;
}
-void
+int
palsa_free (void) {
trace ("palsa_free\n");
if (audio && !alsa_terminate) {
alsa_terminate = 1;
- thread_join (alsa_tid);
+ printf ("waiting for alsa thread to finish\n");
+ deadbeef->thread_join (alsa_tid);
alsa_tid = 0;
snd_pcm_close(audio);
audio = NULL;
- mutex_free (mutex);
+ deadbeef->mutex_free (mutex);
state = 0;
alsa_terminate = 0;
}
+ return 0;
}
static void
@@ -322,15 +360,15 @@ palsa_stop (void) {
return 0;
}
state = 0;
- if (conf_get_int ("alsa.freeonstop", 0)) {
+ if (deadbeef->conf_get_int ("alsa.freeonstop", 0)) {
palsa_free ();
}
else {
- mutex_lock (mutex);
+ deadbeef->mutex_lock (mutex);
snd_pcm_drop (audio);
- mutex_unlock (mutex);
+ deadbeef->mutex_unlock (mutex);
}
- streamer_reset (1);
+ deadbeef->streamer_reset (1);
return 0;
}
@@ -368,11 +406,33 @@ palsa_unpause (void) {
int
palsa_get_rate (void) {
+ if (!audio) {
+ palsa_init ();
+ }
return alsa_rate;
}
+int
+palsa_get_bps (void) {
+ return 16;
+}
+
+int
+palsa_get_channels (void) {
+ return 2;
+}
+
+static int
+palsa_get_endianness (void) {
+#if WORDS_BIGENDIAN
+ return 1;
+#else
+ return 0;
+#endif
+}
+
static void
-palsa_thread (uintptr_t context) {
+palsa_thread (void *context) {
prctl (PR_SET_NAME, "deadbeef-alsa", 0, 0, 0, 0);
int err;
for (;;) {
@@ -384,21 +444,21 @@ palsa_thread (uintptr_t context) {
continue;
}
- mutex_lock (mutex);
+ deadbeef->mutex_lock (mutex);
/* wait till the interface is ready for data, or 1 second
has elapsed.
*/
if ((err = snd_pcm_wait (audio, 500)) < 0 && state == 1) {
if (err == -ESTRPIPE) {
trace ("alsa: trying to recover from suspend...\n");
- messagepump_push (M_REINIT_SOUND, 0, 0, 0);
- mutex_unlock (mutex);
+ deadbeef->sendmessage (M_REINIT_SOUND, 0, 0, 0);
+ deadbeef->mutex_unlock (mutex);
break;
}
else {
trace ("alsa: trying to recover from xrun...\n");
snd_pcm_prepare (audio);
- mutex_unlock (mutex);
+ deadbeef->mutex_unlock (mutex);
continue;
}
}
@@ -408,11 +468,11 @@ palsa_thread (uintptr_t context) {
snd_pcm_sframes_t frames_to_deliver;
if ((frames_to_deliver = snd_pcm_avail_update (audio)) < 0) {
if (frames_to_deliver == -EPIPE) {
- mutex_unlock (mutex);
+ deadbeef->mutex_unlock (mutex);
trace ("an xrun occured\n");
continue;
} else {
- mutex_unlock (mutex);
+ deadbeef->mutex_unlock (mutex);
trace ("unknown ALSA avail update return value (%d)\n",
(int)frames_to_deliver);
continue;
@@ -429,18 +489,18 @@ palsa_thread (uintptr_t context) {
snd_pcm_prepare (audio);
snd_pcm_start (audio);
}
- mutex_unlock (mutex);
+ deadbeef->mutex_unlock (mutex);
usleep (1000); // this must be here to prevent mutex deadlock
}
}
static void
palsa_callback (char *stream, int len) {
- if (!streamer_ok_to_read (len)) {
+ if (!deadbeef->streamer_ok_to_read (len)) {
memset (stream, 0, len);
return;
}
- int bytesread = streamer_read (stream, len);
+ int bytesread = deadbeef->streamer_read (stream, len);
// FIXME: move volume control to streamer_read for copy optimization
#if 0
@@ -474,7 +534,7 @@ palsa_callback (char *stream, int len) {
);
#else
- int16_t ivolume = volume_get_amp () * 1000;
+ int16_t ivolume = deadbeef->volume_get_amp () * 1000;
for (int i = 0; i < bytesread/2; i++) {
((int16_t*)stream)[i] = (int16_t)(((int32_t)(((int16_t*)stream)[i])) * ivolume / 1000);
}
@@ -484,14 +544,15 @@ palsa_callback (char *stream, int len) {
}
}
-void
-palsa_configchanged (void) {
- int alsa_resample = conf_get_int ("alsa.resample", 0);
- const char *alsa_soundcard = conf_get_str ("alsa_soundcard", "default");
+static int
+palsa_configchanged (DB_event_t *ev, uintptr_t data) {
+ int alsa_resample = deadbeef->conf_get_int ("alsa.resample", 0);
+ const char *alsa_soundcard = deadbeef->conf_get_str ("alsa_soundcard", "default");
if (alsa_resample != conf_alsa_resample
|| strcmp (alsa_soundcard, conf_alsa_soundcard)) {
- messagepump_push (M_REINIT_SOUND, 0, 0, 0);
+ deadbeef->sendmessage (M_REINIT_SOUND, 0, 0, 0);
}
+ return 0;
}
// derived from alsa-utils/aplay.c
@@ -522,3 +583,55 @@ palsa_enum_soundcards (void (*callback)(const char *name, const char *desc, void
}
snd_device_name_free_hint(hints);
}
+
+int
+palsa_get_state (void) {
+ return state;
+}
+
+int
+alsa_start (void) {
+ deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_CONFIGCHANGED, DB_CALLBACK (palsa_configchanged), 0);
+ return 0;
+}
+
+int
+alsa_stop (void) {
+ deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_CONFIGCHANGED, DB_CALLBACK (palsa_configchanged), 0);
+ return 0;
+}
+
+DB_plugin_t *
+alsa_load (DB_functions_t *api) {
+ deadbeef = api;
+ return DB_PLUGIN (&plugin);
+}
+
+// define plugin interface
+static DB_output_t plugin = {
+ DB_PLUGIN_SET_API_VERSION
+ .plugin.version_major = 0,
+ .plugin.version_minor = 1,
+ .plugin.nostop = 1,
+ .plugin.type = DB_PLUGIN_OUTPUT,
+ .plugin.name = "ALSA output plugin",
+ .plugin.descr = "plays sound through linux standard alsa library",
+ .plugin.author = "Alexey Yakovenko",
+ .plugin.email = "waker@users.sourceforge.net",
+ .plugin.website = "http://deadbeef.sf.net",
+ .plugin.start = alsa_start,
+ .plugin.stop = alsa_stop,
+ .init = palsa_init,
+ .free = palsa_free,
+ .change_rate = palsa_change_rate,
+ .play = palsa_play,
+ .stop = palsa_stop,
+ .pause = palsa_pause,
+ .unpause = palsa_unpause,
+ .state = palsa_get_state,
+ .samplerate = palsa_get_rate,
+ .bitspersample = palsa_get_bps,
+ .channels = palsa_get_channels,
+ .endianness = palsa_get_endianness,
+ .enum_soundcards = palsa_enum_soundcards,
+};
diff --git a/plugins/cdda/cdda.c b/plugins/cdda/cdda.c
index 4effd077..63b3edde 100644
--- a/plugins/cdda/cdda.c
+++ b/plugins/cdda/cdda.c
@@ -15,6 +15,9 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+
+/* screwed/maintained by Alexey Yakovenko <waker@users.sourceforge.net> */
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -44,11 +47,13 @@ static unsigned int tail_len;
static int current_sector;
static unsigned int current_sample = 0;
static uintptr_t mutex;
+static intptr_t cddb_tid;
+
+#define DEFAULT_SERVER "freedb.org"
+#define DEFAULT_PORT 888
+#define DEFAULT_USE_CDDB 1
+#define DEFAULT_PROTOCOL 1
-static int use_cddb = 1;
-static char server[1024] = "freedb.org";
-static int port = 888;
-static int proto_cddb = 1;
struct cddb_thread_params
{
@@ -72,15 +77,6 @@ trim (char* s)
}
static int
-read_config ()
-{
- use_cddb = deadbeef->conf_get_int ("cdda.freedb.enable", 1);
- strncpy (server, deadbeef->conf_get_str ("cdda.freedb.host", "freedb.org"), sizeof (server)-1);
- port = deadbeef->conf_get_int ("cdda.freedb.port", 888);
- proto_cddb = deadbeef->conf_get_int ("cdda.protocol", 1); // 1 is cddb, 0 is http
-}
-
-static int
cda_init (DB_playItem_t *it) {
// trace ("CDA: initing %s\n", it->fname);
@@ -239,10 +235,10 @@ resolve_disc (CdIo_t *cdio)
conn = cddb_new();
- cddb_set_server_name (conn, server);
- cddb_set_server_port (conn, port);
+ cddb_set_server_name (conn, deadbeef->conf_get_str ("cdda.freedb.host", DEFAULT_SERVER));
+ cddb_set_server_port (conn, deadbeef->conf_get_int ("cdda.freedb.port", DEFAULT_PORT));
- if (!proto_cddb)
+ if (!deadbeef->conf_get_int ("cdda.protocol", DEFAULT_PROTOCOL))
{
cddb_http_enable (conn);
if (deadbeef->conf_get_int ("network.proxy", 0))
@@ -298,7 +294,7 @@ insert_single_track (CdIo_t* cdio, DB_playItem_t *after, const char* file, int t
}
static void
-cddb_thread (uintptr_t items_i)
+cddb_thread (void *items_i)
{
struct cddb_thread_params *params = (struct cddb_thread_params*)items_i;
DB_playItem_t **items = params->items;
@@ -346,6 +342,7 @@ cddb_thread (uintptr_t items_i)
cddb_disc_destroy (disc);
deadbeef->mutex_unlock (mutex);
free (params);
+ cddb_tid = 0;
}
static DB_playItem_t *
@@ -398,7 +395,12 @@ cda_insert (DB_playItem_t *after, const char *fname) {
p->items[i] = res;
}
trace ("cdda: querying freedb...\n");
- deadbeef->thread_start (cddb_thread, (uintptr_t)p); //will destroy cdio
+ if (deadbeef->conf_get_int ("cdda.freedb.enable", DEFAULT_USE_CDDB)) {
+ if (cddb_tid) {
+ deadbeef->thread_join (cddb_tid);
+ }
+ cddb_tid = deadbeef->thread_start (cddb_thread, p); //will destroy cdio
+ }
}
else
{
@@ -412,16 +414,29 @@ cda_insert (DB_playItem_t *after, const char *fname) {
static int
cda_start (void) {
mutex = deadbeef->mutex_create ();
+ return 0;
}
static int
cda_stop (void) {
+ if (cddb_tid) {
+ fprintf (stderr, "cdda: waiting cddb query to end\n");
+ deadbeef->thread_join (cddb_tid);
+ }
deadbeef->mutex_free (mutex);
+ return 0;
}
static const char *exts[] = { "cda", "nrg", NULL };
static const char *filetypes[] = { "cdda", NULL };
+static const char settings_dlg[] =
+ "property \"Use CDDB/FreeDB\" checkbox cdda.freedb.enable 1;\n"
+ "property \"CDDB url (e.g. 'freedb.org')\" entry cdda.freedb.host freedb.org;\n"
+ "property \"CDDB port number (e.g. '888')\" entry cdda.freedb.port 888;\n"
+ "property \"Prefer CDDB protocol over HTTP\" checkbox cdda.protocol 1;"
+;
+
// define plugin interface
static DB_decoder_t plugin = {
DB_PLUGIN_SET_API_VERSION
@@ -430,11 +445,12 @@ static DB_decoder_t plugin = {
.plugin.type = DB_PLUGIN_DECODER,
.plugin.name = "Audio CD player",
.plugin.descr = "using libcdio, includes .nrg image support",
- .plugin.author = "Viktor Semykin",
- .plugin.email = "thesame.ml@gmail.com",
+ .plugin.author = "Viktor Semykin, Alexey Yakovenko",
+ .plugin.email = "thesame.ml@gmail.com, waker@users.sourceforge.net",
.plugin.website = "http://deadbeef.sf.net",
.plugin.start = cda_start,
.plugin.stop = cda_stop,
+ .plugin.configdialog = settings_dlg,
.init = cda_init,
.free = cda_free,
.read_int16 = cda_read_int16,
@@ -449,7 +465,7 @@ static DB_decoder_t plugin = {
DB_plugin_t *
cdda_load (DB_functions_t *api) {
deadbeef = api;
- read_config();
+// read_config();
return DB_PLUGIN (&plugin);
}
diff --git a/plugins/ffap/ffap.c b/plugins/ffap/ffap.c
index 9850f9d8..e04ca40d 100644
--- a/plugins/ffap/ffap.c
+++ b/plugins/ffap/ffap.c
@@ -42,11 +42,12 @@
//#define trace(...) { fprintf(stderr, __VA_ARGS__); }
#define trace(fmt,...)
+#define likely(x) __builtin_expect((x),1)
+#define unlikely(x) __builtin_expect((x),0)
+
static DB_decoder_t plugin;
static DB_functions_t *deadbeef;
-//static float timestart;
-//static float timeend;
static int startsample;
static int endsample;
@@ -256,7 +257,7 @@ typedef struct APEContext {
const uint8_t *ptr; ///< current position in frame data
const uint8_t *last_ptr;
- uint8_t packet_data[PACKET_BUFFER_SIZE];
+ uint8_t *packet_data; // must be PACKET_BUFFER_SIZE
int packet_remaining; // number of bytes in packet_data
int packet_sizeleft; // number of bytes left unread for current ape frame
int samplestoskip;
@@ -603,15 +604,20 @@ static int ape_read_packet(DB_FILE *fp, APEContext *ape_ctx)
else
nblocks = ape->blocksperframe;
-// if (PACKET_MAX_SIZE < ape->frames[ape->currentframe].size + extra_size) {
-// return -1;
-// }
-// packet_sizeleft = ape->frames[ape->currentframe].size + extra_size;
-
AV_WL32(ape->packet_data , nblocks);
AV_WL32(ape->packet_data + 4, ape->frames[ape->currentframe].skip);
// packet_sizeleft -= 8;
+// update bitrate
+ int bitrate = -1;
+ if (nblocks != 0 && ape->frames[ape->currentframe].size != 0) {
+ float sec = (float)nblocks / ape->samplerate;
+ bitrate = ape->frames[ape->currentframe].size / sec * 8;
+ }
+ if (bitrate > 0) {
+ deadbeef->streamer_set_bitrate (bitrate/1000);
+ }
+
int sz = PACKET_BUFFER_SIZE-8;
sz = min (sz, ape->frames[ape->currentframe].size);
// fprintf (stderr, "readsize: %d, packetsize: %d\n", sz, ape->frames[ape->currentframe].size);
@@ -627,6 +633,10 @@ static int ape_read_packet(DB_FILE *fp, APEContext *ape_ctx)
static void
ape_free_ctx (APEContext *ape_ctx) {
int i;
+ if (ape_ctx->packet_data) {
+ free (ape_ctx->packet_data);
+ ape_ctx->packet_data = NULL;
+ }
if (ape_ctx->frames) {
free (ape_ctx->frames);
ape_ctx->frames = NULL;
@@ -649,21 +659,6 @@ ffap_free (void)
ape_free_ctx (&ape_ctx);
}
-#if 0
-static int ape_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)
-{
- AVStream *st = s->streams[stream_index];
- APEContext *ape = s->priv_data;
- int index = av_index_search_timestamp(st, timestamp, flags);
-
- if (index < 0)
- return -1;
-
- ape->currentframe = index;
- return 0;
-}
-#endif
-
static DB_FILE *fp;
static int
@@ -711,17 +706,19 @@ ffap_init(DB_playItem_t *it)
if (it->endsample > 0) {
startsample = it->startsample;
endsample = it->endsample;
-// timestart = it->timestart;
-// timeend = it->timeend;
plugin.seek_sample (0);
//trace ("start: %d/%f, end: %d/%f\n", startsample, timestart, endsample, timeend);
}
else {
- //timestart = 0;
- //timeend = it->duration;
startsample = 0;
endsample = ape_ctx.totalsamples-1;
}
+
+ ape_ctx.packet_data = malloc (PACKET_BUFFER_SIZE);
+ if (!ape_ctx.packet_data) {
+ fprintf (stderr, "ffap: failed to allocate memory for packet data\n");
+ return -1;
+ }
return 0;
}
@@ -919,7 +916,14 @@ static inline int ape_decode_value(APEContext * ctx, APERice *rice)
overflow |= range_decode_bits(ctx, 16);
}
- base = range_decode_culfreq(ctx, pivot);
+// base = range_decode_culfreq(ctx, pivot);
+ range_dec_normalize(ctx);
+ ctx->rc.help = ctx->rc.range / pivot;
+ if (unlikely (ctx->rc.help == 0)) {
+ ctx->error = 1;
+ return 0;
+ }
+ base = ctx->rc.low / ctx->rc.help;
range_decode_update(ctx, 1, base);
x = base + overflow * pivot;
@@ -1507,8 +1511,6 @@ ape_decode_frame(APEContext *s, void *data, int *data_size)
}
if (s->packet_remaining < PACKET_BUFFER_SIZE) {
-// assert (packet_sizeleft >= 0 && packet_remaining >= 0);
-// if (packet_sizeleft == 0 && packet_remaining == 0) {
if (s->samples == 0) {
if (s->currentframe == s->totalframes) {
return -1;
@@ -1527,6 +1529,7 @@ ape_decode_frame(APEContext *s, void *data, int *data_size)
s->ptr = s->last_ptr = s->packet_data;
nblocks = s->samples = bytestream_get_be32(&s->ptr);
+
//fprintf (stderr, "s->samples=%d (1)\n", s->samples);
n = bytestream_get_be32(&s->ptr);
if(n < 0 || n > 3){
@@ -1536,6 +1539,7 @@ ape_decode_frame(APEContext *s, void *data, int *data_size)
s->ptr += n;
s->currentframeblocks = nblocks;
+
//buf += 4;
if (s->samples <= 0) {
*data_size = 0;
@@ -1555,10 +1559,6 @@ ape_decode_frame(APEContext *s, void *data, int *data_size)
sz = sz&~3;
uint8_t *p = s->packet_data + s->packet_remaining;
int r = deadbeef->fread (p, 1, sz, fp);
- //if (r != s) {
- // fprintf (stderr, "unexpected eof while reading ape frame\n");
- // return -1;
- //}
bswap_buf((uint32_t*)p, (const uint32_t*)p, r >> 2);
s->packet_sizeleft -= r;
s->packet_remaining += r;
@@ -1664,7 +1664,7 @@ ffap_insert (DB_playItem_t *after, const char *fname) {
it->filetype = "APE";
deadbeef->pl_set_item_duration (it, duration);
- int v2err = deadbeef->junk_read_id3v2 (it, fp);
+ /*int v2err = */deadbeef->junk_read_id3v2 (it, fp);
int v1err = deadbeef->junk_read_id3v1 (it, fp);
if (v1err >= 0) {
deadbeef->fseek (fp, -128, SEEK_END);
@@ -1672,7 +1672,7 @@ ffap_insert (DB_playItem_t *after, const char *fname) {
else {
deadbeef->fseek (fp, 0, SEEK_END);
}
- int apeerr = deadbeef->junk_read_ape (it, fp);
+ /*int apeerr = */deadbeef->junk_read_ape (it, fp);
deadbeef->fclose (fp);
@@ -1779,7 +1779,7 @@ static DB_decoder_t plugin = {
.plugin.version_minor = 1,
.plugin.type = DB_PLUGIN_DECODER,
.plugin.name = "Monkey's Audio (APE) decoder",
- .plugin.descr = "Derived from ffmpeg code by Benjamin Zores and rockbox code by Dave Chapman",
+ .plugin.descr = "APE player based on code from libavc and rockbox",
.plugin.author = "Alexey Yakovenko",
.plugin.email = "waker@users.sourceforge.net",
.plugin.website = "http://deadbeef.sf.net",
diff --git a/plugins/ffmpeg/Makefile.am b/plugins/ffmpeg/Makefile.am
new file mode 100644
index 00000000..0793990f
--- /dev/null
+++ b/plugins/ffmpeg/Makefile.am
@@ -0,0 +1,9 @@
+if HAVE_FFMPEG
+ffmpegdir = $(libdir)/$(PACKAGE)
+pkglib_LTLIBRARIES = ffmpeg.la
+ffmpeg_la_SOURCES = ffmpeg.c
+ffmpeg_la_LDFLAGS = -module
+
+ffmpeg_la_LIBADD = $(LDADD) $(FFMPEG_DEPS_LIBS)
+AM_CFLAGS = $(CFLAGS) -std=c99 ${FFMPEG_DEPS_CFLAGS}
+endif
diff --git a/plugins/ffmpeg/ffmpeg.c b/plugins/ffmpeg/ffmpeg.c
new file mode 100644
index 00000000..fcbdff78
--- /dev/null
+++ b/plugins/ffmpeg/ffmpeg.c
@@ -0,0 +1,593 @@
+/*
+ DeaDBeeF - ultimate music player for GNU/Linux systems with X11
+ Copyright (C) 2009 Alexey Yakovenko
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+#include "../../deadbeef.h"
+#include <libavformat/avformat.h>
+#include <libavcodec/avcodec.h>
+#include <libavutil/avutil.h>
+#include <libavutil/avstring.h>
+
+#define trace(...) { fprintf(stderr, __VA_ARGS__); }
+//#define trace(fmt,...)
+
+#define min(x,y) ((x)<(y)?(x):(y))
+#define max(x,y) ((x)>(y)?(x):(y))
+
+static DB_decoder_t plugin;
+static DB_functions_t *deadbeef;
+
+static const char * exts[] = { "m4a", "mpc", "mp+", "mpp", "wma", "shn", "aa3", "oma", "ac3", "vqf", NULL };
+
+enum {
+ FT_M4A = 0,
+ FT_MUSEPACK = 1,
+ FT_WMA = 2,
+ FT_SHORTEN = 3,
+ FT_ATRAC3 = 4,
+ FT_VQF = 5,
+ FT_UNKNOWN = 6
+};
+
+static const char *filetypes[] = { "M4A", "MusePack", "WMA", "Shorten", "atrac3", "VQF", "FFMPEG", NULL };
+
+#define FF_PROTOCOL_NAME "deadbeef"
+
+static AVCodec *codec;
+static AVCodecContext *ctx;
+static AVFormatContext *fctx;
+static AVPacket pkt;
+static int stream_id;
+
+static int left_in_packet;
+static int have_packet;
+
+static char *buffer; // must be AVCODEC_MAX_AUDIO_FRAME_SIZE
+static int left_in_buffer;
+
+static int startsample;
+static int endsample;
+static int currentsample;
+
+static int
+ffmpeg_init (DB_playItem_t *it) {
+ // prepare to decode the track
+ // return -1 on failure
+
+ int ret;
+ int l = strlen (it->fname);
+ char *uri = alloca (l + sizeof (FF_PROTOCOL_NAME) + 1);
+ int i;
+
+ // construct uri
+ memcpy (uri, FF_PROTOCOL_NAME, sizeof (FF_PROTOCOL_NAME)-1);
+ memcpy (uri + sizeof (FF_PROTOCOL_NAME)-1, ":", 1);
+ memcpy (uri + sizeof (FF_PROTOCOL_NAME), it->fname, l);
+ uri[sizeof (FF_PROTOCOL_NAME) + l] = 0;
+ trace ("ffmpeg: uri: %s\n", uri);
+
+ // open file
+ if ((ret = av_open_input_file(&fctx, uri, NULL, 0, NULL)) < 0) {
+ trace ("fctx is %p, ret %d/%s", fctx, ret, strerror(-ret));
+ return -1;
+ }
+
+ stream_id = -1;
+ for (i = 0; i < fctx->nb_streams; i++)
+ {
+ ctx = fctx->streams[i]->codec;
+ if (ctx->codec_type == CODEC_TYPE_AUDIO)
+ {
+ av_find_stream_info(fctx);
+ codec = avcodec_find_decoder(ctx->codec_id);
+ if (codec != NULL) {
+ stream_id = i;
+ break;
+ }
+ }
+ }
+
+ if (codec == NULL)
+ {
+ trace ("ffmpeg can't decode %s\n", it->fname);
+ av_close_input_file(fctx);
+ return -1;
+ }
+ trace ("ffmpeg can decode %s\n", it->fname);
+ trace ("ffmpeg: codec=%s, stream=%d\n", codec->name, i);
+
+ if (avcodec_open (ctx, codec) < 0) {
+ trace ("ffmpeg: avcodec_open failed\n");
+ av_close_input_file(fctx);
+ return -1;
+ }
+
+ int bps = av_get_bits_per_sample_format (ctx->sample_fmt);
+ int samplerate = ctx->sample_rate;
+ float duration = fctx->duration / (float)AV_TIME_BASE;
+ trace ("ffmpeg: bits per sample is %d\n", bps);
+ trace ("ffmpeg: samplerate is %d\n", samplerate);
+ trace ("ffmpeg: duration is %lld/%fsec\n", fctx->duration, duration);
+
+ int totalsamples = fctx->duration * samplerate / AV_TIME_BASE;
+ left_in_packet = 0;
+ left_in_buffer = 0;
+
+ memset (&pkt, 0, sizeof (pkt));
+ have_packet = 0;
+
+ buffer = malloc (AVCODEC_MAX_AUDIO_FRAME_SIZE);
+ if (!buffer) {
+ fprintf (stderr, "ffmpeg: failed to allocate buffer memory\n");
+ return -1;
+ }
+
+
+ // fill in mandatory plugin fields
+ plugin.info.readpos = 0;
+ plugin.info.bps = bps;
+ plugin.info.channels = ctx->channels;
+ plugin.info.samplerate = samplerate;
+
+ // subtrack info
+ currentsample = 0;
+ if (it->endsample > 0) {
+ startsample = it->startsample;
+ endsample = it->endsample;
+ plugin.seek_sample (0);
+ }
+ else {
+ startsample = 0;
+ endsample = totalsamples - 1;
+ }
+ return 0;
+}
+
+static void
+ffmpeg_free (void) {
+ if (buffer) {
+ free (buffer);
+ buffer = NULL;
+ }
+ // free everything allocated in _init and _read_int16
+ if (have_packet) {
+ av_free_packet (&pkt);
+ have_packet = 0;
+ }
+ left_in_buffer = 0;
+ left_in_packet = 0;
+ stream_id = -1;
+
+ if (fctx) {
+ av_close_input_file(fctx);
+ fctx = NULL;
+ }
+ if (ctx) {
+ avcodec_close (ctx);
+ ctx = NULL;
+ }
+
+ codec = NULL;
+}
+
+static int
+ffmpeg_read_int16 (char *bytes, int size) {
+ // try decode `size' bytes
+ // return number of decoded bytes
+ // return 0 on EOF
+
+ if (currentsample + size / (2 * plugin.info.channels) > endsample) {
+ size = (endsample - currentsample + 1) * 2 * plugin.info.channels;
+ if (size <= 0) {
+ return 0;
+ }
+ }
+
+ int initsize = size;
+
+ int encsize = 0;
+ int decsize = 0;
+
+ while (size > 0) {
+
+ if (left_in_buffer > 0) {
+ int sz = min (size, left_in_buffer);
+ memcpy (bytes, buffer, sz);
+ if (sz != left_in_buffer) {
+ memmove (buffer, buffer+sz, left_in_buffer-sz);
+ }
+ left_in_buffer -= sz;
+ size -= sz;
+ bytes += sz;
+ }
+
+ while (left_in_packet > 0 && size > 0) {
+ int out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
+ int len;
+ //trace ("in: out_size=%d(%d), size=%d\n", out_size, AVCODEC_MAX_AUDIO_FRAME_SIZE, size);
+#if (LIBAVCODEC_VERSION_MAJOR <= 52) && (LIBAVCODEC_VERSION_MINOR <= 25)
+ len = avcodec_decode_audio2(ctx, (int16_t *)buffer, &out_size, pkt.data, pkt.size);
+#else
+ len = avcodec_decode_audio3(ctx, (int16_t *)buffer, &out_size, &pkt);
+#endif
+ //trace ("out: out_size=%d, len=%d\n", out_size, len);
+ if (len <= 0) {
+ break;
+ }
+ encsize += len;
+ decsize += out_size;
+ left_in_packet -= len;
+ left_in_buffer = out_size;
+ }
+ if (size == 0) {
+ break;
+ }
+
+ // read next packet
+ if (have_packet) {
+ av_free_packet (&pkt);
+ have_packet = 0;
+ }
+ int errcount = 0;
+ for (;;) {
+ int ret;
+ if ((ret = av_read_frame(fctx, &pkt)) < 0) {
+ trace ("ffmpeg: error %d\n", ret);
+ if (ret == AVERROR_EOF || ret == -1) {
+ ret = -1;
+ break;
+ }
+ else {
+ if (++errcount > 4) {
+ trace ("ffmpeg: too many errors in a row (last is %d); interrupting stream\n", ret);
+ ret = -1;
+ break;
+ }
+ else {
+ continue;
+ }
+ }
+ }
+ else {
+ errcount = 0;
+ }
+ if (ret == -1) {
+ break;
+ }
+ //trace ("idx:%d, stream:%d\n", pkt.stream_index, stream_id);
+ if (pkt.stream_index != stream_id) {
+ av_free_packet (&pkt);
+ continue;
+ }
+ //trace ("got packet: size=%d\n", pkt.size);
+ have_packet = 1;
+ left_in_packet = pkt.size;
+
+ if (pkt.duration > 0) {
+ AVRational *time_base = &fctx->streams[stream_id]->time_base;
+ float sec = (float)pkt.duration * time_base->num / time_base->den;
+ int bitrate = pkt.size/sec;
+ if (bitrate > 0) {
+ // FIXME: seems like duration translation is wrong
+ deadbeef->streamer_set_bitrate (bitrate / 100);
+ }
+ }
+
+ break;
+ }
+ if (!have_packet) {
+ break;
+ }
+ }
+
+ currentsample += (initsize-size) / (2 * plugin.info.channels);
+ plugin.info.readpos = (float)currentsample / plugin.info.samplerate;
+
+ return initsize-size;
+}
+
+static int
+ffmpeg_seek_sample (int sample) {
+ // seek to specified sample (frame)
+ // return 0 on success
+ // return -1 on failure
+ if (have_packet) {
+ av_free_packet (&pkt);
+ have_packet = 0;
+ }
+ sample += startsample;
+ int64_t tm = (int64_t)sample/ plugin.info.samplerate * AV_TIME_BASE;
+ trace ("ffmpeg: seek to sample: %d, t: %d\n", sample, (int)tm);
+ left_in_packet = 0;
+ left_in_buffer = 0;
+ if (av_seek_frame(fctx, -1, tm, AVSEEK_FLAG_ANY) < 0) {
+ trace ("ffmpeg: seek error\n");
+ return -1;
+ }
+
+ // update readpos
+ currentsample = sample;
+ plugin.info.readpos = (float)(sample-startsample) / plugin.info.samplerate;
+ return 0;
+}
+
+static int
+ffmpeg_seek (float time) {
+ // seek to specified time in seconds
+ // return 0 on success
+ // return -1 on failure
+ return ffmpeg_seek_sample (time * plugin.info.samplerate);
+}
+
+static DB_playItem_t *
+ffmpeg_insert (DB_playItem_t *after, const char *fname) {
+ // read information from the track
+ // load/process cuesheet if exists
+ // insert track into playlist
+ // return track pointer on success
+ // return NULL on failure
+
+ AVCodec *codec = NULL;
+ AVCodecContext *ctx = NULL;
+ AVFormatContext *fctx = NULL;
+ int ret;
+ int l = strlen (fname);
+ char *uri = alloca (l + sizeof (FF_PROTOCOL_NAME) + 1);
+ int i;
+
+ // construct uri
+ memcpy (uri, FF_PROTOCOL_NAME, sizeof (FF_PROTOCOL_NAME)-1);
+ memcpy (uri + sizeof (FF_PROTOCOL_NAME)-1, ":", 1);
+ memcpy (uri + sizeof (FF_PROTOCOL_NAME), fname, l);
+ uri[sizeof (FF_PROTOCOL_NAME) + l] = 0;
+ trace ("ffmpeg: uri: %s\n", uri);
+
+ // open file
+ if ((ret = av_open_input_file(&fctx, uri, NULL, 0, NULL)) < 0) {
+ trace ("fctx is %p, ret %d/%s", fctx, ret, strerror(-ret));
+ return NULL;
+ }
+
+ av_find_stream_info(fctx);
+ for (i = 0; i < fctx->nb_streams; i++)
+ {
+ ctx = fctx->streams[i]->codec;
+ if (ctx->codec_type == CODEC_TYPE_AUDIO)
+ {
+ codec = avcodec_find_decoder(ctx->codec_id);
+ if (codec != NULL)
+ break;
+ }
+ }
+// AVStream *stream = fctx->streams[i];
+
+ if (codec == NULL)
+ {
+ trace ("ffmpeg can't decode %s\n", fname);
+ av_close_input_file(fctx);
+ return NULL;
+ }
+ trace ("ffmpeg can decode %s\n", fname);
+ trace ("ffmpeg: codec=%s, stream=%d\n", codec->name, i);
+
+ if (avcodec_open (ctx, codec) < 0) {
+ trace ("ffmpeg: avcodec_open failed\n");
+ av_close_input_file(fctx);
+ return NULL;
+ }
+
+ int bps = av_get_bits_per_sample_format (ctx->sample_fmt);
+ int samplerate = ctx->sample_rate;
+ float duration = fctx->duration / (float)AV_TIME_BASE;
+// float duration = stream->duration * stream->time_base.num / (float)stream->time_base.den;
+ trace ("ffmpeg: bits per sample is %d\n", bps);
+ trace ("ffmpeg: samplerate is %d\n", samplerate);
+ trace ("ffmpeg: duration is %f\n", duration);
+
+ int totalsamples = fctx->duration * samplerate / AV_TIME_BASE;
+
+ // find filetype
+ const char *filetype;
+ const char *ext = fname + strlen(fname) - 1;
+ while (ext > fname && *ext != '.') {
+ ext--;
+ }
+ if (*ext == '.') {
+ ext++;
+ }
+
+ if (!strcasecmp (ext, "m4a")) {
+ filetype = filetypes[FT_M4A];
+ }
+ else if (!strcasecmp (ext, "mpc") || !strcasecmp (ext, "mp+") || !strcasecmp (ext, "mpp")) {
+ filetype = filetypes[FT_MUSEPACK];
+ }
+ else if (!strcasecmp (ext, "wma")) {
+ filetype = filetypes[FT_WMA];
+ }
+ else if (!strcasecmp (ext, "shn")) {
+ filetype = filetypes[FT_SHORTEN];
+ }
+ else if (!strcasecmp (ext, "aa3") || !strcasecmp (ext, "oma") || !strcasecmp (ext, "ac3")) {
+ filetype = filetypes[FT_ATRAC3];
+ }
+ else if (!strcasecmp (ext, "vqf")) {
+ filetype = filetypes[FT_VQF];
+ }
+ else {
+ filetype = filetypes[FT_UNKNOWN];
+ }
+
+ // external cuesheet
+ DB_playItem_t *cue = deadbeef->pl_insert_cue (after, fname, &plugin, filetype, totalsamples, samplerate);
+ if (cue) {
+ // cuesheet loaded
+ av_close_input_file(fctx);
+ return cue;
+ }
+ DB_playItem_t *it = deadbeef->pl_item_alloc ();
+ it->decoder = &plugin;
+ it->fname = strdup (fname);
+ it->filetype = filetype;
+
+ deadbeef->pl_set_item_duration (it, duration);
+
+ // add metainfo
+#if LIBAVFORMAT_VERSION_INT < (53<<16)
+ if (!strlen (fctx->title)) {
+ // title is empty, this call will set track title to filename without extension
+ deadbeef->pl_add_meta (it, "title", NULL);
+ }
+ else {
+ deadbeef->pl_add_meta (it, "title", fctx->title);
+ }
+ deadbeef->pl_add_meta (it, "artist", fctx->author);
+ deadbeef->pl_add_meta (it, "album", fctx->album);
+ deadbeef->pl_add_meta (it, "year", fctx->album);
+ deadbeef->pl_add_meta (it, "copyright", fctx->copyright);
+ deadbeef->pl_add_meta (it, "comment", fctx->comment);
+ deadbeef->pl_add_meta (it, "genre", fctx->genre);
+
+ char tmp[10];
+ snprintf (tmp, sizeof (tmp), "%d", fctx->year);
+ deadbeef->pl_add_meta (it, "year", tmp);
+ snprintf (tmp, sizeof (tmp), "%d", fctx->track);
+ deadbeef->pl_add_meta (it, "track", tmp);
+#else
+// read using other means?
+// av_metadata_get?
+#endif
+ // free decoder
+ av_close_input_file(fctx);
+
+ // now the track is ready, insert into playlist
+ after = deadbeef->pl_insert_item (after, it);
+ return after;
+}
+
+// vfs wrapper for ffmpeg
+static int
+ffmpeg_vfs_open(URLContext *h, const char *filename, int flags)
+{
+ DB_FILE *f;
+ av_strstart(filename, FF_PROTOCOL_NAME ":", &filename);
+ if (flags & URL_WRONLY) {
+ return -ENOENT;
+ } else {
+ f = deadbeef->fopen (filename);
+ }
+
+ if (f == NULL)
+ return -ENOENT;
+
+ h->priv_data = f;
+ return 0;
+}
+
+static int
+ffmpeg_vfs_read(URLContext *h, unsigned char *buf, int size)
+{
+ int res = deadbeef->fread (buf, 1, size, h->priv_data);
+ return res;
+}
+
+static int
+ffmpeg_vfs_write(URLContext *h, unsigned char *buf, int size)
+{
+ return -1;
+}
+
+static int64_t
+ffmpeg_vfs_seek(URLContext *h, int64_t pos, int whence)
+{
+ DB_FILE *f = h->priv_data;
+
+ if (whence == AVSEEK_SIZE) {
+ return f->vfs->streaming ? -1 : deadbeef->fgetlength (h->priv_data);
+ }
+ int ret = deadbeef->fseek (h->priv_data, pos, whence);
+ return ret;
+}
+
+static int
+ffmpeg_vfs_close(URLContext *h)
+{
+ trace ("ffmpeg_vfs_close\n");
+ deadbeef->fclose (h->priv_data);
+ return 0;
+}
+
+static URLProtocol vfswrapper = {
+ .name = FF_PROTOCOL_NAME,
+ .url_open = ffmpeg_vfs_open,
+ .url_read = ffmpeg_vfs_read,
+ .url_write = ffmpeg_vfs_write,
+ .url_seek = ffmpeg_vfs_seek,
+ .url_close = ffmpeg_vfs_close,
+};
+
+static int
+ffmpeg_start (void) {
+ // do one-time plugin initialization here
+ // e.g. starting threads for background processing, subscribing to events, etc
+ // return 0 on success
+ // return -1 on failure
+ avcodec_init ();
+ av_register_all ();
+ av_register_protocol (&vfswrapper);
+ return 0;
+}
+static int
+ffmpeg_stop (void) {
+ // undo everything done in _start here
+ // return 0 on success
+ // return -1 on failure
+ trace ("ffmpeg stop\n");
+ return 0;
+}
+
+// define plugin interface
+static DB_decoder_t plugin = {
+ DB_PLUGIN_SET_API_VERSION
+ .plugin.version_major = 0,
+ .plugin.version_minor = 1,
+ .plugin.type = DB_PLUGIN_DECODER,
+ .plugin.name = "FFMPEG audio player",
+ .plugin.descr = "decodes audio formats using FFMPEG libavcodec",
+ .plugin.author = "Alexey Yakovenko",
+ .plugin.email = "waker@users.sourceforge.net",
+ .plugin.website = "http://deadbeef.sf.net",
+ .plugin.start = ffmpeg_start,
+ .plugin.stop = ffmpeg_stop,
+ .init = ffmpeg_init,
+ .free = ffmpeg_free,
+ .read_int16 = ffmpeg_read_int16,
+// .read_float32 = ffmpeg_read_float32,
+ .seek = ffmpeg_seek,
+ .seek_sample = ffmpeg_seek_sample,
+ .insert = ffmpeg_insert,
+ .exts = exts,
+ .id = "ffmpeg",
+ .filetypes = filetypes
+};
+
+DB_plugin_t *
+ffmpeg_load (DB_functions_t *api) {
+ deadbeef = api;
+ return DB_PLUGIN (&plugin);
+}
+
diff --git a/plugins/flac/flac.c b/plugins/flac/flac.c
index 11f8cfe3..4b9f1950 100644
--- a/plugins/flac/flac.c
+++ b/plugins/flac/flac.c
@@ -48,6 +48,7 @@ typedef struct {
int channels;
int totalsamples;
int bps;
+ int bytesread;
} cue_cb_data_t;
static cue_cb_data_t flac_callbacks;
@@ -56,6 +57,7 @@ static cue_cb_data_t flac_callbacks;
FLAC__StreamDecoderReadStatus flac_read_cb (const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) {
cue_cb_data_t *cb = (cue_cb_data_t *)client_data;
size_t r = deadbeef->fread (buffer, 1, *bytes, cb->file);
+ cb->bytesread += r;
*bytes = r;
if (r == 0) {
return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
@@ -91,9 +93,19 @@ FLAC__bool flac_eof_cb (const FLAC__StreamDecoder *decoder, void *client_data) {
static FLAC__StreamDecoderWriteStatus
cflac_write_callback (const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const inputbuffer[], void *client_data) {
+ cue_cb_data_t *cb = (cue_cb_data_t *)client_data;
if (frame->header.blocksize == 0) {
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}
+ int bitrate = -1;
+ float sec = ((float)frame->header.blocksize / frame->header.sample_rate);
+ if (cb->bytesread != 0 && sec != 0) {
+ bitrate = cb->bytesread / sec * 8;
+ }
+ cb->bytesread = 0;
+ if (bitrate > 0) {
+ deadbeef->streamer_set_bitrate (bitrate/1000);
+ }
int readbytes = frame->header.blocksize * plugin.info.channels * plugin.info.bps / 8;
int bufsize = BUFFERSIZE-remaining;
int bufsamples = bufsize / (plugin.info.channels * plugin.info.bps / 8);
@@ -138,17 +150,19 @@ static int cflac_init_stop_decoding;
static void
cflac_init_error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) {
- fprintf(stderr, "cflac: got error callback: %s\n", FLAC__StreamDecoderErrorStatusString[status]);
if (status != FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC) {
+ fprintf(stderr, "cflac: got error callback: %s\n", FLAC__StreamDecoderErrorStatusString[status]);
cflac_init_stop_decoding = 1;
}
}
static int
cflac_init (DB_playItem_t *it) {
+ trace ("cflac_init %s\n", it->fname);
memset (&flac_callbacks, 0, sizeof (flac_callbacks));
flac_callbacks.file = deadbeef->fopen (it->fname);
if (!flac_callbacks.file) {
+ trace ("cflac_init failed to open file\n");
return -1;
}
int skip = deadbeef->junk_get_leading_size (flac_callbacks.file);
@@ -158,10 +172,12 @@ cflac_init (DB_playItem_t *it) {
char sign[4];
if (deadbeef->fread (sign, 1, 4, flac_callbacks.file) != 4) {
plugin.free ();
+ trace ("cflac_init failed to read signature\n");
return -1;
}
if (strncmp (sign, "fLaC", 4)) {
plugin.free ();
+ trace ("cflac_init bad signature\n");
return -1;
}
deadbeef->fseek (flac_callbacks.file, -4, SEEK_CUR);
@@ -176,11 +192,13 @@ cflac_init (DB_playItem_t *it) {
status = FLAC__stream_decoder_init_stream (decoder, flac_read_cb, flac_seek_cb, flac_tell_cb, flac_lenght_cb, flac_eof_cb, cflac_write_callback, cflac_metadata_callback, cflac_error_callback, &flac_callbacks);
if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
plugin.free ();
+ trace ("cflac_init bad decoder status\n");
return -1;
}
//plugin.info.samplerate = -1;
if (!FLAC__stream_decoder_process_until_end_of_metadata (decoder)) {
plugin.free ();
+ trace ("cflac_init metadata failed\n");
return -1;
}
plugin.info.samplerate = flac_callbacks.samplerate;
@@ -189,6 +207,7 @@ cflac_init (DB_playItem_t *it) {
plugin.info.readpos = 0;
if (plugin.info.samplerate == -1) { // not a FLAC stream
plugin.free ();
+ trace ("cflac_init not a flac stream\n");
return -1;
}
buffer = malloc (BUFFERSIZE);
@@ -197,6 +216,7 @@ cflac_init (DB_playItem_t *it) {
endsample = it->endsample;
if (plugin.seek_sample (0) < 0) {
plugin.free ();
+ trace ("cflac_init failed to seek to sample 0\n");
return -1;
}
trace ("flac(cue): startsample=%d, endsample=%d, totalsamples=%d, currentsample=%d\n", startsample, endsample, flac_callbacks.totalsamples, currentsample);
@@ -234,6 +254,7 @@ cflac_read_int16 (char *bytes, int size) {
}
}
int initsize = size;
+ int nbytes_in = 0;
do {
if (remaining) {
int s = size * 2;
diff --git a/plugins/gtkui/Makefile.am b/plugins/gtkui/Makefile.am
new file mode 100644
index 00000000..504984e0
--- /dev/null
+++ b/plugins/gtkui/Makefile.am
@@ -0,0 +1,17 @@
+if HAVE_GTK
+gtkuidir = $(libdir)/$(PACKAGE)
+pkglib_LTLIBRARIES = gtkui.la
+gtkui_la_SOURCES = gtkui.c gtkui.h\
+ callbacks.c interface.c support.c callbacks.h interface.h support.h\
+ gtkplaylist.c gtkplaylist.h\
+ drawing.h gdkdrawing.c\
+ progress.c progress.h\
+ search.c search.h\
+ fileman.c\
+ parser.c parser.h
+
+gtkui_la_LDFLAGS = -module
+
+gtkui_la_LIBADD = $(LDADD) $(GTKUI_DEPS_LIBS)
+AM_CFLAGS = -std=c99 $(GTKUI_DEPS_CFLAGS)
+endif
diff --git a/callbacks.c b/plugins/gtkui/callbacks.c
index 1e7104fe..8d82b8c4 100644
--- a/callbacks.c
+++ b/plugins/gtkui/callbacks.c
@@ -32,30 +32,29 @@
#include "interface.h"
#include "support.h"
-#include "common.h"
-
-#include "playlist.h"
#include "gtkplaylist.h"
-#include "messagepump.h"
-#include "codec.h"
-#include "playback.h"
#include "search.h"
-#include "streamer.h"
#include "progress.h"
-#include "volume.h"
#include "session.h"
-#include "conf.h"
+#include "gtkui.h"
+#include "parser.h"
-#include "plugins.h"
+#define SELECTED(it) (deadbeef->pl_is_selected(it))
+#define SELECT(it, sel) (deadbeef->pl_set_selected(it,sel))
+#define VSELECT(it, sel) {deadbeef->pl_set_selected(it,sel);gtk_pl_redraw_item_everywhere (it);}
+#define PL_NEXT(it, iter) (deadbeef->pl_get_next(it, iter))
+GtkWidget *formatwin = NULL;
+gtkplaylist_t *last_playlist;
extern GtkWidget *mainwin;
extern gtkplaylist_t main_playlist;
extern gtkplaylist_t search_playlist;
+extern DB_functions_t *deadbeef; // defined in gtkui.c
gboolean
playlist_tooltip_handler (GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer unused)
{
- playItem_t *item = gtkpl_get_for_idx (&main_playlist, main_playlist.scrollpos + y / rowheight);
+ DB_playItem_t *item = deadbeef->pl_get_for_idx (main_playlist.scrollpos + y / rowheight);
if (item && item->fname) {
gtk_tooltip_set_text (tooltip, item->fname);
return TRUE;
@@ -63,6 +62,16 @@ playlist_tooltip_handler (GtkWidget *widget, gint x, gint y, gboolean keyboard_m
return FALSE;
}
+static int
+main_get_count (void) {
+ return deadbeef->pl_getcount ();
+}
+
+static int
+search_get_count (void) {
+ return search_count;
+}
+
void
main_playlist_init (GtkWidget *widget) {
// init playlist control structure, and put it into widget user-data
@@ -72,20 +81,18 @@ main_playlist_init (GtkWidget *widget) {
main_playlist.header = lookup_widget (mainwin, "header");
main_playlist.scrollbar = lookup_widget (mainwin, "playscroll");
main_playlist.hscrollbar = lookup_widget (mainwin, "playhscroll");
- main_playlist.pcurr = &playlist_current_ptr;
- main_playlist.pcount = &pl_count;
+// main_playlist.pcurr = &playlist_current_ptr;
+// main_playlist.pcount = &pl_count;
+ main_playlist.get_count = main_get_count;
main_playlist.iterator = PL_MAIN;
- main_playlist.multisel = 1;
+// main_playlist.multisel = 1;
main_playlist.scrollpos = 0;
main_playlist.hscrollpos = 0;
- main_playlist.row = -1;
+// main_playlist.row = -1;
main_playlist.clicktime = -1;
main_playlist.nvisiblerows = 0;
-// FIXME: on 1st run, copy colwidths to new columns
-// main_playlist.colwidths = session_get_main_colwidths_ptr ();
-
- DB_conf_item_t *col = conf_find ("playlist.column.", NULL);
+ DB_conf_item_t *col = deadbeef->conf_find ("playlist.column.", NULL);
if (!col) {
// create default set of columns
gtkpl_column_append (&main_playlist, gtkpl_column_alloc ("Playing", 50, DB_COLUMN_PLAYING, NULL, 0));
@@ -97,7 +104,7 @@ main_playlist_init (GtkWidget *widget) {
else {
while (col) {
gtkpl_append_column_from_textdef (&main_playlist, col->value);
- col = conf_find ("playlist.column.", col);
+ col = deadbeef->conf_find ("playlist.column.", col);
}
}
@@ -108,7 +115,7 @@ main_playlist_init (GtkWidget *widget) {
// FIXME: filepath should be in properties dialog, while tooltip should be
// used to show text that doesn't fit in column width
- if (conf_get_int ("playlist.showpathtooltip", 0)) {
+ if (deadbeef->conf_get_int ("playlist.showpathtooltip", 0)) {
GValue value = {0, };
g_value_init (&value, G_TYPE_BOOLEAN);
g_value_set_boolean (&value, TRUE);
@@ -129,20 +136,19 @@ search_playlist_init (GtkWidget *widget) {
search_playlist.hscrollbar = lookup_widget (searchwin, "searchhscroll");
assert (search_playlist.header);
assert (search_playlist.scrollbar);
- // main_playlist.pcurr = &search_current;
- search_playlist.pcount = &search_count;
- search_playlist.multisel = 0;
+// main_playlist.pcurr = &search_current;
+// search_playlist.pcount = &search_count;
+ search_playlist.get_count = search_get_count;
+// search_playlist.multisel = 0;
search_playlist.iterator = PL_SEARCH;
search_playlist.scrollpos = 0;
search_playlist.hscrollpos = 0;
- search_playlist.row = -1;
+// search_playlist.row = -1;
search_playlist.clicktime = -1;
search_playlist.nvisiblerows = 0;
-// FIXME: port to new columns
-// search_playlist.colwidths = session_get_search_colwidths_ptr ();
// create default set of columns
- DB_conf_item_t *col = conf_find ("search.column.", NULL);
+ DB_conf_item_t *col = deadbeef->conf_find ("search.column.", NULL);
if (!col) {
gtkpl_column_append (&search_playlist, gtkpl_column_alloc ("Artist / Album", 150, DB_COLUMN_ARTIST_ALBUM, NULL, 0));
gtkpl_column_append (&search_playlist, gtkpl_column_alloc ("Track â„–", 50, DB_COLUMN_TRACK, NULL, 1));
@@ -152,7 +158,7 @@ search_playlist_init (GtkWidget *widget) {
else {
while (col) {
gtkpl_append_column_from_textdef (&search_playlist, col->value);
- col = conf_find ("search.column.", col);
+ col = deadbeef->conf_find ("search.column.", col);
}
}
gtk_object_set_data (GTK_OBJECT (search_playlist.playlist), "ps", &search_playlist);
@@ -184,6 +190,95 @@ on_playlist_button_press_event (GtkWidget *widget,
if (event->button == 1) {
gtkpl_mouse1_pressed (ps, event->state, event->x, event->y, event->time);
}
+ else if (event->button == 3) {
+ // get item under cursor
+ int y = event->y / rowheight + ps->scrollpos;
+ if (y < 0 || y >= ps->get_count ()) {
+ y = -1;
+ }
+ DB_playItem_t *it = deadbeef->pl_get_for_idx_and_iter (y, ps->iterator);
+ if (!it) {
+ // clicked empty space -- deselect everything and show insensitive menu
+ it = deadbeef->pl_get_first (ps->iterator);
+ while (it) {
+ SELECT (it, 0);
+ it = PL_NEXT (it, ps->iterator);
+ }
+ playlist_refresh ();
+ // no menu
+ }
+ else {
+ if (!SELECTED (it)) {
+ // item is unselected -- reset selection and select this
+ DB_playItem_t *it2 = deadbeef->pl_get_first (ps->iterator);
+ while (it2) {
+ SELECT (it2, 0);
+ it2 = PL_NEXT (it2, ps->iterator);
+ }
+ SELECT (it, 1);
+ playlist_refresh ();
+ }
+ {
+ int inqueue = deadbeef->pl_playqueue_test (it);
+ GtkWidget *playlist_menu;
+ GtkWidget *add_to_playback_queue1;
+ GtkWidget *remove_from_playback_queue1;
+ GtkWidget *separator9;
+ GtkWidget *remove2;
+ GtkWidget *separator8;
+ GtkWidget *properties1;
+
+ playlist_menu = gtk_menu_new ();
+ add_to_playback_queue1 = gtk_menu_item_new_with_mnemonic ("Add to playback queue");
+ gtk_widget_show (add_to_playback_queue1);
+ gtk_container_add (GTK_CONTAINER (playlist_menu), add_to_playback_queue1);
+ gtk_object_set_data (GTK_OBJECT (add_to_playback_queue1), "ps", ps);
+
+ remove_from_playback_queue1 = gtk_menu_item_new_with_mnemonic ("Remove from playback queue");
+ if (inqueue == -1) {
+ gtk_widget_set_sensitive (remove_from_playback_queue1, FALSE);
+ }
+ gtk_widget_show (remove_from_playback_queue1);
+ gtk_container_add (GTK_CONTAINER (playlist_menu), remove_from_playback_queue1);
+ gtk_object_set_data (GTK_OBJECT (remove_from_playback_queue1), "ps", ps);
+
+ separator9 = gtk_separator_menu_item_new ();
+ gtk_widget_show (separator9);
+ gtk_container_add (GTK_CONTAINER (playlist_menu), separator9);
+ gtk_widget_set_sensitive (separator9, FALSE);
+
+ remove2 = gtk_menu_item_new_with_mnemonic ("Remove");
+ gtk_widget_show (remove2);
+ gtk_container_add (GTK_CONTAINER (playlist_menu), remove2);
+ gtk_object_set_data (GTK_OBJECT (remove2), "ps", ps);
+
+ separator8 = gtk_separator_menu_item_new ();
+ gtk_widget_show (separator8);
+ gtk_container_add (GTK_CONTAINER (playlist_menu), separator8);
+ gtk_widget_set_sensitive (separator8, FALSE);
+
+ properties1 = gtk_menu_item_new_with_mnemonic ("Properties");
+ gtk_widget_set_sensitive (properties1, FALSE);
+ gtk_widget_show (properties1);
+ gtk_container_add (GTK_CONTAINER (playlist_menu), properties1);
+ gtk_object_set_data (GTK_OBJECT (properties1), "ps", ps);
+
+ g_signal_connect ((gpointer) add_to_playback_queue1, "activate",
+ G_CALLBACK (on_add_to_playback_queue1_activate),
+ NULL);
+ g_signal_connect ((gpointer) remove_from_playback_queue1, "activate",
+ G_CALLBACK (on_remove_from_playback_queue1_activate),
+ NULL);
+ g_signal_connect ((gpointer) remove2, "activate",
+ G_CALLBACK (on_remove2_activate),
+ NULL);
+ g_signal_connect ((gpointer) properties1, "activate",
+ G_CALLBACK (on_properties1_activate),
+ NULL);
+ gtk_menu_popup (GTK_MENU (playlist_menu), NULL, NULL, NULL, widget, 0, gtk_get_current_event_time());
+ }
+ }
+ }
return FALSE;
}
@@ -233,7 +328,7 @@ file_filter_func (const GtkFileFilterInfo *filter_info, gpointer data) {
return FALSE;
}
p++;
- DB_decoder_t **codecs = plug_get_decoder_list ();
+ DB_decoder_t **codecs = deadbeef->plug_get_decoder_list ();
for (int i = 0; codecs[i]; i++) {
if (codecs[i]->exts && codecs[i]->insert) {
const char **exts = codecs[i]->exts;
@@ -266,29 +361,6 @@ set_file_filter (GtkWidget *dlg, const char *name) {
gtk_file_filter_set_name (flt, name);
gtk_file_filter_add_custom (flt, GTK_FILE_FILTER_FILENAME, file_filter_func, NULL, NULL);
-#if 0
- DB_decoder_t **codecs = plug_get_decoder_list ();
- for (int i = 0; codecs[i]; i++) {
- if (codecs[i]->exts && codecs[i]->insert) {
- const char **exts = codecs[i]->exts;
- if (exts) {
- for (int e = 0; exts[e]; e++) {
- char filter[20];
- snprintf (filter, 20, "*.%s", exts[e]);
- gtk_file_filter_add_pattern (flt, filter);
- char *p;
- for (p = filter; *p; p++) {
- *p = toupper (*p);
- }
- gtk_file_filter_add_pattern (flt, filter);
- }
- }
- }
- }
- gtk_file_filter_add_pattern (flt, "*.pls");
- gtk_file_filter_add_pattern (flt, "*.m3u");
-#endif
-
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dlg), flt);
gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dlg), flt);
flt = gtk_file_filter_new ();
@@ -307,21 +379,21 @@ on_open_activate (GtkMenuItem *menuitem,
gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dlg), TRUE);
// restore folder
- gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), conf_get_str ("filechooser.lastdir", ""));
+ gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str ("filechooser.lastdir", ""));
int response = gtk_dialog_run (GTK_DIALOG (dlg));
// store folder
gchar *folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dlg));
if (folder) {
- conf_set_str ("filechooser.lastdir", folder);
+ deadbeef->conf_set_str ("filechooser.lastdir", folder);
g_free (folder);
}
if (response == GTK_RESPONSE_OK)
{
- pl_free ();
+ deadbeef->pl_free ();
GSList *lst = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (dlg));
gtk_widget_destroy (dlg);
if (lst) {
- messagepump_push (M_OPENFILES, (uintptr_t)lst, 0, 0);
+ gtkui_open_files (lst);
}
}
else {
@@ -341,12 +413,12 @@ on_add_files_activate (GtkMenuItem *menuitem,
gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dlg), TRUE);
// restore folder
- gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), conf_get_str ("filechooser.lastdir", ""));
+ gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str ("filechooser.lastdir", ""));
int response = gtk_dialog_run (GTK_DIALOG (dlg));
// store folder
gchar *folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dlg));
if (folder) {
- conf_set_str ("filechooser.lastdir", folder);
+ deadbeef->conf_set_str ("filechooser.lastdir", folder);
g_free (folder);
}
if (response == GTK_RESPONSE_OK)
@@ -354,7 +426,7 @@ on_add_files_activate (GtkMenuItem *menuitem,
GSList *lst = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (dlg));
gtk_widget_destroy (dlg);
if (lst) {
- messagepump_push (M_ADDFILES, (uintptr_t)lst, 0, 0);
+ gtkui_add_files (lst);
}
}
else {
@@ -372,12 +444,12 @@ on_add_folders_activate (GtkMenuItem *menuitem,
gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dlg), TRUE);
// restore folder
- gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), conf_get_str ("filechooser.lastdir", ""));
+ gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str ("filechooser.lastdir", ""));
int response = gtk_dialog_run (GTK_DIALOG (dlg));
// store folder
gchar *folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dlg));
if (folder) {
- conf_set_str ("filechooser.lastdir", folder);
+ deadbeef->conf_set_str ("filechooser.lastdir", folder);
g_free (folder);
}
if (response == GTK_RESPONSE_OK)
@@ -386,7 +458,7 @@ on_add_folders_activate (GtkMenuItem *menuitem,
GSList *lst = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (dlg));
gtk_widget_destroy (dlg);
if (lst) {
- messagepump_push (M_ADDDIRS, (uintptr_t)lst, 0, 0);
+ gtkui_add_dirs (lst);
}
}
else {
@@ -408,7 +480,7 @@ on_quit_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
progress_abort ();
- messagepump_push (M_TERMINATE, 0, 0, 0);
+ deadbeef->sendmessage (M_TERMINATE, 0, 0, 0);
}
@@ -416,7 +488,7 @@ void
on_clear1_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
- pl_free ();
+ deadbeef->pl_free ();
gtkplaylist_t *ps = &main_playlist;
GtkWidget *widget = ps->playlist;
gtkpl_setup_scrollbar (ps);
@@ -430,7 +502,7 @@ void
on_select_all1_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
- pl_select_all ();
+ deadbeef->pl_select_all ();
gtkplaylist_t *ps = &main_playlist;
GtkWidget *widget = ps->playlist;
gtkpl_draw_playlist (ps, 0, 0, widget->allocation.width, widget->allocation.height);
@@ -444,11 +516,12 @@ on_remove1_activate (GtkMenuItem *menuitem,
{
gtkplaylist_t *ps = &main_playlist;
GtkWidget *widget = ps->playlist;
- ps->row = pl_delete_selected ();
- if (ps->row != -1) {
- playItem_t *it = pl_get_for_idx (ps->row);
+ int row = deadbeef->pl_delete_selected ();
+ deadbeef->pl_set_cursor (PL_MAIN, row);
+ if (row != -1) {
+ DB_playItem_t *it = deadbeef->pl_get_for_idx (row);
if (it) {
- it->selected = 1;
+ deadbeef->pl_set_selected (it, 1);
}
}
gtkpl_setup_scrollbar (ps);
@@ -464,7 +537,7 @@ on_crop1_activate (GtkMenuItem *menuitem,
{
gtkplaylist_t *ps = &main_playlist;
GtkWidget *widget = ps->playlist;
- pl_crop_selected ();
+ deadbeef->pl_crop_selected ();
gtkpl_setup_scrollbar (ps);
gtkpl_draw_playlist (ps, 0, 0, widget->allocation.width, widget->allocation.height);
gtkpl_expose (ps, 0, 0, widget->allocation.width, widget->allocation.height);
@@ -489,7 +562,7 @@ void
on_stopbtn_clicked (GtkButton *button,
gpointer user_data)
{
- messagepump_push (M_STOPSONG, 0, 0, 0);
+ deadbeef->sendmessage (M_STOPSONG, 0, 0, 0);
}
@@ -497,7 +570,7 @@ void
on_playbtn_clicked (GtkButton *button,
gpointer user_data)
{
- messagepump_push (M_PLAYSONG, 0, 0, 0);
+ deadbeef->sendmessage (M_PLAYSONG, 0, 0, 0);
}
@@ -505,7 +578,7 @@ void
on_pausebtn_clicked (GtkButton *button,
gpointer user_data)
{
- messagepump_push (M_PAUSESONG, 0, 0, 0);
+ deadbeef->sendmessage (M_PAUSESONG, 0, 0, 0);
}
@@ -513,7 +586,7 @@ void
on_prevbtn_clicked (GtkButton *button,
gpointer user_data)
{
- messagepump_push (M_PREVSONG, 0, 0, 0);
+ deadbeef->sendmessage (M_PREVSONG, 0, 0, 0);
}
@@ -521,7 +594,7 @@ void
on_nextbtn_clicked (GtkButton *button,
gpointer user_data)
{
- messagepump_push (M_NEXTSONG, 0, 0, 0);
+ deadbeef->sendmessage (M_NEXTSONG, 0, 0, 0);
}
@@ -529,7 +602,7 @@ void
on_playrand_clicked (GtkButton *button,
gpointer user_data)
{
- messagepump_push (M_PLAYRANDOM, 0, 0, 0);
+ deadbeef->sendmessage (M_PLAYRANDOM, 0, 0, 0);
}
@@ -538,7 +611,14 @@ on_mainwin_key_press_event (GtkWidget *widget,
GdkEventKey *event,
gpointer user_data)
{
- gtkpl_keypress (&main_playlist, event->keyval, event->state);
+
+ if (event->keyval == GDK_n) {
+ // button for that one is not in toolbar anymore, so handle it manually
+ deadbeef->sendmessage (M_PLAYRANDOM, 0, 0, 0);
+ }
+ else {
+ gtkpl_keypress (&main_playlist, event->keyval, event->state);
+ }
return FALSE;
}
@@ -598,15 +678,16 @@ on_playlist_drag_data_get (GtkWidget *widget,
case TARGET_SAMEWIDGET:
{
// format as "STRING" consisting of array of pointers
- int nsel = pl_getselcount ();
+ int nsel = deadbeef->pl_getselcount ();
if (!nsel) {
break; // something wrong happened
}
uint32_t *ptr = malloc (nsel * sizeof (uint32_t));
int idx = 0;
int i = 0;
- for (playItem_t *it = playlist_head[PL_MAIN]; it; it = it->next[PL_MAIN], idx++) {
- if (it->selected) {
+ DB_playItem_t *it = deadbeef->pl_get_first (PL_MAIN);
+ for (; it; it = deadbeef->pl_get_next (it, PL_MAIN), idx++) {
+ if (deadbeef->pl_is_selected (it)) {
ptr[i] = idx;
i++;
}
@@ -634,14 +715,22 @@ on_playlist_drag_data_received (GtkWidget *widget,
GTKPL_PROLOGUE;
gchar *ptr=(char*)data->data;
if (target_type == 0) { // uris
- fprintf (stderr, "calling gtkpl_handle_fm_drag_drop\n");
-// if (!strncmp(ptr,"file:///",8)) {
- gtkpl_handle_fm_drag_drop (ps, y, ptr, data->length);
-// }
+ // this happens when dropped from file manager
+ char *mem = malloc (data->length+1);
+ memcpy (mem, ptr, data->length);
+ mem[data->length] = 0;
+ // we don't pass control structure, but there's only one drag-drop view currently
+ gtkui_receive_fm_drop (mem, data->length, y);
}
else if (target_type == 1) {
uint32_t *d= (uint32_t *)ptr;
- gtkpl_handle_drag_drop (ps, y, d, data->length/4);
+ int length = data->length/4;
+ int drop_row = y / rowheight + ps->scrollpos;
+ DB_playItem_t *drop_before = deadbeef->pl_get_for_idx_and_iter (drop_row, ps->iterator);
+ while (drop_before && SELECTED (drop_before)) {
+ drop_before = PL_NEXT(drop_before, ps->iterator);
+ }
+ deadbeef->pl_move_items (ps->iterator, drop_before, d, length);
}
gtk_drag_finish (drag_context, TRUE, FALSE, time);
}
@@ -676,69 +765,10 @@ on_playlist_drag_leave (GtkWidget *widget,
}
void
-on_voice1_clicked (GtkButton *button,
- gpointer user_data)
-{
- codec_lock ();
- if (str_playing_song.decoder && str_playing_song.decoder->mutevoice) {
- str_playing_song.decoder->mutevoice (0, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 0 : 1);
- }
- codec_unlock ();
-}
-
-
-void
-on_voice2_clicked (GtkButton *button,
- gpointer user_data)
-{
- codec_lock ();
- if (str_playing_song.decoder && str_playing_song.decoder->mutevoice) {
- str_playing_song.decoder->mutevoice (1, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 0 : 1);
- }
- codec_unlock ();
-}
-
-
-void
-on_voice3_clicked (GtkButton *button,
- gpointer user_data)
-{
- codec_lock ();
- if (str_playing_song.decoder && str_playing_song.decoder->mutevoice) {
- str_playing_song.decoder->mutevoice (2, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 0 : 1);
- }
- codec_unlock ();
-}
-
-
-void
-on_voice4_clicked (GtkButton *button,
- gpointer user_data)
-{
- codec_lock ();
- if (str_playing_song.decoder && str_playing_song.decoder->mutevoice) {
- str_playing_song.decoder->mutevoice (3, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 0 : 1);
- }
- codec_unlock ();
-}
-
-
-void
-on_voice5_clicked (GtkButton *button,
- gpointer user_data)
-{
- codec_lock ();
- if (str_playing_song.decoder && str_playing_song.decoder->mutevoice) {
- str_playing_song.decoder->mutevoice (4, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 0 : 1);
- }
- codec_unlock ();
-}
-
-void
on_order_linear_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
- conf_set_int ("playback.order", 0);
+ deadbeef->conf_set_int ("playback.order", 0);
}
@@ -746,7 +776,7 @@ void
on_order_shuffle_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
- conf_set_int ("playback.order", 1);
+ deadbeef->conf_set_int ("playback.order", 1);
}
@@ -754,7 +784,7 @@ void
on_order_random_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
- conf_set_int ("playback.order", 2);
+ deadbeef->conf_set_int ("playback.order", 2);
}
@@ -762,7 +792,7 @@ void
on_loop_all_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
- conf_set_int ("playback.loop", 0);
+ deadbeef->conf_set_int ("playback.loop", 0);
}
@@ -770,7 +800,7 @@ void
on_loop_single_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
- conf_set_int ("playback.loop", 2);
+ deadbeef->conf_set_int ("playback.loop", 2);
}
@@ -778,7 +808,7 @@ void
on_loop_disable_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
- conf_set_int ("playback.loop", 1);
+ deadbeef->conf_set_int ("playback.loop", 1);
}
void
@@ -825,8 +855,7 @@ on_playlist_load_activate (GtkMenuItem *menuitem,
gchar *fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg));
gtk_widget_destroy (dlg);
if (fname) {
- int res = pl_load (fname);
- printf ("load result: %d\n", res);
+ int res = deadbeef->pl_load (fname);
g_free (fname);
gtkplaylist_t *ps = &main_playlist;
gtkpl_setup_scrollbar (ps);
@@ -858,8 +887,7 @@ save_playlist_as (void) {
gtk_widget_destroy (dlg);
if (fname) {
- int res = pl_save (fname);
- printf ("save as res: %d\n", res);
+ int res = deadbeef->pl_save (fname);
if (res >= 0 && strlen (fname) < 1024) {
strcpy (last_playlist_save_name, fname);
}
@@ -879,8 +907,7 @@ on_playlist_save_activate (GtkMenuItem *menuitem,
save_playlist_as ();
}
else {
- int res = pl_save (last_playlist_save_name);
- printf ("save res: %d\n", res);
+ int res = deadbeef->pl_save (last_playlist_save_name);
}
}
@@ -956,7 +983,8 @@ seekbar_draw (GtkWidget *widget) {
if (!cr) {
return;
}
- if (!str_playing_song.decoder || str_playing_song._duration < 0) {
+ DB_playItem_t *trk = deadbeef->streamer_get_playing_track ();
+ if (!trk->decoder || deadbeef->pl_get_item_duration (trk) < 0) {
clearlooks_rounded_rectangle (cr, 2, widget->allocation.height/2-4, widget->allocation.width-4, 8, 4, 0xff);
theme_set_cairo_source_rgb (cr, COLO_SEEKBAR_FRONT);
cairo_stroke (cr);
@@ -975,8 +1003,8 @@ seekbar_draw (GtkWidget *widget) {
pos = x;
}
else {
- if (str_playing_song.decoder && str_playing_song._duration > 0) {
- pos = streamer_get_playpos () / str_playing_song._duration;
+ if (trk->decoder && deadbeef->pl_get_item_duration (trk) > 0) {
+ pos = deadbeef->streamer_get_playpos () / deadbeef->pl_get_item_duration (trk);
pos *= widget->allocation.width;
}
}
@@ -1047,7 +1075,7 @@ on_seekbar_button_press_event (GtkWidget *widget,
GdkEventButton *event,
gpointer user_data)
{
- if (p_isstopped ()) {
+ if (deadbeef->get_output ()->state () == OUTPUT_STATE_STOPPED) {
return FALSE;
}
seekbar_moving = 1;
@@ -1066,12 +1094,12 @@ on_seekbar_button_release_event (GtkWidget *widget,
seekbar_moving = 0;
seekbar_draw (widget);
seekbar_expose (widget, 0, 0, widget->allocation.width, widget->allocation.height);
- float time = event->x * str_playing_song._duration / (widget->allocation.width);
+ DB_playItem_t *trk = deadbeef->streamer_get_playing_track ();
+ float time = event->x * deadbeef->pl_get_item_duration (trk) / (widget->allocation.width);
if (time < 0) {
time = 0;
}
- streamer_set_seek (time);
-// messagepump_push (M_SONGSEEK, 0, time * 1000, 0);
+ deadbeef->streamer_seek (time);
return FALSE;
}
@@ -1090,9 +1118,9 @@ volumebar_draw (GtkWidget *widget) {
if (!cr) {
return;
}
- float range = -volume_get_min_db ();
+ float range = -deadbeef->volume_get_min_db ();
int n = widget->allocation.width / 4;
- float vol = (range + volume_get_db ()) / range * n;
+ float vol = (range + deadbeef->volume_get_db ()) / range * n;
float h = 16;
for (int i = 0; i < n; i++) {
float iy = (float)i + 3;
@@ -1144,7 +1172,7 @@ on_volumebar_motion_notify_event (GtkWidget *widget,
gpointer user_data)
{
if (event->state & GDK_BUTTON1_MASK) {
- float range = -volume_get_min_db ();
+ float range = -deadbeef->volume_get_min_db ();
float volume = event->x / widget->allocation.width * range - range;
if (volume > 0) {
volume = 0;
@@ -1152,7 +1180,7 @@ on_volumebar_motion_notify_event (GtkWidget *widget,
if (volume < -range) {
volume = -range;
}
- volume_set_db (volume);
+ deadbeef->volume_set_db (volume);
volumebar_draw (widget);
volumebar_expose (widget, 0, 0, widget->allocation.width, widget->allocation.height);
}
@@ -1164,7 +1192,7 @@ on_volumebar_button_press_event (GtkWidget *widget,
GdkEventButton *event,
gpointer user_data)
{
- float range = -volume_get_min_db ();
+ float range = -deadbeef->volume_get_min_db ();
float volume = event->x / widget->allocation.width * range - range;
if (volume < -range) {
volume = -range;
@@ -1172,7 +1200,7 @@ on_volumebar_button_press_event (GtkWidget *widget,
if (volume > 0) {
volume = 0;
}
- volume_set_db (volume);
+ deadbeef->volume_set_db (volume);
volumebar_draw (widget);
volumebar_expose (widget, 0, 0, widget->allocation.width, widget->allocation.height);
return FALSE;
@@ -1199,12 +1227,12 @@ on_mainwin_delete_event (GtkWidget *widget,
GdkEvent *event,
gpointer user_data)
{
- int conf_close_send_to_tray = conf_get_int ("close_send_to_tray", 0);
+ int conf_close_send_to_tray = deadbeef->conf_get_int ("close_send_to_tray", 0);
if (conf_close_send_to_tray) {
gtk_widget_hide (widget);
}
else {
- messagepump_push (M_TERMINATE, 0, 0, 0);
+ deadbeef->sendmessage (M_TERMINATE, 0, 0, 0);
}
return TRUE;
}
@@ -1217,8 +1245,8 @@ on_volumebar_scroll_event (GtkWidget *widget,
GdkEventScroll *event,
gpointer user_data)
{
- float range = -volume_get_min_db ();
- float vol = volume_get_db ();
+ float range = -deadbeef->volume_get_min_db ();
+ float vol = deadbeef->volume_get_db ();
if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_RIGHT) {
vol += 1;
}
@@ -1231,7 +1259,7 @@ on_volumebar_scroll_event (GtkWidget *widget,
else if (vol < -range) {
vol = -range;
}
- volume_set_db (vol);
+ deadbeef->volume_set_db (vol);
GtkWidget *volumebar = lookup_widget (mainwin, "volumebar");
volumebar_draw (volumebar);
volumebar_expose (volumebar, 0, 0, volumebar->allocation.width, volumebar->allocation.height);
@@ -1245,7 +1273,21 @@ on_mainwin_configure_event (GtkWidget *widget,
GdkEventConfigure *event,
gpointer user_data)
{
- session_capture_window_attrs ((uintptr_t)widget);
+#if GTK_CHECK_VERSION(2,2,0)
+ GdkWindowState window_state = gdk_window_get_state (GDK_WINDOW (mainwin->window));
+#else
+ GdkWindowState window_state = gdk_window_get_state (G_OBJECT (mainwin));
+#endif
+ if (!(window_state & GDK_WINDOW_STATE_MAXIMIZED) && GTK_WIDGET_VISIBLE (mainwin)) {
+ int x, y;
+ int w, h;
+ gtk_window_get_position (GTK_WINDOW (mainwin), &x, &y);
+ gtk_window_get_size (GTK_WINDOW (mainwin), &w, &h);
+ deadbeef->conf_set_int ("mainwin.geometry.x", x);
+ deadbeef->conf_set_int ("mainwin.geometry.y", y);
+ deadbeef->conf_set_int ("mainwin.geometry.w", w);
+ deadbeef->conf_set_int ("mainwin.geometry.h", h);
+ }
return FALSE;
}
@@ -1254,7 +1296,7 @@ void
on_scroll_follows_playback_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
- conf_set_int ("playlist.scroll.followplayback", gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem)));
+ deadbeef->conf_set_int ("playlist.scroll.followplayback", gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem)));
}
@@ -1371,7 +1413,7 @@ void
on_add_audio_cd_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
- pl_add_file ("all.cda", NULL, NULL);
+ deadbeef->pl_add_file ("all.cda", NULL, NULL);
playlist_refresh ();
}
@@ -1389,7 +1431,7 @@ gtk_enum_sound_callback (const char *name, const char *desc, void *userdata) {
GtkComboBox *combobox = GTK_COMBO_BOX (userdata);
gtk_combo_box_append_text (combobox, desc);
- if (!strcmp (conf_get_str ("alsa_soundcard", "default"), name)) {
+ if (!strcmp (deadbeef->conf_get_str ("alsa_soundcard", "default"), name)) {
gtk_combo_box_set_active (combobox, num_alsa_devices);
}
@@ -1399,50 +1441,103 @@ gtk_enum_sound_callback (const char *name, const char *desc, void *userdata) {
}
void
-on_preferences_activate (GtkMenuItem *menuitem,
- gpointer user_data)
-{
- GtkWidget *w = prefwin = create_prefwin ();
- gtk_window_set_transient_for (GTK_WINDOW (w), GTK_WINDOW (mainwin));
-
- // alsa_soundcard
+on_plugin_active_toggled (GtkCellRendererToggle *cell_renderer, gchar *path, GtkTreeModel *model) {
+ GtkTreePath *p = gtk_tree_path_new_from_string (path);
+ if (p) {
+ int *indices = gtk_tree_path_get_indices (p);
+ //gtk_tree_path_free (p); // wtf?? gtk crashes on this
+ if (indices) {
+ DB_plugin_t **plugins = deadbeef->plug_get_list ();
+ DB_plugin_t *plug = plugins[*indices];
+ gboolean state;
+ GtkTreeIter iter;
+ gtk_tree_model_get_iter (model, &iter, p);
+ gtk_tree_model_get (model, &iter, 0, &state, -1);
+ if (!deadbeef->plug_activate (plug, !state)) {
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, !state, -1);
+ }
+ }
+ g_free (indices);
+ }
+}
- const char *s = conf_get_str ("alsa_soundcard", "default");
+void
+preferences_fill_soundcards (void) {
+ GtkWidget *w = prefwin;
+ if (!prefwin) {
+ return;
+ }
+ const char *s = deadbeef->conf_get_str ("alsa_soundcard", "default");
GtkComboBox *combobox = GTK_COMBO_BOX (lookup_widget (w, "pref_soundcard"));
+ GtkTreeModel *mdl = gtk_combo_box_get_model (combobox);
+ gtk_list_store_clear (GTK_LIST_STORE (mdl));
+
gtk_combo_box_append_text (combobox, "Default Audio Device");
if (!strcmp (s, "default")) {
gtk_combo_box_set_active (combobox, 0);
}
num_alsa_devices = 1;
strcpy (alsa_device_names[0], "default");
- palsa_enum_soundcards (gtk_enum_sound_callback, combobox);
+ if (deadbeef->get_output ()->enum_soundcards) {
+ deadbeef->get_output ()->enum_soundcards (gtk_enum_sound_callback, combobox);
+ gtk_widget_set_sensitive (GTK_WIDGET (combobox), TRUE);
+ }
+ else {
+ gtk_widget_set_sensitive (GTK_WIDGET (combobox), FALSE);
+ }
+}
+
+void
+on_preferences_activate (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+ GtkWidget *w = prefwin = create_prefwin ();
+ gtk_window_set_transient_for (GTK_WINDOW (w), GTK_WINDOW (mainwin));
+
+ GtkComboBox *combobox = NULL;;
+
+ // output plugin selection
+ const char *outplugname = deadbeef->conf_get_str ("output_plugin", "ALSA output plugin");
+ combobox = GTK_COMBO_BOX (lookup_widget (w, "pref_output_plugin"));
+
+ DB_output_t **out_plugs = deadbeef->plug_get_output_list ();
+ for (int i = 0; out_plugs[i]; i++) {
+ gtk_combo_box_append_text (combobox, out_plugs[i]->plugin.name);
+ if (!strcmp (outplugname, out_plugs[i]->plugin.name)) {
+ gtk_combo_box_set_active (combobox, i);
+ }
+ }
+
+ // soundcard (output device) selection
+ preferences_fill_soundcards ();
+
// alsa resampling
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "pref_alsa_resampling")), conf_get_int ("alsa.resample", 0));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "pref_alsa_resampling")), deadbeef->conf_get_int ("alsa.resample", 0));
// alsa freeonstop
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "pref_alsa_freewhenstopped")), conf_get_int ("alsa.freeonstop", 0));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "pref_alsa_freewhenstopped")), deadbeef->conf_get_int ("alsa.freeonstop", 0));
// src_quality
combobox = GTK_COMBO_BOX (lookup_widget (w, "pref_src_quality"));
- gtk_combo_box_set_active (combobox, conf_get_int ("src_quality", 2));
+ gtk_combo_box_set_active (combobox, deadbeef->conf_get_int ("src_quality", 2));
// replaygain_mode
combobox = GTK_COMBO_BOX (lookup_widget (w, "pref_replaygain_mode"));
- gtk_combo_box_set_active (combobox, conf_get_int ("replaygain_mode", 0));
+ gtk_combo_box_set_active (combobox, deadbeef->conf_get_int ("replaygain_mode", 0));
// replaygain_scale
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "pref_replaygain_scale")), conf_get_int ("replaygain_scale", 1));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "pref_replaygain_scale")), deadbeef->conf_get_int ("replaygain_scale", 1));
// close_send_to_tray
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "pref_close_send_to_tray")), conf_get_int ("close_send_to_tray", 0));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "pref_close_send_to_tray")), deadbeef->conf_get_int ("close_send_to_tray", 0));
// network
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "pref_network_enableproxy")), conf_get_int ("network.proxy", 0));
- gtk_entry_set_text (GTK_ENTRY (lookup_widget (w, "pref_network_proxyaddress")), conf_get_str ("network.proxy.address", ""));
- gtk_entry_set_text (GTK_ENTRY (lookup_widget (w, "pref_network_proxyport")), conf_get_str ("network.proxy.port", "8080"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "pref_network_enableproxy")), deadbeef->conf_get_int ("network.proxy", 0));
+ gtk_entry_set_text (GTK_ENTRY (lookup_widget (w, "pref_network_proxyaddress")), deadbeef->conf_get_str ("network.proxy.address", ""));
+ gtk_entry_set_text (GTK_ENTRY (lookup_widget (w, "pref_network_proxyport")), deadbeef->conf_get_str ("network.proxy.port", "8080"));
combobox = GTK_COMBO_BOX (lookup_widget (w, "pref_network_proxytype"));
- const char *type = conf_get_str ("network.proxy.type", "HTTP");
+ const char *type = deadbeef->conf_get_str ("network.proxy.type", "HTTP");
if (!strcasecmp (type, "HTTP")) {
gtk_combo_box_set_active (combobox, 0);
}
@@ -1464,16 +1559,22 @@ on_preferences_activate (GtkMenuItem *menuitem,
// list of plugins
GtkTreeView *tree = GTK_TREE_VIEW (lookup_widget (w, "pref_pluginlist"));
- GtkListStore *store = gtk_list_store_new (1, G_TYPE_STRING);//GTK_LIST_STORE (gtk_tree_view_get_model (tree));
- GtkCellRenderer *rend = gtk_cell_renderer_text_new ();
- GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes ("Title", rend, "text", 0, NULL);
- gtk_tree_view_append_column (tree, col);
- DB_plugin_t **plugins = plug_get_list ();
+ GtkListStore *store = gtk_list_store_new (3, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_BOOLEAN);
+ GtkCellRenderer *rend_toggle = gtk_cell_renderer_toggle_new ();
+ GtkCellRenderer *rend_text = gtk_cell_renderer_text_new ();
+ g_signal_connect ((gpointer)rend_toggle, "toggled",
+ G_CALLBACK (on_plugin_active_toggled),
+ store);
+ GtkTreeViewColumn *col1 = gtk_tree_view_column_new_with_attributes ("Active", rend_toggle, "active", 0, "activatable", 2, NULL);
+ GtkTreeViewColumn *col2 = gtk_tree_view_column_new_with_attributes ("Title", rend_text, "text", 1, NULL);
+ gtk_tree_view_append_column (tree, col1);
+ gtk_tree_view_append_column (tree, col2);
+ DB_plugin_t **plugins = deadbeef->plug_get_list ();
int i;
for (i = 0; plugins[i]; i++) {
GtkTreeIter it;
gtk_list_store_append (store, &it);
- gtk_list_store_set (store, &it, 0, plugins[i]->name, -1);
+ gtk_list_store_set (store, &it, 0, plugins[i]->inactive ? FALSE : TRUE, 1, plugins[i]->name, 2, plugins[i]->nostop ? FALSE : TRUE, -1);
}
gtk_tree_view_set_model (tree, GTK_TREE_MODEL (store));
@@ -1487,8 +1588,42 @@ on_pref_soundcard_changed (GtkComboBox *combobox,
{
int active = gtk_combo_box_get_active (combobox);
if (active >= 0 && active < num_alsa_devices) {
- conf_set_str ("alsa_soundcard", alsa_device_names[active]);
- messagepump_push (M_CONFIGCHANGED, 0, 0, 0);
+ const char *soundcard = deadbeef->conf_get_str ("alsa_soundcard", "default");
+ if (strcmp (soundcard, alsa_device_names[active])) {
+ deadbeef->conf_set_str ("alsa_soundcard", alsa_device_names[active]);
+ deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0);
+ }
+ }
+}
+
+void
+on_pref_output_plugin_changed (GtkComboBox *combobox,
+ gpointer user_data)
+{
+ const char *outplugname = deadbeef->conf_get_str ("output_plugin", "ALSA output plugin");
+ int active = gtk_combo_box_get_active (combobox);
+
+ DB_output_t **out_plugs = deadbeef->plug_get_output_list ();
+ DB_output_t *prev = NULL;
+ DB_output_t *new = NULL;
+
+ for (int i = 0; out_plugs[i]; i++) {
+ if (!strcmp (out_plugs[i]->plugin.name, outplugname)) {
+ prev = out_plugs[i];
+ }
+ if (i == active) {
+ new = out_plugs[i];
+ }
+ }
+
+ if (!new) {
+ fprintf (stderr, "failed to find output plugin selected in preferences window\n");
+ }
+ else {
+ if (prev != new) {
+ deadbeef->conf_set_str ("output_plugin", new->plugin.name);
+ deadbeef->sendmessage (M_REINIT_SOUND, 0, 0, 0);
+ }
}
}
@@ -1497,8 +1632,8 @@ on_pref_alsa_resampling_clicked (GtkButton *button,
gpointer user_data)
{
int active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
- conf_set_int ("alsa.resample", active);
- messagepump_push (M_CONFIGCHANGED, 0, 0, 0);
+ deadbeef->conf_set_int ("alsa.resample", active);
+ deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0);
}
@@ -1507,8 +1642,8 @@ on_pref_src_quality_changed (GtkComboBox *combobox,
gpointer user_data)
{
int active = gtk_combo_box_get_active (combobox);
- conf_set_int ("src_quality", active == -1 ? 2 : active);
- messagepump_push (M_CONFIGCHANGED, 0, 0, 0);
+ deadbeef->conf_set_int ("src_quality", active == -1 ? 2 : active);
+ deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0);
}
@@ -1517,8 +1652,8 @@ on_pref_replaygain_mode_changed (GtkComboBox *combobox,
gpointer user_data)
{
int active = gtk_combo_box_get_active (combobox);
- conf_set_int ("replaygain_mode", active == -1 ? 0 : active);
- messagepump_push (M_CONFIGCHANGED, 0, 0, 0);
+ deadbeef->conf_set_int ("replaygain_mode", active == -1 ? 0 : active);
+ deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0);
}
void
@@ -1526,8 +1661,8 @@ on_pref_replaygain_scale_clicked (GtkButton *button,
gpointer user_data)
{
int active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
- conf_set_int ("replaygain_scale", active);
- messagepump_push (M_CONFIGCHANGED, 0, 0, 0);
+ deadbeef->conf_set_int ("replaygain_scale", active);
+ deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0);
}
@@ -1536,15 +1671,8 @@ on_pref_close_send_to_tray_clicked (GtkButton *button,
gpointer user_data)
{
int active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
- conf_set_int ("close_send_to_tray", active);
- messagepump_push (M_CONFIGCHANGED, 0, 0, 0);
-}
-
-
-void
-on_pref_plugin_configure_activate (GtkButton *button,
- gpointer user_data)
-{
+ deadbeef->conf_set_int ("close_send_to_tray", active);
+ deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0);
}
void
@@ -1559,8 +1687,9 @@ on_pref_pluginlist_cursor_changed (GtkTreeView *treeview,
return;
}
int *indices = gtk_tree_path_get_indices (path);
- DB_plugin_t **plugins = plug_get_list ();
+ DB_plugin_t **plugins = deadbeef->plug_get_list ();
DB_plugin_t *p = plugins[*indices];
+ g_free (indices);
assert (p);
GtkWidget *w = prefwin;//GTK_WIDGET (gtk_widget_get_parent_window (GTK_WIDGET (treeview)));
assert (w);
@@ -1574,13 +1703,42 @@ on_pref_pluginlist_cursor_changed (GtkTreeView *treeview,
gtk_entry_set_text (e, p->website ? p->website : "");
}
+gboolean
+on_prefwin_delete_event (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ prefwin = NULL;
+ return FALSE;
+}
+
+void
+pl_add_column (const char *title, int width, int id, const char *format, int align_right)
+{
+ gtkplaylist_t *ps = last_playlist;
+
+ gtkpl_column_append (ps, gtkpl_column_alloc (title, width, id, format, align_right));
+ gtkpl_header_draw (ps);
+ gtkpl_expose_header (ps, 0, 0, ps->header->allocation.width, ps->header->allocation.height);
+
+ gtkpl_draw_playlist (ps, 0, 0, ps->playlist->allocation.width, ps->playlist->allocation.height);
+ gtkpl_expose (ps, 0, 0, ps->playlist->allocation.width, ps->playlist->allocation.height);
+
+ gtkpl_column_rewrite_config (ps);
+}
void
on_artist_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
-
+ GtkWidget *parent = GTK_WIDGET (menuitem);
+ do
+ {
+ parent = gtk_widget_get_parent (parent);
+ printf ("parent: %x\n", parent);
+ } while (parent);
+ pl_add_column ("Artist", 100, DB_COLUMN_ARTIST, NULL, 0);
}
@@ -1588,7 +1746,7 @@ void
on_album_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
-
+ pl_add_column ("Album", 100, DB_COLUMN_ALBUM, NULL, 0);
}
@@ -1596,7 +1754,7 @@ void
on_tracknum_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
-
+ pl_add_column ("Track â„–", 50, DB_COLUMN_TRACK, NULL, 0);
}
@@ -1604,7 +1762,7 @@ void
on_duration_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
-
+ pl_add_column ("Duration", 50, DB_COLUMN_DURATION, NULL, 0);
}
@@ -1612,7 +1770,7 @@ void
on_playing_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
-
+ pl_add_column ("Playing", 50, DB_COLUMN_PLAYING, NULL, 0);
}
@@ -1620,7 +1778,7 @@ void
on_title_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
-
+ pl_add_column ("Title", 150, DB_COLUMN_TITLE, NULL, 0);
}
@@ -1628,7 +1786,9 @@ void
on_custom_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
-
+ if (!formatwin)
+ formatwin = create_inputformat ();
+ gtk_widget_show (formatwin);
}
@@ -1636,7 +1796,20 @@ void
on_remove_column_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
+ gtkplaylist_t *ps = last_playlist;
+ if (!ps->active_column)
+ return;
+
+ gtkpl_column_remove (ps, ps->active_column);
+
+ gtkpl_header_draw (ps);
+ gtkpl_expose_header (ps, 0, 0, ps->header->allocation.width, ps->header->allocation.height);
+
+ gtkpl_draw_playlist (ps, 0, 0, ps->playlist->allocation.width, ps->playlist->allocation.height);
+ gtkpl_expose (ps, 0, 0, ps->playlist->allocation.width, ps->playlist->allocation.height);
+
+ gtkpl_column_rewrite_config (ps);
}
@@ -1645,14 +1818,14 @@ on_pref_alsa_freewhenstopped_clicked (GtkButton *button,
gpointer user_data)
{
int active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
- conf_set_int ("alsa.freeonstop", active);
+ deadbeef->conf_set_int ("alsa.freeonstop", active);
}
void
on_pref_network_proxyaddress_changed (GtkEditable *editable,
gpointer user_data)
{
- conf_set_str ("network.proxy.address", gtk_entry_get_text (GTK_ENTRY (editable)));
+ deadbeef->conf_set_str ("network.proxy.address", gtk_entry_get_text (GTK_ENTRY (editable)));
}
@@ -1660,7 +1833,7 @@ void
on_pref_network_enableproxy_clicked (GtkButton *button,
gpointer user_data)
{
- conf_set_int ("network.proxy", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)));
+ deadbeef->conf_set_int ("network.proxy", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)));
}
@@ -1668,7 +1841,7 @@ void
on_pref_network_proxyport_changed (GtkEditable *editable,
gpointer user_data)
{
- conf_set_int ("network.proxy.port", atoi (gtk_entry_get_text (GTK_ENTRY (editable))));
+ deadbeef->conf_set_int ("network.proxy.port", atoi (gtk_entry_get_text (GTK_ENTRY (editable))));
}
@@ -1680,25 +1853,25 @@ on_pref_network_proxytype_changed (GtkComboBox *combobox,
int active = gtk_combo_box_get_active (combobox);
switch (active) {
case 0:
- conf_set_str ("network.proxy.type", "HTTP");
+ deadbeef->conf_set_str ("network.proxy.type", "HTTP");
break;
case 1:
- conf_set_str ("network.proxy.type", "HTTP_1_0");
+ deadbeef->conf_set_str ("network.proxy.type", "HTTP_1_0");
break;
case 2:
- conf_set_str ("network.proxy.type", "SOCKS4");
+ deadbeef->conf_set_str ("network.proxy.type", "SOCKS4");
break;
case 3:
- conf_set_str ("network.proxy.type", "SOCKS5");
+ deadbeef->conf_set_str ("network.proxy.type", "SOCKS5");
break;
case 4:
- conf_set_str ("network.proxy.type", "SOCKS4A");
+ deadbeef->conf_set_str ("network.proxy.type", "SOCKS4A");
break;
case 5:
- conf_set_str ("network.proxy.type", "SOCKS5_HOSTNAME");
+ deadbeef->conf_set_str ("network.proxy.type", "SOCKS5_HOSTNAME");
break;
default:
- conf_set_str ("network.proxy.type", "HTTP");
+ deadbeef->conf_set_str ("network.proxy.type", "HTTP");
break;
}
}
@@ -1743,7 +1916,7 @@ on_addlocation_entry_activate (GtkEntry *entry,
{
const char *text = gtk_entry_get_text (entry);
if (text) {
- pl_add_file (text, NULL, NULL);
+ deadbeef->pl_add_file (text, NULL, NULL);
playlist_refresh ();
}
add_location_destroy ();
@@ -1758,7 +1931,7 @@ on_addlocation_ok_clicked (GtkButton *button,
if (entry) {
const char *text = gtk_entry_get_text (entry);
if (text) {
- pl_add_file (text, NULL, NULL);
+ deadbeef->pl_add_file (text, NULL, NULL);
playlist_refresh ();
}
}
@@ -1778,7 +1951,336 @@ on_addlocation_key_press_event (GtkWidget *widget,
}
+void
+on_prop_entry_changed(GtkEditable *editable, gpointer user_data) {
+ const char *key = g_object_get_data (G_OBJECT (editable), "key");
+ if (key) {
+ deadbeef->conf_set_str (key, gtk_entry_get_text (GTK_ENTRY (editable)));
+ }
+}
+
+void
+on_prop_checkbox_clicked (GtkButton *button, gpointer user_data) {
+ const char *key = g_object_get_data (G_OBJECT (button), "key");
+ if (key) {
+ deadbeef->conf_set_int (key, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)));
+ }
+}
+
+void
+on_prop_browse_file (GtkButton *button, gpointer user_data) {
+ GtkWidget *dlg = gtk_file_chooser_dialog_new ("Open file...", GTK_WINDOW (mainwin), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL);
+
+ gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dlg), FALSE);
+ // restore folder
+ gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str ("filechooser.lastdir", ""));
+ int response = gtk_dialog_run (GTK_DIALOG (dlg));
+ // store folder
+ gchar *folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dlg));
+ if (folder) {
+ deadbeef->conf_set_str ("filechooser.lastdir", folder);
+ g_free (folder);
+ }
+ if (response == GTK_RESPONSE_OK) {
+ gchar *file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg));
+ gtk_widget_destroy (dlg);
+ if (file) {
+ GtkWidget *entry = GTK_WIDGET (user_data);
+ gtk_entry_set_text (GTK_ENTRY (entry), file);
+ g_free (file);
+ }
+ }
+ else {
+ gtk_widget_destroy (dlg);
+ }
+}
+
+gboolean
+on_plug_prefwin_key_press_event (GtkWidget *widget, GdkEventKey *event, gpointer user_data) {
+ if (event->keyval == GDK_Escape) {
+ gtk_widget_destroy (widget);
+ }
+ return FALSE;
+}
+
+void
+plugin_configure (GtkWidget *parentwin, DB_plugin_t *p) {
+ // create window
+ GtkWidget *win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+// gtk_widget_set_events (win, GDK_KEY_PRESS_MASK);
+ gtk_widget_set_size_request (win, 300, -1);
+ g_signal_connect ((gpointer) win, "key_press_event", G_CALLBACK (on_plug_prefwin_key_press_event), NULL);
+ char title[200];
+ snprintf (title, sizeof (title), "Setup %s", p->name);
+ gtk_window_set_title (GTK_WINDOW (win), title);
+ gtk_window_set_modal (GTK_WINDOW (win), TRUE);
+ gtk_window_set_transient_for (GTK_WINDOW (win), GTK_WINDOW (parentwin));
+ GtkWidget *tbl;
+ tbl = gtk_table_new (1, 2, FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (tbl), 3);
+ gtk_table_set_col_spacings (GTK_TABLE (tbl), 3);
+ gtk_container_add (GTK_CONTAINER (win), tbl);
+
+ int nrows = 0;
+ // parse script
+ char token[MAX_TOKEN];
+ const char *script = p->configdialog;
+ parser_line = 1;
+ while (script = gettoken (script, token)) {
+ if (strcmp (token, "property")) {
+ fprintf (stderr, "invalid token while loading plugin %s config dialog: %s at line %d\n", p->name, token, parser_line);
+ break;
+ }
+ char labeltext[MAX_TOKEN];
+ script = gettoken_warn_eof (script, labeltext);
+ if (!script) {
+ break;
+ }
+ char type[MAX_TOKEN];
+ script = gettoken_warn_eof (script, type);
+ if (!script) {
+ break;
+ }
+ char key[MAX_TOKEN];
+ script = gettoken_warn_eof (script, key);
+ if (!script) {
+ break;
+ }
+ char def[MAX_TOKEN];
+ script = gettoken_warn_eof (script, def);
+ if (!script) {
+ break;
+ }
+ script = gettoken_warn_eof (script, token);
+ if (!script) {
+ break;
+ }
+ if (strcmp (token, ";")) {
+ fprintf (stderr, "expected `;' while loading plugin %s config dialog: %s at line %d\n", p->name, token, parser_line);
+ break;
+ }
+
+ // add to dialog
+ nrows++;
+ gtk_table_resize (GTK_TABLE (tbl), nrows, 2);
+ GtkWidget *label;
+ GtkWidget *prop;
+ GtkWidget *cont = NULL;
+ label = gtk_label_new (labeltext);
+ if (!strcmp (type, "entry") || !strcmp (type, "password")) {
+ prop = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (prop), deadbeef->conf_get_str (key, def));
+ g_signal_connect ((gpointer) prop, "changed",
+ G_CALLBACK (on_prop_entry_changed),
+ NULL);
+ }
+ else if (!strcmp (type, "checkbox")) {
+ prop = gtk_check_button_new ();
+ int val = deadbeef->conf_get_int (key, atoi (def));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (prop), val);
+ g_signal_connect ((gpointer) prop, "clicked",
+ G_CALLBACK (on_prop_checkbox_clicked),
+ NULL);
+ }
+ else if (!strcmp (type, "file")) {
+ cont = gtk_hbox_new (FALSE, FALSE);
+ prop = gtk_entry_new ();
+ gtk_editable_set_editable (GTK_EDITABLE (prop), FALSE);
+ g_signal_connect ((gpointer) prop, "changed",
+ G_CALLBACK (on_prop_entry_changed),
+ NULL);
+ gtk_entry_set_text (GTK_ENTRY (prop), deadbeef->conf_get_str (key, def));
+ gtk_box_pack_start (GTK_BOX (cont), prop, TRUE, TRUE, 0);
+ GtkWidget *btn = gtk_button_new_with_label ("…");
+ gtk_box_pack_start (GTK_BOX (cont), btn, FALSE, FALSE, 0);
+ g_signal_connect (G_OBJECT (btn), "clicked", G_CALLBACK (on_prop_browse_file), prop);
+ }
+ if (!strcmp (type, "password")) {
+ gtk_entry_set_visibility (GTK_ENTRY (prop), FALSE);
+ }
+ if (!cont) {
+ cont = prop;
+ }
+ if (label && prop) {
+ char *keydup = strdup (key);
+ g_object_set_data_full (G_OBJECT (prop), "key", keydup, (GDestroyNotify)free);
+ gtk_table_attach (GTK_TABLE (tbl), label, 0, 1, nrows-1, nrows, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions)0, 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ gtk_table_attach (GTK_TABLE (tbl), cont, 1, 2, nrows-1, nrows, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions)0, 0, 0);
+ }
+ }
+
+
+ gtk_widget_show_all (win);
+}
+
+void
+on_configure_plugin_clicked (GtkButton *button,
+ gpointer user_data)
+{
+ GtkWidget *w = prefwin;
+ GtkTreeView *treeview = GTK_TREE_VIEW (lookup_widget (w, "pref_pluginlist"));
+ GtkTreePath *path;
+ GtkTreeViewColumn *col;
+ gtk_tree_view_get_cursor (treeview, &path, &col);
+ if (!path || !col) {
+ // reset
+ return;
+ }
+ int *indices = gtk_tree_path_get_indices (path);
+ DB_plugin_t **plugins = deadbeef->plug_get_list ();
+ DB_plugin_t *p = plugins[*indices];
+ if (p->configdialog) {
+ plugin_configure (prefwin, p);
+ }
+}
+
+
+gboolean
+on_mainwin_window_state_event (GtkWidget *widget,
+ GdkEventWindowState *event,
+ gpointer user_data)
+{
+ // based on pidgin maximization handler
+#if GTK_CHECK_VERSION(2,2,0)
+ if (event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) {
+ if (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) {
+ deadbeef->conf_set_int ("mainwin.geometry.maximized", 1);
+ }
+ else {
+ deadbeef->conf_set_int ("mainwin.geometry.maximized", 0);
+ }
+ }
+#else
+ GdkWindowState new_window_state = gdk_window_get_state(G_OBJECT(widget));
+
+ if (new_window_state & GDK_WINDOW_STATE_MAXIMIZED) {
+ deadbeef->conf_set_int ("mainwin.geometry.maximized", 1);
+ }
+ else {
+ deadbeef->conf_set_int ("mainwin.geometry.maximized", 0);
+ }
+#endif
+ return FALSE;
+}
+
+
+void
+on_toggle_status_bar_activate (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+ GtkWidget *sb = lookup_widget (mainwin, "statusbar");
+ if (sb) {
+ if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem))) {
+ deadbeef->conf_set_int ("gtkui.statusbar.visible", 0);
+ gtk_widget_hide (sb);
+ }
+ else {
+ deadbeef->conf_set_int ("gtkui.statusbar.visible", 1);
+ gtk_widget_show (sb);
+ }
+ }
+}
+
+void
+on_toggle_column_headers_activate (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+ GtkWidget *header = lookup_widget (mainwin, "header");
+ if (header) {
+ if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem))) {
+ deadbeef->conf_set_int ("gtkui.headers.visible", 0);
+ gtk_widget_hide (header);
+ }
+ else {
+ deadbeef->conf_set_int ("gtkui.headers.visible", 1);
+ gtk_widget_show (header);
+ }
+ }
+}
+
+void
+on_stop_after_current_activate (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+ deadbeef->conf_set_int ("playlist.stop_after_current", gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem)));
+}
+
+
+void
+on_add_to_playback_queue1_activate (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+ GtkWidget *widget = GTK_WIDGET (menuitem);
+ GTKPL_PROLOGUE;
+ DB_playItem_t *it = deadbeef->pl_get_first (ps->iterator);
+ while (it) {
+ if (SELECTED (it)) {
+ deadbeef->pl_playqueue_push (it);
+ }
+ it = PL_NEXT (it, ps->iterator);
+ }
+ playlist_refresh ();
+}
+void
+on_remove_from_playback_queue1_activate
+ (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+ GtkWidget *widget = GTK_WIDGET (menuitem);
+ GTKPL_PROLOGUE;
+ DB_playItem_t *it = deadbeef->pl_get_first (ps->iterator);
+ while (it) {
+ if (SELECTED (it)) {
+ deadbeef->pl_playqueue_remove (it);
+ }
+ it = PL_NEXT (it, ps->iterator);
+ }
+ playlist_refresh ();
+}
+
+
+void
+on_remove2_activate (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+}
+
+void
+on_properties1_activate (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+
+}
+
+
+
+void
+on_format_cancel_clicked (GtkButton *button,
+ gpointer user_data)
+{
+ gtk_widget_hide (formatwin);
+}
+
+
+void
+on_format_ok_clicked (GtkButton *button,
+ gpointer user_data)
+{
+ const gchar *title = gtk_entry_get_text (GTK_ENTRY (lookup_widget (formatwin, "titleentry")));
+ const gchar *format = gtk_entry_get_text (GTK_ENTRY (lookup_widget (formatwin, "formatentry")));
+ pl_add_column (title, 100, -1, format, 0);
+ gtk_widget_hide (formatwin);
+}
+
+
+void
+on_cursor_follows_playback_activate (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+ deadbeef->conf_set_int ("playlist.scroll.cursorfollowplayback", gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem)));
+}
diff --git a/callbacks.h b/plugins/gtkui/callbacks.h
index d2e3f61a..188cfe8a 100644
--- a/callbacks.h
+++ b/plugins/gtkui/callbacks.h
@@ -692,3 +692,70 @@ on_addlocation_ok_clicked (GtkButton *button,
void
on_addlocation_entry_activate (GtkEntry *entry,
gpointer user_data);
+
+void
+on_configure_plugin_clicked (GtkButton *button,
+ gpointer user_data);
+
+gboolean
+on_mainwin_window_state_event (GtkWidget *widget,
+ GdkEventWindowState *event,
+ gpointer user_data);
+
+void
+on_pref_output_plugin_changed (GtkComboBox *combobox,
+ gpointer user_data);
+
+void
+on_toggle_status_bar_activate (GtkMenuItem *menuitem,
+ gpointer user_data);
+
+void
+on_toggle_menu_activate (GtkMenuItem *menuitem,
+ gpointer user_data);
+
+void
+on_toggle_column_headers_activate (GtkMenuItem *menuitem,
+ gpointer user_data);
+
+void
+on_stop_after_current1_activate (GtkMenuItem *menuitem,
+ gpointer user_data);
+
+void
+on_stop_after_current_activate (GtkMenuItem *menuitem,
+ gpointer user_data);
+
+void
+on_add_to_playback_queue1_activate (GtkMenuItem *menuitem,
+ gpointer user_data);
+
+void
+on_remove_from_playback_queue1_activate
+ (GtkMenuItem *menuitem,
+ gpointer user_data);
+
+void
+on_remove2_activate (GtkMenuItem *menuitem,
+ gpointer user_data);
+
+void
+on_properties1_activate (GtkMenuItem *menuitem,
+ gpointer user_data);
+
+gboolean
+on_prefwin_delete_event (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data);
+
+void
+on_format_cancel_clicked (GtkButton *button,
+ gpointer user_data);
+
+void
+on_format_ok_clicked (GtkButton *button,
+ gpointer user_data);
+
+void
+on_cursor_follows_playback_activate (GtkMenuItem *menuitem,
+ gpointer user_data);
diff --git a/deadbeef.glade b/plugins/gtkui/deadbeef.glade
index b48af591..e017976b 100644
--- a/deadbeef.glade
+++ b/plugins/gtkui/deadbeef.glade
@@ -25,6 +25,7 @@
<signal name="key_press_event" handler="on_mainwin_key_press_event" last_modification_time="Thu, 30 Jul 2009 21:14:26 GMT"/>
<signal name="delete_event" handler="on_mainwin_delete_event" last_modification_time="Thu, 13 Aug 2009 20:35:55 GMT"/>
<signal name="configure_event" handler="on_mainwin_configure_event" last_modification_time="Sun, 23 Aug 2009 15:26:53 GMT"/>
+ <signal name="window_state_event" handler="on_mainwin_window_state_event" last_modification_time="Wed, 09 Dec 2009 19:39:55 GMT"/>
<child>
<widget class="GtkVBox" id="vbox1">
@@ -56,7 +57,7 @@
<accelerator key="O" modifiers="GDK_CONTROL_MASK" signal="activate"/>
<child internal-child="image">
- <widget class="GtkImage" id="image120">
+ <widget class="GtkImage" id="image290">
<property name="visible">True</property>
<property name="stock">gtk-open</property>
<property name="icon_size">1</property>
@@ -83,7 +84,7 @@
<signal name="activate" handler="on_add_files_activate" last_modification_time="Sat, 04 Jul 2009 13:04:01 GMT"/>
<child internal-child="image">
- <widget class="GtkImage" id="image121">
+ <widget class="GtkImage" id="image291">
<property name="visible">True</property>
<property name="stock">gtk-add</property>
<property name="icon_size">1</property>
@@ -104,7 +105,7 @@
<signal name="activate" handler="on_add_folders_activate" last_modification_time="Sun, 06 Sep 2009 17:51:40 GMT"/>
<child internal-child="image">
- <widget class="GtkImage" id="image122">
+ <widget class="GtkImage" id="image292">
<property name="visible">True</property>
<property name="stock">gtk-add</property>
<property name="icon_size">1</property>
@@ -125,7 +126,7 @@
<signal name="activate" handler="on_add_audio_cd_activate" last_modification_time="Sat, 10 Oct 2009 15:29:22 GMT"/>
<child internal-child="image">
- <widget class="GtkImage" id="image123">
+ <widget class="GtkImage" id="image293">
<property name="visible">True</property>
<property name="stock">gtk-add</property>
<property name="icon_size">1</property>
@@ -154,6 +155,39 @@
</child>
<child>
+ <widget class="GtkMenuItem" id="playlist_load">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Load playlist</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_playlist_load_activate" last_modification_time="Sun, 09 Aug 2009 19:04:52 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="playlist_save">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Save playlist</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_playlist_save_activate" last_modification_time="Sun, 09 Aug 2009 19:04:52 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="playlist_save_as">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Save playlist as</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_playlist_save_as_activate" last_modification_time="Sun, 09 Aug 2009 19:04:52 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator8">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
<widget class="GtkImageMenuItem" id="quit">
<property name="visible">True</property>
<property name="label" translatable="yes">_Quit</property>
@@ -162,7 +196,7 @@
<accelerator key="Q" modifiers="GDK_CONTROL_MASK" signal="activate"/>
<child internal-child="image">
- <widget class="GtkImage" id="image124">
+ <widget class="GtkImage" id="image294">
<property name="visible">True</property>
<property name="stock">gtk-quit</property>
<property name="icon_size">1</property>
@@ -182,7 +216,7 @@
<child>
<widget class="GtkMenuItem" id="edit1">
<property name="visible">True</property>
- <property name="label" translatable="yes">Edit</property>
+ <property name="label" translatable="yes">_Edit</property>
<property name="use_underline">True</property>
<child>
@@ -196,7 +230,7 @@
<signal name="activate" handler="on_clear1_activate" last_modification_time="Sun, 06 Sep 2009 18:30:03 GMT"/>
<child internal-child="image">
- <widget class="GtkImage" id="image125">
+ <widget class="GtkImage" id="image295">
<property name="visible">True</property>
<property name="stock">gtk-clear</property>
<property name="icon_size">1</property>
@@ -237,7 +271,7 @@
<accelerator key="Delete" modifiers="0" signal="activate"/>
<child internal-child="image">
- <widget class="GtkImage" id="image126">
+ <widget class="GtkImage" id="image296">
<property name="visible">True</property>
<property name="stock">gtk-remove</property>
<property name="icon_size">1</property>
@@ -293,40 +327,46 @@
</child>
<child>
- <widget class="GtkMenuItem" id="playlist1">
+ <widget class="GtkMenuItem" id="view1">
<property name="visible">True</property>
- <property name="label" translatable="yes">Playlist</property>
+ <property name="label" translatable="yes">_View</property>
<property name="use_underline">True</property>
<child>
- <widget class="GtkMenu" id="playlist1_menu">
+ <widget class="GtkMenu" id="view1_menu">
<child>
- <widget class="GtkMenuItem" id="playlist_load">
+ <widget class="GtkCheckMenuItem" id="view_status_bar">
<property name="visible">True</property>
- <property name="label" translatable="yes">Load</property>
+ <property name="label" translatable="yes">Status bar</property>
<property name="use_underline">True</property>
- <signal name="activate" handler="on_playlist_load_activate" last_modification_time="Sun, 09 Aug 2009 19:04:52 GMT"/>
+ <property name="active">False</property>
+ <signal name="activate" handler="on_toggle_status_bar_activate" last_modification_time="Sun, 13 Dec 2009 17:23:50 GMT"/>
</widget>
</child>
<child>
- <widget class="GtkMenuItem" id="playlist_save">
+ <widget class="GtkCheckMenuItem" id="view_headers">
<property name="visible">True</property>
- <property name="label" translatable="yes">Save</property>
+ <property name="label" translatable="yes">Column headers</property>
<property name="use_underline">True</property>
- <signal name="activate" handler="on_playlist_save_activate" last_modification_time="Sun, 09 Aug 2009 19:04:52 GMT"/>
+ <property name="active">False</property>
+ <signal name="activate" handler="on_toggle_column_headers_activate" last_modification_time="Sun, 13 Dec 2009 17:24:47 GMT"/>
</widget>
</child>
+ </widget>
+ </child>
+ </widget>
+ </child>
- <child>
- <widget class="GtkMenuItem" id="playlist_save_as">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Save As</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="on_playlist_save_as_activate" last_modification_time="Sun, 09 Aug 2009 19:04:52 GMT"/>
- </widget>
- </child>
+ <child>
+ <widget class="GtkMenuItem" id="playback1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Playback</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="playback1_menu">
<child>
<widget class="GtkMenuItem" id="order1">
@@ -427,6 +467,27 @@
<signal name="activate" handler="on_scroll_follows_playback_activate" last_modification_time="Wed, 02 Sep 2009 17:46:43 GMT"/>
</widget>
</child>
+
+ <child>
+ <widget class="GtkCheckMenuItem" id="cursor_follows_playback">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Cursor follows playback</property>
+ <property name="use_underline">True</property>
+ <property name="active">False</property>
+ <signal name="activate" handler="on_cursor_follows_playback_activate" last_modification_time="Sat, 26 Dec 2009 16:48:26 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkCheckMenuItem" id="stop_after_current">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Stop after current</property>
+ <property name="use_underline">True</property>
+ <property name="active">False</property>
+ <signal name="activate" handler="on_stop_after_current_activate" last_modification_time="Sun, 20 Dec 2009 13:32:19 GMT"/>
+ <accelerator key="M" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+ </widget>
+ </child>
</widget>
</child>
</widget>
@@ -458,7 +519,7 @@
<signal name="activate" handler="on_help1_activate" last_modification_time="Tue, 08 Sep 2009 17:32:06 GMT"/>
<child internal-child="image">
- <widget class="GtkImage" id="image127">
+ <widget class="GtkImage" id="image297">
<property name="visible">True</property>
<property name="stock">gtk-help</property>
<property name="icon_size">1</property>
@@ -715,7 +776,7 @@
<widget class="GtkDrawingArea" id="header">
<property name="height_request">24</property>
<property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<signal name="expose_event" handler="on_header_expose_event" last_modification_time="Thu, 06 Aug 2009 14:54:29 GMT"/>
<signal name="configure_event" handler="on_header_configure_event" last_modification_time="Thu, 06 Aug 2009 14:54:33 GMT"/>
<signal name="realize" handler="on_header_realize" last_modification_time="Thu, 06 Aug 2009 14:54:41 GMT"/>
@@ -840,7 +901,7 @@
<property name="events">GDK_KEY_PRESS_MASK</property>
<property name="title" translatable="yes">Search</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
- <property name="window_position">GTK_WIN_POS_CENTER_ALWAYS</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
@@ -925,7 +986,7 @@
<widget class="GtkDrawingArea" id="searchheader">
<property name="height_request">24</property>
<property name="visible">True</property>
- <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<signal name="button_press_event" handler="on_header_button_press_event" last_modification_time="Sat, 08 Aug 2009 22:38:34 GMT"/>
<signal name="button_release_event" handler="on_header_button_release_event" last_modification_time="Sat, 08 Aug 2009 22:38:38 GMT"/>
<signal name="configure_event" handler="on_header_configure_event" last_modification_time="Sat, 08 Aug 2009 22:38:42 GMT"/>
@@ -1340,6 +1401,7 @@
<property name="focus_on_map">True</property>
<property name="urgency_hint">False</property>
<signal name="key_press_event" handler="on_prefwin_key_press_event" last_modification_time="Sat, 07 Nov 2009 15:47:40 GMT"/>
+ <signal name="delete_event" handler="on_prefwin_delete_event" last_modification_time="Tue, 22 Dec 2009 20:16:22 GMT"/>
<child>
<widget class="GtkNotebook" id="notebook2">
@@ -1355,16 +1417,16 @@
<widget class="GtkTable" id="table3">
<property name="border_width">3</property>
<property name="visible">True</property>
- <property name="n_rows">6</property>
+ <property name="n_rows">7</property>
<property name="n_columns">2</property>
<property name="homogeneous">False</property>
<property name="row_spacing">0</property>
<property name="column_spacing">3</property>
<child>
- <widget class="GtkLabel" id="label4">
+ <widget class="GtkLabel" id="label15">
<property name="visible">True</property>
- <property name="label" translatable="yes">Output device</property>
+ <property name="label" translatable="yes">Release ALSA while stopped</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@@ -1382,17 +1444,17 @@
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
- <property name="top_attach">0</property>
- <property name="bottom_attach">1</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label5">
+ <widget class="GtkLabel" id="label9">
<property name="visible">True</property>
- <property name="label" translatable="yes">Software ALSA resampling</property>
+ <property name="label" translatable="yes">Replaygain peak scale</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@@ -1410,17 +1472,17 @@
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label6">
+ <widget class="GtkLabel" id="label8">
<property name="visible">True</property>
- <property name="label" translatable="yes">SRC quality (libsamplerate)</property>
+ <property name="label" translatable="yes">Replaygain mode</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@@ -1438,17 +1500,17 @@
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label8">
+ <widget class="GtkLabel" id="label6">
<property name="visible">True</property>
- <property name="label" translatable="yes">Replaygain mode</property>
+ <property name="label" translatable="yes">SRC quality (libsamplerate)</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@@ -1474,9 +1536,9 @@
</child>
<child>
- <widget class="GtkLabel" id="label9">
+ <widget class="GtkLabel" id="label5">
<property name="visible">True</property>
- <property name="label" translatable="yes">Replaygain peak scale</property>
+ <property name="label" translatable="yes">Software ALSA resampling</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@@ -1494,17 +1556,17 @@
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
- <property name="top_attach">4</property>
- <property name="bottom_attach">5</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label15">
+ <widget class="GtkLabel" id="label4">
<property name="visible">True</property>
- <property name="label" translatable="yes">Release ALSA while stopped</property>
+ <property name="label" translatable="yes">Output device</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@@ -1522,37 +1584,59 @@
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
- <property name="top_attach">5</property>
- <property name="bottom_attach">6</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkCheckButton" id="pref_alsa_resampling">
+ <widget class="GtkLabel" id="label23">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="label" translatable="yes"></property>
- <property name="use_underline">True</property>
- <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="label" translatable="yes">Output plugin</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="pref_soundcard">
+ <property name="visible">True</property>
+ <property name="add_tearoffs">False</property>
<property name="focus_on_click">True</property>
- <property name="active">False</property>
- <property name="inconsistent">False</property>
- <property name="draw_indicator">True</property>
- <signal name="clicked" handler="on_pref_alsa_resampling_clicked" last_modification_time="Sat, 24 Oct 2009 16:50:34 GMT"/>
+ <signal name="changed" handler="on_pref_soundcard_changed" last_modification_time="Sat, 07 Nov 2009 14:12:28 GMT"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
- <property name="y_options"></property>
+ <property name="y_options">fill</property>
</packing>
</child>
<child>
- <widget class="GtkCheckButton" id="pref_replaygain_scale">
+ <widget class="GtkCheckButton" id="pref_alsa_resampling">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes"></property>
@@ -1562,19 +1646,19 @@
<property name="active">False</property>
<property name="inconsistent">False</property>
<property name="draw_indicator">True</property>
- <signal name="clicked" handler="on_pref_replaygain_scale_clicked" last_modification_time="Sat, 10 Oct 2009 18:52:10 GMT"/>
+ <signal name="clicked" handler="on_pref_alsa_resampling_clicked" last_modification_time="Sat, 24 Oct 2009 16:50:34 GMT"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">4</property>
- <property name="bottom_attach">5</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkCheckButton" id="pref_alsa_freewhenstopped">
+ <widget class="GtkCheckButton" id="pref_replaygain_scale">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes"></property>
@@ -1584,7 +1668,7 @@
<property name="active">False</property>
<property name="inconsistent">False</property>
<property name="draw_indicator">True</property>
- <signal name="clicked" handler="on_pref_alsa_freewhenstopped_clicked" last_modification_time="Sat, 07 Nov 2009 11:37:02 GMT"/>
+ <signal name="clicked" handler="on_pref_replaygain_scale_clicked" last_modification_time="Sat, 10 Oct 2009 18:52:10 GMT"/>
</widget>
<packing>
<property name="left_attach">1</property>
@@ -1596,18 +1680,24 @@
</child>
<child>
- <widget class="GtkComboBox" id="pref_soundcard">
+ <widget class="GtkCheckButton" id="pref_alsa_freewhenstopped">
<property name="visible">True</property>
- <property name="add_tearoffs">False</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
- <signal name="changed" handler="on_pref_soundcard_changed" last_modification_time="Sat, 07 Nov 2009 14:12:28 GMT"/>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="clicked" handler="on_pref_alsa_freewhenstopped_clicked" last_modification_time="Sat, 07 Nov 2009 11:37:02 GMT"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">0</property>
- <property name="bottom_attach">1</property>
- <property name="y_options">fill</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="y_options"></property>
</packing>
</child>
@@ -1626,8 +1716,8 @@ linear</property>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
<property name="y_options">fill</property>
</packing>
</child>
@@ -1645,8 +1735,25 @@ Album</property>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="pref_output_plugin">
+ <property name="visible">True</property>
+ <property name="add_tearoffs">False</property>
+ <property name="focus_on_click">True</property>
+ <signal name="changed" handler="on_pref_output_plugin_changed" last_modification_time="Fri, 11 Dec 2009 21:05:28 GMT"/>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
<property name="y_options">fill</property>
</packing>
</child>
@@ -2012,11 +2119,13 @@ SOCKS5_HOSTNAME</property>
<child>
<widget class="GtkHPaned" id="hpaned1">
+ <property name="border_width">3</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow2">
+ <property name="border_width">3</property>
<property name="width_request">280</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
@@ -2030,7 +2139,7 @@ SOCKS5_HOSTNAME</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">True</property>
- <property name="rules_hint">False</property>
+ <property name="rules_hint">True</property>
<property name="reorderable">False</property>
<property name="enable_search">True</property>
<property name="fixed_height_mode">False</property>
@@ -2048,9 +2157,10 @@ SOCKS5_HOSTNAME</property>
<child>
<widget class="GtkTable" id="table5">
+ <property name="border_width">3</property>
<property name="width_request">400</property>
<property name="visible">True</property>
- <property name="n_rows">4</property>
+ <property name="n_rows">5</property>
<property name="n_columns">2</property>
<property name="homogeneous">False</property>
<property name="row_spacing">0</property>
@@ -2251,6 +2361,27 @@ SOCKS5_HOSTNAME</property>
<property name="y_options"></property>
</packing>
</child>
+
+ <child>
+ <widget class="GtkButton" id="configure_plugin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Configure</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <signal name="clicked" handler="on_configure_plugin_clicked" last_modification_time="Tue, 01 Dec 2009 16:54:36 GMT"/>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="y_padding">3</property>
+ <property name="x_options"></property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
</widget>
<packing>
<property name="shrink">True</property>
@@ -2487,4 +2618,212 @@ SOCKS5_HOSTNAME</property>
</child>
</widget>
+<widget class="GtkWindow" id="inputformat">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Column Format</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ALWAYS</property>
+ <property name="modal">True</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox8">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox10">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label26">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Title:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="titleentry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes">Custom</property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">â—</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox9">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="format">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Format:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="formatentry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">â—</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label25">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Format fields:
+%a - artist
+%t - title
+%b - album
+%n - track
+%l - duration</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_DEFAULT_STYLE</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkButton" id="button2">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <signal name="clicked" handler="on_format_cancel_clicked" last_modification_time="Wed, 23 Dec 2009 21:28:57 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button3">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <signal name="clicked" handler="on_format_ok_clicked" last_modification_time="Wed, 23 Dec 2009 21:28:38 GMT"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
</glade-interface>
diff --git a/deadbeef.gladep b/plugins/gtkui/deadbeef.gladep
index 22ea860c..6cfb1d88 100644
--- a/deadbeef.gladep
+++ b/plugins/gtkui/deadbeef.gladep
@@ -7,4 +7,6 @@
<source_directory></source_directory>
<gnome_support>FALSE</gnome_support>
<gettext_support>FALSE</gettext_support>
+ <output_main_file>FALSE</output_main_file>
+ <output_build_files>FALSE</output_build_files>
</glade-project>
diff --git a/drawing.h b/plugins/gtkui/drawing.h
index 23c350b6..23c350b6 100644
--- a/drawing.h
+++ b/plugins/gtkui/drawing.h
diff --git a/plugins/gtkui/fileman.c b/plugins/gtkui/fileman.c
new file mode 100644
index 00000000..d181fa1f
--- /dev/null
+++ b/plugins/gtkui/fileman.c
@@ -0,0 +1,67 @@
+#include "../../deadbeef.h"
+#include <gtk/gtk.h>
+#include <stdlib.h>
+#include "gtkui.h"
+#include "gtkplaylist.h"
+
+static void
+add_dirs_worker (void *data) {
+ GSList *lst = (GSList *)data;
+ gtkpl_add_dirs (&main_playlist, lst);
+}
+
+void
+gtkui_add_dirs (GSList *lst) {
+ deadbeef->thread_start (add_dirs_worker, lst);
+}
+
+static void
+add_files_worker (void *data) {
+ GSList *lst = (GSList *)data;
+ gtkpl_add_files (&main_playlist, lst);
+}
+
+void
+gtkui_add_files (struct _GSList *lst) {
+ deadbeef->thread_start (add_files_worker, lst);
+}
+
+static void
+open_files_worker (void *data) {
+ GSList *lst = (GSList *)data;
+ gtkpl_add_files (&main_playlist, lst);
+ gtkpl_set_cursor (PL_MAIN, 0);
+ deadbeef->sendmessage (M_PLAYSONG, 0, 0, 0);
+}
+
+void
+gtkui_open_files (struct _GSList *lst) {
+ deadbeef->pl_free ();
+ deadbeef->thread_start (open_files_worker, lst);
+}
+
+struct fmdrop_data {
+ char *mem;
+ int length;
+ int drop_y;
+};
+
+static void
+fmdrop_worker (void *ctx) {
+ struct fmdrop_data *data = (struct fmdrop_data *)ctx;
+ gtkpl_add_fm_dropped_files (&main_playlist, data->mem, data->length, data->drop_y);
+ free (data);
+}
+
+void
+gtkui_receive_fm_drop (char *mem, int length, int drop_y) {
+ struct fmdrop_data *data = malloc (sizeof (struct fmdrop_data));
+ if (!data) {
+ fprintf (stderr, "gtkui_receive_fm_drop: malloc failed\n");
+ return;
+ }
+ data->mem = mem;
+ data->length = length;
+ data->drop_y = drop_y;
+ deadbeef->thread_start (fmdrop_worker, data);
+}
diff --git a/gdkdrawing.c b/plugins/gtkui/gdkdrawing.c
index 621ee92e..621ee92e 100644
--- a/gdkdrawing.c
+++ b/plugins/gtkui/gdkdrawing.c
diff --git a/gtkplaylist.c b/plugins/gtkui/gtkplaylist.c
index a32e5456..bf551e5a 100644
--- a/gtkplaylist.c
+++ b/plugins/gtkui/gtkplaylist.c
@@ -34,22 +34,28 @@
#include "callbacks.h"
#include "interface.h"
#include "support.h"
-#include "playlist.h"
-#include "playback.h"
-#include "codec.h"
-#include "common.h"
-#include "messagepump.h"
-#include "streamer.h"
#include "search.h"
#include "progress.h"
#include "drawing.h"
#include "session.h"
#include "deadbeef.h"
-#include "conf.h"
-#include "timeline.h"
+#include "parser.h"
-//#define trace(...) { fprintf(stderr, __VA_ARGS__); }
-#define trace(fmt,...)
+#define min(x,y) ((x)<(y)?(x):(y))
+#define max(x,y) ((x)>(y)?(x):(y))
+
+#define PL_HEAD(iter) (deadbeef->pl_get_first(iter))
+#define PL_TAIL(iter) (deadbeef->pl_get_last(iter))
+#define PL_NEXT(it, iter) (deadbeef->pl_get_next(it, iter))
+#define PL_PREV(it, iter) (deadbeef->pl_get_prev(it, iter))
+#define SELECTED(it) (deadbeef->pl_is_selected(it))
+#define SELECT(it, sel) (deadbeef->pl_set_selected(it,sel))
+#define VSELECT(it, sel) {deadbeef->pl_set_selected(it,sel);gtk_pl_redraw_item_everywhere (it);}
+
+extern DB_functions_t *deadbeef; // defined in gtkui.c
+
+#define trace(...) { fprintf(stderr, __VA_ARGS__); }
+//#define trace(fmt,...)
// debug function for gdk_draw_drawable
static inline void
@@ -63,6 +69,10 @@ extern GtkWidget *mainwin;
extern GtkStatusIcon *trayicon;
extern gtkplaylist_t main_playlist;
+extern gtkplaylist_t *last_playlist;
+
+static GtkWidget *theme_treeview;
+
// orange on dark color scheme
float colo_dark_orange[COLO_COUNT][3] = {
{ 0x7f/255.f, 0x7f/255.f, 0x7f/255.f }, // cursor
@@ -122,6 +132,7 @@ static int header_dragpt[2];
#define COLHDR_ANIM_TIME 0.2f
+#if 0
typedef struct {
int c1;
int c2;
@@ -190,6 +201,7 @@ colhdr_anim_swap (gtkplaylist_t *pl, int c1, int c2, int x1, int x2) {
timeline_init (colhdr_anim.timeline, COLHDR_ANIM_TIME, 100, colhdr_anim_cb, &colhdr_anim);
timeline_start (colhdr_anim.timeline);
}
+#endif
// that must be called before gtk_init
void
@@ -201,14 +213,21 @@ gtkpl_init (void) {
buffering16_pixbuf = draw_load_pixbuf ("buffering_16.png");
rowheight = draw_get_font_size () + 12;
memcpy (colo_current, colo_white_blue, sizeof (colo_current));
+ theme_treeview = gtk_tree_view_new ();
+ gtk_widget_show (theme_treeview);
+ GtkWidget *vbox1 = lookup_widget (mainwin, "vbox1");
+ gtk_box_pack_start (GTK_BOX (vbox1), theme_treeview, FALSE, FALSE, 0);
+ gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (theme_treeview), TRUE);
}
void
gtkpl_free (gtkplaylist_t *pl) {
+#if 0
if (colhdr_anim.timeline) {
timeline_free (colhdr_anim.timeline, 1);
colhdr_anim.timeline = 0;
}
+#endif
while (pl->columns) {
gtkpl_column_t *next = pl->columns->next;
gtkpl_column_free (pl->columns);
@@ -235,16 +254,18 @@ void
gtkpl_setup_scrollbar (gtkplaylist_t *ps) {
GtkWidget *playlist = ps->playlist;
int h = playlist->allocation.height / rowheight;
- int size = (*ps->pcount);
+ int cnt = ps->get_count ();
+ int size = cnt;
if (h >= size) {
size = 0;
}
GtkWidget *scroll = ps->scrollbar;
- if (ps->row >= (*ps->pcount)) {
- ps->row = (*ps->pcount) - 1;
+ int row = deadbeef->pl_get_cursor (ps->iterator);
+ if (row >= cnt) {
+ row = cnt - 1;
}
- if (ps->scrollpos > (*ps->pcount)-ps->nvisiblerows+1) {
- int n = (*ps->pcount) - ps->nvisiblerows + 1;
+ if (ps->scrollpos > cnt-ps->nvisiblerows+1) {
+ int n = cnt - ps->nvisiblerows + 1;
ps->scrollpos = max (0, n);
gtk_range_set_value (GTK_RANGE (scroll), ps->scrollpos);
}
@@ -263,11 +284,14 @@ gtkpl_setup_hscrollbar (gtkplaylist_t *ps) {
GtkWidget *playlist = ps->playlist;
int w = playlist->allocation.width;
int size = 0;
- int i;
gtkpl_column_t *c;
for (c = ps->columns; c; c = c->next) {
size += c->width;
}
+ ps->totalwidth = size;
+ if (ps->totalwidth < ps->playlist->allocation.width) {
+ ps->totalwidth = ps->playlist->allocation.width;
+ }
if (w >= size) {
size = 0;
}
@@ -288,7 +312,7 @@ gtkpl_setup_hscrollbar (gtkplaylist_t *ps) {
}
void
-gtkpl_redraw_pl_row_novis (gtkplaylist_t *ps, int row, playItem_t *it) {
+gtkpl_redraw_pl_row_novis (gtkplaylist_t *ps, int row, DB_playItem_t *it) {
draw_begin ((uintptr_t)ps->backbuf);
gtkpl_draw_pl_row_back (ps, row, it);
if (it) {
@@ -298,7 +322,7 @@ gtkpl_redraw_pl_row_novis (gtkplaylist_t *ps, int row, playItem_t *it) {
}
void
-gtkpl_redraw_pl_row (gtkplaylist_t *ps, int row, playItem_t *it) {
+gtkpl_redraw_pl_row (gtkplaylist_t *ps, int row, DB_playItem_t *it) {
if (row < ps->scrollpos || row >= ps->scrollpos+ps->nvisiblerows) {
return;
}
@@ -314,101 +338,49 @@ gtkpl_redraw_pl_row (gtkplaylist_t *ps, int row, playItem_t *it) {
}
void
-gtkpl_draw_pl_row_back (gtkplaylist_t *ps, int row, playItem_t *it) {
+gtkpl_draw_pl_row_back (gtkplaylist_t *ps, int row, DB_playItem_t *it) {
// draw background
- float w;
- int start, end;
- int startx, endx;
- int width, height;
- draw_get_canvas_size ((uintptr_t)ps->backbuf, &width, &height);
- w = width;
- if (it && ((it->selected && ps->multisel) || (row == ps->row && !ps->multisel))) {
- if (row % 2) {
- theme_set_fg_color (COLO_PLAYLIST_SEL_EVEN);
- }
- else {
- theme_set_fg_color (COLO_PLAYLIST_SEL_ODD);
- }
- draw_rect (0, row * rowheight - ps->scrollpos * rowheight, width, rowheight, 1);
+ GtkWidget *treeview = theme_treeview;
+ if (treeview->style->depth == -1) {
+ return; // drawing was called too early
}
- else {
- if (row % 2) {
- theme_set_fg_color (COLO_PLAYLIST_EVEN);
- }
- else {
- theme_set_fg_color (COLO_PLAYLIST_ODD);
- }
- draw_rect (0, row * rowheight - ps->scrollpos * rowheight, width, rowheight, 1);
- }
- if (row == ps->row) {
- theme_set_fg_color (COLO_PLAYLIST_CURSOR);
- draw_rect (0, row * rowheight - ps->scrollpos * rowheight, width, rowheight-1, 0);
+ GTK_OBJECT_FLAGS (treeview) |= GTK_HAS_FOCUS;
+ int x = -ps->hscrollpos;
+ int w = ps->totalwidth;
+ gtk_paint_flat_box (treeview->style, ps->backbuf, (it && SELECTED(it)) ? GTK_STATE_SELECTED : GTK_STATE_NORMAL, GTK_SHADOW_NONE, NULL, treeview, (row & 1) ? "cell_even_ruled" : "cell_odd_ruled", x, row * rowheight - ps->scrollpos * rowheight, w, rowheight);
+ if (row == deadbeef->pl_get_cursor (ps->iterator)) {
+ // not all gtk engines/themes render focus rectangle in treeviews
+ // but we want it anyway
+ gdk_draw_rectangle (ps->backbuf, treeview->style->fg_gc[GTK_STATE_NORMAL], FALSE, x, row * rowheight - ps->scrollpos * rowheight, w-1, rowheight-1);
+ // gtkstyle focus drawing, for reference
+// gtk_paint_focus (treeview->style, ps->backbuf, (it && SELECTED(it)) ? GTK_STATE_SELECTED : GTK_STATE_NORMAL, NULL, treeview, "treeview", x, row * rowheight - ps->scrollpos * rowheight, w, rowheight);
}
}
void
-gtkpl_draw_pl_row (gtkplaylist_t *ps, int row, playItem_t *it) {
+gtkpl_draw_pl_row (gtkplaylist_t *ps, int row, DB_playItem_t *it) {
if (row-ps->scrollpos >= ps->nvisiblerows || row-ps->scrollpos < 0) {
// fprintf (stderr, "WARNING: attempt to draw row outside of screen bounds (%d)\n", row-ps->scrollpos);
return;
}
int width, height;
draw_get_canvas_size ((uintptr_t)ps->backbuf, &width, &height);
- if (it && ((it->selected && ps->multisel) || (row == ps->row && !ps->multisel))) {
- if (row % 2) {
- theme_set_bg_color (COLO_PLAYLIST_SEL_EVEN);
- }
- else {
- theme_set_bg_color (COLO_PLAYLIST_SEL_ODD);
- }
- theme_set_fg_color (COLO_PLAYLIST_SEL_TEXT);
+ if (it && SELECTED (it)) {
+ GdkColor *clr = &theme_treeview->style->fg[GTK_STATE_SELECTED];
+ float rgb[3] = { clr->red/65535.f, clr->green/65535.f, clr->blue/65535.f };
+ draw_set_fg_color (rgb);
}
else {
- if (row % 2) {
- theme_set_bg_color (COLO_PLAYLIST_EVEN);
- }
- else {
- theme_set_bg_color (COLO_PLAYLIST_ODD);
- }
- theme_set_fg_color (COLO_PLAYLIST_TEXT);
- }
- // draw as columns
- char dur[50];
- pl_format_title (it, dur, sizeof (dur), "%l");
-
- const char *artist = pl_find_meta (it, "artist");
- if (!artist) {
- artist = "?";
- }
- const char *album = pl_find_meta (it, "album");
- if (!album) {
- album = "?";
- }
- const char *track = pl_find_meta (it, "track");
- if (!track) {
- track = "";
+ GdkColor *clr = &theme_treeview->style->fg[GTK_STATE_NORMAL];
+ float rgb[3] = { clr->red/65535.f, clr->green/65535.f, clr->blue/65535.f };
+ draw_set_fg_color (rgb);
}
- const char *title = pl_find_meta (it, "title");
- if (!title) {
- title = "?";
- }
- char artistalbum[1024];
- pl_format_title (it, artistalbum, sizeof (artistalbum), "%a - %b");
-#if 0
- const char *columns[pl_ncolumns] = {
- "",
- artistalbum,
- track,
- title,
- dur
- };
-#endif
int x = -ps->hscrollpos;
gtkpl_column_t *c;
for (c = ps->columns; c; c = c->next) {
- if (it == playlist_current_ptr && c->id == DB_COLUMN_PLAYING/* && !p_isstopped ()*/) {
- int paused = p_ispaused ();
- int buffering = !streamer_ok_to_read (-1);
+ if (it == deadbeef->pl_getcurrent () && c->id == DB_COLUMN_PLAYING) {
+ int paused = deadbeef->get_output ()->state () == OUTPUT_STATE_PAUSED;
+ int buffering = !deadbeef->streamer_ok_to_read (-1);
uintptr_t pixbuf;
if (paused) {
pixbuf = pause16_pixbuf;
@@ -422,41 +394,14 @@ gtkpl_draw_pl_row (gtkplaylist_t *ps, int row, playItem_t *it) {
draw_pixbuf ((uintptr_t)ps->backbuf, pixbuf, x + c->width/2 - 8 - ps->hscrollpos, (row - ps->scrollpos) * rowheight + rowheight/2 - 8, 0, 0, 16, 16);
}
else {
- char fmt_text[1024];
- const char *text = NULL;
- if (c->id != -1) {
- switch (c->id) {
- case DB_COLUMN_ARTIST_ALBUM:
- text = artistalbum;
- break;
- case DB_COLUMN_ARTIST:
- text = artist;
- break;
- case DB_COLUMN_ALBUM:
- text = album;
- break;
- case DB_COLUMN_TITLE:
- text = title;
- break;
- case DB_COLUMN_DURATION:
- text = dur;
- break;
- case DB_COLUMN_TRACK:
- text = track;
- break;
- }
- }
- else if (c->format) {
- pl_format_title (it, fmt_text, sizeof (fmt_text), c->format);
- text = fmt_text;
+ char text[1024];
+ deadbeef->pl_format_title (it, text, sizeof (text), c->id, c->format);
+
+ if (c->align_right) {
+ draw_text (x+5, row * rowheight - ps->scrollpos * rowheight + rowheight/2 - draw_get_font_size ()/2 - 2, c->width-10, 1, text);
}
- if (text) {
- if (c->align_right) {
- draw_text_with_colors (x+5, row * rowheight - ps->scrollpos * rowheight + rowheight/2 - draw_get_font_size ()/2 - 2, c->width-10, 1, text);
- }
- else {
- draw_text_with_colors (x + 5, row * rowheight - ps->scrollpos * rowheight + rowheight/2 - draw_get_font_size ()/2 - 2, c->width-10, 0, text);
- }
+ else {
+ draw_text (x + 5, row * rowheight - ps->scrollpos * rowheight + rowheight/2 - draw_get_font_size ()/2 - 2, c->width-10, 0, text);
}
}
x += c->width;
@@ -466,7 +411,6 @@ gtkpl_draw_pl_row (gtkplaylist_t *ps, int row, playItem_t *it) {
void
gtkpl_draw_playlist (gtkplaylist_t *ps, int x, int y, int w, int h) {
- GtkWidget *widget = ps->playlist;
if (!ps->backbuf) {
return;
}
@@ -476,22 +420,23 @@ gtkpl_draw_playlist (gtkplaylist_t *ps, int x, int y, int w, int h) {
int row2;
int row2_full;
row1 = max (0, y / rowheight + ps->scrollpos);
- row2 = min ((*ps->pcount), (y+h) / rowheight + ps->scrollpos + 1);
+ int cnt = ps->get_count ();
+ row2 = min (cnt, (y+h) / rowheight + ps->scrollpos + 1);
row2_full = (y+h) / rowheight + ps->scrollpos + 1;
// draw background
- playItem_t *it = gtkpl_get_for_idx (ps, ps->scrollpos);
- playItem_t *it_copy = it;
+ DB_playItem_t *it = deadbeef->pl_get_for_idx_and_iter (ps->scrollpos, ps->iterator);
+ DB_playItem_t *it_copy = it;
for (row = row1; row < row2_full; row++) {
gtkpl_draw_pl_row_back (ps, row, it);
if (it) {
- it = it->next[ps->iterator];
+ it = PL_NEXT (it, ps->iterator);
}
}
it = it_copy;
int idx = 0;
for (row = row1; row < row2; row++, idx++) {
gtkpl_draw_pl_row (ps, row, it);
- it = it->next[ps->iterator];
+ it = PL_NEXT (it, ps->iterator);
}
draw_end ();
@@ -542,21 +487,16 @@ gtkpl_expose_header (gtkplaylist_t *ps, int x, int y, int w, int h) {
void
gtkpl_select_single (gtkplaylist_t *ps, int sel) {
- if (!ps->multisel) {
- return;
- }
int idx=0;
- GtkWidget *widget = ps->playlist;
- for (playItem_t *it = playlist_head[ps->iterator]; it; it = it->next[ps->iterator], idx++) {
+ DB_playItem_t *it = PL_HEAD (ps->iterator);
+ for (; it; it = PL_NEXT (it, ps->iterator), idx++) {
if (idx == sel) {
- if (!it->selected) {
- it->selected = 1;
- gtkpl_redraw_pl_row (ps, idx, it);
+ if (!SELECTED (it)) {
+ VSELECT (it, 1);
}
}
- else if (it->selected) {
- it->selected = 0;
- gtkpl_redraw_pl_row (ps, idx, it);
+ else if (SELECTED (it)) {
+ VSELECT (it, 0);
}
}
}
@@ -565,12 +505,12 @@ gtkpl_select_single (gtkplaylist_t *ps, int sel) {
// {{{ [+] if clicked unselected item:
// unselect all
// select clicked item
-// ps->row = clicked
+// deadbeef->pl_get_cursor (ps->iterator) = clicked
// redraw
// start 'area selection' mode
// }}}
// {{{ [+] if clicked selected item:
-// ps->row = clicked
+// deadbeef->pl_get_cursor (ps->iterator) = clicked
// redraw
// wait until next release or motion event, whichever is 1st
// if release is 1st:
@@ -590,16 +530,16 @@ static int shift_sel_anchor = -1;
void
gtkpl_mouse1_pressed (gtkplaylist_t *ps, int state, int ex, int ey, double time) {
// cursor must be set here, but selection must be handled in keyrelease
- if ((*ps->pcount) == 0) {
+ int cnt = ps->get_count ();
+ if (cnt == 0) {
return;
}
- GtkWidget *widget = ps->playlist;
// remember mouse coords for doubleclick detection
ps->lastpos[0] = ex;
ps->lastpos[1] = ey;
// select item
int y = ey/rowheight + ps->scrollpos;
- if (y < 0 || y >= (*ps->pcount)) {
+ if (y < 0 || y >= ps->get_count ()) {
y = -1;
}
@@ -607,21 +547,21 @@ gtkpl_mouse1_pressed (gtkplaylist_t *ps, int state, int ex, int ey, double time)
&& fabs(ps->lastpos[0] - ex) < 3
&& fabs(ps->lastpos[1] - ey) < 3) {
// doubleclick - play this item
- if (ps->row != -1) {
- playItem_t *it = gtkpl_get_for_idx (ps, ps->row);
- it->selected = 1;
- int r = pl_get_idx_of (it);
- int prev = main_playlist.row;
+ if (deadbeef->pl_get_cursor (ps->iterator) != -1) {
+ DB_playItem_t *it = deadbeef->pl_get_for_idx_and_iter (deadbeef->pl_get_cursor (ps->iterator), ps->iterator);
+// SELECT (it, 1);
+ int r = deadbeef->pl_get_idx_of (it);
+ int prev = deadbeef->pl_get_cursor (ps->iterator);
if (prev != r) {
- main_playlist.row = r;
+ deadbeef->pl_set_cursor (ps->iterator, r);
if (prev != -1) {
- gtkpl_redraw_pl_row (&main_playlist, prev, pl_get_for_idx (prev));
+ gtkpl_redraw_pl_row (&main_playlist, prev, deadbeef->pl_get_for_idx (prev));
}
if (r != -1) {
gtkpl_redraw_pl_row (&main_playlist, r, it);
}
}
- messagepump_push (M_PLAYSONGNUM, 0, r, 0);
+ deadbeef->sendmessage (M_PLAYSONGNUM, 0, r, 0);
return;
}
@@ -634,74 +574,64 @@ gtkpl_mouse1_pressed (gtkplaylist_t *ps, int state, int ex, int ey, double time)
int sel = y;
if (y == -1) {
- y = (*ps->pcount) - 1;
+ y = ps->get_count () - 1;
}
- int prev = ps->row;
- ps->row = y;
- shift_sel_anchor = ps->row;
+ int prev = deadbeef->pl_get_cursor (ps->iterator);
+ deadbeef->pl_set_cursor (ps->iterator, y);
+ shift_sel_anchor = deadbeef->pl_get_cursor (ps->iterator);
// handle multiple selection
- if (ps->multisel) {
- if (!(state & (GDK_CONTROL_MASK|GDK_SHIFT_MASK)))
- {
- playItem_t *it = gtkpl_get_for_idx (ps, sel);
- if (!it || !it->selected) {
- // reset selection, and set it to single item
- gtkpl_select_single (ps, sel);
- areaselect = 1;
- areaselect_x = ex;
- areaselect_y = ey;
- areaselect_dx = -1;
- areaselect_dy = -1;
- shift_sel_anchor = ps->row;
- }
- else {
- dragwait = 1;
- gtkpl_redraw_pl_row (ps, prev, gtkpl_get_for_idx (ps, prev));
- if (ps->row != prev) {
- gtkpl_redraw_pl_row (ps, ps->row, gtkpl_get_for_idx (ps, ps->row));
- }
+ if (!(state & (GDK_CONTROL_MASK|GDK_SHIFT_MASK)))
+ {
+ DB_playItem_t *it = deadbeef->pl_get_for_idx_and_iter (sel, ps->iterator);
+ if (!it || !SELECTED (it)) {
+ // reset selection, and set it to single item
+ gtkpl_select_single (ps, sel);
+ areaselect = 1;
+ areaselect_x = ex;
+ areaselect_y = ey;
+ areaselect_dx = -1;
+ areaselect_dy = -1;
+ shift_sel_anchor = deadbeef->pl_get_cursor (ps->iterator);
+ }
+ else {
+ dragwait = 1;
+ gtkpl_redraw_pl_row (ps, prev, gtkpl_get_for_idx (ps, prev));
+ if (deadbeef->pl_get_cursor (ps->iterator) != prev) {
+ gtkpl_redraw_pl_row (ps, deadbeef->pl_get_cursor (ps->iterator), gtkpl_get_for_idx (ps, deadbeef->pl_get_cursor (ps->iterator)));
}
}
- else if (state & GDK_CONTROL_MASK) {
- // toggle selection
- if (y != -1) {
- playItem_t *it = gtkpl_get_for_idx (ps, y);
- if (it) {
- it->selected = 1 - it->selected;
- gtkpl_redraw_pl_row (ps, y, it);
- }
+ }
+ else if (state & GDK_CONTROL_MASK) {
+ // toggle selection
+ if (y != -1) {
+ DB_playItem_t *it = gtkpl_get_for_idx (ps, y);
+ if (it) {
+ VSELECT (it, 1 - SELECTED (it));
}
}
- else if (state & GDK_SHIFT_MASK) {
- // select range
- int start = min (prev, ps->row);
- int end = max (prev, ps->row);
- int idx = 0;
- for (playItem_t *it = playlist_head[ps->iterator]; it; it = it->next[ps->iterator], idx++) {
- if (idx >= start && idx <= end) {
- if (!it->selected) {
- it->selected = 1;
- gtkpl_redraw_pl_row (ps, idx, it);
- }
+ }
+ else if (state & GDK_SHIFT_MASK) {
+ // select range
+ int start = min (prev, deadbeef->pl_get_cursor (ps->iterator));
+ int end = max (prev, deadbeef->pl_get_cursor (ps->iterator));
+ int idx = 0;
+ for (DB_playItem_t *it = PL_HEAD (ps->iterator); it; it = PL_NEXT (it, ps->iterator), idx++) {
+ if (idx >= start && idx <= end) {
+ if (!SELECTED (it)) {
+ VSELECT (it, 1);
}
- else {
- if (it->selected) {
- it->selected = 0;
- gtkpl_redraw_pl_row (ps, idx, it);
- }
+ }
+ else {
+ if (SELECTED (it)) {
+ VSELECT (it, 0);
}
}
}
- if (ps->row != -1 && sel == -1) {
- gtkpl_redraw_pl_row (ps, ps->row, gtkpl_get_for_idx (ps, ps->row));
- }
}
- else {
- if (ps->row != -1) {
- gtkpl_redraw_pl_row (ps, ps->row, gtkpl_get_for_idx (ps, ps->row));
- }
+ if (deadbeef->pl_get_cursor (ps->iterator) != -1 && sel == -1) {
+ gtkpl_redraw_pl_row (ps, deadbeef->pl_get_cursor (ps->iterator), gtkpl_get_for_idx (ps, deadbeef->pl_get_cursor (ps->iterator)));
}
- if (prev != -1 && prev != ps->row) {
+ if (prev != -1 && prev != deadbeef->pl_get_cursor (ps->iterator)) {
gtkpl_redraw_pl_row (ps, prev, gtkpl_get_for_idx (ps, prev));
}
@@ -781,11 +711,10 @@ gtkpl_scroll_playlist_cb (gpointer data) {
playlist_scroll_active = 0;
return FALSE;
}
- if (sc >= *ps->pcount) {
+ if (sc >= ps->get_count ()) {
playlist_scroll_active = 0;
return FALSE;
}
- GDK_THREADS_ENTER ();
gtk_range_set_value (GTK_RANGE (ps->scrollbar), sc);
if (playlist_scroll_mode == 0) {
GdkEventMotion ev;
@@ -795,7 +724,6 @@ gtkpl_scroll_playlist_cb (gpointer data) {
else if (playlist_scroll_mode == 1) {
gtkpl_track_dragdrop (ps, playlist_scroll_pointer_y);
}
- GDK_THREADS_LEAVE ();
scroll_sleep_time -= 0.1;
if (scroll_sleep_time < 0.05) {
scroll_sleep_time = 0.05;
@@ -819,23 +747,20 @@ gtkpl_mousemove (gtkplaylist_t *ps, GdkEventMotion *event) {
}
}
else if (areaselect) {
- GtkWidget *widget = ps->playlist;
int y = event->y/rowheight + ps->scrollpos;
//if (y != shift_sel_anchor)
{
int start = min (y, shift_sel_anchor);
int end = max (y, shift_sel_anchor);
int idx=0;
- for (playItem_t *it = playlist_head[ps->iterator]; it; it = it->next[ps->iterator], idx++) {
+ for (DB_playItem_t *it = PL_HEAD(ps->iterator); it; it = PL_NEXT(it, ps->iterator), idx++) {
if (idx >= start && idx <= end) {
- if (!it->selected) {
- it->selected = 1;
- gtkpl_redraw_pl_row (ps, idx, it);
+ if (!SELECTED (it)) {
+ VSELECT (it, 1);
}
}
- else if (it->selected) {
- it->selected = 0;
- gtkpl_redraw_pl_row (ps, idx, it);
+ else if (SELECTED (it)) {
+ VSELECT (it, 0);
}
}
}
@@ -876,7 +801,7 @@ gtkpl_handle_scroll_event (gtkplaylist_t *ps, int direction) {
GtkWidget *range = ps->scrollbar;;
GtkWidget *playlist = ps->playlist;
int h = playlist->allocation.height / rowheight;
- int size = (*ps->pcount);
+ int size = ps->get_count ();
if (h >= size) {
size = 0;
}
@@ -884,7 +809,6 @@ gtkpl_handle_scroll_event (gtkplaylist_t *ps, int direction) {
return;
}
// pass event to scrollbar
- GtkAdjustment* adj = gtk_range_get_adjustment (GTK_RANGE (range));
int newscroll = gtk_range_get_value (GTK_RANGE (range));
if (direction == GDK_SCROLL_UP) {
newscroll -= 2;
@@ -942,26 +866,16 @@ gtkpl_hscroll (gtkplaylist_t *ps, int newscroll) {
}
void
-gtkpl_randomsong (void) {
- p_stop ();
- pl_randomsong ();
-}
-
-void
-gtkpl_playsongnum (int idx) {
- p_stop ();
- streamer_set_nextsong (idx, 1);
-}
-
-void
gtkpl_songchanged (gtkplaylist_t *ps, int from, int to) {
if (!dragwait && to != -1) {
- GtkWidget *widget = ps->playlist;
- if (conf_get_int ("playlist.scroll.followplayback", 0)) {
+ if (deadbeef->conf_get_int ("playlist.scroll.followplayback", 0)) {
if (to < ps->scrollpos || to >= ps->scrollpos + ps->nvisiblefullrows) {
gtk_range_set_value (GTK_RANGE (ps->scrollbar), to - ps->nvisiblerows/2);
}
}
+ if (deadbeef->conf_get_int ("playlist.scroll.cursorfollowplayback", 1)) {
+ gtkpl_set_cursor (PL_MAIN, to);
+ }
}
if (from >= 0) {
@@ -974,155 +888,83 @@ gtkpl_songchanged (gtkplaylist_t *ps, int from, int to) {
void
gtkpl_keypress (gtkplaylist_t *ps, int keyval, int state) {
- GtkWidget *widget = ps->playlist;
GtkWidget *range = ps->scrollbar;
- int prev = ps->row;
+ int prev = deadbeef->pl_get_cursor (ps->iterator);
int newscroll = ps->scrollpos;
-// C-f is now handled by gtk
-// if ((keyval == GDK_F || keyval == GDK_f) && (state & GDK_CONTROL_MASK)) {
-// search_start ();
-// }
-// else
-// if ((keyval == GDK_A || keyval == GDK_a) && (state & GDK_CONTROL_MASK)) {
-// // select all
-// pl_select_all ();
-// gtkpl_draw_playlist (ps, 0, 0, widget->allocation.width, widget->allocation.height);
-// draw_drawable (widget->window, widget->style->black_gc, ps->backbuf, 0, 0, 0, 0, widget->allocation.width, widget->allocation.height);
-// return;
-// }
-// else if ((keyval == GDK_P || keyval == GDK_p) && (state & GDK_CONTROL_MASK)) {
-// messagepump_push (M_PAUSESONG, 0, 0, 0);
-// }
-// else
-// if (keyval == GDK_Return && ps->row != -1) {
-// messagepump_push (M_PLAYSONGNUM, 0, ps->row, 0);
-// return;
-// }
-// else
-// if (keyval == GDK_Delete) {
-// pl_delete_selected ();
-// playlist_refresh ();
-// return;
-// }
-// else
- if (keyval == GDK_Down && ps->row < (*ps->pcount) - 1) {
- ps->row++;
- if (ps->row > ps->scrollpos + widget->allocation.height / rowheight - 1) {
- newscroll = ps->row - widget->allocation.height / rowheight + 1;
+ if (keyval == GDK_Down) {
+ int cursor = deadbeef->pl_get_cursor (ps->iterator);
+ if (cursor < ps->get_count () - 1) {
+ gtkpl_set_cursor (ps->iterator, cursor+1);
}
}
- else if (keyval == GDK_r) {
- extern int replaygain;
- replaygain = 1-replaygain;
- fprintf (stderr, "replaygain=%d\n", replaygain);
- }
- else if (keyval == GDK_t) {
- extern int replaygain_scale;
- replaygain_scale = 1-replaygain_scale;
- fprintf (stderr, "replaygain_scale=%d\n", replaygain_scale);
- }
- else if (keyval == GDK_Up && ps->row > 0) {
- ps->row--;
- if (ps->row < ps->scrollpos) {
- newscroll = ps->row;
+ else if (keyval == GDK_Up) {
+ int cursor = deadbeef->pl_get_cursor (ps->iterator);
+ if (cursor > 0) {
+ gtkpl_set_cursor (ps->iterator, cursor-1);
}
}
- else if (keyval == GDK_Page_Down && ps->row < (*ps->pcount) - 1) {
- ps->row += 10;
- if (ps->row >= (*ps->pcount)) {
- ps->row = (*ps->pcount) - 1;
- }
- if (ps->row > ps->scrollpos + widget->allocation.height / rowheight - 1) {
- newscroll = ps->row - widget->allocation.height / rowheight + 1;
+ else if (keyval == GDK_Page_Down) {
+ int cursor = deadbeef->pl_get_cursor (ps->iterator);
+ if (cursor < ps->get_count () - 1) {
+ cursor += 10;
+ if (cursor >= ps->get_count ()) {
+ cursor = ps->get_count () - 1;
+ }
+ gtkpl_set_cursor (ps->iterator, cursor);
}
}
- else if (keyval == GDK_Page_Up && ps->row > 0) {
- ps->row -= 10;
- if (ps->row < 0) {
- ps->row = 0;
- }
- if (ps->row < ps->scrollpos) {
- newscroll = ps->row;
+ else if (keyval == GDK_Page_Up) {
+ int cursor = deadbeef->pl_get_cursor (ps->iterator);
+ if (cursor > 0) {
+ cursor -= 10;
+ if (cursor < 0) {
+ cursor = 0;
+ }
+ gtkpl_set_cursor (ps->iterator, cursor);
}
}
- else if (keyval == GDK_End && ps->row != (*ps->pcount) - 1) {
- ps->row = (*ps->pcount) - 1;
- if (ps->row > ps->scrollpos + widget->allocation.height / rowheight - 1) {
- newscroll = ps->row - widget->allocation.height / rowheight + 1;
+ else if (keyval == GDK_End) {
+ int cursor = deadbeef->pl_get_cursor (ps->iterator);
+ if (cursor != ps->get_count () - 1) {
+ gtkpl_set_cursor (ps->iterator, ps->get_count () - 1);
}
}
- else if (keyval == GDK_Home && ps->row != 0) {
- ps->row = 0;
- if (ps->row < ps->scrollpos) {
- newscroll = ps->row;
+ else if (keyval == GDK_Home) {
+ int cursor = deadbeef->pl_get_cursor (ps->iterator);
+ if (cursor != 0) {
+ gtkpl_set_cursor (ps->iterator, 0);
}
}
if (state & GDK_SHIFT_MASK) {
- // select all between shift_sel_anchor and ps->row
- if (prev != ps->row) {
- int minvis = ps->scrollpos;
- int maxvis = ps->scrollpos + ps->nvisiblerows-1;
- int start = min (ps->row, shift_sel_anchor);
- int end = max (ps->row, shift_sel_anchor);
+ // select all between shift_sel_anchor and deadbeef->pl_get_cursor (ps->iterator)
+ if (prev != deadbeef->pl_get_cursor (ps->iterator)) {
+ int start = min (deadbeef->pl_get_cursor (ps->iterator), shift_sel_anchor);
+ int end = max (deadbeef->pl_get_cursor (ps->iterator), shift_sel_anchor);
int idx=0;
- for (playItem_t *it = playlist_head[ps->iterator]; it; it = it->next[ps->iterator], idx++) {
+ for (DB_playItem_t *it = PL_HEAD (ps->iterator); it; it = PL_NEXT(it,ps->iterator), idx++) {
if (idx >= start && idx <= end) {
- it->selected = 1;
- if (idx >= minvis && idx <= maxvis) {
- if (newscroll == ps->scrollpos) {
- gtkpl_redraw_pl_row (ps, idx, it);
- }
- else {
- gtkpl_redraw_pl_row_novis (ps, idx, it);
- }
- }
+ VSELECT (it, 1);
}
- else if (it->selected)
+ else if (SELECTED (it))
{
- it->selected = 0;
- if (idx >= minvis && idx <= maxvis) {
- if (newscroll == ps->scrollpos) {
- gtkpl_redraw_pl_row (ps, idx, it);
- }
- else {
- gtkpl_redraw_pl_row_novis (ps, idx, it);
- }
- }
+ VSELECT (it, 0);
}
}
}
}
else {
// reset selection, set new single cursor/selection
- if (prev != ps->row) {
- int minvis = ps->scrollpos;
- int maxvis = ps->scrollpos + ps->nvisiblerows-1;
- shift_sel_anchor = ps->row;
+ if (prev != deadbeef->pl_get_cursor (ps->iterator)) {
+ shift_sel_anchor = deadbeef->pl_get_cursor (ps->iterator);
int idx=0;
- for (playItem_t *it = playlist_head[ps->iterator]; it; it = it->next[ps->iterator], idx++) {
- if (idx == ps->row) {
- if (!it->selected) {
- it->selected = 1;
- if (idx >= minvis && idx <= maxvis) {
- if (newscroll == ps->scrollpos) {
- gtkpl_redraw_pl_row (ps, idx, it);
- }
- else {
- gtkpl_redraw_pl_row_novis (ps, idx, it);
- }
- }
+ for (DB_playItem_t *it = PL_HEAD (ps->iterator); it; it = PL_NEXT(it, ps->iterator), idx++) {
+ if (idx == deadbeef->pl_get_cursor (ps->iterator)) {
+ if (!SELECTED (it)) {
+ VSELECT (it, 1);
}
}
- else if (it->selected) {
- it->selected = 0;
- if (idx >= minvis && idx <= maxvis) {
- if (newscroll == ps->scrollpos) {
- gtkpl_redraw_pl_row (ps, idx, it);
- }
- else {
- gtkpl_redraw_pl_row_novis (ps, idx, it);
- }
- }
+ else if (SELECTED (it)) {
+ VSELECT (it, 0);
}
}
}
@@ -1183,71 +1025,6 @@ gtkpl_track_dragdrop (gtkplaylist_t *ps, int y) {
}
void
-gtkpl_handle_drag_drop (gtkplaylist_t *ps, int drop_y, uint32_t *d, int length) {
- int drop_row = drop_y / rowheight + ps->scrollpos;
- playItem_t *drop_before = gtkpl_get_for_idx (ps, drop_row);
- while (drop_before && drop_before->selected) {
- drop_before = drop_before->next[ps->iterator];
- }
- // unlink items from playlist, and link together
- playItem_t *head = NULL;
- playItem_t *tail = NULL;
- int processed = 0;
- int idx = 0;
- playItem_t *next = NULL;
- for (playItem_t *it = playlist_head[ps->iterator]; it && processed < length; it = next, idx++) {
- next = it->next[ps->iterator];
- if (idx == d[processed]) {
- if (it->prev[ps->iterator]) {
- it->prev[ps->iterator]->next[ps->iterator] = it->next[ps->iterator];
- }
- else {
- playlist_head[ps->iterator] = it->next[ps->iterator];
- }
- if (it->next[ps->iterator]) {
- it->next[ps->iterator]->prev[ps->iterator] = it->prev[ps->iterator];
- }
- else {
- playlist_tail[ps->iterator] = it->prev[ps->iterator];
- }
- if (tail) {
- tail->next[ps->iterator] = it;
- it->prev[ps->iterator] = tail;
- tail = it;
- }
- else {
- head = tail = it;
- it->prev[ps->iterator] = it->next[ps->iterator] = NULL;
- }
- processed++;
- }
- }
- // find insertion point
- playItem_t *drop_after = NULL;
- if (drop_before) {
- drop_after = drop_before->prev[ps->iterator];
- }
- else {
- drop_after = playlist_tail[ps->iterator];
- }
- // insert in between
- head->prev[ps->iterator] = drop_after;
- if (drop_after) {
- drop_after->next[ps->iterator] = head;
- }
- else {
- playlist_head[ps->iterator] = head;
- }
- tail->next[ps->iterator] = drop_before;
- if (drop_before) {
- drop_before->prev[ps->iterator] = tail;
- }
- else {
- playlist_tail[ps->iterator] = tail;
- }
-}
-
-void
on_playlist_drag_end (GtkWidget *widget,
GdkDragContext *drag_context,
gpointer user_data)
@@ -1302,39 +1079,47 @@ strcopy_special (char *dest, const char *src, int len) {
*dest = 0;
}
+static gboolean
+set_progress_text_idle (gpointer data) {
+ const char *text = (const char *)data;
+ progress_settext (text);
+ return FALSE;
+}
+
int
-gtkpl_add_file_info_cb (playItem_t *it, void *data) {
+gtkpl_add_file_info_cb (DB_playItem_t *it, void *data) {
if (progress_is_aborted ()) {
return -1;
}
- GDK_THREADS_ENTER();
- progress_settext (it->fname);
- GDK_THREADS_LEAVE();
-#if 0
- GtkEntry *e = (GtkEntry *)data;
- GDK_THREADS_ENTER();
- gtk_entry_set_text (GTK_ENTRY (e), it->fname);
- GDK_THREADS_LEAVE();
- usleep (100);
- countdown = 10;
-#endif
+ g_idle_add (set_progress_text_idle, it->fname);
return 0;
}
+static gboolean
+progress_show_idle (gpointer data) {
+ progress_show ();
+ return FALSE;
+}
+
+static gboolean
+progress_hide_idle (gpointer data) {
+ progress_hide ();
+ playlist_refresh ();
+ return FALSE;
+}
+
void
gtkpl_add_fm_dropped_files (gtkplaylist_t *ps, char *ptr, int length, int drop_y) {
- GDK_THREADS_ENTER();
- progress_show ();
- GDK_THREADS_LEAVE();
+ g_idle_add (progress_show_idle, NULL);
int drop_row = drop_y / rowheight + ps->scrollpos;
- playItem_t *drop_before = gtkpl_get_for_idx (ps, drop_row);
- playItem_t *after = NULL;
+ DB_playItem_t *drop_before = deadbeef->pl_get_for_idx_and_iter (drop_row, ps->iterator);
+ DB_playItem_t *after = NULL;
if (drop_before) {
- after = drop_before->prev[ps->iterator];
+ after = PL_PREV (drop_before, ps->iterator);
}
else {
- after = playlist_tail[ps->iterator];
+ after = PL_TAIL (ps->iterator);
}
const uint8_t *p = (const uint8_t*)ptr;
while (*p) {
@@ -1348,9 +1133,9 @@ gtkpl_add_fm_dropped_files (gtkplaylist_t *ps, char *ptr, int length, int drop_y
//strncpy (fname, p, pe - p);
//fname[pe - p] = 0;
int abort = 0;
- playItem_t *inserted = pl_insert_dir (after, fname, &abort, gtkpl_add_file_info_cb, NULL);
+ DB_playItem_t *inserted = deadbeef->pl_insert_dir (after, fname, &abort, gtkpl_add_file_info_cb, NULL);
if (!inserted && !abort) {
- inserted = pl_insert_file (after, fname, &abort, gtkpl_add_file_info_cb, NULL);
+ inserted = deadbeef->pl_insert_file (after, fname, &abort, gtkpl_add_file_info_cb, NULL);
}
if (inserted) {
after = inserted;
@@ -1364,20 +1149,7 @@ gtkpl_add_fm_dropped_files (gtkplaylist_t *ps, char *ptr, int length, int drop_y
}
free (ptr);
- GDK_THREADS_ENTER();
- progress_hide ();
- playlist_refresh ();
- GDK_THREADS_LEAVE();
-}
-
-void
-gtkpl_handle_fm_drag_drop (gtkplaylist_t *ps, int drop_y, void *ptr, int length) {
- // this happens when dropped from file manager
- char *mem = malloc (length+1);
- memcpy (mem, ptr, length);
- mem[length] = 0;
- // we don't pass control structure, but there's only one drag-drop view currently
- messagepump_push (M_FMDRAGDROP, (uintptr_t)mem, length, drop_y);
+ g_idle_add (progress_hide_idle, NULL);
}
void
@@ -1399,6 +1171,7 @@ gtkpl_header_draw (gtkplaylist_t *ps) {
for (c = ps->columns; c; c = c->next, idx++) {
w = c->width;
int xx = x;
+#if 0
if (colhdr_anim.anim_active) {
if (idx == colhdr_anim.c2) {
xx = colhdr_anim.ax1;
@@ -1407,16 +1180,29 @@ gtkpl_header_draw (gtkplaylist_t *ps) {
xx = colhdr_anim.ax2;
}
}
+#endif
if (header_dragging < 0 || idx != header_dragging) {
if (xx >= widget->allocation.width) {
continue;
}
+ int arrow_sz = 10;
if (w > 0) {
gtk_paint_vline (widget->style, ps->backbuf_header, GTK_STATE_NORMAL, NULL, NULL, NULL, 2, h-4, xx+w - 2);
GdkColor *gdkfg = &widget->style->fg[0];
float fg[3] = {(float)gdkfg->red/0xffff, (float)gdkfg->green/0xffff, (float)gdkfg->blue/0xffff};
draw_set_fg_color (fg);
- draw_text (xx + 5, h/2-draw_get_font_size()/2, c->width-10, 0, c->title);
+ int w = c->width-10;
+ if (c->sort_order) {
+ w -= arrow_sz;
+ if (w < 0) {
+ w = 0;
+ }
+ }
+ draw_text (xx + 5, h/2-draw_get_font_size()/2, w, 0, c->title);
+ }
+ if (c->sort_order != 0) {
+ int dir = c->sort_order == 1 ? GTK_ARROW_DOWN : GTK_ARROW_UP;
+ gtk_paint_arrow (widget->style, ps->backbuf_header, GTK_STATE_NORMAL, GTK_SHADOW_NONE, NULL, widget, NULL, dir, TRUE, xx + c->width-arrow_sz-5, widget->allocation.height/2-arrow_sz/2, arrow_sz, arrow_sz);
}
}
else {
@@ -1430,6 +1216,7 @@ gtkpl_header_draw (gtkplaylist_t *ps) {
for (c = ps->columns; c; c = c->next, idx++) {
w = c->width;
if (idx == header_dragging) {
+#if 0
if (colhdr_anim.anim_active) {
if (idx == colhdr_anim.c2) {
x = colhdr_anim.ax1;
@@ -1438,6 +1225,7 @@ gtkpl_header_draw (gtkplaylist_t *ps) {
x = colhdr_anim.ax2;
}
}
+#endif
// draw empty slot
if (x < widget->allocation.width) {
gtk_paint_box (widget->style, ps->backbuf_header, GTK_STATE_ACTIVE, GTK_SHADOW_ETCHED_IN, NULL, widget, "button", x, 0, w, h);
@@ -1500,8 +1288,9 @@ on_header_realize (GtkWidget *widget,
cursor_drag = gdk_cursor_new (GDK_FLEUR);
}
-float last_header_motion_ev = -1;
-int prev_header_x = -1;
+static float last_header_motion_ev = -1; //is it subject to remove?
+static int prev_header_x = -1;
+static int header_prepare = 0;
gboolean
on_header_motion_notify_event (GtkWidget *widget,
@@ -1509,12 +1298,30 @@ on_header_motion_notify_event (GtkWidget *widget,
gpointer user_data)
{
GTKPL_PROLOGUE;
- if (header_dragging >= 0) {
+ int ev_x, ev_y;
+ GdkModifierType ev_state;
+
+ if (event->is_hint)
+ gdk_window_get_pointer (event->window, &ev_x, &ev_y, &ev_state);
+ else
+ {
+ ev_x = event->x;
+ ev_y = event->y;
+ ev_state = event->state;
+ }
+
+
+ if ((ev_state & GDK_BUTTON1_MASK) && header_prepare) {
+ if (gtk_drag_check_threshold (widget, ev_x, prev_header_x, 0, 0)) {
+ header_prepare = 0;
+ }
+ }
+ if (!header_prepare && header_dragging >= 0) {
gdk_window_set_cursor (widget->window, cursor_drag);
gtkpl_column_t *c;
int i;
for (i = 0, c = ps->columns; i < header_dragging && c; c = c->next, i++);
- c->movepos = event->x - header_dragpt[0];
+ c->movepos = ev_x - header_dragpt[0];
// find closest column to the left
int inspos = -1;
@@ -1533,8 +1340,6 @@ on_header_motion_notify_event (GtkWidget *widget,
x += cc->width;
}
if (inspos >= 0 && inspos != header_dragging) {
- int c1 = inspos;
- int c2 = header_dragging;
// remove c from list
if (c == ps->columns) {
ps->columns = c->next;
@@ -1580,7 +1385,7 @@ on_header_motion_notify_event (GtkWidget *widget,
}
else if (header_sizing >= 0) {
last_header_motion_ev = event->time;
- prev_header_x = event->x;
+ prev_header_x = ev_x;
gdk_window_set_cursor (widget->window, cursor_sz);
// get column start pos
int x = -ps->hscrollpos;
@@ -1590,7 +1395,7 @@ on_header_motion_notify_event (GtkWidget *widget,
x += c->width;
}
- int newx = event->x > x + MIN_COLUMN_WIDTH ? event->x : x + MIN_COLUMN_WIDTH;
+ int newx = ev_x > x + MIN_COLUMN_WIDTH ? ev_x : x + MIN_COLUMN_WIDTH;
c->width = newx - x;
gtkpl_setup_hscrollbar (ps);
gtkpl_header_draw (ps);
@@ -1605,7 +1410,7 @@ on_header_motion_notify_event (GtkWidget *widget,
for (c = ps->columns; c; c = c->next) {
int w = c->width;
if (w > 0) { // ignore collapsed columns (hack for search window)
- if (event->x >= x + w - 2 && event->x <= x + w) {
+ if (ev_x >= x + w - 2 && ev_x <= x + w) {
gdk_window_set_cursor (widget->window, cursor_sz);
break;
}
@@ -1622,6 +1427,20 @@ on_header_motion_notify_event (GtkWidget *widget,
return FALSE;
}
+gtkpl_column_t*
+gtkpl_get_column_for_click (gtkplaylist_t *pl, int click_x) {
+ int x = -pl->hscrollpos;
+ gtkpl_column_t *c;
+ for (c = pl->columns; c; c = c->next) {
+ int w = c->width;
+ if (click_x >= x && click_x < x + w) {
+ return c;
+ }
+ x += w;
+ }
+ return NULL;
+}
+
gboolean
on_header_button_press_event (GtkWidget *widget,
GdkEventButton *event,
@@ -1645,14 +1464,23 @@ on_header_button_press_event (GtkWidget *widget,
break;
}
else if (event->x > x + 2 && event->x < x + w - 2) {
+ // prepare to drag or sort
header_dragpt[0] = event->x - x;
+ header_prepare = 1;
header_dragging = i;
header_sizing = -1;
+ prev_header_x = event->x;
break;
}
x += w;
}
}
+ else if (event->button == 3) {
+ ps->active_column = gtkpl_get_column_for_click (ps, event->x);
+ GtkWidget *menu = create_headermenu ();
+ last_playlist = ps;
+ gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, widget, 3, gtk_get_current_event_time());
+ }
prev_header_x = -1;
last_header_motion_ev = -1;
return FALSE;
@@ -1665,110 +1493,110 @@ on_header_button_release_event (GtkWidget *widget,
{
GTKPL_PROLOGUE;
if (event->button == 1) {
- header_sizing = -1;
- int x = 0;
- gtkpl_column_t *c;
- for (c = ps->columns; c; c = c->next) {
- int w = c->width;
- if (event->x >= x + w - 2 && event->x <= x + w) {
- gdk_window_set_cursor (widget->window, cursor_sz);
- break;
- }
- else {
- gdk_window_set_cursor (widget->window, NULL);
- }
- x += w;
- }
- if (header_dragging >= 0) {
+ if (header_prepare) {
+ header_sizing = -1;
header_dragging = -1;
- gtkpl_setup_hscrollbar (ps);
+ header_prepare = 0;
+ // sort
+ gtkpl_column_t *c;
+ int i = 0;
+ int x = -ps->hscrollpos;
+ int sorted = 0;
+ for (c = ps->columns; c; c = c->next, i++) {
+ int w = c->width;
+ if (event->x > x + 2 && event->x < x + w - 2) {
+ if (!c->sort_order) {
+ c->sort_order = 1;
+ }
+ else if (c->sort_order == 1) {
+ c->sort_order = 2;
+ }
+ else if (c->sort_order == 2) {
+ c->sort_order = 1;
+ }
+ deadbeef->pl_sort (ps == &main_playlist ? PL_MAIN : PL_SEARCH, c->id, c->format, c->sort_order-1);
+ sorted = 1;
+ }
+ else {
+ c->sort_order = 0;
+ }
+ x += w;
+ }
+ playlist_refresh ();
gtkpl_header_draw (ps);
gtkpl_expose_header (ps, 0, 0, ps->header->allocation.width, ps->header->allocation.height);
- gtkpl_draw_playlist (ps, 0, 0, ps->playlist->allocation.width, ps->playlist->allocation.height);
- gtkpl_expose (ps, 0, 0, ps->playlist->allocation.width, ps->playlist->allocation.height);
- gtkpl_column_rewrite_config (ps);
+ }
+ else {
+ header_sizing = -1;
+ int x = 0;
+ gtkpl_column_t *c;
+ for (c = ps->columns; c; c = c->next) {
+ int w = c->width;
+ if (event->x >= x + w - 2 && event->x <= x + w) {
+ gdk_window_set_cursor (widget->window, cursor_sz);
+ break;
+ }
+ else {
+ gdk_window_set_cursor (widget->window, NULL);
+ }
+ x += w;
+ }
+ if (header_dragging >= 0) {
+ header_dragging = -1;
+ gtkpl_setup_hscrollbar (ps);
+ gtkpl_header_draw (ps);
+ gtkpl_expose_header (ps, 0, 0, ps->header->allocation.width, ps->header->allocation.height);
+ gtkpl_draw_playlist (ps, 0, 0, ps->playlist->allocation.width, ps->playlist->allocation.height);
+ gtkpl_expose (ps, 0, 0, ps->playlist->allocation.width, ps->playlist->allocation.height);
+ gtkpl_column_rewrite_config (ps);
+ }
}
}
-// NOTE: disabled for 0.3.0 release
-// else if (event->button == 3) {
-// GtkWidget *menu = create_headermenu ();
-// gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, widget, 0, gtk_get_current_event_time());
-// }
return FALSE;
}
void
gtkpl_add_dir (gtkplaylist_t *ps, char *folder) {
- GDK_THREADS_ENTER();
- progress_show ();
- GDK_THREADS_LEAVE();
- pl_add_dir (folder, gtkpl_add_file_info_cb, NULL);
+ g_idle_add (progress_show_idle, NULL);
+ deadbeef->pl_add_dir (folder, gtkpl_add_file_info_cb, NULL);
g_free (folder);
- GDK_THREADS_ENTER();
- progress_hide ();
- playlist_refresh ();
- GDK_THREADS_LEAVE();
+ g_idle_add (progress_hide_idle, NULL);
}
static void
gtkpl_adddir_cb (gpointer data, gpointer userdata) {
- pl_add_dir (data, gtkpl_add_file_info_cb, userdata);
+ deadbeef->pl_add_dir (data, gtkpl_add_file_info_cb, userdata);
g_free (data);
}
void
gtkpl_add_dirs (gtkplaylist_t *ps, GSList *lst) {
- GDK_THREADS_ENTER();
- progress_show ();
- GDK_THREADS_LEAVE();
+ g_idle_add (progress_show_idle, NULL);
g_slist_foreach(lst, gtkpl_adddir_cb, NULL);
g_slist_free (lst);
- GDK_THREADS_ENTER();
- progress_hide ();
- playlist_refresh ();
- GDK_THREADS_LEAVE();
+ g_idle_add (progress_hide_idle, NULL);
}
static void
gtkpl_addfile_cb (gpointer data, gpointer userdata) {
- pl_add_file (data, gtkpl_add_file_info_cb, userdata);
+ deadbeef->pl_add_file (data, gtkpl_add_file_info_cb, userdata);
g_free (data);
}
void
gtkpl_add_files (gtkplaylist_t *ps, GSList *lst) {
- GDK_THREADS_ENTER();
- progress_show ();
- GDK_THREADS_LEAVE();
+ g_idle_add (progress_show_idle, NULL);
g_slist_foreach(lst, gtkpl_addfile_cb, NULL);
g_slist_free (lst);
- GDK_THREADS_ENTER();
- progress_hide ();
- playlist_refresh ();
- GDK_THREADS_LEAVE();
-}
-
-void
-gtkpl_playsong (gtkplaylist_t *ps) {
- if (p_ispaused ()) {
- p_unpause ();
- }
- else if (ps->row != -1) {
- p_stop ();
- streamer_set_nextsong (ps->row, 1);
- }
- else {
- p_stop ();
- streamer_set_nextsong (0, 1);
- }
+ g_idle_add (progress_hide_idle, NULL);
}
int
-gtkpl_get_idx_of (gtkplaylist_t *ps, playItem_t *it) {
- playItem_t *c = playlist_head[ps->iterator];
+gtkpl_get_idx_of (gtkplaylist_t *ps, DB_playItem_t *it) {
+ DB_playItem_t *c = PL_HEAD(ps->iterator);
int idx = 0;
while (c && c != it) {
- c = c->next[ps->iterator];
+ c = PL_NEXT(c, ps->iterator);;
idx++;
}
if (!c) {
@@ -1777,13 +1605,13 @@ gtkpl_get_idx_of (gtkplaylist_t *ps, playItem_t *it) {
return idx;
}
-playItem_t *
+DB_playItem_t *
gtkpl_get_for_idx (gtkplaylist_t *ps, int idx) {
- playItem_t *it = playlist_head[ps->iterator];
+ DB_playItem_t *it = PL_HEAD(ps->iterator);
while (idx--) {
if (!it)
return NULL;
- it = it->next[ps->iterator];
+ it = PL_NEXT(it, ps->iterator);
}
return it;
}
@@ -1862,122 +1690,47 @@ gtkpl_column_remove (gtkplaylist_t *pl, gtkpl_column_t *c) {
void
gtkpl_append_column_from_textdef (gtkplaylist_t *pl, const uint8_t *def) {
// syntax: "title" "format" id width alignright
- char title[128];
- char format[128];
+ char token[MAX_TOKEN];
+ const char *p = def;
+ char title[MAX_TOKEN];
int id;
+ char fmt[MAX_TOKEN];
int width;
- int align_right;
- // title
- if (*def != '"') {
- return;
- }
- def++;
- if (*def == 0) {
- return;
- }
- const uint8_t *e = def;
- e++;
- while (*e && *e != '"') {
- e++;
- }
- if (*e == 0) {
- return;
- }
- memcpy (title, def, e-def);
- title[e-def] = 0;
- // skip whitespace
- def = e;
- def++;
- while (*def && *def <= ' ') {
- def++;
- }
- if (*def == 0) {
- return;
- }
- // format
- if (*def != '"') {
- return;
- }
- def++;
- if (*def == 0) {
- return;
- }
- e = def;
- while (*e && *e != '"') {
- e++;
- }
- if (*e == 0) {
- return;
- }
- memcpy (format, def, e-def);
- format[e-def] = 0;
- // skip whitespace
- def = e;
- def++;
- while (*def && *def <= ' ') {
- def++;
- }
- if (*def == 0) {
+ int align;
+
+ parser_init ();
+
+ p = gettoken_warn_eof (p, token);
+ if (!p) {
return;
}
- // id
- e = def;
- while (*e && (isdigit (*e) || *e == '-')) {
- e++;
- }
- if (*e == 0) {
+ strcpy (title, token);
+
+ p = gettoken_warn_eof (p, token);
+ if (!p) {
return;
}
- {
- char s[e-def+1];
- memcpy (s, def, e-def);
- s[e-def] = 0;
- id = atoi (s);
- }
- // skip whitespace
- def = e;
- def++;
- while (*def && *def <= ' ') {
- def++;
- }
- if (*def == 0) {
+ strcpy (fmt, token);
+
+ p = gettoken_warn_eof (p, token);
+ if (!p) {
return;
}
- // width
- e = def;
- while (*e && isdigit (*e)) {
- e++;
- }
- if (*e == 0) {
+ id = atoi (token);
+
+ p = gettoken_warn_eof (p, token);
+ if (!p) {
return;
}
- {
- char s[e-def+1];
- memcpy (s, def, e-def);
- s[e-def] = 0;
- width = atoi (s);
- }
- // skip whitespace
- def = e;
- def++;
- while (*def && *def <= ' ') {
- def++;
- }
- if (*def == 0) {
+ width = atoi (token);
+
+ p = gettoken_warn_eof (p, token);
+ if (!p) {
return;
}
- // align_right
- e = def;
- while (*e && isdigit (*e)) {
- e++;
- }
- {
- char s[e-def+1];
- memcpy (s, def, e-def);
- s[e-def] = 0;
- align_right = atoi (s);
- }
- gtkpl_column_append (pl, gtkpl_column_alloc (title, width, id, format[0] ? format : NULL, align_right));
+ align = atoi (token);
+
+ gtkpl_column_append (pl, gtkpl_column_alloc (title, width, id, fmt, align));
}
void
@@ -1986,22 +1739,19 @@ gtkpl_column_update_config (gtkplaylist_t *pl, gtkpl_column_t *c, int idx) {
char value[128];
snprintf (key, sizeof (key), "%s.column.%d", pl->title, idx);
snprintf (value, sizeof (value), "\"%s\" \"%s\" %d %d %d", c->title, c->format ? c->format : "", c->id, c->width, c->align_right);
- conf_set_str (key, value);
+ deadbeef->conf_set_str (key, value);
}
void
gtkpl_column_rewrite_config (gtkplaylist_t *pl) {
char key[128];
- char value[128];
snprintf (key, sizeof (key), "%s.column.", pl->title);
- conf_remove_items (key);
+ deadbeef->conf_remove_items (key);
gtkpl_column_t *c;
int i = 0;
for (c = pl->columns; c; c = c->next, i++) {
- snprintf (key, sizeof (key), "%s.column.%d", pl->title, i);
- snprintf (value, sizeof (value), "\"%s\" \"%s\" %d %d %d", c->title, c->format ? c->format : "", c->id, c->width, c->align_right);
- conf_set_str (key, value);
+ gtkpl_column_update_config (pl, c, i);
}
}
@@ -2015,22 +1765,35 @@ set_tray_tooltip (const char *text) {
}
void
-gtkpl_current_track_changed (playItem_t *it) {
+gtkpl_current_track_changed (DB_playItem_t *it) {
char str[600];
- char dname[512];
- pl_format_item_display_name (it, dname, 512);
- snprintf (str, 600, "DeaDBeeF - %s", dname);
+ if (it) {
+ char dname[512];
+ deadbeef->pl_format_item_display_name (it, dname, 512);
+ snprintf (str, sizeof (str), "DeaDBeeF - %s", dname);
+ }
+ else {
+ strcpy (str, "DeaDBeeF");
+ }
gtk_window_set_title (GTK_WINDOW (mainwin), str);
set_tray_tooltip (str);
}
-void
-gtkpl_songchanged_wrapper (int from, int to) {
- GDK_THREADS_ENTER ();
+struct fromto_t {
+ int from;
+ int to;
+};
+
+static gboolean
+update_win_title_idle (gpointer data) {
+ struct fromto_t *ft = (struct fromto_t *)data;
+ int from = ft->from;
+ int to = ft->to;
+ free (ft);
// update window title
if (from >= 0 || to >= 0) {
if (to >= 0) {
- playItem_t *it = pl_get_for_idx (to);
+ DB_playItem_t *it = deadbeef->pl_get_for_idx (to);
if (it) { // it might have been deleted after event was sent
gtkpl_current_track_changed (it);
}
@@ -2042,5 +1805,121 @@ gtkpl_songchanged_wrapper (int from, int to) {
}
// update playlist view
gtkpl_songchanged (&main_playlist, from, to);
- GDK_THREADS_LEAVE ();
+ return FALSE;
+}
+
+static gboolean
+redraw_seekbar_cb (gpointer nothing) {
+ void seekbar_draw (GtkWidget *widget);
+ void seekbar_expose (GtkWidget *widget, int x, int y, int w, int h);
+ GtkWidget *widget = lookup_widget (mainwin, "seekbar");
+ seekbar_draw (widget);
+ seekbar_expose (widget, 0, 0, widget->allocation.width, widget->allocation.height);
+ return FALSE;
+}
+
+void
+redraw_queued_tracks (gtkplaylist_t *pl) {
+ DB_playItem_t *it = deadbeef->pl_get_for_idx_and_iter (pl->scrollpos, pl->iterator);
+ int i = pl->scrollpos;
+ while (it && i < pl->scrollpos + pl->nvisiblerows) {
+ if (deadbeef->pl_playqueue_test (it) != -1) {
+ gtkpl_redraw_pl_row (pl, i, it);
+ }
+ it = PL_NEXT (it, pl->iterator);
+ i++;
+ }
+}
+
+static gboolean
+redraw_queued_tracks_cb (gpointer nothing) {
+ redraw_queued_tracks (&main_playlist);
+ redraw_queued_tracks (&search_playlist);
+ return FALSE;
+}
+
+void
+gtkpl_songchanged_wrapper (int from, int to) {
+ struct fromto_t *ft = malloc (sizeof (struct fromto_t));
+ ft->from = from;
+ ft->to = to;
+ g_idle_add (update_win_title_idle, ft);
+ if (ft->to == -1) {
+ // redraw seekbar
+ g_idle_add (redraw_seekbar_cb, NULL);
+ }
+ g_idle_add (redraw_queued_tracks_cb, NULL);
+}
+
+void
+gtk_pl_redraw_item_everywhere (DB_playItem_t *it) {
+ gtkplaylist_t *pl = &search_playlist;
+ int idx = gtkpl_get_idx_of (pl, it);
+ int minvis = pl->scrollpos;
+ int maxvis = pl->scrollpos + pl->nvisiblerows-1;
+ if (idx >= minvis && idx <= maxvis) {
+ gtkpl_redraw_pl_row (pl, idx, it);
+ }
+ pl = &main_playlist;
+ idx = gtkpl_get_idx_of (pl, it);
+ minvis = pl->scrollpos;
+ maxvis = pl->scrollpos + pl->nvisiblerows-1;
+ if (idx >= minvis && idx <= maxvis) {
+ gtkpl_redraw_pl_row (pl, idx, it);
+ }
+}
+
+struct set_cursor_t {
+ int iter;
+ int cursor;
+ int prev;
+ gtkplaylist_t *pl;
+};
+
+static gboolean
+gtkpl_set_cursor_cb (gpointer data) {
+ struct set_cursor_t *sc = (struct set_cursor_t *)data;
+ deadbeef->pl_set_cursor (sc->iter, sc->cursor);
+ gtkpl_select_single (sc->pl, sc->cursor);
+ DB_playItem_t *it;
+ int minvis = sc->pl->scrollpos;
+ int maxvis = sc->pl->scrollpos + sc->pl->nvisiblerows-1;
+ if (sc->prev >= minvis && sc->prev <= maxvis) {
+ it = deadbeef->pl_get_for_idx_and_iter (sc->prev, sc->iter);
+ gtkpl_redraw_pl_row (sc->pl, sc->prev, it);
+ }
+ if (sc->cursor >= minvis && sc->cursor <= maxvis) {
+ it = deadbeef->pl_get_for_idx_and_iter (sc->cursor, sc->iter);
+ gtkpl_redraw_pl_row (sc->pl, sc->cursor, it);
+ }
+
+ int newscroll = sc->pl->scrollpos;
+ if (sc->cursor < sc->pl->scrollpos) {
+ newscroll = sc->cursor;
+ }
+ else if (sc->cursor >= sc->pl->scrollpos + sc->pl->nvisiblefullrows) {
+ newscroll = sc->cursor - sc->pl->nvisiblefullrows + 1;
+ if (newscroll < 0) {
+ newscroll = 0;
+ }
+ }
+ if (sc->pl->scrollpos != newscroll) {
+ GtkWidget *range = sc->pl->scrollbar;
+ gtk_range_set_value (GTK_RANGE (range), newscroll);
+ }
+
+ free (data);
+ return FALSE;
+}
+
+void
+gtkpl_set_cursor (int iter, int cursor) {
+ gtkplaylist_t *pl = (iter == PL_MAIN) ? &main_playlist : &search_playlist;
+ int prev = deadbeef->pl_get_cursor (iter);
+ struct set_cursor_t *data = malloc (sizeof (struct set_cursor_t));
+ data->prev = prev;
+ data->iter = iter;
+ data->cursor = cursor;
+ data->pl = pl;
+ g_idle_add (gtkpl_set_cursor_cb, data);
}
diff --git a/gtkplaylist.h b/plugins/gtkui/gtkplaylist.h
index 2e827839..ebf27f99 100644
--- a/gtkplaylist.h
+++ b/plugins/gtkui/gtkplaylist.h
@@ -21,7 +21,7 @@
#include <gtk/gtk.h>
#include <stdint.h>
#include <assert.h>
-#include "playlist.h"
+#include "../../deadbeef.h"
// drag and drop targets
enum {
@@ -57,7 +57,7 @@ typedef struct gtkpl_column_s {
int movepos; // valid only while `moving' is 1
struct gtkpl_column_s *next;
unsigned align_right : 1;
-// unsigned moving : 1;
+ unsigned sort_order : 2; // 0=none, 1=asc, 2=desc
} gtkpl_column_t;
// structure of this kind must be set as user data for playlist, header and scrollbar widgets
@@ -69,27 +69,27 @@ typedef struct {
GtkWidget *header;
GtkWidget *scrollbar;
GtkWidget *hscrollbar;
+ int totalwidth; // width of playlist, including invisible part
GdkPixmap *backbuf;
GdkPixmap *backbuf_header;
const char *title; // unique id, used for config writing, etc
// parameters
- playItem_t **pcurr; // pointer to current item
- int *pcount; // pointer to count of items in list
- int iterator; // index into next array of playItem_t struct
+ int (*get_count)(void); // function pointer to get number of tracks
+ int iterator; // index into next array of DB_playItem_t struct
int lastpos[2]; // last mouse position (for playlist widget)
- int multisel; // if it uses multiple selection
// current state
int scrollpos;
int hscrollpos;
- int row;
double clicktime; // for doubleclick detection
int nvisiblerows;
int nvisiblefullrows;
-// int *colwidths;//[pl_ncolumns]; // current column widths
-// int ncolumns;
gtkpl_column_t *columns;
+ gtkpl_column_t *active_column;
} gtkplaylist_t;
+extern gtkplaylist_t main_playlist;
+extern gtkplaylist_t search_playlist;
+
#define GTKPL_PROLOGUE \
gtkplaylist_t *ps = (gtkplaylist_t *)gtk_object_get_data (GTK_OBJECT (widget), "ps"); assert (ps);
@@ -103,19 +103,19 @@ void
gtkpl_free (gtkplaylist_t *pl);
void
-gtkpl_redraw_pl_row (gtkplaylist_t *ps, int row, playItem_t *it);
+gtkpl_redraw_pl_row (gtkplaylist_t *ps, int row, DB_playItem_t *it);
void
-gtkpl_redraw_pl_row_novis (gtkplaylist_t *ps, int row, playItem_t *it);
+gtkpl_redraw_pl_row_novis (gtkplaylist_t *ps, int row, DB_playItem_t *it);
void
gtkpl_setup_scrollbar (gtkplaylist_t *ps);
void
-gtkpl_draw_pl_row_back (gtkplaylist_t *ps, int row, playItem_t *it);
+gtkpl_draw_pl_row_back (gtkplaylist_t *ps, int row, DB_playItem_t *it);
void
-gtkpl_draw_pl_row (gtkplaylist_t *ps, int row, playItem_t *it);
+gtkpl_draw_pl_row (gtkplaylist_t *ps, int row, DB_playItem_t *it);
void
gtkpl_draw_playlist (gtkplaylist_t *ps, int x, int y, int w, int h);
@@ -151,15 +151,6 @@ void
gtkpl_track_dragdrop (gtkplaylist_t *ps, int y);
void
-gtkpl_handle_drag_drop (gtkplaylist_t *ps, int drop_y, uint32_t *d, int length);
-
-void
-gtkpl_handle_fm_drag_drop (gtkplaylist_t *ps, int drop_y, void *ptr, int length);
-
-void
-gtkpl_add_fm_dropped_files (gtkplaylist_t *ps, char *ptr, int length, int drop_y);
-
-void
gtkpl_select_single (gtkplaylist_t *ps, int sel);
void
@@ -178,14 +169,14 @@ void
gtkpl_configure (gtkplaylist_t *ps);
int
-gtkpl_get_idx_of (gtkplaylist_t *ps, playItem_t *it);
+gtkpl_get_idx_of (gtkplaylist_t *ps, DB_playItem_t *it);
-playItem_t *
+DB_playItem_t *
gtkpl_get_for_idx (gtkplaylist_t *ps, int idx);
-// this functions take value from passed playlist, that's why it's here
-void
-gtkpl_playsong (gtkplaylist_t *ps);
+//// this functions take value from passed playlist, that's why it's here
+//void
+//gtkpl_playsong (gtkplaylist_t *ps);
void
gtkpl_songchanged (gtkplaylist_t *ps, int from, int to);
@@ -256,6 +247,15 @@ void
gtkpl_songchanged_wrapper (int from, int to);
void
-gtkpl_current_track_changed (playItem_t *it);
+gtkpl_current_track_changed (DB_playItem_t *it);
+
+void
+gtk_pl_redraw_item_everywhere (DB_playItem_t *it);
+
+void
+gtkpl_set_cursor (int iter, int cursor);
+
+gtkpl_column_t*
+gtkpl_get_column_for_click (gtkplaylist_t *pl, int click_x);
#endif // __GTKPLAYLIST_H
diff --git a/plugins/gtkui/gtkui.c b/plugins/gtkui/gtkui.c
new file mode 100644
index 00000000..85c819e7
--- /dev/null
+++ b/plugins/gtkui/gtkui.c
@@ -0,0 +1,518 @@
+/*
+ DeaDBeeF - ultimate music player for GNU/Linux systems with X11
+ Copyright (C) 2009 Alexey Yakovenko
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+#include "../../deadbeef.h"
+#include <gtk/gtk.h>
+#include <string.h>
+#include <stdlib.h>
+#include "gtkplaylist.h"
+#include "search.h"
+#include "progress.h"
+#include "interface.h"
+#include "callbacks.h"
+#include "support.h"
+
+//#define trace(...) { fprintf(stderr, __VA_ARGS__); }
+#define trace(fmt,...)
+
+static DB_gui_t plugin;
+DB_functions_t *deadbeef;
+
+static intptr_t gtk_tid;
+
+// main widgets
+GtkWidget *mainwin;
+GtkWidget *searchwin;
+GtkStatusIcon *trayicon;
+GtkWidget *traymenu;
+
+// playlist configuration structures
+gtkplaylist_t main_playlist;
+gtkplaylist_t search_playlist;
+
+// update status bar and window title
+static int sb_context_id = -1;
+static char sb_text[512];
+static float last_songpos = -1;
+
+static gboolean
+update_songinfo (gpointer ctx) {
+ char sbtext_new[512] = "-";
+ float songpos = last_songpos;
+
+ float pl_totaltime = deadbeef->pl_get_totaltime ();
+ int daystotal = (int)pl_totaltime / (3600*24);
+ int hourtotal = ((int)pl_totaltime / 3600) % 24;
+ int mintotal = ((int)pl_totaltime/60) % 60;
+ int sectotal = ((int)pl_totaltime) % 60;
+
+ char totaltime_str[512] = "";
+ if (daystotal == 0)
+ snprintf (totaltime_str, sizeof (totaltime_str), "%d:%02d:%02d", hourtotal, mintotal, sectotal);
+
+ else if (daystotal == 1)
+ snprintf (totaltime_str, sizeof (totaltime_str), "1 day %d:%02d:%02d", hourtotal, mintotal, sectotal);
+
+ else
+ snprintf (totaltime_str, sizeof (totaltime_str), "%d days %d:%02d:%02d", daystotal, hourtotal, mintotal, sectotal);
+
+
+
+ DB_playItem_t *track = deadbeef->streamer_get_playing_track ();
+ float duration = deadbeef->pl_get_item_duration (track);
+
+ if (deadbeef->get_output ()->state () == OUTPUT_STATE_STOPPED) {
+ snprintf (sbtext_new, sizeof (sbtext_new), "Stopped | %d tracks | %s total playtime", deadbeef->pl_getcount (), totaltime_str);
+ songpos = 0;
+ }
+ else if (track->decoder) {
+// codec_lock ();
+ DB_decoder_t *c = track->decoder;
+ float playpos = deadbeef->streamer_get_playpos ();
+ int minpos = playpos / 60;
+ int secpos = playpos - minpos * 60;
+ int mindur = duration / 60;
+ int secdur = duration - mindur * 60;
+
+ const char *mode = c->info.channels == 1 ? "Mono" : "Stereo";
+ int samplerate = c->info.samplerate;
+ int bitspersample = c->info.bps;
+ songpos = playpos;
+// codec_unlock ();
+
+ char t[100];
+ if (duration >= 0) {
+ snprintf (t, sizeof (t), "%d:%02d", mindur, secdur);
+ }
+ else {
+ strcpy (t, "-:--");
+ }
+
+ char sbitrate[20] = "";
+#if 1
+ int bitrate = deadbeef->streamer_get_apx_bitrate ();
+ if (bitrate > 0) {
+ snprintf (sbitrate, sizeof (sbitrate), "| %d kbps ", bitrate);
+ }
+#endif
+ const char *spaused = deadbeef->get_output ()->state () == OUTPUT_STATE_PAUSED ? "Paused | " : "";
+ snprintf (sbtext_new, sizeof (sbtext_new), "%s%s %s| %dHz | %d bit | %s | %d:%02d / %s | %d tracks | %s total playtime", spaused, track->filetype ? track->filetype:"-", sbitrate, samplerate, bitspersample, mode, minpos, secpos, t, deadbeef->pl_getcount (), totaltime_str);
+ }
+
+ if (strcmp (sbtext_new, sb_text)) {
+ strcpy (sb_text, sbtext_new);
+
+ // form statusline
+ // FIXME: don't update if window is not visible
+ GtkStatusbar *sb = GTK_STATUSBAR (lookup_widget (mainwin, "statusbar"));
+ if (sb_context_id == -1) {
+ sb_context_id = gtk_statusbar_get_context_id (sb, "msg");
+ }
+
+ gtk_statusbar_pop (sb, sb_context_id);
+ gtk_statusbar_push (sb, sb_context_id, sb_text);
+ }
+
+ if (songpos != last_songpos) {
+ void seekbar_draw (GtkWidget *widget);
+ void seekbar_expose (GtkWidget *widget, int x, int y, int w, int h);
+ if (mainwin) {
+ GtkWidget *widget = lookup_widget (mainwin, "seekbar");
+ // translate volume to seekbar pixels
+ songpos /= duration;
+ songpos *= widget->allocation.width;
+ if (songpos != last_songpos) {
+ seekbar_draw (widget);
+ seekbar_expose (widget, 0, 0, widget->allocation.width, widget->allocation.height);
+ last_songpos = songpos;
+ }
+ }
+ }
+ return FALSE;
+}
+
+gboolean
+on_trayicon_scroll_event (GtkWidget *widget,
+ GdkEventScroll *event,
+ gpointer user_data)
+{
+ float vol = deadbeef->volume_get_db ();
+ if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_RIGHT) {
+ vol += 1;
+ }
+ else if (event->direction == GDK_SCROLL_DOWN || event->direction == GDK_SCROLL_LEFT) {
+ vol -= 1;
+ }
+ if (vol > 0) {
+ vol = 0;
+ }
+ else if (vol < -60) {
+ vol = -60;
+ }
+ deadbeef->volume_set_db (vol);
+ GtkWidget *volumebar = lookup_widget (mainwin, "volumebar");
+ volumebar_draw (volumebar);
+ volumebar_expose (volumebar, 0, 0, volumebar->allocation.width, volumebar->allocation.height);
+ return FALSE;
+}
+
+#if GTK_MINOR_VERSION<=14
+
+gboolean
+on_trayicon_activate (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ if (GTK_WIDGET_VISIBLE (mainwin)) {
+ gtk_widget_hide (mainwin);
+ }
+ else {
+ int x = deadbeef->conf_get_int ("mainwin.geometry.x", 40);
+ int y = deadbeef->conf_get_int ("mainwin.geometry.y", 40);
+ int w = deadbeef->conf_get_int ("mainwin.geometry.w", 500);
+ int h = deadbeef->conf_get_int ("mainwin.geometry.h", 300);
+ gtk_widget_show (mainwin);
+ gtk_window_move (mainwin, x, y);
+ gtk_window_resize (mainwin, w, h);
+ if (deadbeef->conf_get_int ("mainwin.geometry.maximized", 0)) {
+ gtk_window_maximize (GTK_WINDOW (mainwin));
+ }
+ gtk_window_present (GTK_WINDOW (mainwin));
+ }
+ return FALSE;
+}
+
+#else
+
+gboolean
+on_trayicon_button_press_event (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer user_data)
+{
+ if (event->button == 1) {
+ if (GTK_WIDGET_VISIBLE (mainwin)) {
+ gtk_widget_hide (mainwin);
+ }
+ else {
+ gtk_widget_show (mainwin);
+ int x = deadbeef->conf_get_int ("mainwin.geometry.x", 40);
+ int y = deadbeef->conf_get_int ("mainwin.geometry.y", 40);
+ int w = deadbeef->conf_get_int ("mainwin.geometry.w", 500);
+ int h = deadbeef->conf_get_int ("mainwin.geometry.h", 300);
+ gtk_window_move (GTK_WINDOW (mainwin), x, y);
+ gtk_window_resize (GTK_WINDOW (mainwin), w, h);
+ if (deadbeef->conf_get_int ("mainwin.geometry.maximized", 0)) {
+ gtk_window_maximize (GTK_WINDOW (mainwin));
+ }
+ gtk_window_present (GTK_WINDOW (mainwin));
+ }
+ }
+ else if (event->button == 2) {
+ deadbeef->sendmessage (M_PAUSESONG, 0, 0, 0);
+ }
+ return FALSE;
+}
+#endif
+
+gboolean
+on_trayicon_popup_menu (GtkWidget *widget,
+ guint button,
+ guint time,
+ gpointer user_data)
+{
+ gtk_menu_popup (GTK_MENU (traymenu), NULL, NULL, gtk_status_icon_position_menu, trayicon, button, time);
+ return FALSE;
+}
+
+static gboolean
+activate_cb (gpointer nothing) {
+ gtk_widget_show (mainwin);
+ gtk_window_present (GTK_WINDOW (mainwin));
+ return FALSE;
+}
+
+static int
+gtkui_on_activate (DB_event_t *ev, uintptr_t data) {
+ g_idle_add (activate_cb, NULL);
+ return 0;
+}
+
+static int
+gtkui_on_songchanged (DB_event_trackchange_t *ev, uintptr_t data) {
+ gtkpl_songchanged_wrapper (ev->from, ev->to);
+ return 0;
+}
+
+struct trackinfo_t {
+ int index;
+ DB_playItem_t *track;
+};
+
+static gboolean
+trackinfochanged_cb (gpointer data) {
+ struct trackinfo_t *ti = (struct trackinfo_t *)data;
+ gtkpl_redraw_pl_row (&main_playlist, ti->index, ti->track);
+ if (ti->track == deadbeef->pl_getcurrent ()) {
+ gtkpl_current_track_changed (ti->track);
+ }
+ free (ti);
+ return FALSE;
+}
+
+static int
+gtkui_on_trackinfochanged (DB_event_track_t *ev, uintptr_t data) {
+ struct trackinfo_t *ti = malloc (sizeof (struct trackinfo_t));
+ ti->index = ev->index;
+ ti->track = ev->track;
+ g_idle_add (trackinfochanged_cb, ti);
+ return 0;
+}
+
+static gboolean
+paused_cb (gpointer nothing) {
+ DB_playItem_t *curr = deadbeef->pl_getcurrent ();
+ if (curr) {
+ int idx = deadbeef->pl_get_idx_of (curr);
+ gtkpl_redraw_pl_row (&main_playlist, idx, curr);
+ }
+ return FALSE;
+}
+
+static int
+gtkui_on_paused (DB_event_state_t *ev, uintptr_t data) {
+ g_idle_add (paused_cb, NULL);
+}
+
+static gboolean
+playlistchanged_cb (gpointer none) {
+ playlist_refresh ();
+ search_refresh ();
+ return FALSE;
+}
+
+static int
+gtkui_on_playlistchanged (DB_event_t *ev, uintptr_t data) {
+ g_idle_add (playlistchanged_cb, NULL);
+}
+
+static int
+gtkui_on_frameupdate (DB_event_t *ev, uintptr_t data) {
+ g_idle_add (update_songinfo, NULL);
+}
+
+static int
+gtkui_on_volumechanged (DB_event_t *ev, uintptr_t data) {
+ void volumebar_notify_changed (void); // FIXME: do it properly
+ volumebar_notify_changed ();
+ return 0;
+}
+
+static int
+gtkui_on_configchanged (DB_event_t *ev, uintptr_t data) {
+ // order and looping
+ const char *w;
+
+ // order
+ const char *orderwidgets[3] = { "order_linear", "order_shuffle", "order_random" };
+ w = orderwidgets[deadbeef->conf_get_int ("playback.order", PLAYBACK_ORDER_LINEAR)];
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (lookup_widget (mainwin, w)), TRUE);
+
+ // looping
+ const char *loopingwidgets[3] = { "loop_all", "loop_disable", "loop_single" };
+ w = loopingwidgets[deadbeef->conf_get_int ("playback.loop", PLAYBACK_MODE_LOOP_ALL)];
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (lookup_widget (mainwin, w)), TRUE);
+
+ // scroll follows playback
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (lookup_widget (mainwin, "scroll_follows_playback")), deadbeef->conf_get_int ("playlist.scroll.followplayback", 0) ? TRUE : FALSE);
+
+ // cursor follows playback
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (lookup_widget (mainwin, "cursor_follows_playback")), deadbeef->conf_get_int ("playlist.scroll.cursorfollowplayback", 0) ? TRUE : FALSE);
+
+ // stop after current
+ int stop_after_current = deadbeef->conf_get_int ("playlist.stop_after_current", 0);
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (lookup_widget (mainwin, "stop_after_current")), stop_after_current ? TRUE : FALSE);
+ return 0;
+}
+
+static gboolean
+outputchanged_cb (gpointer nothing) {
+ void preferences_fill_soundcards (void);
+ preferences_fill_soundcards ();
+ return FALSE;
+}
+
+static int
+gtkui_on_outputchanged (DB_event_t *ev, uintptr_t nothing) {
+ g_idle_add (outputchanged_cb, NULL);
+ return 0;
+}
+
+
+void
+gtkui_thread (void *ctx) {
+ // let's start some gtk
+ g_thread_init (NULL);
+ add_pixmap_directory (PREFIX "/share/deadbeef/pixmaps");
+ gdk_threads_init ();
+ gdk_threads_enter ();
+ gtk_set_locale ();
+ int argc = 1;
+ char **argv = alloca (sizeof (char *));
+ argv[0] = "deadbeef";
+ gtk_init (&argc, (char ***)&argv);
+
+ // system tray icon
+ traymenu = create_traymenu ();
+ GdkPixbuf *trayicon_pixbuf = create_pixbuf ("play_24.png");
+ trayicon = gtk_status_icon_new_from_pixbuf (trayicon_pixbuf);
+ set_tray_tooltip ("DeaDBeeF");
+ //gtk_status_icon_set_title (GTK_STATUS_ICON (trayicon), "DeaDBeeF");
+#if GTK_MINOR_VERSION <= 14
+ g_signal_connect ((gpointer)trayicon, "activate", G_CALLBACK (on_trayicon_activate), NULL);
+#else
+ g_signal_connect ((gpointer)trayicon, "scroll_event", G_CALLBACK (on_trayicon_scroll_event), NULL);
+ g_signal_connect ((gpointer)trayicon, "button_press_event", G_CALLBACK (on_trayicon_button_press_event), NULL);
+#endif
+ g_signal_connect ((gpointer)trayicon, "popup_menu", G_CALLBACK (on_trayicon_popup_menu), NULL);
+
+ mainwin = create_mainwin ();
+ gtkpl_init ();
+
+ GdkPixbuf *mainwin_icon_pixbuf;
+ mainwin_icon_pixbuf = create_pixbuf ("play_24.png");
+ if (mainwin_icon_pixbuf)
+ {
+ gtk_window_set_icon (GTK_WINDOW (mainwin), mainwin_icon_pixbuf);
+ gdk_pixbuf_unref (mainwin_icon_pixbuf);
+ }
+ {
+ int x = deadbeef->conf_get_int ("mainwin.geometry.x", 40);
+ int y = deadbeef->conf_get_int ("mainwin.geometry.y", 40);
+ int w = deadbeef->conf_get_int ("mainwin.geometry.w", 500);
+ int h = deadbeef->conf_get_int ("mainwin.geometry.h", 300);
+ gtk_window_move (GTK_WINDOW (mainwin), x, y);
+ gtk_window_resize (GTK_WINDOW (mainwin), w, h);
+ if (deadbeef->conf_get_int ("mainwin.geometry.maximized", 0)) {
+ gtk_window_maximize (GTK_WINDOW (mainwin));
+ }
+ }
+
+ gtkui_on_configchanged (NULL, 0);
+
+ // visibility of statusbar and headers
+ GtkWidget *header_mi = lookup_widget (mainwin, "view_headers");
+ GtkWidget *sb_mi = lookup_widget (mainwin, "view_status_bar");
+ GtkWidget *header = lookup_widget (mainwin, "header");
+ GtkWidget *sb = lookup_widget (mainwin, "statusbar");
+ if (deadbeef->conf_get_int ("gtkui.headers.visible", 1)) {
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (header_mi), TRUE);
+ }
+ else {
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (header_mi), FALSE);
+ gtk_widget_hide (header);
+ }
+ if (deadbeef->conf_get_int ("gtkui.statusbar.visible", 1)) {
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (sb_mi), TRUE);
+ }
+ else {
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (sb_mi), FALSE);
+ gtk_widget_hide (sb);
+ }
+
+ searchwin = create_searchwin ();
+ gtk_window_set_transient_for (GTK_WINDOW (searchwin), GTK_WINDOW (mainwin));
+ extern void main_playlist_init (GtkWidget *widget);
+ main_playlist_init (lookup_widget (mainwin, "playlist"));
+ extern void search_playlist_init (GtkWidget *widget);
+ search_playlist_init (lookup_widget (searchwin, "searchlist"));
+
+ progress_init ();
+ gtk_widget_show (mainwin);
+
+ gtk_main ();
+ gdk_threads_leave ();
+}
+
+static int
+gtkui_start (void) {
+ deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_ACTIVATE, DB_CALLBACK (gtkui_on_activate), 0);
+ deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_SONGCHANGED, DB_CALLBACK (gtkui_on_songchanged), 0);
+ deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_TRACKINFOCHANGED, DB_CALLBACK (gtkui_on_trackinfochanged), 0);
+ deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_PAUSED, DB_CALLBACK (gtkui_on_paused), 0);
+ deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_PLAYLISTCHANGED, DB_CALLBACK (gtkui_on_playlistchanged), 0);
+ deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_FRAMEUPDATE, DB_CALLBACK (gtkui_on_frameupdate), 0);
+ deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_VOLUMECHANGED, DB_CALLBACK (gtkui_on_volumechanged), 0);
+ deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_CONFIGCHANGED, DB_CALLBACK (gtkui_on_configchanged), 0);
+ deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_OUTPUTCHANGED, DB_CALLBACK (gtkui_on_outputchanged), 0);
+ // gtk must be running in separate thread
+ gtk_tid = deadbeef->thread_start (gtkui_thread, NULL);
+
+ return 0;
+}
+
+static gboolean
+quit_gtk_cb (gpointer nothing) {
+ gtk_main_quit ();
+ return FALSE;
+}
+
+static int
+gtkui_stop (void) {
+ trace ("unsubscribing events\n");
+ deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_ACTIVATE, DB_CALLBACK (gtkui_on_activate), 0);
+ deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_SONGCHANGED, DB_CALLBACK (gtkui_on_songchanged), 0);
+ deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_TRACKINFOCHANGED, DB_CALLBACK (gtkui_on_trackinfochanged), 0);
+ deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_PAUSED, DB_CALLBACK (gtkui_on_paused), 0);
+ deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_PLAYLISTCHANGED, DB_CALLBACK (gtkui_on_playlistchanged), 0);
+ deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_FRAMEUPDATE, DB_CALLBACK (gtkui_on_frameupdate), 0);
+ deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_VOLUMECHANGED, DB_CALLBACK (gtkui_on_volumechanged), 0);
+ deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_CONFIGCHANGED, DB_CALLBACK (gtkui_on_configchanged), 0);
+ deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_OUTPUTCHANGED, DB_CALLBACK (gtkui_on_outputchanged), 0);
+ trace ("quitting gtk\n");
+ g_idle_add (quit_gtk_cb, NULL);
+ trace ("waiting for gtk thread to finish\n");
+ deadbeef->thread_join (gtk_tid);
+ trace ("gtk thread finished\n");
+ gtk_tid = 0;
+ gtkpl_free (&main_playlist);
+ gtkpl_free (&search_playlist);
+ trace ("gtkui_stop completed\n");
+ return 0;
+}
+
+DB_plugin_t *
+gtkui_load (DB_functions_t *api) {
+ deadbeef = api;
+ return DB_PLUGIN (&plugin);
+}
+
+// define plugin interface
+static DB_gui_t plugin = {
+ DB_PLUGIN_SET_API_VERSION
+ .plugin.version_major = 0,
+ .plugin.version_minor = 1,
+ .plugin.nostop = 1,
+ .plugin.type = DB_PLUGIN_MISC,
+ .plugin.name = "Standard GTK2 user interface",
+ .plugin.descr = "Default DeaDBeeF GUI",
+ .plugin.author = "Alexey Yakovenko",
+ .plugin.email = "waker@users.sourceforge.net",
+ .plugin.website = "http://deadbeef.sf.net",
+ .plugin.start = gtkui_start,
+ .plugin.stop = gtkui_stop
+};
diff --git a/plugins/gtkui/gtkui.h b/plugins/gtkui/gtkui.h
new file mode 100644
index 00000000..ae88ecff
--- /dev/null
+++ b/plugins/gtkui/gtkui.h
@@ -0,0 +1,20 @@
+#ifndef __GTKUI_H
+#define __GTKUI_H
+
+extern DB_functions_t *deadbeef;
+
+struct _GSList;
+
+void
+gtkui_add_dirs (struct _GSList *lst);
+
+void
+gtkui_add_files (struct _GSList *lst);
+
+void
+gtkui_open_files (struct _GSList *lst);
+
+void
+gtkui_receive_fm_drop (char *mem, int length, int drop_y);
+
+#endif
diff --git a/interface.c b/plugins/gtkui/interface.c
index 5c674a58..cfd805a4 100644
--- a/interface.c
+++ b/plugins/gtkui/interface.c
@@ -35,36 +35,41 @@ create_mainwin (void)
GtkWidget *menuitem1;
GtkWidget *menuitem1_menu;
GtkWidget *open;
- GtkWidget *image120;
+ GtkWidget *image290;
GtkWidget *separator2;
GtkWidget *add_files;
- GtkWidget *image121;
+ GtkWidget *image291;
GtkWidget *add_folders;
- GtkWidget *image122;
+ GtkWidget *image292;
GtkWidget *add_audio_cd;
- GtkWidget *image123;
+ GtkWidget *image293;
GtkWidget *add_location1;
GtkWidget *separatormenuitem1;
+ GtkWidget *playlist_load;
+ GtkWidget *playlist_save;
+ GtkWidget *playlist_save_as;
+ GtkWidget *separator8;
GtkWidget *quit;
- GtkWidget *image124;
+ GtkWidget *image294;
GtkWidget *edit1;
GtkWidget *edit1_menu;
GtkWidget *clear1;
- GtkWidget *image125;
+ GtkWidget *image295;
GtkWidget *select_all1;
GtkWidget *selection1;
GtkWidget *selection1_menu;
GtkWidget *remove1;
- GtkWidget *image126;
+ GtkWidget *image296;
GtkWidget *crop1;
GtkWidget *find1;
GtkWidget *separator5;
GtkWidget *preferences;
- GtkWidget *playlist1;
- GtkWidget *playlist1_menu;
- GtkWidget *playlist_load;
- GtkWidget *playlist_save;
- GtkWidget *playlist_save_as;
+ GtkWidget *view1;
+ GtkWidget *view1_menu;
+ GtkWidget *view_status_bar;
+ GtkWidget *view_headers;
+ GtkWidget *playback1;
+ GtkWidget *playback1_menu;
GtkWidget *order1;
GtkWidget *order1_menu;
GSList *order_linear_group = NULL;
@@ -78,11 +83,13 @@ create_mainwin (void)
GtkWidget *loop_single;
GtkWidget *loop_disable;
GtkWidget *scroll_follows_playback;
+ GtkWidget *cursor_follows_playback;
+ GtkWidget *stop_after_current;
GtkWidget *menuitem4;
GtkWidget *menuitem4_menu;
GtkWidget *about1;
GtkWidget *help1;
- GtkWidget *image127;
+ GtkWidget *image297;
GtkWidget *hbox2;
GtkWidget *hbox3;
GtkWidget *stopbtn;
@@ -137,9 +144,9 @@ create_mainwin (void)
GDK_O, (GdkModifierType) GDK_CONTROL_MASK,
GTK_ACCEL_VISIBLE);
- image120 = gtk_image_new_from_stock ("gtk-open", GTK_ICON_SIZE_MENU);
- gtk_widget_show (image120);
- gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (open), image120);
+ image290 = gtk_image_new_from_stock ("gtk-open", GTK_ICON_SIZE_MENU);
+ gtk_widget_show (image290);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (open), image290);
separator2 = gtk_separator_menu_item_new ();
gtk_widget_show (separator2);
@@ -150,25 +157,25 @@ create_mainwin (void)
gtk_widget_show (add_files);
gtk_container_add (GTK_CONTAINER (menuitem1_menu), add_files);
- image121 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU);
- gtk_widget_show (image121);
- gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_files), image121);
+ image291 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU);
+ gtk_widget_show (image291);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_files), image291);
add_folders = gtk_image_menu_item_new_with_mnemonic ("Add folder(s)");
gtk_widget_show (add_folders);
gtk_container_add (GTK_CONTAINER (menuitem1_menu), add_folders);
- image122 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU);
- gtk_widget_show (image122);
- gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_folders), image122);
+ image292 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU);
+ gtk_widget_show (image292);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_folders), image292);
add_audio_cd = gtk_image_menu_item_new_with_mnemonic ("Add Audio CD");
gtk_widget_show (add_audio_cd);
gtk_container_add (GTK_CONTAINER (menuitem1_menu), add_audio_cd);
- image123 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU);
- gtk_widget_show (image123);
- gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_audio_cd), image123);
+ image293 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU);
+ gtk_widget_show (image293);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_audio_cd), image293);
add_location1 = gtk_menu_item_new_with_mnemonic ("Add location");
gtk_widget_show (add_location1);
@@ -179,6 +186,23 @@ create_mainwin (void)
gtk_container_add (GTK_CONTAINER (menuitem1_menu), separatormenuitem1);
gtk_widget_set_sensitive (separatormenuitem1, FALSE);
+ playlist_load = gtk_menu_item_new_with_mnemonic ("Load playlist");
+ gtk_widget_show (playlist_load);
+ gtk_container_add (GTK_CONTAINER (menuitem1_menu), playlist_load);
+
+ playlist_save = gtk_menu_item_new_with_mnemonic ("Save playlist");
+ gtk_widget_show (playlist_save);
+ gtk_container_add (GTK_CONTAINER (menuitem1_menu), playlist_save);
+
+ playlist_save_as = gtk_menu_item_new_with_mnemonic ("Save playlist as");
+ gtk_widget_show (playlist_save_as);
+ gtk_container_add (GTK_CONTAINER (menuitem1_menu), playlist_save_as);
+
+ separator8 = gtk_separator_menu_item_new ();
+ gtk_widget_show (separator8);
+ gtk_container_add (GTK_CONTAINER (menuitem1_menu), separator8);
+ gtk_widget_set_sensitive (separator8, FALSE);
+
quit = gtk_image_menu_item_new_with_mnemonic ("_Quit");
gtk_widget_show (quit);
gtk_container_add (GTK_CONTAINER (menuitem1_menu), quit);
@@ -186,11 +210,11 @@ create_mainwin (void)
GDK_Q, (GdkModifierType) GDK_CONTROL_MASK,
GTK_ACCEL_VISIBLE);
- image124 = gtk_image_new_from_stock ("gtk-quit", GTK_ICON_SIZE_MENU);
- gtk_widget_show (image124);
- gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (quit), image124);
+ image294 = gtk_image_new_from_stock ("gtk-quit", GTK_ICON_SIZE_MENU);
+ gtk_widget_show (image294);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (quit), image294);
- edit1 = gtk_menu_item_new_with_mnemonic ("Edit");
+ edit1 = gtk_menu_item_new_with_mnemonic ("_Edit");
gtk_widget_show (edit1);
gtk_container_add (GTK_CONTAINER (menubar1), edit1);
@@ -201,9 +225,9 @@ create_mainwin (void)
gtk_widget_show (clear1);
gtk_container_add (GTK_CONTAINER (edit1_menu), clear1);
- image125 = gtk_image_new_from_stock ("gtk-clear", GTK_ICON_SIZE_MENU);
- gtk_widget_show (image125);
- gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (clear1), image125);
+ image295 = gtk_image_new_from_stock ("gtk-clear", GTK_ICON_SIZE_MENU);
+ gtk_widget_show (image295);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (clear1), image295);
select_all1 = gtk_menu_item_new_with_mnemonic ("Select all");
gtk_widget_show (select_all1);
@@ -226,9 +250,9 @@ create_mainwin (void)
GDK_Delete, (GdkModifierType) 0,
GTK_ACCEL_VISIBLE);
- image126 = gtk_image_new_from_stock ("gtk-remove", GTK_ICON_SIZE_MENU);
- gtk_widget_show (image126);
- gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (remove1), image126);
+ image296 = gtk_image_new_from_stock ("gtk-remove", GTK_ICON_SIZE_MENU);
+ gtk_widget_show (image296);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (remove1), image296);
crop1 = gtk_menu_item_new_with_mnemonic ("Crop");
gtk_widget_show (crop1);
@@ -250,28 +274,31 @@ create_mainwin (void)
gtk_widget_show (preferences);
gtk_container_add (GTK_CONTAINER (edit1_menu), preferences);
- playlist1 = gtk_menu_item_new_with_mnemonic ("Playlist");
- gtk_widget_show (playlist1);
- gtk_container_add (GTK_CONTAINER (menubar1), playlist1);
+ view1 = gtk_menu_item_new_with_mnemonic ("_View");
+ gtk_widget_show (view1);
+ gtk_container_add (GTK_CONTAINER (menubar1), view1);
- playlist1_menu = gtk_menu_new ();
- gtk_menu_item_set_submenu (GTK_MENU_ITEM (playlist1), playlist1_menu);
+ view1_menu = gtk_menu_new ();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (view1), view1_menu);
- playlist_load = gtk_menu_item_new_with_mnemonic ("Load");
- gtk_widget_show (playlist_load);
- gtk_container_add (GTK_CONTAINER (playlist1_menu), playlist_load);
+ view_status_bar = gtk_check_menu_item_new_with_mnemonic ("Status bar");
+ gtk_widget_show (view_status_bar);
+ gtk_container_add (GTK_CONTAINER (view1_menu), view_status_bar);
- playlist_save = gtk_menu_item_new_with_mnemonic ("Save");
- gtk_widget_show (playlist_save);
- gtk_container_add (GTK_CONTAINER (playlist1_menu), playlist_save);
+ view_headers = gtk_check_menu_item_new_with_mnemonic ("Column headers");
+ gtk_widget_show (view_headers);
+ gtk_container_add (GTK_CONTAINER (view1_menu), view_headers);
- playlist_save_as = gtk_menu_item_new_with_mnemonic ("Save As");
- gtk_widget_show (playlist_save_as);
- gtk_container_add (GTK_CONTAINER (playlist1_menu), playlist_save_as);
+ playback1 = gtk_menu_item_new_with_mnemonic ("_Playback");
+ gtk_widget_show (playback1);
+ gtk_container_add (GTK_CONTAINER (menubar1), playback1);
+
+ playback1_menu = gtk_menu_new ();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (playback1), playback1_menu);
order1 = gtk_menu_item_new_with_mnemonic ("Order");
gtk_widget_show (order1);
- gtk_container_add (GTK_CONTAINER (playlist1_menu), order1);
+ gtk_container_add (GTK_CONTAINER (playback1_menu), order1);
order1_menu = gtk_menu_new ();
gtk_menu_item_set_submenu (GTK_MENU_ITEM (order1), order1_menu);
@@ -296,7 +323,7 @@ create_mainwin (void)
looping1 = gtk_menu_item_new_with_mnemonic ("Looping");
gtk_widget_show (looping1);
- gtk_container_add (GTK_CONTAINER (playlist1_menu), looping1);
+ gtk_container_add (GTK_CONTAINER (playback1_menu), looping1);
looping1_menu = gtk_menu_new ();
gtk_menu_item_set_submenu (GTK_MENU_ITEM (looping1), looping1_menu);
@@ -321,9 +348,20 @@ create_mainwin (void)
scroll_follows_playback = gtk_check_menu_item_new_with_mnemonic ("Scroll follows playback");
gtk_widget_show (scroll_follows_playback);
- gtk_container_add (GTK_CONTAINER (playlist1_menu), scroll_follows_playback);
+ gtk_container_add (GTK_CONTAINER (playback1_menu), scroll_follows_playback);
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (scroll_follows_playback), TRUE);
+ cursor_follows_playback = gtk_check_menu_item_new_with_mnemonic ("Cursor follows playback");
+ gtk_widget_show (cursor_follows_playback);
+ gtk_container_add (GTK_CONTAINER (playback1_menu), cursor_follows_playback);
+
+ stop_after_current = gtk_check_menu_item_new_with_mnemonic ("Stop after current");
+ gtk_widget_show (stop_after_current);
+ gtk_container_add (GTK_CONTAINER (playback1_menu), stop_after_current);
+ gtk_widget_add_accelerator (stop_after_current, "activate", accel_group,
+ GDK_M, (GdkModifierType) GDK_CONTROL_MASK,
+ GTK_ACCEL_VISIBLE);
+
menuitem4 = gtk_menu_item_new_with_mnemonic ("_Help");
gtk_widget_show (menuitem4);
gtk_container_add (GTK_CONTAINER (menubar1), menuitem4);
@@ -339,9 +377,9 @@ create_mainwin (void)
gtk_widget_show (help1);
gtk_container_add (GTK_CONTAINER (menuitem4_menu), help1);
- image127 = gtk_image_new_from_stock ("gtk-help", GTK_ICON_SIZE_MENU);
- gtk_widget_show (image127);
- gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (help1), image127);
+ image297 = gtk_image_new_from_stock ("gtk-help", GTK_ICON_SIZE_MENU);
+ gtk_widget_show (image297);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (help1), image297);
hbox2 = gtk_hbox_new (FALSE, 0);
gtk_widget_show (hbox2);
@@ -457,7 +495,7 @@ create_mainwin (void)
gtk_widget_show (header);
gtk_box_pack_start (GTK_BOX (vbox3), header, FALSE, TRUE, 0);
gtk_widget_set_size_request (header, -1, 24);
- gtk_widget_set_events (header, GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
+ gtk_widget_set_events (header, GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
playlist = gtk_drawing_area_new ();
gtk_widget_show (playlist);
@@ -489,6 +527,9 @@ create_mainwin (void)
g_signal_connect ((gpointer) mainwin, "configure_event",
G_CALLBACK (on_mainwin_configure_event),
NULL);
+ g_signal_connect ((gpointer) mainwin, "window_state_event",
+ G_CALLBACK (on_mainwin_window_state_event),
+ NULL);
g_signal_connect ((gpointer) open, "activate",
G_CALLBACK (on_open_activate),
NULL);
@@ -504,6 +545,15 @@ create_mainwin (void)
g_signal_connect ((gpointer) add_location1, "activate",
G_CALLBACK (on_add_location_activate),
NULL);
+ g_signal_connect ((gpointer) playlist_load, "activate",
+ G_CALLBACK (on_playlist_load_activate),
+ NULL);
+ g_signal_connect ((gpointer) playlist_save, "activate",
+ G_CALLBACK (on_playlist_save_activate),
+ NULL);
+ g_signal_connect ((gpointer) playlist_save_as, "activate",
+ G_CALLBACK (on_playlist_save_as_activate),
+ NULL);
g_signal_connect ((gpointer) quit, "activate",
G_CALLBACK (on_quit_activate),
NULL);
@@ -525,14 +575,11 @@ create_mainwin (void)
g_signal_connect ((gpointer) preferences, "activate",
G_CALLBACK (on_preferences_activate),
NULL);
- g_signal_connect ((gpointer) playlist_load, "activate",
- G_CALLBACK (on_playlist_load_activate),
- NULL);
- g_signal_connect ((gpointer) playlist_save, "activate",
- G_CALLBACK (on_playlist_save_activate),
+ g_signal_connect ((gpointer) view_status_bar, "activate",
+ G_CALLBACK (on_toggle_status_bar_activate),
NULL);
- g_signal_connect ((gpointer) playlist_save_as, "activate",
- G_CALLBACK (on_playlist_save_as_activate),
+ g_signal_connect ((gpointer) view_headers, "activate",
+ G_CALLBACK (on_toggle_column_headers_activate),
NULL);
g_signal_connect ((gpointer) order_linear, "activate",
G_CALLBACK (on_order_linear_activate),
@@ -555,6 +602,12 @@ create_mainwin (void)
g_signal_connect ((gpointer) scroll_follows_playback, "activate",
G_CALLBACK (on_scroll_follows_playback_activate),
NULL);
+ g_signal_connect ((gpointer) cursor_follows_playback, "activate",
+ G_CALLBACK (on_cursor_follows_playback_activate),
+ NULL);
+ g_signal_connect ((gpointer) stop_after_current, "activate",
+ G_CALLBACK (on_stop_after_current_activate),
+ NULL);
g_signal_connect ((gpointer) about1, "activate",
G_CALLBACK (on_about1_activate),
NULL);
@@ -689,36 +742,41 @@ create_mainwin (void)
GLADE_HOOKUP_OBJECT (mainwin, menuitem1, "menuitem1");
GLADE_HOOKUP_OBJECT (mainwin, menuitem1_menu, "menuitem1_menu");
GLADE_HOOKUP_OBJECT (mainwin, open, "open");
- GLADE_HOOKUP_OBJECT (mainwin, image120, "image120");
+ GLADE_HOOKUP_OBJECT (mainwin, image290, "image290");
GLADE_HOOKUP_OBJECT (mainwin, separator2, "separator2");
GLADE_HOOKUP_OBJECT (mainwin, add_files, "add_files");
- GLADE_HOOKUP_OBJECT (mainwin, image121, "image121");
+ GLADE_HOOKUP_OBJECT (mainwin, image291, "image291");
GLADE_HOOKUP_OBJECT (mainwin, add_folders, "add_folders");
- GLADE_HOOKUP_OBJECT (mainwin, image122, "image122");
+ GLADE_HOOKUP_OBJECT (mainwin, image292, "image292");
GLADE_HOOKUP_OBJECT (mainwin, add_audio_cd, "add_audio_cd");
- GLADE_HOOKUP_OBJECT (mainwin, image123, "image123");
+ GLADE_HOOKUP_OBJECT (mainwin, image293, "image293");
GLADE_HOOKUP_OBJECT (mainwin, add_location1, "add_location1");
GLADE_HOOKUP_OBJECT (mainwin, separatormenuitem1, "separatormenuitem1");
+ GLADE_HOOKUP_OBJECT (mainwin, playlist_load, "playlist_load");
+ GLADE_HOOKUP_OBJECT (mainwin, playlist_save, "playlist_save");
+ GLADE_HOOKUP_OBJECT (mainwin, playlist_save_as, "playlist_save_as");
+ GLADE_HOOKUP_OBJECT (mainwin, separator8, "separator8");
GLADE_HOOKUP_OBJECT (mainwin, quit, "quit");
- GLADE_HOOKUP_OBJECT (mainwin, image124, "image124");
+ GLADE_HOOKUP_OBJECT (mainwin, image294, "image294");
GLADE_HOOKUP_OBJECT (mainwin, edit1, "edit1");
GLADE_HOOKUP_OBJECT (mainwin, edit1_menu, "edit1_menu");
GLADE_HOOKUP_OBJECT (mainwin, clear1, "clear1");
- GLADE_HOOKUP_OBJECT (mainwin, image125, "image125");
+ GLADE_HOOKUP_OBJECT (mainwin, image295, "image295");
GLADE_HOOKUP_OBJECT (mainwin, select_all1, "select_all1");
GLADE_HOOKUP_OBJECT (mainwin, selection1, "selection1");
GLADE_HOOKUP_OBJECT (mainwin, selection1_menu, "selection1_menu");
GLADE_HOOKUP_OBJECT (mainwin, remove1, "remove1");
- GLADE_HOOKUP_OBJECT (mainwin, image126, "image126");
+ GLADE_HOOKUP_OBJECT (mainwin, image296, "image296");
GLADE_HOOKUP_OBJECT (mainwin, crop1, "crop1");
GLADE_HOOKUP_OBJECT (mainwin, find1, "find1");
GLADE_HOOKUP_OBJECT (mainwin, separator5, "separator5");
GLADE_HOOKUP_OBJECT (mainwin, preferences, "preferences");
- GLADE_HOOKUP_OBJECT (mainwin, playlist1, "playlist1");
- GLADE_HOOKUP_OBJECT (mainwin, playlist1_menu, "playlist1_menu");
- GLADE_HOOKUP_OBJECT (mainwin, playlist_load, "playlist_load");
- GLADE_HOOKUP_OBJECT (mainwin, playlist_save, "playlist_save");
- GLADE_HOOKUP_OBJECT (mainwin, playlist_save_as, "playlist_save_as");
+ GLADE_HOOKUP_OBJECT (mainwin, view1, "view1");
+ GLADE_HOOKUP_OBJECT (mainwin, view1_menu, "view1_menu");
+ GLADE_HOOKUP_OBJECT (mainwin, view_status_bar, "view_status_bar");
+ GLADE_HOOKUP_OBJECT (mainwin, view_headers, "view_headers");
+ GLADE_HOOKUP_OBJECT (mainwin, playback1, "playback1");
+ GLADE_HOOKUP_OBJECT (mainwin, playback1_menu, "playback1_menu");
GLADE_HOOKUP_OBJECT (mainwin, order1, "order1");
GLADE_HOOKUP_OBJECT (mainwin, order1_menu, "order1_menu");
GLADE_HOOKUP_OBJECT (mainwin, order_linear, "order_linear");
@@ -730,11 +788,13 @@ create_mainwin (void)
GLADE_HOOKUP_OBJECT (mainwin, loop_single, "loop_single");
GLADE_HOOKUP_OBJECT (mainwin, loop_disable, "loop_disable");
GLADE_HOOKUP_OBJECT (mainwin, scroll_follows_playback, "scroll_follows_playback");
+ GLADE_HOOKUP_OBJECT (mainwin, cursor_follows_playback, "cursor_follows_playback");
+ GLADE_HOOKUP_OBJECT (mainwin, stop_after_current, "stop_after_current");
GLADE_HOOKUP_OBJECT (mainwin, menuitem4, "menuitem4");
GLADE_HOOKUP_OBJECT (mainwin, menuitem4_menu, "menuitem4_menu");
GLADE_HOOKUP_OBJECT (mainwin, about1, "about1");
GLADE_HOOKUP_OBJECT (mainwin, help1, "help1");
- GLADE_HOOKUP_OBJECT (mainwin, image127, "image127");
+ GLADE_HOOKUP_OBJECT (mainwin, image297, "image297");
GLADE_HOOKUP_OBJECT (mainwin, hbox2, "hbox2");
GLADE_HOOKUP_OBJECT (mainwin, hbox3, "hbox3");
GLADE_HOOKUP_OBJECT (mainwin, stopbtn, "stopbtn");
@@ -782,7 +842,6 @@ create_searchwin (void)
gtk_widget_set_size_request (searchwin, 600, 150);
gtk_widget_set_events (searchwin, GDK_KEY_PRESS_MASK);
gtk_window_set_title (GTK_WINDOW (searchwin), "Search");
- gtk_window_set_position (GTK_WINDOW (searchwin), GTK_WIN_POS_CENTER_ALWAYS);
gtk_window_set_skip_taskbar_hint (GTK_WINDOW (searchwin), TRUE);
gtk_window_set_skip_pager_hint (GTK_WINDOW (searchwin), TRUE);
@@ -821,7 +880,7 @@ create_searchwin (void)
gtk_widget_show (searchheader);
gtk_box_pack_start (GTK_BOX (vbox5), searchheader, FALSE, TRUE, 0);
gtk_widget_set_size_request (searchheader, -1, 24);
- gtk_widget_set_events (searchheader, GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
+ gtk_widget_set_events (searchheader, GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
searchlist = gtk_drawing_area_new ();
gtk_widget_show (searchlist);
@@ -1152,18 +1211,20 @@ create_prefwin (void)
GtkWidget *prefwin;
GtkWidget *notebook2;
GtkWidget *table3;
- GtkWidget *label4;
- GtkWidget *label5;
- GtkWidget *label6;
- GtkWidget *label8;
- GtkWidget *label9;
GtkWidget *label15;
+ GtkWidget *label9;
+ GtkWidget *label8;
+ GtkWidget *label6;
+ GtkWidget *label5;
+ GtkWidget *label4;
+ GtkWidget *label23;
+ GtkWidget *pref_soundcard;
GtkWidget *pref_alsa_resampling;
GtkWidget *pref_replaygain_scale;
GtkWidget *pref_alsa_freewhenstopped;
- GtkWidget *pref_soundcard;
GtkWidget *pref_src_quality;
GtkWidget *pref_replaygain_mode;
+ GtkWidget *pref_output_plugin;
GtkWidget *Sound;
GtkWidget *table4;
GtkWidget *label7;
@@ -1191,6 +1252,7 @@ create_prefwin (void)
GtkWidget *pref_plugin_author;
GtkWidget *pref_plugin_email;
GtkWidget *pref_plugin_website;
+ GtkWidget *configure_plugin;
GtkWidget *label3;
prefwin = gtk_window_new (GTK_WINDOW_TOPLEVEL);
@@ -1202,81 +1264,88 @@ create_prefwin (void)
gtk_widget_show (notebook2);
gtk_container_add (GTK_CONTAINER (prefwin), notebook2);
- table3 = gtk_table_new (6, 2, FALSE);
+ table3 = gtk_table_new (7, 2, FALSE);
gtk_widget_show (table3);
gtk_container_add (GTK_CONTAINER (notebook2), table3);
gtk_container_set_border_width (GTK_CONTAINER (table3), 3);
gtk_table_set_col_spacings (GTK_TABLE (table3), 3);
- label4 = gtk_label_new ("Output device");
- gtk_widget_show (label4);
- gtk_table_attach (GTK_TABLE (table3), label4, 0, 1, 0, 1,
+ label15 = gtk_label_new ("Release ALSA while stopped");
+ gtk_widget_show (label15);
+ gtk_table_attach (GTK_TABLE (table3), label15, 0, 1, 6, 7,
(GtkAttachOptions) (GTK_FILL),
(GtkAttachOptions) (0), 0, 0);
- gtk_misc_set_alignment (GTK_MISC (label4), 0, 0.5);
+ gtk_misc_set_alignment (GTK_MISC (label15), 0, 0.5);
- label5 = gtk_label_new ("Software ALSA resampling");
- gtk_widget_show (label5);
- gtk_table_attach (GTK_TABLE (table3), label5, 0, 1, 1, 2,
+ label9 = gtk_label_new ("Replaygain peak scale");
+ gtk_widget_show (label9);
+ gtk_table_attach (GTK_TABLE (table3), label9, 0, 1, 5, 6,
(GtkAttachOptions) (GTK_FILL),
(GtkAttachOptions) (0), 0, 0);
- gtk_misc_set_alignment (GTK_MISC (label5), 0, 0.5);
+ gtk_misc_set_alignment (GTK_MISC (label9), 0, 0.5);
+
+ label8 = gtk_label_new ("Replaygain mode");
+ gtk_widget_show (label8);
+ gtk_table_attach (GTK_TABLE (table3), label8, 0, 1, 4, 5,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label8), 0, 0.5);
label6 = gtk_label_new ("SRC quality (libsamplerate)");
gtk_widget_show (label6);
- gtk_table_attach (GTK_TABLE (table3), label6, 0, 1, 2, 3,
+ gtk_table_attach (GTK_TABLE (table3), label6, 0, 1, 3, 4,
(GtkAttachOptions) (GTK_FILL),
(GtkAttachOptions) (0), 0, 0);
gtk_misc_set_alignment (GTK_MISC (label6), 0, 0.5);
- label8 = gtk_label_new ("Replaygain mode");
- gtk_widget_show (label8);
- gtk_table_attach (GTK_TABLE (table3), label8, 0, 1, 3, 4,
+ label5 = gtk_label_new ("Software ALSA resampling");
+ gtk_widget_show (label5);
+ gtk_table_attach (GTK_TABLE (table3), label5, 0, 1, 2, 3,
(GtkAttachOptions) (GTK_FILL),
(GtkAttachOptions) (0), 0, 0);
- gtk_misc_set_alignment (GTK_MISC (label8), 0, 0.5);
+ gtk_misc_set_alignment (GTK_MISC (label5), 0, 0.5);
- label9 = gtk_label_new ("Replaygain peak scale");
- gtk_widget_show (label9);
- gtk_table_attach (GTK_TABLE (table3), label9, 0, 1, 4, 5,
+ label4 = gtk_label_new ("Output device");
+ gtk_widget_show (label4);
+ gtk_table_attach (GTK_TABLE (table3), label4, 0, 1, 1, 2,
(GtkAttachOptions) (GTK_FILL),
(GtkAttachOptions) (0), 0, 0);
- gtk_misc_set_alignment (GTK_MISC (label9), 0, 0.5);
+ gtk_misc_set_alignment (GTK_MISC (label4), 0, 0.5);
- label15 = gtk_label_new ("Release ALSA while stopped");
- gtk_widget_show (label15);
- gtk_table_attach (GTK_TABLE (table3), label15, 0, 1, 5, 6,
+ label23 = gtk_label_new ("Output plugin");
+ gtk_widget_show (label23);
+ gtk_table_attach (GTK_TABLE (table3), label23, 0, 1, 0, 1,
(GtkAttachOptions) (GTK_FILL),
(GtkAttachOptions) (0), 0, 0);
- gtk_misc_set_alignment (GTK_MISC (label15), 0, 0.5);
+ gtk_misc_set_alignment (GTK_MISC (label23), 0, 0.5);
+
+ pref_soundcard = gtk_combo_box_new_text ();
+ gtk_widget_show (pref_soundcard);
+ gtk_table_attach (GTK_TABLE (table3), pref_soundcard, 1, 2, 1, 2,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (GTK_FILL), 0, 0);
pref_alsa_resampling = gtk_check_button_new_with_mnemonic ("");
gtk_widget_show (pref_alsa_resampling);
- gtk_table_attach (GTK_TABLE (table3), pref_alsa_resampling, 1, 2, 1, 2,
+ gtk_table_attach (GTK_TABLE (table3), pref_alsa_resampling, 1, 2, 2, 3,
(GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
(GtkAttachOptions) (0), 0, 0);
pref_replaygain_scale = gtk_check_button_new_with_mnemonic ("");
gtk_widget_show (pref_replaygain_scale);
- gtk_table_attach (GTK_TABLE (table3), pref_replaygain_scale, 1, 2, 4, 5,
+ gtk_table_attach (GTK_TABLE (table3), pref_replaygain_scale, 1, 2, 5, 6,
(GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
(GtkAttachOptions) (0), 0, 0);
pref_alsa_freewhenstopped = gtk_check_button_new_with_mnemonic ("");
gtk_widget_show (pref_alsa_freewhenstopped);
- gtk_table_attach (GTK_TABLE (table3), pref_alsa_freewhenstopped, 1, 2, 5, 6,
+ gtk_table_attach (GTK_TABLE (table3), pref_alsa_freewhenstopped, 1, 2, 6, 7,
(GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
(GtkAttachOptions) (0), 0, 0);
- pref_soundcard = gtk_combo_box_new_text ();
- gtk_widget_show (pref_soundcard);
- gtk_table_attach (GTK_TABLE (table3), pref_soundcard, 1, 2, 0, 1,
- (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
- (GtkAttachOptions) (GTK_FILL), 0, 0);
-
pref_src_quality = gtk_combo_box_new_text ();
gtk_widget_show (pref_src_quality);
- gtk_table_attach (GTK_TABLE (table3), pref_src_quality, 1, 2, 2, 3,
+ gtk_table_attach (GTK_TABLE (table3), pref_src_quality, 1, 2, 3, 4,
(GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
(GtkAttachOptions) (GTK_FILL), 0, 0);
gtk_combo_box_append_text (GTK_COMBO_BOX (pref_src_quality), "sinc_best_quality");
@@ -1287,13 +1356,19 @@ create_prefwin (void)
pref_replaygain_mode = gtk_combo_box_new_text ();
gtk_widget_show (pref_replaygain_mode);
- gtk_table_attach (GTK_TABLE (table3), pref_replaygain_mode, 1, 2, 3, 4,
+ gtk_table_attach (GTK_TABLE (table3), pref_replaygain_mode, 1, 2, 4, 5,
(GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
(GtkAttachOptions) (GTK_FILL), 0, 0);
gtk_combo_box_append_text (GTK_COMBO_BOX (pref_replaygain_mode), "Disable");
gtk_combo_box_append_text (GTK_COMBO_BOX (pref_replaygain_mode), "Track");
gtk_combo_box_append_text (GTK_COMBO_BOX (pref_replaygain_mode), "Album");
+ pref_output_plugin = gtk_combo_box_new_text ();
+ gtk_widget_show (pref_output_plugin);
+ gtk_table_attach (GTK_TABLE (table3), pref_output_plugin, 1, 2, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (GTK_FILL), 0, 0);
+
Sound = gtk_label_new ("Sound");
gtk_widget_show (Sound);
gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook2), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook2), 0), Sound);
@@ -1394,22 +1469,26 @@ create_prefwin (void)
hpaned1 = gtk_hpaned_new ();
gtk_widget_show (hpaned1);
gtk_container_add (GTK_CONTAINER (notebook2), hpaned1);
+ gtk_container_set_border_width (GTK_CONTAINER (hpaned1), 3);
scrolledwindow2 = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_show (scrolledwindow2);
gtk_paned_pack1 (GTK_PANED (hpaned1), scrolledwindow2, FALSE, TRUE);
gtk_widget_set_size_request (scrolledwindow2, 280, -1);
+ gtk_container_set_border_width (GTK_CONTAINER (scrolledwindow2), 3);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow2), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow2), GTK_SHADOW_IN);
pref_pluginlist = gtk_tree_view_new ();
gtk_widget_show (pref_pluginlist);
gtk_container_add (GTK_CONTAINER (scrolledwindow2), pref_pluginlist);
+ gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (pref_pluginlist), TRUE);
- table5 = gtk_table_new (4, 2, FALSE);
+ table5 = gtk_table_new (5, 2, FALSE);
gtk_widget_show (table5);
gtk_paned_pack2 (GTK_PANED (hpaned1), table5, TRUE, TRUE);
gtk_widget_set_size_request (table5, 400, -1);
+ gtk_container_set_border_width (GTK_CONTAINER (table5), 3);
label11 = gtk_label_new ("Description");
gtk_widget_show (label11);
@@ -1471,6 +1550,12 @@ create_prefwin (void)
gtk_editable_set_editable (GTK_EDITABLE (pref_plugin_website), FALSE);
gtk_entry_set_invisible_char (GTK_ENTRY (pref_plugin_website), 9679);
+ configure_plugin = gtk_button_new_with_mnemonic ("Configure");
+ gtk_widget_show (configure_plugin);
+ gtk_table_attach (GTK_TABLE (table5), configure_plugin, 0, 2, 4, 5,
+ (GtkAttachOptions) (0),
+ (GtkAttachOptions) (0), 0, 3);
+
label3 = gtk_label_new ("Plugins");
gtk_widget_show (label3);
gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook2), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook2), 3), label3);
@@ -1478,6 +1563,12 @@ create_prefwin (void)
g_signal_connect ((gpointer) prefwin, "key_press_event",
G_CALLBACK (on_prefwin_key_press_event),
NULL);
+ g_signal_connect ((gpointer) prefwin, "delete_event",
+ G_CALLBACK (on_prefwin_delete_event),
+ NULL);
+ g_signal_connect ((gpointer) pref_soundcard, "changed",
+ G_CALLBACK (on_pref_soundcard_changed),
+ NULL);
g_signal_connect ((gpointer) pref_alsa_resampling, "clicked",
G_CALLBACK (on_pref_alsa_resampling_clicked),
NULL);
@@ -1487,15 +1578,15 @@ create_prefwin (void)
g_signal_connect ((gpointer) pref_alsa_freewhenstopped, "clicked",
G_CALLBACK (on_pref_alsa_freewhenstopped_clicked),
NULL);
- g_signal_connect ((gpointer) pref_soundcard, "changed",
- G_CALLBACK (on_pref_soundcard_changed),
- NULL);
g_signal_connect ((gpointer) pref_src_quality, "changed",
G_CALLBACK (on_pref_src_quality_changed),
NULL);
g_signal_connect ((gpointer) pref_replaygain_mode, "changed",
G_CALLBACK (on_pref_replaygain_mode_changed),
NULL);
+ g_signal_connect ((gpointer) pref_output_plugin, "changed",
+ G_CALLBACK (on_pref_output_plugin_changed),
+ NULL);
g_signal_connect ((gpointer) pref_close_send_to_tray, "clicked",
G_CALLBACK (on_pref_close_send_to_tray_clicked),
NULL);
@@ -1514,23 +1605,28 @@ create_prefwin (void)
g_signal_connect ((gpointer) pref_pluginlist, "cursor_changed",
G_CALLBACK (on_pref_pluginlist_cursor_changed),
NULL);
+ g_signal_connect ((gpointer) configure_plugin, "clicked",
+ G_CALLBACK (on_configure_plugin_clicked),
+ NULL);
/* Store pointers to all widgets, for use by lookup_widget(). */
GLADE_HOOKUP_OBJECT_NO_REF (prefwin, prefwin, "prefwin");
GLADE_HOOKUP_OBJECT (prefwin, notebook2, "notebook2");
GLADE_HOOKUP_OBJECT (prefwin, table3, "table3");
- GLADE_HOOKUP_OBJECT (prefwin, label4, "label4");
- GLADE_HOOKUP_OBJECT (prefwin, label5, "label5");
- GLADE_HOOKUP_OBJECT (prefwin, label6, "label6");
- GLADE_HOOKUP_OBJECT (prefwin, label8, "label8");
- GLADE_HOOKUP_OBJECT (prefwin, label9, "label9");
GLADE_HOOKUP_OBJECT (prefwin, label15, "label15");
+ GLADE_HOOKUP_OBJECT (prefwin, label9, "label9");
+ GLADE_HOOKUP_OBJECT (prefwin, label8, "label8");
+ GLADE_HOOKUP_OBJECT (prefwin, label6, "label6");
+ GLADE_HOOKUP_OBJECT (prefwin, label5, "label5");
+ GLADE_HOOKUP_OBJECT (prefwin, label4, "label4");
+ GLADE_HOOKUP_OBJECT (prefwin, label23, "label23");
+ GLADE_HOOKUP_OBJECT (prefwin, pref_soundcard, "pref_soundcard");
GLADE_HOOKUP_OBJECT (prefwin, pref_alsa_resampling, "pref_alsa_resampling");
GLADE_HOOKUP_OBJECT (prefwin, pref_replaygain_scale, "pref_replaygain_scale");
GLADE_HOOKUP_OBJECT (prefwin, pref_alsa_freewhenstopped, "pref_alsa_freewhenstopped");
- GLADE_HOOKUP_OBJECT (prefwin, pref_soundcard, "pref_soundcard");
GLADE_HOOKUP_OBJECT (prefwin, pref_src_quality, "pref_src_quality");
GLADE_HOOKUP_OBJECT (prefwin, pref_replaygain_mode, "pref_replaygain_mode");
+ GLADE_HOOKUP_OBJECT (prefwin, pref_output_plugin, "pref_output_plugin");
GLADE_HOOKUP_OBJECT (prefwin, Sound, "Sound");
GLADE_HOOKUP_OBJECT (prefwin, table4, "table4");
GLADE_HOOKUP_OBJECT (prefwin, label7, "label7");
@@ -1558,6 +1654,7 @@ create_prefwin (void)
GLADE_HOOKUP_OBJECT (prefwin, pref_plugin_author, "pref_plugin_author");
GLADE_HOOKUP_OBJECT (prefwin, pref_plugin_email, "pref_plugin_email");
GLADE_HOOKUP_OBJECT (prefwin, pref_plugin_website, "pref_plugin_website");
+ GLADE_HOOKUP_OBJECT (prefwin, configure_plugin, "configure_plugin");
GLADE_HOOKUP_OBJECT (prefwin, label3, "label3");
return prefwin;
@@ -1728,3 +1825,98 @@ create_addlocation (void)
return addlocation;
}
+GtkWidget*
+create_inputformat (void)
+{
+ GtkWidget *inputformat;
+ GtkWidget *vbox8;
+ GtkWidget *hbox10;
+ GtkWidget *label26;
+ GtkWidget *titleentry;
+ GtkWidget *hbox9;
+ GtkWidget *format;
+ GtkWidget *formatentry;
+ GtkWidget *label25;
+ GtkWidget *hbuttonbox1;
+ GtkWidget *button2;
+ GtkWidget *button3;
+
+ inputformat = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (inputformat), "Column Format");
+ gtk_window_set_position (GTK_WINDOW (inputformat), GTK_WIN_POS_CENTER_ALWAYS);
+ gtk_window_set_modal (GTK_WINDOW (inputformat), TRUE);
+ gtk_window_set_type_hint (GTK_WINDOW (inputformat), GDK_WINDOW_TYPE_HINT_DIALOG);
+
+ vbox8 = gtk_vbox_new (FALSE, 0);
+ gtk_widget_show (vbox8);
+ gtk_container_add (GTK_CONTAINER (inputformat), vbox8);
+
+ hbox10 = gtk_hbox_new (FALSE, 0);
+ gtk_widget_show (hbox10);
+ gtk_box_pack_start (GTK_BOX (vbox8), hbox10, FALSE, FALSE, 0);
+
+ label26 = gtk_label_new ("Title:");
+ gtk_widget_show (label26);
+ gtk_box_pack_start (GTK_BOX (hbox10), label26, FALSE, FALSE, 0);
+
+ titleentry = gtk_entry_new ();
+ gtk_widget_show (titleentry);
+ gtk_box_pack_start (GTK_BOX (hbox10), titleentry, TRUE, TRUE, 0);
+ gtk_entry_set_text (GTK_ENTRY (titleentry), "Custom");
+ gtk_entry_set_invisible_char (GTK_ENTRY (titleentry), 9679);
+
+ hbox9 = gtk_hbox_new (FALSE, 0);
+ gtk_widget_show (hbox9);
+ gtk_box_pack_start (GTK_BOX (vbox8), hbox9, FALSE, FALSE, 0);
+
+ format = gtk_label_new ("Format:");
+ gtk_widget_show (format);
+ gtk_box_pack_start (GTK_BOX (hbox9), format, FALSE, FALSE, 0);
+
+ formatentry = gtk_entry_new ();
+ gtk_widget_show (formatentry);
+ gtk_box_pack_start (GTK_BOX (hbox9), formatentry, TRUE, TRUE, 0);
+ gtk_entry_set_invisible_char (GTK_ENTRY (formatentry), 9679);
+
+ label25 = gtk_label_new ("Format fields:\n%a - artist\n%t - title\n%b - album\n%n - track\n%l - duration");
+ gtk_widget_show (label25);
+ gtk_box_pack_start (GTK_BOX (vbox8), label25, TRUE, TRUE, 0);
+
+ hbuttonbox1 = gtk_hbutton_box_new ();
+ gtk_widget_show (hbuttonbox1);
+ gtk_box_pack_start (GTK_BOX (vbox8), hbuttonbox1, FALSE, FALSE, 0);
+
+ button2 = gtk_button_new_from_stock ("gtk-cancel");
+ gtk_widget_show (button2);
+ gtk_container_add (GTK_CONTAINER (hbuttonbox1), button2);
+ GTK_WIDGET_SET_FLAGS (button2, GTK_CAN_DEFAULT);
+
+ button3 = gtk_button_new_from_stock ("gtk-ok");
+ gtk_widget_show (button3);
+ gtk_container_add (GTK_CONTAINER (hbuttonbox1), button3);
+ GTK_WIDGET_SET_FLAGS (button3, GTK_CAN_DEFAULT);
+
+ g_signal_connect ((gpointer) button2, "clicked",
+ G_CALLBACK (on_format_cancel_clicked),
+ NULL);
+ g_signal_connect ((gpointer) button3, "clicked",
+ G_CALLBACK (on_format_ok_clicked),
+ NULL);
+
+ /* Store pointers to all widgets, for use by lookup_widget(). */
+ GLADE_HOOKUP_OBJECT_NO_REF (inputformat, inputformat, "inputformat");
+ GLADE_HOOKUP_OBJECT (inputformat, vbox8, "vbox8");
+ GLADE_HOOKUP_OBJECT (inputformat, hbox10, "hbox10");
+ GLADE_HOOKUP_OBJECT (inputformat, label26, "label26");
+ GLADE_HOOKUP_OBJECT (inputformat, titleentry, "titleentry");
+ GLADE_HOOKUP_OBJECT (inputformat, hbox9, "hbox9");
+ GLADE_HOOKUP_OBJECT (inputformat, format, "format");
+ GLADE_HOOKUP_OBJECT (inputformat, formatentry, "formatentry");
+ GLADE_HOOKUP_OBJECT (inputformat, label25, "label25");
+ GLADE_HOOKUP_OBJECT (inputformat, hbuttonbox1, "hbuttonbox1");
+ GLADE_HOOKUP_OBJECT (inputformat, button2, "button2");
+ GLADE_HOOKUP_OBJECT (inputformat, button3, "button3");
+
+ return inputformat;
+}
+
diff --git a/interface.h b/plugins/gtkui/interface.h
index f9b470ff..431e5e12 100644
--- a/interface.h
+++ b/plugins/gtkui/interface.h
@@ -10,3 +10,4 @@ GtkWidget* create_helpwindow (void);
GtkWidget* create_prefwin (void);
GtkWidget* create_headermenu (void);
GtkWidget* create_addlocation (void);
+GtkWidget* create_inputformat (void);
diff --git a/plugins/gtkui/parser.c b/plugins/gtkui/parser.c
new file mode 100644
index 00000000..4cbd2da4
--- /dev/null
+++ b/plugins/gtkui/parser.c
@@ -0,0 +1,107 @@
+/*
+ DeaDBeeF - ultimate music player for GNU/Linux systems with X11
+ Copyright (C) 2009 Alexey Yakovenko
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include "parser.h"
+
+// very basic parser, ripped from psynth, optimized, and extended to support
+// quoted strings and extra special chars
+int parser_line;
+
+void
+parser_init (void) {
+ parser_line = 1;
+}
+
+const char *
+skipws (const char *p) {
+ while (*p <= ' ' && *p) {
+ if (*p == '\n') {
+ parser_line++;
+ }
+ p++;
+ }
+ if (!*p) {
+ return NULL;
+ }
+ return p;
+}
+
+const char *
+gettoken (const char *p, char *tok) {
+ const char *c;
+ assert (p);
+ assert (tok);
+ int n = MAX_TOKEN-1;
+ char specialchars[] = "{}();";
+ if (!(p = skipws (p))) {
+ return NULL;
+ }
+ if (*p == '"') {
+ p++;
+ c = p;
+ while (n > 0 && *c && *c != '"') {
+ if (*c == '\n') {
+ parser_line++;
+ }
+ *tok++ = *c++;
+ n--;
+ }
+ if (*c) {
+ c++;
+ }
+ *tok = 0;
+ return c;
+ }
+ if (strchr (specialchars, *p)) {
+ *tok = *p;
+ tok[1] = 0;
+ return p+1;
+ }
+ c = p;
+ while (n > 0 && *c > ' ' && !strchr (specialchars, *c)) {
+ *tok++ = *c++;
+ n--;
+ }
+ *tok = 0;
+ return c;
+}
+
+const char *
+gettoken_warn_eof (const char *p, char *tok) {
+ p = gettoken (p, tok);
+ if (!p) {
+ fprintf (stderr, "parser: unexpected eof at line %d", parser_line);
+ }
+ return p;
+}
+
+const char *
+gettoken_err_eof (const char *p, char *tok) {
+ p = gettoken (p, tok);
+ if (!p) {
+ fprintf (stderr, "parser: unexpected eof at line %d", parser_line);
+ exit (-1);
+ }
+ return p;
+}
+
+
diff --git a/palsa.h b/plugins/gtkui/parser.h
index 2896702a..61955bcf 100644
--- a/palsa.h
+++ b/plugins/gtkui/parser.h
@@ -15,44 +15,25 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef __PALSA_H
-#define __PALSA_H
+#ifndef __PARSER_H
+#define __PARSER_H
-int
-palsa_init (void);
+#define MAX_TOKEN 256
+extern int parser_line;
void
-palsa_free (void);
+parser_init (void);
-int
-palsa_change_rate (int rate);
+const char *
+skipws (const char *p);
-int
-palsa_play (void);
+const char *
+gettoken (const char *p, char *tok);
-int
-palsa_stop (void);
+const char *
+gettoken_warn_eof (const char *p, char *tok);
-int
-palsa_isstopped (void);
-
-int
-palsa_ispaused (void);
-
-int
-palsa_pause (void);
-
-int
-palsa_unpause (void);
-
-int
-palsa_get_rate (void);
-
-void
-palsa_configchanged (void);
-
-void
-palsa_enum_soundcards (void (*callback)(const char *name, const char *desc, void*), void *userdata);
-
-#endif // __PALSA_H
+const char *
+gettoken_err_eof (const char *p, char *tok);
+#endif
diff --git a/progress.c b/plugins/gtkui/progress.c
index 78acf7c4..78acf7c4 100644
--- a/progress.c
+++ b/plugins/gtkui/progress.c
diff --git a/progress.h b/plugins/gtkui/progress.h
index e4de0e8e..e4de0e8e 100644
--- a/progress.h
+++ b/plugins/gtkui/progress.h
diff --git a/search.c b/plugins/gtkui/search.c
index 1ce99edd..55b5ad3f 100644
--- a/search.c
+++ b/plugins/gtkui/search.c
@@ -29,13 +29,24 @@
#include "interface.h"
#include "support.h"
-#include "common.h"
#include "search.h"
#include "gtkplaylist.h"
-#include "messagepump.h"
-#include "utf8.h"
#include "deadbeef.h"
+#define min(x,y) ((x)<(y)?(x):(y))
+#define max(x,y) ((x)>(y)?(x):(y))
+
+#define PL_HEAD(iter) (deadbeef->pl_get_first(iter))
+#define PL_TAIL(iter) (deadbeef->pl_get_last(iter))
+#define PL_NEXT(it, iter) (deadbeef->pl_get_next(it, iter))
+#define PL_PREV(it, iter) (deadbeef->pl_get_prev(it, iter))
+#define SELECTED(it) (deadbeef->pl_is_selected(it))
+#define SELECT(it, sel) (deadbeef->pl_set_selected(it,sel))
+
+extern DB_functions_t *deadbeef; // defined in gtkui.c
+//#define trace(...) { fprintf(stderr, __VA_ARGS__); }
+#define trace(fmt,...)
+
extern GtkWidget *searchwin;
struct playItem_s *search_current = NULL;
int search_count = 0;
@@ -59,30 +70,7 @@ on_searchentry_changed (GtkEditable *editable,
// with search_head
const gchar *text = gtk_entry_get_text (GTK_ENTRY (editable));
-
- playlist_head[PL_SEARCH] = NULL;
- playlist_tail[PL_SEARCH] = NULL;
- search_count = 0;
- if (*text) {
- for (playItem_t *it = playlist_head[PL_MAIN]; it; it = it->next[PL_MAIN]) {
- for (metaInfo_t *m = it->meta; m; m = m->next) {
-// if (strcasestr (m->value, text)) {
- if (utfcasestr (m->value, text)) {
- // add to list
- it->next[PL_SEARCH] = NULL;
- if (playlist_tail[PL_SEARCH]) {
- playlist_tail[PL_SEARCH]->next[PL_SEARCH] = it;
- playlist_tail[PL_SEARCH] = it;
- }
- else {
- playlist_head[PL_SEARCH] = playlist_tail[PL_SEARCH] = it;
- }
- search_count++;
- break;
- }
- }
- }
- }
+ search_count = deadbeef->pl_process_search (text);
extern gtkplaylist_t search_playlist;
gtkplaylist_t *ps = &search_playlist;
@@ -90,12 +78,20 @@ on_searchentry_changed (GtkEditable *editable,
//memset (ps->fmtcache, 0, sizeof (int16_t) * 3 * pl_ncolumns * ps->nvisiblerows);
gtkpl_draw_playlist (ps, 0, 0, ps->playlist->allocation.width, ps->playlist->allocation.height);
gtkpl_expose (ps, 0, 0, ps->playlist->allocation.width, ps->playlist->allocation.height);
+
+ // redraw main playlist to be in sync selection-wise
+ ps = &main_playlist;
+ gtkpl_draw_playlist (ps, 0, 0, ps->playlist->allocation.width, ps->playlist->allocation.height);
+ gtkpl_expose (ps, 0, 0, ps->playlist->allocation.width, ps->playlist->allocation.height);
}
void
search_refresh (void) {
- if (searchwin) {
- on_searchentry_changed (GTK_EDITABLE (lookup_widget (searchwin, "searchentry")), NULL);
+ if (searchwin && GTK_WIDGET_VISIBLE (searchwin)) {
+ gtkplaylist_t *ps = &search_playlist;
+ gtkpl_draw_playlist (ps, 0, 0, ps->playlist->allocation.width, ps->playlist->allocation.height);
+ gtkpl_expose (ps, 0, 0, ps->playlist->allocation.width, ps->playlist->allocation.height);
+ //on_searchentry_changed (GTK_EDITABLE (lookup_widget (searchwin, "searchentry")), NULL);
}
}
@@ -165,12 +161,16 @@ on_searchwin_key_press_event (GtkWidget *widget,
extern gtkplaylist_t search_playlist;
gtkplaylist_t *ps = &search_playlist;
if (search_count > 0) {
- playItem_t *it = gtkpl_get_for_idx (ps, max (ps->row, 0));
+ int row = deadbeef->pl_get_cursor (ps->iterator);
+ DB_playItem_t *it = deadbeef->pl_get_for_idx_and_iter (max (row, 0), ps->iterator);
if (it) {
- messagepump_push (M_PLAYSONGNUM, 0, pl_get_idx_of (it), 0);
+ deadbeef->sendmessage (M_PLAYSONGNUM, 0, deadbeef->pl_get_idx_of (it), 0);
}
}
}
+ else {
+ gtkpl_keypress (&search_playlist, event->keyval, event->state);
+ }
return FALSE;
}
diff --git a/search.h b/plugins/gtkui/search.h
index 32d45e07..32d45e07 100644
--- a/search.h
+++ b/plugins/gtkui/search.h
diff --git a/support.c b/plugins/gtkui/support.c
index 7dc3c78c..7dc3c78c 100644
--- a/support.c
+++ b/plugins/gtkui/support.c
diff --git a/support.h b/plugins/gtkui/support.h
index 2dea079c..2dea079c 100644
--- a/support.h
+++ b/plugins/gtkui/support.h
diff --git a/plugins/hotkeys/hotkeys.c b/plugins/hotkeys/hotkeys.c
index 415e055c..7d292f10 100644
--- a/plugins/hotkeys/hotkeys.c
+++ b/plugins/hotkeys/hotkeys.c
@@ -33,11 +33,11 @@ static intptr_t loop_tid;
#define MAX_COMMAND_COUNT 256
typedef struct {
- char *name;
+ const char *name;
KeySym keysym;
} xkey_t;
-#define KEY( kname, kcode ) { .name=kname, .keysym=kcode },
+#define KEY(kname, kcode) { .name=kname, .keysym=kcode },
static const xkey_t keys[] = {
#include "keysyms.inc"
@@ -61,27 +61,27 @@ typedef struct {
} known_command_t;
static int
-get_keycode( Display *disp, const char* name ) {
+get_keycode (Display *disp, const char* name) {
static int first_kk, last_kk;
static KeySym* syms;
static int ks_per_kk;
static int first_time = 1;
- int i, j, ks;
+ int i, ks;
- if ( first_time )
+ if (first_time)
{
- XDisplayKeycodes( disp, &first_kk, &last_kk );
+ XDisplayKeycodes (disp, &first_kk, &last_kk);
- syms = XGetKeyboardMapping( disp, first_kk, last_kk - first_kk, &ks_per_kk );
+ syms = XGetKeyboardMapping (disp, first_kk, last_kk - first_kk, &ks_per_kk);
first_time = 0;
}
- for ( i = 0; i < last_kk-first_kk; i++ )
+ for (i = 0; i < last_kk-first_kk; i++)
{
- KeySym sym = *(syms + i*ks_per_kk);
- for ( ks = 0; ks < sizeof( keys ) / sizeof( xkey_t ); ks++ )
+ KeySym sym = * (syms + i*ks_per_kk);
+ for (ks = 0; ks < sizeof (keys) / sizeof (xkey_t); ks++)
{
- if ( (keys[ ks ].keysym == sym) && ( 0 == strcmp(name, keys[ ks ].name) ) )
+ if ( (keys[ ks ].keysym == sym) && (0 == strcmp (name, keys[ ks ].name)))
{
return i+first_kk;
}
@@ -91,81 +91,92 @@ get_keycode( Display *disp, const char* name ) {
}
static char*
-trim(char* s)
+trim (char* s)
{
char *h, *t;
- for ( h = s; *h == ' ' || *h == '\t'; h++ );
- for ( t = s + strlen(s); *t == ' ' || *t == '\t'; t-- );
- *(t+1) = 0;
+ for (h = s; *h == ' ' || *h == '\t'; h++);
+ for (t = s + strlen (s); *t == ' ' || *t == '\t'; t--);
+ * (t+1) = 0;
return h;
}
static void
-cmd_seek_fwd() {
- deadbeef->playback_set_pos( deadbeef->playback_get_pos() + 5 );
+cmd_seek_fwd () {
+ deadbeef->playback_set_pos (deadbeef->playback_get_pos () + 5);
}
static void
-cmd_seek_back() {
- deadbeef->playback_set_pos( deadbeef->playback_get_pos() - 5 );
+cmd_seek_back () {
+ deadbeef->playback_set_pos (deadbeef->playback_get_pos () - 5);
}
static void
-cmd_volume_up() {
- deadbeef->volume_set_db( deadbeef->volume_get_db() + 2 );
+cmd_volume_up () {
+ deadbeef->volume_set_db (deadbeef->volume_get_db () + 2);
}
static void
-cmd_volume_down() {
- deadbeef->volume_set_db( deadbeef->volume_get_db() - 2 );
+cmd_volume_down () {
+ deadbeef->volume_set_db (deadbeef->volume_get_db () - 2);
+}
+
+static void
+cmd_stop_after_current () {
+ int var = deadbeef->conf_get_int ("playlist.stop_after_current", 0);
+ var = 1 - var;
+ deadbeef->conf_set_int ("playlist.stop_after_current", var);
+ deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0);
}
static command_func_t
-get_command( const char* command )
+get_command (const char* command)
{
- if ( 0 == strcasecmp( command, "toggle_pause" ) )
+ if (!strcasecmp (command, "toggle_pause"))
return deadbeef->playback_pause;
- if ( 0 == strcasecmp( command, "play" ) )
+ else if (!strcasecmp (command, "play"))
return deadbeef->playback_play;
- if ( 0 == strcasecmp( command, "prev" ) )
+ else if (!strcasecmp (command, "prev"))
return deadbeef->playback_prev;
- if ( 0 == strcasecmp( command, "next" ) )
+ else if (!strcasecmp (command, "next"))
return deadbeef->playback_next;
- if ( 0 == strcasecmp( command, "stop" ) )
+ else if (!strcasecmp (command, "stop"))
return deadbeef->playback_stop;
- if ( 0 == strcasecmp( command, "play_random" ) )
+ else if (!strcasecmp (command, "play_random"))
return deadbeef->playback_random;
- if ( 0 == strcasecmp( command, "seek_fwd" ) )
+ else if (!strcasecmp (command, "seek_fwd"))
return cmd_seek_fwd;
- if ( 0 == strcasecmp( command, "seek_back" ) )
+ else if (!strcasecmp (command, "seek_back"))
return cmd_seek_back;
- if ( 0 == strcasecmp( command, "volume_up" ) )
+ else if (!strcasecmp (command, "volume_up"))
return cmd_volume_up;
- if ( 0 == strcasecmp( command, "volume_down" ) )
+ else if (!strcasecmp (command, "volume_down"))
return cmd_volume_down;
+ else if (!strcasecmp (command, "toggle_stop_after_current"))
+ return cmd_stop_after_current;
+
return NULL;
}
static int
-read_config( Display *disp )
+read_config (Display *disp)
{
DB_conf_item_t *item = deadbeef->conf_find ("hotkeys.", NULL);
while (item) {
// fprintf (stderr, "hotkeys: adding %s %s\n", item->key, item->value);
- if ( command_count == MAX_COMMAND_COUNT )
+ if (command_count == MAX_COMMAND_COUNT)
{
- fprintf( stderr, "hotkeys: maximum number (%d) of commands exceeded\n", MAX_COMMAND_COUNT );
+ fprintf (stderr, "hotkeys: maximum number (%d) of commands exceeded\n", MAX_COMMAND_COUNT);
break;
}
@@ -177,16 +188,15 @@ read_config( Display *disp )
char param[l+1];
memcpy (param, item->value, l+1);
- char* colon = strchr( param, ':' );
- if ( !colon )
+ char* colon = strchr (param, ':');
+ if (!colon)
{
- fprintf( stderr, "hotkeys: bad config option %s %s\n", item->key, item->value);
+ fprintf (stderr, "hotkeys: bad config option %s %s\n", item->key, item->value);
continue;
}
char* command = colon+1;
*colon = 0;
- int modifier = 0;
char* key = NULL;
int done = 0;
@@ -194,22 +204,22 @@ read_config( Display *disp )
char* space = param - 1;
do {
p = space+1;
- space = strchr( p, ' ' );
- if ( space )
+ space = strchr (p, ' ');
+ if (space)
*space = 0;
else
done = 1;
- if ( 0 == strcasecmp( p, "Ctrl" ) )
+ if (0 == strcasecmp (p, "Ctrl"))
cmd_entry->modifier |= ControlMask;
- else if ( 0 == strcasecmp( p, "Alt" ) )
+ else if (0 == strcasecmp (p, "Alt"))
cmd_entry->modifier |= Mod1Mask;
- else if ( 0 == strcasecmp( p, "Shift" ) )
+ else if (0 == strcasecmp (p, "Shift"))
cmd_entry->modifier |= ShiftMask;
- else if ( 0 == strcasecmp( p, "Super" ) ) {
+ else if (0 == strcasecmp (p, "Super")) {
cmd_entry->modifier |= Mod2Mask;
}
@@ -223,26 +233,26 @@ read_config( Display *disp )
}
else {
// lookup name table
- cmd_entry->keycode = get_keycode( disp, p );
+ cmd_entry->keycode = get_keycode (disp, p);
}
- if ( !cmd_entry->keycode )
+ if (!cmd_entry->keycode)
{
- fprintf( stderr, "hotkeys: Unknown key: <%s> while parsing %s %s\n", key, item->key, item->value );
+ fprintf (stderr, "hotkeys: Unknown key: <%s> while parsing %s %s\n", key, item->key, item->value);
break;
}
}
- } while ( !done );
+ } while (!done);
if (done) {
- if ( cmd_entry->keycode == 0 ) {
- fprintf( stderr, "hotkeys: Key not found while parsing %s %s\n", item->key, item->value);
+ if (cmd_entry->keycode == 0) {
+ fprintf (stderr, "hotkeys: Key not found while parsing %s %s\n", item->key, item->value);
}
else {
command = trim (command);
- cmd_entry->func = get_command( command );
- if ( !cmd_entry->func )
+ cmd_entry->func = get_command (command);
+ if (!cmd_entry->func)
{
- fprintf( stderr, "hotkeys: Unknown command <%s> while parsing %s %s\n", command, item->key, item->value);
+ fprintf (stderr, "hotkeys: Unknown command <%s> while parsing %s %s\n", command, item->key, item->value);
}
else {
command_count++;
@@ -260,56 +270,56 @@ hotkeys_load (DB_functions_t *api) {
}
static void
-cleanup() {
+cleanup () {
command_count = 0;
- XCloseDisplay( disp );
+ XCloseDisplay (disp);
}
static void
-hotkeys_event_loop( uintptr_t unused ) {
+hotkeys_event_loop (void *unused) {
int i;
while (!finished) {
XEvent event;
- while ( XPending( disp ) )
+ while (XPending (disp))
{
- XNextEvent( disp, &event );
+ XNextEvent (disp, &event);
- if ( event.xkey.type == KeyPress )
+ if (event.xkey.type == KeyPress)
{
- for ( i = 0; i < command_count; i++ )
+ for (i = 0; i < command_count; i++)
if ( (event.xkey.keycode == commands[ i ].keycode) &&
- ((event.xkey.state & commands[ i ].modifier) == commands[ i ].modifier) )
+ ( (event.xkey.state & commands[ i ].modifier) == commands[ i ].modifier))
{
- commands[i].func();
+ commands[i].func ();
break;
}
}
}
- usleep( 200 * 1000 );
+ usleep (200 * 1000);
}
}
static int
x_err_handler (Display *d, XErrorEvent *evt) {
char buffer[1024];
- XGetErrorText(d, evt->error_code, buffer, sizeof (buffer));
- fprintf( stderr, "hotkeys: xlib error: %s\n", buffer);
+ XGetErrorText (d, evt->error_code, buffer, sizeof (buffer));
+ fprintf (stderr, "hotkeys: xlib error: %s\n", buffer);
}
static int
hotkeys_start (void) {
finished = 0;
loop_tid = 0;
- disp = XOpenDisplay( NULL );
- if ( !disp )
+ disp = XOpenDisplay (NULL);
+ if (!disp)
{
- fprintf( stderr, "hotkeys: could not open display\n" );
+ fprintf (stderr, "hotkeys: could not open display\n");
return -1;
}
- XSetErrorHandler( x_err_handler );
+ XSetErrorHandler (x_err_handler);
- read_config( disp );
+ read_config (disp);
int i;
// need to grab it here to prevent gdk_x_error from being called while we're
// doing it on other thread
@@ -318,10 +328,10 @@ hotkeys_start (void) {
}
XSync (disp, 0);
if (command_count > 0) {
- loop_tid = deadbeef->thread_start( hotkeys_event_loop, 0 );
+ loop_tid = deadbeef->thread_start (hotkeys_event_loop, 0);
}
else {
- cleanup();
+ cleanup ();
}
}
@@ -329,8 +339,8 @@ static int
hotkeys_stop (void) {
if (loop_tid) {
finished = 1;
- deadbeef->thread_join( loop_tid );
- cleanup();
+ deadbeef->thread_join (loop_tid);
+ cleanup ();
}
}
diff --git a/plugins/lastfm/lastfm.c b/plugins/lastfm/lastfm.c
index 776e2bbe..9864ed02 100644
--- a/plugins/lastfm/lastfm.c
+++ b/plugins/lastfm/lastfm.c
@@ -34,7 +34,8 @@ static DB_misc_t plugin;
static DB_functions_t *deadbeef;
#define LFM_CLIENTID "ddb"
-#define SCROBBLER_URL_V1 "http://post.audioscrobbler.com"
+#define SCROBBLER_URL_LFM "http://post.audioscrobbler.com"
+#define SCROBBLER_URL_LIBRE "http://turtle.libre.fm"
static char lfm_user[100];
static char lfm_pass[100];
@@ -47,7 +48,6 @@ static uintptr_t lfm_mutex;
static uintptr_t lfm_cond;
static int lfm_stopthread;
static intptr_t lfm_tid;
-static int lfm_abort;
DB_plugin_t *
lastfm_load (DB_functions_t *api) {
@@ -65,9 +65,24 @@ static char lfm_nowplaying[2048]; // packet for nowplaying, or ""
//static char lfm_subm_queue[LFM_SUBMISSION_QUEUE_SIZE][2048];
static DB_playItem_t *lfm_subm_queue[LFM_SUBMISSION_QUEUE_SIZE];
+static void
+lfm_update_auth (void) {
+ const char *user = deadbeef->conf_get_str ("lastfm.login", "");
+ const char *pass = deadbeef->conf_get_str ("lastfm.password", "");
+ if (strcmp (user, lfm_user) || strcmp (pass, lfm_pass)) {
+ strcpy (lfm_user, user);
+ strcpy (lfm_pass, pass);
+ lfm_sess[0] = 0;
+ }
+}
+
static size_t
lastfm_curl_res (void *ptr, size_t size, size_t nmemb, void *stream)
{
+ if (lfm_stopthread) {
+ trace ("lfm: lastfm_curl_res: aborting current request\n");
+ return 0;
+ }
int len = size * nmemb;
if (lfm_reply_sz + len >= MAX_REPLY) {
trace ("reply is too large. stopping.\n");
@@ -85,7 +100,7 @@ lastfm_curl_res (void *ptr, size_t size, size_t nmemb, void *stream)
static int
lfm_curl_control (void *stream, double dltotal, double dlnow, double ultotal, double ulnow) {
trace ("lfm_curl_control\n");
- if (lfm_abort) {
+ if (lfm_stopthread) {
trace ("lfm: aborting current request\n");
return -1;
}
@@ -100,7 +115,6 @@ curl_req_send (const char *req, const char *post) {
trace ("lastfm: failed to init curl\n");
return -1;
}
- curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
curl_easy_setopt(curl, CURLOPT_URL, req);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, lastfm_curl_res);
memset(lfm_err, 0, sizeof(lfm_err));
@@ -162,6 +176,7 @@ curl_req_cleanup (void) {
static int
auth (void) {
+ lfm_update_auth ();
if (lfm_sess[0]) {
return 0;
}
@@ -176,10 +191,11 @@ auth (void) {
deadbeef->md5 (sig, token, strlen (token));
deadbeef->md5_to_str (token, sig);
+ const char *scrobbler_url = deadbeef->conf_get_str ("lastfm.scrobbler_url", SCROBBLER_URL_LFM);
#if LFM_TESTMODE
- snprintf (req, sizeof (req), "%s/?hs=true&p=1.2.1&c=tst&v=1.0&u=%s&t=%d&a=%s", SCROBBLER_URL_V1, lfm_user, timestamp, token);
+ snprintf (req, sizeof (req), "%s/?hs=true&p=1.2.1&c=tst&v=1.0&u=%s&t=%d&a=%s", scrobbler_url, lfm_user, timestamp, token);
#else
- snprintf (req, sizeof (req), "%s/?hs=true&p=1.2.1&c=%s&v=%d.%d&u=%s&t=%d&a=%s", SCROBBLER_URL_V1, LFM_CLIENTID, plugin.plugin.version_major, plugin.plugin.version_minor, lfm_user, timestamp, token);
+ snprintf (req, sizeof (req), "%s/?hs=true&p=1.2.1&c=%s&v=%d.%d&u=%s&t=%d&a=%s", scrobbler_url, LFM_CLIENTID, plugin.plugin.version_major, plugin.plugin.version_minor, lfm_user, timestamp, token);
#endif
// handshake
int status = curl_req_send (req, NULL);
@@ -445,9 +461,9 @@ lfm_format_uri (int subm, DB_playItem_t *song, char *out, int outl) {
}
static int
-lastfm_songstarted (DB_event_song_t *ev, uintptr_t data) {
+lastfm_songstarted (DB_event_track_t *ev, uintptr_t data) {
deadbeef->mutex_lock (lfm_mutex);
- if (lfm_format_uri (-1, ev->song, lfm_nowplaying, sizeof (lfm_nowplaying)) < 0) {
+ if (lfm_format_uri (-1, ev->track, lfm_nowplaying, sizeof (lfm_nowplaying)) < 0) {
lfm_nowplaying[0] = 0;
}
// trace ("%s\n", lfm_nowplaying);
@@ -460,28 +476,28 @@ lastfm_songstarted (DB_event_song_t *ev, uintptr_t data) {
}
static int
-lastfm_songfinished (DB_event_song_t *ev, uintptr_t data) {
+lastfm_songfinished (DB_event_track_t *ev, uintptr_t data) {
trace ("lfm songfinished\n");
#if !LFM_IGNORE_RULES
// check submission rules
// duration must be >= 30 sec
- if (deadbeef->pl_get_item_duration (ev->song) < 30) {
- trace ("song duration is %f seconds. not eligible for submission\n", ev->song->duration);
+ if (deadbeef->pl_get_item_duration (ev->track) < 30) {
+ trace ("track duration is %f seconds. not eligible for submission\n", deadbeef->pl_get_item_duration (ev->track));
return 0;
}
// must be played for >=240sec of half the total time
- if (ev->song->playtime < 240 && ev->song->playtime < deadbeef->pl_get_item_duration (ev->song)/2) {
- trace ("song playtime=%f seconds. not eligible for submission\n", ev->song->playtime);
+ if (ev->track->playtime < 240 && ev->track->playtime < deadbeef->pl_get_item_duration (ev->track)/2) {
+ trace ("track playtime=%f seconds. not eligible for submission\n", ev->track->playtime);
return 0;
}
#endif
- if (!deadbeef->pl_find_meta (ev->song, "artist")
- || !deadbeef->pl_find_meta (ev->song, "title")
-// || !deadbeef->pl_find_meta (ev->song, "album")
+ if (!deadbeef->pl_find_meta (ev->track, "artist")
+ || !deadbeef->pl_find_meta (ev->track, "title")
+// || !deadbeef->pl_find_meta (ev->track, "album")
) {
- trace ("lfm: not enough metadata for submission, artist=%s, title=%s, album=%s\n", deadbeef->pl_find_meta (ev->song, "artist"), deadbeef->pl_find_meta (ev->song, "title"), deadbeef->pl_find_meta (ev->song, "album"));
+ trace ("lfm: not enough metadata for submission, artist=%s, title=%s, album=%s\n", deadbeef->pl_find_meta (ev->track, "artist"), deadbeef->pl_find_meta (ev->track, "title"), deadbeef->pl_find_meta (ev->track, "album"));
return 0;
}
deadbeef->mutex_lock (lfm_mutex);
@@ -490,7 +506,7 @@ lastfm_songfinished (DB_event_song_t *ev, uintptr_t data) {
if (!lfm_subm_queue[i]) {
trace ("lfm: song is now in queue for submission\n");
lfm_subm_queue[i] = deadbeef->pl_item_alloc ();
- deadbeef->pl_item_copy (lfm_subm_queue[i], ev->song);
+ deadbeef->pl_item_copy (lfm_subm_queue[i], ev->track);
break;
}
}
@@ -512,6 +528,7 @@ lfm_send_nowplaying (void) {
snprintf (s, sizeof (s), "s=%s&", lfm_sess);
int l = strlen (lfm_nowplaying);
strcpy (lfm_nowplaying+l, s);
+ trace ("content:\n%s\n", lfm_nowplaying);
#if !LFM_NOSEND
for (int attempts = 2; attempts > 0; attempts--) {
int status = curl_req_send (lfm_nowplaying_url, lfm_nowplaying);
@@ -625,24 +642,29 @@ lfm_send_submissions (void) {
}
static void
-lfm_thread (uintptr_t ctx) {
+lfm_thread (void *ctx) {
//trace ("lfm_thread started\n");
for (;;) {
- deadbeef->cond_wait (lfm_cond, lfm_mutex);
- trace ("cond signalled!\n");
if (lfm_stopthread) {
deadbeef->mutex_unlock (lfm_mutex);
- deadbeef->cond_signal (lfm_cond);
trace ("lfm_thread end\n");
return;
}
+ trace ("lfm wating for cond...\n");
+ deadbeef->cond_wait (lfm_cond, lfm_mutex);
+ if (lfm_stopthread) {
+ deadbeef->mutex_unlock (lfm_mutex);
+ trace ("lfm_thread end[2]\n");
+ return;
+ }
+ trace ("cond signalled!\n");
deadbeef->mutex_unlock (lfm_mutex);
+ trace ("lfm sending nowplaying...\n");
// try to send nowplaying
if (lfm_nowplaying[0]) {
lfm_send_nowplaying ();
}
-
lfm_send_submissions ();
}
}
@@ -738,20 +760,14 @@ auth_v2 (void) {
static int
lastfm_start (void) {
- // load login/pass
+ lfm_sess[0] = 0;
lfm_mutex = 0;
lfm_cond = 9;
lfm_tid = 0;
- lfm_abort = 0;
- strcpy (lfm_user, deadbeef->conf_get_str ("lastfm.login", ""));
- strcpy (lfm_pass, deadbeef->conf_get_str ("lastfm.password", ""));
- if (!lfm_user[0] || !lfm_pass[0]) {
- return 0;
- }
lfm_stopthread = 0;
lfm_mutex = deadbeef->mutex_create ();
lfm_cond = deadbeef->cond_create ();
- lfm_tid = deadbeef->thread_start (lfm_thread, 0);
+ lfm_tid = deadbeef->thread_start (lfm_thread, NULL);
// subscribe to frameupdate event
deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_SONGSTARTED, DB_CALLBACK (lastfm_songstarted), 0);
deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_SONGFINISHED, DB_CALLBACK (lastfm_songfinished), 0);
@@ -763,11 +779,13 @@ static int
lastfm_stop (void) {
trace ("lastfm_stop\n");
if (lfm_mutex) {
- lfm_abort = 1;
+ lfm_stopthread = 1;
deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_SONGSTARTED, DB_CALLBACK (lastfm_songstarted), 0);
deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_SONGFINISHED, DB_CALLBACK (lastfm_songfinished), 0);
- lfm_stopthread = 1;
+
+ trace ("lfm_stop signalling cond\n");
deadbeef->cond_signal (lfm_cond);
+ trace ("waiting for thread to finish\n");
deadbeef->thread_join (lfm_tid);
lfm_tid = 0;
deadbeef->cond_free (lfm_cond);
@@ -776,6 +794,12 @@ lastfm_stop (void) {
return 0;
}
+static const char settings_dlg[] =
+ "property Username entry lastfm.login \"\";\n"
+ "property Password password lastfm.password \"\";"
+ "property \"Scrobble URL\" entry lastfm.scrobbler_url \""SCROBBLER_URL_LFM"\";"
+;
+
// define plugin interface
static DB_misc_t plugin = {
DB_PLUGIN_SET_API_VERSION
@@ -788,5 +812,6 @@ static DB_misc_t plugin = {
.plugin.email = "waker@users.sourceforge.net",
.plugin.website = "http://deadbeef.sf.net",
.plugin.start = lastfm_start,
- .plugin.stop = lastfm_stop
+ .plugin.stop = lastfm_stop,
+ .plugin.configdialog = settings_dlg
};
diff --git a/plugins/mpgmad/mpgmad.c b/plugins/mpgmad/mpgmad.c
index 567c8405..abee585b 100644
--- a/plugins/mpgmad/mpgmad.c
+++ b/plugins/mpgmad/mpgmad.c
@@ -792,7 +792,7 @@ cmp3_stream_frame (void) {
// synthesize single frame
mad_synth_frame(&synth,&frame);
buffer.decode_remaining = synth.pcm.length;
- deadbeef->playback_update_bitrate (frame.header.bitrate/1000);
+ deadbeef->streamer_set_bitrate (frame.header.bitrate/1000);
break;
}
return eof;
diff --git a/plugins/nullout/Makefile.am b/plugins/nullout/Makefile.am
new file mode 100644
index 00000000..da914823
--- /dev/null
+++ b/plugins/nullout/Makefile.am
@@ -0,0 +1,5 @@
+nulloutdir = $(libdir)/$(PACKAGE)
+pkglib_LTLIBRARIES = nullout.la
+nullout_la_SOURCES = nullout.c
+nullout_la_LDFLAGS = -module
+AM_CFLAGS = $(CFLAGS) -std=c99
diff --git a/plugins/nullout/nullout.c b/plugins/nullout/nullout.c
new file mode 100644
index 00000000..1e08121b
--- /dev/null
+++ b/plugins/nullout/nullout.c
@@ -0,0 +1,242 @@
+/*
+ DeaDBeeF - ultimate music player for GNU/Linux systems with X11
+ Copyright (C) 2009 Alexey Yakovenko
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/prctl.h>
+#include <stdio.h>
+#include <string.h>
+#include "deadbeef.h"
+
+//#define trace(...) { fprintf(stderr, __VA_ARGS__); }
+#define trace(fmt,...)
+
+static DB_output_t plugin;
+DB_functions_t *deadbeef;
+
+static intptr_t null_tid;
+static int null_terminate;
+static int null_rate;
+static int state;
+
+static void
+pnull_callback (char *stream, int len);
+
+static void
+pnull_thread (void *context);
+
+static int
+pnull_init (void);
+
+static int
+pnull_free (void);
+
+static int
+pnull_change_rate (int rate);
+
+static int
+pnull_play (void);
+
+static int
+pnull_stop (void);
+
+static int
+pnull_pause (void);
+
+static int
+pnull_unpause (void);
+
+static int
+pnull_get_rate (void);
+
+static int
+pnull_get_bps (void);
+
+static int
+pnull_get_channels (void);
+
+static int
+pnull_get_endianness (void);
+
+int
+pnull_init (void) {
+ trace ("pnull_init\n");
+ state = OUTPUT_STATE_STOPPED;
+ null_rate = 44100;
+ null_terminate = 0;
+ null_tid = deadbeef->thread_start (pnull_thread, NULL);
+ return 0;
+}
+
+int
+pnull_change_rate (int rate) {
+ null_rate = rate;
+ return null_rate;
+}
+
+int
+pnull_free (void) {
+ trace ("pnull_free\n");
+ if (!null_terminate) {
+ if (null_tid) {
+ null_terminate = 1;
+ deadbeef->thread_join (null_tid);
+ }
+ null_tid = 0;
+ state = OUTPUT_STATE_STOPPED;
+ null_terminate = 0;
+ }
+ return 0;
+}
+
+int
+pnull_play (void) {
+ if (!null_tid) {
+ pnull_init ();
+ }
+ state = OUTPUT_STATE_PLAYING;
+ return 0;
+}
+
+int
+pnull_stop (void) {
+ state = OUTPUT_STATE_STOPPED;
+ deadbeef->streamer_reset (1);
+ return 0;
+}
+
+int
+pnull_pause (void) {
+ if (state == OUTPUT_STATE_STOPPED) {
+ return -1;
+ }
+ // set pause state
+ state = OUTPUT_STATE_PAUSED;
+ return 0;
+}
+
+int
+pnull_unpause (void) {
+ // unset pause state
+ if (state == OUTPUT_STATE_PAUSED) {
+ state = OUTPUT_STATE_PLAYING;
+ }
+ return 0;
+}
+
+int
+pnull_get_rate (void) {
+ return null_rate;
+}
+
+int
+pnull_get_bps (void) {
+ return 16;
+}
+
+int
+pnull_get_channels (void) {
+ return 2;
+}
+
+static int
+pnull_get_endianness (void) {
+#if WORDS_BIGENDIAN
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+static void
+pnull_thread (void *context) {
+ prctl (PR_SET_NAME, "deadbeef-null", 0, 0, 0, 0);
+ for (;;) {
+ if (null_terminate) {
+ break;
+ }
+ if (state != OUTPUT_STATE_PLAYING) {
+ usleep (10000);
+ continue;
+ }
+
+ char buf[4096];
+ pnull_callback (buf, 1024);
+ }
+}
+
+static void
+pnull_callback (char *stream, int len) {
+ if (!deadbeef->streamer_ok_to_read (len)) {
+ memset (stream, 0, len);
+ return;
+ }
+ int bytesread = deadbeef->streamer_read (stream, len);
+
+ if (bytesread < len) {
+ memset (stream + bytesread, 0, len-bytesread);
+ }
+}
+
+int
+pnull_get_state (void) {
+ return state;
+}
+
+int
+null_start (void) {
+ return 0;
+}
+
+int
+null_stop (void) {
+ return 0;
+}
+
+DB_plugin_t *
+nullout_load (DB_functions_t *api) {
+ deadbeef = api;
+ return DB_PLUGIN (&plugin);
+}
+
+// define plugin interface
+static DB_output_t plugin = {
+ DB_PLUGIN_SET_API_VERSION
+ .plugin.version_major = 0,
+ .plugin.version_minor = 1,
+ .plugin.nostop = 1,
+ .plugin.type = DB_PLUGIN_OUTPUT,
+ .plugin.name = "null output plugin",
+ .plugin.descr = "doesn't play anything",
+ .plugin.author = "Alexey Yakovenko",
+ .plugin.email = "waker@users.sourceforge.net",
+ .plugin.website = "http://deadbeef.sf.net",
+ .plugin.start = null_start,
+ .plugin.stop = null_stop,
+ .init = pnull_init,
+ .free = pnull_free,
+ .change_rate = pnull_change_rate,
+ .play = pnull_play,
+ .stop = pnull_stop,
+ .pause = pnull_pause,
+ .unpause = pnull_unpause,
+ .state = pnull_get_state,
+ .samplerate = pnull_get_rate,
+ .bitspersample = pnull_get_bps,
+ .channels = pnull_get_channels,
+ .endianness = pnull_get_endianness,
+};
diff --git a/plugins/sndfile/sndfile.c b/plugins/sndfile/sndfile.c
index 46e3a99f..dfb956de 100644
--- a/plugins/sndfile/sndfile.c
+++ b/plugins/sndfile/sndfile.c
@@ -23,26 +23,75 @@
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
-#define trace(...) { fprintf(stderr, __VA_ARGS__); }
-//#define trace(fmt,...)
+//#define trace(...) { fprintf(stderr, __VA_ARGS__); }
+#define trace(fmt,...)
static DB_decoder_t plugin;
static DB_functions_t *deadbeef;
typedef struct {
SNDFILE *ctx;
+ DB_FILE *file;
int startsample;
int endsample;
int currentsample;
+ int bitrate;
} sndfilectx_t;
static sndfilectx_t sfctx;
+
+// vfs wrapper for sf
+static sf_count_t
+sf_vfs_get_filelen (void *user_data) {
+ sndfilectx_t *ctx = user_data;
+ return deadbeef->fgetlength (ctx->file);
+}
+
+static sf_count_t
+sf_vfs_read (void *ptr, sf_count_t count, void *user_data) {
+ sndfilectx_t *ctx = user_data;
+ return deadbeef->fread (ptr, 1, count, ctx->file);
+}
+
+static sf_count_t
+sf_vfs_write (const void *ptr, sf_count_t count, void *user_data) {
+ return -1;
+}
+
+static sf_count_t
+sf_vfs_seek (sf_count_t offset, int whence, void *user_data) {
+ sndfilectx_t *ctx = user_data;
+ return deadbeef->fseek (ctx->file, offset, whence);
+}
+
+static sf_count_t
+sf_vfs_tell (void *user_data) {
+ sndfilectx_t *ctx = user_data;
+ return deadbeef->ftell (ctx->file);
+}
+
+static SF_VIRTUAL_IO vfs = {
+ .get_filelen = sf_vfs_get_filelen,
+ .seek = sf_vfs_seek,
+ .read = sf_vfs_read,
+ .write = sf_vfs_write,
+ .tell = sf_vfs_tell
+};
+
static int
sndfile_init (DB_playItem_t *it) {
memset (&sfctx, 0, sizeof (sfctx));
SF_INFO inf;
- sfctx.ctx = sf_open (it->fname, SFM_READ, &inf);
+ DB_FILE *fp = deadbeef->fopen (it->fname);
+ if (!fp) {
+ trace ("sndfile: failed to open %s\n", it->fname);
+ return -1;
+ }
+ int fsize = deadbeef->fgetlength (fp);
+
+ sfctx.file = fp;
+ sfctx.ctx = sf_open_virtual (&vfs, SFM_READ, &inf, &sfctx);
if (!sfctx.ctx) {
return -1;
}
@@ -62,6 +111,15 @@ sndfile_init (DB_playItem_t *it) {
sfctx.startsample = 0;
sfctx.endsample = inf.frames-1;
}
+ // hack bitrate
+ float sec = (float)(sfctx.endsample-sfctx.startsample) / inf.samplerate;
+ if (sec != 0) {
+ sfctx.bitrate = fsize / sec * 8 / 1000;
+ }
+ else {
+ sfctx.bitrate = -1;
+ }
+
return 0;
}
@@ -70,6 +128,9 @@ sndfile_free (void) {
if (sfctx.ctx) {
sf_close (sfctx.ctx);
}
+ if (sfctx.file) {
+ deadbeef->fclose (sfctx.file);
+ }
memset (&sfctx, 0, sizeof (sfctx));
}
@@ -86,6 +147,9 @@ sndfile_read_int16 (char *bytes, int size) {
sfctx.currentsample += n;
size = n * 2 * plugin.info.channels;
plugin.info.readpos = (float)(sfctx.currentsample-sfctx.startsample)/plugin.info.samplerate;
+ if (sfctx.bitrate > 0) {
+ deadbeef->streamer_set_bitrate (sfctx.bitrate);
+ }
return size;
}
@@ -102,6 +166,9 @@ sndfile_read_float32 (char *bytes, int size) {
sfctx.currentsample += n;
size = n * 4 * plugin.info.channels;
plugin.info.readpos = (float)(sfctx.currentsample-sfctx.startsample)/plugin.info.samplerate;
+ if (sfctx.bitrate > 0) {
+ deadbeef->streamer_set_bitrate (sfctx.bitrate);
+ }
return size;
}
@@ -120,14 +187,22 @@ sndfile_seek (float sec) {
static DB_playItem_t *
sndfile_insert (DB_playItem_t *after, const char *fname) {
SF_INFO inf;
- SNDFILE *sf = sf_open (fname, SFM_READ, &inf);
- if (!sf) {
+ sndfilectx_t sfctx;
+ sfctx.file = deadbeef->fopen (fname);
+ if (!sfctx.file) {
+ trace ("sndfile: failed to open %s\n", fname);
+ return NULL;
+ }
+ sfctx.ctx = sf_open_virtual (&vfs, SFM_READ, &inf, &sfctx);
+ if (!sfctx.ctx) {
trace ("sndfile: sf_open failed");
return NULL;
}
int totalsamples = inf.frames;
int samplerate = inf.samplerate;
- sf_close (sf);
+ sf_close (sfctx.ctx);
+ deadbeef->fclose (sfctx.file);
+
float duration = (float)totalsamples / samplerate;
DB_playItem_t *cue_after = deadbeef->pl_insert_cue (after, fname, &plugin, "sndfile", totalsamples, samplerate);
if (cue_after) {
diff --git a/plugins/vfs_curl/vfs_curl.c b/plugins/vfs_curl/vfs_curl.c
index 034220e2..b4ea90fa 100644
--- a/plugins/vfs_curl/vfs_curl.c
+++ b/plugins/vfs_curl/vfs_curl.c
@@ -57,6 +57,8 @@ typedef struct {
char *content_name;
char *content_genre;
uint8_t status;
+// int icy_metaint;
+// int wait_meta;
// flags (bitfields to save some space)
unsigned seektoend : 1; // indicates that next tell must return length
unsigned gotheader : 1; // tells that all headers (including ICY) were processed (to start reading body)
@@ -137,10 +139,30 @@ http_curl_write (void *ptr, size_t size, size_t nmemb, void *stream) {
deadbeef->mutex_unlock (fp->mutex);
break;
}
+#if 0
+ if (fp->wait_meta == 0) {
+ char sz;
+ memcpy (&sz, ptr, 1);
+ printf ("reading %d bytes of metadata, seekpos:%d!\n", (int)sz*4, fp->pos);
+ ptr += 16 * sz;
+ avail -= 16 * sz + 1;
+ printf ("avail=%d!\n", avail);
+ fp->wait_meta = fp->icy_metaint;
+ }
+#endif
int sz = BUFFER_SIZE/2 - fp->remaining; // number of bytes free in buffer
// don't allow to fill more than half -- used for seeking backwards
+
if (sz > 5000) { // wait until there are at least 5k bytes free
int cp = min (avail, sz);
+#if 0
+ if (fp->wait_meta - cp <= 0) {
+ printf ("cp=%d->%d\n", cp, fp->wait_meta);
+ cp = fp->wait_meta;
+ }
+ fp->wait_meta -= cp;
+#endif
+
int writepos = (fp->pos + fp->remaining) & BUFFER_MASK;
// copy 1st portion (before end of buffer
int part1 = BUFFER_SIZE - writepos;
@@ -252,8 +274,6 @@ http_content_header_handler (void *ptr, size_t size, size_t nmemb, void *stream)
assert (stream);
HTTP_FILE *fp = (HTTP_FILE *)stream;
const uint8_t c_type_str[] ="Content-Type:";
- const uint8_t icy_name_str[] ="icy-name:";
- const uint8_t icy_genre_str[] ="icy-genre:";
const uint8_t *p = ptr;
const uint8_t *end = p + size*nmemb;
uint8_t key[256];
@@ -282,6 +302,11 @@ http_content_header_handler (void *ptr, size_t size, size_t nmemb, void *stream)
}
fp->content_genre = strdup (value);
}
+// else if (!strcasecmp (key, "icy-metaint")) {
+// //printf ("icy-metaint: %d\n", atoi (value));
+// fp->icy_metaint = atoi (value);
+// fp->wait_meta = fp->icy_metaint;
+// }
}
if (!fp->icyheader) {
fp->gotsomeheader = 1;
@@ -307,7 +332,7 @@ http_curl_control (void *stream, double dltotal, double dlnow, double ultotal, d
}
static void
-http_thread_func (uintptr_t ctx) {
+http_thread_func (void *ctx) {
HTTP_FILE *fp = (HTTP_FILE *)ctx;
CURL *curl;
curl = curl_easy_init ();
@@ -338,6 +363,7 @@ http_thread_func (uintptr_t ctx) {
trace ("vfs_curl: started loading data\n");
for (;;) {
+// struct curl_slist *headers = NULL;
curl_easy_reset (curl);
curl_easy_setopt (curl, CURLOPT_URL, fp->url);
curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1);
@@ -354,6 +380,8 @@ http_thread_func (uintptr_t ctx) {
// enable up to 10 redirects
curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt (curl, CURLOPT_MAXREDIRS, 10);
+// headers = curl_slist_append (headers, "Icy-Metadata:1");
+// curl_easy_setopt (curl, CURLOPT_HTTPHEADER, headers);
if (fp->pos > 0) {
curl_easy_setopt (curl, CURLOPT_RESUME_FROM, fp->pos);
}
@@ -401,6 +429,7 @@ http_thread_func (uintptr_t ctx) {
fp->status = STATUS_INITIAL;
trace ("seeking to %d\n", fp->pos);
deadbeef->mutex_unlock (fp->mutex);
+// curl_slist_free_all (headers);
}
curl_easy_cleanup (curl);
@@ -413,7 +442,7 @@ http_thread_func (uintptr_t ctx) {
static void
http_start_streamer (HTTP_FILE *fp) {
fp->mutex = deadbeef->mutex_create ();
- fp->tid = deadbeef->thread_start (http_thread_func, (uintptr_t)fp);
+ fp->tid = deadbeef->thread_start (http_thread_func, fp);
}
static DB_FILE *
diff --git a/plugins/vorbis/vorbis.c b/plugins/vorbis/vorbis.c
index 5679f852..0b6507b4 100644
--- a/plugins/vorbis/vorbis.c
+++ b/plugins/vorbis/vorbis.c
@@ -280,6 +280,7 @@ cvorbis_read (char *bytes, int size) {
}
plugin.info.readpos = (float)(ov_pcm_tell(&vorbis_file)-startsample)/vi->rate;
trace ("cvorbis_read got %d bytes, readpos %f, currentsample %d, ret %d\n", initsize-size, plugin.info.readpos, currentsample, ret);
+ deadbeef->streamer_set_bitrate (ov_bitrate_instant (&vorbis_file)/1000);
return initsize - size;
}
@@ -370,8 +371,8 @@ cvorbis_insert (DB_playItem_t *after, const char *fname) {
}
static int
-vorbis_trackdeleted (DB_event_song_t *ev, uintptr_t data) {
- if (ev->song == ptrack) {
+vorbis_trackdeleted (DB_event_track_t *ev, uintptr_t data) {
+ if (ev->track == ptrack) {
ptrack = NULL;
}
return 0;
@@ -380,11 +381,13 @@ vorbis_trackdeleted (DB_event_song_t *ev, uintptr_t data) {
static int
vorbis_start (void) {
deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_TRACKDELETED, DB_CALLBACK (vorbis_trackdeleted), 0);
+ return 0;
}
static int
vorbis_stop (void) {
deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_TRACKDELETED, DB_CALLBACK (vorbis_trackdeleted), 0);
+ return 0;
}
static const char * exts[] = { "ogg", NULL };
diff --git a/plugins/vtx/Makefile.am b/plugins/vtx/Makefile.am
new file mode 100644
index 00000000..abfa8c98
--- /dev/null
+++ b/plugins/vtx/Makefile.am
@@ -0,0 +1,6 @@
+vtxdir = $(libdir)/$(PACKAGE)
+pkglib_LTLIBRARIES = vtx.la
+vtx_la_SOURCES = vtx.c ay8912.c ayemu_8912.h ayemu.h ayemu_vtxfile.h lh5dec.c vtxfile.c
+vtx_la_LDFLAGS = -module
+
+AM_CFLAGS = $(CFLAGS) -std=c99
diff --git a/plugins/vtx/ay8912.c b/plugins/vtx/ay8912.c
new file mode 100644
index 00000000..9ac72245
--- /dev/null
+++ b/plugins/vtx/ay8912.c
@@ -0,0 +1,498 @@
+/* AY/YM emulator implementation. */
+
+#include "ayemu.h"
+
+#define debuglog stderr;
+
+char *ayemu_err;
+
+static const char VERSION[] = "libayemu 0.9";
+
+const int MAGIC1 = 0xcdef; /* for check ayemu_t structure inited */
+
+enum {
+/* Max amplitude value for stereo signal for avoiding for possible
+ folowwing SSRC for clipping */
+ AYEMU_MAX_AMP = 24575,
+ AYEMU_DEFAULT_CHIP_FREQ = 1773400
+};
+
+/* sound chip volume envelops (will calculated by gen_env()) */
+static int bEnvGenInit = 0;
+static int Envelope [16][128];
+
+
+/* AY volume table (c) by V_Soft and Lion 17 */
+static int Lion17_AY_table [16] =
+ { 0, 513, 828, 1239, 1923, 3238, 4926, 9110,
+ 10344, 17876, 24682, 30442, 38844, 47270, 56402, 65535};
+
+/* YM volume table (c) by V_Soft and Lion 17 */
+static int Lion17_YM_table [32] =
+ { 0, 0, 190, 286, 375, 470, 560, 664,
+ 866, 1130, 1515, 1803, 2253, 2848, 3351, 3862,
+ 4844, 6058, 7290, 8559, 10474, 12878, 15297, 17787,
+ 21500, 26172, 30866, 35676, 42664, 50986, 58842, 65535};
+
+/* AY volume table (c) by Hacker KAY */
+static int KAY_AY_table [16] =
+ { 0, 836, 1212, 1773, 2619, 3875, 5397, 8823,
+ 10392, 16706, 23339, 29292, 36969, 46421, 55195, 65535};
+
+/* YM volume table (c) by Hacker KAY */
+static int KAY_YM_table [32] =
+ { 0, 0, 248, 450, 670, 826, 1010, 1239,
+ 1552, 1919, 2314, 2626, 3131, 3778, 4407, 5031,
+ 5968, 7161, 8415, 9622, 11421, 13689, 15957, 18280,
+ 21759, 26148, 30523, 34879, 41434, 49404, 57492, 65535};
+
+/* default equlaizer (layout) settings for AY and YM, 7 stereo types */
+static const int default_layout [2][7][6] = {
+ {
+ /* A_l, A_r, B_l, B_r, C_l, C_r */
+
+ /* for AY */
+ {100, 100, 100, 100, 100, 100}, // _MONO
+ {100, 33, 70, 70, 33, 100}, // _ABC
+ {100, 33, 33, 100, 70, 70}, // _ACB
+ {70, 70, 100, 33, 33, 100}, // _BAC
+ {33, 100, 100, 33, 70, 70}, // _BCA
+ {70, 70, 33, 100, 100, 33}, // _CAB
+ {33, 100, 70, 70, 100, 33}}, // _CBA
+ {
+ /* for YM */
+ {100, 100, 100, 100, 100, 100}, // _MONO
+ {100, 5, 70, 70, 5, 100}, // _ABC
+ {100, 5, 5, 100, 70, 70}, // _ACB
+ {70, 70, 100, 5, 5, 100}, // _BAC
+ {5, 100, 100, 5, 70, 70}, // _BCA
+ {70, 70, 5, 100, 100, 5}, // _CAB
+ {5, 100, 70, 70, 100, 5}} // _CBA
+};
+
+
+static int check_magic(ayemu_ay_t *ay)
+{
+ if (ay->magic == MAGIC1)
+ return 1;
+ fprintf(stderr, "libayemu: passed pointer %p to uninitialized ayemu_ay_t structure\n", ay);
+ return 0;
+}
+
+
+/* make chip hardware envelop tables.
+ Will execute once before first use. */
+static void gen_env()
+{
+ int env;
+ int pos;
+ int hold;
+ int dir;
+ int vol;
+
+ for (env = 0; env < 16; env++) {
+ hold = 0;
+ dir = (env & 4)? 1 : -1;
+ vol = (env & 4)? -1 : 32;
+ for (pos = 0; pos < 128; pos++) {
+ if (!hold) {
+ vol += dir;
+ if (vol < 0 || vol >= 32) {
+ if ( env & 8 ) {
+ if ( env & 2 ) dir = -dir;
+ vol = (dir > 0 )? 0:31;
+ if ( env & 1 ) {
+ hold = 1;
+ vol = ( dir > 0 )? 31:0;
+ }
+ } else {
+ vol = 0;
+ hold = 1;
+ }
+ }
+ }
+ Envelope[env][pos] = vol;
+ }
+ }
+ bEnvGenInit = 1;
+}
+
+
+/**
+ * \retval ayemu_init none.
+ *
+*/
+void ayemu_init(ayemu_ay_t *ay)
+{
+ ay->default_chip_flag = 1;
+ ay->ChipFreq = AYEMU_DEFAULT_CHIP_FREQ;
+ ay->default_stereo_flag = 1;
+ ay->default_sound_format_flag = 1;
+ ay->dirty = 1;
+ ay->magic = MAGIC1;
+
+ ayemu_reset(ay);
+}
+
+/** Reset AY/YM chip.
+ *
+ * \arg \c ay - pointer to ayemu_ay_t structure.
+ * \return none.
+ */
+void ayemu_reset(ayemu_ay_t *ay)
+{
+ if (!check_magic(ay)) return;
+
+ ay->cnt_a = ay->cnt_b = ay->cnt_c = ay->cnt_n = ay->cnt_e = 0;
+ ay->bit_a = ay->bit_b = ay->bit_c = ay->bit_n = 0;
+ ay->env_pos = ay->EnvNum = 0;
+ ay->Cur_Seed = 0xffff;
+}
+
+
+static void set_table_ay (ayemu_ay_t *ay, int tbl[16])
+{
+ int n;
+ for (n = 0; n < 32; n++)
+ ay->table[n] = tbl[n/2];
+ ay->type = AYEMU_AY;
+}
+
+static void set_table_ym (ayemu_ay_t *ay, int tbl[32])
+{
+ int n;
+ for (n = 0; n < 32; n++)
+ ay->table[n] = tbl[n];
+ ay->type = AYEMU_YM;
+}
+
+
+/** Set chip type. */
+int ayemu_set_chip_type(ayemu_ay_t *ay, ayemu_chip_t type, int *custom_table)
+{
+if (!check_magic(ay))
+ return 0;
+
+ if (!(type == AYEMU_AY_CUSTOM || type == AYEMU_YM_CUSTOM) && custom_table != NULL) {
+ ayemu_err = "For non-custom chip type 'custom_table' param must be NULL";
+ return 0;
+ }
+
+ switch(type) {
+ case AYEMU_AY:
+ case AYEMU_AY_LION17:
+ set_table_ay(ay, Lion17_AY_table);
+ break;
+ case AYEMU_YM:
+ case AYEMU_YM_LION17:
+ set_table_ym(ay, Lion17_YM_table);
+ break;
+ case AYEMU_AY_KAY:
+ set_table_ay(ay, KAY_AY_table);
+ break;
+ case AYEMU_YM_KAY:
+ set_table_ym(ay, KAY_YM_table);
+ break;
+ case AYEMU_AY_CUSTOM:
+ set_table_ay(ay, custom_table);
+ break;
+ case AYEMU_YM_CUSTOM:
+ set_table_ym(ay, custom_table);
+ break;
+ default:
+ ayemu_err = "Incorrect chip type";
+ return 0;
+ }
+
+ ay->default_chip_flag = 0;
+ ay->dirty = 1;
+ return 1;
+}
+
+
+/** Set chip frequency. */
+void ayemu_set_chip_freq(ayemu_ay_t *ay, int chipfreq)
+{
+ if (!check_magic(ay)) return;
+
+ ay->ChipFreq = chipfreq;
+ ay->dirty = 1;
+}
+
+/*! Set output sound format
+ * \arg \c ay - pointer to ayemu_t structure
+ * \arg \c freq - sound freq (44100 for example)
+ * \arg \c chans - number of channels (1-mono, 2-stereo)
+ * \arg \c bits - now supported only 16 and 8.
+ * \retval \b 1 on success, \b 0 if error occure
+ */
+int ayemu_set_sound_format (ayemu_ay_t *ay, int freq, int chans, int bits)
+{
+ if (!check_magic(ay))
+ return 0;
+
+ if (!(bits == 16 || bits == 8)) {
+ ayemu_err = "Incorrect bits value";
+ return 0;
+ }
+ else if (!(chans == 1 || chans == 2)) {
+ ayemu_err = "Incorrect number of channels";
+ return 0;
+ }
+ else if (freq < 50) {
+ ayemu_err = "Incorrect output sound freq";
+ return 0;
+ }
+ else {
+ ay->sndfmt.freq = freq;
+ ay->sndfmt.channels = chans;
+ ay->sndfmt.bpc = bits;
+ }
+
+ ay->default_sound_format_flag = 0;
+ ay->dirty = 1;
+ return 1;
+}
+
+
+/*! Set amplitude factor for each of channels (A,B anc C, tone and noise).
+ * Factor's value must be from (-100) to 100.
+ * \arg ay - pointer to ayemu_t structure
+ * \arg stereo_type - type of stereo
+ * \arg custom_eq - NULL or pointer to custom table of mixer layout.
+ * \retval 1 if OK, 0 if error occures.
+ */
+int ayemu_set_stereo(ayemu_ay_t *ay, ayemu_stereo_t stereo_type, int *custom_eq)
+{
+ int i;
+ int chip;
+
+ if (!check_magic(ay))
+ return 0;
+
+ if (stereo_type != AYEMU_STEREO_CUSTOM && custom_eq != NULL) {
+ ayemu_err = "Stereo type not custom, 'custom_eq' parametr must be NULL";
+ return 0;
+ }
+
+ chip = (ay->type == AYEMU_AY)? 0 : 1;
+
+ switch(stereo_type) {
+ case AYEMU_MONO:
+ case AYEMU_ABC:
+ case AYEMU_ACB:
+ case AYEMU_BAC:
+ case AYEMU_BCA:
+ case AYEMU_CAB:
+ case AYEMU_CBA:
+ for (i = 0 ; i < 6 ; i++)
+ ay->eq[i] = default_layout[chip][stereo_type][i];
+ break;
+ case AYEMU_STEREO_CUSTOM:
+ for (i = 0 ; i < 6 ; i++)
+ ay->eq[i] = custom_eq[i];
+ break;
+ default:
+ ayemu_err = "Incorrect stereo type";
+ return 0;
+ }
+
+ ay->default_stereo_flag = 0;
+ ay->dirty = 1;
+ return 1;
+}
+
+
+#if 0
+#define WARN_IF_REGISTER_GREAT_THAN(r,m) \
+if (*(regs + r) > m) \
+ fprintf(stderr, "ayemu_set_regs: warning: possible bad register data- R%d > %d\n", r, m)
+#else
+#define WARN_IF_REGISTER_GREAT_THAN(r,m)
+#endif
+
+
+/** Assign values for AY registers.
+ *
+ * You must pass array of char [14] to this function
+ */
+void ayemu_set_regs(ayemu_ay_t *ay, ayemu_ay_reg_frame_t regs)
+{
+ if (!check_magic(ay)) return;
+
+ WARN_IF_REGISTER_GREAT_THAN(1,15);
+ WARN_IF_REGISTER_GREAT_THAN(3,15);
+ WARN_IF_REGISTER_GREAT_THAN(5,15);
+ WARN_IF_REGISTER_GREAT_THAN(8,31);
+ WARN_IF_REGISTER_GREAT_THAN(9,31);
+ WARN_IF_REGISTER_GREAT_THAN(10,31);
+
+ ay->regs.tone_a = regs[0] + ((regs[1]&0x0f) << 8);
+ ay->regs.tone_b = regs[2] + ((regs[3]&0x0f) << 8);
+ ay->regs.tone_c = regs[4] + ((regs[5]&0x0f) << 8);
+
+ ay->regs.noise = regs[6] & 0x1f;
+
+ ay->regs.R7_tone_a = ! (regs[7] & 0x01);
+ ay->regs.R7_tone_b = ! (regs[7] & 0x02);
+ ay->regs.R7_tone_c = ! (regs[7] & 0x04);
+
+ ay->regs.R7_noise_a = ! (regs[7] & 0x08);
+ ay->regs.R7_noise_b = ! (regs[7] & 0x10);
+ ay->regs.R7_noise_c = ! (regs[7] & 0x20);
+
+ ay->regs.vol_a = regs[8] & 0x0f;
+ ay->regs.vol_b = regs[9] & 0x0f;
+ ay->regs.vol_c = regs[10] & 0x0f;
+ ay->regs.env_a = regs[8] & 0x10;
+ ay->regs.env_b = regs[9] & 0x10;
+ ay->regs.env_c = regs[10] & 0x10;
+ ay->regs.env_freq = regs[11] + (regs[12] << 8);
+
+ if (regs[13] != 0xff) { /* R13 = 255 means continue curent envelop */
+ ay->regs.env_style = regs[13] & 0x0f;
+ ay->env_pos = ay->cnt_e = 0;
+ }
+}
+
+
+static void prepare_generation(ayemu_ay_t *ay)
+{
+ int vol, max_l, max_r;
+
+ if (!ay->dirty) return;
+
+ if (!bEnvGenInit) gen_env ();
+
+ if (ay->default_chip_flag) ayemu_set_chip_type(ay, AYEMU_AY, NULL);
+
+ if (ay->default_stereo_flag) ayemu_set_stereo(ay, AYEMU_ABC, NULL);
+
+ if (ay->default_sound_format_flag) ayemu_set_sound_format(ay, 44100, 2, 16);
+
+ ay->ChipTacts_per_outcount = ay->ChipFreq / ay->sndfmt.freq / 8;
+
+ { /* GenVols */
+ int n, m;
+ int vol;
+ for (n = 0; n < 32; n++) {
+ vol = ay->table[n];
+ for (m=0; m < 6; m++)
+ ay->vols[m][n] = (int) (((double) vol * ay->eq[m]) / 100);
+ }
+ }
+
+ /* ÄÉÎÁÍÉÞÅÓËÁÑ ÎÁÓÔÒÏÊËÁ ÇÌÏÂÁÌØÎÏÇÏ ËÏÜÆÆÉÃÉÅÎÔÁ ÕÓÉÌÅÎÉÑ
+ ÐÏÄÒÁÚÕÍÅ×ÁÅÔÓÑ, ÞÔÏ × vols [x][31] ÌÅÖÉÔ ÓÁÍÁÑ ÂÏÌØÛÁÑ ÇÒÏÍËÏÓÔØ
+ TODO: óÄÅÌÁÔØ ÐÒÏ×ÅÒËÕ ÎÁ ÜÔÏ ;-)
+ */
+ max_l = ay->vols[0][31] + ay->vols[2][31] + ay->vols[3][31];
+ max_r = ay->vols[1][31] + ay->vols[3][31] + ay->vols[5][31];
+ vol = (max_l > max_r) ? max_l : max_r; // =157283 on all defaults
+ ay->Amp_Global = ay->ChipTacts_per_outcount *vol / AYEMU_MAX_AMP;
+
+ ay->dirty = 0;
+}
+
+
+/*! Generate sound.
+ * Fill sound buffer with current register data
+ * Return value: pointer to next data in output sound buffer
+ * \retval \b 1 if OK, \b 0 if error occures.
+ */
+void *ayemu_gen_sound(ayemu_ay_t *ay, void *buff, size_t sound_bufsize)
+{
+ int mix_l, mix_r;
+ int tmpvol;
+ int m;
+ int snd_numcount;
+ unsigned char *sound_buf = buff;
+
+ if (!check_magic(ay))
+ return 0;
+
+ prepare_generation(ay);
+
+ snd_numcount = sound_bufsize / (ay->sndfmt.channels * (ay->sndfmt.bpc >> 3));
+ while (snd_numcount-- > 0) {
+ mix_l = mix_r = 0;
+
+ for (m = 0 ; m < ay->ChipTacts_per_outcount ; m++) {
+ if (++ay->cnt_a >= ay->regs.tone_a) {
+ ay->cnt_a = 0;
+ ay->bit_a = ! ay->bit_a;
+ }
+ if (++ay->cnt_b >= ay->regs.tone_b) {
+ ay->cnt_b = 0;
+ ay->bit_b = ! ay->bit_b;
+ }
+ if (++ay->cnt_c >= ay->regs.tone_c) {
+ ay->cnt_c = 0;
+ ay->bit_c = ! ay->bit_c;
+ }
+
+ /* GenNoise (c) Hacker KAY & Sergey Bulba */
+ if (++ay->cnt_n >= (ay->regs.noise * 2)) {
+ ay->cnt_n = 0;
+ ay->Cur_Seed = (ay->Cur_Seed * 2 + 1) ^ \
+ (((ay->Cur_Seed >> 16) ^ (ay->Cur_Seed >> 13)) & 1);
+ ay->bit_n = ((ay->Cur_Seed >> 16) & 1);
+ }
+
+ if (++ay->cnt_e >= ay->regs.env_freq) {
+ ay->cnt_e = 0;
+ if (++ay->env_pos > 127)
+ ay->env_pos = 64;
+ }
+
+#define ENVVOL Envelope [ay->regs.env_style][ay->env_pos]
+
+ if ((ay->bit_a | !ay->regs.R7_tone_a) & (ay->bit_n | !ay->regs.R7_noise_a)) {
+ tmpvol = (ay->regs.env_a)? ENVVOL : ay->regs.vol_a * 2 + 1;
+ mix_l += ay->vols[0][tmpvol];
+ mix_r += ay->vols[1][tmpvol];
+ }
+
+ if ((ay->bit_b | !ay->regs.R7_tone_b) & (ay->bit_n | !ay->regs.R7_noise_b)) {
+ tmpvol =(ay->regs.env_b)? ENVVOL : ay->regs.vol_b * 2 + 1;
+ mix_l += ay->vols[2][tmpvol];
+ mix_r += ay->vols[3][tmpvol];
+ }
+
+ if ((ay->bit_c | !ay->regs.R7_tone_c) & (ay->bit_n | !ay->regs.R7_noise_c)) {
+ tmpvol = (ay->regs.env_c)? ENVVOL : ay->regs.vol_c * 2 + 1;
+ mix_l += ay->vols[4][tmpvol];
+ mix_r += ay->vols[5][tmpvol];
+ }
+ } /* end for (m=0; ...) */
+
+ mix_l /= ay->Amp_Global;
+ mix_r /= ay->Amp_Global;
+
+ if (ay->sndfmt.bpc == 8) {
+ mix_l = (mix_l >> 8) | 128; /* 8 bit sound */
+ mix_r = (mix_r >> 8) | 128;
+ *sound_buf++ = mix_l;
+ if (ay->sndfmt.channels != 1)
+ *sound_buf++ = mix_r;
+ } else {
+ *sound_buf++ = mix_l & 0x00FF; /* 16 bit sound */
+ *sound_buf++ = (mix_l >> 8);
+ if (ay->sndfmt.channels != 1) {
+ *sound_buf++ = mix_r & 0x00FF;
+ *sound_buf++ = (mix_r >> 8);
+ }
+ }
+ }
+ return sound_buf;
+}
+
+/** Free all data allocated by emulator
+ *
+ * For now it do nothing.
+ */
+void ayemu_free (ayemu_ay_t *ay)
+{
+ /* nothing to do here */
+ return;
+}
diff --git a/plugins/vtx/ayemu.h b/plugins/vtx/ayemu.h
new file mode 100644
index 00000000..637cea91
--- /dev/null
+++ b/plugins/vtx/ayemu.h
@@ -0,0 +1,84 @@
+/*
+ ayemu - AY/YM sound chip emulator and music file loader
+ Copyright (C) 2003-2004 Sashnov Alexander
+
+ 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
+
+ Alexander Sashnov
+ sashnov@ngs.ru
+*/
+
+/*!
+ * \mainpage libayemu library
+ *
+ * Homepage on sourceforge.net: http://sourceforge.net/projects/libayemu
+ *
+ * This library will help you to add AY/YM music in your own game, demos, etc.
+ *
+ * For example of how to use it see playvtx and xmms-vtx packages.
+ *
+ * Python wrapper and example comming soon.
+ *
+ */
+
+#ifndef _AYEMU_H
+#define _AYEMU_H
+
+#ifdef __cplusplus
+# define BEGIN_C_DECLS extern "C" {
+# define END_C_DECLS }
+#else /* !__cplusplus */
+# define BEGIN_C_DECLS
+# define END_C_DECLS
+#endif /* __cplusplus */
+
+/* Make sure the correct platform symbols are defined */
+#if !defined(WIN32) && defined(_WIN32)
+#define WIN32
+#endif /* Windows */
+
+/* Some compilers use a special export keyword */
+#ifndef DECLSPEC
+# ifdef __BEOS__
+# if defined(__GNUC__)
+# define DECLSPEC __declspec(dllexport)
+# else
+# define DECLSPEC __declspec(export)
+# endif
+# else
+# ifdef WIN32
+# ifdef __BORLANDC__
+# ifdef BUILD_SDL
+# define DECLSPEC
+# else
+# define DECLSPEC __declspec(dllimport)
+# endif
+# else
+# define DECLSPEC __declspec(dllexport)
+# endif
+# else
+# define DECLSPEC
+# endif
+# endif
+#endif
+
+#define EXTERN extern DECLSPEC
+
+
+/* include other library headers */
+#include "ayemu_8912.h"
+#include "ayemu_vtxfile.h"
+
+#endif
diff --git a/plugins/vtx/ayemu_8912.h b/plugins/vtx/ayemu_8912.h
new file mode 100644
index 00000000..e8e50e5a
--- /dev/null
+++ b/plugins/vtx/ayemu_8912.h
@@ -0,0 +1,156 @@
+/**
+ * AY/YM emulator include file
+ */
+
+#ifndef _AYEMU_ay8912_h
+#define _AYEMU_ay8912_h
+
+#include <stddef.h>
+
+BEGIN_C_DECLS
+
+// TODO: ayemu_ay_t - hide all private data, allocate it in init function.
+
+typedef unsigned char ayemu_ay_reg_frame_t[14];
+
+
+
+/** Types of stereo.
+ The codes of stereo types used for generage sound. */
+typedef enum
+{
+ AYEMU_MONO = 0,
+ AYEMU_ABC,
+ AYEMU_ACB,
+ AYEMU_BAC,
+ AYEMU_BCA,
+ AYEMU_CAB,
+ AYEMU_CBA,
+ AYEMU_STEREO_CUSTOM = 255
+} ayemu_stereo_t;
+
+/** Sound chip type.
+ Constant for identify used chip for emulation */
+typedef enum {
+ AYEMU_AY, /**< default AY chip (lion17 for now) */
+ AYEMU_YM, /**< default YM chip (lion17 for now) */
+ AYEMU_AY_LION17, /**< emulate AY with Lion17 table */
+ AYEMU_YM_LION17, /**< emulate YM with Lion17 table */
+ AYEMU_AY_KAY, /**< emulate AY with HACKER KAY table */
+ AYEMU_YM_KAY, /**< emulate YM with HACKER KAY table */
+ AYEMU_AY_LOG, /**< emulate AY with logariphmic table */
+ AYEMU_YM_LOG, /**< emulate YM with logariphmic table */
+ AYEMU_AY_CUSTOM, /**< use AY with custom table. */
+ AYEMU_YM_CUSTOM /**< use YM with custom table. */
+} ayemu_chip_t;
+
+/** parsed by #ayemu_set_regs() AY registers data \internal */
+typedef struct
+{
+ int tone_a; /**< R0, R1 */
+ int tone_b; /**< R2, R3 */
+ int tone_c; /**< R4, R5 */
+ int noise; /**< R6 */
+ int R7_tone_a; /**< R7 bit 0 */
+ int R7_tone_b; /**< R7 bit 1 */
+ int R7_tone_c; /**< R7 bit 2 */
+ int R7_noise_a; /**< R7 bit 3 */
+ int R7_noise_b; /**< R7 bit 4 */
+ int R7_noise_c; /**< R7 bit 5 */
+ int vol_a; /**< R8 bits 3-0 */
+ int vol_b; /**< R9 bits 3-0 */
+ int vol_c; /**< R10 bits 3-0 */
+ int env_a; /**< R8 bit 4 */
+ int env_b; /**< R9 bit 4 */
+ int env_c; /**< R10 bit 4 */
+ int env_freq; /**< R11, R12 */
+ int env_style; /**< R13 */
+}
+ayemu_regdata_t;
+
+
+/** Output sound format \internal */
+typedef struct
+{
+ int freq; /**< sound freq */
+ int channels; /**< channels (1-mono, 2-stereo) */
+ int bpc; /**< bits (8 or 16) */
+}
+ayemu_sndfmt_t;
+
+/**
+ * \defgroup libayemu Functions for emulate AY/YM chip
+ */
+/*@{*/
+
+/** Data structure for sound chip emulation \internal
+ *
+ */
+typedef struct
+{
+ /* emulator settings */
+ int table[32]; /**< table of volumes for chip */
+ ayemu_chip_t type; /**< general chip type (\b AYEMU_AY or \b AYEMU_YM) */
+ int ChipFreq; /**< chip emulator frequency */
+ int eq[6]; /**< volumes for channels.
+ Array contains 6 elements:
+ A left, A right, B left, B right, C left and C right;
+ range -100...100 */
+ ayemu_regdata_t regs; /**< parsed registers data */
+ ayemu_sndfmt_t sndfmt; /**< output sound format */
+
+ /* flags */
+ int magic; /**< structure initialized flag */
+ int default_chip_flag; /**< =1 after init, resets in #ayemu_set_chip_type() */
+ int default_stereo_flag; /**< =1 after init, resets in #ayemu_set_stereo() */
+ int default_sound_format_flag; /**< =1 after init, resets in #ayemu_set_sound_format() */
+ int dirty; /**< dirty flag. Sets if any emulator properties changed */
+
+ int bit_a; /**< state of channel A generator */
+ int bit_b; /**< state of channel B generator */
+ int bit_c; /**< state of channel C generator */
+ int bit_n; /**< current generator state */
+ int cnt_a; /**< back counter of A */
+ int cnt_b; /**< back counter of B */
+ int cnt_c; /**< back counter of C */
+ int cnt_n; /**< back counter of noise generator */
+ int cnt_e; /**< back counter of envelop generator */
+ int ChipTacts_per_outcount; /**< chip's counts per one sound signal count */
+ int Amp_Global; /**< scale factor for amplitude */
+ int vols[6][32]; /**< stereo type (channel volumes) and chip table.
+ This cache calculated by #table and #eq */
+ int EnvNum; /**< number of current envilopment (0...15) */
+ int env_pos; /**< current position in envelop (0...127) */
+ int Cur_Seed; /**< random numbers counter */
+}
+ayemu_ay_t;
+
+EXTERN void
+ayemu_init(ayemu_ay_t *ay);
+
+EXTERN void
+ayemu_reset(ayemu_ay_t *ay);
+
+EXTERN int
+ayemu_set_chip_type(ayemu_ay_t *ay, ayemu_chip_t chip, int *custom_table);
+
+EXTERN void
+ayemu_set_chip_freq(ayemu_ay_t *ay, int chipfreq);
+
+EXTERN int
+ayemu_set_stereo(ayemu_ay_t *ay, ayemu_stereo_t stereo, int *custom_eq);
+
+EXTERN int
+ayemu_set_sound_format (ayemu_ay_t *ay, int freq, int chans, int bits);
+
+EXTERN void
+ayemu_set_regs (ayemu_ay_t *ay, ayemu_ay_reg_frame_t regs);
+
+EXTERN void*
+ayemu_gen_sound (ayemu_ay_t *ay, void *buf, size_t bufsize);
+
+/*@}*/
+
+END_C_DECLS
+
+#endif
diff --git a/plugins/vtx/ayemu_vtxfile.h b/plugins/vtx/ayemu_vtxfile.h
new file mode 100644
index 00000000..3856b45d
--- /dev/null
+++ b/plugins/vtx/ayemu_vtxfile.h
@@ -0,0 +1,73 @@
+#ifndef _AYEMU_vtxfile_h
+#define _AYEMU_vtxfile_h
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include "ayemu_8912.h"
+
+BEGIN_C_DECLS
+
+/**
+ * \defgroup vtxfile Functions for extract data from vtx files
+ */
+/*@{*/
+
+/** structure for VTX file format handler
+ * \internal
+ * It stores VTX file header and current state
+ * (open file pointer, extracted register data, etc).
+ */
+typedef struct
+{
+ ayemu_chip_t chiptype; /**< Type of sound chip */
+ int stereo; /**< Type of stereo: ABC, BCA,... */
+ int loop; /**< song loop */
+ int chipFreq; /**< AY chip freq (1773400 for ZX) */
+ int playerFreq; /**< 50 Hz for ZX, 60 Hz for yamaha */
+ int year; /**< year song composed */
+ char *title; /**< song title */
+ char *author; /**< song author */
+ char *from; /**< song from */
+ char *tracker; /**< tracker */
+ char *comment; /**< comment */
+ int regdata_size; /**< size of unpacked data (need for unpack). */
+ unsigned char *regdata; /**< unpacked song data */
+ size_t frames; /**< number of AY data frames */
+} ayemu_vtx_t;
+
+
+/*! Load only header (song info) from vtx file.
+ * Purpose: read tags in play list.
+ */
+EXTERN ayemu_vtx_t * ayemu_vtx_header(const char *buf, size_t size);
+
+/*! Load song (header and unpack data).
+ *
+ */
+EXTERN ayemu_vtx_t * ayemu_vtx_load(const char *buf, size_t size);
+
+EXTERN void ayemu_vtx_getframe(const ayemu_vtx_t *vtx, size_t frame_n,
+ ayemu_ay_reg_frame_t regs);
+
+/** Free all allocaded data and structure itself.
+ * You must call this for any ayemu_vtx_t pointer allocated by this lib
+ * by functions ayemu_vtx_header() and ayemu_vtx_load().
+ */
+EXTERN void ayemu_vtx_free(ayemu_vtx_t *vtx);
+
+/*! Load song header from file.
+ * Helper (non-all platform) function
+ */
+EXTERN ayemu_vtx_t * ayemu_vtx_header_from_file(const char *filename);
+
+/*! Load song header and data from file.
+ * Helper (non-all platform) function
+ */
+EXTERN ayemu_vtx_t * ayemu_vtx_load_from_file(const char *filename);
+
+/*@}*/
+
+END_C_DECLS
+
+#endif
diff --git a/plugins/vtx/lh5dec.c b/plugins/vtx/lh5dec.c
new file mode 100644
index 00000000..1b2456bb
--- /dev/null
+++ b/plugins/vtx/lh5dec.c
@@ -0,0 +1,304 @@
+/* extractiong lh5 module
+ (c) Haruhiko Okumura
+ (m) Roman Scherbakov
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h> /* memmove */
+#include <limits.h>
+
+static unsigned short bitbuf;
+
+#define BITBUFSIZ (CHAR_BIT * sizeof bitbuf)
+
+#define DICBIT 13 /* 12(-lh4-) or 13(-lh5-) */
+#define DICSIZ (1L << DICBIT)
+#define MATCHBIT 8 /* bits for MAXMATCH - THRESHOLD */
+#define MAXMATCH 256 /* formerly F (not more than unsigned char_MAX + 1) */
+#define THRESHOLD 3 /* choose optimal value */
+#define NC (UCHAR_MAX + MAXMATCH + 2 - THRESHOLD)
+#define CBIT 9 /* $\lfloor \log_2 NC \rfloor + 1$ */
+#define CODE_BIT 16 /* codeword length */
+#define NP (DICBIT + 1)
+#define NT (CODE_BIT + 3)
+#define PBIT 4 /* smallest integer such that (1U << PBIT) > NP */
+#define TBIT 5 /* smallest integer such that (1U << TBIT) > NT */
+#if NT > NP
+#define NPT NT
+#else
+#define NPT NP
+#endif
+
+static unsigned long origsize, compsize;
+static const unsigned char *in_buf;
+static unsigned char *out_buf;
+
+static unsigned short subbitbuf;
+static int bitcount;
+
+static unsigned short left[2 * NC - 1], right[2 * NC - 1];
+static unsigned char c_len[NC], pt_len[NPT];
+static unsigned short blocksize;
+
+static unsigned short c_table[4096], pt_table[256];
+
+static int j; /* remaining bytes to copy */
+
+
+static void error(char *msg)
+{
+ fprintf(stderr, "libayemu: lh5dec.c: %s\n", msg);
+ exit(EXIT_FAILURE);
+}
+
+static void fillbuf(int n) /* Shift bitbuf n bits left, read n bits */
+{
+ bitbuf <<= n;
+ while (n > bitcount) {
+ bitbuf |= subbitbuf << (n -= bitcount);
+ if (compsize != 0) {
+ compsize--; subbitbuf = *in_buf++;
+ } else subbitbuf = 0;
+ bitcount = CHAR_BIT;
+ }
+ bitbuf |= subbitbuf >> (bitcount -= n);
+}
+
+static unsigned short getbits(int n)
+{
+ unsigned short x;
+
+ x = bitbuf >> (BITBUFSIZ - n); fillbuf(n);
+ return x;
+}
+
+// make table for decoding
+
+static void make_table(int nchar, unsigned char bitlen[], int tablebits, unsigned short table[])
+{
+ unsigned short count[17], weight[17], start[18], *p;
+ unsigned short i, k, len, ch, jutbits, avail, nextcode, mask;
+
+ for (i = 1; i <= 16; i++) count[i] = 0;
+ for (i = 0; i < nchar; i++) count[bitlen[i]]++;
+
+ start[1] = 0;
+ for (i = 1; i <= 16; i++)
+ start[i + 1] = start[i] + (count[i] << (16 - i));
+ if (start[17] != (unsigned short)(1U << 16)) error("Bad table");
+
+ jutbits = 16 - tablebits;
+ for (i = 1; i <= tablebits; i++) {
+ start[i] >>= jutbits;
+ weight[i] = 1U << (tablebits - i);
+ }
+ while (i <= 16) weight[i++] = 1U << (16 - i);
+
+ i = start[tablebits + 1] >> jutbits;
+ if (i != (unsigned short)(1U << 16)) {
+ k = 1U << tablebits;
+ while (i != k) table[i++] = 0;
+ }
+
+ avail = nchar;
+ mask = 1U << (15 - tablebits);
+ for (ch = 0; ch < nchar; ch++) {
+ if ((len = bitlen[ch]) == 0) continue;
+ nextcode = start[len] + weight[len];
+ if (len <= tablebits) {
+ for (i = start[len]; i < nextcode; i++) table[i] = ch;
+ } else {
+ k = start[len];
+ p = &table[k >> jutbits];
+ i = len - tablebits;
+ while (i != 0) {
+ if (*p == 0) {
+ right[avail] = left[avail] = 0;
+ *p = avail++;
+ }
+ if (k & mask) p = &right[*p];
+ else p = &left[*p];
+ k <<= 1; i--;
+ }
+ *p = ch;
+ }
+ start[len] = nextcode;
+ }
+}
+
+// static Huffman
+
+static void read_pt_len(int nn, int nbit, int i_special)
+{
+ int i, c, n;
+ unsigned short mask;
+
+ n = getbits(nbit);
+ if (n == 0) {
+ c = getbits(nbit);
+ for (i = 0; i < nn; i++) pt_len[i] = 0;
+ for (i = 0; i < 256; i++) pt_table[i] = c;
+ } else {
+ i = 0;
+ while (i < n) {
+ c = bitbuf >> (BITBUFSIZ - 3);
+ if (c == 7) {
+ mask = 1U << (BITBUFSIZ - 1 - 3);
+ while (mask & bitbuf) { mask >>= 1; c++; }
+ }
+ fillbuf((c < 7) ? 3 : c - 3);
+ pt_len[i++] = c;
+ if (i == i_special) {
+ c = getbits(2);
+ while (--c >= 0) pt_len[i++] = 0;
+ }
+ }
+ while (i < nn) pt_len[i++] = 0;
+ make_table(nn, pt_len, 8, pt_table);
+ }
+}
+
+static void read_c_len(void)
+{
+ int i, c, n;
+ unsigned short mask;
+
+ n = getbits(CBIT);
+ if (n == 0) {
+ c = getbits(CBIT);
+ for (i = 0; i < NC; i++) c_len[i] = 0;
+ for (i = 0; i < 4096; i++) c_table[i] = c;
+ } else {
+ i = 0;
+ while (i < n) {
+ c = pt_table[bitbuf >> (BITBUFSIZ - 8)];
+ if (c >= NT) {
+ mask = 1U << (BITBUFSIZ - 1 - 8);
+ do {
+ if (bitbuf & mask) c = right[c];
+ else c = left [c];
+ mask >>= 1;
+ } while (c >= NT);
+ }
+ fillbuf(pt_len[c]);
+ if (c <= 2) {
+ if (c == 0) c = 1;
+ else if (c == 1) c = getbits(4) + 3;
+ else c = getbits(CBIT) + 20;
+ while (--c >= 0) c_len[i++] = 0;
+ } else c_len[i++] = c - 2;
+ }
+ while (i < NC) c_len[i++] = 0;
+ make_table(NC, c_len, 12, c_table);
+ }
+}
+
+
+static unsigned short decode_c(void)
+{
+ unsigned short j, mask;
+
+ if (blocksize == 0) {
+ blocksize = getbits(16);
+ read_pt_len(NT, TBIT, 3);
+ read_c_len();
+ read_pt_len(NP, PBIT, -1);
+ }
+ blocksize--;
+ j = c_table[bitbuf >> (BITBUFSIZ - 12)];
+ if (j >= NC) {
+ mask = 1U << (BITBUFSIZ - 1 - 12);
+ do {
+ if (bitbuf & mask) j = right[j];
+ else j = left [j];
+ mask >>= 1;
+ } while (j >= NC);
+ }
+ fillbuf(c_len[j]);
+ return j;
+}
+
+
+static unsigned short decode_p(void)
+{
+ unsigned short j, mask;
+
+ j = pt_table[bitbuf >> (BITBUFSIZ - 8)];
+ if (j >= NP) {
+ mask = 1U << (BITBUFSIZ - 1 - 8);
+ do {
+ if (bitbuf & mask) j = right[j];
+ else j = left [j];
+ mask >>= 1;
+ } while (j >= NP);
+ }
+ fillbuf(pt_len[j]);
+ if (j != 0) j = (1U << (j - 1)) + getbits(j - 1);
+ return j;
+}
+
+
+static void decode(unsigned short count, unsigned char buffer[])
+{
+ static unsigned short i;
+ unsigned short r, c;
+
+ r = 0;
+ while (--j >= 0) {
+ buffer[r] = buffer[i];
+ i = (i + 1) & (DICSIZ - 1);
+ if (++r == count) return;
+ }
+ for ( ; ; ) {
+ c = decode_c();
+ if (c <= UCHAR_MAX) {
+ buffer[r] = c & UCHAR_MAX;
+ if (++r == count) return;
+ } else {
+ j = c - (UCHAR_MAX + 1 - THRESHOLD);
+ i = (r - decode_p() - 1) & (DICSIZ - 1);
+ while (--j >= 0) {
+ buffer[r] = buffer[i];
+ i = (i + 1) & (DICSIZ - 1);
+ if (++r == count) return;
+ }
+ }
+ }
+}
+
+void lh5_decode(const unsigned char *inp, unsigned char *outp, unsigned long original_size, unsigned long packed_size)
+{
+ unsigned short n;
+ unsigned char *buffer;
+
+ compsize = packed_size;
+ origsize = original_size;
+ in_buf = inp;
+ out_buf = outp;
+
+#if 0
+ fprintf(stderr, "DEBUG: compsize = %ld, origsize = %ld, first 8 bytes of packed data:\n", packed_size, original_size);
+ fprintf(stderr, " %02x %02x %02x %02x %02x %02x %02x %02x \n",
+ *(inp), *(inp+1),*(inp+2),*(inp+3),
+ *(inp+4),*(inp+5),*(inp+6),*(inp+7));
+#endif
+
+ buffer = (unsigned char *) malloc(DICSIZ);
+ if (!buffer) error ("Out of memory");
+
+ bitbuf = 0; subbitbuf = 0; bitcount = 0;
+ fillbuf(BITBUFSIZ);
+ blocksize = 0;
+ j = 0;
+
+ while (origsize != 0) {
+ n = (origsize > DICSIZ) ? DICSIZ : (unsigned short)origsize;
+ decode(n, buffer);
+ memmove(out_buf, buffer, n);
+ out_buf += n;
+ origsize -= n;
+ }
+
+ if (buffer) free (buffer);
+ buffer = NULL;
+}
diff --git a/plugins/vtx/vtx.c b/plugins/vtx/vtx.c
new file mode 100644
index 00000000..836ded0b
--- /dev/null
+++ b/plugins/vtx/vtx.c
@@ -0,0 +1,308 @@
+/*
+ DeaDBeeF - ultimate music player for GNU/Linux systems with X11
+ Copyright (C) 2009 Alexey Yakovenko
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+#include <stdlib.h>
+#include <string.h>
+#include "../../deadbeef.h"
+#include "ayemu.h"
+
+#define min(x,y) ((x)<(y)?(x):(y))
+#define max(x,y) ((x)>(y)?(x):(y))
+
+//#define trace(...) { fprintf (stderr, __VA_ARGS__); }
+#define trace(fmt,...)
+
+static DB_decoder_t plugin;
+static DB_functions_t *deadbeef;
+
+static const char * exts[] = { "vtx", NULL };
+static const char *filetypes[] = { "VTX", NULL };
+
+static ayemu_vtx_t *decoder;
+static ayemu_ay_t ay;
+
+#define AY_FRAME_SIZE 14
+
+static char regs[AY_FRAME_SIZE];
+static int vtx_pos;
+static int left;
+static int rate;
+static int currentsample;
+static int16_t soundbuffer[];
+
+static int
+vtx_init (DB_playItem_t *it) {
+ // prepare to decode the track
+ // return -1 on failure
+
+ size_t sz = 0;
+ char *buf = NULL;
+
+ DB_FILE *fp = deadbeef->fopen (it->fname);
+ if (!fp) {
+ trace ("vtx: failed to open file %s\n", it->fname);
+ return -1;
+ }
+
+ sz = deadbeef->fgetlength (fp);
+ if (sz <= 0) {
+ trace ("vtx: bad file size\n");
+ return -1;
+ }
+
+ buf = malloc (sz);
+ if (!buf) {
+ trace ("vtx: out of memory\n");
+ return -1;
+ }
+ if (deadbeef->fread (buf, 1, sz, fp) != sz) {
+ trace ("vtx: read failed\n");
+ free (buf);
+ return -1;
+ }
+
+ decoder = ayemu_vtx_load (buf, sz);
+ if (!decoder) {
+ trace ("vtx: ayemu_vtx_load failed\n");
+ free (buf);
+ return -1;
+ }
+ trace ("vtx: data=%p, size=%d\n", decoder->regdata, decoder->regdata_size);
+
+ free (buf);
+
+ ayemu_init (&ay);
+ ayemu_set_chip_type (&ay, decoder->chiptype, NULL);
+ ayemu_set_chip_freq(&ay, decoder->chipFreq);
+ ayemu_set_stereo (&ay, decoder->stereo, NULL);
+
+ int samplerate = deadbeef->get_output ()->samplerate ();
+
+ ayemu_set_sound_format (&ay, samplerate, deadbeef->get_output ()->channels (), deadbeef->get_output ()->bitspersample ());
+
+ left = 0;
+ rate = deadbeef->get_output ()->channels () * deadbeef->get_output ()->bitspersample () / 8;
+ vtx_pos = 0;
+ memset (regs, 0, sizeof (regs));
+ plugin.info.bps = deadbeef->get_output ()->bitspersample ();
+ plugin.info.channels = deadbeef->get_output ()->channels ();
+ plugin.info.samplerate = samplerate;
+ plugin.info.readpos = 0;
+ return 0;
+}
+
+static void
+vtx_free (void) {
+ // free everything allocated in _init
+ if (decoder) {
+ ayemu_vtx_free (decoder);
+ decoder = NULL;
+ }
+ ayemu_reset (&ay);
+}
+
+/** Get next 14-bytes frame of AY register data.
+ *
+ * Return value: 1 on success, 0 on eof
+ */
+static int
+ayemu_vtx_get_next_frame (ayemu_vtx_t *vtx, char *regs)
+{
+ int numframes = vtx->regdata_size / AY_FRAME_SIZE;
+ if (vtx_pos++ >= numframes) {
+ return 0;
+ }
+ else {
+ int n;
+ char *p = vtx->regdata + vtx_pos;
+ for(n = 0 ; n < AY_FRAME_SIZE ; n++, p += numframes) {
+ regs[n] = *p;
+ }
+ return 1;
+ }
+}
+
+static int
+vtx_read_int16 (char *bytes, int size) {
+ // try decode `size' bytes
+ // return number of decoded bytes
+ // return 0 on EOF
+ int initsize = size;
+ int donow = 0;
+
+ while (size > 0) {
+ if (left > 0) {
+ donow = (size > left) ? left : size;
+ left -= donow;
+ bytes = ayemu_gen_sound (&ay, (char *)bytes, donow);
+ size -= donow;
+ }
+ else {
+ if ((ayemu_vtx_get_next_frame (decoder, regs) == 0)) {
+ break; // eof
+ }
+ else {
+ // number of samples it current frame
+ left = plugin.info.samplerate / decoder->playerFreq;
+ // mul by rate to get number of bytes;
+ left *= rate;
+ ayemu_set_regs (&ay, regs);
+ donow = 0;
+ }
+ }
+ }
+ currentsample += (initsize - size) / 4;
+ plugin.info.readpos = (float)currentsample / plugin.info.samplerate;
+ return initsize - size;
+}
+
+static int
+vtx_seek_sample (int sample) {
+ // seek to specified sample (frame)
+ // return 0 on success
+ // return -1 on failure
+
+ // get frame
+ int num_frames = decoder->regdata_size / AY_FRAME_SIZE;
+ int samples_per_frame = plugin.info.samplerate / decoder->playerFreq;
+
+ // start of frame
+ vtx_pos = sample / samples_per_frame;
+ if (vtx_pos >= num_frames) {
+ return -1; // eof
+ }
+ // copy register data
+ int n;
+ char *p = decoder->regdata + vtx_pos;
+ for(n = 0 ; n < AY_FRAME_SIZE ; n++, p += num_frames) {
+ regs[n] = *p;
+ }
+ // set number of bytes left in frame
+ left = plugin.info.samplerate / decoder->playerFreq - (sample % samples_per_frame);
+ // mul by rate to get number of bytes
+ left *= rate;
+ currentsample = sample;
+ plugin.info.readpos = (float)currentsample / plugin.info.samplerate;
+
+ return 0;
+}
+
+static int
+vtx_seek (float time) {
+ // seek to specified time in seconds
+ // return 0 on success
+ // return -1 on failure
+ return vtx_seek_sample (time * plugin.info.samplerate);
+}
+
+static DB_playItem_t *
+vtx_insert (DB_playItem_t *after, const char *fname) {
+ // read information from the track
+ // load/process cuesheet if exists
+ // insert track into playlist
+ // return track pointer on success
+ // return NULL on failure
+
+ trace ("vtx_insert %s\n");
+ // load header
+ DB_FILE *fp = deadbeef->fopen (fname);
+ if (!fp) {
+ trace ("vtx: failed to open file\n");
+ return NULL;
+ }
+ char buf[0x4000];
+ size_t sz;
+ sz = deadbeef->fread (buf, 1, sizeof (buf), fp);
+ deadbeef->fclose (fp);
+ if (sz <= 0) {
+ trace ("vtx: failed to read header data from file\n");
+ return NULL;
+ }
+ ayemu_vtx_t *hdr = ayemu_vtx_header (buf, sz);
+ if (!hdr) {
+ trace ("vtx: failed to read header\n");
+ return NULL;
+ }
+ trace ("vtx: datasize: %d\n", hdr->regdata_size);
+
+ DB_playItem_t *it = deadbeef->pl_item_alloc ();
+
+ it->decoder = &plugin;
+ it->fname = strdup (fname);
+ it->filetype = filetypes[0];
+
+ int numframes = hdr->regdata_size / AY_FRAME_SIZE;
+// int totalsamples = numframes * hdr->playerFreq;
+ trace ("vtx: numframes=%d, playerFreq=%d\n", numframes, hdr->playerFreq);
+ deadbeef->pl_set_item_duration (it, (float)numframes / hdr->playerFreq);
+
+ // add metadata
+ deadbeef->pl_add_meta (it, "title", hdr->title);
+ deadbeef->pl_add_meta (it, "artist", hdr->author);
+ deadbeef->pl_add_meta (it, "album", hdr->from);
+
+ ayemu_vtx_free (hdr);
+ after = deadbeef->pl_insert_item (after, it);
+ return after;
+}
+
+static int
+vtx_start (void) {
+ // do one-time plugin initialization here
+ // e.g. starting threads for background processing, subscribing to events, etc
+ // return 0 on success
+ // return -1 on failure
+ return 0;
+}
+static int
+vtx_stop (void) {
+ // undo everything done in _start here
+ // return 0 on success
+ // return -1 on failure
+ return 0;
+}
+// define plugin interface
+static DB_decoder_t plugin = {
+ DB_PLUGIN_SET_API_VERSION
+ .plugin.version_major = 0,
+ .plugin.version_minor = 1,
+ .plugin.type = DB_PLUGIN_DECODER,
+ .plugin.name = "VTX decoder",
+ .plugin.descr = "AY8910/12 chip emulator and vtx file player",
+ .plugin.author = "Alexey Yakovenko",
+ .plugin.email = "waker@users.sourceforge.net",
+ .plugin.website = "http://deadbeef.sf.net",
+ .plugin.start = vtx_start,
+ .plugin.stop = vtx_stop,
+ .init = vtx_init,
+ .free = vtx_free,
+ .read_int16 = vtx_read_int16,
+// .read_float32 = vtx_read_float32,
+ .seek = vtx_seek,
+ .seek_sample = vtx_seek_sample,
+ .insert = vtx_insert,
+ .exts = exts,
+ .id = "vtx",
+ .filetypes = filetypes
+};
+
+DB_plugin_t *
+vtx_load (DB_functions_t *api) {
+ deadbeef = api;
+ return DB_PLUGIN (&plugin);
+}
diff --git a/plugins/vtx/vtxfile.c b/plugins/vtx/vtxfile.c
new file mode 100644
index 00000000..9dbeb9f7
--- /dev/null
+++ b/plugins/vtx/vtxfile.c
@@ -0,0 +1,283 @@
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "ayemu.h"
+
+#define AYEMU_VTX_STRING_MAX 254
+
+typedef const char * data_ptr_t;
+
+
+/* LHA5 decoder, defined in lh5dec.c */
+extern void lh5_decode(unsigned const char *inp,
+ unsigned char *outp,
+ unsigned long original_size,
+ unsigned long packed_size);
+
+/* Read 8-bit integer from file.
+ */
+static data_ptr_t read_byte(data_ptr_t pos, int *p)
+{
+ const unsigned char *data = (const unsigned char*)pos;
+ *p = *data++;
+ return (data_ptr_t)data;
+}
+
+/* Read 16-bit little-endian 1234 integer from file.
+ */
+static data_ptr_t read_word16(data_ptr_t pos, int *p)
+{
+ const unsigned char *data = (const unsigned char*)pos;
+ *p = *data++;
+ *p += *data++ << 8;
+ return (data_ptr_t)data;
+}
+
+/* read 32-bit integer from file.
+ */
+static data_ptr_t read_word32(data_ptr_t pos, int *p)
+{
+ const unsigned char *data = (const unsigned char*)pos;
+ *p = *data++;
+ *p += *data++ << 8;
+ *p += *data++ << 16;
+ *p += *data++ << 24;
+ return (data_ptr_t)data;
+}
+
+/* read_string: max 254 chars len (+1 for null terminator).
+ */
+static data_ptr_t read_string(data_ptr_t pos, char **res)
+{
+ int len;
+
+ if (pos == NULL)
+ return NULL;
+
+ len = strlen(pos);
+
+ if (len > AYEMU_VTX_STRING_MAX) {
+ fprintf(stderr, "Error: string len more than %d (=%d)\n", AYEMU_VTX_STRING_MAX, len);
+ return NULL;
+ }
+
+ *res = calloc(1, len + 1);
+
+ strcpy(*res, pos);
+
+ return pos + len + 1;
+}
+
+static data_ptr_t read_header(data_ptr_t buf, ayemu_vtx_t **target, size_t size)
+{
+ char hdr[3];
+ ayemu_vtx_t *vtx;
+
+ hdr[0] = tolower(*buf++);
+ hdr[1] = tolower(*buf++);
+ hdr[2] = '\0';
+
+ if (size < 20) {
+ fprintf(stderr, "ayemu_vtx_open: file size < 20 bytes - it is impossible\n");
+ return NULL;
+ }
+
+ vtx = (ayemu_vtx_t*) calloc(1, sizeof(ayemu_vtx_t));
+
+ if (strncmp(hdr, "ay", 2) == 0)
+ vtx->chiptype = AYEMU_AY;
+ else if (strncmp (hdr, "ym", 2) == 0)
+ vtx->chiptype = AYEMU_YM;
+ else {
+ fprintf (stderr, "File is _not_ VORTEX format!\n"
+ "It not begins with 'ay' or 'ym' string.\n");
+ goto clean_and_ret_null;
+ }
+
+ buf = read_byte(buf, &vtx->stereo);
+ buf = read_word16(buf, &vtx->loop);
+ buf = read_word32(buf, &vtx->chipFreq);
+ buf = read_byte(buf, &vtx->playerFreq);
+ buf = read_word16(buf, &vtx->year);
+ buf = read_word32(buf, &vtx->regdata_size);
+
+ buf = read_string(buf, &vtx->title);
+ buf = read_string(buf, &vtx->author);
+ buf = read_string(buf, &vtx->from);
+ buf = read_string(buf, &vtx->tracker);
+ buf = read_string(buf, &vtx->comment);
+
+ *target = vtx;
+ return buf;
+
+ clean_and_ret_null:
+
+ ayemu_vtx_free(vtx);
+ return NULL;
+}
+
+
+ayemu_vtx_t * ayemu_vtx_header(data_ptr_t buf, size_t size)
+{
+ ayemu_vtx_t *vtx;
+
+ const char *data;
+
+ data = read_header(buf, &vtx, size);
+
+ return vtx;
+}
+
+
+ayemu_vtx_t * ayemu_vtx_load(data_ptr_t buf, size_t size)
+{
+ ayemu_vtx_t *vtx;
+
+ const char *data = read_header(buf, &vtx, size);
+
+ if (! data) {
+ fprintf(stderr, "ayemu_vtx_load: Cannot parse file header\n");
+ return NULL;
+ }
+
+ // unpack data
+ size -= (buf - data);
+
+ if ((vtx->regdata = (unsigned char *) malloc (vtx->regdata_size)) == NULL) {
+ fprintf (stderr, "ayemu_vtx_load_data: Can allocate %d bytes"
+ " for unpack register data\n", vtx->regdata_size);
+ return NULL;
+ }
+
+ lh5_decode ((unsigned char*)data, vtx->regdata, vtx->regdata_size, size);
+
+ vtx->frames = vtx->regdata_size / 14;
+
+ return vtx;
+}
+
+
+/** Get specified data frame by number.
+ *
+ */
+void ayemu_vtx_getframe(const ayemu_vtx_t *vtx, size_t frame_n, ayemu_ay_reg_frame_t regs)
+{
+ int n;
+
+ if (frame_n >= vtx->frames)
+ return;
+
+ // calculate begin of data
+ unsigned char *p = vtx->regdata + frame_n;
+
+ // step is data size / 14
+ for(n = 0 ; n < 14 ; n++) {
+ regs[n] = *p;
+ p += vtx->frames;
+ }
+}
+
+/** Free all allocaded resources.
+ *
+ */
+void ayemu_vtx_free(ayemu_vtx_t *vtx)
+{
+#define FREE_PTR(x) if (x) { free(x); x = NULL; }
+
+ FREE_PTR(vtx->title);
+ FREE_PTR(vtx->author);
+ FREE_PTR(vtx->from);
+ FREE_PTR(vtx->tracker);
+ FREE_PTR(vtx->comment);
+ FREE_PTR(vtx->regdata);
+}
+
+
+
+
+ayemu_vtx_t * ayemu_vtx_header_from_file(const char *filename)
+{
+ ayemu_vtx_t *ret;
+ size_t size;
+ const size_t page_size = (size_t) sysconf (_SC_PAGESIZE);
+ int fd;
+ struct stat st;
+
+ // printf("Page size is %d\n", page_size);
+
+ if (stat(filename, &st) != 0) {
+ fprintf(stderr, "Can't stat file %s: %s\n", filename, strerror(errno));
+ return NULL;
+ }
+ size = st.st_size;
+
+ fd = open(filename, O_RDONLY, 0);
+ if (fd == 0) {
+ fprintf(stderr, "Can't open file %s: %s\n", filename, strerror(errno));
+ return NULL;
+ }
+
+ size_t data_len = (size / page_size + 1) * page_size;
+
+ char *data = mmap(NULL, data_len, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (data == (void*)(-1)) {
+ fprintf(stderr, "Can't mmap file %s: %s\n", filename, strerror(errno));
+ return NULL;
+ }
+
+ ret = ayemu_vtx_header(data, size);
+
+ if (munmap(data, data_len) != 0) {
+ fprintf(stderr, "Can't munmmap file %s: %s\n", filename, strerror(errno));
+ }
+
+ return ret;
+}
+
+
+ayemu_vtx_t * ayemu_vtx_load_from_file(const char *filename)
+{
+ size_t size;
+ const size_t page_size = (size_t) sysconf (_SC_PAGESIZE);
+ int fd;
+ struct stat st;
+ ayemu_vtx_t *ret;
+
+ // printf("Page size is %d\n", page_size);
+
+ if (stat(filename, &st) != 0) {
+ fprintf(stderr, "Can't stat file %s: %s\n", filename, strerror(errno));
+ return NULL;
+ }
+ size = st.st_size;
+
+ fd = open(filename, O_RDONLY, 0);
+ if (fd == 0) {
+ fprintf(stderr, "Can't open file %s: %s\n", filename, strerror(errno));
+ return NULL;
+ }
+
+ size_t data_len = (size / page_size + 1) * page_size;
+
+ char *data = mmap(NULL, data_len, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (data == (void*)(-1)) {
+ fprintf(stderr, "Can't mmap file %s: %s\n", filename, strerror(errno));
+ return NULL;
+ }
+
+ ret = ayemu_vtx_load(data, size);
+
+ if (munmap(data, data_len) != 0) {
+ fprintf(stderr, "Can't munmmap file %s: %s\n", filename, strerror(errno));
+ }
+
+ return ret;
+}
diff --git a/plugins/wavpack/wavpack.c b/plugins/wavpack/wavpack.c
index cdb26776..44aec9ea 100644
--- a/plugins/wavpack/wavpack.c
+++ b/plugins/wavpack/wavpack.c
@@ -160,6 +160,9 @@ wv_read_int16 (char *bytes, int size) {
n--;
}
plugin.info.readpos = (float)(WavpackGetSampleIndex (wvctx.ctx)-wvctx.startsample)/WavpackGetSampleRate (wvctx.ctx);
+
+ deadbeef->streamer_set_bitrate (WavpackGetInstantBitrate (wvctx.ctx) / 1000);
+
return size;
}
@@ -188,6 +191,7 @@ wv_read_float32 (char *bytes, int size) {
n--;
}
plugin.info.readpos = (float)(WavpackGetSampleIndex (wvctx.ctx)-wvctx.startsample)/WavpackGetSampleRate (wvctx.ctx);
+ deadbeef->streamer_set_bitrate (WavpackGetInstantBitrate (wvctx.ctx) / 1000);
return size;
}
diff --git a/session.h b/session.h
index 06d0e407..363f4962 100644
--- a/session.h
+++ b/session.h
@@ -27,11 +27,4 @@ session_save (const char *fname);
int
session_load (const char *fname);
-void
-session_capture_window_attrs (uintptr_t window);
-
-void
-session_restore_window_attrs (uintptr_t window);
-
-
#endif // __SESSION_H
diff --git a/streamer.c b/streamer.c
index ecfa8769..88e02386 100644
--- a/streamer.c
+++ b/streamer.c
@@ -78,7 +78,8 @@ static int badsong = -1;
static float seekpos = -1;
static float playpos = 0; // play position of current song
-static float avg_bitrate = -1; // avg bitrate of current song
+static int avg_bitrate = -1; // avg bitrate of current song
+static int last_bitrate = -1; // last bitrate of current song
static int prevtrack_samplerate = -1;
@@ -98,9 +99,17 @@ streamer_get_streaming_track (void) {
return orig_streaming_song;
}
+playItem_t *
+streamer_get_playing_track (void) {
+ return &str_playing_song;
+}
+
// playlist must call that whenever item was removed
void
streamer_song_removed_notify (playItem_t *it) {
+ if (!mutex) {
+ return; // streamer is not running
+ }
plug_trigger_event (DB_EV_TRACKDELETED, (uintptr_t)it);
if (it == orig_playing_song) {
orig_playing_song = NULL;
@@ -124,10 +133,9 @@ streamer_set_current (playItem_t *it) {
to = it ? pl_get_idx_of (it) : -1;
if (!orig_playing_song || p_isstopped ()) {
playlist_current_ptr = it;
+ //trace ("from=%d, to=%d\n", from, to);
+ //messagepump_push (M_SONGCHANGED, 0, from, to);
}
- trace ("from=%d, to=%d\n", from, to);
- trace ("sending songchanged\n");
- messagepump_push (M_SONGCHANGED, 0, from, to);
trace ("streamer_set_current %p, buns=%d\n", it);
if(str_streaming_song.decoder) {
str_streaming_song.decoder->free ();
@@ -212,14 +220,16 @@ streamer_get_playpos (void) {
return playpos;
}
-float
-streamer_get_bitrate (void) {
- return avg_bitrate;
+void
+streamer_set_bitrate (int bitrate) {
+ if (bytes_until_next_song <= 0) { // prevent next track from resetting current playback bitrate
+ last_bitrate = bitrate;
+ }
}
-void
-streamer_update_bitrate (float bitrate) {
- avg_bitrate = bitrate;
+int
+streamer_get_apx_bitrate (void) {
+ return avg_bitrate;
}
void
@@ -257,7 +267,7 @@ static int
streamer_read_async (char *bytes, int size);
void
-streamer_thread (uintptr_t ctx) {
+streamer_thread (void *ctx) {
prctl (PR_SET_NAME, "deadbeef-stream", 0, 0, 0, 0);
codecleft = 0;
@@ -279,6 +289,16 @@ streamer_thread (uintptr_t ctx) {
continue;
}
playItem_t *try = pl_get_for_idx (sng);
+ if (!try) { // track is not in playlist
+ trace ("track #%d is not in playlist; stopping playback\n", sng);
+ p_stop ();
+ pl_item_free (&str_playing_song);
+ pl_item_free (&str_streaming_song);
+ orig_playing_song = NULL;
+ orig_streaming_song = NULL;
+ messagepump_push (M_SONGCHANGED, 0, -1, -1);
+ continue;
+ }
int ret = streamer_set_current (try);
if (ret < 0) {
trace ("failed to play track %s, skipping...\n", try->fname);
@@ -297,6 +317,8 @@ streamer_thread (uintptr_t ctx) {
p_stop ();
}
else if (pstate == 1) {
+ last_bitrate = -1;
+ avg_bitrate = -1;
p_play ();
}
else if (pstate == 2) {
@@ -313,7 +335,6 @@ streamer_thread (uintptr_t ctx) {
trace ("sending songfinished to plugins [1]\n");
plug_trigger_event (DB_EV_SONGFINISHED, 0);
}
-// messagepump_push (M_SONGCHANGED, 0, pl_get_idx_of (orig_playing_song), -1);
streamer_set_current (NULL);
pl_item_free (&str_playing_song);
orig_playing_song = NULL;
@@ -330,7 +351,6 @@ streamer_thread (uintptr_t ctx) {
// means last song was deleted during final drain
nextsong = -1;
p_stop ();
-// messagepump_push (M_SONGCHANGED, 0, pl_get_idx_of (playlist_current_ptr), -1);
streamer_set_current (NULL);
continue;
}
@@ -350,6 +370,8 @@ streamer_thread (uintptr_t ctx) {
pl_item_free (&str_playing_song);
// copy streaming into playing
pl_item_copy (&str_playing_song, &str_streaming_song);
+ last_bitrate = -1;
+ avg_bitrate = -1;
orig_playing_song = orig_streaming_song;
if (orig_playing_song) {
orig_playing_song->played = 1;
@@ -362,10 +384,9 @@ streamer_thread (uintptr_t ctx) {
trace ("sending songstarted to plugins\n");
plug_trigger_event (DB_EV_SONGSTARTED, 0);
playpos = 0;
- avg_bitrate = -1;
// change samplerate
if (prevtrack_samplerate != str_playing_song.decoder->info.samplerate) {
- palsa_change_rate (str_playing_song.decoder->info.samplerate);
+ plug_get_output ()->change_rate (str_playing_song.decoder->info.samplerate);
prevtrack_samplerate = str_playing_song.decoder->info.samplerate;
}
}
@@ -376,6 +397,7 @@ streamer_thread (uintptr_t ctx) {
seekpos = -1;
if (orig_playing_song != orig_streaming_song) {
+ trace ("streamer already switched to next track\n");
// restart playing from new position
if(str_streaming_song.decoder) {
str_streaming_song.decoder->free ();
@@ -384,6 +406,14 @@ streamer_thread (uintptr_t ctx) {
orig_streaming_song = orig_playing_song;
pl_item_copy (&str_streaming_song, orig_streaming_song);
bytes_until_next_song = -1;
+ int ret = str_streaming_song.decoder->init (DB_PLAYITEM (orig_streaming_song));
+ if (ret < 0) {
+ trace ("failed to restart prev track on seek, trying to jump to next track\n");
+ pl_nextsong (0);
+ trace ("pl_nextsong switched to track %d\n", nextsong);
+ usleep (50000);
+ continue;
+ }
}
streamer_buffering = 1;
@@ -401,6 +431,8 @@ streamer_thread (uintptr_t ctx) {
if (str_playing_song.decoder->seek (pos) >= 0) {
playpos = str_playing_song.decoder->info.readpos;
}
+ last_bitrate = -1;
+ avg_bitrate = -1;
streamer_unlock();
}
}
@@ -434,6 +466,7 @@ streamer_thread (uintptr_t ctx) {
alloc_time -= ms;
if (alloc_time > 0) {
usleep (alloc_time * 1000);
+// usleep (1000);
}
// trace ("fill: %d/%d\n", streambuffer_fill, STREAM_BUFFER_SIZE);
}
@@ -461,7 +494,7 @@ streamer_init (void) {
if (!src) {
return -1;
}
- streamer_tid = thread_start (streamer_thread, 0);
+ streamer_tid = thread_start (streamer_thread, NULL);
return 0;
}
@@ -470,7 +503,9 @@ streamer_free (void) {
streaming_terminate = 1;
thread_join (streamer_tid);
mutex_free (decodemutex);
+ decodemutex = 0;
mutex_free (mutex);
+ mutex = 0;
}
void
@@ -757,7 +792,12 @@ streamer_read_async (char *bytes, int size) {
if (bytes_until_next_song < 0) {
trace ("finished streaming song, queueing next\n");
bytes_until_next_song = streambuffer_fill;
- pl_nextsong (0);
+ if (conf_get_int ("playlist.stop_after_current", 0)) {
+ streamer_set_nextsong (-2, 1);
+ }
+ else {
+ pl_nextsong (0);
+ }
}
break;
}
@@ -787,27 +827,6 @@ streamer_read (char *bytes, int size) {
memcpy (bytes, streambuffer, sz);
memmove (streambuffer, streambuffer+sz, streambuffer_fill-sz);
streambuffer_fill -= sz;
-#if 0
- int cp = sz;
- int readpos = streambuffer_pos & STREAM_BUFFER_MASK;
- int part1 = STREAM_BUFFER_SIZE-readpos;
- part1 = min (part1, cp);
- if (part1 > 0) {
- memcpy (bytes, streambuffer+readpos, part1);
- streambuffer_pos += part1;
- streambuffer_fill -= part1;
- cp -= part1;
- bytes += part1;
- }
- if (cp > 0) {
- memcpy (bytes, streambuffer, cp);
- streambuffer_pos += cp;
- streambuffer_fill -= cp;
- bytes += cp;
- }
- assert (streambuffer_fill>=0);
- streambuffer_pos &= STREAM_BUFFER_MASK;
-#endif
playpos += (float)sz/p_get_rate ()/4.f;
str_playing_song.playtime += (float)sz/p_get_rate ()/4.f;
if (playlist_current_ptr) {
@@ -824,6 +843,32 @@ streamer_read (char *bytes, int size) {
}
}
streamer_unlock ();
+
+ // approximate bitrate
+ if (last_bitrate != -1) {
+ if (avg_bitrate == -1) {
+ avg_bitrate = last_bitrate;
+ }
+ else {
+ if (avg_bitrate < last_bitrate) {
+ avg_bitrate += 5;
+ if (avg_bitrate > last_bitrate) {
+ avg_bitrate = last_bitrate;
+ }
+ }
+ else if (avg_bitrate > last_bitrate) {
+ avg_bitrate -= 5;
+ if (avg_bitrate < last_bitrate) {
+ avg_bitrate = last_bitrate;
+ }
+ }
+ }
+// printf ("apx bitrate: %d (last %d)\n", avg_bitrate, last_bitrate);
+ }
+ else {
+ avg_bitrate = -1;
+ }
+
#if 0
struct timeval tm2;
gettimeofday (&tm2, NULL);
@@ -887,3 +932,23 @@ streamer_configchanged (void) {
mutex_unlock (decodemutex);
}
}
+
+void
+streamer_play_current_track (void) {
+ if (p_ispaused ()) {
+ // unpause currently paused track
+ p_unpause ();
+ plug_trigger_event_paused (0);
+ }
+ else if (playlist_current_row[PL_MAIN] != -1) {
+ // play currently selected track
+ p_stop ();
+ streamer_set_nextsong (playlist_current_row[PL_MAIN], 1);
+ }
+ else {
+ // restart currently playing track
+ p_stop ();
+ streamer_set_nextsong (0, 1);
+ }
+}
+
diff --git a/streamer.h b/streamer.h
index 9544c755..526dd161 100644
--- a/streamer.h
+++ b/streamer.h
@@ -60,12 +60,6 @@ streamer_ok_to_read (int len);
float
streamer_get_playpos (void);
-float
-streamer_get_bitrate (void);
-
-void
-streamer_update_bitrate (float bitrate);
-
int
streamer_is_buffering (void);
@@ -75,7 +69,19 @@ streamer_song_removed_notify (playItem_t *it);
playItem_t *
streamer_get_streaming_track (void);
+playItem_t *
+streamer_get_playing_track (void);
+
void
streamer_configchanged (void);
+void
+streamer_play_current_track (void);
+
+void
+streamer_set_bitrate (int bitrate);
+
+int
+streamer_get_apx_bitrate (void);
+
#endif // __STREAMER_H
diff --git a/threading.h b/threading.h
index 2e986b59..51a72f74 100644
--- a/threading.h
+++ b/threading.h
@@ -21,7 +21,7 @@
#include <stdint.h>
intptr_t
-thread_start (void (*fn)(uintptr_t ctx), uintptr_t ctx);
+thread_start (void (*fn)(void *ctx), void *ctx);
int
thread_join (intptr_t tid);
diff --git a/threading_pthread.c b/threading_pthread.c
index 942ddbfe..3ef22b26 100644
--- a/threading_pthread.c
+++ b/threading_pthread.c
@@ -21,7 +21,7 @@
#include "threading.h"
intptr_t
-thread_start (void (*fn)(uintptr_t ctx), uintptr_t ctx) {
+thread_start (void (*fn)(void *ctx), void *ctx) {
pthread_t tid;
pthread_attr_t attr;
int s = pthread_attr_init (&attr);
diff --git a/timeline.c b/timeline.c
index c9a40fb5..6126fa1d 100644
--- a/timeline.c
+++ b/timeline.c
@@ -68,7 +68,7 @@ timeline_stop (timeline_t *tl, int wait) {
}
void
-timeline_thread_func (uintptr_t ctx) {
+timeline_thread_func (void *ctx) {
printf ("timeline thread started\n");
timeline_t *tl = (timeline_t *)ctx;
@@ -113,7 +113,7 @@ timeline_start (timeline_t *tl) {
tl->stop = 0;
tl->destroy = 0;
if (!tl->tid) {
- tl->tid = thread_start (timeline_thread_func, (uintptr_t)tl);
+ tl->tid = thread_start (timeline_thread_func, tl);
}
else {
printf ("reusing existing thread\n");
diff --git a/vfs.c b/vfs.c
index 23b1628d..384b3aef 100644
--- a/vfs.c
+++ b/vfs.c
@@ -21,8 +21,8 @@
#include "vfs.h"
#include "plugins.h"
-#define trace(...) { fprintf(stderr, __VA_ARGS__); }
-//#define trace(fmt,...)
+//#define trace(...) { fprintf(stderr, __VA_ARGS__); }
+#define trace(fmt,...)
DB_FILE *
vfs_fopen (const char *fname) {
diff --git a/web/index.html b/web/index.html
index 46eb041d..2863a8ee 100644
--- a/web/index.html
+++ b/web/index.html
@@ -403,7 +403,7 @@ implemented basic session management, window size/position, volume, playmode are
<p>official jabber conference for russian speaking users is here: deadbeef-ru@conference.jabber.ru</p>
<p>email: <a href="mailto:waker@users.sourceforge.net">Alexey Yakovenko</a></p>
<h1 id='screenshots'>Screenshots</h1>
-<img src="https://sourceforge.net/dbimage.php?id=228167" alt="main window, version 0.1.0"/>
+<img src="screenshot02.png" alt="main window, version 0.1.0"/>
<hr/>
<p><a href="http://sourceforge.net/">Project Web Hosted by<img src="http://sflogo.sourceforge.net/sflogo.php?group_id=272657&amp;type=3" alt="SourceForge.net"/></a></p>