using System; using System.Collections.Generic; using System.Text.RegularExpressions; using System.IO; using System.Threading; using NAudio.CoreAudioApi; using NAudio.Wave; using NAudio.Wave.SampleProviders; namespace SeikaCenter { class AudioCapture { WasapiLoopbackCapture capDev = null; // ループバックキャプチャオブジェクト BufferedWaveProvider buffWaveProvider = null; WaveFileWriter capWriter = null; Stream fstream = null; WasapiOut loopbackDev = null; string tempFilePath; string waveFilePath; bool isDispatchDefault = false; public decimal SkipAudioHeadTime { get; set; } public string CapDeviceName { get; set; } public bool EnableLoopback { get; set; } private void capDataAvailable(object sender, WaveInEventArgs e) { capWriter.Write(e.Buffer, 0, e.BytesRecorded); if (isDispatchDefault) buffWaveProvider.AddSamples(e.Buffer, 0, e.BytesRecorded); } private void RecordingStopped(object sender, StoppedEventArgs e) { try { if (capWriter != null) { capWriter.Dispose(); capWriter = null; } if (fstream != null) { //fstream.Flush(); fstream.Close(); fstream.Dispose(); fstream = null; } //capDev.Dispose(); //capDev = null; } catch(Exception eau3) { Console.WriteLine("eau3:{0}", eau3.Message); } } private void InitCaptureSettings() { ReleaseCaptureObjects(); capDev = new WasapiLoopbackCapture(GetMMDevice(CapDeviceName)); capDev.ShareMode = AudioClientShareMode.Shared; capDev.DataAvailable += capDataAvailable; capDev.RecordingStopped += RecordingStopped; if (EnableLoopback) { loopbackDev = new WasapiOut(); buffWaveProvider = new BufferedWaveProvider(capDev.WaveFormat); loopbackDev.Init(buffWaveProvider); } } private void ReleaseCaptureObjects() { if (capDev != null) { capDev.DataAvailable -= capDataAvailable; capDev.RecordingStopped -= RecordingStopped; capDev.Dispose(); capDev = null; } loopbackDev?.Dispose(); capWriter?.Dispose(); } public AudioCapture() { CapDeviceName = ""; EnableLoopback = false; InitCaptureSettings(); } public AudioCapture(string capDevicename, bool enableLoopback) { ResetCapture(capDevicename, enableLoopback); } public void ResetCapture(string capDevicename, bool enableLoopback) { CapDeviceName = capDevicename; EnableLoopback = enableLoopback; InitCaptureSettings(); } public bool StartCapture(string WavFilePath) { waveFilePath = WavFilePath; tempFilePath = Regex.Replace(waveFilePath, @"\.[Ww][Aa][Vv]$", ".temp.wav"); try { if (File.Exists(tempFilePath)) File.Delete(tempFilePath); if (File.Exists(waveFilePath)) File.Delete(waveFilePath); fstream = new FileStream(tempFilePath, FileMode.Create, FileAccess.Write, FileShare.Write); capWriter = new WaveFileWriter(fstream, capDev.WaveFormat); capDev.StartRecording(); loopbackDev?.Play(); } catch (Exception eff) { Console.WriteLine("eff:{0},{1}", eff.Message, eff.StackTrace); capWriter?.Dispose(); fstream?.Dispose(); return false; } return true; } public double GetWaveTime() { TimeSpan wavetime = new TimeSpan(0, 0, 0); using (var inputReader = new AudioFileReader(waveFilePath)) { var mono = new StereoToMonoSampleProvider(inputReader); mono.LeftVolume = 0.0f; // discard the left channel mono.RightVolume = 1.0f; // keep the right channel WaveFileWriter.CreateWaveFile16(waveFilePath, mono); wavetime = inputReader.TotalTime; } return wavetime.TotalMilliseconds == 0 ? -1.0 : wavetime.TotalMilliseconds; } public double StopCapture() { TimeSpan wavetime = new TimeSpan(0, 0, 0); try { loopbackDev?.Stop(); capDev.StopRecording(); while (capDev.CaptureState != CaptureState.Stopped) Thread.Sleep(5); int cnt1 = 0; while (IsLocked(tempFilePath)) { Thread.Sleep(10); cnt1++; if (cnt1 > 3000) return -1; } TimeSpan ms = TimeSpan.FromMilliseconds(Convert.ToDouble(SkipAudioHeadTime)); using (var inputReader = new AudioFileReader(tempFilePath)) { Console.WriteLine("time1:{0}", inputReader.CurrentTime); inputReader.CurrentTime = ms; Console.WriteLine("time2:{0}", inputReader.CurrentTime); var mono = new StereoToMonoSampleProvider(inputReader); mono.LeftVolume = 0.0f; // discard the left channel mono.RightVolume = 1.0f; // keep the right channel WaveFileWriter.CreateWaveFile16(waveFilePath, mono); wavetime = inputReader.TotalTime - ms; Console.WriteLine("time3:{0}", wavetime); } int cnt2 = 0; while (IsLocked(waveFilePath)) { Thread.Sleep(10); cnt2++; if (cnt2 > 3000) return -1; } if (File.Exists(tempFilePath)) File.Delete(tempFilePath); } catch(Exception eu2) { Console.WriteLine("eu2:{0}", eu2); } return wavetime.TotalMilliseconds == 0 ? -1.0: wavetime.TotalMilliseconds; } public bool PlayWaveFile(string WavFilePath) { try { var dev = GetMMDevice(CapDeviceName); using (var audioFile = new AudioFileReader(WavFilePath)) using (var outputDevice = new WasapiOut(dev, AudioClientShareMode.Shared, false, 0)) { outputDevice.Init(audioFile); outputDevice.Play(); while (outputDevice.PlaybackState == PlaybackState.Playing) { Thread.Sleep(5); } } dev?.Dispose(); } catch (Exception f2sd) { Console.WriteLine("f2sd:{0}", f2sd.Message); } return true; } public string GetDefaultMMDeviceName() { MMDevice mmCapDev = new MMDeviceEnumerator().GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); return mmCapDev.FriendlyName; } public string[] GetMMDeviceNames(string capDeviceName) { List list = new List(); foreach (var item in new MMDeviceEnumerator().EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active)) { list.Add(item.FriendlyName); } return list.ToArray(); } public MMDevice GetMMDevice(string mmDeviceName) { MMDevice mmCapDev = null; if (mmDeviceName == "") { mmCapDev = new MMDeviceEnumerator().GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); } else { foreach (var item in new MMDeviceEnumerator().EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active)) { if (item.FriendlyName == mmDeviceName) { mmCapDev = item; break; } } if (mmCapDev == null) { mmCapDev = new MMDeviceEnumerator().GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); } } return mmCapDev; } private bool IsLocked(string filePath) { FileStream stream = null; try { stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None); } catch { return true; } finally { if (stream != null) { stream.Close(); } } return false; } } }