public void LoadMusic()
        {
            _channels = new Sfx[4] {
                null, null, null, null
            };
            IsPlaying = false;

            _oscillator  = new Oscillator(AudioUnit.SampleRate);
            _patternData = new PatternData[64];

            for (int i = 0; i < _patternData.Length; i += 1)
            {
                byte[] vals =
                {
                    _emulator.Memory.ram[i * 4 + 0 + RamAddress.Song],
                    _emulator.Memory.ram[i * 4 + 1 + RamAddress.Song],
                    _emulator.Memory.ram[i * 4 + 2 + RamAddress.Song],
                    _emulator.Memory.ram[i * 4 + 3 + RamAddress.Song]
                };

                if ((vals[0] & 0x80) == 0x80)
                {
                    _patternData[i].loopStart = true;
                }

                if ((vals[1] & 0x80) == 0x80)
                {
                    _patternData[i].loopEnd = true;
                }

                if ((vals[2] & 0x80) == 0x80)
                {
                    _patternData[i].shouldStop = true;
                }

                _patternData[i].channelCount = new ChannelData[4];

                for (int j = 0; j < 4; j += 1)
                {
                    _patternData[i].channelCount[j] = new ChannelData();

                    if ((vals[j] & 0b01000000) != 0)
                    {
                        _patternData[i].channelCount[j].isSilent = true;
                    }

                    _patternData[i].channelCount[j].sfxIndex = (byte)(vals[j] & 0b00111111);
                }
            }
        }
        public Sfx(byte[] _sfxData, int _sfxIndex, ref float[] audioBuffer, ref Oscillator oscillator, int sampleRate,
                   int audioBufferIndex = 0)
        {
            notes        = new PicoNote[32];
            _audioBuffer = audioBuffer;

            duration  = _sfxData[65] / 120.0f;
            startLoop = _sfxData[66];
            endLoop   = _sfxData[67];

            _sampleRate = sampleRate;
            SfxIndex    = _sfxIndex;

            _oscillator = oscillator;

            // Console.WriteLine($"header {_sfxData[64]} {_sfxData[65]} {_sfxData[66]} {_sfxData[67]}");

            for (int i = 0; i < _sfxData.Length - 4; i += 2)
            {
                byte lo = _sfxData[i];
                byte hi = _sfxData[i + 1];

                notes[i / 2].pitch    = (byte)(lo & 0b00111111);
                notes[i / 2].waveform = (byte)(((lo & 0b11000000) >> 6) | ((hi & 0b1) << 2));
                notes[i / 2].volume   = (byte)((hi & 0b00001110) >> 1);
                notes[i / 2].effect   = (byte)((hi & 0b01110000) >> 4);
                notes[i / 2].isCustom = (byte)((hi & 0b10000000) >> 7) == 1;

                // Console.WriteLine($"{i} {notes[i / 2].pitch} {notes[i / 2].waveform} {notes[i / 2].volume} {notes[i / 2].effect} {notes[i / 2].isCustom}");
            }

            oscillator   = new Oscillator(sampleRate);
            _notesToPlay = new Queue <Note>();

            this.AudioBufferIndex = audioBufferIndex;

            IsActive = true;

            _fadeIn = 0.05f / duration;
        }
        public Note(ref float[] audioBuffer, int sampleRate, ref Oscillator oscillator, float duration, byte volume,
                    byte waveform, byte pitch, int pitchFrom = -1, float fadeIn = 1, float fadeOut = 1, bool vibrato = false)
        {
            _audioBuffer = audioBuffer;

            _duration   = duration;
            _sampleRate = sampleRate;

            _fadeIn  = fadeIn * duration / 100.0f;
            _fadeOut = fadeOut * duration / 100.0f;

            _timePassed = 0.0f;
            _volume     = 1;

            isCustom      = waveform > 7;
            targetVolume  = volume / 7.0f;
            this.waveform = waveform;
            this.pitch    = pitch;

            _pitchFrom = pitchFrom == -1 ? pitch : pitchFrom;

            _oscillator = oscillator;
            _vibrato    = vibrato;
        }
        public void Sfx(int n, int?channel = -1, int?offset = 0, int?length = 32)
        {
            switch (n)
            {
            case -1:
                if (channel == -1)
                {
                    StopAllChannelCount();
                    break;
                }

                if (channel < 0 || channel >= ChannelCount)
                {
                    break;
                }

                sfxChannels[channel.Value] = null;
                break;

            case -2:
                if (channel.Value < 0 || channel.Value >= ChannelCount)
                {
                    break;
                }

                if (sfxChannels[channel.Value] != null)
                {
                    sfxChannels[channel.Value].loop = false;
                }

                break;

            default:
                // If sound is already playing, stop it.
                int?index = FindSoundOnAChannel(n);

                if (index != null)
                {
                    sfxChannels[index.Value] = null;
                }

                if (channel == -1)
                {
                    channel = FindAvailableChannel();

                    if (channel == null)
                    {
                        break;
                    }
                }

                if (channel == -2)
                {
                    break;
                }

                byte[] _sfxData = new byte[68];
                Buffer.BlockCopy(Emulator.Memory.ram, RamAddress.Sfx + 68 * n, _sfxData, 0, 68);

                var osc = new Oscillator(SampleRate);
                sfxChannels[channel.Value]             = new Sfx(_sfxData, n, ref audioBuffer, ref osc, SampleRate);
                sfxChannels[channel.Value].CurrentNote = offset.Value;
                sfxChannels[channel.Value].LastIndex   = offset.Value + length.Value - 1;
                sfxChannels[channel.Value].Start();
                break;
            }
        }