GUS PAT patch

View: New views
2 Messages — Rating Filter:   Alert me  

GUS PAT patch

by Elias Pschernig-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I made a crude patch against soundtracker-0.6.8.gtk2-20080114.tar.bz2 to
load GUS PAT files - the original FT2 could also read them in addition
to XI. It's the same instruments included in Debian's "freepats" package
and used by the timidity midi player. Maybe it's useful to someone.

--
Elias Pschernig <elias@...>

[soundtracker-0.6.8.gtk2-guspat.diff]

diff -u -N -r soundtracker-0.6.8.gtk2/app/instrument-editor.c soundtracker-0.6.8.gtk2-new/app/instrument-editor.c
--- soundtracker-0.6.8.gtk2/app/instrument-editor.c 2005-12-04 12:20:08.000000000 +0100
+++ soundtracker-0.6.8.gtk2-new/app/instrument-editor.c 2008-02-09 15:53:26.000000000 +0100
@@ -29,6 +29,7 @@
 #include "instrument-editor.h"
 #include "envelope-box.h"
 #include "xm.h"
+#include "pat.h"
 #include "st-subs.h"
 #include "gui.h"
 #include "gui-subs.h"
@@ -157,8 +158,17 @@
     f = fopen(fn, "rb");
     if(f) {
         statusbar_update(STATUS_LOADING_INSTRUMENT, TRUE);
-        xm_load_xi(instr, f);
-       statusbar_update(STATUS_INSTRUMENT_LOADED, FALSE);
+        // We base our recognition of PAT files only on the file extension.
+        int is_pat = 0;
+        char *dot = strrchr(fn, '.');
+        if (dot) {
+            if(!strcmp(dot, ".pat")) is_pat = 1;
+        }
+        if(is_pat)
+            pat_load_pat(instr, f);
+        else
+            xm_load_xi(instr, f);
+        statusbar_update(STATUS_INSTRUMENT_LOADED, FALSE);
  fclose(f);
     } else {
  error_error(_("Can't open file."));
diff -u -N -r soundtracker-0.6.8.gtk2/app/Makefile.am soundtracker-0.6.8.gtk2-new/app/Makefile.am
--- soundtracker-0.6.8.gtk2/app/Makefile.am 2008-01-14 08:37:47.000000000 +0100
+++ soundtracker-0.6.8.gtk2-new/app/Makefile.am 2008-02-09 15:27:03.000000000 +0100
@@ -25,6 +25,7 @@
  menubar.c menubar.h \
  mixer.h \
  module-info.c module-info.h \
+ pat.c pat.h \
  playlist.c playlist.h \
  poll.c poll.h \
  preferences.c preferences.h \
diff -u -N -r soundtracker-0.6.8.gtk2/app/pat.c soundtracker-0.6.8.gtk2-new/app/pat.c
--- soundtracker-0.6.8.gtk2/app/pat.c 1970-01-01 01:00:00.000000000 +0100
+++ soundtracker-0.6.8.gtk2-new/app/pat.c 2008-02-09 18:00:27.000000000 +0100
@@ -0,0 +1,259 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <glib.h>
+
+#include "xm.h"
+#include "st-subs.h"
+#include "recode.h"
+#include "errors.h"
+#include "i18n.h"
+#include "endian-conv.h"
+
+#include "pat.h"
+
+static int getuh(FILE *f)
+{
+    int b1 = getc(f);
+    int b2 = getc(f);
+    return b1 + 0x100 * b2;
+}
+
+static int geth(FILE *f)
+{
+    int h = getuh(f);
+    if (h > 32767) h = h - 65536;
+    return h;
+}
+
+static int geti(FILE *f)
+{
+    int b1 = getc(f);
+    int b2 = getc(f);
+    int b3 = getc(f);
+    int b4 = getc(f);
+    return b1 + 0x100 * b2 + 0x10000 * b3 + 0x1000000 * b4;
+}
+
+static void skip(FILE *f, int bytes)
+{
+    fseek(f, bytes, SEEK_CUR);
+}
+
+static int get_note_from_frequency(int frequency)
+{
+    /* The PAT has a frequency (multiplied by 1000). Get the MIDI note number
+     * this corresponds to. */
+    int n = 129.0 + 12.0 * log(frequency / 1000.0 / 14080.0) / M_LN2;
+    /* Convert from MIDI note number to XM note number. */
+    n += 50 - 60;
+    return n;
+}
+
+/* pat_load_pat loads a GUS (Gravis Ultrasound) PAT file, as supported by the
+ * original FastTracker2.
+ */
+gboolean
+pat_load_pat (STInstrument *instr,
+    FILE *f)
+{
+    char buf[256];
+    int num_samples;
+    int i, j;
+    int envelope_rate[6], envelope_offset[6];
+    int sample_flags = 0;
+    int offset[6], rate[6];
+
+    st_clean_instrument(instr, NULL);
+
+    /* Identification string for PAT. */
+    fread(buf, 1, 22, f);
+    if(strncmp(buf, "GF1PATCH110\0ID#000002\0", 22)) {
+        error_error(_("File is no PAT instrument."));
+        return 0;
+    }
+
+    /* Copyright info. We skip it. */
+    fread(buf, 1, 60, f);
+    
+    /* Number of instruments. We assume 1 and ignore it. */
+    fgetc(f);
+    
+    skip(f, 4);
+
+    /* Volume. We ignore it. */
+    geth(f);
+
+    skip(f, 40);
+    
+    /* The first instrument follows. */
+
+    /* Instrument number. */
+    skip(f, 2);
+    
+    /* Instrument name. */
+    fread(buf, 1, 16, f);
+    buf[17] = '\0';
+    strcpy(instr->name, buf);
+    recode_ibmpc_to_latin1(instr->name, 22);
+
+    /* Instrument size and layers count. We ignore it and assume 1 layer. */
+    skip(f, 45);
+
+    /* The first layer follows. */
+    skip(f, 6);
+  
+    num_samples = fgetc(f);
+    
+    skip(f, 40);
+
+    for(i = 0; i < num_samples; i++) {
+        STSample *s = instr->samples + i;
+        int frequency, first, last, base;
+        int flags;
+
+        /* Sample name. */
+        fread(buf, 1, 8, f);
+        buf[9] = '\0';
+        strcpy(s->name, buf);
+    recode_ibmpc_to_latin1(s->name, 22);
+
+        s->sample.length = geti(f);
+        s->sample.loopstart = geti(f);
+        s->sample.loopend = geti(f);
+        
+        /* We set the volume and panning to fixed values. FT2 does the same? */
+        s->volume = 64;
+        s->panning = 128;
+        
+        frequency = getuh(f);
+        first = geti(f);
+        last = geti(f);
+        base = geti(f);
+
+        base = get_note_from_frequency(base);
+        
+        /* Else it is different from FT2. Anyone knows why? */
+        base += 12;
+
+        /* Convert it to the relnote/finetune format used by XM. */
+        xm_freq_note_to_relnote_finetune(frequency, base,
+            &s->relnote, &s->finetune);  
+
+        /* Insert this sample into the sample map. */
+        first = get_note_from_frequency(first);
+        last = get_note_from_frequency(last);
+        
+        /* Have to reduce both by one to match FT2. */
+        first -= 1;
+        last -= 1;
+        for(j = first; j <= last; j++)
+            if(j < 96) instr->samplemap[j] = i;
+
+        /* Skip fine tune from PAT. */
+        geth(f);
+
+        /* Skip panning. */
+        fgetc(f);
+
+        /* Envelope. PAT has this per-sample, we just use the one for the
+         * first sample. FT2 does the same.
+         */
+        if (i == 0) {
+            /* Attack/Decay/Sustain/Release/Release2/Release3. */
+            for(j = 0; j < 6; j++) envelope_rate[j] = fgetc(f);
+            for(j = 0; j < 6; j++) envelope_offset[j] = fgetc(f);
+        } else {
+            skip(f, 12);
+        }
+
+        /* Skip tremolo and vibrato. */
+        skip(f, 6);
+        
+        flags = fgetc(f);
+        if (i == 0) sample_flags = flags;
+        s->sample.looptype = 0;
+        if(flags & 4) s->sample.looptype |= 1;
+
+        s->treat_as_8bit = !(flags & 1);
+
+        /* Skip frequency scale and factor. */
+        skip(f, 4);
+        
+        skip(f, 36);
+        
+        if(!s->treat_as_8bit) {
+            /* 16 bit sample. */
+            s->sample.length >>= 1;
+            s->sample.loopstart >>= 1;
+            s->sample.loopend >>= 1;
+                
+            s->sample.data = malloc(2 * s->sample.length);
+            fread(s->sample.data, 2, s->sample.length, f);
+            le_16_array_to_host_order(s->sample.data, s->sample.length);
+        } else {
+            /* 8 bit sample. We convert to 16 bit. */
+            char *data = malloc(2 * s->sample.length);
+            s->sample.data = (gint16 *)data;
+        fread(data + s->sample.length, 1, s->sample.length, f);
+        for (j = 0; j < s->sample.length; j++) {
+                s->sample.data[j] = data[s->sample.length + j] << 8;
+        }
+        }
+        
+        /* Are the data unsigned? */
+        if(flags & 2) {
+            for (j = 0; j < s->sample.length; j++) {
+                s->sample.data[j] ^= 0x8000;
+            }
+        }
+    }
+
+    /* Envelope. */
+
+    /* This is in some weird GUS HW format, I think FT2 converts it
+     * quite wrong as well. Since I could not find any GUS specifications for
+     * the envelope, I tried to reverse engineer the FT2 behavior as that is
+     * what FT2 users would expect anynway, but didn't finish it through to
+     * the end. So this is now often similar to FT2, but not quite, and
+     * definitly not what it sounds on a real GUS.
+     * The point here just is to use PAT instruments with SoundTracker,
+     * so this is fine, you have to adjust the envelope. In PAT it also is
+     * different for each sample, so this can fundamentally not work anyway :P
+     */
+    offset[0] = 0;
+    rate[0] = 0;
+    for (j = 1; j < 6; j++) {
+        offset[j] = 255 - envelope_offset[j - 1] + offset[j - 1];
+        rate[j] = envelope_rate[j - 1];
+        if (j > 3) rate[j] >>= 2;
+    }
+    
+    for (j = 0; j < 6; j++) {
+        instr->vol_env.points[j].pos = offset[j] >> 3;
+        instr->vol_env.points[j].val = rate[j] > 64 ? 64 : rate[j];
+    }
+    instr->vol_env.num_points = 6;
+ instr->vol_env.sustain_point = 3;
+ instr->vol_env.loop_start = 4;
+ instr->vol_env.loop_end = 5;
+ instr->vol_env.flags = 0;
+ if(sample_flags & 32) instr->vol_env.flags |= 2;
+    if(sample_flags & 64) instr->vol_env.flags |= 1;
+
+ instr->pan_env.num_points = 0;
+ instr->pan_env.sustain_point = 0;
+ instr->pan_env.loop_start = 0;
+ instr->pan_env.loop_end = 0;
+ instr->pan_env.flags = 0;
+
+ /* FT2 seems to do the same. */
+ instr->volfade = 128;
+
+ xm_check_envelope(&instr->vol_env);
+ xm_check_envelope(&instr->pan_env);
+
+    return 1;
+}
diff -u -N -r soundtracker-0.6.8.gtk2/app/pat.h soundtracker-0.6.8.gtk2-new/app/pat.h
--- soundtracker-0.6.8.gtk2/app/pat.h 1970-01-01 01:00:00.000000000 +0100
+++ soundtracker-0.6.8.gtk2-new/app/pat.h 2008-02-09 14:41:26.000000000 +0100
@@ -0,0 +1,6 @@
+#ifndef _PAT_H
+#define _PAT_H
+
+gboolean pat_load_pat(STInstrument *instr, FILE *f);
+
+#endif /* _PAT_H */
diff -u -N -r soundtracker-0.6.8.gtk2/app/xm.c soundtracker-0.6.8.gtk2-new/app/xm.c
--- soundtracker-0.6.8.gtk2/app/xm.c 2008-01-14 14:33:51.000000000 +0100
+++ soundtracker-0.6.8.gtk2-new/app/xm.c 2008-02-09 15:59:03.000000000 +0100
@@ -433,7 +433,7 @@
     }
 }
 
-static void
+void
 xm_check_envelope (STEnvelope *e)
 {
     int i;
diff -u -N -r soundtracker-0.6.8.gtk2/app/xm.h soundtracker-0.6.8.gtk2-new/app/xm.h
--- soundtracker-0.6.8.gtk2/app/xm.h 2006-09-28 21:12:42.000000000 +0200
+++ soundtracker-0.6.8.gtk2-new/app/xm.h 2008-02-09 15:59:00.000000000 +0100
@@ -179,6 +179,9 @@
 }
 
 void
+xm_check_envelope (STEnvelope *e);
+
+void
 xm_freq_note_to_relnote_finetune (float frequency,
   unsigned note,
   gint8 *relnote,


-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Soundtracker-discuss mailing list
Soundtracker-discuss@...
https://lists.sourceforge.net/lists/listinfo/soundtracker-discuss

Re: GUS PAT patch

by Yury Aliaev :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Elias Pschernig scripsit:
> I made a crude patch against soundtracker-0.6.8.gtk2-20080114.tar.bz2 to
> load GUS PAT files - the original FT2 could also read them in addition
> to XI. It's the same instruments included in Debian's "freepats" package
> and used by the timidity midi player. Maybe it's useful to someone.

Cool! I missed this feature a lot! Thank you very much :)

Yury.

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Soundtracker-discuss mailing list
Soundtracker-discuss@...
https://lists.sourceforge.net/lists/listinfo/soundtracker-discuss