예제 #1
0
            public Note(Data.Buffer data)
            {
                byte b1 = data.Pop <byte>();
                byte b2 = data.Pop <byte>();
                byte b3 = data.Pop <byte>();
                byte b4 = data.Pop <byte>();

                SampleNumber  = (byte)((b1 & 0xf0) | (b3 >> 4));
                Period        = b2 | ((b1 & 0x0f) << 8);
                EffectCommand = new Effect((uint)(b4 | ((b3 & 0x0f) << 8)));
            }
예제 #2
0
파일: XMI.cs 프로젝트: kaphula/freeserf.net
        uint ParseDeltaTime(Data.Buffer data)
        {
            uint deltaTime = 0;

            byte b = data.Pop <byte>();

            while ((b & 0x80) != 0)
            {
                deltaTime = (deltaTime << 7) | (uint)(b & 0x7f);
                b         = data.Pop <byte>();
            }

            deltaTime = (deltaTime << 7) | (uint)(b & 0x7f);

            return(deltaTime);
        }
예제 #3
0
파일: XMI.cs 프로젝트: kaphula/freeserf.net
        void ParseMetaEvent(Data.Buffer data)
        {
            // we ignore most of them
            // we only need the "set tempo" event
            byte type = data.Pop <byte>();
            byte len  = data.Pop <byte>();

            if (type == 0x51) // set tempo
            {
                if (len != 3)
                {
                    throw new ExceptionAudio("Invalid event length.");
                }

                // 24 bit value: microseconds per quarternote
                byte high = data.Pop <byte>();
                byte mid  = data.Pop <byte>();
                byte low  = data.Pop <byte>();

                tempo = (uint)high << 16 | (uint)mid << 8 | low;

                events.Add(new SetTempoEvent(tempo, currentTime, currentTick));

                Log.Verbose.Write(ErrorSystemType.Audio, $"XMI Tempo Changed: {tempo} microseconds per quarter note.");
            }
            else if (type == 0x58)
            {
                if (len != 4)
                {
                    throw new ExceptionAudio("Invalid event length.");
                }

                int numerator          = data.Pop <byte>();
                int denominator        = 1 << data.Pop <byte>();
                int numMidiClocks      = data.Pop <byte>();
                int num32InQuarterNote = data.Pop <byte>();

                Log.Verbose.Write(ErrorSystemType.Audio, $"XMI Time Signature: {numerator}/{denominator} ({numMidiClocks} MIDI Clocks, {num32InQuarterNote} 32nd-notes in quarter note.");
            }
            else
            {
                // skip event data
                data.Pop(len);
            }
        }
예제 #4
0
        public static short[] ConvertToWav(Data.Buffer data, int level = 0, bool invert = false)
        {
            List <short> wavData = new List <short>();

            while (data.Readable())
            {
                int value = data.Pop <byte>();

                value = value + level;

                if (invert)
                {
                    value = 0xFF - value;
                }

                value *= 0xFF;

                wavData.Add((short)value);
            }

            return(wavData.ToArray());
        }
예제 #5
0
        public MOD(Data.Buffer data)
        {
            data.SetEndianess(Endian.Endianess.Big);

            Log.Info.Write(ErrorSystemType.Data, $"Loading MOD song: {System.Text.Encoding.ASCII.GetString(data.Pop(20).ReinterpretAsArray(20))}"); // songname

            // add dummy sample 0
            samples.Add(new Sample());

            for (int i = 1; i < 32; ++i)                                                                                                               // samples 1-31
            {
                Log.Info.Write(ErrorSystemType.Data, $"Sample {i} name: {System.Text.Encoding.ASCII.GetString(data.Pop(22).ReinterpretAsArray(22))}"); // samplename

                samples.Add(new Sample()
                {
                    Length            = data.Pop <UInt16>() * 2,
                    FineTune          = ConvertFineTune(data.Pop <byte>() & 0xf),
                    Volume            = data.Pop <byte>(), // 0-64, change in dB = 20*log10(Volume/64)
                    RepeatPointOffset = data.Pop <UInt16>() * 2,
                    RepeatLength      = Math.Max(0, data.Pop <UInt16>() - 1) * 2
                });
            }

            int songLength = data.Pop <byte>();

            if (songLength < 1 || songLength > 128)
            {
                throw new ExceptionAudio("Invalid MOD format.");
            }

            data.Skip(1);

            byte[] songPatterns = data.Pop(128).ReinterpretAsArray(128);

            var magic = data.Pop(4).ToString();

            if (magic != "M!K!" && magic != "M.K.")
            {
                throw new ExceptionAudio("Invalid or unsupported MOD format.");
            }

            int            numPatterns = songPatterns.Max() + 1;
            List <Pattern> patterns    = new List <Pattern>(numPatterns);

            for (int i = 0; i < numPatterns; ++i)
            {
                var pattern = new Pattern();

                for (int div = 0; div < 64; ++div)
                {
                    for (int chan = 0; chan < 4; ++chan)
                    {
                        pattern.AddNote(chan, new Note(data));
                    }
                }

                patterns.Add(pattern);
            }

            for (int i = 0; i < samples.Count; ++i)
            {
                if (samples[i].Length > 0)
                {
                    data.Skip(2); // ignore tracker word
                    samples[i].Length -= 2;

                    uint length = (uint)samples[i].Length;

                    if (length > 0)
                    {
                        samples[i].SetData(data.Pop(length).ReinterpretAsArray(length));
                    }
                }
            }

            for (int i = 0; i < songLength; ++i)
            {
                song.Add(patterns[songPatterns[i]]);
            }
        }
예제 #6
0
파일: XMI.cs 프로젝트: kaphula/freeserf.net
        public XMI(Data.Buffer data)
        {
            // Note: Chunk length and so on are encoded as big endian.
            // But as we don't use them we use little endian because
            // the XMI data is encoded in little endian.
            data.SetEndianess(Endian.Endianess.Little);

            // Form chunk
            if (data.ToString(4) != "FORM")
            {
                return;
            }

            data.Pop(4);       // FORM
            data.Pop <uint>(); // FORM chunk length

            // format XDIR
            if (data.ToString(4) != "XDIR")
            {
                return;
            }

            data.Pop(4); // XDIR

            if (data.ToString(4) != "INFO")
            {
                return;
            }

            data.Pop(4);       // INFO
            data.Pop <uint>(); // INFO chunk length

            int numTracks = data.Pop <ushort>();

            if (numTracks != 1)
            {
                return; // we only support one track per file
            }
            if (data.ToString(4) != "CAT ")
            {
                return;
            }

            data.Pop(4);       // CAT_
            data.Pop <uint>(); // CAT chunk length

            // format XMID
            if (data.ToString(4) != "XMID")
            {
                return;
            }

            data.Pop(4); // XMID

            // load the one track

            // Form chunk
            if (data.ToString(4) != "FORM")
            {
                return;
            }

            data.Pop(4);       // FORM
            data.Pop <uint>(); // FORM chunk length

            // format XMID
            if (data.ToString(4) != "XMID")
            {
                return;
            }

            data.Pop(4); // XMID

            // TIMB chunk
            if (data.ToString(4) != "TIMB")
            {
                return;
            }

            data.Pop(4);       // TIMB
            data.Pop <uint>(); // TIMB chunk length

            int count = data.Pop <ushort>();

            for (int j = 0; j < count; ++j)
            {
                // we don't need the TIMB information, just skip it
                data.Pop(2);
            }

            // EVNT chunk
            if (data.ToString(4) != "EVNT")
            {
                return;
            }

            data.Pop(4);       // EVNT
            data.Pop <uint>(); // EVNT chunk length

            // read xmi/midi events
            while (data.Readable())
            {
                ParseEvent(data);
            }

            var eventIndices = new Dictionary <Event, int>();

            for (int i = 0; i < events.Count; ++i)
            {
                eventIndices.Add(events[i], i);
            }

            events.Sort((a, b) =>
            {
                int result = a.StartTime.CompareTo(b.StartTime);

                if (result == 0)
                {
                    return(eventIndices[a].CompareTo(eventIndices[b]));
                }

                return(result);
            });
        }
예제 #7
0
파일: XMI.cs 프로젝트: kaphula/freeserf.net
        void ParseEvent(Data.Buffer data)
        {
            byte status = data.PeekByte();

            if (status == 0xff) // meta event
            {
                data.Pop(1);
                ParseMetaEvent(data);
                return;
            }

            int channel   = status & 0xf;
            int eventType = status >> 4;

            if (eventType < 0x8)
            {
                uint ticks = data.Pop <byte>();
                currentTime += ConvertTicksToTime(ticks);
                currentTick += ticks;
                return;
            }
            else if (eventType < 0xF)
            {
                data.Pop(1);
            }

            switch (eventType)
            {
            case 0x9:     // Note on
                // Note: In XMI it has 3 parameters (last for duration).
                // But we create two events (note on and off).
            {
                byte note     = data.Pop <byte>();
                byte velocity = data.Pop <byte>();
                uint length   = ParseDeltaTime(data);

                if (velocity != 0)
                {
                    var onEvent  = new PlayNoteEvent((byte)channel, note, velocity, currentTime, currentTick);
                    var offEvent = new StopNoteEvent((byte)channel, note, currentTime + ConvertTicksToTime(length), currentTick + length);

                    events.Add(onEvent);
                    events.Add(offEvent);
                }
            }
            break;

            case 0xB:     // Control change
            {
                byte controller = (byte)(data.Pop <byte>() & 0x7f);
                byte value      = (byte)(data.Pop <byte>() & 0x7f);

                if (controller < 120)         // ignore reserved controller events >= 120
                {
                    events.Add(new SetControllerValueEvent((byte)channel, controller, value, currentTime, currentTick));
                }
            }
            break;

            case 0x8:
            case 0xA:
            case 0xE:
                data.Pop(2);
                break;

            case 0xC:
                events.Add(new SetInstrumentEvent((byte)channel, data.Pop <byte>(), currentTime, currentTick));
                break;

            case 0xD:
                data.Pop(1);
                break;

            default:
                throw new ExceptionAudio("Unsupported xmi/midi event type.");
            }
        }
예제 #8
0
        void ParseEvent(Data.Buffer data)
        {
            byte status = data.PeekByte();

            if (status == 0xff) // meta event
            {
                data.Pop(1);
                ParseMetaEvent(data);
                return;
            }

            int channel   = status & 0xf;
            int eventType = status >> 4;

            if (eventType < 0x8)
            {
                currentTime += ConvertTicksToTime(data.Pop <byte>());
                return;
            }
            else if (eventType < 0xF)
            {
                data.Pop(1);
            }

            switch (eventType)
            {
            case 0x9:     // Note on
                // Note: In XMI it has 3 parameters (last for duration).
                // But we create two events (note on and off).
            {
                byte note     = data.Pop <byte>();
                byte velocity = data.Pop <byte>();
                uint length   = ParseDeltaTime(data);

                if (velocity != 0)
                {
                    var onEvent  = new PlayNoteEvent((byte)channel, note, currentTime);
                    var offEvent = new StopNoteEvent((byte)channel, note, currentTime + ConvertTicksToTime(length));

                    events.Add(onEvent);
                    events.Add(offEvent);
                }
            }
            break;

            case 0x8:
            case 0xA:
            case 0xB:
            case 0xE:
                data.Pop(2);
                break;

            case 0xC:
            {
                var patchEvent = new SetInstrumentEvent((byte)channel, data.Pop <byte>(), currentTime);

                events.Add(patchEvent);
            }
            break;

            case 0xD:
                data.Pop(1);
                break;

            default:
                throw new ExceptionAudio("Unsupported xmi/midi event type.");
            }
        }