Archives
-
Office 2010 Helps Me Out
I stink at proof reading. I miss a slew of grammatical and spelling errors. Okay, slew is an understatement.
When I started working heavily with speech technology a few years ago I discovered something interesting. If I used text-to-speech to read what I wrote I caught all the errors. My completely unprofessional diagnosis - my brain is filtering the data stream from my eyes but not my ears. I immediately started using this new trick to improve my proof reading.
The one problem I had however was that almost nothing has built-in TTS support. I was able to add it to Word using a macro and copy and paste everything into Word but this was prone to errors and frankly a pain in the butt.
Today I happen to be digging through Outlook and discovered a new command in 2010 that I hadn't seen before –> Speak. It is detailed here but essentially it reads the selected text and works throughout Office (finally giving me TTS functionality for my emails).
-
SQL Denali Makes Even Fast Look Slow
I've seen a lot of performance testing over the years, every so often I'm even impressed a bit by the results. Here is one however that will melt the mind of anyone working against large SQL databases: SQL Denali + 1.4 billion with a b rows = 455x increase from 2008 R2. No, I didn't fat finger an extra 5 there. It was that big.
How? By using a new feature called a Column Store Index (aka Column Indexing). For those wanting the details on how it works and how they tested it you can find the PDF here. Not interested? You sure? I promise, its really short.
One major caveat however is that not all data types are supported within it. Most wouldn't make sense for this type of use anyway but one in particular was a pain –> uniqueidentifier. I've long used uniqueidentifier as my ID for tables (makes n-tier, replication and merging databases a lot easier). The other gotcha types are the (max) text columns. If you thought you could get away with just setting everything to (max) then you can mark today as when you found out that was a bad idea. You can find the other limitations and restrictions here.
Looking at the performance I really could have used this feature more than once in the past.
-
Welcome! Support group meets Wednesdays at the Library...
Popularity has its downsides. My favorite bit is "Independent Mac security experts believe there is enough security built into the OS X operating system to protect users, given the current level of risk." This raises a question for me. If there is no need to protect OS X because of its built in security, why would there be a need for "Independent Mac Security Experts" in the first place?
-
Blue Badge
I've been very lucky in my career so far. I've had the pleasure of working on some amazing software with many brilliant people over the years. And starting in two weeks, I'll be continuing that trend. Starting at the end of the month, I'll be joining Microsoft. The official title is "Senior ISV Architect Evangelist" but, as with most titles, it doesn't really tell you much. Basically I'll be helping ISVs leverage the Microsoft stack. I'm excited because it means I'll be knee-deep in the latest and greatest bits. And given the number of cool toys coming to the developer's toy box, there will be plenty to wade through. Back in the early 90's I read Guy Kawasaki's "Selling the Dream" and thought this sounded like a dream job. And beginning next month, I get to start doing it.
-
Implementing InstanceDependencyProperty
If you were looking to the UCMA 3.0 documentation for information on how to use InstanceDependencyProperty, don't. Its incomplete and what is there is incorrect. In an effort to implement my custom activities in the same way as Microsoft, I wanted to ensure that property values didn't get destroyed when you moved on to the next activity in your workflow. This is vital if you plan on returning values from a Workflow Activity for another Activity to make use of. For example, you likely want the Recognition Result from the question you asked them so you can act on it. The InstanceDependencyProperty allows you to keep property changes you made within an Activity. Without it, the second the instance of that Activity is finished executing, the property changes are discarded. If you checked them from another activity, all you'd get are the default values. Not very useful if you ask me. So here is an stripped down example of an Activity that implements InstanceDependencyProperties properly: [csharp] using System.Workflow.ComponentModel; using System.Workflow.ComponentModel.Serialization; using Microsoft.Rtc.Workflow.Activities; namespace AudioRecordingActivity { public partial class Activity1 : Activity, IInstanceDependencyContainer { private static System.Collections.Generic.Dictionary _instanceDependencyProperties; public Activity1() { _instanceDependencyProperties = new System.Collections.Generic.Dictionary(); } public System.Collections.Generic.Dictionary InstanceDependencyProperties { get { return _instanceDependencyProperties; } } public readonly static InstanceDependencyProperty MyStringProperty = InstanceDependencyProperty.Register("MyString", typeof(string), typeof(Activity1), "Empty"); public string MyString { get { if (base.DesignMode) return InstanceDependencyHelper.GetValue(this, MyStringProperty).ToString(); else return InstanceDependencyHelper.GetValue(this, this.WorkflowInstanceId, this.MyStringProperty).ToString(); } set { if (base.DesignMode) InstanceDependencyHelper.SetValue(this, MyStringProperty, value); else return InstanceDependencyHelper.SetValue(this, this.WorkflowInstanceId, MyStringProperty, value); } } protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext) { MyString = "Test String"; return base.Execute(executionContext); } } } [/csharp] Here the key things to know:
- Your Activity needs to implement the IInstanceDependencyContainer interface
When you impement the interface, the private _instanceDependencyProperties dictionary must be static- Your properties must implement both prototypes of GetValue/SetValue. One is used for design-time, the other for run-time.
One other important note. In the documentation example they use a property with a TimeStamp type. You need to decorate this property with [TypeConverter(typeof(TimeSpanConverter))] or it will give you a very vague error at compile time. UPDATE: I had initially thought I had this fully working until I attempted to drop more than one of my custom activities on my workflow. I quickly found an issue with the static _instanceDependencyProperties however. Because it was static, changes to properties on Activity1 were reflected on Activity2. After some time with Reflector, looking at how Microsoft's activities were built, I noticed they used two different versions of InstanceDependencyHelper, one for design time and one for run time. Low and behold, if you implement both versions and remove the static statement you get the correct behavior. Its very unfortunate that the documentation doesn't explain this.
-
Building a New Record Audio Activity
One of the key activities missing from UCMA that we used in Speech Sever 2007 is the Record Audio Activity. This was one of my "go to" tools in the Speech Server arsenal, so I've been hurting without. So to eliminate my pain, and hopefully some other's, I've started building a replacement. While what I've built so far isn't nearly feature-complete, it will hopefully provide a suitable starting point for those looking to simply record some audio. Trust me, starting from scratch wasn't much fun. It may seem simple (and it really is) but it was extremely unclear when I started. A special thanks to Marshall Harrison who helped me put this together. What's Included
- InitialSilenceTimeout – How long should we record silence before we decide the calling isn't going to start talking
- EndingSilenceTimeout – How long should we record silence before we decided the caller has stopped talking
- FilePath – Full path where we should save the file (this is new, it wasn't definable in Speech Server)
- AudioEncoding – Format to encode the saved file as (Wma16Kbps,Wma48Kbps, Pcm16Khz or Pcm8Khz)
- RecordedLength - The length of the recorded made
What's Not Included
- LeadingSilenceTrimmedLength – We're just saving the raw audio, there is no post-processing of the file
- TrailingSilenceTrimmedLength – See above
- PlayBeep – This object simply records audio, it doesn't play any prompts or tones
- Prompts - See above
- TerminationDigits - The only method used for stopping recording at this point is silence from the caller
- CanBargeIn – Given the lack of prompts, there was nothing to "barge in" on
I've included the full project (and the compiled Debug and Release versions) here:Â AudioRecordingActivity1.2
UPDATE: I've fixed a few issues and updated the download to v1.1.Update: We're now at v1.2. This version solves some problems with Properties when multiple copies of the activity are in the same workflow -
UCMA 3 How-To: Inbound Call Throttling
Speech Server included a setting in the Administrator Console for inbound call throttling, simply set it and forget it. Here we'll discus how we can accomplish graceful inbound call throttling with UCMA 3.0. The basis for what we're discussing here is covered in the prior article "Decline A Call". We'll extend this code such that it checks for the number of calls currently connected to the application and gracefully declines calls with a busy signal we've reached capacity. One thing to understand about UCMA applications is that there are two objects in play here. The first is the Conversation object, the second is the Call object. Each Conversation instance can have one or more Calls associated with it. This is because (unlike Speech Server) UCMA supports conferencing. A conference is a single Conversation involving multiple Calls. For most IVR style applications however you'll have only a single Call per Conversation. For this reason (and because frankly its easier to understand) our sample here will be counting the number of "Conversations" rather than the number of "Calls". The ApplicationEndpoint class has a method called GetConverstations() which returns a generic collection of Conversation objects. When you create a new UCMA Workflow the default name for our ApplicationEnpoint is _endpoint. By executing _endpoint.GetConverstaions().Count we get an integer representing the number of Conversations active on this endpoint. Because we're making some assumptions with our application (a. there is a 1:1 ration or Conversations to Calls and b. we've got a single endpoint) we can test this value against our maximum threshold. For example, lets assume we have a maximum load of 50 calls:
if (_endpoint.GetConversations().Count > 50){
}
One note here, the call we are going decline here is included in GetConverstations().Count. We're not counting how many callers are within the Workflow, we counting the number of connections hitting the endpoint. This means there is a potential race condition here, if 50 people tried to call us at the exact same moment in time it would decline them all even though none of them had made it into the Workflow. Given how fast we reach this point it is unlikely, but it is something to keep in mind you are expecting that kind "instant load". In other words, American Idol needs a more nuanced method here. Now that we've got our count, we can simply Decline the call and return the status 486 (BUSY HERE) trigger a busy signal to the caller:
if (_endpoint.GetConversations().Count > 50) { call.Decline(new CallDeclineOptions(486)); return; }
-
Testing UCMA Applications
One of the first things you notice when you start developing UCMA is the lack of an integrated SIP Phone for testing your applications. Speech Server made it trivial to test out your application, simply press F5 and everything was ready to go. UCMA lacks the integrated environment so you'll need a software phone of some kind to try out your app. I have been using one called PhonerLite for years. It is freely downloadable from http://www.phonerlite.de/index_en.htm and works like a charm. The key reasons I like it is that it supports UDP/TCP/TLS is extremely simply to use and has a build-in debugger that shows you the SIP messaging (something that is extremely useful when your application simply doesn't answer your call). I would recommend going with the beta version (currently 1.85) at http://www.phoner.de/PhonerLiteBeta.zip. Unzip it someplace and run the PhonerLite executable. Select "manual configuration" Give yourself a user name (I always use "1234") Take the defaults for the next two screens Select the "Configuration" tab and then the "Network" sub-tab Change your Port to something unused and your connection type to TCP Enter the SIP address for your app in the "Destination name" field and click the green phone icon and you're off.
-
UCMA 3 How-To: Decline A Call
In Speech Server we had an activity for declining a call. Typically this was used when you wanted to pull your application "off-line" for some reason. UCMA 3.0 doesn't have this activity, but the Core API still has support for it. Rather than declining the call within the Workflow, we'll be declining the call prior to the Workflow starting. I'll show how to do this using the out-of-the-box code you get when creating a new CommunicationWorkflow. If you look inside Program.cs you'll find the StartWorkflow() method. When we reach this point in the application we already have a "call" object which makes it trivial to Decline the call. The default code looks like this:
1: private static void StartWorkflow(Call call, SipRequestData requestData)
2: {
3: Debug.Assert(call != null, "call != null");
4: Debug.Assert(call is AudioVideoCall || call is InstantMessagingCall,
5: "Only AudioVideoCall and InstantMessagingCall are subscribed to above.");
6:
7: WorkflowInstance workflowInstance = _workflowRuntime.CreateWorkflow(typeof(Workflow1));
We'll insert our code starting at line 6, we'll also add some intelligence so that it allows me to call from my developer line while rejecting everyone else.
1: private static void StartWorkflow(Call call, SipRequestData requestData)
2: {
3: Debug.Assert(call != null, "call != null");
4: Debug.Assert(call is AudioVideoCall || call is InstantMessagingCall,
5: "Only AudioVideoCall and InstantMessagingCall are subscribed to above.");
6:
7: if (!call.RemoteEndpoint.Participant.UserAtHost.StartsWith("1234@"))
8: {
9: call.Decline(new CallDeclineOptions(503));
10: return;
11: }
12:
13: WorkflowInstance workflowInstance = _workflowRuntime.CreateWorkflow(typeof(Workflow1));
14: ...
A couple of tips:
- Take a look at the call object. It has a lot of interesting properties that let you make intelligent decisions about the call before your workflow has started.
- Make sure you use a SIP code (CallDeclineOptions) that your gateways understand and make sense. You don't want to toss a BUSY HERE if it isn't in fact BUSY.
-
Workflow Differences in UCMA 3.0
The following is a list of activities from Speech Server 2007 and UCMA 3.0. UCMA brings with it a number of new activities (due mostly to UCMA handling both Speech and Instant Messaging) but it also drops a number of activities we've become used to having in Speech Server.
Speech Server 2007 UCMA 3.0 AnswerCall AcceptCall BlindTransfer BlindTransfer Command SpeechCommand ConsecutiveNoInputsSpeechEvent ConsecutiveNoInputsSpeechEvent ConsecutiveNoRecognitionsSpeechEvent ConsecutiveNoRecognitionsSpeechEvent ConsecutiveSilencesSpeechEvent ConsecutiveSilencesSpeechEvent DeclineCall -
DetectAnsweringMachine -
DisconnectCall DisconnectCall FormFillingDialog -
GetAndConfirm -
GoTo GoTo HelpCommand SpeechHelpCommand InvokeWorkflow -
MakeCall OutboundCall Menu -
NavigableList -
QuestionAnswer SpeechQuestionAnswer RecordAudio -
RecordMessage -
RepeatCommand SpeechRepeatCommand SaltInterpreter -
SetTaskStatus -
SpeechSequence CommunicationsSquence Statement SpeechStatement Validator -
VoiceXmlInterpreter -
-
CallDisconnectedEvent -
CallOnHoldEvent -
CallOnHoldTimeoutEvent -
CallRetrievedEvent -
GetPresence -
InstantMessagingStatement -
InstantMessagingQuestioNAnswer -
InstantMessagingCommand -
InstantMessagingCommand -
InstantMessagingHelpCommand -
ConsecutiveNoInputsInstantMessageEvent -
ConsecutiveSilencesInstantMessagingEvent -
ConsecutiveNoRecognitionsInstantMessagingEvent In the coming weeks I'll be covering some workarounds to the missing activities.