예제 #1
0
            void AddInstruments(MLSSVoiceTable table)
            {
                for (int i = 0; i < table.Length; i++)
                {
                    var voice   = (MLSSWrappedVoice)table[i];
                    var entries = voice.GetSubVoices().Cast <MLSSVoiceEntry>();
                    if (entries.Count() == 0)
                    {
                        continue;
                    }

                    string name = "Instrument " + i;
                    sf2.AddPreset(name, (ushort)i, 0);
                    sf2.AddPresetBag();
                    sf2.AddPresetGenerator(SF2Generator.Instrument,
                                           new SF2GeneratorAmount {
                        Amount = (short)sf2.AddInstrument(name)
                    });
                    foreach (var entry in entries)
                    {
                        sf2.AddInstrumentBag();
                        if (!(entry.MinKey == 0 && entry.MaxKey == 0x7F))
                        {
                            sf2.AddInstrumentGenerator(SF2Generator.KeyRange, new SF2GeneratorAmount {
                                LowByte = entry.MinKey, HighByte = entry.MaxKey
                            });
                        }
                        if (entry.IsFixedFrequency == 0x80)
                        {
                            sf2.AddInstrumentGenerator(SF2Generator.ScaleTuning, new SF2GeneratorAmount {
                                Amount = 0
                            });
                        }
                        if (entry.Sample < table.Samples.Length)
                        {
                            var sample = table.Samples[entry.Sample];
                            if (sample != null)
                            {
                                sf2.AddInstrumentGenerator(SF2Generator.SampleModes, new SF2GeneratorAmount {
                                    Amount = (short)(sample.GetSample().bLoop ? 1 : 0)
                                });
                                sf2.AddInstrumentGenerator(SF2Generator.SampleID, new SF2GeneratorAmount {
                                    Amount = (short)addedSamples[sample]
                                });
                            }
                            else
                            {
                                Console.WriteLine("Voice {0} uses a null sample id ({1})", i, entry.Sample);
                            }
                        }
                        else
                        {
                            Console.WriteLine("Voice {0} uses an invalid sample id ({1})", i, entry.Sample);
                        }
                    }
                }
            }
예제 #2
0
        private SF2 SoundFundry(AKAO sequencer, AKAO[] sampleCollections)
        {
            DLS dls = new DLS();

            dls.SetName(FileName + ".dls");
            SF2 sf2 = new SF2();

            sf2.InfoChunk.Bank      = "Vagrant Story SoundFont for " + FileName;
            sf2.InfoChunk.Products  = "Vagrant Story";
            sf2.InfoChunk.Tools     = "https://github.com/korobetski/Vagrant-Story-Unity-Parser";
            sf2.InfoChunk.Designer  = "Korobetski Sigfrid";
            sf2.InfoChunk.Date      = DateTime.Now.ToString();
            sf2.InfoChunk.Copyright = "Musics & Samples belong to Hitoshi Sakimoto @ Squaresoft";
            sf2.InfoChunk.Comment   = string.Concat("This SoundFont was generated by reading raw AKAO format from the original game in SOUND folder.\r",
                                                    "\nNever forget that musics and samples belongs to SquareEnix, don't use them as your own. Sample collections : ",
                                                    sampleCollections[0].FileName, ", ",
                                                    sampleCollections[1].FileName);

            List <AKAOSample> Samples = new List <AKAOSample>();

            // MUSIC024.DAT has no instruments Oo, maybe unfinished work, or development test
            // we use composer program change to load articulations from WAVE000.DAT the only file where start id = 0
            if (sequencer.instruments == null)
            {
                foreach (uint id in sequencer.composer.progIDs)
                {
                    AKAOInstrument instrument = new AKAOInstrument(id, AKAOInstrument.InstrumentType.INSTR_MELODIC);
                    instrument.name    = "No instrument " + id;
                    instrument.regions = new AKAORegion[1];
                    AKAORegion defaultRegion = new AKAORegion();
                    defaultRegion.articulationId = (byte)id;
                    instrument.regions[0]        = defaultRegion;
                    sequencer.instruments        = new List <AKAOInstrument>();
                    sequencer.instruments.Add(instrument);
                }
            }

            if (sequencer.instruments != null)
            {
                uint i = 0;
                foreach (AKAOInstrument instrument in sequencer.instruments)
                {
                    if (composer.progIDs.Contains(instrument.program) || composer.A1Calls.Contains(instrument.program) || instrument.IsDrum())
                    {
                        uint midiBank = 0x00000000;
                        if (instrument.IsDrum())
                        {
                            midiBank = DLS.F_INSTRUMENT_DRUMS;
                            sf2.AddPreset(instrument.name, 0, 128);
                        }
                        else
                        {
                            sf2.AddPreset(instrument.name, (ushort)instrument.program, 0);
                        }

                        sf2.AddPresetBag();
                        sf2.AddPresetGenerator(SF2Generator.ReverbEffectsSend, new SF2GeneratorAmount {
                            UAmount = (ushort)1000
                        });
                        sf2.AddPresetGenerator(SF2Generator.Instrument, new SF2GeneratorAmount {
                            UAmount = (ushort)i
                        });
                        sf2.AddInstrument(instrument.name);
                        i++;

                        if (instrument.regions.Length > 0)
                        {
                            Lins DSLInstrument = new Lins(midiBank, instrument.program, instrument.name);

                            foreach (AKAORegion region in instrument.regions)
                            {
                                AKAOArticulation articulation = null;
                                AKAO             coll         = sampleCollections[2];

                                if (region.articulationId >= 0 && region.articulationId < 32)
                                {
                                    // trick for MUSIC024.DAT
                                    coll         = sampleCollections[1];
                                    articulation = coll.articulations[region.articulationId];
                                }
                                else if (region.articulationId >= 32 && region.articulationId < 64)
                                {
                                    coll         = sampleCollections[0];
                                    articulation = coll.articulations[region.articulationId - coll.startingArticulationId];
                                }
                                else if (region.articulationId >= 64 && region.articulationId < 128)
                                {
                                    coll = sampleCollections[1];
                                    if (region.articulationId - coll.startingArticulationId < coll.articulations.Length /* && !instrument.a1*/)
                                    {
                                        articulation = coll.articulations[region.articulationId - coll.startingArticulationId];
                                    }
                                    else
                                    {
                                        // we check in additional collection
                                        //Debug.LogWarning(region.articulationId);
                                        coll         = sampleCollections[2];
                                        articulation = coll.articulations[region.articulationId - coll.startingArticulationId];
                                    }
                                }
                                if (UseDebug)
                                {
                                    Debug.Log(string.Concat("Instrument ", i, "  ", instrument.name, "  |  Region articulation  " + region.articulationId + "  found in ", coll.FileName));
                                }



                                if (articulation != null)
                                {
                                    articulation.BuildADSR();
                                    region.articulation = articulation;
                                    AKAOSample sample = coll.samples[articulation.sampleNum];

                                    if (instrument.IsDrum())
                                    {
                                        region.unityKey = (uint)articulation.unityKey + region.lowRange - region.relativeKey; // maybe
                                    }
                                    else
                                    {
                                        region.unityKey = articulation.unityKey;
                                    }

                                    short ft = articulation.fineTune;
                                    if (ft < 0)
                                    {
                                        ft += short.MaxValue;
                                    }

                                    double freq_multiplier = ((ft * 32) + 0x100000) / (double)0x100000;
                                    double cents           = (short)(1200 * Math.Log(freq_multiplier, 2));
                                    if (articulation.fineTune < 0)
                                    {
                                        cents -= 1200;
                                    }

                                    region.fineTune  = (short)cents;
                                    sample.loopStart = (uint)(articulation.loopPt * 1.75);
                                    sample.unityKey  = (byte)region.unityKey;

                                    if (!Samples.Contains(sample))
                                    {
                                        Samples.Add(sample);
                                    }

                                    int sampleIDX = Samples.IndexOf(sample);


                                    // Making DLS
                                    Lrgn   reg = new Lrgn(region.lowRange, region.hiRange, 0x00, 0x7F);
                                    CKwsmp smp = new CKwsmp((ushort)region.unityKey, region.fineTune, region.attenuation, 1);
                                    if (articulation.loopPt != 0)
                                    {
                                        smp.AddLoop(new Loop(1, (uint)(articulation.loopPt * 1.75f), (uint)(sample.size * 1.75f - articulation.loopPt * 1.75f)));
                                    }
                                    reg.SetSample(smp);
                                    CKart2 iart = new CKart2();
                                    iart.AddPan(0x40);
                                    iart.AddADSR(articulation.A, articulation.D, articulation.S, articulation.R, articulation.AT, articulation.RT);
                                    reg.AddArticulation(iart);
                                    reg.SetWaveLinkInfo(0, 0, 1, region.sampleNum);
                                    DSLInstrument.AddRegion(reg);

                                    // http://linuxmao.org/SoundFont+specification+SF2
                                    sf2.AddInstrumentBag();
                                    sf2.AddInstrumentGenerator(SF2Generator.KeyRange, new SF2GeneratorAmount {
                                        LowByte = region.lowRange, HighByte = region.hiRange
                                    });
                                    //sf2.AddInstrumentGenerator(SF2Generator.VelRange, new SF2GeneratorAmount { LowByte = region.lowVel, HighByte = region.hiVel }); // not sure
                                    sf2.AddInstrumentGenerator(SF2Generator.VelRange, new SF2GeneratorAmount {
                                        LowByte = 0, HighByte = 127
                                    });

                                    /* C'est l'atténuation, en centibels, pour laquelle une note est atténuée en dessous de la valeur maximum prévue.
                                     * Si = 0, il n'y a aucune atténuation, la note sera jouée au maximum prévu.
                                     * Ex : 60 indique que la note sera jouée à 6 dB en-dessous du maximum prévu pour la note.
                                     * Max value = 1440 */
                                    sf2.AddInstrumentGenerator(SF2Generator.InitialAttenuation, new SF2GeneratorAmount {
                                        UAmount = (ushort)(region.attenuation / 10)
                                    });
                                    //sf2.AddInstrumentGenerator(SF2Generator.ReverbEffectsSend, new SF2GeneratorAmount { Amount = 1000 });
                                    sf2.AddInstrumentGenerator(SF2Generator.Pan, new SF2GeneratorAmount {
                                        Amount = region.pan
                                    });
                                    sf2.AddInstrumentGenerator(SF2Generator.SampleModes, new SF2GeneratorAmount {
                                        UAmount = (articulation.loopPt != 0) ? (ushort)1 : (ushort)0
                                    });
                                    sf2.AddInstrumentGenerator(SF2Generator.OverridingRootKey, new SF2GeneratorAmount {
                                        UAmount = (ushort)region.unityKey
                                    });

                                    sf2.AddInstrumentGenerator(SF2Generator.DelayVolEnv, new SF2GeneratorAmount {
                                        Amount = (short)short.MinValue
                                    });

                                    /* En timecents absolu, c'est la durée, depuis la fin du délai de l'enveloppe de volume jusqu'au point où la valeur de l'enveloppe de volume atteint son apogée.
                                     * Une valeur de 0 indique 1 seconde. Une valeur négative indique un temps inférieur à une seconde, une valeur positive un temps supérieur à une seconde.
                                     * Le nombre le plus négatif (-32768) indique conventionnellement une attaque instantanée.
                                     * Ex : un temps d'attaque de 10 ms serait 1200log2 (.01) = -7973.
                                     * En musique, le logarithme binaire intervient dans la formule permettant de déterminer la valeur en cents d’un intervalle.
                                     * Un cent, ou centième de demi-ton au tempérament égal, vaut 1200 fois le logarithme binaire du rapport de fréquence des sons concernés.
                                     * 546 * 60 ~= short.MaxValue */
                                    sf2.AddInstrumentGenerator(SF2Generator.AttackVolEnv, new SF2GeneratorAmount {
                                        Amount = (short)(1200 * Math.Log(articulation.A, 2))
                                    });
                                    sf2.AddInstrumentGenerator(SF2Generator.HoldVolEnv, new SF2GeneratorAmount {
                                        Amount = (short)0
                                    });

                                    /* C'est le temps, en timecents absolus, pour une variation de 100% de la valeur de l'enveloppe du volume pendant la phase de décroissance.
                                     * Pour l'enveloppe de volume, la décroissance tend linéairement vers le niveau de maintien, ce qui provoque un changement de dB constant pour chaque unité de temps.
                                     * Si le niveau de maintien = -100dB, le temps de décroissance de l'enveloppe de volume = temps de la phase de décroissance.
                                     * Une valeur de 0 indique 1 seconde de temps de décroissance pour un niveau zéro. Une valeur négative indique un temps inférieur à une seconde,
                                     * une valeur positive un temps supérieur à une seconde.
                                     * Ex : un temps de décroissance de 10 msec serait 1200log2 (.01) = -7973.*/
                                    sf2.AddInstrumentGenerator(SF2Generator.DecayVolEnv, new SF2GeneratorAmount {
                                        Amount = (short)(1200 * Math.Log(articulation.D, 2))
                                    });

                                    /* C'est le taux de la diminution, exprimé en centibels, pour laquelle l'enveloppe de volume décroît au cours de la phase de décroissance.
                                     * Pour l'enveloppe de volume, le niveau d'atténuation du sustain est mieux exprimé en centibels. Une valeur de 0 indique que le niveau est maximum.
                                     * Une valeur positive indique une décroissance au niveau correspondant. Les valeurs inférieures à zéro doivent être interprétés comme zéro;
                                     * conventionnellement 1000 indique une atténuation complète.
                                     * Ex : un niveau de soutien qui correspond à une valeur absolue de 12 dB en dessous du pic serait 120.*/
                                    sf2.AddInstrumentGenerator(SF2Generator.SustainVolEnv, new SF2GeneratorAmount {
                                        Amount = (short)(articulation.S)
                                    });

                                    /* C'est la durée, en timecents absolu, pour une variation de 100% de la valeur de l'enveloppe du volume pendant la phase de libération (release).
                                     * Pour l'enveloppe de volume, la phase de libération tend linéairement vers zéro depuis la niveau en cours,
                                     * ce qui provoque un changement en dB constant pour chaque unité de temps.
                                     * Si le niveau actuel est maximum, la durée du release de l'enveloppe de volume sera le temps de libération jusqu'à ce que 100 dB d'atténuation soit atteint.
                                     * Une valeur de 0 indique 1 seconde de temps de décroissance pour finir complètement. Une valeur négative indique un temps inférieur à une seconde,
                                     * une valeur positive un temps de plus d'une seconde.
                                     * Ex : un temps de libération de 10 msec serait 1200log2 (.01) = -7973. */
                                    sf2.AddInstrumentGenerator(SF2Generator.ReleaseVolEnv, new SF2GeneratorAmount {
                                        Amount = (short)(1200 * Math.Log(articulation.R, 2))
                                    });

                                    /* Décalage de la hauteur, en cents, qui sera appliqué à la note.
                                     * Il est additionnel à coarseTune. Une valeur positive indique que le son est reproduit à une hauteur plus élevée, une valeur négative indique une hauteur inférieure.
                                     * Ex : une valeur finetune = -5 provoquera un son joué cinq cents plus bas. */
                                    sf2.AddInstrumentGenerator(SF2Generator.FineTune, new SF2GeneratorAmount {
                                        Amount = (short)(region.fineTune)
                                    });
                                    sf2.AddInstrumentGenerator(SF2Generator.SampleID, new SF2GeneratorAmount {
                                        UAmount = (ushort)sampleIDX
                                    });
                                }
                            }

                            dls.AddInstrument(DSLInstrument);
                        }
                    }
                }
            }

            if (Samples.Count > 0)
            {
                foreach (AKAOSample AKAOsmp in Samples)
                {
                    WAV nw = AKAOsmp.ConvertToWAV();
                    nw.SetName(AKAOsmp.name);
                    nw.Riff = false;
                    dls.AddWave(nw);

                    short[] pcm = AKAOsmp.WAVDatas.ToArray();
                    sf2.AddSample(pcm, AKAOsmp.name, (AKAOsmp.loopStart > 0), AKAOsmp.loopStart, 44100, AKAOsmp.unityKey, 0);
                }
            }

            if (bDLS)
            {
                ToolBox.DirExNorCreate("Assets/Resources/Sounds/DLS/");
                dls.WriteFile("Assets/Resources/Sounds/DLS/" + FileName + ".dls");
            }

            if (bSF2)
            {
                ToolBox.DirExNorCreate("Assets/Resources/Sounds/SF2/");
                sf2.Save("Assets/Resources/Sounds/SF2/" + FileName + ".sf2");
            }


            return(sf2);
        }
예제 #3
0
        void AddTable(M4AVoiceTable table, bool saveAfter7F, bool isNewInst)
        {
            int amt = saveAfter7F ? 0xFF : 0x7F;

            for (ushort i = 0; i <= amt; i++)
            {
                var voice = table[i];
                if (instruments.Contains(voice))
                {
                    continue;
                }
                instruments.Add(voice);

                if (isNewInst)
                {
                    string name = "Instrument " + i;
                    AddPreset(name, i);
                    sf2.AddInstrument(name);
                }

                if (voice is M4ASDirect direct)
                {
                    if (!isNewInst)
                    {
                        AddDirect(direct, (byte)i, (byte)i);
                        sf2.AddINSTGenerator(SF2Generator.overridingRootKey, new GenAmountType((ushort)(i - (direct.Voice.GetRootNote() - 60))));
                    }
                    else
                    {
                        AddDirect(direct);
                    }
                }
                else if (voice.Voice is M4APSG_Square_1 || voice.Voice is M4APSG_Square_2 || voice.Voice is M4APSG_Wave || voice.Voice is M4APSG_Noise)
                {
                    var m4 = (M4AVoice)voice.Voice;
                    if (!isNewInst)
                    {
                        if (voice.Voice is M4APSG_Noise)
                        {
                            AddPSG(m4, (byte)i, (byte)i);
                            sf2.AddINSTGenerator(SF2Generator.overridingRootKey, new GenAmountType((ushort)(i - (m4.RootNote - 60))));
                        }
                    }
                    else
                    {
                        AddPSG(m4);
                        if (!(voice.Voice is M4APSG_Noise))
                        {
                            sf2.AddINSTGenerator(SF2Generator.overridingRootKey, new GenAmountType(69));
                        }
                    }
                }
                else if (isNewInst && voice is M4ASMulti multi)
                {
                    foreach (var key in multi.Keys)
                    {
                        if (key.Item1 > amt || key.Item2 > amt)
                        {
                            continue;
                        }
                        var subvoice = multi.Table[key.Item1];

                        if (subvoice is M4ASDirect subdirect)
                        {
                            AddDirect(subdirect, key.Item2, key.Item3);
                        }
                    }
                }
                else if (voice is M4ASDrum drum)
                {
                    AddTable(drum.Table, saveAfter7F, false);
                }
            }
        }
예제 #4
0
            void AddTable(M4AVoiceTable table, bool saveAfter7F, bool fromDrum)
            {
                int tableOffset = table.GetOffset();

                if (addedTables.Contains(tableOffset))
                {
                    return;
                }
                addedTables.Add(tableOffset);

                int amt = saveAfter7F ? 0xFF : 0x7F;

                for (int i = 0; i <= amt; i++)
                {
                    var voice = table[i];
                    //Console.WriteLine("{0} {1} {2}", i, fromDrum, voice);

                    if (!fromDrum)
                    {
                        string name = "Instrument " + i;
                        sf2.AddPreset(name, (ushort)i, 0);
                        //sf2.AddPreset(name, (ushort)i, (ushort)(voice is M4AWrappedDrum ? 128 : 0));
                        sf2.AddPresetBag();
                        sf2.AddPresetGenerator(SF2Generator.Instrument,
                                               new SF2GeneratorAmount {
                            Amount = (short)sf2.AddInstrument(name)
                        });
                    }

                    if (voice is M4AWrappedDirect direct)
                    {
                        if (fromDrum)
                        {
                            AddDirect(direct, (byte)i, (byte)i);
                            sf2.AddInstrumentGenerator(SF2Generator.OverridingRootKey,
                                                       new SF2GeneratorAmount {
                                Amount = (short)(i - (direct.Voice.GetRootNote() - 60))
                            });
                        }
                        else
                        {
                            AddDirect(direct);
                        }
                    }
                    else if (voice is M4AWrappedKeySplit keySplit)
                    {
                        if (fromDrum)
                        {
                            Console.WriteLine("Skipping nested key split within a drum at table 0x{0:X7} index {1}.", tableOffset, i);
                            continue;
                        }
                        foreach (var key in keySplit.Keys)
                        {
                            if (key.Item1 > amt || key.Item2 >= 0x80)
                            {
                                continue;
                            }
                            var subvoice = keySplit.Table[key.Item1];

                            var m4 = (M4AVoiceEntry)voice.Voice;
                            if (subvoice is M4AWrappedDirect subdirect)
                            {
                                AddDirect(subdirect, key.Item2, key.Item3);
                            }
                            else if (m4.Type == (int)M4AVoiceFlags.KeySplit)
                            {
                                Console.WriteLine("Skipping nested key split within a key split at table 0x{0:X7} index {1}.", tableOffset, i);
                            }
                            else if (m4.Type == (int)M4AVoiceFlags.Drum)
                            {
                                Console.WriteLine("Skipping nested drum within a key split at table 0x{0:X7} index {1}.", tableOffset, i);
                            }
                            else if (m4.IsGBInstrument())
                            {
                                AddPSG(m4);
                            }
                            else // Invalid
                            {
                                Console.WriteLine("Skipping invalid instrument within a key split at table 0x{0:X7} index {1}.", tableOffset, i);
                            }
                        }
                    }
                    else if (voice is M4AWrappedDrum drum)
                    {
                        if (fromDrum)
                        {
                            Console.WriteLine("Skipping nested drum within a drum at table 0x{0:X7} index {1}.", tableOffset, i);
                            continue;
                        }
                        AddTable(drum.Table, saveAfter7F, true);
                    }
                    else
                    {
                        var m4 = (M4AVoiceEntry)voice.Voice;
                        if (m4.IsInvalid())
                        {
                            Console.WriteLine("Skipping invalid instrument at table 0x{0:X7} index {1}.", tableOffset, i);
                            continue;
                        }
                        if (fromDrum)
                        {
                            AddPSG(m4, (byte)i, (byte)i);
                            sf2.AddInstrumentGenerator(SF2Generator.OverridingRootKey,
                                                       new SF2GeneratorAmount {
                                Amount = (short)(i - (m4.GetRootNote() - 60))
                            });
                        }
                        else
                        {
                            AddPSG(m4);
                        }
                    }
                }
            }