Example #1
0
        /// <summary>
        /// Render callback for the output node. Can simulataneously write to a file.
        /// </summary>
        /// <returns>The render delegate.</returns>
        /// <param name="actionFlags">Action flags.</param>
        /// <param name="timeStamp">Time stamp.</param>
        /// <param name="busNumber">Bus number.</param>
        /// <param name="numberFrames">Number frames.</param>
        /// <param name="data">Data.</param>
        AudioUnitStatus OutputRenderDelegate(AudioUnitRenderActionFlags actionFlags, AudioTimeStamp timeStamp, uint busNumber, uint numberFrames, AudioBuffers data)
        {
            // propagate tempo change on start of a new cycle
            if (_tempoChanged)
            {
                PropagateTempoChange(_tempoChangeRatio);

                _tempoChanged = false;
            }

            var e = MixerNode.Render(ref actionFlags, timeStamp, busNumber, numberFrames, data);

            cycle++;

            // check for a queued layer change
            if (Metronome.Instance.NeedToChangeLayer == true)
            {
                Metronome.Instance.CycleToChange     = cycle;
                Metronome.Instance.NeedToChangeLayer = false;
                Metronome.Instance.ChangeLayerTurnstyle.Set();
            }
            else if (Metronome.Instance.NeedToChangeLayer == null)
            {
                // top off the fat forward
                double cycleDiff = cycle - Metronome.Instance.CycleToChange;

                Metronome.Instance.FastForwardChangedLayers(cycleDiff);

                foreach (KeyValuePair <int, Layer> pair in Metronome.Instance.LayersToChange)
                {
                    Layer copy = pair.Value;
                    Layer real = Metronome.Instance.Layers[pair.Key];

                    int  numberRemoved = 0;
                    bool isMuted       = false;
                    // remove old sources
                    foreach (IStreamProvider src in real.GetAllStreams())
                    {
                        RemoveStream(src);
                        src.Dispose();
                        numberRemoved++;
                        isMuted = src.IsMuted;
                    }

                    // transfer sources to real layer
                    real.AudioSources    = copy.AudioSources;
                    real.BaseAudioSource = copy.BaseAudioSource;
                    real.PitchSource     = copy.PitchSource;
                    real.BaseSourceName  = copy.BaseSourceName;
                    real.HasHiHatOpen    = copy.HasHiHatOpen;
                    real.HasHiHatClosed  = copy.HasHiHatClosed;
                    real.Beat            = copy.Beat;

                    foreach (IStreamProvider src in real.GetAllStreams().OrderBy(x => x.Info.HiHatStatus != StreamInfoProvider.HiHatStatuses.Down))
                    {
                        src.IsMuted = isMuted;
                        src.Layer   = real;
                        if (numberRemoved <= 0)
                        {
                            // it crashes if we try to add a rendercallback for preexisting bus
                            Metronome.Instance.AddAudioSource(src);
                        }
                        else
                        {
                            Streams.Add(src);
                        }

                        numberRemoved--;
                    }

                    copy.AudioSources    = null;
                    copy.BaseAudioSource = null;
                    copy.PitchSource     = null;
                    copy.Beat            = null;
                    Metronome.Instance.Layers.Remove(copy);

                    foreach (IStreamProvider src in Streams)
                    {
                        // keep muting consistent when shuffling buffer indexs
                        if (src.IsMuted)
                        {
                            EnableInput(src, false);
                        }
                        else
                        {
                            EnableInput(src, true);
                        }

                        SetPan(src, src.Pan);
                        SetInputVolume(src, (float)src.Volume);
                    }
                }

                Metronome.Instance.LayersToChange.Clear();
                Metronome.Instance.NeedToChangeLayer = false;


                // trigger beat changed event
                AppKit.NSApplication.SharedApplication.BeginInvokeOnMainThread(
                    () => { Metronome.Instance.OnBeatChanged(null); });
            }

            // check if recording to file
            if (_fileRecordingQueued)
            {
                // convert the buffer
                using (AudioBuffers convBuffer = new AudioBuffers(1))
                {
                    convBuffer[0] = new AudioBuffer()
                    {
                        DataByteSize   = data[0].DataByteSize,
                        NumberChannels = 1,
                        Data           = Marshal.AllocHGlobal(sizeof(float) * data[0].DataByteSize)
                    };

                    _converter.ConvertComplexBuffer((int)numberFrames, data, convBuffer);

                    _file.Write(numberFrames, convBuffer);
                }
            }

            return(AudioUnitStatus.OK);
        }