1 /* This file is part of OpenTX Recorder.
2 * OpenTX Recorder is free software: you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation, either version 3 of the License, or
5 * (at your option) any later version.
7 * This code is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with OpenTX Recorder. If not, see <http://www.gnu.org/licenses/>.
15 * Copyright 2014 Tomas Andersson */
19 using System
.Threading
;
20 using System
.Runtime
.InteropServices
;
24 public delegate void BufferDoneEventHandler(IntPtr data
, int size
);
26 internal class WaveInBuffer
28 private ManualResetEvent m_RecordEvent
= new ManualResetEvent(false);
29 private IntPtr m_WaveIn
;
31 private WaveNative
.WaveHdr m_Header
;
32 private byte[] m_HeaderData
;
33 private GCHandle m_HeaderHandle
;
34 private GCHandle m_HeaderDataHandle
;
36 internal static void WaveInProc(IntPtr hdrvr
, int uMsg
, int dwUser
, ref WaveNative
.WaveHdr wavhdr
, int dwParam2
)
38 if (uMsg
== WaveNative
.MM_WIM_DATA
)
40 GCHandle h
= (GCHandle
)wavhdr
.dwUser
;
41 WaveInBuffer buf
= (WaveInBuffer
)h
.Target
;
46 public WaveInBuffer(IntPtr waveInHandle
, int size
)
48 m_WaveIn
= waveInHandle
;
50 m_HeaderHandle
= GCHandle
.Alloc(m_Header
, GCHandleType
.Pinned
);
51 m_Header
.dwUser
= (IntPtr
)GCHandle
.Alloc(this);
52 m_HeaderData
= new byte[size
];
53 m_HeaderDataHandle
= GCHandle
.Alloc(m_HeaderData
, GCHandleType
.Pinned
);
54 m_Header
.lpData
= m_HeaderDataHandle
.AddrOfPinnedObject();
55 m_Header
.dwBufferLength
= size
;
56 WaveInRecorder
.ThrowOnError(WaveNative
.waveInPrepareHeader(m_WaveIn
, ref m_Header
, Marshal
.SizeOf(m_Header
)));
61 WaveInRecorder
.ThrowOnError(WaveNative
.waveInUnprepareHeader(m_WaveIn
, ref m_Header
, Marshal
.SizeOf(m_Header
)));
62 m_HeaderHandle
.Free();
63 m_Header
.lpData
= IntPtr
.Zero
;
64 m_HeaderDataHandle
.Free();
69 get { return m_Header.dwBufferLength; }
74 get { return m_Header.lpData; }
79 m_RecordEvent
.Reset();
80 WaveInRecorder
.ThrowOnError(WaveNative
.waveInAddBuffer(m_WaveIn
, ref m_Header
, Marshal
.SizeOf(m_Header
)));
85 m_RecordEvent
.WaitOne();
88 private void OnCompleted()
94 public class WavFileWriter
96 BinaryWriter filewriter
;
97 long audiobyteswritten
;
99 public WavFileWriter(string filename
, int samplerate
, int bits
, int channels
)
101 filewriter
= new BinaryWriter(File
.Open(filename
, FileMode
.Create
));
103 filewriter
.Write((uint)0x46464952); // "RIFF"
104 filewriter
.Write((uint)0); // chunkisize (filled in when we close)
105 filewriter
.Write((uint)0x45564157); // "WAVE"
108 filewriter
.Write((uint)0x20746d66); // "fmt "
109 filewriter
.Write((uint)16);
110 filewriter
.Write((UInt16
)1);
111 filewriter
.Write((UInt16
)channels
);
112 filewriter
.Write((uint)samplerate
);
113 filewriter
.Write((uint)(channels
* samplerate
* bits
/ 8));
114 filewriter
.Write((UInt16
)(channels
* bits
/ 8));
115 filewriter
.Write((UInt16
)bits
);
118 filewriter
.Write((uint)0x61746164); // "data"
119 filewriter
.Write((uint)0); // chunkisize (filled in when we close)
122 public void Write(byte[] data
, int size
)
124 filewriter
.Write(data
, 0, size
);
125 audiobyteswritten
+= size
;
130 filewriter
.Seek(4, SeekOrigin
.Begin
);
131 filewriter
.Write((uint)(audiobyteswritten
+ 36)); // 36 = total size of chunk headers
133 filewriter
.Seek(40, SeekOrigin
.Begin
);
134 filewriter
.Write((uint)audiobyteswritten
);
140 public class WaveInRecorder
142 private IntPtr m_WaveIn
;
143 private WaveInBuffer buffer1
, buffer2
;
144 private WaveInBuffer m_CurrentBuffer
;
145 private Thread m_Thread
;
146 private BufferDoneEventHandler m_DoneProc
;
147 private bool m_Finished
;
149 private WaveNative
.WaveDelegate m_BufferProc
= new WaveNative
.WaveDelegate(WaveInBuffer
.WaveInProc
);
151 public static int DeviceCount
153 get { return WaveNative.waveInGetNumDevs(); }
156 public WaveInRecorder(int device
, WaveFormat format
, int bufferSize
, BufferDoneEventHandler doneProc
)
158 m_DoneProc
= doneProc
;
159 WaveInRecorder
.ThrowOnError(WaveNative
.waveInOpen(out m_WaveIn
, device
, format
, m_BufferProc
, 0, WaveNative
.CALLBACK_FUNCTION
));
161 buffer1
= new WaveInBuffer(m_WaveIn
, bufferSize
);
162 buffer2
= new WaveInBuffer(m_WaveIn
, bufferSize
);
167 m_CurrentBuffer
= buffer1
;
169 WaveInRecorder
.ThrowOnError(WaveNative
.waveInStart(m_WaveIn
));
170 m_Thread
= new Thread(new ThreadStart(ThreadProc
));
177 WaveInRecorder
.ThrowOnError(WaveNative
.waveInReset(m_WaveIn
));
189 WaveInRecorder
.ThrowOnError(WaveNative
.waveInClose(m_WaveIn
));
190 m_WaveIn
= IntPtr
.Zero
;
193 private void ThreadProc()
197 m_CurrentBuffer
= (m_CurrentBuffer
== buffer1
) ? buffer2
: buffer1
;
199 m_CurrentBuffer
.WaitFor();
200 m_DoneProc(m_CurrentBuffer
.Data
, m_CurrentBuffer
.Size
);
202 if (m_Finished
) break;
204 m_CurrentBuffer
.Record();
208 public static void ThrowOnError(int err
)
210 if (err
!= WaveNative
.MMSYSERR_NOERROR
) throw new Exception(err
.ToString());