Пример #1
0
        public void Dispose()
        {
            _instream?.Dispose();
            _instream = null;

            Device?.RemoveReference();
            Device = null;

            _api?.Disconnect();
            _api?.Dispose();
        }
Пример #2
0
        private void initInternal(AudioFormat format)
        {
            if (Device == null)
            {
                throw new Exception("No device is selected");
            }

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

            var native = Soundio.ToSoundioFormat(format);

            if (!native.HasValue)
            {
                throw new NotSupportedException("Format is not supported : " + format);
            }

            _instream                  = Device.CreateInStream();
            _instream.Format           = native.Value;
            _instream.SampleRate       = format.SampleRate;
            _instream.ReadCallback     = ReadCallback;
            _instream.OverflowCallback = () => Overflow?.Invoke(this, EventArgs.Empty);
            _instream.ErrorCallback    = () => UnrecoverableError?.Invoke(this, EventArgs.Empty);
            _instream.SoftwareLatency  = DesiredLatency.TotalSeconds;
            _instream.Open();

            // Open後にチャンネルは設定しないと動作しない模様
            if (Device.CurrentLayout.ChannelCount != format.Channels)
            {
                checkFormatInternal(format, out var channelLayout);
                if (!channelLayout.HasValue)
                {
                    throw new NotSupportedException("No suitable channel layout found : " + format.Channels);
                }

                _instream.Layout = channelLayout.Value;
            }
            _instream.SoftwareLatency = DesiredLatency.TotalSeconds;

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


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

            _ringBuffer = new RingBuffer <byte>((uint)capacity);
        }
Пример #3
0
        static void ReadCallback(SoundIOInStream instream, int frameCountMin, int frameCountMax)
        {
            SoundIORingBuffer   ringBuffer = ringBuffers[instream.UserData];
            SoundIOChannelAreas areas;
            SoundIoError        err;

            IntPtr writePtr  = ringBuffer.WritePointer;
            int    freeBytes = ringBuffer.FreeCount;
            int    freeCount = freeBytes / instream.BytesPerFrame;

            if (frameCountMin > freeCount)
            {
                throw new SoundIOException("Ring buffer overflow");
            }

            int writeFrames = Math.Min(freeCount, frameCountMax);
            int framesLeft  = writeFrames;

            while (true)
            {
                int frameCount = framesLeft;

                err = instream.BeginRead(out areas, ref frameCount);
                if (err != SoundIoError.None)
                {
                    throw new SoundIOException(string.Format("Begin read error: {0}.", err.GetErrorMessage()));
                }

                if (frameCount == 0)
                {
                    break;
                }

                if (areas == null)
                {
                    // Due to an overflow there is a hole. Fill the ring buffer with
                    // silence for the size of the hole.
                    int count = frameCount * instream.BytesPerFrame;
                    for (int i = 0; i < count; i++)
                    {
                        Marshal.WriteByte(writePtr + i, 0);
                    }
                }
                else
                {
                    int channelCount   = instream.Layout.ChannelCount;
                    int bytesPerSample = instream.BytesPerSample;
                    for (int frame = 0; frame < frameCount; frame++)
                    {
                        for (int ch = 0; ch < channelCount; ch++)
                        {
                            unsafe
                            {
                                Buffer.MemoryCopy((void *)areas[ch].Pointer, (void *)writePtr, bytesPerSample, bytesPerSample);
                            }
                            areas[ch].AdvancePointer();
                            writePtr += bytesPerSample;
                        }
                    }
                }

                err = instream.EndRead();
                if (err != SoundIoError.None)
                {
                    throw new SoundIOException(string.Format("End read error: {0}.", err.GetErrorMessage()));
                }

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

            int advanceBytes = writeFrames * instream.BytesPerFrame;

            ringBuffer.AdvanceWritePointer(advanceBytes);
        }
Пример #4
0
        public static int Main(string[] args)
        {
            string         exe      = "SoundIORecord";
            SoundIoBackend backend  = SoundIoBackend.None;
            string         deviceId = null;
            bool           isRaw    = true;
            string         outfile  = "1.pcm";
            SoundIoError   err;

            for (int i = 0; i < args.Length; i++)
            {
                string arg = args[i];
                if (arg.StartsWith("--"))
                {
                    if (arg.CompareTo("--raw") == 0)
                    {
                        isRaw = true;
                    }
                    else 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
                    {
                        return(Usage(exe));
                    }
                }
                else if (outfile == null)
                {
                    outfile = arg;
                }
                else
                {
                    return(Usage(exe));
                }
            }

            if (outfile == null)
            {
                return(Usage(exe));
            }

            using (SoundIO soundIo = new SoundIO())
            {
                err = (backend == SoundIoBackend.None) ? soundIo.Connect() : soundIo.ConnectBackend(backend);
                if (err != SoundIoError.None)
                {
                    Console.Error.WriteLine("Error connecting: {0}.", err.GetErrorMessage());
                    return(1);
                }
                soundIo.FlushEvents();
                SoundIODevice selectedDevice = null;
                if (deviceId != null)
                {
                    foreach (var dev in soundIo)
                    {
                        if (dev.Aim == SoundIoDeviceAim.Input && dev.Id.Equals(deviceId) && dev.IsRaw == isRaw)
                        {
                            selectedDevice = dev;
                            break;
                        }
                    }

                    if (selectedDevice == null)
                    {
                        Console.Error.WriteLine("Invalid device id: {0}.", deviceId);
                        return(1);
                    }

                    selectedDevice.AddRef(); // Enumerator cleans up itself on dispose
                }
                else
                {
                    selectedDevice = soundIo.GetDefaultInputDevice();
                    if (selectedDevice == null)
                    {
                        Console.Error.WriteLine("No input devices available.");
                        return(1);
                    }
                }

                Console.WriteLine("Device: {0}.", selectedDevice.Name);

                if (selectedDevice.ProbeError != 0)
                {
                    Console.Error.WriteLine("Unable to probe device: {0}.", selectedDevice.ProbeError.GetErrorMessage());
                    return(1);
                }

                selectedDevice.SortChannelLayouts();

                int sampleRate = prioritizedSampleRates.FirstOrDefault(sr => selectedDevice.SupportsSampleRate(sr));
                if (sampleRate == 0)
                {
                    sampleRate = selectedDevice.SampleRates[0].Max;
                }

                SoundIoFormat fmt = prioritizedFormats.FirstOrDefault(f => selectedDevice.SupportsFormat(f));
                if (fmt == SoundIoFormat.Invalid)
                {
                    fmt = selectedDevice.Formats[0];
                }

                using (SoundIOInStream instream = new SoundIOInStream(selectedDevice))
                {
                    instream.Format             = fmt;
                    instream.SampleRate         = sampleRate;
                    instream.OnReadCallback     = ReadCallback;
                    instream.OnOverflowCallback = OverflowCallback;

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

                    Console.WriteLine("{0} {1}Hz {2} interleaved", instream.Layout.Name, sampleRate, fmt.GetFormatString());

                    const int         ringBufferDurationSeconds = 30;
                    int               capacity   = ringBufferDurationSeconds * instream.SampleRate * instream.BytesPerFrame;
                    SoundIORingBuffer ringBuffer = new SoundIORingBuffer(soundIo, capacity);
                    instream.UserData = ringBuffer.Handle;
                    ringBuffers.Add(ringBuffer.Handle, ringBuffer);

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

                    Console.WriteLine("Recording data for 10 seconds.");
                    int timeout = 100;
                    using (var fs = File.OpenWrite(outfile))
                    {
                        byte[] buffer = new byte[capacity];
                        while (true) // No memory allocations allowed
                        {
                            soundIo.FlushEvents();
                            Thread.Sleep(100);
                            int    fillBytes = ringBuffer.FillCount;
                            IntPtr readBuf   = ringBuffer.ReadPointer;
                            Marshal.Copy(readBuf, buffer, 0, fillBytes);
                            fs.Write(buffer, 0, fillBytes);

                            ringBuffer.AdvanceReadPointer(fillBytes);

                            if (--timeout <= 0)
                            {
                                break;
                            }
                        }
                    }
                }
                selectedDevice.Release();
                return(0);
            }
        }
Пример #5
0
 static void OverflowCallback(SoundIOInStream instream)
 {
     Console.Error.WriteLine("Overflow {0}.", overflowCount++);
 }
Пример #6
0
 static void overflow_callback(SoundIOInStream instream)
 {
     Console.Error.WriteLine("overflow {0}", overflow_callback_count++);
 }
Пример #7
0
        static void read_callback(SoundIOInStream instream, int frame_count_min, int frame_count_max)
        {
            var write_ptr  = ring_buffer.WritePointer;
            int free_bytes = ring_buffer.FreeCount;
            int free_count = free_bytes / instream.BytesPerFrame;

            if (frame_count_min > free_count)
            {
                throw new InvalidOperationException("ring buffer overflow");                  // panic()
            }
            int write_frames = Math.Min(free_count, frame_count_max);
            int frames_left  = write_frames;

            for (; ;)
            {
                int frame_count = frames_left;

                var areas = instream.BeginRead(ref frame_count);

                if (frame_count == 0)
                {
                    break;
                }

                if (areas.IsEmpty)
                {
                    // Due to an overflow there is a hole. Fill the ring buffer with
                    // silence for the size of the hole.
                    for (int i = 0; i < frame_count * instream.BytesPerFrame; i++)
                    {
                        Marshal.WriteByte(write_ptr + i, 0);
                    }
                    Console.Error.WriteLine("Dropped {0} frames due to internal overflow", frame_count);
                }
                else
                {
                    for (int frame = 0; frame < frame_count; frame += 1)
                    {
                        int chCount  = instream.Layout.ChannelCount;
                        int copySize = instream.BytesPerSample;
                        unsafe {
                            for (int ch = 0; ch < chCount; ch += 1)
                            {
                                var area = areas.GetArea(ch);
                                Buffer.MemoryCopy((void *)area.Pointer, (void *)write_ptr, copySize, copySize);
                                area.Pointer += area.Step;
                                write_ptr    += copySize;
                            }
                        }
                    }
                }

                instream.EndRead();

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

            int advance_bytes = write_frames * instream.BytesPerFrame;

            ring_buffer.AdvanceWritePointer(advance_bytes);
        }