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>
|
||||
<DataTemplate x:Key="image">
|
||||
<Image x:Name="TheImage" />
|
||||
<DataTemplate x:Key="imagePlay">
|
||||
<Image x:Name="ThePlayImage" />
|
||||
<DataTemplate.Triggers>
|
||||
<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>
|
||||
</DataTemplate.Triggers>
|
||||
</DataTemplate>
|
||||
|
@ -30,15 +30,17 @@
|
|||
<ListView Name="lvSentences"
|
||||
SelectionMode="Single"
|
||||
MouseDoubleClick="play"
|
||||
VerticalAlignment="Stretch" >
|
||||
|
||||
VerticalAlignment="Stretch"
|
||||
ItemsSource="{Binding sentences, Mode=TwoWay}"
|
||||
IsSynchronizedWithCurrentItem="True"
|
||||
>
|
||||
<ListView.View>
|
||||
<GridView>
|
||||
<GridView.Columns>
|
||||
<GridViewColumn Header="File Name" Width="90" DisplayMemberBinding="{Binding fileName}" />
|
||||
<GridViewColumn Header="Description" Width="190" DisplayMemberBinding="{Binding description}" />
|
||||
<GridViewColumn Header="Voice" Width="190" DisplayMemberBinding="{Binding voiceString}" />
|
||||
<GridViewColumn Width="30" CellTemplate="{StaticResource image}" />
|
||||
<GridViewColumn Width="30" CellTemplate="{StaticResource imagePlay}" />
|
||||
</GridView.Columns>
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
|
@ -55,7 +57,8 @@
|
|||
DisplayMemberPath="lName"
|
||||
SelectedValuePath="sName"
|
||||
SelectionChanged="switchLanguage"
|
||||
Width="200" Height="23" />
|
||||
Width="200" Height="23"
|
||||
ItemsSource="{Binding languages, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<Separator Height="5" />
|
||||
|
@ -82,6 +85,21 @@
|
|||
</Grid>
|
||||
<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">
|
||||
<TextBlock Text="Cut Level" Width="70" Margin="10,10,0,0" />
|
||||
<Slider Name="noiceLevelSlider"
|
||||
|
@ -96,7 +114,6 @@
|
|||
<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" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,5,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" >
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Media;
|
||||
using System.Threading;
|
||||
|
@ -34,10 +35,10 @@ namespace OpenTXrecorder
|
|||
{
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
SentenceTables tables = new SentenceTables();
|
||||
Sentences sentences = new Sentences();
|
||||
Languages languages = new Languages();
|
||||
Environment env;
|
||||
SentenceTables tables = new SentenceTables();
|
||||
public Sentences sentences { get; set; }
|
||||
public Languages languages { get; set; }
|
||||
|
||||
WavFileWriter filewriter;
|
||||
WaveInRecorder recorder;
|
||||
|
@ -51,15 +52,18 @@ namespace OpenTXrecorder
|
|||
|
||||
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");
|
||||
splash.Show(true);
|
||||
Thread.Sleep(1500);
|
||||
|
||||
InitializeComponent();
|
||||
recordingBuffer = new byte[recBuffersize];
|
||||
|
||||
lvSentences.ItemsSource = sentences;
|
||||
cbLanguages.ItemsSource = languages;
|
||||
languages.Add("English", "en");
|
||||
languages.Add("Czech", "cz");
|
||||
languages.Add("German", "de");
|
||||
|
@ -70,7 +74,9 @@ namespace OpenTXrecorder
|
|||
languages.Add("Swedish", "se");
|
||||
languages.Add("Slovak", "sk");
|
||||
languages.Add("Spanish", "es");
|
||||
env = new Environment(languages[0].sName);
|
||||
|
||||
env = new Environment(languages[0]);
|
||||
|
||||
cbLanguages.SelectedIndex = 0; // Note: Sets current langugage -> triggers loadlanguage()
|
||||
}
|
||||
|
||||
|
@ -86,8 +92,8 @@ namespace OpenTXrecorder
|
|||
}
|
||||
catch (IOException)
|
||||
{
|
||||
system_strings = tables.default_system_strings[tables.toInt(env.shortLanguage)];
|
||||
other_strings = tables.default_other_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.lang.sName)];
|
||||
}
|
||||
sentences.Clear();
|
||||
|
||||
|
@ -98,8 +104,6 @@ namespace OpenTXrecorder
|
|||
|
||||
foreach (string str in other_strings)
|
||||
sentences.Add(str, env.baseDir);
|
||||
|
||||
lvSentences.Items.Refresh(); // Workaround - Two way binding is better
|
||||
}
|
||||
|
||||
private void saveLanguage()
|
||||
|
@ -122,7 +126,7 @@ namespace OpenTXrecorder
|
|||
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -143,7 +147,8 @@ namespace OpenTXrecorder
|
|||
}
|
||||
while (env.fileExists(newFile));
|
||||
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.ScrollIntoView(this.lvSentences.SelectedItem);
|
||||
}
|
||||
|
@ -211,6 +216,7 @@ namespace OpenTXrecorder
|
|||
processor.StripSilence(sentence.fullPath, noiceLevel);
|
||||
processor.ToneIn(sentence.fullPath);
|
||||
processor.ToneOut(sentence.fullPath);
|
||||
processor.SpeedUp(sentence.fullPath, (int)this.speedSlider.Value);
|
||||
}
|
||||
|
||||
private void DataArrived(IntPtr data, int size)
|
||||
|
@ -222,9 +228,9 @@ namespace OpenTXrecorder
|
|||
|
||||
public class Environment
|
||||
{
|
||||
public string shortLanguage { get; set; }
|
||||
public string baseDir { get { return @"SOUNDS\" + shortLanguage + @"\"; } }
|
||||
public string sysDir { get { return @"SOUNDS\" + shortLanguage + @"\SYSTEM\"; } }
|
||||
public Language lang { get; set; }
|
||||
public string baseDir { get { return @"SOUNDS\" + lang.sName + @"\"; } }
|
||||
public string sysDir { get { return @"SOUNDS\" + lang.sName + @"\SYSTEM\"; } }
|
||||
public string otherSounds { get { return baseDir + "other_sounds.txt"; } }
|
||||
public string systemSounds { get { return sysDir + "system_sounds.txt"; } }
|
||||
|
||||
|
@ -237,15 +243,15 @@ namespace OpenTXrecorder
|
|||
return false;
|
||||
}
|
||||
|
||||
public Environment(string str)
|
||||
public Environment(Language language)
|
||||
{
|
||||
shortLanguage = str;
|
||||
lang = language;
|
||||
}
|
||||
}
|
||||
|
||||
// Data container classes
|
||||
|
||||
public class Languages : List<Language>
|
||||
public class Languages : ObservableCollection<Language>
|
||||
{
|
||||
public void Add(string longer, string shorter)
|
||||
{
|
||||
|
@ -259,7 +265,7 @@ namespace OpenTXrecorder
|
|||
public string sName { get; set; }
|
||||
}
|
||||
|
||||
public class Sentences : List<Sentence>
|
||||
public class Sentences : ObservableCollection<Sentence>
|
||||
{
|
||||
public void Add(string rawString, string dirPath)
|
||||
{
|
||||
|
|
|
@ -29,44 +29,33 @@ public class wavProcessor
|
|||
if (!wain.WaveHeaderIN(@strPath)) return false;
|
||||
byte[] arrfile = GetWAVEData(strPath);
|
||||
|
||||
|
||||
int startpos = 0;
|
||||
int endpos = arrfile.Length - 1;
|
||||
|
||||
// Check for silence at start
|
||||
for (int j = 0; j < arrfile.Length; j += 2)
|
||||
{
|
||||
short snd = ComplementToSigned(ref arrfile, j);
|
||||
if (snd > (-1 * noiceLevel) && snd < noiceLevel) startpos = j;
|
||||
else
|
||||
break;
|
||||
}
|
||||
for (int j = 0; isSilence(arrfile, j, noiceLevel); j += 20)
|
||||
startpos = j;
|
||||
|
||||
// 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;
|
||||
if (startpos < 0)
|
||||
startpos = 0;
|
||||
|
||||
// Check foor silence at end
|
||||
for (int k = arrfile.Length - 1; k >= 0; k -= 2)
|
||||
{
|
||||
short snd = ComplementToSigned(ref arrfile, k - 1);
|
||||
if (snd > (-1 * noiceLevel) && snd < noiceLevel)
|
||||
// Check for silence at end. No need to check tone out buffer
|
||||
for (int k = arrfile.Length - buffer; (k >= 0) && (isSilence(arrfile, k, noiceLevel)); k -= 20)
|
||||
endpos = k;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// Allow room for tone-out buffer
|
||||
endpos = endpos + buffer;
|
||||
if (endpos > (arrfile.Length - 1))
|
||||
endpos = arrfile.Length - 1;
|
||||
if (endpos > arrfile.Length)
|
||||
endpos = arrfile.Length - 2;
|
||||
|
||||
if (startpos == endpos) return false;
|
||||
if ((endpos - startpos) < 1) return false;
|
||||
if (startpos >= endpos)
|
||||
return false;
|
||||
|
||||
byte[] newarr = new byte[(endpos - startpos) + 1];
|
||||
|
||||
for (int ni = 0, m = startpos; m <= endpos; m++, ni++)
|
||||
byte[] newarr = new byte[endpos - startpos];
|
||||
for (int ni = 0, m = startpos; ni < newarr.Length; m++, ni++)
|
||||
newarr[ni] = arrfile[m];
|
||||
|
||||
// write file back
|
||||
|
@ -77,6 +66,23 @@ public class wavProcessor
|
|||
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>
|
||||
/// Tone in wav file
|
||||
/// </summary>
|
||||
|
@ -94,7 +100,7 @@ public class wavProcessor
|
|||
|
||||
// Calculate constants
|
||||
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;
|
||||
|
||||
//change volume
|
||||
|
@ -130,7 +136,7 @@ public class wavProcessor
|
|||
|
||||
// Calculate constants
|
||||
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;
|
||||
|
||||
//change volume
|
||||
|
@ -150,6 +156,43 @@ public class wavProcessor
|
|||
|
||||
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>
|
||||
/// 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
|
||||
{
|
||||
short snd = BitConverter.ToInt16(bytArr, intPos);
|
||||
if (intPos >= bytArr.Length) return 0;
|
||||
if (snd != 0)
|
||||
snd = Convert.ToInt16((~snd | 1));
|
||||
return snd;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue