mirror of
https://github.com/opentx/opentx.git
synced 2025-07-13 11:29:51 +03:00
Switched recorder library to one custom made by Tomas Andersson.
Lots of small changes to both Speaker and Recorder.
This commit is contained in:
parent
5a5da9da32
commit
eceedefa94
16 changed files with 575 additions and 69 deletions
|
@ -10,7 +10,7 @@
|
|||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with OpenTX Recorder. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2014 Kjell Kernen */
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
<ListView Name="lvSentences"
|
||||
SelectionMode="Single"
|
||||
MouseDoubleClick="lvSentences_MouseDoubleClick"
|
||||
MouseDoubleClick="sentenceRowDblClk"
|
||||
VerticalAlignment="Stretch" >
|
||||
|
||||
|
||||
|
@ -55,7 +55,7 @@
|
|||
<ComboBox Name="cbLanguages"
|
||||
DisplayMemberPath="lName"
|
||||
SelectedValuePath="sName"
|
||||
SelectionChanged="cbLanguages_SelectionChanged"
|
||||
SelectionChanged="switchLanguage"
|
||||
Width="200" Height="23" />
|
||||
</StackPanel>
|
||||
|
||||
|
@ -86,21 +86,21 @@
|
|||
|
||||
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,5,0,0">
|
||||
<Button Content="New Sentence" Width="120" Height="32" Name="buttonAddItem" Click="buttonAddItem_Click"/>
|
||||
<Button Name="btnRecord" ToolTip="Start recording" Click="btnRecord_Click" Margin="20,0,0,0" >
|
||||
<Button Content="New Sentence" Width="120" Height="32" Name="buttonAddItem" Click="addSentence"/>
|
||||
<Button Name="btnRecord" ToolTip="Start recording" Click="record" Margin="20,0,0,0" >
|
||||
<Image Height="32" Source="/OpenTXrecorder;component/record.png"/>
|
||||
</Button>
|
||||
<Button Name="btnStop" ToolTip="Stop recording" Click="btnStop_Click" Margin="5,0,0,0" >
|
||||
<Button Name="btnStop" ToolTip="Stop recording" Click="stop" Margin="5,0,0,0" >
|
||||
<Image Height="32" Source="/OpenTXrecorder;component/stop.png" />
|
||||
</Button>
|
||||
<Button Name="btnPlay" ToolTip="Play Sentence" Click="btnPlay_Click" Margin="30,0,0,0" >
|
||||
<Button Name="btnPlay" ToolTip="Play Sentence" Click="play" Margin="30,0,0,0" >
|
||||
<Image Height="32" Source="/OpenTXrecorder;component/play.png" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<Separator Height="5" />
|
||||
<Label Content="RECORDING" Name="lblRecording" HorizontalAlignment="Center" Foreground="Red" FontSize="24" FontWeight="Black" Visibility="Hidden" />
|
||||
</StackPanel>
|
||||
<Image Grid.Row="0" Grid.Column="1" Name="openTXLogo" Height="48" MouseLeftButtonDown="openTXLogo_MouseLeftButtonDown" HorizontalAlignment="Right" Cursor="Hand" Margin="0,0,0,0" Source="/OpenTXrecorder;component/recorder_logo.png" Stretch="Uniform" VerticalAlignment="Bottom" />
|
||||
<Image Grid.Row="0" Grid.Column="1" Name="openTXLogo" Height="48" MouseLeftButtonDown="about" HorizontalAlignment="Right" Cursor="Hand" Margin="0,0,0,0" Source="/OpenTXrecorder;component/recorder_logo.png" Stretch="Uniform" VerticalAlignment="Bottom" />
|
||||
|
||||
</Grid>
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with OpenTX Recorder. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2014 Kjell Kernen */
|
||||
|
||||
|
@ -26,19 +26,22 @@ using System.Windows.Controls;
|
|||
using System.Windows.Input;
|
||||
using System.Windows.Navigation;
|
||||
using System.Runtime.InteropServices;
|
||||
using WaveLib;
|
||||
using System.Net;
|
||||
|
||||
|
||||
namespace OpenTXrecorder
|
||||
{
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
[DllImport("winmm.dll", EntryPoint = "mciSendStringA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
|
||||
private static extern int mciSendString(string lpstrCommand, string lpstrReturnString, int uReturnLength, int hwndCallback);
|
||||
|
||||
SentenceTables tables = new SentenceTables();
|
||||
Languages languages = new Languages();
|
||||
Language curLang;
|
||||
Sentences sentences = new Sentences();
|
||||
int added_files = 0;
|
||||
Languages languages = new Languages();
|
||||
Environment env;
|
||||
|
||||
WavFileWriter filewriter;
|
||||
WaveInRecorder recorder;
|
||||
byte[] tmparray;
|
||||
bool isRecording = false;
|
||||
|
||||
public MainWindow()
|
||||
|
@ -61,7 +64,7 @@ namespace OpenTXrecorder
|
|||
languages.Add("Swedish", "se");
|
||||
languages.Add("Slovak", "sk");
|
||||
languages.Add("Spanish", "es");
|
||||
curLang = languages[0];
|
||||
env = new Environment(languages[0].sName);
|
||||
cbLanguages.SelectedIndex = 0; // Note: Sets current langugage -> triggers loadlanguage()
|
||||
}
|
||||
|
||||
|
@ -72,38 +75,38 @@ namespace OpenTXrecorder
|
|||
|
||||
try
|
||||
{
|
||||
system_strings = System.IO.File.ReadAllLines(@"SOUNDS\" + curLang.sName + @"\SYSTEM\system_sounds.txt");
|
||||
other_strings = System.IO.File.ReadAllLines(@"SOUNDS\" + curLang.sName + @"\other_sounds.txt");
|
||||
system_strings = System.IO.File.ReadAllLines(env.systemSounds());
|
||||
other_strings = System.IO.File.ReadAllLines(env.otherSounds());
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
system_strings = tables.default_system_strings[tables.toInt(curLang.sName)];
|
||||
other_strings = tables.default_other_strings[tables.toInt(curLang.sName)];
|
||||
system_strings = tables.default_system_strings[tables.toInt(env.shortLanguage)];
|
||||
other_strings = tables.default_other_strings[tables.toInt(env.shortLanguage)];
|
||||
}
|
||||
sentences.RemoveRange(0, sentences.Count);
|
||||
sentences.Clear();
|
||||
|
||||
foreach (string str in system_strings)
|
||||
sentences.Add(str, @"SOUNDS\" + curLang.sName + @"\SYSTEM\");
|
||||
sentences.Add(str, env.sysDir());
|
||||
|
||||
sentences.Add(@";^ System Sounds ^;", "");
|
||||
|
||||
foreach (string str in other_strings)
|
||||
sentences.Add(str, @"SOUNDS\" + curLang.sName + @"\");
|
||||
sentences.Add(str, env.baseDir());
|
||||
|
||||
lvSentences.Items.Refresh(); // Workaround - Two way binding is better
|
||||
}
|
||||
|
||||
private void saveLanguage()
|
||||
{
|
||||
System.IO.Directory.CreateDirectory(@"SOUNDS\" + curLang.sName + @"\SYSTEM");
|
||||
StreamWriter sw = File.CreateText(@"SOUNDS\" + curLang.sName + @"\SYSTEM\system_sounds.txt");
|
||||
System.IO.Directory.CreateDirectory(env.sysDir());
|
||||
StreamWriter sw = File.CreateText(env.systemSounds());
|
||||
int i;
|
||||
for (i = 0; sentences[i].fileName != ""; i++)
|
||||
{
|
||||
sw.WriteLine(sentences[i].toRaw());
|
||||
}
|
||||
sw.Close();
|
||||
sw = File.CreateText(@"SOUNDS\" + curLang.sName + @"\other_sounds.txt");
|
||||
sw = File.CreateText(env.otherSounds());
|
||||
for (i++; i < sentences.Count; i++)
|
||||
{
|
||||
sw.WriteLine(sentences[i].toRaw());
|
||||
|
@ -111,15 +114,26 @@ namespace OpenTXrecorder
|
|||
sw.Close();
|
||||
}
|
||||
|
||||
private void cbLanguages_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
private void switchLanguage(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
curLang = (Language)e.AddedItems[0];
|
||||
env = new Environment(((Language)e.AddedItems[0]).sName);
|
||||
loadLanguage();
|
||||
}
|
||||
|
||||
private void lvSentences_MouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||
|
||||
private void about(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (this.lvSentences.SelectedItems.Count < 1) return;
|
||||
AboutWindow aboutWindow = new AboutWindow();
|
||||
aboutWindow.ShowDialog();
|
||||
}
|
||||
|
||||
private void sentenceRowDblClk(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (this.lvSentences.SelectedItems.Count < 1)
|
||||
return;
|
||||
|
||||
if (isRecording)
|
||||
stop(null, null);
|
||||
|
||||
Sentence sentence = (Sentence)this.lvSentences.SelectedItem;
|
||||
if (sentence.fileExists)
|
||||
|
@ -133,27 +147,33 @@ namespace OpenTXrecorder
|
|||
}
|
||||
}
|
||||
|
||||
private void openTXLogo_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
private void play(object sender, RoutedEventArgs e)
|
||||
{
|
||||
AboutWindow aboutWindow = new AboutWindow();
|
||||
aboutWindow.ShowDialog();
|
||||
sentenceRowDblClk(null, null);
|
||||
}
|
||||
|
||||
private void buttonAddItem_Click(object sender, RoutedEventArgs e)
|
||||
private void addSentence(object sender, RoutedEventArgs e)
|
||||
{
|
||||
added_files++;
|
||||
sentences.Add(new Sentence("new_file_" + added_files.ToString() + ";New Description;New Voice Message", @"SOUNDS\" + curLang.sName + @"\"));
|
||||
int i = 0;
|
||||
string newFile;
|
||||
do
|
||||
{
|
||||
newFile = "new_file_" + i.ToString();
|
||||
i++;
|
||||
}
|
||||
while (env.fileExists(newFile));
|
||||
sentences.Add(new Sentence(newFile + ";New Description;New Voice Message", env.baseDir()));
|
||||
this.lvSentences.Items.Refresh();
|
||||
this.lvSentences.SelectedIndex = this.lvSentences.Items.Count - 1;
|
||||
this.lvSentences.ScrollIntoView(this.lvSentences.SelectedItem);
|
||||
}
|
||||
|
||||
private void btnRecord_Click(object sender, RoutedEventArgs e)
|
||||
private void record(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (this.lvSentences.SelectedItems.Count < 1) return;
|
||||
|
||||
if (isRecording)
|
||||
btnStop_Click(sender, e);
|
||||
stop(sender, e);
|
||||
else
|
||||
{
|
||||
saveLanguage();
|
||||
|
@ -164,30 +184,77 @@ namespace OpenTXrecorder
|
|||
Directory.CreateDirectory(Path.GetDirectoryName(path)); // Create directory if it doesn't exist
|
||||
System.IO.File.WriteAllText(path, ""); // Creates and flushes a file if it does not exist
|
||||
|
||||
mciSendString("open new Type waveaudio Alias recsound", "", 0, 0);
|
||||
mciSendString("record recsound", "", 0, 0);
|
||||
int samplerate = 16000;
|
||||
int bits = 16; // 8 or 16
|
||||
int channels = 1; // 1 or 2
|
||||
|
||||
filewriter = new WavFileWriter(path, samplerate, bits, channels);
|
||||
WaveFormat fmt = new WaveFormat(samplerate, bits, channels);
|
||||
|
||||
// devicenumber, wavformat, buffersize, callback
|
||||
int buffersize = 1024 * 2;
|
||||
tmparray = new byte[buffersize];
|
||||
recorder = new WaveInRecorder(-1, fmt, buffersize, this.DataArrived);
|
||||
}
|
||||
}
|
||||
|
||||
private void btnStop_Click(object sender, RoutedEventArgs e)
|
||||
private void stop(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (this.lvSentences.SelectedItems.Count < 1) return;
|
||||
if (!isRecording || this.lvSentences.SelectedItems.Count < 1)
|
||||
return;
|
||||
|
||||
isRecording = false;
|
||||
recorder.Close();
|
||||
filewriter.Close();
|
||||
|
||||
int index = this.lvSentences.SelectedIndex;
|
||||
lblRecording.Visibility = System.Windows.Visibility.Hidden;
|
||||
isRecording = false;
|
||||
Sentence sentence = (Sentence)this.lvSentences.SelectedItem;
|
||||
mciSendString("save recsound " + sentence.fullPath(), "", 0, 0);
|
||||
mciSendString("close recsound ", "", 0, 0);
|
||||
|
||||
loadLanguage(); // Called to refresh the sentence data of the current langugae
|
||||
this.lvSentences.SelectedIndex = index;
|
||||
}
|
||||
|
||||
private void btnPlay_Click(object sender, RoutedEventArgs e)
|
||||
private void DataArrived(IntPtr data, int size)
|
||||
{
|
||||
lblRecording.Visibility = System.Windows.Visibility.Hidden;
|
||||
lvSentences_MouseDoubleClick(sender, null);
|
||||
if (!isRecording)
|
||||
return;
|
||||
|
||||
Marshal.Copy(data, tmparray, 0, size);
|
||||
filewriter.Write(tmparray);
|
||||
}
|
||||
}
|
||||
|
||||
public class Environment
|
||||
{
|
||||
public string shortLanguage { get; set; }
|
||||
public string baseDir() { return @"SOUNDS\" + shortLanguage + @"\"; }
|
||||
public string sysDir() { return @"SOUNDS\" + shortLanguage + @"\SYSTEM\"; }
|
||||
public string otherSounds() { return baseDir() + "other_sounds.txt"; }
|
||||
public string systemSounds() { return sysDir() + "system_sounds.txt"; }
|
||||
|
||||
public bool fileExists(string fName)
|
||||
{
|
||||
if (File.Exists(System.AppDomain.CurrentDomain.BaseDirectory + baseDir() + fName + ".wav"))
|
||||
return true;
|
||||
if (File.Exists(System.AppDomain.CurrentDomain.BaseDirectory + sysDir() + fName + ".wav"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public Environment(string str)
|
||||
{
|
||||
shortLanguage = str;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Data container classes
|
||||
*/
|
||||
public class Languages : List<Language>
|
||||
{
|
||||
public void Add(string longer, string shorter)
|
||||
{
|
||||
this.Add(new Language { lName = longer, sName = shorter });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,21 +264,21 @@ namespace OpenTXrecorder
|
|||
public string sName { get; set; }
|
||||
}
|
||||
|
||||
public class Languages : List<Language>
|
||||
public class Sentences : List<Sentence>
|
||||
{
|
||||
public void Add(string longer, string shorter)
|
||||
public void Add(string rawString, string dirPath)
|
||||
{
|
||||
this.Add(new Language { lName = longer, sName = shorter });
|
||||
this.Add(new Sentence(rawString, dirPath));
|
||||
}
|
||||
}
|
||||
|
||||
public class Sentence
|
||||
{
|
||||
public bool fileExists { get; set; }
|
||||
public string fileName { get; set; }
|
||||
public string description { get; set; }
|
||||
public string voiceString { get; set; }
|
||||
public string path { get; set; }
|
||||
public bool fileExists { get; set; }
|
||||
|
||||
public Sentence(string rawString, string dirPath)
|
||||
{
|
||||
|
@ -234,11 +301,4 @@ namespace OpenTXrecorder
|
|||
}
|
||||
}
|
||||
|
||||
public class Sentences : List<Sentence>
|
||||
{
|
||||
public void Add(string rawString, string dirPath)
|
||||
{
|
||||
this.Add(new Sentence(rawString, dirPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,6 +81,8 @@
|
|||
<DependentUpon>App.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="sentencetables.cs" />
|
||||
<Compile Include="WaveIn.cs" />
|
||||
<Compile Include="WaveNative.cs" />
|
||||
<Page Include="MainWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
|
|
101
sound/recorder/Program.cs
Normal file
101
sound/recorder/Program.cs
Normal file
|
@ -0,0 +1,101 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using WaveLib;
|
||||
|
||||
namespace AudioRecorder
|
||||
{
|
||||
class Program
|
||||
{
|
||||
WavFileWriter filewriter;
|
||||
byte[] tmparray;
|
||||
|
||||
void Go()
|
||||
{
|
||||
int samplerate = 16000;
|
||||
int bits = 16; // 8 or 16
|
||||
int channels = 1; // 1 or 2
|
||||
|
||||
filewriter = new WavFileWriter("out.wav", samplerate, bits, channels);
|
||||
|
||||
WaveFormat fmt = new WaveFormat(samplerate, bits, channels);
|
||||
|
||||
// devicenumber, wavformat, buffersize, callback
|
||||
int buffersize = 16384;
|
||||
WaveInRecorder rec = new WaveInRecorder(-1, fmt, buffersize, this.DataArrived);
|
||||
tmparray = new byte[buffersize];
|
||||
|
||||
Console.WriteLine("Recording - press Enter to end");
|
||||
Console.ReadLine();
|
||||
rec.Close();
|
||||
|
||||
filewriter.Close();
|
||||
|
||||
Console.WriteLine("Bye");
|
||||
}
|
||||
|
||||
private void DataArrived(IntPtr data, int size)
|
||||
{
|
||||
Console.WriteLine("DataArrived {0} bytes", size);
|
||||
Marshal.Copy(data, tmparray, 0, size);
|
||||
filewriter.Write(tmparray);
|
||||
}
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
new Program().Go();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class WavFileWriter
|
||||
{
|
||||
BinaryWriter filewriter;
|
||||
long audiobyteswritten;
|
||||
|
||||
public WavFileWriter(string filename, int samplerate, int bits, int channels)
|
||||
{
|
||||
filewriter = new BinaryWriter(File.Open(filename, FileMode.Create));
|
||||
|
||||
filewriter.Write((uint)0x46464952); // "RIFF"
|
||||
filewriter.Write((uint)0); // chunkisize (filled in when we close)
|
||||
filewriter.Write((uint)0x45564157); // "WAVE"
|
||||
|
||||
// subchunk1
|
||||
filewriter.Write((uint)0x20746d66); // "fmt "
|
||||
filewriter.Write((uint)16);
|
||||
filewriter.Write((UInt16)1);
|
||||
filewriter.Write((UInt16)channels);
|
||||
filewriter.Write((uint)samplerate);
|
||||
filewriter.Write((uint)(channels*samplerate*bits/8));
|
||||
filewriter.Write((UInt16)(channels*bits/8));
|
||||
filewriter.Write((UInt16)bits);
|
||||
|
||||
// subchunk2
|
||||
filewriter.Write((uint)0x61746164); // "data"
|
||||
filewriter.Write((uint)0); // chunkisize (filled in when we close)
|
||||
}
|
||||
|
||||
public void Write(byte[] data)
|
||||
{
|
||||
filewriter.Write(data);
|
||||
audiobyteswritten += data.Length;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
filewriter.Seek(4, SeekOrigin.Begin);
|
||||
filewriter.Write((uint)(audiobyteswritten + 36)); // 36 = total size of chunk headers
|
||||
|
||||
filewriter.Seek(40, SeekOrigin.Begin);
|
||||
filewriter.Write((uint)audiobyteswritten);
|
||||
|
||||
filewriter.Close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@
|
|||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with OpenTX Recorder. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2014 Kjell Kernen */
|
||||
|
||||
|
|
214
sound/recorder/WaveIn.cs
Normal file
214
sound/recorder/WaveIn.cs
Normal file
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* This is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2014 Tomas Andersson */
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WaveLib
|
||||
{
|
||||
public delegate void BufferDoneEventHandler(IntPtr data, int size);
|
||||
|
||||
internal class WaveInBuffer
|
||||
{
|
||||
private ManualResetEvent m_RecordEvent = new ManualResetEvent(false);
|
||||
private IntPtr m_WaveIn;
|
||||
|
||||
private WaveNative.WaveHdr m_Header;
|
||||
private byte[] m_HeaderData;
|
||||
private GCHandle m_HeaderHandle;
|
||||
private GCHandle m_HeaderDataHandle;
|
||||
|
||||
internal static void WaveInProc(IntPtr hdrvr, int uMsg, int dwUser, ref WaveNative.WaveHdr wavhdr, int dwParam2)
|
||||
{
|
||||
if (uMsg == WaveNative.MM_WIM_DATA)
|
||||
{
|
||||
GCHandle h = (GCHandle)wavhdr.dwUser;
|
||||
WaveInBuffer buf = (WaveInBuffer)h.Target;
|
||||
buf.OnCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
public WaveInBuffer(IntPtr waveInHandle, int size)
|
||||
{
|
||||
m_WaveIn = waveInHandle;
|
||||
|
||||
m_HeaderHandle = GCHandle.Alloc(m_Header, GCHandleType.Pinned);
|
||||
m_Header.dwUser = (IntPtr)GCHandle.Alloc(this);
|
||||
m_HeaderData = new byte[size];
|
||||
m_HeaderDataHandle = GCHandle.Alloc(m_HeaderData, GCHandleType.Pinned);
|
||||
m_Header.lpData = m_HeaderDataHandle.AddrOfPinnedObject();
|
||||
m_Header.dwBufferLength = size;
|
||||
WaveInRecorder.ThrowOnError(WaveNative.waveInPrepareHeader(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header)));
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
WaveInRecorder.ThrowOnError(WaveNative.waveInUnprepareHeader(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header)));
|
||||
m_HeaderHandle.Free();
|
||||
m_Header.lpData = IntPtr.Zero;
|
||||
m_HeaderDataHandle.Free();
|
||||
}
|
||||
|
||||
public int Size
|
||||
{
|
||||
get { return m_Header.dwBufferLength; }
|
||||
}
|
||||
|
||||
public IntPtr Data
|
||||
{
|
||||
get { return m_Header.lpData; }
|
||||
}
|
||||
|
||||
public void Record()
|
||||
{
|
||||
m_RecordEvent.Reset();
|
||||
WaveInRecorder.ThrowOnError(WaveNative.waveInAddBuffer(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header)));
|
||||
}
|
||||
|
||||
public void WaitFor()
|
||||
{
|
||||
m_RecordEvent.WaitOne();
|
||||
}
|
||||
|
||||
private void OnCompleted()
|
||||
{
|
||||
m_RecordEvent.Set();
|
||||
}
|
||||
}
|
||||
|
||||
class WavFileWriter
|
||||
{
|
||||
BinaryWriter filewriter;
|
||||
long audiobyteswritten;
|
||||
|
||||
public WavFileWriter(string filename, int samplerate, int bits, int channels)
|
||||
{
|
||||
filewriter = new BinaryWriter(File.Open(filename, FileMode.Create));
|
||||
|
||||
filewriter.Write((uint)0x46464952); // "RIFF"
|
||||
filewriter.Write((uint)0); // chunkisize (filled in when we close)
|
||||
filewriter.Write((uint)0x45564157); // "WAVE"
|
||||
|
||||
// subchunk1
|
||||
filewriter.Write((uint)0x20746d66); // "fmt "
|
||||
filewriter.Write((uint)16);
|
||||
filewriter.Write((UInt16)1);
|
||||
filewriter.Write((UInt16)channels);
|
||||
filewriter.Write((uint)samplerate);
|
||||
filewriter.Write((uint)(channels * samplerate * bits / 8));
|
||||
filewriter.Write((UInt16)(channels * bits / 8));
|
||||
filewriter.Write((UInt16)bits);
|
||||
|
||||
// subchunk2
|
||||
filewriter.Write((uint)0x61746164); // "data"
|
||||
filewriter.Write((uint)0); // chunkisize (filled in when we close)
|
||||
}
|
||||
|
||||
public void Write(byte[] data)
|
||||
{
|
||||
filewriter.Write(data);
|
||||
audiobyteswritten += data.Length;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
filewriter.Seek(4, SeekOrigin.Begin);
|
||||
filewriter.Write((uint)(audiobyteswritten + 36)); // 36 = total size of chunk headers
|
||||
|
||||
filewriter.Seek(40, SeekOrigin.Begin);
|
||||
filewriter.Write((uint)audiobyteswritten);
|
||||
|
||||
filewriter.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public class WaveInRecorder
|
||||
{
|
||||
private IntPtr m_WaveIn;
|
||||
private WaveInBuffer buffer1, buffer2;
|
||||
private WaveInBuffer m_CurrentBuffer;
|
||||
private Thread m_Thread;
|
||||
private BufferDoneEventHandler m_DoneProc;
|
||||
private bool m_Finished;
|
||||
|
||||
private WaveNative.WaveDelegate m_BufferProc = new WaveNative.WaveDelegate(WaveInBuffer.WaveInProc);
|
||||
|
||||
public static int DeviceCount
|
||||
{
|
||||
get { return WaveNative.waveInGetNumDevs(); }
|
||||
}
|
||||
|
||||
public WaveInRecorder(int device, WaveFormat format, int bufferSize, BufferDoneEventHandler doneProc)
|
||||
{
|
||||
m_DoneProc = doneProc;
|
||||
WaveInRecorder.ThrowOnError(WaveNative.waveInOpen(out m_WaveIn, device, format, m_BufferProc, 0, WaveNative.CALLBACK_FUNCTION));
|
||||
|
||||
buffer1 = new WaveInBuffer(m_WaveIn, bufferSize);
|
||||
buffer2 = new WaveInBuffer(m_WaveIn, bufferSize);
|
||||
|
||||
buffer1.Record();
|
||||
buffer2.Record();
|
||||
|
||||
m_CurrentBuffer = buffer1;
|
||||
|
||||
WaveInRecorder.ThrowOnError(WaveNative.waveInStart(m_WaveIn));
|
||||
m_Thread = new Thread(new ThreadStart(ThreadProc));
|
||||
m_Thread.Start();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
m_Finished = true;
|
||||
WaveInRecorder.ThrowOnError(WaveNative.waveInReset(m_WaveIn));
|
||||
|
||||
m_Thread.Join();
|
||||
m_Thread = null;
|
||||
m_DoneProc = null;
|
||||
|
||||
buffer1.WaitFor();
|
||||
buffer2.WaitFor();
|
||||
|
||||
buffer1.Close();
|
||||
buffer2.Close();
|
||||
|
||||
WaveInRecorder.ThrowOnError(WaveNative.waveInClose(m_WaveIn));
|
||||
m_WaveIn = IntPtr.Zero;
|
||||
}
|
||||
|
||||
private void ThreadProc()
|
||||
{
|
||||
while (!m_Finished)
|
||||
{
|
||||
m_CurrentBuffer = (m_CurrentBuffer == buffer1) ? buffer2 : buffer1;
|
||||
|
||||
m_CurrentBuffer.WaitFor();
|
||||
m_DoneProc(m_CurrentBuffer.Data, m_CurrentBuffer.Size);
|
||||
|
||||
if (m_Finished) break;
|
||||
|
||||
m_CurrentBuffer.Record();
|
||||
}
|
||||
}
|
||||
|
||||
public static void ThrowOnError(int err)
|
||||
{
|
||||
if (err != WaveNative.MMSYSERR_NOERROR) throw new Exception(err.ToString());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
129
sound/recorder/WaveNative.cs
Normal file
129
sound/recorder/WaveNative.cs
Normal file
|
@ -0,0 +1,129 @@
|
|||
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
|
||||
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
|
||||
// PURPOSE.
|
||||
//
|
||||
// Email: ianier@hotmail.com
|
||||
//
|
||||
// Copyright (C) 1999-2003 Ianier Munoz. All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WaveLib
|
||||
{
|
||||
public enum WaveFormats
|
||||
{
|
||||
Pcm = 1,
|
||||
Float = 3
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class WaveFormat
|
||||
{
|
||||
public short wFormatTag;
|
||||
public short nChannels;
|
||||
public int nSamplesPerSec;
|
||||
public int nAvgBytesPerSec;
|
||||
public short nBlockAlign;
|
||||
public short wBitsPerSample;
|
||||
public short cbSize;
|
||||
|
||||
public WaveFormat(int rate, int bits, int channels)
|
||||
{
|
||||
wFormatTag = (short)WaveFormats.Pcm;
|
||||
nChannels = (short)channels;
|
||||
nSamplesPerSec = rate;
|
||||
wBitsPerSample = (short)bits;
|
||||
cbSize = 0;
|
||||
|
||||
nBlockAlign = (short)(channels * (bits / 8));
|
||||
nAvgBytesPerSec = nSamplesPerSec * nBlockAlign;
|
||||
}
|
||||
}
|
||||
|
||||
internal class WaveNative
|
||||
{
|
||||
// consts
|
||||
public const int MMSYSERR_NOERROR = 0; // no error
|
||||
|
||||
public const int MM_WOM_OPEN = 0x3BB;
|
||||
public const int MM_WOM_CLOSE = 0x3BC;
|
||||
public const int MM_WOM_DONE = 0x3BD;
|
||||
|
||||
public const int MM_WIM_OPEN = 0x3BE;
|
||||
public const int MM_WIM_CLOSE = 0x3BF;
|
||||
public const int MM_WIM_DATA = 0x3C0;
|
||||
|
||||
public const int CALLBACK_FUNCTION = 0x00030000; // dwCallback is a FARPROC
|
||||
|
||||
public const int TIME_MS = 0x0001; // time in milliseconds
|
||||
public const int TIME_SAMPLES = 0x0002; // number of wave samples
|
||||
public const int TIME_BYTES = 0x0004; // current byte offset
|
||||
|
||||
// callbacks
|
||||
public delegate void WaveDelegate(IntPtr hdrvr, int uMsg, int dwUser, ref WaveHdr wavhdr, int dwParam2);
|
||||
|
||||
// structs
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)] public struct WaveHdr
|
||||
{
|
||||
public IntPtr lpData; // pointer to locked data buffer
|
||||
public int dwBufferLength; // length of data buffer
|
||||
public int dwBytesRecorded; // used for input only
|
||||
public IntPtr dwUser; // for client's use
|
||||
public int dwFlags; // assorted flags (see defines)
|
||||
public int dwLoops; // loop control counter
|
||||
public IntPtr lpNext; // PWaveHdr, reserved for driver
|
||||
public int reserved; // reserved for driver
|
||||
}
|
||||
|
||||
private const string mmdll = "winmm.dll";
|
||||
|
||||
// WaveOut calls
|
||||
[DllImport(mmdll)]
|
||||
public static extern int waveOutGetNumDevs();
|
||||
[DllImport(mmdll)]
|
||||
public static extern int waveOutPrepareHeader(IntPtr hWaveOut, ref WaveHdr lpWaveOutHdr, int uSize);
|
||||
[DllImport(mmdll)]
|
||||
public static extern int waveOutUnprepareHeader(IntPtr hWaveOut, ref WaveHdr lpWaveOutHdr, int uSize);
|
||||
[DllImport(mmdll)]
|
||||
public static extern int waveOutWrite(IntPtr hWaveOut, ref WaveHdr lpWaveOutHdr, int uSize);
|
||||
[DllImport(mmdll)]
|
||||
public static extern int waveOutOpen(out IntPtr hWaveOut, int uDeviceID, WaveFormat lpFormat, WaveDelegate dwCallback, int dwInstance, int dwFlags);
|
||||
[DllImport(mmdll)]
|
||||
public static extern int waveOutReset(IntPtr hWaveOut);
|
||||
[DllImport(mmdll)]
|
||||
public static extern int waveOutClose(IntPtr hWaveOut);
|
||||
[DllImport(mmdll)]
|
||||
public static extern int waveOutPause(IntPtr hWaveOut);
|
||||
[DllImport(mmdll)]
|
||||
public static extern int waveOutRestart(IntPtr hWaveOut);
|
||||
[DllImport(mmdll)]
|
||||
public static extern int waveOutGetPosition(IntPtr hWaveOut, out int lpInfo, int uSize);
|
||||
[DllImport(mmdll)]
|
||||
public static extern int waveOutSetVolume(IntPtr hWaveOut, int dwVolume);
|
||||
[DllImport(mmdll)]
|
||||
public static extern int waveOutGetVolume(IntPtr hWaveOut, out int dwVolume);
|
||||
|
||||
// WaveIn calls
|
||||
[DllImport(mmdll)]
|
||||
public static extern int waveInGetNumDevs();
|
||||
[DllImport(mmdll)]
|
||||
public static extern int waveInAddBuffer(IntPtr hwi, ref WaveHdr pwh, int cbwh);
|
||||
[DllImport(mmdll)]
|
||||
public static extern int waveInClose(IntPtr hwi);
|
||||
[DllImport(mmdll)]
|
||||
public static extern int waveInOpen(out IntPtr phwi, int uDeviceID, WaveFormat lpFormat, WaveDelegate dwCallback, int dwInstance, int dwFlags);
|
||||
[DllImport(mmdll)]
|
||||
public static extern int waveInPrepareHeader(IntPtr hWaveIn, ref WaveHdr lpWaveInHdr, int uSize);
|
||||
[DllImport(mmdll)]
|
||||
public static extern int waveInUnprepareHeader(IntPtr hWaveIn, ref WaveHdr lpWaveInHdr, int uSize);
|
||||
[DllImport(mmdll)]
|
||||
public static extern int waveInReset(IntPtr hwi);
|
||||
[DllImport(mmdll)]
|
||||
public static extern int waveInStart(IntPtr hwi);
|
||||
[DllImport(mmdll)]
|
||||
public static extern int waveInStop(IntPtr hwi);
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@
|
|||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with OpenTX Recorder. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2014 Kjell Kernen */
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with OpenTX Recorder. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2014 Kjell Kernen */
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with OpenTX Speaker. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2014 Kjell Kernen */
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with OpenTX Speaker. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2014 Kjell Kernen */
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with OpenTX Speaker. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2014 Kjell Kernen */
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with OpenTX Speaker. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2014 Kjell Kernen */
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with OpenTX Speaker. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2014 Kjell Kernen */
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with OpenTX Speaker. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2014 Kjell Kernen */
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue