static public void Setup()
        {
            if (Sounder != null)
            {
                return;
            }
            Sounder = new GameObject("Sounder");
            Object.DontDestroyOnLoad(Sounder);

            SetSourcePoolSize(16);

            Oscillator.RebuildWaveTables(AudioSettings.outputSampleRate);
        }
        public override void OnInspectorGUI()
        {
            if (Application.isPlaying)
            {
                return;
            }

            MakePlayer();

            int sampleRate = EditorGUILayout.IntField(new GUIContent("Sample Rate", "Samples per second. Smaller values build faster larger values sound better."), effect.SampleRate);

            if (sampleRate > 50000)
            {
                sampleRate = 50000;
            }
            if (sampleRate > 1 && effect.SampleRate != sampleRate)
            {
                Undo.RecordObject(effect, "SounderEffect Sample Rate");
                effect.SampleRate = sampleRate;
            }


            Oscillator.WaveForm form = (Oscillator.WaveForm)EditorGUILayout.EnumPopup(new GUIContent("Waveform", "Waveform determines the base timbre of the sound."), effect.waveForm);
            if (form != effect.waveForm)
            {
                Undo.RecordObject(effect, "SounderEffect WaveForm");
                effect.waveForm = form;
            }

            #region WaveDraw
            Rect r = EditorGUILayout.BeginHorizontal();
            r.height = 48;

            if (waveTexture == null || GUI.changed || waveTexture.width != r.width)
            {
                if (waveTexture != null)
                {
                    DestroyImmediate(waveTexture);
                }
                waveTexture = WaveDrawer.TextureFromSamples(Oscillator.GetWaveTable(effect.waveForm), (int)r.width, (int)r.height);
            }
            EditorGUI.DrawPreviewTexture(r, waveTexture);

            EditorGUILayout.LabelField(new GUIContent(), GUILayout.Width(r.width), GUILayout.Height(r.height));
            EditorGUILayout.EndHorizontal();
            #endregion

            EditorGUILayout.Separator();

            float delay = EditorGUILayout.FloatField(new GUIContent("Delay", "Delay in seconds before starting"), effect.delay);
            if (delay < .0f)
            {
                delay = .0f;
            }

            if (delay != effect.delay)
            {
                Undo.RecordObject(effect, "SounderEffect Delay");
                effect.delay = delay;
            }

            EditorGUILayout.Separator();

            amplitudeEditor.DrawEditor();

            EditorGUILayout.Separator();

            frequencyEditor.DrawEditor();

            string saveHash = AssetDatabase.GetAssetPath(effect).GetHashCode().ToString();

            EditorGUILayout.Separator();
            float test = EditorGUILayout.Slider(new GUIContent("LFO Amplitude", "Amount the low frequency oscillator affects the sound."), effect.LFOAmplitude, .0f, 1.0f);
            if (test != effect.LFOAmplitude)
            {
                Undo.RecordObject(effect, "SounderEffect LFO Amplitude");
                effect.LFOAmplitude = test;
            }

            if (effect.LFOAmplitude > float.Epsilon)
            {
                Oscillator.WaveForm lfoForm = (Oscillator.WaveForm)EditorGUILayout.EnumPopup(new GUIContent("LFO Waveform", "Low Frequency Oscillator."), effect.LFOForm);
                if (lfoForm != effect.LFOForm)
                {
                    Undo.RecordObject(effect, "SounderEffect LFO WaveForm");
                    effect.LFOForm = lfoForm;
                }

                test = effect.LFOFrequency;
                GUIHelpers.RangedSlider(ref test, 100.0f, "LFO Frequency",
                                        "Frequency of the low frequency oscillator.", saveHash, false);
                if (test != effect.LFOFrequency)
                {
                    Undo.RecordObject(effect, "SounderEffect LFO Frequency");
                    effect.LFOFrequency = test;
                }
            }

            EditorGUILayout.Separator();
            test = EditorGUILayout.Slider(new GUIContent("Flanger Amplitude", "Resonates a copy of the sound."), effect.flangerAmplitude, .0f, 1.0f);
            if (test != effect.flangerAmplitude)
            {
                Undo.RecordObject(effect, "SounderEffect Flanger Amplitude");
                effect.flangerAmplitude = test;
            }
            if (effect.flangerAmplitude > float.Epsilon)
            {
                test = effect.flangerOffset;
                GUIHelpers.RangedSlider(ref test, 1.0f, "Flanger Offset", "Offset of the flanger, as a percent of the wavetable.", saveHash, false);
                if (test != effect.flangerOffset)
                {
                    Undo.RecordObject(effect, "SounderEffect Flanger Offset");
                    effect.flangerOffset = test;
                }

                test = effect.flangerOffsetDelta;
                GUIHelpers.RangedSlider(ref test, 25.0f, "Offset Delta", "Accellerate the flanger offset by this much.", saveHash, false);
                if (test != effect.flangerOffsetDelta)
                {
                    Undo.RecordObject(effect, "SounderEffect Flanger Offset Delta");
                    effect.flangerOffsetDelta = test;
                }
            }

            EditorGUILayout.Separator();
            test = EditorGUILayout.Slider(new GUIContent("Phase Reset", "Resets most variables to their starting values after this much time has passed."), effect.phaseReset, .0f, effect.Duration);
            if (test != effect.phaseReset)
            {
                Undo.RecordObject(effect, "SounderEffect Phase Reset");
                effect.phaseReset = test;
            }

            test = effect.echoTime;
            GUIHelpers.RangedSlider(ref test, 1.0f, "Echo Time", "Echoes the sound every echoTime seconds", saveHash, true);
            if (test != effect.echoTime)
            {
                Undo.RecordObject(effect, "SounderEffect Echo Time");
                effect.echoTime = test;
            }
            if (effect.echoTime > float.Epsilon)
            {
                test = EditorGUILayout.Slider(new GUIContent("Echo Value", "How loud the echo is as a percent of the original sound"), effect.echoValue, 0f, 1.0f);
                if (test != effect.echoValue)
                {
                    Undo.RecordObject(effect, "SounderEffect Echo Value");
                    effect.echoValue = test;
                }

                int itest = EditorGUILayout.IntSlider(new GUIContent("Echo Count", "How many times to echo. If less than 0 echoes until very quiet."), effect.echoCount, -1, 10);
                if (itest != effect.echoCount)
                {
                    Undo.RecordObject(effect, "SounderEffect Echo Count");
                    effect.echoCount = itest;
                }
            }

            EditorGUILayout.Separator();
            test = effect.highPass;
            GUIHelpers.RangedSlider(ref test, 8000.0f, "High Pass", "Frequencies lower than this are attenuated. Set to 0 to do nothing.", saveHash, true);
            if (test != effect.highPass)
            {
                Undo.RecordObject(effect, "SounderEffect High Pass");
                effect.highPass = test;
            }

            test = effect.lowPass;
            GUIHelpers.RangedSlider(ref test, 8000.0f, "Low Pass", "Frequencies higher than this are attenuated. Set to 0 to do nothing.", saveHash, true);
            if (test != effect.lowPass)
            {
                Undo.RecordObject(effect, "SounderEffect Low Pass");
                effect.lowPass = test;
            }

            test = effect.Q;
            GUIHelpers.RangedSlider(ref test, 10.0f, "Resonance", "Amount to attenuate outside frequencies.", saveHash, true);
            if (test != effect.Q)
            {
                Undo.RecordObject(effect, "SounderEffect Resonance");
                effect.Q = test;
            }
            EditorGUILayout.Separator();

            SubEffects();

            EditorGUILayout.Separator();

            GUILayout.BeginHorizontal();
            bool play = false;
            if (GUILayout.Button("Play"))
            {
                play = true;
            }
            EditorGUILayout.Separator();
            EditorGUIUtility.labelWidth = GUI.skin.label.CalcSize(new GUIContent("Play on Change")).x;
            playOnChange = EditorGUILayout.Toggle("Play on Change", playOnChange);
            EditorGUIUtility.labelWidth = GUI.skin.label.CalcSize(new GUIContent("Play Looping")).x;
            playLooping = EditorGUILayout.Toggle("Play Looping", playLooping);
            EditorGUIUtility.labelWidth = 0;

            if (GUILayout.Button("Save to .Wav"))
            {
                string path = "";
                try { path = Path.GetDirectoryName(AssetDatabase.GetAssetPath(effect)); } catch {}

                string wavName = EditorUtility.SaveFilePanel("Save sound to wav", path, effect.name, "wav");
                WavBuilder.WriteWav(wavName, effect.GetClip());
                AssetDatabase.Refresh();
            }
            GUILayout.EndHorizontal();


            play = play | DrawPresets();

            if (GUILayout.Button("Mutate"))
            {
                Undo.RecordObject(effect, "SounderEffect Mutate");

                SounderEffect se = Presets.Mutate(effect);
                effect.Copy(se);
                DestroyImmediate(se);
                play = true;
            }

            if (Event.current.type == EventType.ValidateCommand && Event.current.commandName.CompareTo("UndoRedoPerformed") == 0)
            {
                GUI.changed = true;
            }

            if (GUI.changed)
            {
                EditorUtility.SetDirty(effect);
                player.Stop();
                if (player.clip != null)
                {
                    DestroyImmediate(player.clip);
                }
                effect.MakeClip();

                if (effectTexture != null)
                {
                    DestroyImmediate(effectTexture);
                }
                effectTexture = null;
            }

            if (play || (GUI.changed && playOnChange))
            {
                player.clip   = effect.GetClip();
                player.volume = 1.0f;
                player.loop   = playLooping;
                player.Play();
            }
        }