Qhimm.com Forums

Miscellaneous Forums => Scripting and Reverse Engineering => Topic started by: Maki on 2018-06-24 11:59:50

Title: .SGT audio
Post by: Maki on 2018-06-24 11:59:50
I'm currently on hunt to implement .SGT playing. I got many needed information like how is RIFF container with DMSG built, like here: http://www.vgmpf.com/Wiki/index.php?title=SGT
It's part of DirectMusic that was a part of Dx8, yet I've seen the FMOD works with .SGT and .DLS media. I grabbed FMOD, made the wrappers working and tested with .MP3 and .MID files work flawless yet on .SGT it throws ERR_FORMAT like it doesn't know how to play .SGT (yet it loads .DLS bank properly). I browsed the web and found the FMUSIC of FMOD is capable of playing .SGT, but it's like 14 years ago and no such thing as FMUSIC exists anymore. Therefore- is any of you aware of any library that supports playing .SGT? Or converting to .MID or almost anything else I can use on good license and is capable of storing the data in memory only? I'm trying to implement an audio playing working on real files instead of re-converting them (or at least not leaving any trash behind). I'm working with Dx11, so I can't just grab the old DirectMusic from dx8

Working C# example in my last post!

Basically C# wrapper of C++ wrapper of Dx8 DirectMusic
Title: Re: .SGT audio
Post by: Maki on 2018-06-25 12:16:00
I began to work with it by myself. It's hard to find any documentation about .SGT file, but I made it to split the file content into five tracks. I'm working with 0004s-run.sgt (Run from FFVIII):

Looks like every track have header like this:
TRACK HEADER:
Code: [Select]
const char[8] DMTKrekh
uint size
byte[size] UNKNOWN

UNKNOWN:
Code: [Select]
byte[24] UNKNOWN
char[4] modeIndicator1
char[4] modeIndicator2

Some tracks have modeIndicator1 filled and modeIndicator2 nulled, and some have modeIndicator1 nulled and modeIndicator2 filled.



Track#1:
Code: [Select]
TRACKHEADER 44 bytes
LIST->cord (in my example 156 bytes + 8 ('cord' and size uint))
Chords data?

Track#2:
Code: [Select]
TRACKHEADER 44 bytes
tetr (in my example 20 bytes + 8 ('tetr' and size uint))
No idea, looks quite empty

Track#3:
Code: [Select]
TRACKHEADER 44 bytes
seqt (in my example 14460 bytes + 8 ('seqt' and size uint))
---evtl (in my example 60664 bytes + 8)
---curl (in my example 83780 bytes + 8)
This needs to be the main sequence, what evtl and curl are?
FOUND IT!
https://msdn.microsoft.com/en-us/library/ms900331.aspx

Track#4:
Code: [Select]
TRACKHEADER 44 bytes
tims (in my example 20 bytes + 8 ('tims' and size uint))
No idea, looks quite empty

Track#5:
Code: [Select]
TRACKHEADER 44 bytes
RIFF CONTAINER -> DMBT -> LIST; LIST; LIST; UNFO ("Band1"); (...) Inname ("FF8 Instruments File * FF8.dls")
Probably set-up of DLS/ bands/ instruments, lot of sub-cointainers with 'LIST' indicator.


@UPDATE:
Yeah, like I found the SGT chunks documentation:
https://msdn.microsoft.com/en-us/library/ms900553.aspx
-but some pages are in JP only https://msdn.microsoft.com/ja-jp/cc354074(ja-jp)

Python, track 2/ tetr, last 8 bytes:
Code: [Select]
>struct.unpack('d', '0000000000405f40'.decode('hex'))[0]
125.0


dmusicf.h found at:
https://github.com/tycho/arc/blob/master/contrib/DirectX/include/dmusicf.h

Sequence:
Code: [Select]
typedef struct _DMUS_IO_SEQ_ITEM
{
    MUSIC_TIME    mtTime;
    MUSIC_TIME    mtDuration;
    DWORD         dwPChannel;
    short         nOffset;
    BYTE          bStatus;
    BYTE          bByte1;
    BYTE          bByte2;
} DMUS_IO_SEQ_ITEM;


typedef struct _DMUS_IO_CURVE_ITEM
{
    MUSIC_TIME  mtStart;
    MUSIC_TIME  mtDuration;
    MUSIC_TIME  mtResetDuration;
    DWORD       dwPChannel;
    short       nOffset;
    short       nStartValue;
    short       nEndValue;
    short       nResetValue;
    BYTE        bType;
    BYTE        bCurveShape;
    BYTE        bCCData;
    BYTE        bFlags;
} DMUS_IO_CURVE_ITEM;
Title: Re: .SGT audio
Post by: Maki on 2018-06-25 12:48:31
I though about other way of doing these things- why not just wrap DirectMusic?
I found this:
https://sourceforge.net/projects/directmidinet/?source=typ_redirect

It's a C# wrapper of C++ wrapper of original DirectXMusic

Sample bootstrap code:
Code: [Select]
            string pt = Memory.FF8DIR + "/../Music/dmusic/004s-run.sgt";
            CDirectMusic cdm = new CDirectMusic();
            cdm.Initialize();
            CDLSLoader loader = new CDLSLoader();
            loader.Initialize();
            CSegment segment;
            loader.LoadSegment(pt, out segment);
            CAPathPerformance path = new CAPathPerformance();
            path.Initialize(cdm, null, null, DMUS_APATH.DYNAMIC_3D, 128);
            CPortPerformance cport = new CPortPerformance();
            cport.Initialize(cdm, null, null);
            COutputPort outport = new COutputPort();
            outport.Initialize(cdm);

            uint dwPortCount = 0;
            INFOPORT infoport;
            do
                outport.GetPortInfo(++dwPortCount, out infoport);
            while (( infoport.dwFlags & DMUS_PC.SOFTWARESYNTH)==0);

            outport.SetPortParams(0, 0, 0, SET.REVERB | SET.CHORUS, 44100);
            outport.ActivatePort(infoport);

            cport.AddPort(outport, 0, 1);

            segment.Download(cport);
            cport.PlaySegment(segment);

I need to find the way to put .DLS in it, as the audio lacks some instruments, but it plays so great at another thread*

*looks like I need to make it FIXED as the memory relocation either kills the DirectMusic or whole application due to memory access violation
**Looks like the memory is moving only when reference pointer is nulled. Just make sure to set all available DirectMusic members as static and whole class as static to pin them.


Code for DLS support:
Code: [Select]
string pt = Memory.FF8DIR + "/../Music/dmusic/013s-battle2.sgt";
            cdm = new CDirectMusic();
            cdm.Initialize();
            loader = new CDLSLoader();
            loader.Initialize();
            loader.LoadSegment(pt, out segment);
            ccollection = new CCollection();
            loader.LoadDLS(Memory.FF8DIR + "/../Music/dmusic/FF8.dls",out ccollection );
            uint dwInstrumentIndex = 0;
            INSTRUMENTINFO iInfo;
            while (ccollection.EnumInstrument(++dwInstrumentIndex, out iInfo) == S_OK)
            {
                Debug.WriteLine(iInfo.szInstName);
            }

            path = new CAPathPerformance();
            path.Initialize(cdm, null, null, DMUS_APATH.DYNAMIC_3D, 128);
            cport = new CPortPerformance();
            cport.Initialize(cdm, null, null);
            outport = new COutputPort();
            outport.Initialize(cdm);

            uint dwPortCount = 0;
            INFOPORT infoport;
            do
                outport.GetPortInfo(++dwPortCount, out infoport);
            while ((infoport.dwFlags & DMUS_PC.SOFTWARESYNTH) == 0);

            outport.SetPortParams(0, 0, 0, SET.REVERB | SET.CHORUS, 44100);
            outport.ActivatePort(infoport);

            cport.AddPort(outport, 0, 1);

            for (int i = 0; i < dwInstrumentIndex; i++)
            {
                ccollection.GetInstrument(out instrument, i);
                outport.DownloadInstrument(instrument);
            }


            outport.DownloadInstrument(instrument);
            segment.Download(cport);
            //GCHandle.Alloc(cdm, GCHandleType.Pinned);
            //GCHandle.Alloc(loader, GCHandleType.Pinned);
            //GCHandle.Alloc(segment, GCHandleType.Pinned);
            //GCHandle.Alloc(path, GCHandleType.Pinned);
            //GCHandle.Alloc(cport, GCHandleType.Pinned);
            //GCHandle.Alloc(outport, GCHandleType.Pinned);
            //GCHandle.Alloc(infoport, GCHandleType.Pinned);

            cport.PlaySegment(segment);


Full class with DLS support for collection based on static for GC:
Code: [Select]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using DirectMidi;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace FF8
{
    static class init_debugger_Audio
    {
        public static CDirectMusic cdm;
        public static CDLSLoader loader;
        public static CSegment segment;
        public static CAPathPerformance path;
        public static CPortPerformance cport;
        public static COutputPort outport;
        public static CCollection ccollection;
        public static CInstrument instrument;
        public static CInstrument[] instruments;


        public const int S_OK = 0x00000000;


        internal static void DEBUG()
        {

            PlayMusic();
            //FileStream fs = new FileStream(pt, FileMode.Open, FileAccess.Read);
            //BinaryReader br = new BinaryReader(fs);
            //string RIFF = Encoding.ASCII.GetString(br.ReadBytes(4));
            //if (RIFF != "RIFF") throw new Exception("NewDirectMusic::NOT RIFF");
            //uint eof = br.ReadUInt32();
            //if (fs.Length != eof + 8) throw new Exception("NewDirectMusic::RIFF length/size indicator error");
            //string dmsg = Encoding.ASCII.GetString(br.ReadBytes(4));
            //var SegmentHeader = GetSGTSection(br);
            //var guid = GetSGTSection(br);
            //var list = GetSGTSection(br);
            //var vers = GetSGTSection(br);
            //var list_unfo = GetSGTSection(br);
            //string UNFO = SGT_ReadUNAM(list_unfo).TrimEnd('\0');
            //var trackList = GetSGTSection(br);
            //List<byte[]> Tracks = ProcessTrackList(trackList.Item2);
            //byte[] sequenceTrack = Tracks[2];
            //PseudoBufferedStream pbs = new PseudoBufferedStream(sequenceTrack);
            //pbs.Seek(44, PseudoBufferedStream.SEEK_BEGIN);
            //string seqt = Encoding.ASCII.GetString(BitConverter.GetBytes(pbs.ReadUInt()));
        }

        internal static void update()

        {

        }

        unsafe private static void PlayMusic()
        {
            string pt = Memory.FF8DIR + "/../Music/dmusic/013s-battle2.sgt";
            cdm = new CDirectMusic();
            cdm.Initialize();
            loader = new CDLSLoader();
            loader.Initialize();
            loader.LoadSegment(pt, out segment);
            ccollection = new CCollection();
            loader.LoadDLS(Memory.FF8DIR + "/../Music/dmusic/FF8.dls",out ccollection );
            uint dwInstrumentIndex = 0;
            INSTRUMENTINFO iInfo;
            while (ccollection.EnumInstrument(++dwInstrumentIndex, out iInfo) == S_OK)
            {
                Debug.WriteLine(iInfo.szInstName);
            }
            instruments = new CInstrument[dwInstrumentIndex];

            path = new CAPathPerformance();
            path.Initialize(cdm, null, null, DMUS_APATH.DYNAMIC_3D, 128);
            cport = new CPortPerformance();
            cport.Initialize(cdm, null, null);
            outport = new COutputPort();
            outport.Initialize(cdm);

            uint dwPortCount = 0;
            INFOPORT infoport;
            do
                outport.GetPortInfo(++dwPortCount, out infoport);
            while ((infoport.dwFlags & DMUS_PC.SOFTWARESYNTH) == 0);

            outport.SetPortParams(0, 0, 0, SET.REVERB | SET.CHORUS, 44100);
            outport.ActivatePort(infoport);

            cport.AddPort(outport, 0, 1);

            for (int i = 0; i < dwInstrumentIndex; i++)
            {
                ccollection.GetInstrument(out instruments[i], i);
                outport.DownloadInstrument(instruments[i]);
            }
            segment.Download(cport);
            //GCHandle.Alloc(cdm, GCHandleType.Pinned);
            //GCHandle.Alloc(loader, GCHandleType.Pinned);
            //GCHandle.Alloc(segment, GCHandleType.Pinned);
            //GCHandle.Alloc(path, GCHandleType.Pinned);
            //GCHandle.Alloc(cport, GCHandleType.Pinned);
            //GCHandle.Alloc(outport, GCHandleType.Pinned);
            //GCHandle.Alloc(infoport, GCHandleType.Pinned);

            cport.PlaySegment(segment);
        }

@UPDATE:
segment.ConnectToDls takes CCollection
Title: Re: .SGT audio
Post by: Mcindus on 2018-07-21 20:45:52
Whoa... what whoa?

I just saw this for the first time.  cool.