Archive
HowTo: Optimize your website enabling web server GZIP compression
Was recently checking out the fine tools from GTmetrix for optimizing some websites I manage and one of the issues I noticed on a client’s site was that GZIP compression was turned off.
They have a GZIP compression setup article for IIS and Apache web servers that contains a sample for Apache with compression rules for various mime types and exceptions for older Mozilla 4-era and MSIE browsers that may have issues with it.
There is also GZIP compression at NGINX
A simple tool to check a remote URL just for GZIP compression is at https://varvy.com/tools/gzip/ and at their read more page they list alternative ways to set up GZIP compression for Apache web servers (they do list fewer MIME type there, so do checkout the sample from GTmetrix too and combine) and an example for NGINX that also defines some extra rules for IE6 and exclusion for IE<6.
HowTo: Use DotNetZip (Ionic.Zip) library in Silverlight
If you want to use the latest DotNetZip library (version 1.9.1.8) with Silverlight, you should get a patched version from http://dotnetzip.codeplex.com/workitem/14049, instead of the official download for Silverlight.
It seems the Silverlight build in 1.9.1.8 official download doesn’t work correctly, since it tries to get IBM437 Encoding (as the original ZIP spec required) from Silverlight, but that encoding is not available via Encoding.GetEncoding method in Silverlight (neither encoder and decoder fallbacks for GetEncoding are available as in .NET 4.5).
So the patched version uses Unicode encoding instead (only UTF-8 and Unicode [UTF-16] Encodings are available in Silverlight), although it could have also used a text encoding class generator for the missing encoding and change the source code similar to how Mike Taulty had done with an older version of DotNetZip.
I do prefer the Unicode approach, since using the IBM437 codepage would have issues with Unicode filenames. ZIPs with such filenames are supported fine by most modern ZIP viewers, including Windows Explorer’s Compressed Folders views in recent versions of Windows.
HowTo: Write directly into a ZIP stream using DotNetZip
At ClipFlair’s Playground Silverlight app I’m using DotNetZip library (Ionic.Zip namespace) for writing saved component state to a .ZIP file. In fact the Activity container there can create one .ZIP for each state that contains one .ZIP per-component it hosts (it can even host multiple instances of its own Activity component type to have nested Activities in its ZUI interface).
DotNetZip is a perfect match for Silverlight’s security restrictions, where OpenFileDialog and SaveFileDialog only give you a stream to work with (unless your app has been granted elevated trust), since apart from working with filesystem files and folders it allows you to also give it content to compress in the form of a stream.
Note: the latest DotNetZip 1.9.1.8 official version for Silverlight doesn’t work correctly – you need a patched build, see: https://zoomicon.wordpress.com/2012/11/25/howto-use-dotnetzip-ionic-zip-library-in-silverlight/
For working with streams at DotNetZip there are two options (as explained at http://dotnetzip.codeplex.com/discussions/231712):
1) pass it an in-memory buffer (this is a non-optimal method)
public override void SaveOptions(ZipFile zip, string zipFolder = "")
{
base.SaveOptions(zip, zipFolder);
MemoryStream stream = new MemoryStream(); //non-optimal, uses memory buffer
gridCaptions.WriteCaptions(stream, "captions.srt"); stream.Position = 0;
zip.AddEntry(zipFolder + "/captions.srt", stream);
}
2) the other is to pass it a write delegate for a method that writes the data to a stream that DotNetZip will pass to it when needed.
public override void SaveOptions(ZipFile zip, string zipFolder = "") { base.SaveOptions(zip, zipFolder); zip.AddEntry(zipFolder + "/captions.srt", SaveCaptions); } public void SaveCaptions(string entryName, Stream stream) { gridCaptions.WriteCaptions(stream, entryName); }
In case one needs to pass more parameters (apart from the zip entry name and the stream that DotNetZip passes to the delegate), they could instantiate a class instance and keep the parameter there, then pass a method of that class via the delegate to DotNetZip. An easier way is to use an anonymous method and define it in a context where the parameter is available, e.g. as a local variable.
public override void SaveOptions(ZipFile zip, string zipFolder = "") { base.SaveOptions(zip, zipFolder); foreach (BaseWindow window in activity.Windows) SaveWindow(zip, zipFolder, window); } private static void SaveWindow(ZipFile zip, string zipFolder, BaseWindow window) { string title = ((string)window.Title).TrimStart();
//using TrimStart() to not have filenames start with
//space chars in case it's an issue with ZIP spec if (title == "") title = window.GetType().Name; zip.AddEntry( zipFolder + "/" + title + " - " + Guid.NewGuid() + ".clipflair.zip", new WriteDelegate((entryName, stream) => { window.SaveOptions(stream); }) );
//save ZIP file for child window }
HowTo: Compress speech audio using CSpeex codec at Silverlight apps
Following up on my post on how to fix CSpeex (http://cspeex.codeplex.com) to compile at Silverlight 5 (see https://zoomicon.wordpress.com/2012/06/09/fix-compile-cspeex-audio-codec-at-silverlight-4-rc-and-silverlight-5/).
Initially I thought it was not working correctly, but then I noticed I was running it on Terminal Server and didn’t have an audio device available. Running SLAudioDemo included in the CSpeex distribution (after doing the fix mentioned at my post above) seems to work fine on my local PC.
The SLAudioDemo demonstrates capturing audio into memory as a compressed CSpeex stream which it can decompress and save into a .WAV file that one can play say with Windows Media Player. The WAV is uncompressed, since SLAudioDemo compresses in memory (via StreamAudioSink) and decompresses to normal PCM WAV when you save (else it would be unplayable).
Obviously the Speex codec is useful for uploading (e.g. via HSS Interlink) compressed audio to a server (or to the other end at some voice chat app) and decompress there before playback (similarly the other way around).
At the server side if you want to do audio storage too, you could also keep an extra decoded copy of the compressed Speex audio data, recoded into WMA using automation of Microsoft Expression Encoder for example (see Microsoft Transform Manager, can do this in the background if you uncompress into a .WAV file at some incoming folder). That way you could serve that audio to your Silverlight client as Speex-encoded data file, but also to other users that use Windows Media Player to get the audio file from some URL etc. Could also encode to MP4/AAC or MP3 but there are licensing issues with those from what I know.
For the CSpeex encoder see StreamAudioSink.cs. Only 2 lines are needed in MainPage.xaml.cs of SLAudioDemo:
streamAudioSink = new StreamAudioSink();
streamAudioSink.CaptureSource = _captureSource;
after you 1st declare at the top level (must have this as a top-level field, else the garbage collector can get it)
private StreamAudioSink streamAudioSink;
Here’s the decoding code that saves the WAV (from MainPage.xaml.cs):
if (sfd.ShowDialog() == true) { // User selected item. Only property we can get to is. using (Stream stream = sfd.OpenFile()) { JSpeexDec decoder = new JSpeexDec(); decoder.setDestFormat(JSpeexDec.FILE_FORMAT_WAVE); decoder.setStereo(true); Stream memStream = streamAudioSink.MemFile.InnerStream; memStream.Position = 0; decoder.decode(new RandomInputStream(memStream),
new RandomOutputStream(stream)); stream.Close(); } }
Don’t get puzzled by the use of RandomInputStream / RandomOutputStream. These are from Java’s I/O package (java.io.*) that CSpeex has ported to .NET, since it’s in fact a port of JSpeex, a Java implementation of the Speex codec.
Fix: compile CSpeex audio codec at Silverlight 4 RC+ and Silverlight 5
Just added an issue to CSpeex project – http://cspeex.codeplex.com/workitem/18209 – with some fixes to make it compile for Silverlight 4 RC and higher (was for Silverlight 4 Beta).
1) at "MainPage.xaml.cs", replace with this method implementation:
private void TakeSnapshot_Click(object sender, RoutedEventArgs e)
{
if (_captureSource != null)
{
// capture the current frame and add it to our observable collection
_captureSource.CaptureImageCompleted += (source, eventargs) =>
{
_images.Add(eventargs.Result);
};//”ImageCaptureAsync" method replaced by "CaptureImageAsync" at Silverlight 4 RC
_captureSource.CaptureImageAsync();
}
}
2) at "StreamAudioSync.cs" do this small change:
protected override void OnFormatChange(AudioFormat audioFormat)
{
if (audioFormat.WaveFormat == WaveFormatType.Pcm)//”PCM" renamed to "Pcm" at Silverlight 4 RC (after SL4beta)
{
…
Note that the CSpeex-SL project won’t load if you don’t have Silverlight 3 SDK installed. After loading you can open up properties of that project and of SLAudioDemo one and set the target to Silverlight 5 from Silverlight 3, then save those two projects and rebuild the solution.