UWP/MVVM: minimal implementation

And barely that. Code

Advertisements

PowerShell: call your own DLLs

Firstly credit for the syntax.

I want to write a library in C#, and invoke it from PowerShell. Let us also say this is a bare-bones Windows 7…10 PC, and therefore has no C# compiler, but obviously does have PowerShell natively, even if the versions vary (2-5).

C# Compiler

In fact every Windows PC has a C# compiler, in the framework folder, e.g. [C:\Windows\Microsoft.NET\Framework\v4.0.30319]. I did not want to add this to the path. I also wanted to understand the minimum set of C# artifacts to compile code. Turns out there are just 2 required: [csc.exe] and [cscui.dll]. I copied this into my PowerShell working folder. So let us say we have a library [MathLib.cs] (see the credit). You can compile this thus:

.\csc.exe /target:library MathLib.cs

, which results in this:

psdll01

Load and call a DLL from PowerShell

You can then load it using…

[Reflection.Assembly]::LoadFile(“d:\sandbox\psdll\MathLib.dll”)

and invoke it like this:

[MathLib.Methods]::Sum(10, 2)

, or load it like this:

$mathInstance = new-object MathLib.Methods

and invoke it like this:

$mathInstance.Product(10, 2)

 

Again, all credit to Lee Holmes – I’ve just taken what he did and re-presented for my preferences.

Gist.

 

 

 

DotNet Core: Libraries, XUnit, VS Code. Part 1

On this page, there is a tutorial, involving full-fat VS, and at the bottom there are a number of comments around difficulties building the solution.

dotnetcorelib11

dotnetcorelib12

I saw a challenge in trying to prove it is actually easier to build using the DotNet CLI and VS Code, rather than FF VS. One reason might be that you are closer to the bare metal of DotNet Core: if you are running in FFVS, it might be hard to deduce if the build error is due to DotNet Core problems, or problems with Visual Studio’s integration with DotNet Core.

My previous articles on DotNet Core have only been around simple command line stuff, namely, using the default [dotnet new] command.

To start off, let’s get our CLI environment right. The next few lines (being PowerShell) set the date time as the prompt, recursively remove any existing files from the current root down, and custom-set colours (but then dotnet CLI comes along and ignores that, as we see in a moment):

function prompt(){"$(Get-Date)> "}
Remove-Item -Recurse *
$color = (Get-Host).PrivateData
$color.ErrorForegroundColor = "White"

Whereas before, I used [dotnet new], this time we’ll use a nonsense line to show some options:

The contrast in the red error message is poor. It says (note the typo):

Unrecognized type: nonsense
Avaiable types for C# :
- Console
- Web
- Lib
- xunittest

That tells us what is available. The msdn tutorial is for a library and a test, so let’s start with the lib option [dotnet new -t Lib], and then see the files that creates:

So nothing built yet. Now we’ll try [dotnet run]:

Perhaps now we can do a run? No, it’s a library, you don’t Run a library:

So we can distribute a library. OK…

At this point, we have a library file that contains and does precisely nothing:

DotNetCoreLib10.PNG

That’s enough for a first post on this, as I have stuff to do. I’ll probably complete it over a number of days. More anon.

Unmanaged code: calling win32 api from .Net

The first and second shots show the happy path. The third shot shows the effect of willfully misspelling MessageBox. You don’t get a compile time error because it is after all unmanaged code that .Net is accessing.

Useful site here

 

using System;
using System.Runtime.InteropServices;
namespace cli01 {
 internal class Program {
enum Options {
 OptionOk = 0,
 OptionOkCancel,
 OptionAbortRetryIgnore,
 OptionYesNoCancel,
 OptionYesNo,
 OptionRetryCancel,
 OptionCancelTryagainContinue
 }
[DllImport("user32.dll")]
 private static extern int MessageBox(IntPtr hWnd, string boxContent, string boxCaption, Options options);
private static void Main() {
 MessageBox(IntPtr.Zero, "The message in the box", "The caption", Options.OptionOkCancel);
 }
 }
}


The MSDN link on MessageBox. Look to be some errors in there suggesting it doesn’t get much love.

Windows 10 Speech: speaking and storing as audio

This both speaks and stores as audio (wav) the passed text:


 

The code underpinning that:


 

using Windows.UI.Xaml.Controls;
using TextToSpeech;
namespace App1 {
public sealed partial class MainPage : Page
 {
 public MainPage()
 {
 InitializeComponent();
 var si = new SpeakIt();
 var textToSpeak = " I have a high respect for your nerves";
 SpeakIt.ReadText(textToSpeak);
 si.StoreText(textToSpeak);
 }
 }
}

 

 

using System;
using Windows.UI.Xaml.Controls;
using Windows.Media.SpeechSynthesis;
using System.Linq;
using Windows.Storage;
using Windows.Storage.Streams;
using System.Threading.Tasks;
namespace TextToSpeech
{
 public class SpeakIt
 {
 private const string PreferredVoice = "Susan";
 private const int BufferSize = 4096;
 private SpeechSynthesizer _synthesizer = new SpeechSynthesizer();
public SpeakIt() {
 SetPreferredVoice();
 }
public static async void ReadText(string mytext) {
 // requires the using Windows.UI.Xaml.Controls namespace...
 var mediaPlayer = new MediaElement();
using (var speech = new SpeechSynthesizer()) {
 speech.Voice = SpeechSynthesizer.AllVoices.First(voice => voice.Id.Contains(PreferredVoice));
 var stream = await speech.SynthesizeTextToStreamAsync(mytext);
 mediaPlayer.SetSource(stream, stream.ContentType);
 mediaPlayer.Play();
 }
 }
 
 public async void StoreText(string myText) {
 var synthesisStream = await _synthesizer.SynthesizeTextToStreamAsync(myText);
 var sf = await CreateLocalFile($"{Guid.NewGuid()}.wav");
 await SaveSpeechStreamToStorageFile(synthesisStream, sf);
 }
private static async Task<StorageFile> CreateLocalFile(string fileName) {
 // https://msdn.microsoft.com/en-gb/library/windows/apps/br227251
 var sfo = ApplicationData.Current.LocalFolder;
 var sf = await sfo.CreateFileAsync(fileName); 
 return sf;
 }
private static async Task SaveSpeechStreamToStorageFile(SpeechSynthesisStream synthesisStream, StorageFile sf) {
 var writeStream = await sf.OpenAsync(FileAccessMode.ReadWrite);
 var outputStream = writeStream.GetOutputStreamAt(0);
 var dataWriter = new DataWriter(outputStream);
 var buffer = new Windows.Storage.Streams.Buffer(BufferSize);
while (synthesisStream.Position < synthesisStream.Size) {
 await synthesisStream.ReadAsync(buffer, BufferSize, InputStreamOptions.None);
 dataWriter.WriteBuffer(buffer);
 }
 dataWriter.StoreAsync().AsTask().Wait();
 outputStream.FlushAsync().AsTask().Wait();
 outputStream.Dispose();
 writeStream.Dispose();
 }
private void SetPreferredVoice() {
 _synthesizer.Voice = SpeechSynthesizer.AllVoices.First(voice => voice.Id.Contains(PreferredVoice));
 }
 }
}

					

UWP / Speech

Pretty much just screenshots. In practice Speech remains as-is from Phone 8.1 as far as I can see. On this occasion I moved my VSFF to a VM running W10 Enterprise, as you need that for the emulators. However although on a Windows 8.1 install in 2014 I could get the emulator working, this time I could not.

I had not previously grasped that whereas the previous generation of Speech (references e.g. the “Hazel” voice) manage both the speech creation and the audio “rendering”, the new generation (references e.g. the “Susan” voice) does not: typically you can create the speech stream in a non-e.g. XAML context, but to render you typically use MediaElement… which uses a XAML page. Terrible generalisations on my part, but I can see it is probably related to a wish to be SOLID.

Anyhoo, these are the key takeaways for me right now… real simple, but proves the point (what point? 🙂 ). The use of the voice “Susan” in the snippet assumes that you have installed it.

using Windows.UI.Xaml.Controls;
using TextToSpeech;
namespace App1 {
public sealed partial class MainPage : Page
 {
 public MainPage()
 {
 InitializeComponent();
 SpeakIt.ReadText("this is a test; oh yes");
 }
 }
}

 

using System;
using Windows.UI.Xaml.Controls;
using Windows.Media.SpeechSynthesis;
using System.Linq;
namespace TextToSpeech
{
 public static class SpeakIt
 {
 private const string PreferredVoice = "Susan";
public static async void ReadText(string mytext) {
 MediaElement mediaPlayer = new MediaElement();
using (var speech = new SpeechSynthesizer()) {
 speech.Voice = SpeechSynthesizer.AllVoices.First(voice => voice.Id.Contains(PreferredVoice));
var x = SpeechSynthesizer.AllVoices.ToList();
 foreach (var item in x) {
 var x1 = item.Description;
 var x2 = item.DisplayName;
 var x3 = item.Gender;
 var x4 = item.Id;
 var x5 = item.Language;
 }
SpeechSynthesisStream stream = await speech.SynthesizeTextToStreamAsync(mytext);
 mediaPlayer.SetSource(stream, stream.ContentType);
 mediaPlayer.Play();
 }
 }
 }
}

This is a useful MVA video on 8.1 (i.e. 10) speech.

Just the screenshots I took while trying to get a result – may of them are very close to those from 1.5 years back, the only difference being Windows 10 now, Windows 8.1 then:

 

PowerShell: splitting an input file and saving to wav format in chunks

On 4 out of 5 days, I have a car journey that is between 0.75 and 1.25 hours. I want to be able to take a free (e.g. Project Gutenberg) book, or at least a DRM free book, split it into sections, and create an audio file from each section.
Let’s say that the following is my entirety of my book:

Guten01

I want to read/hear in sections: lines 1 and 2 (section 1), lines 3 and 4 (section 2), lines 5 and 6 (section 3), line 7 (section 4), giving this:

Guten02

This PowerShell is one way to do that (Although I write the split text back out to disk and then read it back in, that step could be removed).

Guten03

function Get-FileName($extension = "txt") {
 "{0}{1}_{2}.{3}" -f ($outputRootDir, $outputFileNamePrefix, $chunk, $extension)
}
function Write-WavFile() {
 $speech = New-Object -TypeName System.Speech.Synthesis.SpeechSynthesizer
 $speech.SelectVoice("Microsoft Hazel Desktop")
 $textToSpeak = Get-Content -Path $(Get-FileName) -Encoding UTF8
 $speech.SetOutputToWaveFile($(Get-FileName "wav"))
 $speech.Speak($textToSpeak)
 $speech.Dispose()
 $speech = $null
}
function Split-File (
 $fileToSplit = 'C:\Temp\pandp.txt',
 $splitMarker = "SPLITHERE",
 $outputFileNamePrefix = "TheseLinesAudio",
 $outputRootDir = "c:\temp\"
) {
 Add-Type -AssemblyName System.Speech
 $reader = New-Object -TypeName System.IO.StreamReader($fileToSplit)
 $chunk = 1
 $speech = New-Object -TypeName System.Speech.Synthesis.SpeechSynthesizer
 $speech.SelectVoice("Microsoft Hazel Desktop")
 while (($line = $reader.ReadLine()) -ne $null) {
 if ($line -match $splitMarker) {
 Write-WavFile
 $chunk++
 } else {
 Add-Content -Path $(Get-FileName "txt") -Value $line -Encoding utf8
 }
 }
 Write-WavFile
 $reader.Close()
 $reader.Dispose()
 $reader = $null
}
#entry point...
Split-File