Skip to content

Commit

Permalink
Corrected bug that caused uneven data length for 16-bit wav files (bi…
Browse files Browse the repository at this point in the history
…g no, no). Added speed increase for recorded sound.
  • Loading branch information
Dvogonen committed Mar 25, 2014
1 parent 76368f3 commit 47b79b1
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 61 deletions.
37 changes: 27 additions & 10 deletions sound/recorder/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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>
Expand All @@ -54,8 +56,9 @@
<ComboBox Name="cbLanguages"
DisplayMemberPath="lName"
SelectedValuePath="sName"
SelectionChanged="switchLanguage"
Width="200" Height="23" />
SelectionChanged="switchLanguage"
Width="200" Height="23"
ItemsSource="{Binding languages, Mode=TwoWay}" />
</StackPanel>
</StackPanel>
<Separator Height="5" />
Expand All @@ -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"
Expand All @@ -96,8 +114,7 @@
<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">
<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" >
<Image Height="32" Source="/OpenTXrecorder;component/record.png"/>
Expand Down
50 changes: 28 additions & 22 deletions sound/recorder/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -51,15 +52,18 @@ public partial class MainWindow : Window

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");
Expand All @@ -70,7 +74,9 @@ public MainWindow()
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()
}

Expand All @@ -86,8 +92,8 @@ private void loadLanguage()
}
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();

Expand All @@ -98,8 +104,6 @@ private void loadLanguage()

foreach (string str in other_strings)
sentences.Add(str, env.baseDir);

lvSentences.Items.Refresh(); // Workaround - Two way binding is better
}

private void saveLanguage()
Expand All @@ -122,7 +126,7 @@ private void saveLanguage()

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();
}

Expand All @@ -143,7 +147,8 @@ private void addSentence(object sender, RoutedEventArgs e)
}
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);
}
Expand Down Expand Up @@ -211,6 +216,7 @@ private void stop(object sender, RoutedEventArgs e)
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)
Expand All @@ -222,9 +228,9 @@ private void DataArrived(IntPtr data, int size)

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"; } }

Expand All @@ -237,15 +243,15 @@ public bool fileExists(string fName)
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)
{
Expand All @@ -259,7 +265,7 @@ public class Language
public string sName { get; set; }
}

public class Sentences : List<Sentence>
public class Sentences : ObservableCollection<Sentence>
{
public void Add(string rawString, string dirPath)
{
Expand Down
102 changes: 73 additions & 29 deletions sound/recorder/clsWaveProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,44 +29,33 @@ public bool StripSilence(string strPath, int noiceLevel)
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)
endpos = k;
else
break;
}
// 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;

// 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;

byte[] newarr = new byte[(endpos - startpos) + 1];
if (startpos >= endpos)
return false;

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
Expand All @@ -77,6 +66,23 @@ public bool StripSilence(string strPath, int noiceLevel)
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>
Expand All @@ -94,7 +100,7 @@ public bool ToneIn(string strPath)

// 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
Expand All @@ -112,7 +118,7 @@ public bool ToneIn(string strPath)
writer.Close();

return true;
}
}
/// <summary>
/// Tone out wav file
/// </summary>
Expand All @@ -130,7 +136,7 @@ public bool ToneOut(string strPath)

// 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
Expand All @@ -150,6 +156,43 @@ public bool ToneOut(string strPath)

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.
Expand Down Expand Up @@ -201,6 +244,7 @@ private bool WaveHeaderIN(string strPath)
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;
Expand Down

0 comments on commit 47b79b1

Please sign in to comment.