Oct 11 2011

What Does Windows 8 Mean for Windows Phone Developers?

Category: Desktop and Server | MobileJoel Ivory Johnson @ 01:24

It was only a few weeks ago that Windows 8 was unveiled at the Build conference. At first glance it looks a lot like Windows Phone with heavy use of the Metro visual language. The only part of the system that hasn't had the Metro touch is the desktop (unlike previous versions of Windows the Desktop is not something that is always running). The Start menu looks like the Windows Phone start screen only it scrolls horizontally instead of vertically. The only mention of Silverlight is that you could still use it in Desktop mode for backwards compatibility, but the Metro [default] instance of IE would run no plug-ins, including Silverlight. The programming model also is not based off of Silverlight or the Desktop .Net runtime. Its based on something new called WinRT (Windows Runtime). 

At first glance this is something that has concerned Silverlight and Windows Phone developers. At first glance some one might come to the conclusion that the skill in which he or she has invested has become second class in Windows 8. Is Silverlight really getting killed off? What's going to happen for the Silverlight based Windows Phone?

I don't know the future any more than the next person, but what I saw at Build isn't something that raised concern. I found it to be rather reassuring. Before I explain why let me grant the elephant in the room, the rumor that Silverlight is going to be dead. I don't believe this rumour. For years people have predicted that certain Microsoft Technologies were dead (DirectX, .Net, and many other technologies that we still use today). But I'll grant it anyway so that we can explore what seems to be a popular concern. 

Let's assume that next year Microsoft announces that it is going to sunset Silverlight and toss out the Windows Phone programming model in favour of the Windows 8 programming model. What does this mean for the skills that you have developed? Are they now useless? You've been developing skills in C#/VB, XAML, asynchronous programming, and some APIs that were specific to Silverlight and Windows Phone. Let's look at how each one of these will contribute to your Windows 8 development. 

Languages: C# and VB

C# and VB are still being used on Windows 8. If you've been using these languages you can continue to use them. Additionally if you know C++ or have algorithms that had been written in C++ you'll be able to port them over to Windows 8. Windows 8 also supports JavaScript as a programming language too. 

XAML

XAML is still used on Windows 8 for building your UI. Many of the elements you've become familiar with are present in addition to some new ones. No huge changes there. 

Asynchronous Programming

One of the challenges for developers that were new to Silverlight was that task that one may have been used to doing synchronously are only available as asynchronous calls. On Windows 8 you'll find that many of the tasks that were asynchronous in Silverlight and Windows Phone are still asynchronous. Additionally other APIs have been made asynchronous, including File IO. 

The Familiar and What This All Means

You'll come across APIs that look similar of not identical to what you've seen in Windows Phone and Silverlight. Windows 8 has the concept of an application getting tombstoned, specifying the permissions it needs, and so on. Windows 8 Metro applications will only be distributed through the Marketplace. Doesn't all of this sound familiar. If you are a Windows Phone developer it should. You've already got a head start on Windows 8 development. This is far from the doom and gloom picture that some stories would have some one believe. 

If you want to dive into Windows 8 programming the 64-bit development images are available for download. I suggest running them on real hardware. I tried them in the emulator VirtualBox and it's just not the same experience there. 

Tags: , , ,

Sep 20 2011

Remember the Permission

Category: MobileJoel Ivory Johnson @ 08:01

I had a bit of a late night last night. I had just downloaded the Windows Live SDK and wanted to try it out. I took the Voice Memo application tha tI put on CodeProject.com and tried to add a feature to upload recordings to Skydrive. In the SDK there is a SignInButton controll and setting some parameters on this control along with responding to an event the control has is all that is needed to to make use of it. When I ran my program it failed with an error about the WebBrowserControl neededing to be on the visual tree before it is used. 

You cannot call WebBrowser methods until it is in the visual tree."

This was a little confusing to me at first. I am not using a web browser control anywhere in my program. So I played with the parameters on the control trying to see if I could change the response I was getting. After continual failure I posted a question on the Windows Live Connect support forums.  One of the Microsoft employees asked me if I could make a simple project demonstrating the problem. When I made my simple project the error would not occur. I then realized what was causing the problem. The Voice Memo application has had its permissions appropriately reduced. But those reduced permissions did not include what was needed by the login control. I ran the Windows Phone Marketplace Test kit and it told me the other permissions that I needed; ID_CAP_WEBBROWSERCOMPONENT  and ID_CAP_NETWORKING. After adding those permissions it worked like a charm!

Rather than sweep my mistake under a rug I thought I would share it in the hopes that posting this will save some one from having made the simple yet time consuming oversight that I had

 

Tags: ,

Sep 16 2011

Windows Phone Camp is Coming!

Category: MobileJoel Ivory Johnson @ 16:08

What's a Windows Phone Camp? 
A free, full day event chock-full of everything you need to know to develop a Windows Phone application. Whether you're a seasoned veteran or just getting started with .NET development this full-day event is for you. Interested in profit? We'll also lead discussions on how to monetize your applications and generate profits with your apps.

Don't miss the new Windows Phone 7.5 (codename "Mango") features as well - with detailed sessions in the afternoon around Fast Application Switching, Multitasking, Live Tiles, Push Notifications, and more. 

The day will be capped with an 
open lab hands-on session and prizes for apps completed. This is the perfect opportunity to begin work on your dream application, or finish that app you've already started, with Windows Phone experts there to guide you every step of the way. Bring your own laptop to join in the fun and show off your killer app! 

Agenda

9:00 AM

 

Description: Description: Description: C:\Users\Public\Documents\Webcamps Email - All\PHONE CAMPS\phonecamps_metro_er_allup_OFT_EVANG\spacer.gif

 

Welcome Campers

9:15 AM

 

Description: Description: Description: C:\Users\Public\Documents\Webcamps Email - All\PHONE CAMPS\phonecamps_metro_er_allup_OFT_EVANG\spacer.gif

 

How to make money with your Windows Phone app

10:00 AM

 

Description: Description: Description: C:\Users\Public\Documents\Webcamps Email - All\PHONE CAMPS\phonecamps_metro_er_allup_OFT_EVANG\spacer.gif

 

Frameworks for fun and profit > Silverlight and XNA

 

 

Description: Description: Description: C:\Users\Public\Documents\Webcamps Email - All\PHONE CAMPS\phonecamps_metro_er_allup_OFT_EVANG\spacer.gif

 

Canteen

1:00 PM

 

Description: Description: Description: C:\Users\Public\Documents\Webcamps Email - All\PHONE CAMPS\phonecamps_metro_er_allup_OFT_EVANG\spacer.gif

 

Hands-on lab

3:00 PM

 

Description: Description: Description: C:\Users\Public\Documents\Webcamps Email - All\PHONE CAMPS\phonecamps_metro_er_allup_OFT_EVANG\spacer.gif

 

Cool stuff your app can do

4:00 PM

 

Description: Description: Description: C:\Users\Public\Documents\Webcamps Email - All\PHONE CAMPS\phonecamps_metro_er_allup_OFT_EVANG\spacer.gif

 

To the Cloud

4:45 PM

 

Description: Description: Description: C:\Users\Public\Documents\Webcamps Email - All\PHONE CAMPS\phonecamps_metro_er_allup_OFT_EVANG\spacer.gif

 

Be What's Next > People's Choice Awards


Prerequisites 

Bring a notebook computer and identification. Also check out the APP HUB where you can get developer toolslearn about application featuresunderstand common task for Apps and register and load your APP.


Notes:
 
This event is brought to you by Microsoft and is free of charge. However, you are responsible for booking and paying for your own travel and accommodations.

Event Locations
9/20 - Charlotte, NC
9/22 - Alpharetta, GA
9/27 - Malvern, PA
9/29 - Reston, VA
10/18 - Chevy Chase, MD
10/19 - New York, NY
10/25 - Tampa, FL
10/27 - Burlington, VT
11/2 - Raleigh, NC
11/4 - Ft. Lauderdale, FL
11/8 - Orlando, FL
11/10 - New Paltz, NY
11/10 - Miami, FL
11/15 - Blacksburg, VA
11/17 - Washington, DC
11/29 - Pittsburgh, PA
11/29 - Atlanta, GA
12/1 - Long Island, NY

Tags: , ,

Aug 29 2011

StartUp Camp

Category: Joel Ivory Johnson @ 16:01

This weekend I went to an event known as "StartUp." Apparently these events are not uncommon, but it was new to me and was an interesting experience. In a Start-up weekend people present their ideas for an application to developers, artists, and people of other skills. After the presentation of the initial ideas people organize themselves into teams and try to make the application a reality.

The event ran from Friday to Sunday. Of those three days I was only present on Sunday to help some people out and ended up being dedicated to a single team helping them out with their Windows Phone coding. At the end of the event every one does their presentations on what they came up with to a panel of judges. There's also people there that are ready to fund projects. I wish I had known of events like this before. These are perfect opportunities for people that have application ideas but can't make the application by themselves. 

Tags: ,

Aug 27 2011

Slides from What's New in Windows Phone Mango

Category: Joel Ivory Johnson @ 16:36
Last month I did a presentation at the Macon .Net user group on what's new in Windows Phone Mango. I also presented this at the Atlanta Windows Phone developer's group. The slides were uploaded to Skydrive a few days ago but I never did post the link. So here it is. You can download the slides and the example code from here.

Tags: , ,

Aug 7 2011

Windows Phone NTP Client

Category: MobileJoel Ivory Johnson @ 04:17
Download Code (53 KB)

I've got plans for an application in which I need to know with certainty the current time. So I don't want to trust the time as reported by the user's device. Instead I need to retrieve the time from a server. I made a NTP (Network Time Protocol) client for retrieving this information. This is something that couldn't be done on Windows Phone 7 prior to Mango. But with the fall update (Mango) access to sockets is granted.

The use of the client is pretty simple. At minimum one can create the client with the default constructor, subscribe to the ReceivedTime event, and call the RequestTime method to initiate a request. The ReceivedTime event may be called on a thread other than the UI thread so remember to use a dispatcher when making UI updates.

NTP Client

This is an example of a client using the code to display both the system time and the network time.

public partial class MainPage : PhoneApplicationPage
{
    private NtpClient _ntpClient;
    public MainPage()
    {
        InitializeComponent();
        _ntpClient = new NtpClient();
        _ntpClient.TimeReceived += new EventHandler<NtpClient.TimeReceivedEventArgs>(_ntpClient_TimeReceived);
    }

    void _ntpClient_TimeReceived(object sender, NtpClient.TimeReceivedEventArgs e)
    {
        this.Dispatcher.BeginInvoke(() =>
                                        {
                                            txtCurrentTime.Text = e.CurrentTime.ToLongTimeString();
                                            txtSystemTime.Text = DateTime.Now.ToUniversalTime().ToLongTimeString();
                                        });
    }

    private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
    {
            
    }

    private void UpdateTimeButton_Click(object sender, RoutedEventArgs e)
    {
        _ntpClient.RequestTime();
    }
}

Tags: ,

Jul 10 2011

XNA Animated Sprite code uploaded to CodeProject.com

Category: Desktop and ServerJoel Ivory Johnson @ 06:51

I've uploaded some code I was working on to animate sprites in XNA.

Animating a sprite isn't difficult, but I wanted some way to animate them but reduce the coupling between code and the animation. The Content Pipeline is perfect for this. So I created a component that will handle the animation scenarios that I need along with a content extension so that I could load these animations as content. Right now the animation information is in an XML file. This is a stepping point towards having a graphical tool for handling this.

You can read about the code here or see a brief description of it in the video below

Tags: , , ,

Jun 30 2011

Mango Beta 2 Available for Phones Today!

Category: MobileJoel Ivory Johnson @ 03:48

The Beta 2 Mango Windows Phone Tools are available to developers today! Included with the beta is the ability for developers registered with the AppHub to flash their retail devices.

I know there are some non-developers out there that want to also flash their phones and they may wonder how they get get their phones reflashed with the Mango beta. For the time being they cannot. There is an inherent risk in reflashing the phone; you could end up with a bricked phone if something goes bad. If this happens Microsoft has budgeted to take care of repairing up to one phone per developer. But Microsoft doesn't see this risk as being appropriate for user audiences. [Some] developers on the other hand are willing to risk their device's life and limb to have early access to something new. If you brick your device today Microsoft won't be prepared to act on it for another couple of weeks. That's not the best case scenario. But the alternative was to wait another couple of weeks before releasing the Mango tools. If you don't feel safe walking the tight rope without a safety net then don't re-flash your device yet.

According to the Windows Phone Developer site if you are a registered developer you will receive an e-mail inviting you to participate in early access to Mango.

Tags: , ,

Jun 28 2011

Changing the Pitch of a Sound

Category: MobileJoel Ivory Johnson @ 15:28

I got a tweet earlier today from some one asking me how to change the pitch of a wave file. The person asking was aware that SoundEffectInstance has a setting to alter pitch but it wasn't sufficient for his needs. He needed to be able to save the modified WAV to a file. It's something that is easy to do. So I made a quick example

Video Example

I used a technique that comes close to matching linear interpolation. It get's the job done but isn't the best technique because of the opportunity for certain types of distortion to introduced. Methods with less distortion are available at the cost of potentially more CPU cycles. For the example I made no matter what the original sample rate was I am playing back at 44KHz and adjusting my interpolation accordingly so that no unintentional changes in pitch are introduced.

To do the work I've created a class named AdjustedSoundEffect. It has a Play() method that takes as it's argument the factor by which the pitch should be adjusted where 1 plays the sound at the original pitch, 2 plays it at twice its pitch, and 0.5 plays it at half its pitch.

If you are interested the code I used is below.

using System;
using System.IO;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Xna.Framework.Audio;

namespace J2i.Net.VoiceRecorder.Utility
{
    public class AdjustedSoundEffect
    {
        //I will always playback at 44KHz regardless of the original sample rate. 
        //I'm making appropriate adjustments to prevent this from resulting in the
        //pitch being shifted. 
        private const int PlaybackSampleRate = 16000;
        private const int BufferSize = PlaybackSampleRate*2;

        private int _channelCount = 1;
        private int _sampleRate;
        private int _bytesPerSample = 16;
        private int _byteCount = 0;
        private float _baseStepRate = 1;
        private float _adjustedStepRate;
        private float _index = 0;
        private int playbackBufferIndex = 0;
        private int _sampleStep = 2;

        private bool _timeToStop = false;

        private byte[][] _playbackBuffers;

        public bool IsPlaying { get; set;  }

        public object SyncRoot = new object();


        private DynamicSoundEffectInstance _dse;

        public static AdjustedSoundEffect FromStream(Stream source)
        {
            var retVal = new AdjustedSoundEffect(source);
            return retVal;
        }

        public AdjustedSoundEffect()
        {
            _playbackBuffers = new byte[3][];
            for (var i = 0; i < _playbackBuffers.Length;++i )
            {
                _playbackBuffers[i] = new byte[BufferSize];
            }
                _dse = new DynamicSoundEffectInstance(PlaybackSampleRate, AudioChannels.Stereo);
            _dse.BufferNeeded += new EventHandler<EventArgs>(_dse_BufferNeeded);
        }

        void SubmitNextBuffer()
        {
            if(_timeToStop)
            {
                Stop();
            }
            lock (SyncRoot)
            {
                byte[] nextBuffer = _playbackBuffers[playbackBufferIndex];
                playbackBufferIndex = (playbackBufferIndex + 1)%_playbackBuffers.Length;
                int i_step = 0;
                int i = 0;

                int endOfBufferMargin = 2*_channelCount;
                for (;
                    i < (nextBuffer.Length / 4) && (_index < (_sourceBuffer.Length - endOfBufferMargin));
                    ++i, i_step += 4)
                {

                    int k = _sampleStep*(int) _index;
                    if (k > _sourceBuffer.Length - endOfBufferMargin)
                        k = _sourceBuffer.Length -endOfBufferMargin ;
                    nextBuffer[i_step + 0] = _sourceBuffer[k + 0];
                    nextBuffer[i_step + 1] = _sourceBuffer[k + 1];
                    if (_channelCount == 2)
                    {
                        nextBuffer[i_step + 2] = _sourceBuffer[k + 2];
                        nextBuffer[i_step + 3] = _sourceBuffer[k + 3];
                    }
                    else
                    {
                        nextBuffer[i_step + 2] = _sourceBuffer[k + 0];
                        nextBuffer[i_step + 3] = _sourceBuffer[k + 1];

                    }
                    _index += _adjustedStepRate;
                }

                if ((_index >= _sourceBuffer.Length - endOfBufferMargin))
                    _timeToStop = true;
                for (; i < (nextBuffer.Length/4); ++i, i_step += 4)
                {
                    nextBuffer[i_step + 0] = 0;
                    nextBuffer[i_step + 1] = 0;
                    if (_channelCount == 2)
                    {
                        nextBuffer[i_step + 2] = 0;
                        nextBuffer[i_step + 3] = 0;
                    }
                }
                _dse.SubmitBuffer(nextBuffer);
            }
        }

        void _dse_BufferNeeded(object sender, EventArgs e)
        {
            SubmitNextBuffer();
        }

        private byte[] _sourceBuffer;
        

        public AdjustedSoundEffect(Stream source): this()
        {
            byte[] header = new byte[44];
            source.Read(header, 0, 44);

            // I'm assuming you passed a proper wave file so I won't bother 
            // verifying  that  the  header  is properly formatted and will 
            // accept it on faith :-)

            _channelCount = header[22] + (header[23] << 8);
            _sampleRate = header[24] | (header[25] << 8) | (header[26] << 16) | (header[27] << 24);
            _bytesPerSample = header[34]/8;
            _byteCount = header[40] | (header[41] << 8) | (header[42] << 16) | (header[43] << 24);
            _sampleStep = _bytesPerSample*_channelCount;
            _sourceBuffer = new byte[_byteCount];
            source.Read(_sourceBuffer, 0, _sourceBuffer.Length);


            _baseStepRate = ((float)_sampleRate) / PlaybackSampleRate;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pitchFactor">Factor by which pitch will be adjusted. 2 doubles the frequency,
        /// // 1 is normal speed, 0.5 halfs the frequency</param>
        public void Play(float pitchFactor)
        {
            _timeToStop = false;

            _index = 0;
            lock (SyncRoot)
            {
                _adjustedStepRate = _baseStepRate * pitchFactor;
                _index = 0;
                playbackBufferIndex = 0;
            }
            if(!IsPlaying)
            {
                SubmitNextBuffer();
                SubmitNextBuffer();
                SubmitNextBuffer();
                _dse.Play();
                IsPlaying = true;
            }
        }

        public void Stop()
        {
            if(IsPlaying)
            {
                _dse.Stop();
            }
        }
    }
}

Tags: , , ,

Jun 22 2011

Adding an E-Mail Account to the WP Emulator

Category: MobileJoel Ivory Johnson @ 07:03

For one reason or another you may find that you want to add a real e-mail account to the Windows Phone emulator. Unfortunately the emulator doesn't directly expose a way for you to do this; the settings area on the phone doesn't display the tile to access the e-mail settings. You can get to the settings application indirectly though. This path is convoluted, but it works.

You'll need to make a simple application that does nothing more than show a phone call task. Once the task is displayed accept the phone call then select the option to add another caller. This takes you to the People Hub. Swipe through the People Hub to the "What's New" and you will be prompted to add a Facebook or Twitter account. Select the option to do this (even though you are not really adding an account of that type) and when you asked what type of account you want to add you can select one of the e-mail account types.

Tags: ,

Jun 21 2011

Setting Custom Ringtones from Code [Mango:Beta 1]

Category: MobileJoel Ivory Johnson @ 03:20
Written against pre-release information

One of the new features coming with the next update to Windows Phone 7 is the ability to set custom ring tones. From within code you can make a ring tone available to a user (it's up to the user to accept the ring tone, so user settings won't ever be changed without user permission). I was looking at the new API for doing this, the SaveRingtonTask()

To use the API you first need to get the ringtone of interest into isolated storage. It can be either an MP3 file or a WMA file up to 30 seconds in length. If the file is a part of your application. Just set it's build type to "Resource".

file settings

Getting the file from being packed in the application to isolated storage is a matter of reading from a resource stream and writing to isolated storage.

var
s =
    Application.GetResourceStream(new Uri("/MyApplicationName;component/1up.mp3",
                                            UriKind.Relative));
{
    using (var f = IsolatedStorageFile.GetUserStoreForApplication().CreateFile("1up.mp3"))
    {

        var buffer = new byte[2048];
        int bytesRead = 0;

        do
        {
            bytesRead = s.Stream.Read(buffer, 0, 1024);
            f.Write(buffer, 0, bytesRead);
        } while (bytesRead > 0);

        f.Close();
    }
}

Once the file is in isolated storage you must pass the URL to the SaveRingtoneTask(). URIs to isolated storage are preceded with "isostore:" (there is also an "appdata:" prefix, but we won't be using it here). Give the ringtone a display name and call the show method to present the user with the option to save it. If you don't set the

SaveRingtoneTask srt = new SaveRingtoneTask();
srt.DisplayName = "1up";
srt.IsoStore= new Uri("isostore:/1up.mp3", UriKind.Absolute);
srt.IsShareable = true;
srt.Show();

Tags: , ,

Jun 20 2011

Peer Communication on Windows Phone 7

Category: MobileJoel Ivory Johnson @ 14:28

Written against pre-release information

One of the new things that we get with Windows Phone 7 is socket support. While I expected to be able to open sockets to other machines with servers running on them one thing caught me by surprised; that you can also send communication from phone to phone using UDP. I've got to give credit to Ricky_T for pointint out the presence of this feature and posting a code sample. I wanted to try this out myself. So I made a version of the code sample that would run on both Windows Phone and on the desktop (Silverlight 4 in Out of Browser mode). I was pleasantly surprised to that I was able to open up peer communication between the desktop and phone without a problem. This capability provides a number of solutions for other problems that I've been considering, such as automatic discovery and configuration for communicating with services hosted on a user's local network. 

Most of the code used in the desktop and phone version of this example are identical; I've shared some of the same files between projects. From the files that are not shared the counterparts in the phone and desktop version are still similar.  The core of the code is in a class called Peer. Let's take a look at part of the body of that class. 

 

//Define the port and multicast address to be used for communication
private string _channelAddress = "224.0.0.1";
private int _channelPort = 3007;

//The event to be raised when a message comes in
public event EventHandler<MessageReceivedEventArgs> MessageReceived; 

//the UDP channel over which communication will occur.
private UdpAnySourceMulticastClient _channel;

//Create tje cjamme;
public void Initialize()
{
    _channel = new UdpAnySourceMulticastClient(IPAddress.Parse(_channelAddress), _channelPort);
}

//Open the channel and start listening
public void Open()
{
    if (_channel == null)
        Initialize();
    ClientState = ClientStatesEnum.Opening;
            

    _openResult = _channel.BeginJoinGroup((result) =>
                                                {
                                                    _channel.EndJoinGroup(result);
                                                    ClientState = ClientStatesEnum.Opened;
                                                }, null);   
            
    Receive();
}


 

//The receive method is recursive. At the end of a call to receive it calls itself 
//so that the class can continue listening for incoming requests.
void Receive()
{
    byte[] _receiveBuffer = new byte[1024];

    _channel.BeginReceiveFromGroup(_receiveBuffer, 0, _receiveBuffer.Length, (r) =>
    {
        if(ClientState!=ClientStatesEnum.Closing)
        {
            try
            {
            IPEndPoint source;
            int size= _channel.EndReceiveFromGroup(r, out source);
            OnMessageReceived(_receiveBuffer, size,  source);                                                                                   
            }
            catch (Exception )
            {
            }
            finally
            {
                this.Receive();
            }
        }
    }, null);
}
public void Send(byte[] data)
{
    if(ClientState==ClientStatesEnum.Opened)
    {
        _channel.BeginSendToGroup(data, 0, data.Length, (r) => _channel.EndSendToGroup(r),null);
    }
}

This class only sends and receives byte arrays. My only goal here was to see the code work so there are other considerations that I have decided to overlook for now. I made a client to use this code too. The client sends and receives plain text. Before sending a block of text it is necessary to convert the text to a byte array. The encoding classes in .Net will take care of this for me. When a message comes in I can also use an encoder to convert the byte array back to a string.

For this program I am adding the incoming message to a list along with the IP address from which it came

void _peer_MessageReceived(object sender, MessageReceivedEventArgs e)
{
    Action a = () =>
                    {
                        string message = System.Text.UTF8Encoding.Unicode.GetString(e.Data, 0, e.Size);
                        MessageList.Add(String.Format("{0}:{1}", e.Endpoint.Address.ToString(), message));
                        OnIncomingMessageReceived(message, e.Endpoint.Address.ToString());
                    };
    if (UIDispatcher == null)
        a();
    else
        UIDispatcher.BeginInvoke(a);
}

public void SendMessage(string message)
{
    byte[] encodedMessage= UTF8Encoding.Unicode.GetBytes(message);
    _peer.Send(encodedMessage);
}

When the code is run on any combination of multiply phones or computers a message types on any one of the devices appears on all of them. Nice! Now to start making use of it.

Tags: ,

Apr 13 2011

What's Coming to Windows Phone in 2011?

Category: MobileJoel Ivory Johnson @ 06:15

With more that 13,000 applications in their Marketplace Microsoft came out on stage today to make some new announcements about what's coming to Windows Phone 7. We'll see a major upgrade on all Windows Phone devices by the end of the year and developers are getting access to a lot of new APIs enabling new application scenarios.

Here is a summary list of the new features.
  • Multitasking - in addition to faster application switching multitasking will allow applications to continue processing in the background.
  • Live Tile Functionality Enhancements -
  • Sensor Library Enhancements - You'll have enhanced access to the sensor library and
      Access to the camera and compass-! Reality augmentation is possible!
    • Sockets - This needs no explanation
    • Database -
    • IE9 - with hardware acceleration
    • Silverlight+XNA - you can use Silverlight and XNA in the same application
    • Twitter in the People Hub
    • Background Transfers
    • Profile
    • Silverlight 4 Runtime
    Several new countries are being added to the Marketplace. This brings up the total count from 17 to 35. The new countries are listed below with *.
    • Australia
    • Austria
    • Belgium
    • Brazil*
    • Canada
    • Chile*
    • Columbia*
    • Czech Republic*
    • Denmark*
    • Finland*
    • France
    • Germany
    • Greece*
    • Hong Kong
    • Hungary*
    • India*
    • Ireland
    • Italy
    • Japan*
    • Mexico
    • Netherlands*
    • New Zealand
    • Norway*
    • Poland*
    • Portugal*
    • Russia*
    • Singapore
    • South Africa*
    • South Korea*
    • Spain
    • Sweden*
    • Switzerland
    • Taiwan*
    • UK
    • USA /

    Tags: , ,

Apr 7 2011

Streaming from the Microphone to IsolatedStorage

Category: MobileJoel Ivory Johnson @ 16:45
Last week I posted a sample voice recorder on CodeProject. The application would buffer the entire recording in memory before writing it to a file. A rather astute reader asked me what would happen if the user let the recording go long enough to fill up memory. The answer to that question is the application would crash due to an exception being trhown when it fails to allocate more memory and all of the recordingwould be lost. I had already been thinking of a sime reusable solution for doing this but I also offered to the user the following code sample to handle streaming directly to IsolatedStorage.
My two goals in writing it were to keep it simple and keep it portable/reusable. As far as usage goes I can't think of any ways to make it any easier.
   //To start a recording
   StreamingRecorder myRecorder  = new StreamingRecorder();
   myRecorder.Start("myFileName");

  //To stop a recording();
  myRecorder.Stop();
After the code has run you will have a WAVE file with a proper header ready to be consumed by a SoundEffect, MediaElement, or whatever it is that you want to do with it.
In implementing this I must say that I have a hiher appreciation for how MediaElement's interface is designed. The starting and stopping process are not immediate. In otherwords when you call Start() or Stop() it is not until a few moments later that the request is fully processed. Because of the asynchronous nature of these processes I've implemented the event RecordingStateChanged and the property RecordingState so that I would know when a state change was complete. If you are familiar with the media element class then your recognize the similarity of this pattern.
I'll go into further details on how this works along with implemeting some other functionality (such as a Pause method) in a later post. But the code is in a working state now so I'm sharing it. :-)

Here is the source:
public class StreamingRecorder :INotifyPropertyChanged,  IDisposable
{


    object SyncLock = new object();

    private Queue<MemoryStream> _availablBufferQueue;        
    private Queue<MemoryStream> _writeBufferQueue;

    private int _bufferCount;
    private byte[] _audioBuffer;

    //private int _currentRecordingBufferIndex;
        

    private TimeSpan _bufferDuration;
    private int _bufferSize;
    private Stream _outputStream;
    private Microphone _currentMicrophone;
    private bool _ownsStream = false;
    private long _startPosition;

    

    public  StreamingRecorder(TimeSpan? bufferDuration = null, int bufferCount=2)
    {
        _bufferDuration = bufferDuration.HasValue ? bufferDuration.Value : TimeSpan.FromSeconds(0);
        _bufferCount = bufferCount;
        _currentMicrophone= Microphone.Default;   
    }

    private MemoryStream CurrentBuffer
    {
        get; set;
    }

    public void Start(string fileName)
    {
        var isoStore = System.IO.IsolatedStorage.IsolatedStorageFile.GetUserStoreForApplication();
        var targetFile = isoStore.OpenFile(fileName, FileMode.Create);
        Start(targetFile, true);
    }

    public void Start(Stream outputStream, bool ownsStream=false)
    {
        _outputStream = outputStream;
        _ownsStream = ownsStream;
        _startPosition = outputStream.Position;

        Size = 0;

        //Create our recording buffers
        _availablBufferQueue = new Queue<MemoryStream>();
        _writeBufferQueue = new Queue<MemoryStream>();
        _audioBuffer = new byte[_currentMicrophone.GetSampleSizeInBytes(_currentMicrophone.BufferDuration)];
        _bufferSize = _currentMicrophone.GetSampleSizeInBytes(_bufferDuration + _currentMicrophone.BufferDuration);
        for (var i = 0; i < _bufferCount; ++i)
        {
            _availablBufferQueue.Enqueue(new MemoryStream(_bufferSize));
        }

        CurrentBuffer = _availablBufferQueue.Dequeue();
        //Stuff a bogus wave header in the output stream as a space holder.
        //we will come back and make it valid later. For now the size is invalid.
        //I could have just as easily stuffed any set of values here as long as 
        //the size of those values equaled 0x2C
        WaveHeaderWriter.WriteHeader(CurrentBuffer, -1, 1, _currentMicrophone.SampleRate);
        Size += (int)CurrentBuffer.Position;

        //Subscribe to the Microphone's buffer ready event and start listening.
        _currentMicrophone.BufferReady += new EventHandler<EventArgs>(_currentMicrophone_BufferReady);            
        _currentMicrophone.Start();
    }


    void _currentMicrophone_BufferReady(object sender, EventArgs e)
    {
        _currentMicrophone.GetData(_audioBuffer);
        //If the recorder is paused (not implemented) then don't add this audio chunk to
        // the output. If HasFlushed is set then the recording is actually ready to shut
        //down and we shouldn't accumulate anything more. 
        if ((CurrentState != RecordingState.Paused))
        {
            //Append the audio chunk to our current buffer
            CurrentBuffer.Write(_audioBuffer, 0, _audioBuffer.Length);
            //Increment the size of the recording.
            Size += _audioBuffer.Length;
            //If the buffer is full or if we are shutting down then we need to submit
            //the buffer to be written to the output stream.
            if ((CurrentBuffer.Length > _bufferSize)||(CurrentState==RecordingState.Stopping))
            {

                SubmitToWriteBuffer(CurrentBuffer);
                //If we were shutting down then set a flag so that it is known that the last audio
                //chunk has been written. 
                if (CurrentState == RecordingState.Stopping)
                {
                    _currentMicrophone.Stop();
                    _currentMicrophone.BufferReady -= _currentMicrophone_BufferReady;
                }
                CurrentBuffer = _availablBufferQueue.Count > 0 ? _availablBufferQueue.Dequeue() : new MemoryStream();
            }
        }
    }

                

    // CurrentState - generated from ObservableField snippet - Joel Ivory Johnson

    private RecordingState _currentState;
    public RecordingState CurrentState
    {
        get { return _currentState; }
        set
        {
            if (_currentState != value)
            {
                _currentState = value;
                OnPropertyChanged("CurrentState");
                OnRecordingStateChanged(value);
            }
        }
    }
    //-----


    void WriteData(object a )
    {

        lock(SyncLock)
        {                
            while (_writeBufferQueue.Count > 0)
            {
                var item = _writeBufferQueue.Dequeue();
                var buffer = item.GetBuffer();
                _outputStream.Write(buffer, 0,(int) item.Length);
                item.SetLength(0);

                _availablBufferQueue.Enqueue(item);

                if (CurrentState == RecordingState.Stopping)
                {
                    //Correct the information in the wave header. After it is
                    //written set the file pointer back to the end of the file.
                    long prePosition = _outputStream.Position;
                    _outputStream.Seek(_startPosition, SeekOrigin.Begin);
                    WaveHeaderWriter.WriteHeader(_outputStream,Size-44,1,_currentMicrophone.SampleRate);
                    _outputStream.Seek(prePosition, SeekOrigin.Begin);
                    _outputStream.Flush();
                    if (_ownsStream)
                        _outputStream.Close();
                    CurrentState = RecordingState.Stopped;
                }
            }
        }
    }

    void SubmitToWriteBuffer(MemoryStream target)
    {
        //Do the writing on another thread so that processing on this thread can continue. 
        _writeBufferQueue.Enqueue(target);
        ThreadPool.QueueUserWorkItem(new WaitCallback(WriteData));
    }

    public void Pause()
    {
        if ((CurrentState != RecordingState.Paused) && (CurrentState != RecordingState.Recording))
        {
            throw new Exception("you can't pause if you are not recording");
        }
        CurrentState = RecordingState.Paused;
    }

    public void Stop()
    {
        CurrentState = RecordingState.Stopping;
    }


    // Size - generated from ObservableField snippet - Joel Ivory Johnson

    private int  _size;
    public int Size
    {
        get { return _size; }
        set
        {
            if (_size != value)
            {
                _size = value;
                OnPropertyChanged("Size");
            }
        }
    }
    //-----

    public long RemainingSpace
    {
        get
        {                
            return System.IO.IsolatedStorage.IsolatedStorageFile.GetUserStoreForApplication().AvailableFreeSpace;
        }
    }

    public TimeSpan RecordingDuration
    {
        get
        {
            return _currentMicrophone.GetSampleDuration((int)Size);
        }
    }

    public TimeSpan RemainingRecordingTime
    {
        get
        {
            return _currentMicrophone.GetSampleDuration((int)RemainingSpace);
        }
    }

    //-------

    public event EventHandler<RecordingStateChangedEventArgs> RecordingStateChanged;
    protected void OnRecordingStateChanged(RecordingState newState)
    {
        if(RecordingStateChanged!=null)
        {
            RecordingStateChanged(this, new RecordingStateChangedEventArgs(){NewState = newState});
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public void Dispose()
    {
        Stop();
    }
}

 

Tags: , ,

Mar 30 2011

Voice Memo source for WP7

Category: MobileJoel Ivory Johnson @ 18:00

This question keeps coming up in the forums so I decided to put the application together and make it publically available. If you head over to CodeProject you'll find a small article that I uploaded on making a voice memo application on Windows Phone 7. Among other things is demonstrates how to convert the raw recording bytes to a proper wave file, simple serialization, and a few other tid bits. For the sake of the article I did send the code through certification. 

However the application looks ugly right now. I've got a graphic artist that I'll be paying to design the UI for me and since I'm paying her for this I've decided not to include the graphic assets that she is producing in the code that I'm gicing away for free. 

There's no obligations attached to the code. But if you use it in your own products I would appreciate a heads up just so that I know where it's being used. 

Share photos on twitter with Twitpic

Tags: , ,