Skip to content

Commit 47b79b1

Browse files
committed
Corrected bug that caused uneven data length for 16-bit wav files (big no, no). Added speed increase for recorded sound.
1 parent 76368f3 commit 47b79b1

File tree

3 files changed

+128
-61
lines changed

3 files changed

+128
-61
lines changed

Diff for: sound/recorder/MainWindow.xaml

+27-10
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010

1111

1212
<Window.Resources>
13-
<DataTemplate x:Key="image">
14-
<Image x:Name="TheImage" />
13+
<DataTemplate x:Key="imagePlay">
14+
<Image x:Name="ThePlayImage" />
1515
<DataTemplate.Triggers>
1616
<DataTrigger Binding="{Binding Path=fileExists}" Value="true">
17-
<Setter TargetName="TheImage" Property="Source" Value="/OpenTXrecorder;component/play.png" />
17+
<Setter TargetName="ThePlayImage" Property="Source" Value="/OpenTXrecorder;component/play.png" />
1818
</DataTrigger>
1919
</DataTemplate.Triggers>
2020
</DataTemplate>
@@ -30,15 +30,17 @@
3030
<ListView Name="lvSentences"
3131
SelectionMode="Single"
3232
MouseDoubleClick="play"
33-
VerticalAlignment="Stretch" >
34-
33+
VerticalAlignment="Stretch"
34+
ItemsSource="{Binding sentences, Mode=TwoWay}"
35+
IsSynchronizedWithCurrentItem="True"
36+
>
3537
<ListView.View>
3638
<GridView>
3739
<GridView.Columns>
3840
<GridViewColumn Header="File Name" Width="90" DisplayMemberBinding="{Binding fileName}" />
3941
<GridViewColumn Header="Description" Width="190" DisplayMemberBinding="{Binding description}" />
4042
<GridViewColumn Header="Voice" Width="190" DisplayMemberBinding="{Binding voiceString}" />
41-
<GridViewColumn Width="30" CellTemplate="{StaticResource image}" />
43+
<GridViewColumn Width="30" CellTemplate="{StaticResource imagePlay}" />
4244
</GridView.Columns>
4345
</GridView>
4446
</ListView.View>
@@ -54,8 +56,9 @@
5456
<ComboBox Name="cbLanguages"
5557
DisplayMemberPath="lName"
5658
SelectedValuePath="sName"
57-
SelectionChanged="switchLanguage"
58-
Width="200" Height="23" />
59+
SelectionChanged="switchLanguage"
60+
Width="200" Height="23"
61+
ItemsSource="{Binding languages, Mode=TwoWay}" />
5962
</StackPanel>
6063
</StackPanel>
6164
<Separator Height="5" />
@@ -82,6 +85,21 @@
8285
</Grid>
8386
<Separator Height="5" Margin="0,5,0,0" />
8487

88+
<StackPanel Orientation="Horizontal" Margin="0,0,0,0">
89+
<TextBlock Text="Speed Up" Width="70" Margin="10,10,0,0" />
90+
<Slider Name="speedSlider"
91+
Value="0"
92+
Minimum="0"
93+
Maximum="17"
94+
TickPlacement="BottomRight"
95+
TickFrequency="1"
96+
IsSnapToTickEnabled="True"
97+
Width="170"
98+
Margin="10,10,10,0" ToolTip="Increase speech speed" />
99+
<TextBlock Text="{Binding ElementName=speedSlider, Path=Value}" Width="40" Margin="0,10,0,0"/>
100+
<TextBlock Text="Voice Rate" Width="70" Margin="10,10,0,0" />
101+
</StackPanel>
102+
85103
<StackPanel Orientation="Horizontal" Margin="0,0,0,0">
86104
<TextBlock Text="Cut Level" Width="70" Margin="10,10,0,0" />
87105
<Slider Name="noiceLevelSlider"
@@ -96,8 +114,7 @@
96114
<TextBlock Text="{Binding ElementName=noiceLevelSlider, Path=Value}" Width="40" Margin="0,10,0,0"/>
97115
<TextBlock Text="Voice Rate" Width="70" Margin="10,10,0,0" />
98116
</StackPanel>
99-
100-
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,5,0,0">
117+
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,5,0,0">
101118
<Button Content="New Sentence" Width="120" Height="32" Name="buttonAddItem" Click="addSentence"/>
102119
<Button Name="btnRecord" ToolTip="Start recording" Click="record" Margin="20,0,0,0" >
103120
<Image Height="32" Source="/OpenTXrecorder;component/record.png"/>

Diff for: sound/recorder/MainWindow.xaml.cs

+28-22
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using System;
1919
using System.IO;
2020
using System.Collections.Generic;
21+
using System.Collections.ObjectModel;
2122
using System.Linq;
2223
using System.Media;
2324
using System.Threading;
@@ -34,10 +35,10 @@ namespace OpenTXrecorder
3435
{
3536
public partial class MainWindow : Window
3637
{
37-
SentenceTables tables = new SentenceTables();
38-
Sentences sentences = new Sentences();
39-
Languages languages = new Languages();
4038
Environment env;
39+
SentenceTables tables = new SentenceTables();
40+
public Sentences sentences { get; set; }
41+
public Languages languages { get; set; }
4142

4243
WavFileWriter filewriter;
4344
WaveInRecorder recorder;
@@ -51,15 +52,18 @@ public partial class MainWindow : Window
5152

5253
public MainWindow()
5354
{
55+
this.DataContext = this;
56+
sentences = new Sentences();
57+
languages = new Languages();
58+
recordingBuffer = new byte[recBuffersize];
59+
60+
InitializeComponent();
61+
62+
// Start by displaying Splash Screen
5463
SplashScreen splash = new SplashScreen("recorder_logo.png");
5564
splash.Show(true);
5665
Thread.Sleep(1500);
5766

58-
InitializeComponent();
59-
recordingBuffer = new byte[recBuffersize];
60-
61-
lvSentences.ItemsSource = sentences;
62-
cbLanguages.ItemsSource = languages;
6367
languages.Add("English", "en");
6468
languages.Add("Czech", "cz");
6569
languages.Add("German", "de");
@@ -70,7 +74,9 @@ public MainWindow()
7074
languages.Add("Swedish", "se");
7175
languages.Add("Slovak", "sk");
7276
languages.Add("Spanish", "es");
73-
env = new Environment(languages[0].sName);
77+
78+
env = new Environment(languages[0]);
79+
7480
cbLanguages.SelectedIndex = 0; // Note: Sets current langugage -> triggers loadlanguage()
7581
}
7682

@@ -86,8 +92,8 @@ private void loadLanguage()
8692
}
8793
catch (IOException)
8894
{
89-
system_strings = tables.default_system_strings[tables.toInt(env.shortLanguage)];
90-
other_strings = tables.default_other_strings[tables.toInt(env.shortLanguage)];
95+
system_strings = tables.default_system_strings[tables.toInt(env.lang.sName)];
96+
other_strings = tables.default_other_strings[tables.toInt(env.lang.sName)];
9197
}
9298
sentences.Clear();
9399

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

99105
foreach (string str in other_strings)
100106
sentences.Add(str, env.baseDir);
101-
102-
lvSentences.Items.Refresh(); // Workaround - Two way binding is better
103107
}
104108

105109
private void saveLanguage()
@@ -122,7 +126,7 @@ private void saveLanguage()
122126

123127
private void switchLanguage(object sender, SelectionChangedEventArgs e)
124128
{
125-
env = new Environment(((Language)e.AddedItems[0]).sName);
129+
env = new Environment(((Language)e.AddedItems[0])); // AddedItems is a strange name. It contains the last selection
126130
loadLanguage();
127131
}
128132

@@ -143,7 +147,8 @@ private void addSentence(object sender, RoutedEventArgs e)
143147
}
144148
while (env.fileExists(newFile));
145149
sentences.Add(new Sentence(newFile + ";New Description;New Voice Message", env.baseDir));
146-
this.lvSentences.Items.Refresh();
150+
151+
// Extremely ugly - direct access to the listview to scroll down and select the new object
147152
this.lvSentences.SelectedIndex = this.lvSentences.Items.Count - 1;
148153
this.lvSentences.ScrollIntoView(this.lvSentences.SelectedItem);
149154
}
@@ -211,6 +216,7 @@ private void stop(object sender, RoutedEventArgs e)
211216
processor.StripSilence(sentence.fullPath, noiceLevel);
212217
processor.ToneIn(sentence.fullPath);
213218
processor.ToneOut(sentence.fullPath);
219+
processor.SpeedUp(sentence.fullPath, (int)this.speedSlider.Value);
214220
}
215221

216222
private void DataArrived(IntPtr data, int size)
@@ -222,9 +228,9 @@ private void DataArrived(IntPtr data, int size)
222228

223229
public class Environment
224230
{
225-
public string shortLanguage { get; set; }
226-
public string baseDir { get { return @"SOUNDS\" + shortLanguage + @"\"; } }
227-
public string sysDir { get { return @"SOUNDS\" + shortLanguage + @"\SYSTEM\"; } }
231+
public Language lang { get; set; }
232+
public string baseDir { get { return @"SOUNDS\" + lang.sName + @"\"; } }
233+
public string sysDir { get { return @"SOUNDS\" + lang.sName + @"\SYSTEM\"; } }
228234
public string otherSounds { get { return baseDir + "other_sounds.txt"; } }
229235
public string systemSounds { get { return sysDir + "system_sounds.txt"; } }
230236

@@ -237,15 +243,15 @@ public bool fileExists(string fName)
237243
return false;
238244
}
239245

240-
public Environment(string str)
246+
public Environment(Language language)
241247
{
242-
shortLanguage = str;
248+
lang = language;
243249
}
244250
}
245251

246252
// Data container classes
247253

248-
public class Languages : List<Language>
254+
public class Languages : ObservableCollection<Language>
249255
{
250256
public void Add(string longer, string shorter)
251257
{
@@ -259,7 +265,7 @@ public class Language
259265
public string sName { get; set; }
260266
}
261267

262-
public class Sentences : List<Sentence>
268+
public class Sentences : ObservableCollection<Sentence>
263269
{
264270
public void Add(string rawString, string dirPath)
265271
{

Diff for: sound/recorder/clsWaveProcessor.cs

+73-29
Original file line numberDiff line numberDiff line change
@@ -29,44 +29,33 @@ public bool StripSilence(string strPath, int noiceLevel)
2929
if (!wain.WaveHeaderIN(@strPath)) return false;
3030
byte[] arrfile = GetWAVEData(strPath);
3131

32-
3332
int startpos = 0;
3433
int endpos = arrfile.Length - 1;
35-
34+
3635
// Check for silence at start
37-
for (int j = 0; j < arrfile.Length; j += 2)
38-
{
39-
short snd = ComplementToSigned(ref arrfile, j);
40-
if (snd > (-1 * noiceLevel) && snd < noiceLevel) startpos = j;
41-
else
42-
break;
43-
}
36+
for (int j = 0; isSilence(arrfile, j, noiceLevel); j += 20)
37+
startpos = j;
38+
4439
// Allow room for tone-in buffer
45-
int buffer = wain.SampleRate * (wain.BitsPerSample / 8) / 8;
40+
int buffer = wain.SampleRate * (wain.BitsPerSample / 8) / 32; // 1/32 seconds lead time
4641
startpos = startpos - buffer;
4742
if (startpos < 0)
4843
startpos = 0;
4944

50-
// Check foor silence at end
51-
for (int k = arrfile.Length - 1; k >= 0; k -= 2)
52-
{
53-
short snd = ComplementToSigned(ref arrfile, k - 1);
54-
if (snd > (-1 * noiceLevel) && snd < noiceLevel)
55-
endpos = k;
56-
else
57-
break;
58-
}
45+
// Check for silence at end. No need to check tone out buffer
46+
for (int k = arrfile.Length - buffer; (k >= 0) && (isSilence(arrfile, k, noiceLevel)); k -= 20)
47+
endpos = k;
48+
5949
// Allow room for tone-out buffer
6050
endpos = endpos + buffer;
61-
if (endpos > (arrfile.Length - 1))
62-
endpos = arrfile.Length - 1;
51+
if (endpos > arrfile.Length)
52+
endpos = arrfile.Length - 2;
6353

64-
if (startpos == endpos) return false;
65-
if ((endpos - startpos) < 1) return false;
66-
67-
byte[] newarr = new byte[(endpos - startpos) + 1];
54+
if (startpos >= endpos)
55+
return false;
6856

69-
for (int ni = 0, m = startpos; m <= endpos; m++, ni++)
57+
byte[] newarr = new byte[endpos - startpos];
58+
for (int ni = 0, m = startpos; ni < newarr.Length; m++, ni++)
7059
newarr[ni] = arrfile[m];
7160

7261
// write file back
@@ -77,6 +66,23 @@ public bool StripSilence(string strPath, int noiceLevel)
7766
return true;
7867
}
7968

69+
// Helper function that checks if the next 10 samples is silence
70+
private bool isSilence(byte[] buff, int index, int noiceLevel)
71+
{
72+
if (buff.Length <= (index + 20))
73+
return false;
74+
75+
int totalSnd = 0;
76+
for (int i = 0; i < 20; i += 2)
77+
{
78+
short snd = ComplementToSigned(ref buff, i + index);
79+
if (snd < 0)
80+
snd = (short)(snd * -1);
81+
totalSnd += snd;
82+
}
83+
return (totalSnd < (10 * noiceLevel));
84+
}
85+
8086
/// <summary>
8187
/// Tone in wav file
8288
/// </summary>
@@ -94,7 +100,7 @@ public bool ToneIn(string strPath)
94100

95101
// Calculate constants
96102
int start = 0;
97-
int end = wain.SampleRate * (wain.BitsPerSample / 8) / 8; // 0.125 seconds
103+
int end = wain.SampleRate * (wain.BitsPerSample / 8) / 16; // 1/16 seconds
98104
int span = end - start;
99105

100106
//change volume
@@ -112,7 +118,7 @@ public bool ToneIn(string strPath)
112118
writer.Close();
113119

114120
return true;
115-
}
121+
}
116122
/// <summary>
117123
/// Tone out wav file
118124
/// </summary>
@@ -130,7 +136,7 @@ public bool ToneOut(string strPath)
130136

131137
// Calculate constants
132138
int end = wain.Length;
133-
int start = end - (wain.SampleRate * (wain.BitsPerSample / 8) / 8); // 0.125 seconds from end
139+
int start = end - (wain.SampleRate * (wain.BitsPerSample / 8) / 16); // 1/16 seconds from end
134140
int span = end - start;
135141

136142
//change volume
@@ -150,6 +156,43 @@ public bool ToneOut(string strPath)
150156

151157
return true;
152158
}
159+
/// <summary>
160+
/// Speed up wav file to mimic Donald Duck
161+
/// </summary>
162+
/// <param name="strPath">Source wave</param>
163+
/// <param name="speed">Speed between 0 and 19 </param>
164+
/// <returns>True/False</returns>
165+
public bool SpeedUp(string strPath, int speed)
166+
{
167+
if ((strPath == null) || (strPath == ""))
168+
return false;
169+
170+
if ((speed < 0) || (speed > 19))
171+
return false;
172+
173+
// Read from file
174+
wavProcessor wain = new wavProcessor();
175+
if (!wain.WaveHeaderIN(@strPath)) return false;
176+
byte[] arrfile = GetWAVEData(strPath);
177+
byte[] newfile = new byte[arrfile.Length];
178+
179+
int skip = 21-speed;
180+
int j = 0;
181+
for (int i = 0; i < arrfile.Length; i += 2)
182+
{
183+
if (skip > 20 || (((i/2) % skip) != 0))
184+
{
185+
newfile[j] = arrfile[i];
186+
newfile[j + 1] = arrfile[i + 1];
187+
j += 2;
188+
}
189+
}
190+
// write file back
191+
WavFileWriter writer = new WavFileWriter(@strPath, wain.SampleRate, wain.BitsPerSample, wain.Channels);
192+
writer.Write(newfile, j);
193+
writer.Close();
194+
return true;
195+
}
153196

154197
/// <summary>
155198
/// Read the wave file header and store the key values in public variable.
@@ -201,6 +244,7 @@ private bool WaveHeaderIN(string strPath)
201244
private short ComplementToSigned(ref byte[] bytArr, int intPos) // 2's complement to normal signed value
202245
{
203246
short snd = BitConverter.ToInt16(bytArr, intPos);
247+
if (intPos >= bytArr.Length) return 0;
204248
if (snd != 0)
205249
snd = Convert.ToInt16((~snd | 1));
206250
return snd;

0 commit comments

Comments
 (0)