-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathProgram.cs
308 lines (250 loc) · 10.5 KB
/
Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
// System
using System.Media;
using System.Diagnostics;
// Google Cloud TTS API
using Google.Cloud.TextToSpeech.V1;
// Add the following assembly references to the project for PowerPoint automation
//
// C:\Program Files (x86)\Microsoft Visual Studio\Shared\Visual Studio Tools for Office\PIA\Office15\Office.dll
// C:\Program Files (x86)\Microsoft Visual Studio\Shared\Visual Studio Tools for Office\PIA\Office15\Microsoft.Office.Interop.Excel.dll
// C:\Program Files (x86)\Microsoft Visual Studio\Shared\Visual Studio Tools for Office\PIA\Office15\Microsoft.Office.Interop.PowerPoint.dll
//
// Repair Office 365 (Online mode) in case of any COM errors
//
using Microsoft.Office.Core;
using Excel = Microsoft.Office.Interop.Excel;
using PowerPoint = Microsoft.Office.Interop.PowerPoint;
namespace PPrun
{
internal class Program
{
// Start and stop slides
static int First = 1;
static int Last = 999;
// Excel Application
static Excel.Application exa;
// PowerPoint Application
static PowerPoint.Application ppa;
// PowerPoint Presentation
static PowerPoint.Presentation ppp;
// Google text to speech client
static TextToSpeechClient tts;
// Sound player
static SoundPlayer player;
/// <summary>
/// Defines the entry point of the application.
/// </summary>
/// <param name="args">The arguments.</param>
static void Main(string[] args)
{
try
{
// Run in a safe context
MainSafe(args);
}
catch (Exception e)
{
// Show error message
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
}
finally
{
// Kill orphaned processes
Kill("EXCEL", "");
Kill("POWERPNT", "PowerPoint");
Console.ReadLine();
}
}
/// <summary>
/// Defines the entry point of the application (Safe Context).
/// </summary>
/// <param name="args">The arguments.</param>
static void MainSafe(string[] args)
{
// Check for presentation name
if (args.Length == 0) throw new Exception("Presentation name missing!");
// Get the command line arguments
string fileXLSX = Path.GetFullPath(args[0] + ".xlsx");
string filePPTX = Path.GetFullPath(args[0] + ".pptx");
// Sound player
player = new SoundPlayer();
// Create speech client
tts = TextToSpeechClient.Create();
// Progress message
Speak("Launching Excel ....", 200);
// Launch Excel
exa = new Excel.Application();
// Progress message
Speak("Launching PowerPoint ....", 200);
// Launch powerpoint
ppa = new PowerPoint.Application();
// Build temporary filenames
string tempXLSX = Path.Combine(Path.GetDirectoryName(fileXLSX), "~" + Path.GetFileName(fileXLSX));
string tempPPTX = Path.Combine(Path.GetDirectoryName(filePPTX), "~" + Path.GetFileName(filePPTX));
// Copy the source files
File.Copy(fileXLSX, tempXLSX, true);
File.Copy(filePPTX, tempPPTX, true);
// Use temporary files (automation is notorious for corrupting files!!!!!)
fileXLSX = tempXLSX;
filePPTX = tempPPTX;
// Open workbook
exa.Workbooks.Open(fileXLSX);
// Make application visible
exa.Visible = true;
// Open presentation
ppp = ppa.Presentations.Open(filePPTX);
// Make application visible
ppa.Visible = MsoTriState.msoTrue;
// Start video recorder
Speak("Now generating speech ....", 200);
// Parse the script
PPScript script = ParseScript();
// Progress message
Speak("Closing Excel ....", 200);
// Quit Excel
try { exa.ActiveWorkbook.Close(); } catch { }
try { exa.Quit(); } catch { }
try { Kill("EXCEL", ""); } catch { }
// Start video recorder
Speak("Please disable multi monitor software like display fusion");
Speak("Please also disable all PowerPoint Add-ins");
Speak("Start your video recorder and press enter");
// Wait for key
Console.ReadLine();
// Execute script
script.Run(First);
// Progress message
Speak("Closing PowerPoint ....", 200);
// Quit PowerPoint
try { ppp.Close(); } catch { }
try { ppa.Quit(); } catch { }
try { Kill("POWERPNT", "PowerPoint"); } catch { }
// Delete temporary files
File.Delete(fileXLSX);
File.Delete(filePPTX);
// Stop video recorder
Speak("Please stop your video recorder and press enter", 200);
}
/// <summary>
/// Speaks the specified text using an English female voice (system messages).
/// </summary>
/// <param name="text">The text.</param>
static void Speak(string text, int delay = 0)
{
Console.WriteLine(text);
var input = new SynthesisInput
{
Text = text
};
// Voice selection parameters
var vspp = new VoiceSelectionParams
{
Name = "en-US-Neural2-E", // Voice name (male or female)
LanguageCode = "en-US" // Take language code from voice name
};
// Specify the type of audio file.
var ac = new AudioConfig
{
AudioEncoding = AudioEncoding.Linear16, // Wave
VolumeGainDb = 10
};
// Make the API call
var response = tts.SynthesizeSpeech(input, vspp, ac);
// Connect as memory stream to the sound player
player.Stream = new MemoryStream(response.AudioContent.ToByteArray(), true);
// Wait
if (delay > 0) Thread.Sleep(delay);
// Play
player.PlaySync();
}
/// <summary>
/// Parses the settings and script from the Excel workbook.
/// </summary>
static PPScript ParseScript()
{
// Slide counter
int s = 0;
// Create a script object
PPScript script = new PPScript(ppp, player);
// Initialize the list of actions
script.Actions = new List<PPAction>();
var vspp = new VoiceSelectionParams();
var ac = new AudioConfig();
// Iterate all worksheets
foreach (Excel.Worksheet ws in exa.ActiveWorkbook.Worksheets)
{
// Settings
if (ws.Name.StartsWith("Settings", StringComparison.OrdinalIgnoreCase))
{
First = (int)(double)ws.Range["B8"].Value;
Last = (int)(double)ws.Range["B9"].Value;
string name = ws.Range["B3"].Text.ToString();
string[] segs = name.Split('-');
// Voice selection parameters
vspp = new VoiceSelectionParams
{
Name = name, // Voice name (male or female)
LanguageCode = segs[0] + "-" + segs[1] // Take language code from voice name
};
// Specify the type of audio file.
ac = new AudioConfig
{
AudioEncoding = AudioEncoding.Linear16, // Wave
VolumeGainDb = (double)ws.Range["B4"].Value, // Volume gain in Db (-96 .. +16)
Pitch = (double)ws.Range["B5"].Value, // Pitch in semitones (-20 .. +20)
SpeakingRate = (double)ws.Range["B6"].Value // Rate factor (1/4 .. 4 )
};
}
// Slides
else if (ws.Name.StartsWith("Slide", StringComparison.OrdinalIgnoreCase))
{
int i = 0; s++;
if (s >= First && s <= Last)
{
while (ws.Range["B2"].Offset[i, 0].Text.ToString() != "")
{
// Get delay and argument
int del = (int)(double)ws.Range["A2"].Offset[i, 0].Value;
string arg = ws.Range["B2"].Offset[i, 0].Text.ToString();
// Create and add the action
PPAction action = new PPAction(del, arg);
script.Actions.Add(action);
// Prefetch voices
if (action.Com == EPCommand.Speak)
{
Console.WriteLine(arg);
var input = new SynthesisInput { Text = arg };
var response = tts.SynthesizeSpeech(input, vspp, ac);
action.Wav = new MemoryStream(response.AudioContent.ToByteArray(), true);
}
// Increment action counter
i++;
}
}
}
}
// Return the script
return script;
}
/// <summary>
/// Kills the process indentified by its signature.
/// </summary>
/// <param name="signature"></param>
static void Kill(string signature, string title)
{
// Create an array of all running Excel processes
Process[] processes = Process.GetProcessesByName(signature);
// Loop over these processes
foreach (var process in processes)
{
// Only look at the instance with an empty window title
if (process.MainWindowTitle == title)
{
// Kill the process
process.Kill();
}
}
}
}
}