mirror of
https://github.com/opentx/opentx.git
synced 2025-07-12 19:10:19 +03:00
Corrected bug that caused uneven data length for 16-bit wav files (big no, no). Added speed increase for recorded sound.
This commit is contained in:
parent
76368f3e71
commit
47b79b16ba
3 changed files with 128 additions and 61 deletions
|
@ -10,11 +10,11 @@
|
||||||
|
|
||||||
|
|
||||||
<Window.Resources>
|
<Window.Resources>
|
||||||
<DataTemplate x:Key="image">
|
<DataTemplate x:Key="imagePlay">
|
||||||
<Image x:Name="TheImage" />
|
<Image x:Name="ThePlayImage" />
|
||||||
<DataTemplate.Triggers>
|
<DataTemplate.Triggers>
|
||||||
<DataTrigger Binding="{Binding Path=fileExists}" Value="true">
|
<DataTrigger Binding="{Binding Path=fileExists}" Value="true">
|
||||||
<Setter TargetName="TheImage" Property="Source" Value="/OpenTXrecorder;component/play.png" />
|
<Setter TargetName="ThePlayImage" Property="Source" Value="/OpenTXrecorder;component/play.png" />
|
||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
</DataTemplate.Triggers>
|
</DataTemplate.Triggers>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
@ -30,15 +30,17 @@
|
||||||
<ListView Name="lvSentences"
|
<ListView Name="lvSentences"
|
||||||
SelectionMode="Single"
|
SelectionMode="Single"
|
||||||
MouseDoubleClick="play"
|
MouseDoubleClick="play"
|
||||||
VerticalAlignment="Stretch" >
|
VerticalAlignment="Stretch"
|
||||||
|
ItemsSource="{Binding sentences, Mode=TwoWay}"
|
||||||
|
IsSynchronizedWithCurrentItem="True"
|
||||||
|
>
|
||||||
<ListView.View>
|
<ListView.View>
|
||||||
<GridView>
|
<GridView>
|
||||||
<GridView.Columns>
|
<GridView.Columns>
|
||||||
<GridViewColumn Header="File Name" Width="90" DisplayMemberBinding="{Binding fileName}" />
|
<GridViewColumn Header="File Name" Width="90" DisplayMemberBinding="{Binding fileName}" />
|
||||||
<GridViewColumn Header="Description" Width="190" DisplayMemberBinding="{Binding description}" />
|
<GridViewColumn Header="Description" Width="190" DisplayMemberBinding="{Binding description}" />
|
||||||
<GridViewColumn Header="Voice" Width="190" DisplayMemberBinding="{Binding voiceString}" />
|
<GridViewColumn Header="Voice" Width="190" DisplayMemberBinding="{Binding voiceString}" />
|
||||||
<GridViewColumn Width="30" CellTemplate="{StaticResource image}" />
|
<GridViewColumn Width="30" CellTemplate="{StaticResource imagePlay}" />
|
||||||
</GridView.Columns>
|
</GridView.Columns>
|
||||||
</GridView>
|
</GridView>
|
||||||
</ListView.View>
|
</ListView.View>
|
||||||
|
@ -55,7 +57,8 @@
|
||||||
DisplayMemberPath="lName"
|
DisplayMemberPath="lName"
|
||||||
SelectedValuePath="sName"
|
SelectedValuePath="sName"
|
||||||
SelectionChanged="switchLanguage"
|
SelectionChanged="switchLanguage"
|
||||||
Width="200" Height="23" />
|
Width="200" Height="23"
|
||||||
|
ItemsSource="{Binding languages, Mode=TwoWay}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Separator Height="5" />
|
<Separator Height="5" />
|
||||||
|
@ -82,6 +85,21 @@
|
||||||
</Grid>
|
</Grid>
|
||||||
<Separator Height="5" Margin="0,5,0,0" />
|
<Separator Height="5" Margin="0,5,0,0" />
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,0,0,0">
|
||||||
|
<TextBlock Text="Speed Up" Width="70" Margin="10,10,0,0" />
|
||||||
|
<Slider Name="speedSlider"
|
||||||
|
Value="0"
|
||||||
|
Minimum="0"
|
||||||
|
Maximum="17"
|
||||||
|
TickPlacement="BottomRight"
|
||||||
|
TickFrequency="1"
|
||||||
|
IsSnapToTickEnabled="True"
|
||||||
|
Width="170"
|
||||||
|
Margin="10,10,10,0" ToolTip="Increase speech speed" />
|
||||||
|
<TextBlock Text="{Binding ElementName=speedSlider, Path=Value}" Width="40" Margin="0,10,0,0"/>
|
||||||
|
<TextBlock Text="Voice Rate" Width="70" Margin="10,10,0,0" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,0">
|
<StackPanel Orientation="Horizontal" Margin="0,0,0,0">
|
||||||
<TextBlock Text="Cut Level" Width="70" Margin="10,10,0,0" />
|
<TextBlock Text="Cut Level" Width="70" Margin="10,10,0,0" />
|
||||||
<Slider Name="noiceLevelSlider"
|
<Slider Name="noiceLevelSlider"
|
||||||
|
@ -96,7 +114,6 @@
|
||||||
<TextBlock Text="{Binding ElementName=noiceLevelSlider, Path=Value}" Width="40" Margin="0,10,0,0"/>
|
<TextBlock Text="{Binding ElementName=noiceLevelSlider, Path=Value}" Width="40" Margin="0,10,0,0"/>
|
||||||
<TextBlock Text="Voice Rate" Width="70" Margin="10,10,0,0" />
|
<TextBlock Text="Voice Rate" Width="70" Margin="10,10,0,0" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,5,0,0">
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,5,0,0">
|
||||||
<Button Content="New Sentence" Width="120" Height="32" Name="buttonAddItem" Click="addSentence"/>
|
<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" >
|
<Button Name="btnRecord" ToolTip="Start recording" Click="record" Margin="20,0,0,0" >
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Media;
|
using System.Media;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
@ -34,10 +35,10 @@ namespace OpenTXrecorder
|
||||||
{
|
{
|
||||||
public partial class MainWindow : Window
|
public partial class MainWindow : Window
|
||||||
{
|
{
|
||||||
SentenceTables tables = new SentenceTables();
|
|
||||||
Sentences sentences = new Sentences();
|
|
||||||
Languages languages = new Languages();
|
|
||||||
Environment env;
|
Environment env;
|
||||||
|
SentenceTables tables = new SentenceTables();
|
||||||
|
public Sentences sentences { get; set; }
|
||||||
|
public Languages languages { get; set; }
|
||||||
|
|
||||||
WavFileWriter filewriter;
|
WavFileWriter filewriter;
|
||||||
WaveInRecorder recorder;
|
WaveInRecorder recorder;
|
||||||
|
@ -51,15 +52,18 @@ namespace OpenTXrecorder
|
||||||
|
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
|
this.DataContext = this;
|
||||||
|
sentences = new Sentences();
|
||||||
|
languages = new Languages();
|
||||||
|
recordingBuffer = new byte[recBuffersize];
|
||||||
|
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
// Start by displaying Splash Screen
|
||||||
SplashScreen splash = new SplashScreen("recorder_logo.png");
|
SplashScreen splash = new SplashScreen("recorder_logo.png");
|
||||||
splash.Show(true);
|
splash.Show(true);
|
||||||
Thread.Sleep(1500);
|
Thread.Sleep(1500);
|
||||||
|
|
||||||
InitializeComponent();
|
|
||||||
recordingBuffer = new byte[recBuffersize];
|
|
||||||
|
|
||||||
lvSentences.ItemsSource = sentences;
|
|
||||||
cbLanguages.ItemsSource = languages;
|
|
||||||
languages.Add("English", "en");
|
languages.Add("English", "en");
|
||||||
languages.Add("Czech", "cz");
|
languages.Add("Czech", "cz");
|
||||||
languages.Add("German", "de");
|
languages.Add("German", "de");
|
||||||
|
@ -70,7 +74,9 @@ namespace OpenTXrecorder
|
||||||
languages.Add("Swedish", "se");
|
languages.Add("Swedish", "se");
|
||||||
languages.Add("Slovak", "sk");
|
languages.Add("Slovak", "sk");
|
||||||
languages.Add("Spanish", "es");
|
languages.Add("Spanish", "es");
|
||||||
env = new Environment(languages[0].sName);
|
|
||||||
|
env = new Environment(languages[0]);
|
||||||
|
|
||||||
cbLanguages.SelectedIndex = 0; // Note: Sets current langugage -> triggers loadlanguage()
|
cbLanguages.SelectedIndex = 0; // Note: Sets current langugage -> triggers loadlanguage()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,8 +92,8 @@ namespace OpenTXrecorder
|
||||||
}
|
}
|
||||||
catch (IOException)
|
catch (IOException)
|
||||||
{
|
{
|
||||||
system_strings = tables.default_system_strings[tables.toInt(env.shortLanguage)];
|
system_strings = tables.default_system_strings[tables.toInt(env.lang.sName)];
|
||||||
other_strings = tables.default_other_strings[tables.toInt(env.shortLanguage)];
|
other_strings = tables.default_other_strings[tables.toInt(env.lang.sName)];
|
||||||
}
|
}
|
||||||
sentences.Clear();
|
sentences.Clear();
|
||||||
|
|
||||||
|
@ -98,8 +104,6 @@ namespace OpenTXrecorder
|
||||||
|
|
||||||
foreach (string str in other_strings)
|
foreach (string str in other_strings)
|
||||||
sentences.Add(str, env.baseDir);
|
sentences.Add(str, env.baseDir);
|
||||||
|
|
||||||
lvSentences.Items.Refresh(); // Workaround - Two way binding is better
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveLanguage()
|
private void saveLanguage()
|
||||||
|
@ -122,7 +126,7 @@ namespace OpenTXrecorder
|
||||||
|
|
||||||
private void switchLanguage(object sender, SelectionChangedEventArgs e)
|
private void switchLanguage(object sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
env = new Environment(((Language)e.AddedItems[0]).sName);
|
env = new Environment(((Language)e.AddedItems[0])); // AddedItems is a strange name. It contains the last selection
|
||||||
loadLanguage();
|
loadLanguage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +147,8 @@ namespace OpenTXrecorder
|
||||||
}
|
}
|
||||||
while (env.fileExists(newFile));
|
while (env.fileExists(newFile));
|
||||||
sentences.Add(new Sentence(newFile + ";New Description;New Voice Message", env.baseDir));
|
sentences.Add(new Sentence(newFile + ";New Description;New Voice Message", env.baseDir));
|
||||||
this.lvSentences.Items.Refresh();
|
|
||||||
|
// Extremely ugly - direct access to the listview to scroll down and select the new object
|
||||||
this.lvSentences.SelectedIndex = this.lvSentences.Items.Count - 1;
|
this.lvSentences.SelectedIndex = this.lvSentences.Items.Count - 1;
|
||||||
this.lvSentences.ScrollIntoView(this.lvSentences.SelectedItem);
|
this.lvSentences.ScrollIntoView(this.lvSentences.SelectedItem);
|
||||||
}
|
}
|
||||||
|
@ -211,6 +216,7 @@ namespace OpenTXrecorder
|
||||||
processor.StripSilence(sentence.fullPath, noiceLevel);
|
processor.StripSilence(sentence.fullPath, noiceLevel);
|
||||||
processor.ToneIn(sentence.fullPath);
|
processor.ToneIn(sentence.fullPath);
|
||||||
processor.ToneOut(sentence.fullPath);
|
processor.ToneOut(sentence.fullPath);
|
||||||
|
processor.SpeedUp(sentence.fullPath, (int)this.speedSlider.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DataArrived(IntPtr data, int size)
|
private void DataArrived(IntPtr data, int size)
|
||||||
|
@ -222,9 +228,9 @@ namespace OpenTXrecorder
|
||||||
|
|
||||||
public class Environment
|
public class Environment
|
||||||
{
|
{
|
||||||
public string shortLanguage { get; set; }
|
public Language lang { get; set; }
|
||||||
public string baseDir { get { return @"SOUNDS\" + shortLanguage + @"\"; } }
|
public string baseDir { get { return @"SOUNDS\" + lang.sName + @"\"; } }
|
||||||
public string sysDir { get { return @"SOUNDS\" + shortLanguage + @"\SYSTEM\"; } }
|
public string sysDir { get { return @"SOUNDS\" + lang.sName + @"\SYSTEM\"; } }
|
||||||
public string otherSounds { get { return baseDir + "other_sounds.txt"; } }
|
public string otherSounds { get { return baseDir + "other_sounds.txt"; } }
|
||||||
public string systemSounds { get { return sysDir + "system_sounds.txt"; } }
|
public string systemSounds { get { return sysDir + "system_sounds.txt"; } }
|
||||||
|
|
||||||
|
@ -237,15 +243,15 @@ namespace OpenTXrecorder
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Environment(string str)
|
public Environment(Language language)
|
||||||
{
|
{
|
||||||
shortLanguage = str;
|
lang = language;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data container classes
|
// Data container classes
|
||||||
|
|
||||||
public class Languages : List<Language>
|
public class Languages : ObservableCollection<Language>
|
||||||
{
|
{
|
||||||
public void Add(string longer, string shorter)
|
public void Add(string longer, string shorter)
|
||||||
{
|
{
|
||||||
|
@ -259,7 +265,7 @@ namespace OpenTXrecorder
|
||||||
public string sName { get; set; }
|
public string sName { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Sentences : List<Sentence>
|
public class Sentences : ObservableCollection<Sentence>
|
||||||
{
|
{
|
||||||
public void Add(string rawString, string dirPath)
|
public void Add(string rawString, string dirPath)
|
||||||
{
|
{
|
||||||
|
|
|
@ -29,44 +29,33 @@ public class wavProcessor
|
||||||
if (!wain.WaveHeaderIN(@strPath)) return false;
|
if (!wain.WaveHeaderIN(@strPath)) return false;
|
||||||
byte[] arrfile = GetWAVEData(strPath);
|
byte[] arrfile = GetWAVEData(strPath);
|
||||||
|
|
||||||
|
|
||||||
int startpos = 0;
|
int startpos = 0;
|
||||||
int endpos = arrfile.Length - 1;
|
int endpos = arrfile.Length - 1;
|
||||||
|
|
||||||
// Check for silence at start
|
// Check for silence at start
|
||||||
for (int j = 0; j < arrfile.Length; j += 2)
|
for (int j = 0; isSilence(arrfile, j, noiceLevel); j += 20)
|
||||||
{
|
startpos = j;
|
||||||
short snd = ComplementToSigned(ref arrfile, j);
|
|
||||||
if (snd > (-1 * noiceLevel) && snd < noiceLevel) startpos = j;
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Allow room for tone-in buffer
|
// Allow room for tone-in buffer
|
||||||
int buffer = wain.SampleRate * (wain.BitsPerSample / 8) / 8;
|
int buffer = wain.SampleRate * (wain.BitsPerSample / 8) / 32; // 1/32 seconds lead time
|
||||||
startpos = startpos - buffer;
|
startpos = startpos - buffer;
|
||||||
if (startpos < 0)
|
if (startpos < 0)
|
||||||
startpos = 0;
|
startpos = 0;
|
||||||
|
|
||||||
// Check foor silence at end
|
// Check for silence at end. No need to check tone out buffer
|
||||||
for (int k = arrfile.Length - 1; k >= 0; k -= 2)
|
for (int k = arrfile.Length - buffer; (k >= 0) && (isSilence(arrfile, k, noiceLevel)); k -= 20)
|
||||||
{
|
|
||||||
short snd = ComplementToSigned(ref arrfile, k - 1);
|
|
||||||
if (snd > (-1 * noiceLevel) && snd < noiceLevel)
|
|
||||||
endpos = k;
|
endpos = k;
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Allow room for tone-out buffer
|
// Allow room for tone-out buffer
|
||||||
endpos = endpos + buffer;
|
endpos = endpos + buffer;
|
||||||
if (endpos > (arrfile.Length - 1))
|
if (endpos > arrfile.Length)
|
||||||
endpos = arrfile.Length - 1;
|
endpos = arrfile.Length - 2;
|
||||||
|
|
||||||
if (startpos == endpos) return false;
|
if (startpos >= endpos)
|
||||||
if ((endpos - startpos) < 1) return false;
|
return false;
|
||||||
|
|
||||||
byte[] newarr = new byte[(endpos - startpos) + 1];
|
byte[] newarr = new byte[endpos - startpos];
|
||||||
|
for (int ni = 0, m = startpos; ni < newarr.Length; m++, ni++)
|
||||||
for (int ni = 0, m = startpos; m <= endpos; m++, ni++)
|
|
||||||
newarr[ni] = arrfile[m];
|
newarr[ni] = arrfile[m];
|
||||||
|
|
||||||
// write file back
|
// write file back
|
||||||
|
@ -77,6 +66,23 @@ public class wavProcessor
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function that checks if the next 10 samples is silence
|
||||||
|
private bool isSilence(byte[] buff, int index, int noiceLevel)
|
||||||
|
{
|
||||||
|
if (buff.Length <= (index + 20))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int totalSnd = 0;
|
||||||
|
for (int i = 0; i < 20; i += 2)
|
||||||
|
{
|
||||||
|
short snd = ComplementToSigned(ref buff, i + index);
|
||||||
|
if (snd < 0)
|
||||||
|
snd = (short)(snd * -1);
|
||||||
|
totalSnd += snd;
|
||||||
|
}
|
||||||
|
return (totalSnd < (10 * noiceLevel));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tone in wav file
|
/// Tone in wav file
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -94,7 +100,7 @@ public class wavProcessor
|
||||||
|
|
||||||
// Calculate constants
|
// Calculate constants
|
||||||
int start = 0;
|
int start = 0;
|
||||||
int end = wain.SampleRate * (wain.BitsPerSample / 8) / 8; // 0.125 seconds
|
int end = wain.SampleRate * (wain.BitsPerSample / 8) / 16; // 1/16 seconds
|
||||||
int span = end - start;
|
int span = end - start;
|
||||||
|
|
||||||
//change volume
|
//change volume
|
||||||
|
@ -130,7 +136,7 @@ public class wavProcessor
|
||||||
|
|
||||||
// Calculate constants
|
// Calculate constants
|
||||||
int end = wain.Length;
|
int end = wain.Length;
|
||||||
int start = end - (wain.SampleRate * (wain.BitsPerSample / 8) / 8); // 0.125 seconds from end
|
int start = end - (wain.SampleRate * (wain.BitsPerSample / 8) / 16); // 1/16 seconds from end
|
||||||
int span = end - start;
|
int span = end - start;
|
||||||
|
|
||||||
//change volume
|
//change volume
|
||||||
|
@ -150,6 +156,43 @@ public class wavProcessor
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Speed up wav file to mimic Donald Duck
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strPath">Source wave</param>
|
||||||
|
/// <param name="speed">Speed between 0 and 19 </param>
|
||||||
|
/// <returns>True/False</returns>
|
||||||
|
public bool SpeedUp(string strPath, int speed)
|
||||||
|
{
|
||||||
|
if ((strPath == null) || (strPath == ""))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ((speed < 0) || (speed > 19))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Read from file
|
||||||
|
wavProcessor wain = new wavProcessor();
|
||||||
|
if (!wain.WaveHeaderIN(@strPath)) return false;
|
||||||
|
byte[] arrfile = GetWAVEData(strPath);
|
||||||
|
byte[] newfile = new byte[arrfile.Length];
|
||||||
|
|
||||||
|
int skip = 21-speed;
|
||||||
|
int j = 0;
|
||||||
|
for (int i = 0; i < arrfile.Length; i += 2)
|
||||||
|
{
|
||||||
|
if (skip > 20 || (((i/2) % skip) != 0))
|
||||||
|
{
|
||||||
|
newfile[j] = arrfile[i];
|
||||||
|
newfile[j + 1] = arrfile[i + 1];
|
||||||
|
j += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// write file back
|
||||||
|
WavFileWriter writer = new WavFileWriter(@strPath, wain.SampleRate, wain.BitsPerSample, wain.Channels);
|
||||||
|
writer.Write(newfile, j);
|
||||||
|
writer.Close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read the wave file header and store the key values in public variable.
|
/// Read the wave file header and store the key values in public variable.
|
||||||
|
@ -201,6 +244,7 @@ public class wavProcessor
|
||||||
private short ComplementToSigned(ref byte[] bytArr, int intPos) // 2's complement to normal signed value
|
private short ComplementToSigned(ref byte[] bytArr, int intPos) // 2's complement to normal signed value
|
||||||
{
|
{
|
||||||
short snd = BitConverter.ToInt16(bytArr, intPos);
|
short snd = BitConverter.ToInt16(bytArr, intPos);
|
||||||
|
if (intPos >= bytArr.Length) return 0;
|
||||||
if (snd != 0)
|
if (snd != 0)
|
||||||
snd = Convert.ToInt16((~snd | 1));
|
snd = Convert.ToInt16((~snd | 1));
|
||||||
return snd;
|
return snd;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue