summaryrefslogtreecommitdiff
path: root/libmodplay
diff options
context:
space:
mode:
authorXavi Del Campo <xavi.dcr@tutanota.com>2020-01-31 10:32:23 +0100
committerXavi Del Campo <xavi.dcr@tutanota.com>2020-01-31 10:32:23 +0100
commit7c24e9a9b02b04dcaf9507acb94091ea70a2c02d (patch)
treec28d0748652ad4b4222309e46e6cfc82c0906220 /libmodplay
parenta2b7b6bb1cc2f4a3258b7b2dbc92399d151f864d (diff)
downloadpsxsdk-7c24e9a9b02b04dcaf9507acb94091ea70a2c02d.tar.gz
Imported pristine psxsdk-20190410 from official repo
Diffstat (limited to 'libmodplay')
-rw-r--r--libmodplay/Makefile38
-rw-r--r--libmodplay/mod.c328
-rw-r--r--libmodplay/modplay.c290
-rw-r--r--libmodplay/modplay.h222
-rw-r--r--libmodplay/modplay_int.h20
-rw-r--r--libmodplay/modtbl.h62
6 files changed, 960 insertions, 0 deletions
diff --git a/libmodplay/Makefile b/libmodplay/Makefile
new file mode 100644
index 0000000..af4a8e5
--- /dev/null
+++ b/libmodplay/Makefile
@@ -0,0 +1,38 @@
+# Makefile for libmodplay
+
+include ../Makefile.cfg
+
+CFLAGS += -I../libadpcm
+
+all: libmodplay.a libmodplay_nopsx.a
+
+modplay.o: modplay.c
+ $(CC) $(CFLAGS) -c modplay.c
+
+mod.o: mod.c
+ $(CC) $(CFLAGS) -c mod.c
+
+libmodplay.a: modplay.o mod.o
+ rm -f libmodplay.a
+ $(AR) r libmodplay.a modplay.o mod.o
+ $(RANLIB) libmodplay.a
+
+modplay_nopsx.o: modplay.c
+ $(HOST_CC) $(HOST_CFLAGS) -DNO_PSX_LIB -c modplay.c -o modplay_nopsx.o
+
+mod_nopsx.o: mod.c
+ $(HOST_CC) $(HOST_CFLAGS) -DNO_PSX_LIB -c mod.c -o mod_nopsx.o
+
+libmodplay_nopsx.a: modplay_nopsx.o mod_nopsx.o
+ rm -f libmodplay_nopsx.a
+ $(HOST_AR) r libmodplay_nopsx.a modplay_nopsx.o mod_nopsx.o
+ $(HOST_RANLIB) libmodplay_nopsx.a
+
+install: all
+ cp libmodplay.a $(TOOLCHAIN_PREFIX)/lib
+ cp modplay.h $(TOOLCHAIN_PREFIX)/include
+
+clean:
+ rm -f *.o *.a
+
+distclean: clean
diff --git a/libmodplay/mod.c b/libmodplay/mod.c
new file mode 100644
index 0000000..3b569a6
--- /dev/null
+++ b/libmodplay/mod.c
@@ -0,0 +1,328 @@
+// Ultimate SoundTracker / NoiseTracker / ProTracker module file support for MODPlay
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "modplay.h"
+#include "modplay_int.h"
+#include "modtbl.h" // Period -> frequency table for ProTracker MODs
+
+ModMusic *MODLoad_MOD(void *d)
+{
+ unsigned char *c = d;
+ ModMusic *m;
+ int x;
+ int mp=0;
+ int y;
+
+// Allocate memory for mod structure
+ m = (ModMusic*)malloc(sizeof(ModMusic));
+
+// Get title
+ memcpy(m->title, &c[0], 20);
+
+// For now let's assume there are 32 samples...
+ mp+=20;
+
+ memcpy(m->id, &c[0x438], 4);
+
+// If there is not a valid ID, this is the start of pattern data,
+// otherwise jump four bytes
+
+ if(strncmp(m->id, "M.K.",4) == 0 || strncmp(m->id, "FLT4", 4) == 0 ||
+ strncmp(m->id, "M!K!",4) == 0 || strncmp(m->id, "4CHN", 4) == 0 ||
+ strncmp(m->id, "6CHN", 4) == 0 || strncmp(m->id, "8CHN", 4) == 0)
+ m->sample_num = 31;
+ else
+ m->sample_num = 15;
+
+// Standard channel number is 4
+
+ if(strncmp(m->id, "6CHN", 4) == 0)
+ m->channel_num = 6;
+ else if(strncmp(m->id, "8CHN", 4) == 0)
+ m->channel_num = 8;
+ else
+ m->channel_num = 4;
+
+// Get sample information
+
+ m->sample = malloc(sizeof(ModSample) * m->sample_num);
+
+ for(x = 0; x < m->sample_num; x++)
+ {
+ // Get sample name
+ memcpy(m->sample[x].name, &c[mp], 22);
+ mp+=22;
+ // Get sample length
+ m->sample[x].length = (c[mp] << 8) | c[mp+1];
+ m->sample[x].length*= 2;
+ mp+=2;
+ // Get finetune value
+ m->sample[x].finetune = c[mp] & 0xf;
+
+ if(m->sample[x].finetune & 0x8)
+ m->sample[x].finetune|=0xf0;
+
+ mp++;
+ // Get sample volume
+ m->sample[x].volume = c[mp];
+ mp++;
+
+ // Get sample repeat offset
+ m->sample[x].repeat_off = (c[mp] << 8) | c[mp+1];
+ m->sample[x].repeat_off *= 2;
+ mp+=2;
+
+ // Get sample repeat length
+ m->sample[x].repeat_len = (c[mp] << 8) | c[mp+1];
+ m->sample[x].repeat_len *= 2;
+ mp+=2;
+
+ // Samples are always 8-bit
+ m->sample[x].bits = 8;
+
+ // Data type is always 0
+ m->sample[x].data_type = 0;
+ }
+
+// Get number of song positons
+ m->song_pos_num = c[mp++];
+
+// Ignore this value...
+ mp++;
+
+// Get pattern table
+ memcpy(m->pattern_tbl, &c[mp], 128);
+ mp+=128;
+
+
+
+// Get ID (it is not assured that this value will be valid)
+ memcpy(m->id, &c[0x438], 4);
+// If there is not a valid ID, this is the start of pattern data,
+// otherwise jump four bytes
+
+ if(strncmp(m->id, "M.K.",4) == 0 || strncmp(m->id, "FLT4", 4) == 0 ||
+ strncmp(m->id, "M!K!",4) == 0 || strncmp(m->id, "4CHN", 4) == 0 ||
+ strncmp(m->id, "6CHN", 4) == 0 || strncmp(m->id, "8CHN", 4) == 0)
+ mp+=4;
+
+// Get number of patterns
+// This is actually done by scanning the pattern table for the highest value
+ y = 0;
+
+ for(x=0;x<128;x++)
+ {
+ //printf("%x, \n", m->pattern_tbl[x]);
+ if(m->pattern_tbl[x] > y)
+ y = m->pattern_tbl[x];
+ }
+
+ //printf("\n");
+
+ m->pattern_num = y+1;
+
+// Pattern row sizes are always 64
+ for(x = 0; x < m->pattern_num; x++)
+ m->pattern_row_num[x] = 64;
+
+// Allocate memory for patterns...
+ m->pattern_data = malloc(m->pattern_num * ((4*m->channel_num)*64));
+
+// Get pattern data
+ memcpy(m->pattern_data,&c[mp], m->pattern_num * ((4*m->channel_num)*64));
+ mp += m->pattern_num * ((4*m->channel_num)*64);
+
+// Allocate & Get sample data
+ for(x = 0; x < m->sample_num; x++)
+ {
+ if(m->sample[x].length < 32 || (modload_flags & MODLOAD_NOSAMPLES))
+ m->sample[x].data = NULL;
+ else
+ {
+ m->sample[x].data = malloc(m->sample[x].length);
+ memcpy(m->sample[x].data, &c[mp], m->sample[x].length);
+
+ // Convert to unsigned 8-bit format
+ // Most sound cards/programs nowadays want data in this format
+ for(y = 0; y < m->sample[x].length; y++)
+ m->sample[x].data[y] ^= 0x80;
+ }
+
+ mp += m->sample[x].length;
+ }
+
+ m->song_pos = 0;
+ m->pat_pos = 0;
+ m->divisions_sec = 7;
+ m->beats_minute = 125;
+ m->ticks_division = 6;
+
+ for(x = 0; x<8;x++)
+ {
+ m->old_samples[x] = 1;
+ m->old_periods[x] = 0;
+ }
+
+ m->cur_tick = 0;
+ m->fmt = MOD_FMT_MOD;
+// MOD has no instruments!
+ m->instrument_num = 0;
+
+ return m;
+}
+
+void MODPlay_MOD(ModMusic *m,int *t)
+{
+ int cur_pat = m->pattern_tbl[m->song_pos];
+ int cur_pat_pos = m->pat_pos;
+ unsigned char b[4];
+ int s, p, e,x,y;
+ int do_not_increase_pat = 0;
+ int v1,f;
+
+ if(*t == 0)
+ return;
+
+
+ m->cur_tick++;
+
+ if(m->cur_tick != (50 / m->divisions_sec))
+ return;
+
+ for(x = 0; x < m->channel_num; x++)
+ {
+ memcpy(b, &m->pattern_data[(cur_pat * ((4*m->channel_num)*64)) + (cur_pat_pos * (4*m->channel_num)) + (x*4)], 4);
+
+ // Get sample
+ s = (b[2] & 0xf0)>>4;
+ s |= b[0] & 0xf0;
+
+ // Get period
+ p = b[1];
+ p|= (b[0] & 0xf)<<8;
+ p&=~(2048|1024);
+
+ // Get effect
+ e = b[3];
+ e|= (b[2] & 0xf)<<8;
+
+ if(s != 0 && p==0)
+ p = m->old_periods[x];
+
+ if(s == 0 && p != 0)
+ s=m->old_samples[x];
+
+ v1 = m->sample[s-1].volume;
+
+ switch(e & 0xf00)
+ {
+ case 0xc00: // Set volume
+ v1 = e & 0xff;
+ break;
+ }
+
+ f = -1;
+
+ for(y = 0; y < sizeof(modplay_pitch_per_tbl) / 4; y++)
+ {
+ if(modplay_pitch_per_tbl[y<<1] == p)
+ {
+ f = modplay_pitch_per_tbl[(y<<1)+1];
+ break;
+ }
+ }
+
+ if(f==-1 && p!=0)
+ {
+ printf("Couldn't find period %d in table. Calculating it.\n", p);
+ f = SsFreqToPitch(7159090/(p*2));
+ }
+
+ f+=m->transpose;
+
+ if(f<0)f=0;
+ else if(f>0x3fff)f=0x3fff;
+
+ v1 <<= 8;
+
+ if(v1 >= 0x4000)
+ v1 = 0x3fff;
+
+
+ if(s && p!=0)
+ {
+ if(x == 0 || x == 3 || x == 4 || x == 7)
+ MODPlay_func(m, x, s-1, f, v1, 0);
+ else
+ MODPlay_func(m, x, s-1, f, 0, v1);
+ }
+
+ switch(e & 0xf00)
+ {
+ case 0xb00: // Position Jump
+ m->song_pos = e & 0xff;
+ m->pat_pos = 0;
+ // printf("Jump to song pos %d\n", m->song_pos);
+
+ // this fixes some mods which jump over the mod itself
+
+ if(m->song_pos >= m->song_pos_num)
+ m->song_pos = 0;
+
+ do_not_increase_pat = 1;
+ break;
+ case 0xd00: // Pattern break
+ m->song_pos++;
+ m->pat_pos = (((e&0xf0)>>4)*10)+(e&0xf);
+ // printf("Pattern break, newpatpos=%d\n", m->pat_pos);
+
+ // this fixes some mods which jump over themselves
+
+ if(m->song_pos >= m->song_pos_num)
+ m->song_pos = 0;
+
+ do_not_increase_pat = 1;
+ break;
+ case 0xf00: // Tempo
+ /*v1 = (e & 0xf0) >> 4;
+ v2 = e & 0xf;
+ v3 = (v1*16)+v2;*/
+
+ if((e & 0xff) <= 32)
+ {
+ if((e&0xff) == 0)e++;
+ m->ticks_division = e & 0xff;
+ }
+ else
+ m->beats_minute = e & 0xff;
+
+ m->divisions_sec = 24 * m->beats_minute;
+ m->divisions_sec /= m->ticks_division;
+ m->divisions_sec /= 60;
+ break;
+ }
+
+ if(s) m->old_samples[x] = s;
+ if(p) m->old_periods[x] = p;
+
+ m->cur_tick = 0;
+ }
+
+ if(!do_not_increase_pat)m->pat_pos++;
+
+ if(m->pat_pos == 64)
+ {
+ m->song_pos++;
+ if(m->song_pos >= m->song_pos_num)
+ {
+ *t-=1;
+
+ MODRewind(m);
+ }
+
+ m->pat_pos = 0;
+ }
+}
diff --git a/libmodplay/modplay.c b/libmodplay/modplay.c
new file mode 100644
index 0000000..9f5f33d
--- /dev/null
+++ b/libmodplay/modplay.c
@@ -0,0 +1,290 @@
+// MODplay for the PS1
+// Music Module Player
+// Supports ProTracker (.mod) module format
+
+// Requires libADPCM!
+
+// If NO_PSX_LIB is defined, no parts using PSXSDK functions are compiled
+// This is useful if you want to use the library to handle module files in tools
+
+#ifndef NO_PSX_LIB
+#include <psx.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+#ifndef NO_PSX_LIB
+#include <adpcm.h>
+#endif
+
+#include "modplay.h"
+#include "modplay_int.h"
+
+// Configuration defines
+
+// Size of ADPCM buffer used by MODUploadSamples when
+// converting 8-bit unsigned PCM samples to PS1 ADPCM format
+// By default it is set to 0x4000, 16 kilobytes
+
+#define ADPCM_BUFFER_SIZE 0x4000
+
+
+
+int modplay_base_voice = 0;
+int modplay_max_vol = 0x3fff;
+int modplay_chan_vols[8];
+int modplay_int_cnt = 0;
+int modplay_samples_off[32];
+int modplay_chan_mask = 0;
+int modplay_is_mono = 0;
+unsigned char modplay_adpcm_buffer[ADPCM_BUFFER_SIZE];
+
+unsigned int modload_flags = 0;
+
+ModMusic *MODLoad(void *d)
+{
+ return MODLoadEx(d, 0);
+}
+
+ModMusic *MODLoadEx(void *d, unsigned int flags)
+{
+ modload_flags = flags;
+
+ // If the module file was in no other format, assume the module file is
+ // in ProTracker format. There's no real way to detect a ProTracker module
+ // file 100% correctly so this will do.
+
+ return MODLoad_MOD(d);
+}
+
+void MODUnload(ModMusic *m)
+{
+ int x;
+
+ MODStop(m);
+
+ switch(m->fmt)
+ {
+ case MOD_FMT_MOD:
+ free(m->pattern_data);
+
+ for(x = 0; x < m->sample_num; x++)
+ {
+ if(m->sample[x].data != NULL)
+ free(m->sample[x].data);
+ }
+
+ free(m->sample);
+
+ free(m);
+ break;
+ }
+
+}
+
+#ifdef NO_PSX_LIB
+void MODPlay_func(ModMusic *m, int c, int s, int p, int vl, int vr)
+{
+ // Just a stub
+}
+#else
+void MODPlay_func(ModMusic *m, int c, int s, int p, int vl, int vr)
+{
+ int v = c + modplay_base_voice;
+// static int mask = 0;
+
+// if(s != -1)
+// {
+ // SsKeyOff(v);
+ if(p != -1)
+ SsVoicePitch(v, p);
+// }
+
+ if(modplay_max_vol != 0x3fff)
+ {
+ vl = (modplay_max_vol * vl) / 0x4000;
+ vr = (modplay_max_vol * vr) / 0x4000;
+ vl&=0x3fff;
+ vr&=0x3fff;
+ }
+
+ if(modplay_is_mono)
+ {
+ if(vl>vr)
+ vr=vl;
+ else
+ vl=vr;
+ }
+
+ SsVoiceVol(v, vl, vr);
+
+ if(s != -1)
+ {
+ if(modplay_samples_off[s] != -1)
+ {
+ SsVoiceStartAddr(v, modplay_samples_off[s]);
+ modplay_chan_mask|=(1<<v);
+ }
+ }
+}
+#endif
+
+void MODPlay(ModMusic *m, int *t)
+{
+ modplay_chan_mask = 0;
+
+ switch(m->fmt)
+ {
+ case MOD_FMT_MOD:
+ MODPlay_MOD(m, t);
+ break;
+ }
+
+ //printf("modplay_chan_mask = %d\n", modplay_chan_mask);
+#ifndef NO_PSX_LIB
+ SsKeyOnMask(modplay_chan_mask);
+#endif
+}
+
+void MODStop(ModMusic *m)
+{
+#ifndef NO_PSX_LIB
+ int mask = 0;
+ int x;
+
+ for(x = 0; x<m->channel_num; x++)
+ mask|=1<<(modplay_base_voice+x);
+
+ SsKeyOffMask(mask);
+#endif
+}
+
+#ifndef NO_PSX_LIB
+int MODUploadSamples(ModMusic *m, int base_addr)
+{
+ int x, b;
+
+ if(base_addr == -1)
+ base_addr = SPU_DATA_BASE_ADDR;
+
+ modplay_samples_off[0] = base_addr;
+
+ for(x = 0; x < m->sample_num; x++)
+ {
+ b = SsAdpcmPack(m->sample[x].data, modplay_adpcm_buffer,
+ m->sample[x].length, FMT_U8, sizeof(modplay_adpcm_buffer), 0);
+ SsUpload(modplay_adpcm_buffer, b, modplay_samples_off[x]);
+
+ if(x!=30)
+ modplay_samples_off[x+1] = modplay_samples_off[x]+b;
+ }
+
+ return modplay_samples_off[x];
+}
+
+int MOD4PSX_Upload(void *d, int base_addr)
+{
+ unsigned char *c = d;
+ int x;
+ int o;
+ int sz;
+ int n;
+ int smpOff;
+
+// Check magic string
+
+ if(strncmp((char*)c, "_mod4psx", 8) != 0)
+ return -1;
+
+ o = 12;
+ n = *((int*)(c+8));
+
+ if(base_addr == -1)
+ smpOff = SPU_DATA_BASE_ADDR;
+ else
+ smpOff = base_addr;
+
+ //smpOff = modplay_samples_off[0];
+
+ printf("Number of samples: %d\n", n);
+
+ for(x = 0; x < n; x++)
+ {
+// Get size
+ sz = *((int*)(c+o));
+ printf("Size: %d\n", sz);
+// Ignore eight reserved bytes (for future expension)
+ o+=12;
+
+ if(sz > 0)
+ {
+ modplay_samples_off[x] = smpOff;
+
+ SsUpload(c+o, sz, modplay_samples_off[x]);
+
+ smpOff+=sz;
+ }
+ else
+ modplay_samples_off[x] = -1;
+
+ o += sz;
+ }
+
+ return modplay_samples_off[x];
+}
+
+#endif
+
+void MODSetBaseVoice(int base_voice)
+{
+ modplay_base_voice = base_voice;
+}
+
+void MODSetMaxVolume(unsigned short max_volume)
+{
+ // Default is 0x3fff.
+ // Valid values 0 (MUTE) - 0x3FFF (MAX)
+
+ modplay_max_vol = max_volume & 0x3fff;
+}
+
+void MODRewind(ModMusic *m)
+{
+ MODStop(m);
+ m->song_pos = 0;
+ m->pat_pos = 0;
+
+ if(m->fmt == MOD_FMT_MOD)
+ {
+ m->divisions_sec = 7;
+ m->beats_minute = 125;
+ m->ticks_division = 6;
+ }
+}
+
+void MODSetTranspose(ModMusic *m, short transpose)
+{
+ m->transpose = transpose;
+}
+
+void MODSetMono(int value)
+{
+ /* Sets mono audio mode
+ left volume = right volume */
+
+ modplay_is_mono = value;
+}
+
+#ifdef NO_PSX_LIB
+
+// Some code might use this, so use a stub.
+
+unsigned short SsFreqToPitch(int hz)
+{
+ return 0;
+}
+
+#endif
diff --git a/libmodplay/modplay.h b/libmodplay/modplay.h
new file mode 100644
index 0000000..0370bc6
--- /dev/null
+++ b/libmodplay/modplay.h
@@ -0,0 +1,222 @@
+#ifndef _MODPLAY_H
+#define _MODPLAY_H
+
+#ifndef NO_PSX_LIB
+ #include <psx.h>
+#endif
+
+/** Format identification IDs. */
+
+enum modplay_formats
+{
+ MOD_FMT_MOD, /** Ultimate SoundTracker / NoiseTracker / ProTracker */
+};
+
+typedef struct
+{
+ char name[32];
+ unsigned int length; // Length in bytes
+ char finetune;
+ unsigned char volume;
+ unsigned int repeat_off;
+ unsigned int repeat_len;
+ unsigned char bits;
+ unsigned char data_type;
+ unsigned char *data;
+}ModSample;
+
+/** Instrument. */
+
+typedef struct
+{
+ char name[64];
+ int sample_num;
+ unsigned char sample_ids[8];
+}ModInstrument;
+
+/** Music */
+
+typedef struct
+{
+ /** Music title. */
+ char title[32];
+ /** Number of samples in the music */
+ int sample_num;
+ /** Number of channels used by the music */
+ int channel_num;
+ /** Number of instruments used by the music */
+ int instrument_num;
+ /** Pointer to an array of ModSample structures. */
+ ModSample *sample;
+ /** Pointer to an array of ModInstrument structures. */
+ ModInstrument *instrument;
+ /** Number of song positions. */
+ unsigned char song_pos_num;
+ /** Pattern table. */
+ unsigned char pattern_tbl[256];
+ /** Number of rows for each pattern. */
+ unsigned char pattern_row_num[256];
+ /** ID, such as "M!K!","M.K.","FLT4", etc. */
+ char id[4];
+ /** Number of patterns. */
+ int pattern_num;
+ /** Pointer to pattern data */
+ unsigned char *pattern_data;
+ /** Format of music. */
+ int fmt;
+
+ /** [Runtime] Current song position */
+ unsigned char song_pos;
+ /** [Runtime] Position inside the pattern currently being played */
+ unsigned char pat_pos;
+ /** [Runtime] Divisions per second */
+ int divisions_sec;
+ /** [Runtime] Beats per minute */
+ unsigned char beats_minute;
+ /** [Runtime] Ticks per division */
+ unsigned char ticks_division;
+ /** [Runtime] Current tick count */
+ unsigned char cur_tick;
+ /** [Runtime] Old periods for each channel. */
+ unsigned short old_periods[8];
+ /** [Runtime] Old sample numbers for each channel. */
+ unsigned char old_samples[8];
+ /** [Runtime] In PlayStation pitch, this is added to the original sample pitch
+ and can be used to change the pitch of the music for special effects */
+ short transpose;
+}ModMusic;
+
+/** Flags for MODLoad */
+enum modload_flags
+{
+ /** Do not load the samples in memory */
+ MODLOAD_NOSAMPLES = 1,
+};
+
+/**
+ * Allocate a ModMusic structure and copy data to it from
+ * data in memory containing a music module file
+ *
+ * Almost all data from the music module file is copied into another location
+ * in memory for the ModMusic structure.
+ *
+ * This means your free memory should be roughly the double of
+ * the size of the MOD you're loading.
+ *
+ * You can avoid loading the samples in the module file's native format,
+ * thus saving useful memory, by passing the MODLOAD_NOSAMPLES
+ * flag. This is especially the case when you use MOD4PSX_Upload().
+ *
+ * @param d Pointer to a buffer containing a music module file
+ * @param flags Flag bitmask.
+ * @return Pointer to newly allocated ModMusic structure.
+ */
+
+ModMusic *MODLoadEx(void *d, unsigned int flags);
+
+/**
+ * Just like MODLoadEx() but with the flags parameters set to zero, i.e.
+ * default behaviour.
+ *
+ * @param d Pointer to a buffer containing a music module file
+ * @return Pointer to newly allocated ModMusic structure.
+ */
+
+ModMusic *MODLoad(void *d);
+
+/**
+ * Play a tick of a music.
+ *
+ * This has to be called 60 / 50 times per second.
+ *
+ * MODPlay decreases the value referenced by t every time
+ * the music finishes.
+ *
+ * Set the variable pointed by t when you want to set the number of times again!
+ *
+ * @param m Pointer to ModMusic structure
+ * @param t Pointer to an int which contains how many times the music module has to be played.
+ * i.e. if *t == 1, play once, if *t == 2, play twice, ..., if *t == -1, loop endlessly
+ *
+ */
+
+void MODPlay(ModMusic *m,int *t);
+
+/**
+ * Stop a music.
+ * @param m Pointer to ModMusic structure for the music.
+ */
+void MODStop(ModMusic *m);
+
+/**
+ * Rewind music, that is, make it restart from the beginning.
+ * @param m Pointer to ModMusic structure for the music.
+ */
+void MODRewind(ModMusic *m);
+
+/**
+ * Upload the samples of the module music to Sound RAM
+ * @param m Pointer to ModMusic structure for the music.
+ * @param base_addr Sound RAM address to start from when uploading to Sound RAM
+ * If -1 it is interpreted to be the same as the start of the section for sound data in Sound RAM
+ * (SPU_DATA_BASE_ADDR). base_addr must be a multiply of 8.
+ * @return The sound address after all the uploaded samples
+ */
+
+int MODUploadSamples(ModMusic *m, int base_addr);
+
+/**
+ * Sets the SPU voice to use as the first channel when playing music.
+ *
+ * The voice for the second channel will then be this (value+1), and so on...
+ *
+ * Usually the base voice is 0; a MOD file can have up to eight channels, so take care of that.
+ *
+ * @param base_voice Desired base voice (0-23)
+ */
+
+void MODSetBaseVoice(int base_voice);
+
+/**
+ * Sets transpose for music
+ *
+ * Changing the transpose value for a music shifts the frequency
+ * its samples are played at, but the music's tempo is unchanged.
+ * @param m Pointer to ModMusic structure
+ * @param transpose Transpose value
+ */
+
+void MODSetTranspose(ModMusic *m, short transpose);
+
+/**
+ * Upload preconverted ADPCM samples, as generated by the mod4psx tool.
+ *
+ * @param d Pointer to buffer containing the ADPCM samples archive
+ * @param base_addr Base address at which the samples will start to be uploaded.
+ * @return The sound address after all the uploaded samples
+ */
+
+int MOD4PSX_Upload(void *d, int base_addr);
+
+/**
+ * Free memory allocated for music module
+ * @param m Pointer to ModMusic structure
+ */
+
+void MODUnload(ModMusic *m);
+
+/**
+ * Set maximum volume for MODPlay
+ * @param max_volume Maximum volume desired (0-0x3FFF)
+ */
+
+void MODSetMaxVolume(unsigned short max_volume);
+
+/**
+ * Set mono mode
+ * @param value If 0 set stereo mode, if 1 set mono mode
+ */
+
+void MODSetMono(int value);
+
+#endif
diff --git a/libmodplay/modplay_int.h b/libmodplay/modplay_int.h
new file mode 100644
index 0000000..9a8a124
--- /dev/null
+++ b/libmodplay/modplay_int.h
@@ -0,0 +1,20 @@
+/**
+ * MODPlay: internal prototypes
+ */
+
+#ifndef _MODPLAY_INT_H
+#define _MODPLAY_INT_H
+
+void MODPlay_func(ModMusic *m, int c, int s, int p, int vl, int vr);
+extern int modplay_int_cnt;
+extern unsigned int modload_flags;
+extern const unsigned short modplay_pitch_per_tbl[120];
+
+ModMusic *MODLoad_MOD(void *d);
+void MODPlay_MOD(ModMusic *m, int *t);
+
+#ifdef NO_PSX_LIB
+unsigned short SsFreqToPitch(int hz);
+#endif
+
+#endif \ No newline at end of file
diff --git a/libmodplay/modtbl.h b/libmodplay/modtbl.h
new file mode 100644
index 0000000..96bd407
--- /dev/null
+++ b/libmodplay/modtbl.h
@@ -0,0 +1,62 @@
+const unsigned short modplay_pitch_per_tbl[120] = {
+1712, 194,
+1616, 205,
+1525, 217,
+1440, 230,
+1357, 244,
+1281, 259,
+1209, 274,
+1141, 291,
+1077, 308,
+1017, 326,
+961, 345,
+907, 366,
+856, 388,
+808, 411,
+762, 436,
+720, 461,
+678, 490,
+640, 519,
+604, 550,
+570, 583,
+538, 617,
+508, 654,
+480, 692,
+453, 733,
+428, 776,
+404, 822,
+381, 872,
+360, 923,
+339, 980,
+320, 1038,
+302, 1100,
+285, 1166,
+269, 1235,
+254, 1308,
+240, 1385,
+226, 1471,
+214, 1553,
+202, 1645,
+190, 1749,
+180, 1847,
+170, 1955,
+160, 2077,
+151, 2201,
+143, 2324,
+135, 2462,
+127, 2617,
+120, 2770,
+113, 2942,
+107, 3107,
+101, 3291,
+95, 3499,
+90, 3694,
+85, 3911,
+80, 4155,
+76, 4374,
+71, 4682,
+67, 4962,
+64, 5194,
+60, 5541,
+57, 5832,
+};