Exemplo n.º 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);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Renders the given number of seconds to the given wav file
        /// </summary>
        /// <param name="fileName">File name.</param>
        /// <param name="seconds">Seconds.</param>
        public void RenderToFile(string fileName, double seconds)
        {
            long samples = (long)(seconds * Metronome.SampleRate);

            var inputStream = MixerNode.GetAudioFormat(AudioUnitScopeType.Output);

            var outputStream = AudioStreamBasicDescription.CreateLinearPCM(44100, 2);

            AudioConverter converter = AudioConverter.Create(inputStream, outputStream);

            var file = ExtAudioFile.CreateWithUrl(
                new Foundation.NSUrl(fileName, false),
                AudioFileType.WAVE,
                outputStream,
                AudioFileFlags.EraseFlags,
                out ExtAudioFileError e
                );

            long samplesRead = 0;

            // initialize the buffers
            var buffers = new AudioBuffers(2);

            buffers[0] = new AudioBuffer()
            {
                DataByteSize   = BufferSize * 4,
                NumberChannels = 1,
                Data           = Marshal.AllocHGlobal(sizeof(float) * BufferSize)
            };
            buffers[1] = new AudioBuffer()
            {
                DataByteSize   = BufferSize * 4,
                NumberChannels = 1,
                Data           = Marshal.AllocHGlobal(sizeof(float) * BufferSize)
            };

            var convBuffers = new AudioBuffers(1);

            convBuffers[0] = new AudioBuffer()
            {
                DataByteSize   = BufferSize * 4,
                NumberChannels = 2,
                Data           = Marshal.AllocHGlobal(sizeof(float) * BufferSize)
            };

            while (samples > 0)
            {
                int numSamples = (int)(Math.Min(BufferSize, samples));

                // get samples from the mixer
                Render((uint)numSamples, buffers, samplesRead);

                // conver to the file's format
                converter.ConvertComplexBuffer(numSamples, buffers, convBuffers);

                // write samples to the file
                var error = file.Write((uint)numSamples, convBuffers);
                if (error != ExtAudioFileError.OK)
                {
                    throw new ApplicationException();
                }

                samples     -= BufferSize;
                samplesRead += numSamples;
            }

            buffers.Dispose();
            convBuffers.Dispose();
            converter.Dispose();
            file.Dispose();
        }