Ejemplo n.º 1
0
        public void Init(SoundIODevice device)
        {
            OutputDevice = device;
            if (OutputDevice.ProbeError != 0)
            {
                throw new Exception($"Probe Error : {OutputDevice.ProbeError}");
            }

            Console.WriteLine($"Device Name : {OutputDevice.Name}");

            outstream = OutputDevice.CreateOutStream();
            outstream.WriteCallback     = (min, max) => write_callback(outstream, min, max);
            outstream.UnderflowCallback = () => underflow_callback(outstream);
            outstream.SoftwareLatency   = 0;
            outstream.SampleRate        = Source.Format.SampleRate;

            if (OutputDevice.SupportsFormat(SoundIODevice.Float32NE))
            {
                outstream.Format = SoundIODevice.Float32NE;
            }
            else
            {
                outstream.Dispose();
                outstream = null;
                throw new Exception("No suitable format");
            }
            outstream.Open();

            if (outstream.LayoutErrorMessage != null)
            {
                Console.WriteLine("Layout Error : " + outstream.LayoutErrorMessage);
            }
        }
Ejemplo n.º 2
0
        void write_callback(SoundIOOutStream outstream, int frame_count_min, int frame_count_max)
        {
            int frames_left = frame_count_min;
            var writeSize   = (int)(Source.Format.SampleRate * WriteLatency);

            int frame_count = Math.Max(frames_left, Math.Min(writeSize, frame_count_max));

            if (frame_count == 0)
            {
                return;
            }

            var results = outstream.BeginWrite(ref frame_count);
            SoundIOChannelLayout layout = outstream.Layout;

            var buffer = isPaused ? new float[frame_count] : Source.Next(frame_count);

            for (int frame = 0; frame < frame_count; frame++)
            {
                for (int channel = 0; channel < layout.ChannelCount; channel++)
                {
                    var area = results.GetArea(channel);
                    write_sample(area.Pointer, buffer[frame]);
                    area.Pointer += area.Step;
                }
            }

            outstream.EndWrite();
        }
Ejemplo n.º 3
0
        public void Start()
        {
            if (State != SynthesizerState.Initial)
            {
                return;
            }

            soundio = new SoundIO();
            soundio.Connect();
            soundio.FlushEvents();
            var device = soundio.GetOutputDevice(soundio.DefaultOutputDeviceIndex);

            if (device.ProbeError != 0)
            {
                throw new DX7SynthesizerException($"Cannot probe device {device.Name}.");
            }
            out_stream = device.CreateOutStream();
            if (!device.SupportsFormat(SoundIOFormat.S16LE))
            {
                throw new NotSupportedException();
            }
            out_stream.Format            = SoundIOFormat.S16LE;
            out_stream.WriteCallback     = (min, max) => WriteCallback(min, max);
            out_stream.UnderflowCallback = () => { Debug.WriteLine("underflow"); };
            out_stream.ErrorCallback     = () => {
                throw new DX7SynthesizerException($"ERROR at libsoundio: {out_stream.LayoutErrorMessage}");
            };
            out_stream.Open();
            State = SynthesizerState.Started;
            out_stream.Start();
            soundio.FlushEvents();
        }
Ejemplo n.º 4
0
        public void Initialize(SoundIODevice device, AudioFormat format)
        {
            if (format.Channels != 1)
            {
                throw new OutputInitializationException("Format must qualify channels == 1");
            }

            Device = device;
            Format = format;

            var bytesPerSample = format.BitDepth / 8;
            var capacity       = Format.SampleRate * Format.Channels * bytesPerSample * 30;

            ringBuffer = new RingBuffer <float>((uint)capacity);
            if (Device.ProbeError != 0)
            {
                throw new OutputInitializationException($"Probe Error : {Device.ProbeError}");
            }

            outstream = Device.CreateOutStream();
            outstream.WriteCallback     = (min, max) => write_callback(outstream, min, max);
            outstream.UnderflowCallback = () => underflow_callback(outstream);
            outstream.SampleRate        = Format.SampleRate;
            outstream.SoftwareLatency   = DesiredLatency.TotalSeconds;
            outstream.Format            = SoundIODevice.S16NE;

            outstream.Open();
            outstream.Layout          = SoundIOChannelLayout.GetDefault(Format.Channels);
            outstream.SoftwareLatency = DesiredLatency.TotalSeconds;
            api.FlushEvents();

            ActualLatency = TimeSpan.FromSeconds(outstream.SoftwareLatency);
        }
Ejemplo n.º 5
0
        void write_callback(SoundIOOutStream outstream, int frame_count_min, int frame_count_max)
        {
            int frameCount = frame_count_max;

            if (frameCount <= 0)
            {
                return;
            }

            var results = outstream.BeginWrite(ref frameCount);
            var samples = new float[frameCount];

            var e = new OutputBufferEventArgs(frameCount);

            Buffer?.Invoke(this, e);
            if (e.Buffer != null)
            {
                ringBuffer.Enqueue(e.Buffer);
            }

            ringBuffer.Dequeue(samples, frameCount);

            var layout = outstream.Layout;

            for (int frame = 0; frame < frameCount; frame++)
            {
                for (int channel = 0; channel < layout.ChannelCount; channel++)
                {
                    var area = results.GetArea(channel);
                    // Raspberry Piではなぜかshortじゃないと音がプツプツする
                    write_short_sample(area.Pointer, samples[frame]);
                    area.Pointer += area.Step;
                }
            }

            outstream.EndWrite();

            unsafe void write_short_sample(IntPtr ptr, float sample)
            {
                short *buf = (short *)ptr;

                *buf = (short)(sample * short.MaxValue);
            }

            unsafe void write_float_sample(IntPtr ptr, float sample)
            {
                float *buf = (float *)ptr;

                *buf = sample;
            }
        }

        void underflow_callback(SoundIOOutStream outstream)
        {
            Underflow?.Invoke(this, EventArgs.Empty);
        }
    }
}
Ejemplo n.º 6
0
        static void WriteCallback(SoundIOOutStream stream, int frameCountMin, int frameCountMax)
        {
            double floatSampleRate = stream.SampleRate;
            double secondsPerFrame = 1.0 / floatSampleRate;
            SoundIOChannelAreas areas;
            SoundIoError        err;
            int framesLeft = frameCountMax;

            while (true)
            {
                int frameCount = framesLeft;
                err = stream.BeginWrite(out areas, ref frameCount);
                if (err != SoundIoError.None)
                {
                    throw new SoundIOException(string.Format("Unrecoverable stream error: {0}.", err.GetErrorMessage()));
                }

                if (areas == null || frameCount == 0)
                {
                    break;
                }

                SoundIOChannelLayout layout = stream.Layout;

                double pitch            = 440.0;
                double radiansPerSecond = pitch * 2.0 * Math.PI;
                for (int frame = 0; frame < frameCount; frame++)
                {
                    double sample = Math.Sin((secondsOffset + frame * secondsPerFrame) * radiansPerSecond);
                    for (int channel = 0; channel < layout.ChannelCount; channel += 1)
                    {
                        writeSample(areas[channel].Pointer, sample);
                        areas[channel].AdvancePointer();
                    }
                }
                secondsOffset = (secondsOffset + secondsPerFrame * frameCount) % 1.0;

                err = stream.EndWrite();
                if (err != SoundIoError.None)
                {
                    Console.WriteLine("EndWrite failed with error: {0}.", err);
                    if (err == SoundIoError.Underflow)
                    {
                        return;
                    }

                    throw new SoundIOException(string.Format("Unrecoverable stream error: {0}.", err.GetErrorMessage()));
                }

                framesLeft -= frameCount;
                if (framesLeft <= 0)
                {
                    break;
                }
            }

            stream.Pause(wantPause);
        }
        private void SetupOutputStream()
        {
            _outputStream = _driver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount);
            _outputStream.WriteCallback += Update;

            // TODO: Setup other callbacks (errors, ect).

            _outputStream.Open();
        }
Ejemplo n.º 8
0
        unsafe void write_callback(SoundIOOutStream outstream, int frame_count_min, int frame_count_max)
        {
            int frame_count = frame_count_max;
            var results     = outstream.BeginWrite(ref frame_count);

            SoundIOChannelLayout layout = outstream.Layout;

            int readBytes = frame_count * outstream.BytesPerFrame;
            int readCount = 0;
            int tryCount  = -1;
            int read;

            while (readBytes - readCount > 0 && tryCount++ < UnderflowRetryCount)
            {
                int bufferLength = (int)_ringBuffer.GetLength();
                if (bufferLength % outstream.BytesPerSample != 0)
                {
                    bufferLength -= outstream.BytesPerSample - (bufferLength % outstream.BytesPerSample);
                }

                read       = Math.Min(bufferLength, readBytes - readCount);
                readCount += read;

                byte[] buffer = new byte[read];
                _ringBuffer.Dequeue(buffer);

                SoundIOChannelArea area;
                fixed(byte *buf = buffer)
                {
                    byte *ptr;

                    for (var i = 0; i < buffer.Length; i += outstream.BytesPerSample * layout.ChannelCount)
                    {
                        for (int channel = 0; layout.ChannelCount > channel; channel++)
                        {
                            area = results.GetArea(channel);

                            ptr = (byte *)area.Pointer;
                            for (int j = 0; j < outstream.BytesPerSample; j++)
                            {
                                *ptr = buf[i + j + (channel * outstream.BytesPerSample)];
                                ptr++;
                            }

                            area.Pointer += area.Step;
                        }
                    }
                }

                if (readBytes - readCount > 0)
                {
                    Underflow?.Invoke(this, new UnderflowEventArgs(readBytes - readCount));
                }
            }

            outstream.EndWrite();
        }
Ejemplo n.º 9
0
        public void Stop()
        {
            if (_outstream == null)
            {
                throw new Exception("SoundioOutput is not initialized");
            }

            _outstream.Dispose();
            _outstream = null;
        }
Ejemplo n.º 10
0
        public void Dispose()
        {
            _outstream?.Dispose();
            _outstream = null;

            Device?.RemoveReference();
            Device = null;

            _api?.Disconnect();
            _api?.Dispose();
            _api = null;
        }
Ejemplo n.º 11
0
        static void write_callback(SoundIOOutStream outstream, int frame_count_min, int frame_count_max)
        {
            double float_sample_rate = outstream.SampleRate;
            double seconds_per_frame = 1.0 / float_sample_rate;

            int frames_left = frame_count_max;
            int frame_count = 0;

            for (; ;)
            {
                frame_count = frames_left;
                var results = outstream.BeginWrite(ref frame_count);

                if (frame_count == 0)
                {
                    break;
                }

                SoundIOChannelLayout layout = outstream.Layout;

                double pitch = 440.0;
                double radians_per_second = pitch * 2.0 * Math.PI;
                for (int frame = 0; frame < frame_count; frame += 1)
                {
                    double sample = Math.Sin((seconds_offset + frame * seconds_per_frame) * radians_per_second);
                    for (int channel = 0; channel < layout.ChannelCount; channel += 1)
                    {
                        var area = results.GetArea(channel);
                        write_sample(area.Pointer, sample);
                        area.Pointer += area.Step;
                    }
                }
                seconds_offset = Math.IEEERemainder(seconds_offset + seconds_per_frame * frame_count, 1.0);

                outstream.EndWrite();

                frames_left -= frame_count;
                if (frames_left <= 0)
                {
                    break;
                }
            }

            outstream.Pause(want_pause);
        }
Ejemplo n.º 12
0
        private void initInternal(AudioFormat format)
        {
            if (Device == null)
            {
                throw new Exception("No device is selected");
            }

            if (Device.ProbeError != 0)
            {
                throw new OutputInitializationException($"Probe Error : {Device.ProbeError}");
            }

            _outstream = Device.CreateOutStream();
            _outstream.WriteCallback     = (min, max) => write_callback(_outstream, min, max);
            _outstream.UnderflowCallback = () => Underflow?.Invoke(this, new UnderflowEventArgs(null));
            _outstream.ErrorCallback     = () => UnrecoverableError?.Invoke(this, EventArgs.Empty);
            _outstream.SampleRate        = format.SampleRate;
            _outstream.SoftwareLatency   = DesiredLatency.TotalSeconds;

            var soundioFormat = Soundio.ToSoundioFormat(format);

            _outstream.Format = soundioFormat ?? SoundIOFormat.Invalid;

            if (_outstream.LayoutErrorMessage != null)
            {
                var msg = _outstream.LayoutErrorMessage;
                Console.WriteLine($"Channel Layout Error : {msg}");
            }

            _outstream.Open();
            _api.FlushEvents();

            Format          = Soundio.ToManagedFormat(_outstream.Format, _outstream.SampleRate, _outstream.Layout.ChannelCount);
            SoftwareLatency = TimeSpan.FromSeconds(_outstream.SoftwareLatency);

            var bytesPerSample = _outstream.BytesPerSample;
            var capacity       = Format.SampleRate * Format.Channels * bytesPerSample *
                                 _bufferDuration.TotalSeconds;

            _ringBuffer = new RingBuffer <byte>((uint)capacity);
        }
Ejemplo n.º 13
0
        void WriteCallback(SoundIOOutStream outStream, int min, int max)
        {
            if (!play_audio)
            {
                return;
            }

            Console.WriteLine($"WriteCallback invoked: {min} / {max}");

            double float_sample_rate = outStream.SampleRate;
            double seconds_per_frame = 1.0 / float_sample_rate;

            //if (max - min > samples.Length)
            //	samples = new short [max - min];

#if test_with_sine
            //double seconds_offset = 0;
#else
            synth.GetSamples(samples, 0, samples.Length);
#endif
            //if (samples.Any (s => s != 0))
            //    Console.WriteLine (string.Concat (samples.Take (50).Select (s => s.ToString ("X04"))));
            int frameRemaining = max;
            int frameCount     = frameRemaining;
            while (play_audio && frameRemaining > 0)
            {
                var results = outStream.BeginWrite(ref frameCount);
                if (frameCount == 0)
                {
                    break;
                }

#if test_with_sine
                double pitch = 440.0;
                double radians_per_second = pitch * 2.0 * Math.PI;
                for (int frame = 0; frame < frameCount; frame += 1)
                {
                    double sample = Math.Sin((seconds_offset + frame * seconds_per_frame) * radians_per_second);
                    for (int channel = 0; channel < outStream.Layout.ChannelCount; channel += 1)
                    {
                        var    area  = results.GetArea(channel);
                        double range = (double)short.MaxValue - (double)short.MinValue;
                        double val   = sample * range / 2.0;
                        unsafe { *((short *)area.Pointer) = (short)val; }
                        area.Pointer += area.Step;
                    }
                }
                seconds_offset = Math.IEEERemainder(seconds_offset + seconds_per_frame * frameCount, 1.0);
#else
                for (int i = 0; i < outStream.Layout.ChannelCount; i++)
                {
                    var area = results.GetArea(i);
                    unsafe {
                        Marshal.Copy(samples, 0, area.Pointer, samples.Length);
                        area.Pointer += area.Step * samples.Length;
                    }
                }
#endif

                outStream.EndWrite();

                frameRemaining -= frameCount;
            }
        }
Ejemplo n.º 14
0
        static void write_callback(SoundIOOutStream outstream, int frame_count_min, int frame_count_max)
        {
            SoundIOChannelAreas areas = default(SoundIOChannelAreas);
            int frames_left           = 0;
            int frame_count           = 0;

            var read_ptr   = ring_buffer.ReadPointer;
            int fill_bytes = ring_buffer.FillCount;
            int fill_count = fill_bytes / outstream.BytesPerFrame;

            if (frame_count_min > fill_count)
            {
                // Ring buffer does not have enough data, fill with zeroes.
                frames_left = frame_count_min;
                for (; ;)
                {
                    frame_count = frames_left;
                    if (frame_count <= 0)
                    {
                        return;
                    }
                    areas = outstream.BeginWrite(ref frame_count);
                    if (frame_count <= 0)
                    {
                        return;
                    }
                    var chCount = outstream.Layout.ChannelCount;
                    for (int frame = 0; frame < frame_count; frame += 1)
                    {
                        for (int ch = 0; ch < chCount; ch += 1)
                        {
                            var area = areas.GetArea(ch);
                            // FIXME: there should be more efficient way for memset(ptr, 0);
                            for (int i = 0; i < outstream.BytesPerSample; i++)
                            {
                                Marshal.WriteByte(area.Pointer, 0);
                            }
                            area.Pointer += area.Step;
                        }
                    }
                    outstream.EndWrite();
                    frames_left -= frame_count;
                }
            }

            int read_count = Math.Min(frame_count_max, fill_count);

            frames_left = read_count;

            while (frames_left > 0)
            {
                frame_count = frames_left;

                areas = outstream.BeginWrite(ref frame_count);

                if (frame_count <= 0)
                {
                    break;
                }

                var chCount  = outstream.Layout.ChannelCount;
                var copySize = outstream.BytesPerSample;
                for (int frame = 0; frame < frame_count; frame += 1)
                {
                    unsafe {
                        for (int ch = 0; ch < chCount; ch += 1)
                        {
                            var area = areas.GetArea(ch);
                            Buffer.MemoryCopy((void *)read_ptr, (void *)area.Pointer, copySize, copySize);
                            area.Pointer += area.Step;
                            read_ptr     += outstream.BytesPerSample;
                        }
                    }
                }
                outstream.EndWrite();

                frames_left -= frame_count;
            }
            ring_buffer.AdvanceReadPointer(read_count * outstream.BytesPerFrame);
        }
Ejemplo n.º 15
0
        public static int Main(string[] args)
        {
            string         exe        = "SoundIOSine";
            SoundIoBackend backend    = SoundIoBackend.None;
            string         deviceId   = null;
            bool           raw        = false;
            string         streamName = null;
            double         latency    = 0.0;
            int            sampleRate = 0;
            SoundIoError   err;
            SoundIODevice  device = null;

            for (int i = 0; i < args.Length; i++)
            {
                string arg = args[i];
                if (arg.StartsWith("--"))
                {
                    if (arg.CompareTo("--raw") == 0)
                    {
                        raw = true;
                    }
                    else
                    {
                        i++;
                        if (i >= args.Length)
                        {
                            return(Usage(exe));
                        }
                        else if (arg.CompareTo("--backend") == 0)
                        {
                            backend = (SoundIoBackend)Enum.Parse(typeof(SoundIoBackend), args[i]);
                        }
                        else if (arg.CompareTo("--device") == 0)
                        {
                            deviceId = args[i];
                        }
                        else if (arg.CompareTo("--name") == 0)
                        {
                            streamName = args[i];
                        }
                        else if (arg.CompareTo("--latency") == 0)
                        {
                            latency = double.Parse(args[i]);
                        }
                        else if (arg.CompareTo("--sample-rate") == 0)
                        {
                            sampleRate = int.Parse(args[i]);
                        }
                        else
                        {
                            return(Usage(exe));
                        }
                    }
                }
                else
                {
                    return(Usage(exe));
                }
            }

            SoundIO soundIo = new SoundIO();

            err = (backend == SoundIoBackend.None) ?
                  soundIo.Connect() : soundIo.ConnectBackend(backend);

            if (err != SoundIoError.None)
            {
                Console.Error.WriteLine("Unable to connect to backend: {0}.", err.GetErrorMessage());
                return(1);
            }

            Console.WriteLine("Backend: {0}.", soundIo.CurrentBackend);

            soundIo.FlushEvents();

            if (deviceId != null)
            {
                foreach (var dev in soundIo)
                {
                    if (dev.Aim == SoundIoDeviceAim.Output && dev.Id.Equals(deviceId) && dev.IsRaw == raw)
                    {
                        device = dev;
                        break;
                    }
                }

                if (device == null)
                {
                    Console.Error.WriteLine("Output device not found.");
                    return(1);
                }

                device.AddRef(); // Enumerator cleans up itself on dispose
            }
            else
            {
                device = soundIo.GetDefaultOutputDevice();
            }

            Console.WriteLine("Output device: {0}.", device.Name);

            if (device.ProbeError != SoundIoError.None)
            {
                Console.Error.WriteLine("Cannot probe device: {0}.", device.ProbeError.GetErrorMessage());
                return(1);
            }

            SoundIOOutStream outstream = new SoundIOOutStream(device);

            outstream.OnWriteCallback     = WriteCallback;
            outstream.OnUnderflowCallback = UnderflowCallback;
            if (streamName != null)
            {
                outstream.Name = streamName;
            }
            outstream.SoftwareLatency = latency;
            if (sampleRate != 0)
            {
                outstream.SampleRate = sampleRate;
            }

            if (device.SupportsFormat(SoundIoFormats.Float32NE))
            {
                outstream.Format = SoundIoFormats.Float32NE;
                writeSample      = WriteSampleFloat32NE;
            }
            else if (device.SupportsFormat(SoundIoFormats.Float64NE))
            {
                outstream.Format = SoundIoFormats.Float64NE;
                writeSample      = WriteSampleFloat64NE;
            }
            else if (device.SupportsFormat(SoundIoFormats.S32NE))
            {
                outstream.Format = SoundIoFormats.S32NE;
                writeSample      = WriteSampleS32NE;
            }
            else if (device.SupportsFormat(SoundIoFormats.S16NE))
            {
                outstream.Format = SoundIoFormats.S16NE;
                writeSample      = WriteSampleS16NE;
            }
            else
            {
                Console.Error.WriteLine("No suitable format available.");
                return(1);
            }

            err = outstream.Open();
            if (err != SoundIoError.None)
            {
                Console.Error.WriteLine("Unable to open device: {0}.", err.GetErrorMessage());
                return(1);
            }

            Console.WriteLine("Software latency: {0:N6}.", outstream.SoftwareLatency);
            Console.WriteLine(
                "\t'p' - pause\n" +
                "\t'u' - unpause\n" +
                "\t'P' - pause from within callback\n" +
                "\t'c' - clear buffer\n" +
                "\t'q' - quit\n");

            if (outstream.LayoutError != SoundIoError.None)
            {
                Console.Error.WriteLine("Unable to set channel layout: {0}.", outstream.LayoutError.GetErrorMessage());
            }

            err = outstream.Start();
            if (err != SoundIoError.None)
            {
                Console.Error.WriteLine("Unable to start device {0}.", err.GetErrorMessage());
                return(1);
            }

            while (true)
            {
                soundIo.FlushEvents();

                int c = Console.Read();
                if (c == 'p')
                {
                    err = outstream.Pause(true);
                    Console.Error.WriteLine("Pausing result: {0}.", err.GetErrorMessage());
                }
                else if (c == 'P')
                {
                    wantPause = true;
                }
                else if (c == 'u')
                {
                    wantPause = false;
                    err       = outstream.Pause(false);
                    Console.Error.WriteLine("Unpausing result: {0}.", err.GetErrorMessage());
                }
                else if (c == 'c')
                {
                    err = outstream.ClearBuffer();
                    Console.Error.WriteLine("Clear buffer result: {0}.", err.GetErrorMessage());
                }
                else if (c == 'q')
                {
                    break;
                }
                else if (c == '\r' || c == '\n')
                {
                    // ignore
                }
                else
                {
                    Console.Error.WriteLine("Unrecognized command: {0}.", (char)c);
                }
            }

            outstream.Dispose();
            device.Release();
            soundIo.Dispose();
            return(0);
        }
Ejemplo n.º 16
0
 static void UnderflowCallback(SoundIOOutStream o)
 {
     Console.Error.WriteLine("Underflow {0}.", underflowCount++);
 }
Ejemplo n.º 17
0
 public static void UnderflowCallback(SoundIOOutStream stream)
 {
     Console.WriteLine("Underflow");
 }
Ejemplo n.º 18
0
        public static int Main(string[] args)
        {
            int            sampleRate;
            string         filename = null;
            SoundIoBackend backend  = SoundIoBackend.None;
            string         deviceId = null;
            bool           isRaw    = false;
            SoundIoError   err;

            try
            {
                if (args.Length < 1)
                {
                    Usage();
                    return(1);
                }
                for (int i = 0; i < args.Length; i++)
                {
                    string arg = args[i];
                    if (arg.StartsWith("--"))
                    {
                        if (++i > args.Length)
                        {
                            return(Usage());
                        }
                        else if (arg.CompareTo("--backend") == 0)
                        {
                            backend = (SoundIoBackend)Enum.Parse(typeof(SoundIoBackend), args[i]);
                        }
                        else if (arg.CompareTo("--device") == 0)
                        {
                            deviceId = args[i];
                        }
                        else
                        {
                            return(Usage());
                        }
                    }
                    else
                    {
                        if (File.Exists(args[i]))
                        {
                            filename = args[i];
                        }
                        else
                        {
                            Usage();
                            return(1);
                        }
                    }
                }
                if (string.IsNullOrEmpty(filename))
                {
                    throw new Exception("Input file name can not null.");
                }
            }
            catch (IndexOutOfRangeException)
            {
                Usage();
                return(1);
            }

            using (_soundIO = new SoundIO())
            {
                using (_waveFile = new WaveFileReader(filename))
                {
                    _channels  = _waveFile.WaveFormat.Channels;
                    sampleRate = _waveFile.WaveFormat.SampleRate;
                    _soundIO.Connect();
                    _soundIO.FlushEvents();
                    SoundIODevice device = null;

                    if (deviceId != null)
                    {
                        foreach (var dev in _soundIO)
                        {
                            if (dev.Aim == SoundIoDeviceAim.Output && dev.Id.Equals(deviceId) && dev.IsRaw == isRaw)
                            {
                                device = dev;
                                break;
                            }
                        }

                        if (device == null)
                        {
                            Console.Error.WriteLine("Output device not found.");
                            return(1);
                        }

                        device.AddRef(); // Enumerator cleans up itself on dispose
                    }
                    else
                    {
                        device = _soundIO.GetDefaultOutputDevice();
                    }
                    Console.WriteLine("Device: {0}.", device.Name);
                    if (device.ProbeError != SoundIoError.None)
                    {
                        Console.WriteLine("Cannot probe device: {0}", device.ProbeError);
                        return(1);
                    }
                    Console.WriteLine("Output device: {0}", device.Name);
                    if (device.ProbeError != SoundIoError.None)
                    {
                        Console.WriteLine("Cannot probe device: {0}", device.ProbeError);
                        return(1);
                    }
                    var outstream = new SoundIOOutStream(device)
                    {
                        OnWriteCallback     = WriteCallback,
                        OnUnderflowCallback = UnderflowCallback,
                        Name       = "sio_play",
                        SampleRate = sampleRate
                    };

                    // look for maching layout for wav file...
                    var foundLayout = false;
                    foreach (var layout in device.Layouts)
                    {
                        if (layout.ChannelCount == _channels)
                        {
                            outstream.Layout = layout;
                            foundLayout      = true;
                            break;
                        }
                    }

                    // TODO: may need to look at endian issues and other formats...
                    // when paired with NAudioLite, ISampleProvider the conversion to Float32 is automatic.
                    if (device.SupportsFormat(SoundIoFormats.Float32NE))
                    {
                        outstream.Format = SoundIoFormats.Float32NE;
                    }
                    else if (device.SupportsFormat(SoundIoFormats.Float64NE))
                    {
                        outstream.Format = SoundIoFormats.Float64NE;
                    }
                    else if (device.SupportsFormat(SoundIoFormats.S32NE))
                    {
                        outstream.Format = SoundIoFormats.S32NE;
                    }
                    else if (device.SupportsFormat(SoundIoFormats.S16NE))
                    {
                        outstream.Format = SoundIoFormats.S16NE;
                    }
                    else
                    {
                        Console.WriteLine("No suitable device format available.");
                        return(1);
                    }

                    Console.WriteLine();
                    Console.WriteLine("Playing file: {0}, Format: {1}", Path.GetFullPath(filename), _waveFile.WaveFormat);

                    err = outstream.Open();
                    if (err != SoundIoError.None)
                    {
                        Console.WriteLine($"Unable to open device: {err.GetErrorMessage()}, with sample rate: {outstream.LayoutError}");
                        return(1);
                    }

                    if (outstream.LayoutError != SoundIoError.None)
                    {
                        Console.WriteLine($"Unable to set channel layout: {err.GetErrorMessage()}");
                    }

                    // revisit layout...
                    // if no suitable layout found
                    if (!foundLayout)
                    {
                        Console.WriteLine("No native channel layout found, Device Channels: {0}, Wav File Channels: {1}, requires sampler...", outstream.Layout.ChannelCount, _channels);
                    }

                    // get sample provider that matches outstream.Layout
                    if (outstream.Layout.ChannelCount == 1)
                    {
                        // mono
                        if (_waveFile.WaveFormat.Channels == 1)
                        {
                            _sampleProvider = _waveFile.ToSampleProvider();
                        }
                        else
                        {
                            _sampleProvider = _waveFile.ToSampleProvider().ToMono();
                        }
                    }
                    else if (outstream.Layout.ChannelCount == 2)
                    {
                        //stereo
                        if (_waveFile.WaveFormat.Channels == 1)
                        {
                            _sampleProvider = _waveFile.ToSampleProvider().ToStereo();
                        }
                        else
                        {
                            _sampleProvider = _waveFile.ToSampleProvider();
                        }
                    }

                    outstream.Start();
                    _soundIO.OnBackendDisconnect += SoundIo_OnBackendDisconnected;

                    while (!_fileDone)
                    {
                        System.Threading.Thread.Sleep(100);
                        _soundIO.FlushEvents();
                    }

                    System.Threading.Thread.Sleep(500);
                    if (_fileDone && outstream != null)
                    {
                        outstream.Dispose();
                        outstream = null;
                    }

                    Console.WriteLine("End Program");
                    return(0);
                }
            }
        }
Ejemplo n.º 19
0
        public static void WriteCallback(SoundIOOutStream stream, int frameCountMin, int frameCountMax)
        {
            SoundIoError err;
            DateTime     callbackTime = DateTime.Now;

            // loop to utilize as much as the frameCountMax as possible
            while (frameCountMax > 0)
            {
                int frameCount = frameCountMax;
                if ((err = stream.BeginWrite(out SoundIOChannelAreas areas, ref frameCount)) != SoundIoError.None)
                {
                    Console.WriteLine("unrecoverable stream error: {0}", err.GetErrorMessage());
                    _fileDone = true;
                }

                if (frameCount == 0)
                {
                    break;
                }

                var bufferCount = frameCount * stream.Layout.ChannelCount;

                // if buffer is done add silence based on latency to allow stream to complete through
                // audio path before stream is Disposed()
                if (_waveFile.Position >= _waveFile.Length)
                {
                    if (_startSilence)
                    {
                        // windows latency is a little higher (using DateTime to determine the callback time delay)
                        // and needs to be accoutned for...
                        _latencySeconds         -= (DateTime.Now - callbackTime).TotalMilliseconds / 1000.0;
                        _silentSamplesRemaining  = (int)((stream.SampleRate * stream.Layout.ChannelCount) * _latencySeconds);
                        _silentSamplesRemaining -= _silentSamplesAlreadySent;
                        _startSilence            = false;
                    }

                    int silentBufferSize;
                    if (_silentSamplesRemaining > bufferCount)
                    {
                        silentBufferSize         = bufferCount;
                        _silentSamplesRemaining -= bufferCount;
                    }
                    else
                    {
                        silentBufferSize        = _silentSamplesRemaining;
                        _silentSamplesRemaining = 0;
                    }

                    if (silentBufferSize > 0)
                    {
                        // create a new buffer initialized to 0 and copy to native buffer
                        var silenceBuffer = new float[silentBufferSize];
                        stream.CopyTo(silenceBuffer, 0, areas, silentBufferSize);
                    }
                    if (_silentSamplesRemaining == 0)
                    {
                        _fileDone = true;
                        stream.EndWrite();
                        stream.Pause(true);
                        return;
                    }
                    // if the remaining audioBuffer will only partially fill the frameCount
                    // copy the remaining amount and set the startSilence flag to allow
                    // stream to play to the end.
                }
                else if (_waveFile.Position + (frameCount * _waveFile.WaveFormat.Channels) >= _waveFile.Length)
                {
                    float[] audioBuffer       = new float[bufferCount];
                    var     actualSamplesRead = _sampleProvider.Read(audioBuffer, 0, bufferCount);
                    stream.CopyTo(audioBuffer, 0, areas, bufferCount);

                    _silentSamplesAlreadySent = bufferCount - actualSamplesRead;
                    _latencySeconds           = 0.0;
                    _startSilence             = true;
                }
                else
                {
                    // copy audioBuffer data to native buffer and advance the bufferPos
                    float[] audioBuffer       = new float[bufferCount];
                    var     actualSamplesRead = _sampleProvider.Read(audioBuffer, 0, bufferCount);
                    stream.CopyTo(audioBuffer, 0, areas, actualSamplesRead);

                    if (_waveFile.Position >= _waveFile.Length)
                    {
                        _latencySeconds = 0.0;
                        _startSilence   = true;
                    }
                }

                if ((err = stream.EndWrite()) != SoundIoError.None)
                {
                    if (err == SoundIoError.Underflow)
                    {
                        return;
                    }
                    Console.WriteLine("Unrecoverable stream error: {0}", err.GetErrorMessage());
                    _fileDone = true;
                }

                if (_startSilence)
                {
                    // get actual latency in order to compute number of silent frames
                    stream.GetLatency(out _latencySeconds);
                    callbackTime = DateTime.Now;
                }

                // loop until frameCountMax is used up
                frameCountMax -= frameCount;
            }
            return;
        }
Ejemplo n.º 20
0
 static void underflow_callback(SoundIOOutStream outstream)
 {
     //Console.Error.WriteLine("underflow {0}", underflow_callback_count++);
 }
Ejemplo n.º 21
0
        /// <summary>
        /// Determines if SoundIO can connect to a supported backend
        /// </summary>
        /// <returns></returns>
        private static bool IsSupportedInternal()
        {
            SoundIO          context = null;
            SoundIODevice    device  = null;
            SoundIOOutStream stream  = null;

            bool backendDisconnected = false;

            try
            {
                context = new SoundIO();

                context.OnBackendDisconnect = (i) => {
                    backendDisconnected = true;
                };

                context.Connect();
                context.FlushEvents();

                if (backendDisconnected)
                {
                    return(false);
                }

                if (context.OutputDeviceCount == 0)
                {
                    return(false);
                }

                device = FindNonRawDefaultAudioDevice(context);

                if (device == null || backendDisconnected)
                {
                    return(false);
                }

                stream = device.CreateOutStream();

                if (stream == null || backendDisconnected)
                {
                    return(false);
                }

                return(true);
            }
            catch
            {
                return(false);
            }
            finally
            {
                if (stream != null)
                {
                    stream.Dispose();
                }

                if (context != null)
                {
                    context.Dispose();
                }
            }
        }
Ejemplo n.º 22
0
 public void Stop()
 {
     IsRunning = false;
     outstream?.Dispose();
     outstream = null;
 }